diff --git a/README.md b/README.md index a5eb4177..09d2b187 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Форк учебной ОС Xv6 для ITMO CSE > [!IMPORTANT] -> Этот репозиторий **содержит решения** лабораторных работ. Он -> создан исключительно для ревью их качества практиком курса Операционных систем -> ITMO CSE. +> Этот репозиторий **содержит решения** лабораторных работ. Он создан +> исключительно для ревью их качества практиком курса Операционных систем ITMO +> CSE. ## Начало работы @@ -25,7 +25,7 @@ Сделать утилиту для вывода системной информации => `sysinfo` -- executing `dump2tests`: +### executing `dump2tests`: ```fish $ dump2tests > /dev &; sysinfo @@ -38,7 +38,7 @@ System info: Number of open files: 8 ``` -- idle +### idle ```fish $ sysinfo @@ -46,3 +46,35 @@ System info: Number of procs: 3 Number of open files: 1 ``` + +## Доп. задание к лаб.4 - `RAID0` + +### Аппаратная реализация на x86_64 + +Два HDD диска от Seagate подключаются по SATA3 к материнской плате Х99 v205 на сокете LGA 2011v3. Операции производятся процессором Xeon Е5-2630v3, на плате установлено 32 гигабайта ECC DDR4 оперативной памяти, работающей на частоте 2400 Мгц. + +|![image](https://github.com/user-attachments/assets/57bb1f4a-2043-4623-9db1-47a025b8443a)| +|-| + +### Теория + +RAID 0 работает так: данные делятся на страйпы (обычно 64KB) и записываются поочерёдно на два диска: первый блок на диск A, второй на диск B и так далее. При записи оба диска работают параллельно, что увеличивает пропускную способность. Прирост есть только в скорости записи (и иногда последовательного чтения), так как данные записываются/читаются одновременно с двух дисков. Избыточности нет — выход из строя одного диска уничтожает все данные. + +|![image](https://github.com/user-attachments/assets/53618427-77aa-4d69-a55e-fef2c51cab24)| +|-| + + +### Программная реализация массива raid0 на ОС windows 10 + +|![image_2024-12-19_23-14-41](https://github.com/user-attachments/assets/63b5521a-48b8-4ecb-8c26-877f5eb1985f)| +|-| + +### Профилирование + +> [!NOTE] +> Видим жёский прирост на записи, спасибо raid0 + +|jbod (just hdd)|raid0 array| +|-|-| +|![image_2024-12-19_23-18-47](https://github.com/user-attachments/assets/9b814864-d75f-48c5-a412-aa5b93628c69)|![image_2024-12-19_23-11-58](https://github.com/user-attachments/assets/da892e34-ef42-4ea1-adfd-2005d834f788)| + diff --git a/vfs-kernel-module/source/vtfs.c b/vfs-kernel-module/source/vtfs.c index e95eb1b2..536a47fb 100644 --- a/vfs-kernel-module/source/vtfs.c +++ b/vfs-kernel-module/source/vtfs.c @@ -1,21 +1,654 @@ +#include #include #include #include +#include +#include + +#include "http.h" + +const char* SERVER_IP = "0.0.0.0"; +const int SERVER_PORT = 8080; + +// callee should call free_request on received buffer +int fill_request( + struct kvec* vec, const char* token, const char* method, size_t arg_size, va_list args +) { + // 2048 bytes for URL and 64 bytes for anything else + char* request_buffer = kzalloc(2048 + 64, GFP_KERNEL); + if (request_buffer == 0) { + return -ENOMEM; + } + + strcpy(request_buffer, "GET /api/"); + strcat(request_buffer, method); + + strcat(request_buffer, "?token="); + strcat(request_buffer, token); + + int i; + for (i = 0; i < arg_size; i++) { + strcat(request_buffer, "&"); + strcat(request_buffer, va_arg(args, char*)); + strcat(request_buffer, "="); + strcat(request_buffer, va_arg(args, char*)); + } + + strcat(request_buffer, " HTTP/1.1\r\nHost:"); + strcat(request_buffer, SERVER_IP); + strcat(request_buffer, "\r\nConnection: close\r\n\r\n"); + + memset(vec, 0, sizeof(struct kvec)); + vec->iov_base = request_buffer; + vec->iov_len = strlen(request_buffer); + + return 0; +} + +int receive_all(struct socket* sock, char* buffer, size_t buffer_size) { + struct msghdr hdr; + struct kvec vec; + + int read = 0; + + while (read < buffer_size) { + memset(&hdr, 0, sizeof(struct msghdr)); + memset(&vec, 0, sizeof(struct kvec)); + vec.iov_base = buffer + read; + vec.iov_len = buffer_size - read; + int ret = kernel_recvmsg(sock, &hdr, &vec, 1, vec.iov_len, 0); + if (ret == 0) { + break; + } else if (ret < 0) { + return -4; + } + read += ret; + } + + return read; +} + +int64_t parse_http_response( + char* raw_response, size_t raw_response_size, char* response, size_t response_size +) { + char* buffer = raw_response; + + // Read Response Line + { + char* status_line = strsep(&buffer, "\r"); + strsep(&status_line, " "); + if (status_line == 0) { + return -6; + } + char* status_code = strsep(&status_line, " "); + printk(KERN_INFO "Received response with status code %s\n", status_code); + if (strcmp(status_code, "200") != 0) { + return -5; + } + } + + int length = -1; + + while (true) { + if (buffer == 0) { + return -6; + } + char* header = strsep(&buffer, "\r"); + ++header; // skip \n + if (strcmp(header, "") == 0) { + // end of headers + break; + } + + if (strncmp(header, "Content-Length: ", 16) == 0) { + int error = kstrtoint(header + 16, 0, &length); + if (error != 0) { + return -6; + } + printk(KERN_INFO "Received response with content length %d\n", length); + } + } + ++buffer; // skip last '\n' + + if (length == -1) { + return -6; + } + + if (buffer + length > raw_response + raw_response_size) { + return -6; + } + + if (length < sizeof(int64_t)) { + return -7; + } + + length -= sizeof(int64_t); + + if (length > response_size) { + return -ENOSPC; + } + + int64_t return_value; + memcpy(&return_value, buffer, sizeof(int64_t)); + + buffer += sizeof(int64_t); + memcpy(response, buffer, length); + + return return_value; +} + +int64_t vtfs_http_call( + const char* token, + const char* method, + char* response_buffer, + size_t buffer_size, + size_t arg_size, + ... +) { + struct socket* sock; + int64_t error; + + error = sock_create_kern(&init_net, AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock); + if (error < 0) { + return -1; + } + + struct sockaddr_in s_addr = { + .sin_family = AF_INET, + .sin_addr = {.s_addr = in_aton(SERVER_IP)}, + .sin_port = htons(SERVER_PORT) + }; + + error = kernel_connect(sock, (struct sockaddr*)&s_addr, sizeof(struct sockaddr_in), 0); + if (error != 0) { + sock_release(sock); + return -2; + } + + struct kvec kvec; + va_list args; + va_start(args, arg_size); + error = fill_request(&kvec, token, method, arg_size, args); + va_end(args); + + if (error != 0) { + kernel_sock_shutdown(sock, SHUT_RDWR); + sock_release(sock); + return error; + } + + struct msghdr msg; + memset(&msg, 0, sizeof(struct msghdr)); + + error = kernel_sendmsg(sock, &msg, &kvec, 1, kvec.iov_len); + kfree(kvec.iov_base); + + if (error < 0) { + kernel_sock_shutdown(sock, SHUT_RDWR); + sock_release(sock); + return -3; + } + + size_t raw_buffer_size = buffer_size + 1024; // add 1KB for HTTP headers + char* raw_response_buffer = kmalloc(raw_buffer_size, GFP_KERNEL); + if (raw_response_buffer == 0) { + kernel_sock_shutdown(sock, SHUT_RDWR); + sock_release(sock); + return -ENOMEM; + } + int read_bytes = receive_all(sock, raw_response_buffer, raw_buffer_size); + + kernel_sock_shutdown(sock, SHUT_RDWR); + sock_release(sock); + + if (read_bytes < 0) { + kfree(raw_response_buffer); + return -4; + } + + error = parse_http_response(raw_response_buffer, read_bytes, response_buffer, buffer_size); + + kfree(raw_response_buffer); + return error; +} + +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'; +} + +struct __attribute__((__packed__)) lookup_response { + uint32_t ino; + uint8_t mode; +}; + +struct __attribute__((__packed__)) create_response { + uint32_t ino; +}; + +struct __attribute__((__packed__)) remove_response {}; + +struct __attribute__((__packed__)) iterate_response { + uint32_t count; + struct __attribute__((__packed__)) r_dentry { + char name[256]; + uint32_t ino; + uint8_t mode; + } r_dentries[8]; +}; + +struct __attribute__((__packed__)) read_response { + uint32_t size; + char data[2048]; +}; + +struct __attribute__((__packed__)) write_response {}; + +struct __attribute__((__packed__)) max_ino_response { + uint32_t ino; +}; #define MODULE_NAME "vtfs" +#define ROOT_INODE_INO 100 + MODULE_LICENSE("GPL"); MODULE_AUTHOR("secs-dev"); 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; + umode_t mode; + size_t i_size; + char i_data[1024]; +}; + +struct vtfs_dentry { + struct dentry* d_dentry; + char d_name[256]; + int d_parent_ino; + struct vtfs_inode* d_inode; + struct list_head list; +}; + +struct { + struct super_block* sb; + struct list_head dentries; +} 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) { + register_filesystem(&vtfs_fs_type); + + if (init() != 0) { + LOG("VTFS could not join the kernel\n"); + return -1; + } + LOG("VTFS joined the kernel\n"); return 0; } static void __exit vtfs_exit(void) { + unregister_filesystem(&vtfs_fs_type); LOG("VTFS left the kernel\n"); } diff --git a/xv6-os/.gitignore b/xv6-os/.gitignore index c19de7ba..23c9fdf7 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 00000000..f39983d7 --- /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); +}