finish exec5.1-4

This commit is contained in:
KAAAsS 2021-05-23 22:21:04 +08:00
parent 1be2b1fcdf
commit 9862d87027
Signed by: KAAAsS
GPG Key ID: D56625F3E671882F
2 changed files with 703 additions and 0 deletions

View File

@ -0,0 +1,538 @@
#include "tmpfs.h"
#include <defs.h>
#include <syscall.h>
#include <string.h>
#include <cpio.h>
#include <launcher.h>
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;
}

View File

@ -0,0 +1,165 @@
#include "tmpfs_server.h"
#include <syscall.h>
// 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;
}