init commit of lab4_3

lab4_3_hardlink
Zhiyuan Shao 2 years ago
parent 3e29933551
commit 38183534a3

@ -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)

@ -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;

@ -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);

@ -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);
}

@ -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

@ -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

@ -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);

@ -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);
}

@ -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);

@ -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.
//

@ -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))

@ -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;
}

@ -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;
}

@ -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
//

@ -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

Loading…
Cancel
Save