diff --git a/lab5/user/lab5/tmpfs/tmpfs.c b/lab5/user/lab5/tmpfs/tmpfs.c new file mode 100644 index 0000000..4595064 --- /dev/null +++ b/lab5/user/lab5/tmpfs/tmpfs.c @@ -0,0 +1,538 @@ +#include "tmpfs.h" + +#include +#include +#include +#include +#include + +static struct inode *tmpfs_root; + +/* + * Helper functions to calucate hash value of string + */ +static inline u64 hash_chars(const char *str, ssize_t len) +{ + u64 seed = 131; /* 31 131 1313 13131 131313 etc.. */ + u64 hash = 0; + int i; + + if (len < 0) { + while (*str) { + hash = (hash * seed) + *str; + str++; + } + } else { + for (i = 0; i < len; ++i) + hash = (hash * seed) + str[i]; + } + + return hash; +} + +/* BKDR hash */ +static inline u64 hash_string(struct string *s) +{ + return (s->hash = hash_chars(s->str, s->len)); +} + +static inline int init_string(struct string *s, const char *name, size_t len) +{ + int i; + + s->str = malloc(len + 1); + if (!s->str) + return -ENOMEM; + s->len = len; + + for (i = 0; i < len; ++i) + s->str[i] = name[i]; + s->str[len] = '\0'; + + hash_string(s); + return 0; +} + +/* + * Helper functions to create instances of key structures + */ +static inline struct inode *new_inode(void) +{ + struct inode *inode = malloc(sizeof(*inode)); + + if (!inode) + return ERR_PTR(-ENOMEM); + + inode->type = 0; + inode->size = 0; + + return inode; +} + +static struct inode *new_dir(void) +{ + struct inode *inode; + + inode = new_inode(); + if (IS_ERR(inode)) + return inode; + inode->type = FS_DIR; + init_htable(&inode->dentries, 1024); + + return inode; +} + +static struct inode *new_reg(void) +{ + struct inode *inode; + + inode = new_inode(); + if (IS_ERR(inode)) + return inode; + inode->type = FS_REG; + init_radix_w_deleter(&inode->data, free); + + return inode; +} + +static struct dentry *new_dent(struct inode *inode, const char *name, + size_t len) +{ + struct dentry *dent; + int err; + + dent = malloc(sizeof(*dent)); + if (!dent) + return ERR_PTR(-ENOMEM); + err = init_string(&dent->name, name, len); + if (err) { + free(dent); + return ERR_PTR(err); + } + dent->inode = inode; + + return dent; +} + +// this function create a file (directory if `mkdir` == true, otherwise regular +// file) and its size is `len`. You should create an inode and corresponding +// dentry, then add dentey to `dir`'s htable by `htable_add`. +// Assume that no separator ('/') in `name`. +static int tfs_mknod(struct inode *dir, const char *name, size_t len, int mkdir) +{ + struct inode *inode; + struct dentry *dent; + + BUG_ON(!name); + + if (len == 0) { + WARN("mknod with len of 0"); + return -ENOENT; + } + // TODO: write your code here + + // 创建 inode + if (mkdir) { + inode = new_dir(); + } else { + inode = new_reg(); + } + + // 创建 dentry + dent = malloc(sizeof(struct dentry)); + init_string(&dent->name, name, len); + dent->inode = inode; + + // 添加至哈希表 + htable_add(&dir->dentries, hash_chars(name, len), &dent->node); + + return 0; +} + +int tfs_mkdir(struct inode *dir, const char *name, size_t len) +{ + return tfs_mknod(dir, name, len, 1 /* mkdir */ ); +} + +int tfs_creat(struct inode *dir, const char *name, size_t len) +{ + return tfs_mknod(dir, name, len, 0 /* mkdir */ ); +} + +// look up a file called `name` under the inode `dir` +// and return the dentry of this file +static struct dentry *tfs_lookup(struct inode *dir, const char *name, + size_t len) +{ + u64 hash = hash_chars(name, len); + struct dentry *dent; + struct hlist_head *head; + + head = htable_get_bucket(&dir->dentries, (u32) hash); + + for_each_in_hlist(dent, node, head) { + if (dent->name.len == len && 0 == strcmp(dent->name.str, name)) + return dent; + } + return NULL; +} + +// Walk the file system structure to locate a file with the pathname stored in `*name` +// and saves parent dir to `*dirat` and the filename to `*name`. +// If `mkdir_p` is true, you need to create intermediate directories when it missing. +// If the pathname `*name` starts with '/', then lookup starts from `tmpfs_root`, +// else from `*dirat`. +// Note that when `*name` ends with '/', the inode of last component will be +// saved in `*dirat` regardless of its type (e.g., even when it's FS_REG) and +// `*name` will point to '\0' +int tfs_namex(struct inode **dirat, const char **name, int mkdir_p) +{ + BUG_ON(dirat == NULL); + BUG_ON(name == NULL); + BUG_ON(*name == NULL); + + char buff[MAX_FILENAME_LEN + 1]; + int buff_len; + struct dentry *dent = NULL; + int err; + char ch; + + if (**name == '/') { + *dirat = tmpfs_root; + // make sure `name` starts with actual name + while (**name && **name == '/') + ++(*name); + } else { + BUG_ON(*dirat == NULL); + BUG_ON((*dirat)->type != FS_DIR); + } + + // make sure a child name exists + if (!**name) + return -EINVAL; + + // 遍历各级目录 + while (**name != '\0') { + // 读文件名 + buff_len = 0; + while ((ch = (*name)[buff_len]) && ch != '/') { + buff[buff_len] = ch; + buff_len++; + } + if (ch == '\0') { + // 为文件名,直接结束循环 + break; + } + *name += buff_len; + buff[buff_len] = '\0'; + // 确保父目录 + if ((*dirat)->type == FS_REG) { + err = -ENOTDIR; + goto output_err; + } + // 找文件 + dent = tfs_lookup(*dirat, buff, buff_len); + if (dent == NULL) { + if (mkdir_p) { + err = tfs_mkdir(*dirat, buff, buff_len); + if (err != 0) { + goto output_err; + } + dent = tfs_lookup(*dirat, buff, buff_len); + } else { + err = -ENOTDIR; + goto output_err; + } + } + // 检查是否为目录 + if (**name == '/') { + *dirat = dent->inode; + dent = NULL; + // 忽略连续 slash + while (**name && **name == '/') + ++(*name); + } + } + + return 0; + output_err: + return err; +} + +int tfs_remove(struct inode *dir, const char *name, size_t len) +{ + u64 hash = hash_chars(name, len); + struct dentry *dent, *target = NULL; + struct hlist_head *head; + + BUG_ON(!name); + + if (len == 0) { + WARN("mknod with len of 0"); + return -ENOENT; + } + + head = htable_get_bucket(&dir->dentries, (u32) hash); + + for_each_in_hlist(dent, node, head) { + if (dent->name.len == len && 0 == strcmp(dent->name.str, name)) { + target = dent; + break; + } + } + + if (!target) + return -ENOENT; + + BUG_ON(!target->inode); + + // remove only when file is closed by all processes + if (target->inode->type == FS_REG) { + // free radix tree + radix_free(&target->inode->data); + // free inode + free(target->inode); + // remove dentry from parent + htable_del(&target->node); + // free dentry + free(target); + } else if (target->inode->type == FS_DIR) { + if (!htable_empty(&target->inode->dentries)) + return -ENOTEMPTY; + + // free htable + htable_free(&target->inode->dentries); + // free inode + free(target->inode); + // remove dentry from parent + htable_del(&target->node); + // free dentry + free(target); + } else { + BUG("inode type that shall not exist"); + } + + return 0; +} + +int init_tmpfs(void) +{ + tmpfs_root = new_dir(); + + return 0; +} + +// write memory into `inode` at `offset` from `buf` for length is `size` +// it may resize the file +// `radix_get`, `radix_add` are used in this function +// You can use memory functions defined in libc +ssize_t tfs_file_write(struct inode * inode, off_t offset, const char *data, + size_t size) +{ + BUG_ON(inode->type != FS_REG); + BUG_ON(offset > inode->size); + + u64 page_no, page_off; + u64 cur_off = offset; + size_t to_write, cur_to_write; + void *page; + + // TODO: write your code here + + // 计算长度 + to_write = size; + + // 尝试写 + while (to_write > 0) { + page_no = cur_off / PAGE_SIZE; + page_off = cur_off & (PAGE_SIZE - 1); + page = radix_get(&inode->data, page_no); + // 计算读取大小 + cur_to_write = to_write; + if (cur_to_write > PAGE_SIZE - page_off) { + cur_to_write = PAGE_SIZE - page_off; + } + + // 不存在页就创建 + if (page == NULL) { + page = calloc(1, PAGE_SIZE); + radix_add(&inode->data, page_no, page); + } + // 写数据 + memcpy(page + page_off, data + cur_off, cur_to_write); + + to_write -= cur_to_write; + cur_off += cur_to_write; + } + + if (cur_off > inode->size) { + inode->size = cur_off; + } + + printf("Size: %d\n", cur_off - offset); + return cur_off - offset; +} + +// read memory from `inode` at `offset` in to `buf` for length is `size`, do not +// exceed the file size +// `radix_get` is used in this function +// You can use memory functions defined in libc +ssize_t tfs_file_read(struct inode * inode, off_t offset, char *buff, + size_t size) +{ + BUG_ON(inode->type != FS_REG); + BUG_ON(offset > inode->size); + + u64 page_no, page_off; + u64 cur_off = offset; + size_t to_read, cur_to_read; + void *page; + + // 计算未读取数量 + to_read = size; + if (to_read > inode->size) { + to_read = inode->size; + } + + // 尝试读取 + while (to_read > 0) { + page_no = cur_off / PAGE_SIZE; + page_off = cur_off & (PAGE_SIZE - 1); + page = radix_get(&inode->data, page_no); + // 计算读取大小 + cur_to_read = to_read; + if (cur_to_read > PAGE_SIZE - page_off) { + cur_to_read = PAGE_SIZE - page_off; + } + // 读取数据 + if (page == NULL) { + memset(buff + cur_off, 0, cur_to_read); + } else { + memcpy(buff + cur_off, page + page_off, cur_to_read); + } + to_read -= cur_to_read; + cur_off += cur_to_read; + } + + return cur_off - offset; +} + +// load the cpio archive into tmpfs with the begin address as `start` in memory +// You need to create directories and files if necessary. You also need to write +// the data into the tmpfs. +int tfs_load_image(const char *start) +{ + struct cpio_file *f; + struct inode *dirat; + struct dentry *dent; + const char *leaf; + size_t len; + int err; + ssize_t write_count; + char pathname[MAX_FILENAME_LEN + 1]; + + BUG_ON(start == NULL); + + cpio_init_g_files(); + cpio_extract(start, "/"); + + for (f = g_files.head.next; f; f = f->next) { + // TODO: Lab5: your code is here + // 忽略文件夹 + len = f->header.c_filesize; + if (len == 0) { + continue; + } + // 处理文件名 + pathname[0] = '/'; pathname[1] = '\0'; + strcat(pathname, f->name); + leaf = pathname; + printf("Load file: %s, %lx, %d\n", leaf, f->data, len); + // 创建目录 + err = tfs_namex(&dirat, &leaf, 1); + if (err != 0) { + return err; + } + // 创建文件 + err = tfs_creat(dirat, leaf, strlen(leaf)); + if (err != 0) { + return err; + } + dent = tfs_lookup(dirat, leaf, strlen(leaf)); + if (dent == NULL) { + return -EFAULT; + } + // 写入数据 + // printf("Created file: %s\n", leaf); + if (len != tfs_file_write(dent->inode, 0, f->data, len)) { + return -EFAULT; + } + } + + return 0; +} + +static int dirent_filler(void **dirpp, void *end, char *name, off_t off, + unsigned char type, ino_t ino) +{ + struct dirent *dirp = *(struct dirent **)dirpp; + void *p = dirp; + unsigned short len = strlen(name) + 1 + + sizeof(dirp->d_ino) + + sizeof(dirp->d_off) + sizeof(dirp->d_reclen) + sizeof(dirp->d_type); + p += len; + if (p > end) + return -EAGAIN; + dirp->d_ino = ino; + dirp->d_off = off; + dirp->d_reclen = len; + dirp->d_type = type; + strcpy(dirp->d_name, name); + *dirpp = p; + return len; +} + +int tfs_scan(struct inode *dir, unsigned int start, void *buf, void *end) +{ + s64 cnt = 0; + int b; + int ret; + ino_t ino; + void *p = buf; + unsigned char type; + struct dentry *iter; + + for_each_in_htable(iter, b, node, &dir->dentries) { + if (cnt >= start) { + type = iter->inode->type; + ino = iter->inode->size; + ret = dirent_filler(&p, end, iter->name.str, + cnt, type, ino); + if (ret <= 0) { + return cnt - start; + } + } + cnt++; + } + return cnt - start; + +} + +/* path[0] must be '/' */ +struct inode *tfs_open_path(const char *path) +{ + struct inode *dirat = NULL; + const char *leaf = path; + struct dentry *dent; + int err; + + if (*path == '/' && !*(path + 1)) + return tmpfs_root; + + err = tfs_namex(&dirat, &leaf, 0); + if (err) + return NULL; + + dent = tfs_lookup(dirat, leaf, strlen(leaf)); + return dent ? dent->inode : NULL; +} diff --git a/lab5/user/lab5/tmpfs/tmpfs_server.c b/lab5/user/lab5/tmpfs/tmpfs_server.c new file mode 100644 index 0000000..69b40e2 --- /dev/null +++ b/lab5/user/lab5/tmpfs/tmpfs_server.c @@ -0,0 +1,165 @@ +#include "tmpfs_server.h" + +#include + +// int fs_stat(const char *pathname, struct stat *statbuf); +// int fs_getdents(int fd, struct dirent *dirp, size_t count); + +int fs_server_init(u64 cpio_start) +{ + init_tmpfs(); + usys_fs_load_cpio(cpio_start); + return tfs_load_image((char *)cpio_start); +} + +int fs_server_mkdir(const char *path) +{ + struct inode *dirat = NULL; + const char *leaf = path; + int err; + + BUG_ON(!path); + BUG_ON(*path != '/'); + + err = tfs_namex(&dirat, &leaf, 0); + if (err) + return err; + + err = tfs_mkdir(dirat, leaf, strlen(leaf)); + return err; +} + +int fs_server_creat(const char *path) +{ + struct inode *dirat = NULL; + const char *leaf = path; + int err; + + BUG_ON(!path); + BUG_ON(*path != '/'); + + err = tfs_namex(&dirat, &leaf, 0); + if (err) + return err; + + err = tfs_creat(dirat, leaf, strlen(leaf)); + return err; +} + +int fs_server_unlink(const char *path) +{ + struct inode *dirat = NULL; + const char *leaf = path; + int err; + + BUG_ON(!path); + BUG_ON(*path != '/'); + + err = tfs_namex(&dirat, &leaf, 0); + if (err) + return err; + + err = tfs_remove(dirat, leaf, strlen(leaf)); + return err; +} + +int fs_server_rmdir(const char *path) +{ + struct inode *dirat = NULL; + const char *leaf = path; + int err; + + BUG_ON(!path); + BUG_ON(*path != '/'); + + dirat = tfs_open_path(path); + if (dirat != NULL && dirat->type != FS_DIR) { + return -ENOTDIR; + } + if (dirat != NULL && !htable_empty(&dirat->dentries)) { + return -ENOTEMPTY; + } + + err = tfs_namex(&dirat, &leaf, 0); + if (err) + return err; + + err = tfs_remove(dirat, leaf, strlen(leaf)); + return err; +} + +/* use absolute path, offset and count to read directly */ +int fs_server_read(const char *path, off_t offset, void *buf, size_t count) +{ + struct inode *inode; + int ret = -ENOENT; + + BUG_ON(!path); + BUG_ON(*path != '/'); + + inode = tfs_open_path(path); + if (inode) { + ret = tfs_file_read(inode, offset, buf, count); + } + + return ret; +} + +/* use absolute path, offset and count to write directly */ +int fs_server_write(const char *path, off_t offset, const void *buf, + size_t count) +{ + struct inode *inode; + int ret = -ENOENT; + + BUG_ON(!path); + BUG_ON(*path != '/'); + + inode = tfs_open_path(path); + if (inode) { + ret = tfs_file_write(inode, offset, buf, count); + } + + return ret; +} + +ssize_t fs_server_get_size(const char *path) +{ + struct inode *inode; + int ret = -ENOENT; + + BUG_ON(!path); + BUG_ON(*path != '/'); + + inode = tfs_open_path(path); + if (inode) + ret = inode->size; + return ret; +} + +/* Scan several dirent structures from the directory referred to by the path + * into the buffer pointed by dirp. The argument count specifies the size of + * that buffer. + * + * RETURN VALUE + * On success, the number of items is returned. On end of directory, 0 is + * returned. On error, -errno is returned. + * + * The caller should call this function over and over again until it returns 0 + * */ +int fs_server_scan(const char *path, unsigned int start, void *buf, + unsigned int count) +{ + struct inode *inode; + + BUG_ON(!path); + BUG_ON(*path != '/'); + + inode = tfs_open_path(path); + if (inode) { + if (inode->type == FS_DIR) + return tfs_scan(inode, start, buf, buf + count); + return -ENOTDIR; + } + return -ENOENT; +}