From 38183534a3a0c2027ad2ea04e63169292790c1eb Mon Sep 17 00:00:00 2001 From: Zhiyuan Shao Date: Mon, 8 May 2023 15:41:02 +0800 Subject: [PATCH] init commit of lab4_3 --- Makefile | 2 +- kernel/hostfs.c | 13 ++++ kernel/hostfs.h | 2 + kernel/proc_file.c | 14 +++++ kernel/proc_file.h | 3 + kernel/rfs.c | 141 +++++++++++++++++++++++++++++++++++++++++++ kernel/rfs.h | 2 + kernel/syscall.c | 22 +++++++ kernel/syscall.h | 3 + kernel/vfs.c | 99 ++++++++++++++++++++++++++++++ kernel/vfs.h | 8 +++ user/app_directory.c | 60 ------------------ user/app_hardlink.c | 94 +++++++++++++++++++++++++++++ user/user_lib.c | 14 +++++ user/user_lib.h | 5 ++ 15 files changed, 421 insertions(+), 61 deletions(-) delete mode 100644 user/app_directory.c create mode 100644 user/app_hardlink.c diff --git a/Makefile b/Makefile index 11faa01..980e035 100644 --- a/Makefile +++ b/Makefile @@ -68,7 +68,7 @@ USER_CPPS := user/*.c USER_CPPS := $(wildcard $(USER_CPPS)) USER_OBJS := $(addprefix $(OBJ_DIR)/, $(patsubst %.c,%.o,$(USER_CPPS))) -USER_TARGET := $(OBJ_DIR)/app_directory +USER_TARGET := $(OBJ_DIR)/app_hardlink #------------------------targets------------------------ $(OBJ_DIR): @-mkdir -p $(OBJ_DIR) diff --git a/kernel/hostfs.c b/kernel/hostfs.c index 62fc763..03348d8 100644 --- a/kernel/hostfs.c +++ b/kernel/hostfs.c @@ -24,6 +24,8 @@ const struct vinode_ops hostfs_i_ops = { .viop_write_back_vinode = hostfs_write_back_vinode, // not implemented + .viop_link = hostfs_link, + .viop_unlink = hostfs_unlink, .viop_readdir = hostfs_readdir, .viop_mkdir = hostfs_mkdir, }; @@ -233,6 +235,17 @@ int hostfs_lseek(struct vinode *f_inode, ssize_t new_offset, int whence, return -1; } +int hostfs_link(struct vinode *parent, struct dentry *sub_dentry, + struct vinode *link_node) { + panic("hostfs_link not implemented!\n"); + return -1; +} + +int hostfs_unlink(struct vinode *parent, struct dentry *sub_dentry, struct vinode *unlink_node) { + panic("hostfs_unlink not implemented!\n"); + return -1; +} + int hostfs_readdir(struct vinode *dir_vinode, struct dir *dir, int *offset) { panic("hostfs_readdir not implemented!\n"); return -1; diff --git a/kernel/hostfs.h b/kernel/hostfs.h index c788463..9909bd2 100644 --- a/kernel/hostfs.h +++ b/kernel/hostfs.h @@ -28,6 +28,8 @@ struct vinode *hostfs_lookup(struct vinode *parent, struct dentry *sub_dentry); struct vinode *hostfs_create(struct vinode *parent, struct dentry *sub_dentry); int hostfs_lseek(struct vinode *f_inode, ssize_t new_offset, int whence, int *offset); +int hostfs_link(struct vinode *parent, struct dentry *sub_dentry, struct vinode *link_node); +int hostfs_unlink(struct vinode *parent, struct dentry *sub_dentry, struct vinode *unlink_node); int hostfs_hook_open(struct vinode *f_inode, struct dentry *f_dentry); int hostfs_hook_close(struct vinode *f_inode, struct dentry *dentry); int hostfs_readdir(struct vinode *dir_vinode, struct dir *dir, int *offset); diff --git a/kernel/proc_file.c b/kernel/proc_file.c index 7575e9e..3d99cbb 100644 --- a/kernel/proc_file.c +++ b/kernel/proc_file.c @@ -207,3 +207,17 @@ int do_closedir(int fd) { struct file *pfile = get_opened_file(fd); return vfs_closedir(pfile); } + +// +// create hard link to a file +// +int do_link(char *oldpath, char *newpath) { + return vfs_link(oldpath, newpath); +} + +// +// remove a hard link to a file +// +int do_unlink(char *path) { + return vfs_unlink(path); +} diff --git a/kernel/proc_file.h b/kernel/proc_file.h index e233d0c..2cfb051 100644 --- a/kernel/proc_file.h +++ b/kernel/proc_file.h @@ -21,6 +21,9 @@ int do_readdir(int fd, struct dir *dir); int do_mkdir(char *pathname); int do_closedir(int fd); +int do_link(char *oldpath, char *newpath); +int do_unlink(char *path); + void fs_init(void); // data structure that manages all openned files in a PCB diff --git a/kernel/rfs.c b/kernel/rfs.c index b1f63db..e32cefb 100644 --- a/kernel/rfs.c +++ b/kernel/rfs.c @@ -25,6 +25,8 @@ const struct vinode_ops rfs_i_ops = { .viop_create = rfs_create, .viop_lseek = rfs_lseek, .viop_disk_stat = rfs_disk_stat, + .viop_link = rfs_link, + .viop_unlink = rfs_unlink, .viop_lookup = rfs_lookup, .viop_readdir = rfs_readdir, @@ -571,6 +573,145 @@ int rfs_disk_stat(struct vinode *vinode, struct istat *istat) { return 0; } +// +// create a hard link under a direntry "parent" for an existing file of "link_node" +// +int rfs_link(struct vinode *parent, struct dentry *sub_dentry, struct vinode *link_node) { + // TODO (lab4_3): we now need to establish a hard link to an existing file whose vfs + // inode is "link_node". To do that, we need first to know the name of the new (link) + // file, and then, we need to increase the link count of the existing file. Lastly, + // we need to make the changes persistent to disk. To know the name of the new (link) + // file, you need to stuty the structure of dentry, that contains the name member; + // To incease the link count of the existing file, you need to study the structure of + // vfs inode, since it contains the inode information of the existing file. + // + // hint: to accomplish this experiment, you need to: + // 1) increase the link count of the file to be hard-linked; + // 2) append the new (link) file as a dentry to its parent directory; you can use + // rfs_add_direntry here. + // 3) persistent the changes to disk. you can use rfs_write_back_vinode here. + // + panic("You need to implement the code for creating a hard link in lab4_3.\n" ); +} + +// +// remove a hard link with "name" under a direntry "parent" +// +int rfs_unlink(struct vinode *parent, struct dentry *sub_dentry, struct vinode *unlink_vinode) { + struct rfs_device *rdev = rfs_device_list[parent->sb->s_dev->dev_id]; + + // ** find the direntry in the directory file + int total_direntrys = parent->size / sizeof(struct rfs_direntry); + int one_block_direntrys = RFS_BLKSIZE / sizeof(struct rfs_direntry); + + struct rfs_direntry *p_direntry = NULL; + int delete_index; + for (delete_index = 0; delete_index < total_direntrys; ++delete_index) { + // read in the disk block at boundary + if (delete_index % one_block_direntrys == 0) { + rfs_r1block(rdev, parent->addrs[delete_index / one_block_direntrys]); + p_direntry = (struct rfs_direntry *)rdev->iobuffer; + } + if (strcmp(p_direntry->name, sub_dentry->name) == 0) { // found + break; + } + ++p_direntry; + } + + int inum = p_direntry->inum; + + if (delete_index == total_direntrys) { + sprint("unlink: file %s not found.\n", sub_dentry->name); + return -1; + } + + // ** read the disk inode of the file to be unlinked + struct rfs_dinode *unlink_dinode = rfs_read_dinode(rdev, inum); + + // if this assertion fails, it indicates that the previous modification to nlinks + // was not written back to disk, which is not allowed + assert(unlink_vinode->nlinks == unlink_dinode->nlinks); + + // ** decrease vinode nlinks by 1 + unlink_vinode->nlinks--; + + // ** update disk inode nlinks + unlink_dinode->nlinks = unlink_vinode->nlinks; + + // ** if nlinks == 0, free the disk inode and disk blocks + if (unlink_dinode->nlinks == 0) { + // free disk blocks + for (int i = 0; i < unlink_dinode->blocks; ++i) { + rfs_free_block(parent->sb, unlink_dinode->addrs[i]); + } + // free disk inode + unlink_dinode->type = R_FREE; + } + // ** write the disk inode back to disk + rfs_write_dinode(rdev, unlink_dinode, inum); + free_page(unlink_dinode); + + // ** remove the direntry from the directory + + // handle the first block + int delete_block_index = delete_index / one_block_direntrys; + rfs_r1block(rdev, parent->addrs[delete_block_index]); + + int offset = delete_index % one_block_direntrys; + memmove(rdev->iobuffer + offset * sizeof(struct rfs_direntry), + rdev->iobuffer + (offset + 1) * sizeof(struct rfs_direntry), + (one_block_direntrys - offset - 1) * sizeof(struct rfs_direntry)); + + struct rfs_direntry *previous_block = (struct rfs_direntry *)alloc_page(); + memcpy(previous_block, rdev->iobuffer, RFS_BLKSIZE); + + for (int i = delete_block_index + 1; i < parent->blocks; i++) { + rfs_r1block(rdev, parent->addrs[i]); + struct rfs_direntry *this_block = (struct rfs_direntry *)alloc_page(); + memcpy(this_block, rdev->iobuffer, RFS_BLKSIZE); + + // copy the first direntry of this block to the last direntry + // of previous block + memcpy(previous_block + one_block_direntrys - 1, rdev->iobuffer, + sizeof(struct rfs_direntry)); + + // move the direntry in this block forward by one + memmove(this_block, this_block + 1, + (one_block_direntrys - 1) * sizeof(struct rfs_direntry)); + + // write the previous block back to disk + memcpy(rdev->iobuffer, previous_block, RFS_BLKSIZE); + rfs_w1block(rdev, parent->addrs[i - 1]); + + // update previous block + free_page(previous_block); + previous_block = this_block; + } + + // write the last block back to disk + memcpy(rdev->iobuffer, previous_block, RFS_BLKSIZE); + rfs_w1block(rdev, parent->addrs[parent->blocks - 1]); + free_page(previous_block); + + // if the last block is empty, free it + total_direntrys--; + if (total_direntrys % one_block_direntrys == 0 && parent->blocks > 1) { + rfs_free_block(parent->sb, parent->addrs[parent->blocks - 1]); + parent->blocks--; + } + + // ** update the directory file's size + parent->size -= sizeof(struct rfs_direntry); + + // ** write the directory file's inode back to disk + if (rfs_write_back_vinode(parent) != 0) { + sprint("rfs_unlink: rfs_write_back_vinode failed"); + return -1; + } + + return 0; +} + // // when a directory is opened, the contents of the directory file are read // into the memory for directory read operations diff --git a/kernel/rfs.h b/kernel/rfs.h index 5e8971b..923b20c 100644 --- a/kernel/rfs.h +++ b/kernel/rfs.h @@ -79,6 +79,8 @@ struct vinode *rfs_lookup(struct vinode *parent, struct dentry *sub_dentry); struct vinode *rfs_create(struct vinode *parent, struct dentry *sub_dentry); int rfs_lseek(struct vinode *f_inode, ssize_t new_offset, int whence, int *offset); int rfs_disk_stat(struct vinode *vinode, struct istat *istat); +int rfs_link(struct vinode *parent, struct dentry *sub_dentry, struct vinode *link_node); +int rfs_unlink(struct vinode *parent, struct dentry *sub_dentry, struct vinode *unlink_vinode); int rfs_hook_opendir(struct vinode *dir_vinode, struct dentry *dentry); int rfs_hook_closedir(struct vinode *dir_vinode, struct dentry *dentry); diff --git a/kernel/syscall.c b/kernel/syscall.c index 0efcade..4ef7a33 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -196,6 +196,23 @@ ssize_t sys_user_closedir(int fd){ return do_closedir(fd); } +// +// lib call to link +// +ssize_t sys_user_link(char * vfn1, char * vfn2){ + char * pfn1 = (char*)user_va_to_pa((pagetable_t)(current->pagetable), (void*)vfn1); + char * pfn2 = (char*)user_va_to_pa((pagetable_t)(current->pagetable), (void*)vfn2); + return do_link(pfn1, pfn2); +} + +// +// lib call to unlink +// +ssize_t sys_user_unlink(char * vfn){ + char * pfn = (char*)user_va_to_pa((pagetable_t)(current->pagetable), (void*)vfn); + return do_unlink(pfn); +} + // // [a0]: the syscall number; [a1] ... [a7]: arguments to the syscalls. // returns the code of success, (e.g., 0 means success, fail for otherwise) @@ -239,6 +256,11 @@ long do_syscall(long a0, long a1, long a2, long a3, long a4, long a5, long a6, l return sys_user_mkdir((char *)a1); case SYS_user_closedir: return sys_user_closedir(a1); + // added @lab4_3 + case SYS_user_link: + return sys_user_link((char *)a1, (char *)a2); + case SYS_user_unlink: + return sys_user_unlink((char *)a1); default: panic("Unknown syscall %ld \n", a0); } diff --git a/kernel/syscall.h b/kernel/syscall.h index 886f58e..0486c81 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -27,6 +27,9 @@ #define SYS_user_readdir (SYS_user_base + 25) #define SYS_user_mkdir (SYS_user_base + 26) #define SYS_user_closedir (SYS_user_base + 27) +// added @lab4_3 +#define SYS_user_link (SYS_user_base + 28) +#define SYS_user_unlink (SYS_user_base + 29) long do_syscall(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7); diff --git a/kernel/vfs.c b/kernel/vfs.c index 2088fb0..c2d1c9d 100644 --- a/kernel/vfs.c +++ b/kernel/vfs.c @@ -255,6 +255,105 @@ int vfs_disk_stat(struct file *file, struct istat *istat) { return viop_disk_stat(file->f_dentry->dentry_inode, istat); } +// +// make hard link to the file specified by "oldpath" with the name "newpath" +// return: -1 on failure, 0 on success. +// +int vfs_link(const char *oldpath, const char *newpath) { + struct dentry *parent = vfs_root_dentry; + char miss_name[MAX_PATH_LEN]; + + // lookup oldpath + struct dentry *old_file_dentry = + lookup_final_dentry(oldpath, &parent, miss_name); + if (!old_file_dentry) { + sprint("vfs_link: cannot find the file!\n"); + return -1; + } + + if (old_file_dentry->dentry_inode->type != FILE_I) { + sprint("vfs_link: cannot link a directory!\n"); + return -1; + } + + parent = vfs_root_dentry; + // lookup the newpath + // note that parent is changed to be the last directory entry to be accessed + struct dentry *new_file_dentry = + lookup_final_dentry(newpath, &parent, miss_name); + if (new_file_dentry) { + sprint("vfs_link: the new file already exists!\n"); + return -1; + } + + char basename[MAX_PATH_LEN]; + get_base_name(newpath, basename); + if (strcmp(miss_name, basename) != 0) { + sprint("vfs_link: cannot create file in a non-exist directory!\n"); + return -1; + } + + // do the real hard-link + new_file_dentry = alloc_vfs_dentry(basename, old_file_dentry->dentry_inode, parent); + int err = + viop_link(parent->dentry_inode, new_file_dentry, old_file_dentry->dentry_inode); + if (err) return -1; + + // make a new dentry for the new link + hash_put_dentry(new_file_dentry); + + return 0; +} + +// +// unlink (delete) a file specified by "path". +// return: -1 on failure, 0 on success. +// +int vfs_unlink(const char *path) { + struct dentry *parent = vfs_root_dentry; + char miss_name[MAX_PATH_LEN]; + + // lookup the file, find its parent direntry + struct dentry *file_dentry = lookup_final_dentry(path, &parent, miss_name); + if (!file_dentry) { + sprint("vfs_unlink: cannot find the file!\n"); + return -1; + } + + if (file_dentry->dentry_inode->type != FILE_I) { + sprint("vfs_unlink: cannot unlink a directory!\n"); + return -1; + } + + if (file_dentry->d_ref > 0) { + sprint("vfs_unlink: the file is still opened!\n"); + return -1; + } + + // do the real unlink + struct vinode *unlinked_vinode = file_dentry->dentry_inode; + int err = viop_unlink(parent->dentry_inode, file_dentry, unlinked_vinode); + if (err) return -1; + + // remove the dentry from the hash table + hash_erase_dentry(file_dentry); + free_vfs_dentry(file_dentry); + unlinked_vinode->ref--; + + // if this inode has been removed from disk + if (unlinked_vinode->nlinks == 0) { + // no one will remember a dead inode + assert(unlinked_vinode->ref == 0); + + // we don't write back the inode, because it has disappeared from the disk + hash_erase_vinode(unlinked_vinode); + free_page(unlinked_vinode); // free the vinode + } + + + return 0; +} + // // close a file at vfs layer. // diff --git a/kernel/vfs.h b/kernel/vfs.h index 6f030d5..12f4d28 100644 --- a/kernel/vfs.h +++ b/kernel/vfs.h @@ -28,6 +28,8 @@ ssize_t vfs_write(struct file *file, const char *buf, size_t count); ssize_t vfs_lseek(struct file *file, ssize_t offset, int whence); int vfs_stat(struct file *file, struct istat *istat); int vfs_disk_stat(struct file *file, struct istat *istat); +int vfs_link(const char *oldpath, const char *newpath); +int vfs_unlink(const char *path); int vfs_close(struct file *file); // directory interfaces @@ -140,6 +142,10 @@ struct vinode_ops { struct vinode *(*viop_create)(struct vinode *parent, struct dentry *sub_dentry); int (*viop_lseek)(struct vinode *node, ssize_t new_off, int whence, int *off); int (*viop_disk_stat)(struct vinode *node, struct istat *istat); + int (*viop_link)(struct vinode *parent, struct dentry *sub_dentry, + struct vinode *link_node); + int (*viop_unlink)(struct vinode *parent, struct dentry *sub_dentry, + struct vinode *unlink_node); struct vinode *(*viop_lookup)(struct vinode *parent, struct dentry *sub_dentry); @@ -171,6 +177,8 @@ struct vinode_ops { #define viop_create(node, name) (node->i_ops->viop_create(node, name)) #define viop_lseek(node, new_off, whence, off) (node->i_ops->viop_lseek(node, new_off, whence, off)) #define viop_disk_stat(node, istat) (node->i_ops->viop_disk_stat(node, istat)) +#define viop_link(node, name, link_node) (node->i_ops->viop_link(node, name, link_node)) +#define viop_unlink(node, name, unlink_node) (node->i_ops->viop_unlink(node, name, unlink_node)) #define viop_lookup(parent, sub_dentry) (parent->i_ops->viop_lookup(parent, sub_dentry)) #define viop_readdir(dir_vinode, dir, offset) (dir_vinode->i_ops->viop_readdir(dir_vinode, dir, offset)) #define viop_mkdir(dir, sub_dentry) (dir->i_ops->viop_mkdir(dir, sub_dentry)) diff --git a/user/app_directory.c b/user/app_directory.c deleted file mode 100644 index 8636756..0000000 --- a/user/app_directory.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "user_lib.h" -#include "util/string.h" -#include "util/types.h" - -void ls(char *path) { - int dir_fd = opendir_u(path); - printu("------------------------------\n"); - printu("ls \"%s\":\n", path); - printu("[name] [inode_num]\n"); - struct dir dir; - int width = 20; - while(readdir_u(dir_fd, &dir) == 0) { - // we do not have %ms :( - char name[width + 1]; - memset(name, ' ', width + 1); - name[width] = '\0'; - if (strlen(dir.name) < width) { - strcpy(name, dir.name); - name[strlen(dir.name)] = ' '; - printu("%s %d\n", name, dir.inum); - } - else - printu("%s %d\n", dir.name, dir.inum); - } - printu("------------------------------\n"); - closedir_u(dir_fd); -} - -int main(int argc, char *argv[]) { - char str[] = "hello world"; - int fd; - - printu("\n======== Test 1: open and read dir ========\n"); - - ls("/RAMDISK0"); - - printu("\n======== Test 2: make dir ========\n"); - - mkdir_u("/RAMDISK0/sub_dir"); - printu("make: /RAMDISK0/sub_dir\n"); - - ls("/RAMDISK0"); - - // try to write a file in the new dir - printu("write: /RAMDISK0/sub_dir/ramfile\n"); - - fd = open("/RAMDISK0/sub_dir/ramfile", O_RDWR | O_CREAT); - printu("file descriptor fd: %d\n", fd); - - write_u(fd, str, strlen(str)); - printu("write content: \n%s\n", str); - - close(fd); - - ls("/RAMDISK0/sub_dir"); - - printu("\nAll tests passed!\n\n"); - exit(0); - return 0; -} diff --git a/user/app_hardlink.c b/user/app_hardlink.c new file mode 100644 index 0000000..7e209e5 --- /dev/null +++ b/user/app_hardlink.c @@ -0,0 +1,94 @@ +#include "user_lib.h" +#include "util/string.h" +#include "util/types.h" + +void ls(char *path) { + int dir_fd = opendir_u(path); + printu("------------------------------\n"); + printu("ls \"%s\":\n", path); + printu("[name] [inode_num]\n"); + struct dir dir; + int width = 20; + while(readdir_u(dir_fd, &dir) == 0) { + // we do not have %ms :( + char name[width + 1]; + memset(name, ' ', width + 1); + name[width] = '\0'; + if (strlen(dir.name) < width) { + strcpy(name, dir.name); + name[strlen(dir.name)] = ' '; + printu("%s %d\n", name, dir.inum); + } + else + printu("%s %d\n", dir.name, dir.inum); + } + printu("------------------------------\n"); + closedir_u(dir_fd); +} + +int main(int argc, char *argv[]) { + int MAXBUF = 512; + char str[] = "hello world"; + char buf[MAXBUF]; + int fd1, fd2; + + printu("\n======== establish the file ========\n"); + + fd1 = open("/RAMDISK0/ramfile", O_RDWR | O_CREAT); + printu("create file: /RAMDISK0/ramfile\n"); + close(fd1); + + printu("\n======== Test 1: hard link ========\n"); + + link_u("/RAMDISK0/ramfile", "/RAMDISK0/ramfile2"); + printu("create hard link: /RAMDISK0/ramfile2 -> /RAMDISK0/ramfile\n"); + + fd1 = open("/RAMDISK0/ramfile", O_RDWR); + fd2 = open("/RAMDISK0/ramfile2", O_RDWR); + + printu("file descriptor fd1 (ramfile): %d\n", fd1); + printu("file descriptor fd2 (ramfile2): %d\n", fd2); + + // check the number of hard links to ramfile on disk + struct istat st; + disk_stat_u(fd1, &st); + printu("ramfile hard links: %d\n", st.st_nlinks); + if (st.st_nlinks != 2) { + printu("ERROR: the number of hard links to ramfile should be 2, but it is %d\n", + st.st_nlinks); + exit(-1); + } + + write_u(fd1, str, strlen(str)); + printu("/RAMDISK0/ramfile write content: \n%s\n", str); + + read_u(fd2, buf, MAXBUF); + printu("/RAMDISK0/ramfile2 read content: \n%s\n", buf); + + close(fd1); + close(fd2); + + printu("\n======== Test 2: unlink ========\n"); + + ls("/RAMDISK0"); + + unlink_u("/RAMDISK0/ramfile"); + printu("unlink: /RAMDISK0/ramfile\n"); + + ls("/RAMDISK0"); + + // check the number of hard links to ramfile2 on disk + fd2 = open("/RAMDISK0/ramfile2", O_RDWR); + disk_stat_u(fd2, &st); + printu("ramfile2 hard links: %d\n", st.st_nlinks); + if (st.st_nlinks != 1) { + printu("ERROR: the number of hard links to ramfile should be 1, but it is %d\n", + st.st_nlinks); + exit(-1); + } + close(fd2); + + printu("\nAll tests passed!\n\n"); + exit(0); + return 0; +} diff --git a/user/user_lib.c b/user/user_lib.c index ca9ba1b..1c4f6d1 100644 --- a/user/user_lib.c +++ b/user/user_lib.c @@ -147,6 +147,20 @@ int closedir_u(int fd) { return do_user_call(SYS_user_closedir, fd, 0, 0, 0, 0, 0, 0); } +// +// lib call to link +// +int link_u(const char *fn1, const char *fn2){ + return do_user_call(SYS_user_link, (uint64)fn1, (uint64)fn2, 0, 0, 0, 0, 0); +} + +// +// lib call to unlink +// +int unlink_u(const char *fn){ + return do_user_call(SYS_user_unlink, (uint64)fn, 0, 0, 0, 0, 0, 0); +} + // // lib call to close // diff --git a/user/user_lib.h b/user/user_lib.h index cee321f..95a686d 100644 --- a/user/user_lib.h +++ b/user/user_lib.h @@ -29,4 +29,9 @@ int readdir_u(int fd, struct dir *dir); int mkdir_u(const char *pathname); int closedir_u(int fd); +// added @ lab4_3 +int link_u(const char *fn1, const char *fn2); +int unlink_u(const char *fn); + + #endif