From 50942e3e12ccb9e5a3fb99c2b2f97569be1d4e27 Mon Sep 17 00:00:00 2001 From: worthant Date: Fri, 27 Dec 2024 04:14:07 +0300 Subject: [PATCH 1/5] :hammer: feat(vtfs.c)!: RAM vfs module --- vfs-kernel-module/source/vtfs.c | 399 ++++++++++++++++++++++++++++++++ 1 file changed, 399 insertions(+) diff --git a/vfs-kernel-module/source/vtfs.c b/vfs-kernel-module/source/vtfs.c index e95eb1b..a5b8b30 100644 --- a/vfs-kernel-module/source/vtfs.c +++ b/vfs-kernel-module/source/vtfs.c @@ -1,7 +1,37 @@ +// What this module does essentially? +// It's out custom virtual filesystem implementation, like ext4, NFS or others +// You can find it's creation in struct vtfs_fs_type + +// It provides an interface for different operations: +// - lookup (find file / directory by name) +// - create/unlink (create/delete file from RAM) +// - mkdir/rmdir (create/remove directory from RAM) +// - read/write/iterate + +// Why do we even need those operations? +// So that you can do "ls", "cd", "rm", "cp" and other shell commands, that call those operations. + +#include #include #include #include +#include +#include +void encode(const char* src, char* dst) { + while (*src != '\0') { + if ((*src >= '0' && *src <= '9') || (*src >= 'a' && *src <= 'z') || + (*src >= 'A' && *src <= 'Z')) { + *dst = *src; + dst++; + } else { + sprintf(dst, "%%%02X", (unsigned char)*src); + dst += 3; + } + src++; + } + *dst = '\0'; +} #define MODULE_NAME "vtfs" MODULE_LICENSE("GPL"); @@ -10,6 +40,375 @@ MODULE_DESCRIPTION("A simple FS kernel module"); #define LOG(fmt, ...) pr_info("[" MODULE_NAME "]: " fmt, ##__VA_ARGS__) +unsigned long next_ino = ROOT_INODE_INO; + +struct inode* vtfs_get_inode(struct super_block*, const struct inode*, umode_t, int); + +struct vtfs_inode { + int ino; // Номер inode (наш внутренний) + umode_t mode; // Права доступа, тип (директория / файл) + size_t i_size; // Сколько байт данных (для файла) + char i_data[1024]; // Собственно, содержимое файла (до 1 КБ) +}; + +// Discovery entry +// links filename with it's inode +struct vtfs_dentry { + struct dentry* d_dentry; // Ссылка на ядровую dentry (которая хранит имя и связь с VFS) + char d_name[256]; // Имя файла/директории (например, "hello.txt") + int d_parent_ino; // Номер inode родительской директории + struct vtfs_inode* d_inode; // Ссылка на нашу vtfs_inode (данные) + struct list_head list; // Для включения в общий список (в vtfs_sb) +}; + +struct { + struct super_block* sb; // Ссылка на ядровый super_block + struct list_head dentries; // Двусвязный список всех "vtfs_dentry" +} vtfs_sb; + +// Find child file or dir in vtfs_sb.dentries +struct dentry* vtfs_lookup( + struct inode* parent_inode, struct dentry* child_dentry, unsigned int flag +) { + // ----------------| RAM |--------------------- + + struct vtfs_dentry* dentry; + struct list_head* pos; + struct inode* inode; + + list_for_each(pos, &vtfs_sb.dentries) { + dentry = list_entry(pos, struct vtfs_dentry, list); + + if (dentry->d_parent_ino == parent_inode->i_ino && + strcmp(dentry->d_name, child_dentry->d_name.name) == 0) { + inode = vtfs_get_inode(vtfs_sb.sb, parent_inode, dentry->d_inode->mode, dentry->d_inode->ino); + d_add(child_dentry, inode); + return NULL; + } + } + return NULL; // if nothing is found +}; + +// Create file in RAM +int vtfs_create(struct inode* parent_inode, struct dentry* child_dentry, umode_t mode, bool b) { + // ----------------| RAM |--------------------- + + struct inode* inode; + struct vtfs_dentry* new_dentry; + struct vtfs_inode* new_inode; + + inode = vtfs_get_inode(vtfs_sb.sb, parent_inode, mode, next_ino++); + if (!inode) { + return -ENOMEM; + } + + new_dentry = kmalloc(sizeof(struct vtfs_dentry), GFP_KERNEL); + if (!new_dentry) { + iput(inode); + return -ENOMEM; + } + + new_inode = kmalloc(sizeof(struct vtfs_inode), GFP_KERNEL); + if (!new_inode) { + kfree(new_dentry); + iput(inode); + return -ENOMEM; + } + + new_dentry->d_dentry = child_dentry; + strcpy(new_dentry->d_name, child_dentry->d_name.name); + new_dentry->d_parent_ino = parent_inode->i_ino; + new_dentry->d_inode = new_inode; + new_dentry->d_inode->ino = inode->i_ino; + new_dentry->d_inode->mode = inode->i_mode; + new_dentry->d_inode->i_size = 0; + + list_add(&new_dentry->list, &vtfs_sb.dentries); + + d_add(child_dentry, inode); + + printk(KERN_INFO "File %s added successfully\n", child_dentry->d_name.name); + + return 0; +} + +// Delete file from RAM +int vtfs_unlink(struct inode* parent_inode, struct dentry* child_dentry) { + // ----------------| RAM |--------------------- + + struct vtfs_dentry* found_dentry = NULL; + struct list_head* pos; + + list_for_each(pos, &vtfs_sb.dentries) { + found_dentry = list_entry(pos, struct vtfs_dentry, list); + + if (strcmp(found_dentry->d_name, child_dentry->d_name.name) == 0 && + found_dentry->d_parent_ino == parent_inode->i_ino) { + list_del(&found_dentry->list); + + kfree(found_dentry); + + printk(KERN_INFO "File %s deleted successfully\n", child_dentry->d_name.name); + + return 0; + } + } + + return -ENOENT; +} + +// Create dir in RAM +int vtfs_mkdir(struct inode* parent_inode, struct dentry* child_dentry, umode_t mode) { + // ----------------| RAM |--------------------- + + struct inode* inode; + struct vtfs_dentry* new_dentry; + + inode = vtfs_get_inode(vtfs_sb.sb, parent_inode, mode | S_IFDIR, next_ino++); + if (!inode) { + return -ENOMEM; + } + + new_dentry = kmalloc(sizeof(struct vtfs_dentry), GFP_KERNEL); + if (!new_dentry) { + iput(inode); + return -ENOMEM; + } + + new_dentry->d_inode = kmalloc(sizeof(struct vtfs_inode), GFP_KERNEL); + if (!new_dentry->d_inode) { + kfree(new_dentry); + iput(inode); + return -ENOMEM; + } + + new_dentry->d_dentry = child_dentry; + strcpy(new_dentry->d_name, child_dentry->d_name.name); + new_dentry->d_parent_ino = parent_inode->i_ino; + new_dentry->d_inode->ino = inode->i_ino; + new_dentry->d_inode->mode = inode->i_mode; + new_dentry->d_inode->i_size = 0; + + list_add(&new_dentry->list, &vtfs_sb.dentries); + + d_add(child_dentry, inode); + + printk(KERN_INFO "Directory %s created successfully\n", child_dentry->d_name.name); + + return 0; +} + +// Delete dir from RAM +int vtfs_rmdir(struct inode* parent_inode, struct dentry* child_dentry) { + // ----------------| RAM |--------------------- + + struct vtfs_dentry* found_dentry = NULL; + struct list_head* pos; + + list_for_each(pos, &vtfs_sb.dentries) { + found_dentry = list_entry(pos, struct vtfs_dentry, list); + + if (strcmp(found_dentry->d_name, child_dentry->d_name.name) == 0 && + found_dentry->d_parent_ino == parent_inode->i_ino) { + list_del(&found_dentry->list); + kfree(found_dentry); + + printk(KERN_INFO "File %s deleted successfully\n", child_dentry->d_name.name); + + return 0; + } + } + + return -ENOENT; // couldn't find any dir +} + +struct inode_operations vtfs_inode_ops = { + .lookup = vtfs_lookup, + .create = vtfs_create, + .unlink = vtfs_unlink, + .mkdir = vtfs_mkdir, + .rmdir = vtfs_rmdir, +}; + +// Iterate through all dir includes in RAM +int vtfs_iterate(struct file* file, struct dir_context* ctx) { + struct vtfs_dentry* dentry; + struct list_head* pos; + struct inode* dir_inode = file->f_path.dentry->d_inode; + unsigned char type; + + if (!dir_emit_dots(file, ctx)) + return 0; + + if (ctx->pos >= 3) { + return ctx->pos; + } + + list_for_each(pos, &vtfs_sb.dentries) { + dentry = list_entry(pos, struct vtfs_dentry, list); + + printk( + KERN_INFO "Dentry %s inode %ld data %s\n", + dentry->d_name, + dentry->d_inode->ino, + dentry->d_inode->i_data + ); + + if (S_ISDIR(dentry->d_inode->mode)) + type = DT_DIR; + else if (S_ISREG(dentry->d_inode->mode)) + type = DT_REG; + else + type = DT_UNKNOWN; + + if (dentry->d_parent_ino == dir_inode->i_ino && + !dir_emit(ctx, dentry->d_name, strlen(dentry->d_name), dentry->d_inode->ino, type)) { + return -ENOMEM; + } + + ctx->pos += 1; + } + + return ctx->pos; +} + +// Read file data from RAM +ssize_t vtfs_read(struct file* file, char* buffer, size_t len, loff_t* offset) { + // ----------------| RAM |--------------------- + + struct vtfs_inode* found_inode; + struct vtfs_dentry* found_dentry; + struct inode* file_inode = file->f_inode; + struct list_head* pos; + ssize_t to_read; + + list_for_each(pos, &vtfs_sb.dentries) { + found_dentry = list_entry(pos, struct vtfs_dentry, list); + found_inode = found_dentry->d_inode; + + if (found_dentry->d_inode->ino == file_inode->i_ino) { + if (*offset > found_inode->i_size) + return 0; + + to_read = min(len, found_inode->i_size - *offset); + if (copy_to_user(buffer, found_inode->i_data + *offset, to_read)) + return -EFAULT; + + *offset += to_read; + + return to_read; + } + } + + return -ENOENT; +} + +// write file data to RAM +ssize_t vtfs_write(struct file* file, const char* buffer, size_t len, loff_t* offset) { + // ----------------| RAM |--------------------- + + struct vtfs_inode* found_inode; + struct vtfs_dentry* found_dentry; + struct inode* file_inode = file->f_inode; + struct list_head* pos; + void* new_data; + ssize_t new_size; + + list_for_each(pos, &vtfs_sb.dentries) { + found_dentry = list_entry(pos, struct vtfs_dentry, list); + found_inode = found_dentry->d_inode; + + if (found_dentry->d_inode->ino == file_inode->i_ino) { + new_size = max(found_inode->i_size, *offset + len); + + if (copy_from_user(found_inode->i_data + *offset, buffer, len)) { + return -EFAULT; + } + + found_inode->i_size = new_size; + + *offset += len; + + return len; + } + } + + return -ENOENT; +} + +struct file_operations vtfs_dir_ops = { + .iterate = vtfs_iterate, + .read = vtfs_read, + .write = vtfs_write, +}; + +struct inode* vtfs_get_inode( + struct super_block* sb, const struct inode* dir, umode_t mode, int i_ino +) { + struct inode* inode = new_inode(sb); + if (inode != NULL) { + inode_init_owner(inode, dir, mode); + } + + inode->i_ino = i_ino; + inode->i_op = &vtfs_inode_ops; + inode->i_fop = &vtfs_dir_ops; + + inc_nlink(inode); + + return inode; +} + +int vtfs_fill_super(struct super_block* sb, void* data, int silent) { + umode_t mode = S_IFDIR | 0777; + + struct inode* inode = vtfs_get_inode(sb, NULL, mode, ROOT_INODE_INO); + if (!inode) { + printk(KERN_ERR "Failed to create a root inode"); + return -ENOMEM; + } + + sb->s_root = d_make_root(inode); + if (sb->s_root == NULL) { + printk(KERN_ERR "Failed to create a root dentry"); + return -ENOMEM; + } + + vtfs_sb.sb = sb; + INIT_LIST_HEAD(&vtfs_sb.dentries); + + printk(KERN_INFO "return 0\n"); + + return 0; +} + +struct dentry* vtfs_mount( + struct file_system_type* fs_type, int flags, const char* token, void* data +) { + struct dentry* ret = mount_nodev(fs_type, flags, data, vtfs_fill_super); + if (ret == NULL) { + printk(KERN_ERR "Can't mount file system"); + } else { + printk(KERN_INFO "Mounted successfuly"); + } + return ret; +} + +int init(void) { + next_ino = ROOT_INODE_INO; + return 0; +} + +void vtfs_kill_sb(struct super_block* sb) { + printk(KERN_INFO "vtfs super block is destroyed. Unmount successfully.\n"); +} + +struct file_system_type vtfs_fs_type = { + .name = "vtfs", + .mount = vtfs_mount, + .kill_sb = vtfs_kill_sb, +}; + static int __init vtfs_init(void) { LOG("VTFS joined the kernel\n"); return 0; From cafea698351a6cb24d0ad3d568523b502086c839 Mon Sep 17 00:00:00 2001 From: Boris Dvorkin Date: Fri, 27 Dec 2024 05:48:22 +0300 Subject: [PATCH 2/5] :ledger: docs(readme): Showcase vtfs module --- vfs-kernel-module/preview.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 vfs-kernel-module/preview.md diff --git a/vfs-kernel-module/preview.md b/vfs-kernel-module/preview.md new file mode 100644 index 0000000..342432a --- /dev/null +++ b/vfs-kernel-module/preview.md @@ -0,0 +1,13 @@ +## Showcase of solution in action + +- shell commands + +|![image](https://github.com/user-attachments/assets/3fe78c64-e4e2-416a-85af-21bfc0801132)| +|-| + +- logs + +|![image](https://github.com/user-attachments/assets/a5f2c272-6f0c-41d5-9dc8-f99dfdaa5f85)| +|-| + + From 38f10f752d3c91293eb5037726345c71ec540562 Mon Sep 17 00:00:00 2001 From: Boris Dvorkin Date: Fri, 27 Dec 2024 05:50:25 +0300 Subject: [PATCH 3/5] :ledger: docs(readme): Link vtfs preview doc --- vfs-kernel-module/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vfs-kernel-module/README.md b/vfs-kernel-module/README.md index 21b260a..d81e0d8 100644 --- a/vfs-kernel-module/README.md +++ b/vfs-kernel-module/README.md @@ -1,5 +1,13 @@ # ЛР. Файловая система +--- + +> [!IMPORTANT] +> [Реализация модуля](./preview.md) vtfs в RAM + +--- + + Ядро ОС `Linux` — [монолитное][1]. Это означает, что все его части работают в общем адресном пространстве. Однако, это не означает, что для добавления какой-то возможности необходимо полностью перекомпилировать ядро. Новую функциональность можно добавить в виде модуля ядра. Такие модули можно легко загружать и выгружать по необходимости прямо во время работы системы. Подробнее смотри глава 2.10 в книге «Операционные системы: внутренняя структура и принципы проектирования», Вильям Столлингс. From fa4ebcce86046e3d77dcef6b3609139004e2c7db Mon Sep 17 00:00:00 2001 From: Boris Dvorkin Date: Fri, 27 Dec 2024 06:11:37 +0300 Subject: [PATCH 4/5] :milky_way: docs(preview): Beautify vtfs module documentation --- vfs-kernel-module/preview.md | 176 ++++++++++++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 5 deletions(-) diff --git a/vfs-kernel-module/preview.md b/vfs-kernel-module/preview.md index 342432a..33b7ae5 100644 --- a/vfs-kernel-module/preview.md +++ b/vfs-kernel-module/preview.md @@ -1,13 +1,179 @@ -## Showcase of solution in action +# Showcase of solution in action - shell commands -|![image](https://github.com/user-attachments/assets/3fe78c64-e4e2-416a-85af-21bfc0801132)| -|-| +| ![image](https://github.com/user-attachments/assets/3fe78c64-e4e2-416a-85af-21bfc0801132) | +| ----------------------------------------------------------------------------------------- | - logs -|![image](https://github.com/user-attachments/assets/a5f2c272-6f0c-41d5-9dc8-f99dfdaa5f85)| -|-| +| ![image](https://github.com/user-attachments/assets/a5f2c272-6f0c-41d5-9dc8-f99dfdaa5f85) | +| ----------------------------------------------------------------------------------------- | +# Virtual File System (VTFS) module in the Kernel +## 1. Краткое описание + +В рамках данного проекта я реализовал простую файловую систему (**VTFS**) на +уровне ядра Linux. Основная идея — дать возможность монтировать собственную VFS +и работать с ней как с обычной директорией. При этом основная логика хранится в +структуре данных, расположенной в **inode** (и/или дополнительных структурах, +связанных с VFS). + +- **Ключевые изменения**: + - Подготовил и зарегистрировал файл `vtfs.c` как модуль ядра (kernel module). + - Реализовал внутри него функции, необходимые для работы с VFS: `lookup`, + `create`, `mkdir`, `rmdir`, `unlink`, `read`, `write`, `iterate` (и др.). + - Создал собственные структуры для хранения в оперативной памяти: + `vtfs_inode`, `vtfs_dentry`, а также глобальную `vtfs_sb` для связи с + `super_block`. + - В итоге получилась виртуальная ФС, которая может быть примонтирована (через + `mount -t vtfs`) и управляется обычными командами шела `ls`, `cd`, `mkdir`, + `touch`, `rm`, и т.д. + +--- + +## 2. Устройство VTFS + +### 2.1. Основные структуры + +1. **`struct vtfs_inode`** + + - Хранит номер inode (ino), режим (mode), размер (i_size) и массив данных + (i_data). + - Для директорий хранит информацию о том, что это `S_IFDIR`, для **файлов** — + `S_IFREG`. + - `i_data[1024]` позволяет хранить содержимое (для файлов) прямо в + оперативной памяти. + +2. **`struct vtfs_dentry`** + + - Связывает **имя** (d_name) с конкретным `vtfs_inode`. + - Запоминает `d_parent_ino` (какой inode является родительским). + - Включена в двусвязный список `vtfs_sb.dentries` (через + `struct list_head list`). + +3. **`vtfs_sb` (глобальная структура)** + ```c + struct { + struct super_block* sb; + struct list_head dentries; + } vtfs_sb; + ``` + +- sb указывает на суперблок, зарегистрированный в ядре при монтировании. +- dentries — двусвязный список vtfs_dentry, в котором мы храним все + файлы/директории, когда работаем в RAM-режиме + +4. Ядровые структуры (уже существуют в Linux): + +- `struct inode` (ядро) — метаданные файла в общей системе VFS: хранит указатели + на операции (i_op, i_fop) и номер inode (i_ino). +- `struct dentry` (ядро) — объект, который связывает имя (в каталоге) с + конкретным inode. +- `struct super_block` (ядро) — описывает весь «том» (в данном случае + виртуальную ФС), указывает на корневую dentry. + +## Связь между командами и соответствующими операциями + +| Команда Shell | Что делает VFS | Функция в нашем модуле | +| --------------------------------- | -------------------------------------------------- | ---------------------------------------------------- | +| `ls /mnt/vt` | Открывает каталог `/mnt/vt`, вызывает перечисление | `vtfs_iterate` | +| `cd /mnt/vt` | Ищет директорию `/mnt/vt`, проверяет доступ | `lookup` (для корня), дальше VFS управляет переходом | +| `touch /mnt/vt/hello.txt` | Создаёт (или открывает) файл | `vtfs_create` | +| `echo "data" > /mnt/vt/hello.txt` | Открывает файл на запись, пишет данные | `vtfs_write` | +| `cat /mnt/vt/hello.txt` | Открывает файл на чтение, читает данные | `vtfs_read` | +| `mkdir /mnt/vt/newdir` | Создаёт поддиректорию | `vtfs_mkdir` | +| `rmdir /mnt/vt/newdir` | Удаляет директорию | `vtfs_rmdir` | +| `rm /mnt/vt/hello.txt` | Удаляет файл | `vtfs_unlink` | + +### 4.1. Загрузка модуля и монтирование + +```bash +sudo insmod vtfs.ko +``` + +Ядро загружает наш модуль. +В `vtfs_init` мы регистрируем файл `vtfs_fs_type` через `register_filesystem`. + +```bash +sudo mount -t vtfs /mnt/vt +``` + +Ядро вызывает vtfs_mount → mount_nodev → vtfs_fill_super. +`vtfs_fill_super` создаёт корневой **inode**, формирует sb->s_root = +d_make_root(inode). Инициализируем vtfs_sb.sb = sb; +INIT_LIST_HEAD(&vtfs_sb.dentries);. + +### 4.2. Создание файла + +```bash +touch /mnt/vt/test.txt +``` + +Shell вызывает системный вызов open("/mnt/vt/test.txt", O_CREAT, ...). +VFS видит, что это «vtfs», и ищет операцию create в vtfs_inode_ops. + +- В vtfs_create: + +Выделяется vtfs_inode с новым номером (ino), mode = S_IFREG. +Создаётся vtfs_dentry, записывается имя "test.txt", родитель ROOT_INODE_INO. +Запись добавляется в список vtfs_sb.dentries. Вызывается d_add(child_dentry, +inode). + +### 4.3. Запись в файл + +```bash +echo "Hello" > /mnt/vt/test.txt +``` + +Shell вызывает write (внутри echo). +Ядро идёт в file_operations → .write = vtfs_write. + +- В vtfs_write: + +Находится в vtfs_sb.dentries нужный vtfs_dentry, берётся d_inode->i_data. +Данные копируются из user space в этот массив, обновляется i_size. + +### 4.4. Чтение файла + +```bash +cat /mnt/vt/test.txt +``` + +Shell вызывает `read` (через cat). +Ядро вызывает vtfs_dir_ops.read → vtfs_read. + +- В vtfs_read: + +Ищется файл по `inode->i_ino`, находится `vtfs_inode->i_data`. +Данные копируются из `i_data` в user space (выводятся на экран). + +### 4.5. Удаление файла + +```bash +rm /mnt/vt/test.txt +``` + +Shell вызывает `unlink("/mnt/vt/test.txt")`. +Ядро вызывает vtfs_inode_ops.unlink. + +- В vtfs_unlink: + +Ищется запись в vtfs_sb.dentries, где d_name == "test.txt" и parent_ino == +ROOT_INODE_INO. Запись удаляется из списка (list_del), освобождается (kfree). + +### 4.6. Размонтирование и выгрузка модуля + +```bash +sudo umount /mnt/vt +``` + +Ядро вызывает vtfs_kill_sb, удаляет корневую dentry. + +```bash +sudo rmmod vtfs +``` + +Ядро вызывает vtfs_exit, мы делаем unregister_filesystem. Все структуры, которые +были в оперативке, освобождаются, и файловая система исчезает. From 9aae34d6eddf1c0014041290cfd29b3622429c19 Mon Sep 17 00:00:00 2001 From: worthant Date: Wed, 1 Jul 2026 20:24:12 +0300 Subject: [PATCH 5/5] :wrench: fix(ci): restore mkfs dropped by refactor; fix .gitignore --- xv6-os/.gitignore | 2 +- xv6-os/mkfs/mkfs.c | 303 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 xv6-os/mkfs/mkfs.c diff --git a/xv6-os/.gitignore b/xv6-os/.gitignore index c19de7b..23c9fdf 100644 --- a/xv6-os/.gitignore +++ b/xv6-os/.gitignore @@ -11,7 +11,7 @@ entryother initcode initcode.out kernelmemfs -mkfs +mkfs/mkfs kernel/kernel user/usys.S .gdbinit diff --git a/xv6-os/mkfs/mkfs.c b/xv6-os/mkfs/mkfs.c new file mode 100644 index 0000000..f39983d --- /dev/null +++ b/xv6-os/mkfs/mkfs.c @@ -0,0 +1,303 @@ +#include +#include +#include +#include +#include +#include + +#define stat xv6_stat // avoid clash with host struct stat +#include "kernel/types.h" +#include "kernel/fs.h" +#include "kernel/stat.h" +#include "kernel/param.h" + +#ifndef static_assert +#define static_assert(a, b) do { switch (0) case 0: case (a): ; } while (0) +#endif + +#define NINODES 200 + +// Disk layout: +// [ boot block | sb block | log | inode blocks | free bit map | data blocks ] + +int nbitmap = FSSIZE/BPB + 1; +int ninodeblocks = NINODES / IPB + 1; +int nlog = LOGSIZE; +int nmeta; // Number of meta blocks (boot, sb, nlog, inode, bitmap) +int nblocks; // Number of data blocks + +int fsfd; +struct superblock sb; +char zeroes[BSIZE]; +uint freeinode = 1; +uint freeblock; + + +void balloc(int); +void wsect(uint, void*); +void winode(uint, struct dinode*); +void rinode(uint inum, struct dinode *ip); +void rsect(uint sec, void *buf); +uint ialloc(ushort type); +void iappend(uint inum, void *p, int n); +void die(const char *); + +// convert to riscv byte order +ushort +xshort(ushort x) +{ + ushort y; + uchar *a = (uchar*)&y; + a[0] = x; + a[1] = x >> 8; + return y; +} + +uint +xint(uint x) +{ + uint y; + uchar *a = (uchar*)&y; + a[0] = x; + a[1] = x >> 8; + a[2] = x >> 16; + a[3] = x >> 24; + return y; +} + +int +main(int argc, char *argv[]) +{ + int i, cc, fd; + uint rootino, inum, off; + struct dirent de; + char buf[BSIZE]; + struct dinode din; + + + static_assert(sizeof(int) == 4, "Integers must be 4 bytes!"); + + if(argc < 2){ + fprintf(stderr, "Usage: mkfs fs.img files...\n"); + exit(1); + } + + assert((BSIZE % sizeof(struct dinode)) == 0); + assert((BSIZE % sizeof(struct dirent)) == 0); + + fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666); + if(fsfd < 0) + die(argv[1]); + + // 1 fs block = 1 disk sector + nmeta = 2 + nlog + ninodeblocks + nbitmap; + nblocks = FSSIZE - nmeta; + + sb.magic = FSMAGIC; + sb.size = xint(FSSIZE); + sb.nblocks = xint(nblocks); + sb.ninodes = xint(NINODES); + sb.nlog = xint(nlog); + sb.logstart = xint(2); + sb.inodestart = xint(2+nlog); + sb.bmapstart = xint(2+nlog+ninodeblocks); + + printf("nmeta %d (boot, super, log blocks %u inode blocks %u, bitmap blocks %u) blocks %d total %d\n", + nmeta, nlog, ninodeblocks, nbitmap, nblocks, FSSIZE); + + freeblock = nmeta; // the first free block that we can allocate + + for(i = 0; i < FSSIZE; i++) + wsect(i, zeroes); + + memset(buf, 0, sizeof(buf)); + memmove(buf, &sb, sizeof(sb)); + wsect(1, buf); + + rootino = ialloc(T_DIR); + assert(rootino == ROOTINO); + + bzero(&de, sizeof(de)); + de.inum = xshort(rootino); + strcpy(de.name, "."); + iappend(rootino, &de, sizeof(de)); + + bzero(&de, sizeof(de)); + de.inum = xshort(rootino); + strcpy(de.name, ".."); + iappend(rootino, &de, sizeof(de)); + + for(i = 2; i < argc; i++){ + // get rid of "user/" + char *shortname; + if(strncmp(argv[i], "user/", 5) == 0) + shortname = argv[i] + 5; + else + shortname = argv[i]; + + assert(index(shortname, '/') == 0); + + if((fd = open(argv[i], 0)) < 0) + die(argv[i]); + + // Skip leading _ in name when writing to file system. + // The binaries are named _rm, _cat, etc. to keep the + // build operating system from trying to execute them + // in place of system binaries like rm and cat. + if(shortname[0] == '_') + shortname += 1; + + assert(strlen(shortname) <= DIRSIZ); + + inum = ialloc(T_FILE); + + bzero(&de, sizeof(de)); + de.inum = xshort(inum); + strncpy(de.name, shortname, DIRSIZ); + iappend(rootino, &de, sizeof(de)); + + while((cc = read(fd, buf, sizeof(buf))) > 0) + iappend(inum, buf, cc); + + close(fd); + } + + // fix size of root inode dir + rinode(rootino, &din); + off = xint(din.size); + off = ((off/BSIZE) + 1) * BSIZE; + din.size = xint(off); + winode(rootino, &din); + + balloc(freeblock); + + exit(0); +} + +void +wsect(uint sec, void *buf) +{ + if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE) + die("lseek"); + if(write(fsfd, buf, BSIZE) != BSIZE) + die("write"); +} + +void +winode(uint inum, struct dinode *ip) +{ + char buf[BSIZE]; + uint bn; + struct dinode *dip; + + bn = IBLOCK(inum, sb); + rsect(bn, buf); + dip = ((struct dinode*)buf) + (inum % IPB); + *dip = *ip; + wsect(bn, buf); +} + +void +rinode(uint inum, struct dinode *ip) +{ + char buf[BSIZE]; + uint bn; + struct dinode *dip; + + bn = IBLOCK(inum, sb); + rsect(bn, buf); + dip = ((struct dinode*)buf) + (inum % IPB); + *ip = *dip; +} + +void +rsect(uint sec, void *buf) +{ + if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE) + die("lseek"); + if(read(fsfd, buf, BSIZE) != BSIZE) + die("read"); +} + +uint +ialloc(ushort type) +{ + uint inum = freeinode++; + struct dinode din; + + bzero(&din, sizeof(din)); + din.type = xshort(type); + din.nlink = xshort(1); + din.size = xint(0); + winode(inum, &din); + return inum; +} + +void +balloc(int used) +{ + uchar buf[BSIZE]; + int i; + + printf("balloc: first %d blocks have been allocated\n", used); + assert(used < BPB); + bzero(buf, BSIZE); + for(i = 0; i < used; i++){ + buf[i/8] = buf[i/8] | (0x1 << (i%8)); + } + printf("balloc: write bitmap block at sector %d\n", sb.bmapstart); + wsect(sb.bmapstart, buf); +} + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +void +iappend(uint inum, void *xp, int n) +{ + char *p = (char*)xp; + uint fbn, off, n1; + struct dinode din; + char buf[BSIZE]; + uint indirect[NINDIRECT]; + uint x; + + rinode(inum, &din); + off = xint(din.size); + // printf("append inum %d at off %d sz %d\n", inum, off, n); + while(n > 0){ + fbn = off / BSIZE; + assert(fbn < MAXFILE); + if(fbn < NDIRECT){ + if(xint(din.addrs[fbn]) == 0){ + din.addrs[fbn] = xint(freeblock++); + } + x = xint(din.addrs[fbn]); + } else { + if(xint(din.addrs[NDIRECT]) == 0){ + din.addrs[NDIRECT] = xint(freeblock++); + } + rsect(xint(din.addrs[NDIRECT]), (char*)indirect); + if(indirect[fbn - NDIRECT] == 0){ + indirect[fbn - NDIRECT] = xint(freeblock++); + wsect(xint(din.addrs[NDIRECT]), (char*)indirect); + } + x = xint(indirect[fbn-NDIRECT]); + } + n1 = min(n, (fbn + 1) * BSIZE - off); + rsect(x, buf); + bcopy(p, buf + off - (fbn * BSIZE), n1); + wsect(x, buf); + n -= n1; + off += n1; + p += n1; + } + din.size = xint(off); + winode(inum, &din); +} + +void +die(const char *s) +{ + perror(s); + exit(1); +}