parent
							
								
									448d994bba
								
							
						
					
					
						commit
						bbcac5cf62
					
				| @ -0,0 +1,2 @@ | |||||||
|  | This is an apple.  | ||||||
|  | Apples are good for our health.  | ||||||
| @ -0,0 +1,273 @@ | |||||||
|  | /*
 | ||||||
|  |  * Interface functions between VFS and host-fs. added @lab4_1. | ||||||
|  |  */ | ||||||
|  | #include "hostfs.h" | ||||||
|  | 
 | ||||||
|  | #include "pmm.h" | ||||||
|  | #include "spike_interface/spike_file.h" | ||||||
|  | #include "spike_interface/spike_utils.h" | ||||||
|  | #include "util/string.h" | ||||||
|  | #include "util/types.h" | ||||||
|  | #include "vfs.h" | ||||||
|  | 
 | ||||||
|  | /**** host-fs vinode interface ****/ | ||||||
|  | const struct vinode_ops hostfs_i_ops = { | ||||||
|  |     .viop_read = hostfs_read, | ||||||
|  |     .viop_write = hostfs_write, | ||||||
|  |     .viop_create = hostfs_create, | ||||||
|  |     .viop_lseek = hostfs_lseek, | ||||||
|  |     .viop_lookup = hostfs_lookup, | ||||||
|  | 
 | ||||||
|  |     .viop_hook_open = hostfs_hook_open, | ||||||
|  |     .viop_hook_close = hostfs_hook_close, | ||||||
|  | 
 | ||||||
|  |     .viop_write_back_vinode = hostfs_write_back_vinode, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**** hostfs utility functions ****/ | ||||||
|  | //
 | ||||||
|  | // append hostfs to the fs list.
 | ||||||
|  | //
 | ||||||
|  | int register_hostfs() { | ||||||
|  |   struct file_system_type *fs_type = (struct file_system_type *)alloc_page(); | ||||||
|  |   fs_type->type_num = HOSTFS_TYPE; | ||||||
|  |   fs_type->get_superblock = hostfs_get_superblock; | ||||||
|  | 
 | ||||||
|  |   for (int i = 0; i < MAX_SUPPORTED_FS; i++) { | ||||||
|  |     if (fs_list[i] == NULL) { | ||||||
|  |       fs_list[i] = fs_type; | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // append new device under "name" to vfs_dev_list.
 | ||||||
|  | //
 | ||||||
|  | struct device *init_host_device(char *name) { | ||||||
|  |   // find rfs in registered fs list
 | ||||||
|  |   struct file_system_type *fs_type = NULL; | ||||||
|  |   for (int i = 0; i < MAX_SUPPORTED_FS; i++) { | ||||||
|  |     if (fs_list[i] != NULL && fs_list[i]->type_num == HOSTFS_TYPE) { | ||||||
|  |       fs_type = fs_list[i]; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (!fs_type) | ||||||
|  |     panic("init_host_device: No HOSTFS file system found!\n"); | ||||||
|  | 
 | ||||||
|  |   // allocate a vfs device
 | ||||||
|  |   struct device *device = (struct device *)alloc_page(); | ||||||
|  |   // set the device name and index
 | ||||||
|  |   strcpy(device->dev_name, name); | ||||||
|  |   // we only support one host-fs device
 | ||||||
|  |   device->dev_id = 0; | ||||||
|  |   device->fs_type = fs_type; | ||||||
|  | 
 | ||||||
|  |   // add the device to the vfs device list
 | ||||||
|  |   for (int i = 0; i < MAX_VFS_DEV; i++) { | ||||||
|  |     if (vfs_dev_list[i] == NULL) { | ||||||
|  |       vfs_dev_list[i] = device; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return device; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // recursive call to assemble a path.
 | ||||||
|  | //
 | ||||||
|  | void path_backtrack(char *path, struct dentry *dentry) { | ||||||
|  |   if (dentry->parent == NULL) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   path_backtrack(path, dentry->parent); | ||||||
|  |   strcat(path, "/"); | ||||||
|  |   strcat(path, dentry->name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // obtain the absolute path for "dentry", from root to file.
 | ||||||
|  | //
 | ||||||
|  | void get_path_string(char *path, struct dentry *dentry) { | ||||||
|  |   strcpy(path, H_ROOT_DIR); | ||||||
|  |   path_backtrack(path, dentry); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // allocate a vfs inode for an host fs file.
 | ||||||
|  | //
 | ||||||
|  | struct vinode *hostfs_alloc_vinode(struct super_block *sb) { | ||||||
|  |   struct vinode *vinode = default_alloc_vinode(sb); | ||||||
|  |   vinode->inum = -1;  | ||||||
|  |   vinode->i_fs_info = NULL; | ||||||
|  |   vinode->i_ops = &hostfs_i_ops; | ||||||
|  |   return vinode; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int hostfs_write_back_vinode(struct vinode *vinode) { return 0; } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // populate the vfs inode of an hostfs file, according to its stats.
 | ||||||
|  | //
 | ||||||
|  | int hostfs_update_vinode(struct vinode *vinode) { | ||||||
|  |   spike_file_t *f = vinode->i_fs_info; | ||||||
|  |   if ((int64)f < 0) {  // is a direntry
 | ||||||
|  |     vinode->type = H_DIR; | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   struct stat stat; | ||||||
|  |   spike_file_stat(f, &stat); | ||||||
|  | 
 | ||||||
|  |   vinode->inum = stat.st_ino; | ||||||
|  |   vinode->size = stat.st_size; | ||||||
|  |   vinode->nlinks = stat.st_nlink; | ||||||
|  |   vinode->blocks = stat.st_blocks; | ||||||
|  | 
 | ||||||
|  |   if (S_ISDIR(stat.st_mode)) { | ||||||
|  |     vinode->type = H_DIR; | ||||||
|  |   } else if (S_ISREG(stat.st_mode)) { | ||||||
|  |     vinode->type = H_FILE; | ||||||
|  |   } else { | ||||||
|  |     sprint("hostfs_lookup:unknown file type!"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**** vfs-host-fs interface functions ****/ | ||||||
|  | //
 | ||||||
|  | // read a hostfs file.
 | ||||||
|  | //
 | ||||||
|  | ssize_t hostfs_read(struct vinode *f_inode, char *r_buf, ssize_t len, | ||||||
|  |                     int *offset) { | ||||||
|  |   spike_file_t *pf = (spike_file_t *)f_inode->i_fs_info; | ||||||
|  |   if (pf < 0) { | ||||||
|  |     sprint("hostfs_read: invalid file handle!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |   int read_len = spike_file_read(pf, r_buf, len); | ||||||
|  |   // obtain current offset
 | ||||||
|  |   *offset = spike_file_lseek(pf, 0, 1); | ||||||
|  |   return read_len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // write a hostfs file.
 | ||||||
|  | //
 | ||||||
|  | ssize_t hostfs_write(struct vinode *f_inode, const char *w_buf, ssize_t len, | ||||||
|  |                      int *offset) { | ||||||
|  |   spike_file_t *pf = (spike_file_t *)f_inode->i_fs_info; | ||||||
|  |   if (pf < 0) { | ||||||
|  |     sprint("hostfs_write: invalid file handle!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |   int write_len = spike_file_write(pf, w_buf, len); | ||||||
|  |   // obtain current offset
 | ||||||
|  |   *offset = spike_file_lseek(pf, 0, 1); | ||||||
|  |   return write_len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // lookup a hostfs file, and establish its vfs inode in PKE vfs.
 | ||||||
|  | //
 | ||||||
|  | struct vinode *hostfs_lookup(struct vinode *parent, struct dentry *sub_dentry) { | ||||||
|  |   // get complete path string
 | ||||||
|  |   char path[MAX_PATH_LEN]; | ||||||
|  |   get_path_string(path, sub_dentry); | ||||||
|  | 
 | ||||||
|  |   spike_file_t *f = spike_file_open(path, O_RDWR, 0); | ||||||
|  | 
 | ||||||
|  |   struct vinode *child_inode = hostfs_alloc_vinode(parent->sb); | ||||||
|  |   child_inode->i_fs_info = f; | ||||||
|  |   hostfs_update_vinode(child_inode); | ||||||
|  | 
 | ||||||
|  |   child_inode->ref = 0; | ||||||
|  |   return child_inode; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // creates a hostfs file, and establish its vfs inode. 
 | ||||||
|  | //
 | ||||||
|  | struct vinode *hostfs_create(struct vinode *parent, struct dentry *sub_dentry) { | ||||||
|  |   char path[MAX_PATH_LEN]; | ||||||
|  |   get_path_string(path, sub_dentry); | ||||||
|  | 
 | ||||||
|  |   spike_file_t *f = spike_file_open(path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); | ||||||
|  |   if ((int64)f < 0) { | ||||||
|  |     sprint("hostfs_create cannot create the given file.\n"); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   struct vinode *new_inode = hostfs_alloc_vinode(parent->sb); | ||||||
|  |   new_inode->i_fs_info = f; | ||||||
|  | 
 | ||||||
|  |   if (hostfs_update_vinode(new_inode) != 0) return NULL; | ||||||
|  | 
 | ||||||
|  |   new_inode->ref = 0; | ||||||
|  |   return new_inode; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // reposition read/write file offset
 | ||||||
|  | //
 | ||||||
|  | int hostfs_lseek(struct vinode *f_inode, ssize_t new_offset, int whence, | ||||||
|  |                   int *offset) { | ||||||
|  |   spike_file_t *f = (spike_file_t *)f_inode->i_fs_info; | ||||||
|  |   if (f < 0) { | ||||||
|  |     sprint("hostfs_lseek: invalid file handle!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   *offset = spike_file_lseek(f, new_offset, whence); | ||||||
|  |   if (*offset >= 0) | ||||||
|  |     return 0; | ||||||
|  |   return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**** vfs-hostfs hook interface functions ****/ | ||||||
|  | //
 | ||||||
|  | // open a hostfs file (after having its vfs inode).
 | ||||||
|  | //
 | ||||||
|  | int hostfs_hook_open(struct vinode *f_inode, struct dentry *f_dentry) { | ||||||
|  |   if (f_inode->i_fs_info != NULL) return 0; | ||||||
|  | 
 | ||||||
|  |   char path[MAX_PATH_LEN]; | ||||||
|  |   get_path_string(path, f_dentry); | ||||||
|  |   spike_file_t *f = spike_file_open(path, O_RDWR, 0); | ||||||
|  |   if ((int64)f < 0) { | ||||||
|  |     sprint("hostfs_hook_open cannot open the given file.\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   f_inode->i_fs_info = f; | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // close a hostfs file.
 | ||||||
|  | //
 | ||||||
|  | int hostfs_hook_close(struct vinode *f_inode, struct dentry *dentry) { | ||||||
|  |   spike_file_t *f = (spike_file_t *)f_inode->i_fs_info; | ||||||
|  |   spike_file_close(f); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**** vfs-hostfs file system type interface functions ****/ | ||||||
|  | struct super_block *hostfs_get_superblock(struct device *dev) { | ||||||
|  |   // set the data for the vfs super block
 | ||||||
|  |   struct super_block *sb = alloc_page(); | ||||||
|  |   sb->s_dev = dev; | ||||||
|  | 
 | ||||||
|  |   struct vinode *root_inode = hostfs_alloc_vinode(sb); | ||||||
|  |   root_inode->type = H_DIR; | ||||||
|  | 
 | ||||||
|  |   struct dentry *root_dentry = alloc_vfs_dentry("/", root_inode, NULL); | ||||||
|  |   sb->s_root = root_dentry; | ||||||
|  | 
 | ||||||
|  |   return sb; | ||||||
|  | } | ||||||
| @ -0,0 +1,37 @@ | |||||||
|  | #ifndef _HOSTFS_H_ | ||||||
|  | #define _HOSTFS_H_ | ||||||
|  | #include "vfs.h" | ||||||
|  | 
 | ||||||
|  | #define HOSTFS_TYPE 1 | ||||||
|  | 
 | ||||||
|  | // dinode type
 | ||||||
|  | #define H_FILE FILE_I | ||||||
|  | #define H_DIR DIR_I | ||||||
|  | 
 | ||||||
|  | // root directory
 | ||||||
|  | #define H_ROOT_DIR "./hostfs_root" | ||||||
|  | 
 | ||||||
|  | // hostfs utility functin declarations
 | ||||||
|  | int register_hostfs(); | ||||||
|  | struct device *init_host_device(char *name); | ||||||
|  | void get_path_string(char *path, struct dentry *dentry); | ||||||
|  | struct vinode *hostfs_alloc_vinode(struct super_block *sb); | ||||||
|  | int hostfs_write_back_vinode(struct vinode *vinode); | ||||||
|  | int hostfs_update_vinode(struct vinode *vinode); | ||||||
|  | 
 | ||||||
|  | // hostfs interface function declarations
 | ||||||
|  | ssize_t hostfs_read(struct vinode *f_inode, char *r_buf, ssize_t len, | ||||||
|  |                     int *offset); | ||||||
|  | ssize_t hostfs_write(struct vinode *f_inode, const char *w_buf, ssize_t len, | ||||||
|  |                      int *offset); | ||||||
|  | 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_hook_open(struct vinode *f_inode, struct dentry *f_dentry); | ||||||
|  | int hostfs_hook_close(struct vinode *f_inode, struct dentry *dentry); | ||||||
|  | struct super_block *hostfs_get_superblock(struct device *dev); | ||||||
|  | 
 | ||||||
|  | extern const struct vinode_ops hostfs_node_ops; | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| @ -0,0 +1,162 @@ | |||||||
|  | /*
 | ||||||
|  |  * Interface functions between file system and kernel/processes. added @lab4_1 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "proc_file.h" | ||||||
|  | 
 | ||||||
|  | #include "hostfs.h" | ||||||
|  | #include "pmm.h" | ||||||
|  | #include "process.h" | ||||||
|  | #include "ramdev.h" | ||||||
|  | #include "rfs.h" | ||||||
|  | #include "riscv.h" | ||||||
|  | #include "spike_interface/spike_file.h" | ||||||
|  | #include "spike_interface/spike_utils.h" | ||||||
|  | #include "util/functions.h" | ||||||
|  | #include "util/string.h" | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // initialize file system
 | ||||||
|  | //
 | ||||||
|  | void fs_init(void) { | ||||||
|  |   // initialize the vfs
 | ||||||
|  |   vfs_init(); | ||||||
|  | 
 | ||||||
|  |   // register hostfs and mount it as the root
 | ||||||
|  |   if( register_hostfs() < 0 ) panic( "fs_init: cannot register hostfs.\n" ); | ||||||
|  |   struct device *hostdev = init_host_device("HOSTDEV"); | ||||||
|  |   vfs_mount("HOSTDEV", MOUNT_AS_ROOT); | ||||||
|  | 
 | ||||||
|  |   // register and mount rfs
 | ||||||
|  |   if( register_rfs() < 0 ) panic( "fs_init: cannot register rfs.\n" ); | ||||||
|  |   struct device *ramdisk0 = init_rfs_device("RAMDISK0"); | ||||||
|  |   rfs_format_dev(ramdisk0); | ||||||
|  |   vfs_mount("RAMDISK0", MOUNT_DEFAULT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // initialize a proc_file_management data structure for a process.
 | ||||||
|  | // return the pointer to the page containing the data structure.
 | ||||||
|  | //
 | ||||||
|  | proc_file_management *init_proc_file_management(void) { | ||||||
|  |   proc_file_management *pfiles = (proc_file_management *)alloc_page(); | ||||||
|  |   pfiles->cwd = vfs_root_dentry; // by default, cwd is the root
 | ||||||
|  |   pfiles->nfiles = 0; | ||||||
|  | 
 | ||||||
|  |   for (int fd = 0; fd < MAX_FILES; ++fd) | ||||||
|  |     pfiles->opened_files[fd].status = FD_NONE; | ||||||
|  | 
 | ||||||
|  |   sprint("FS: created a file management struct for a process.\n"); | ||||||
|  |   return pfiles; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // reclaim the open-file management data structure of a process.
 | ||||||
|  | // note: this function is not used as PKE does not actually reclaim a process.
 | ||||||
|  | //
 | ||||||
|  | void reclaim_proc_file_management(proc_file_management *pfiles) { | ||||||
|  |   free_page(pfiles); | ||||||
|  |   return; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // get an opened file from proc->opened_file array.
 | ||||||
|  | // return: the pointer to the opened file structure.
 | ||||||
|  | //
 | ||||||
|  | struct file *get_opened_file(int fd) { | ||||||
|  |   struct file *pfile = NULL; | ||||||
|  | 
 | ||||||
|  |   // browse opened file list to locate the fd
 | ||||||
|  |   for (int i = 0; i < MAX_FILES; ++i) { | ||||||
|  |     pfile = &(current->pfiles->opened_files[i]);  // file entry
 | ||||||
|  |     if (i == fd) break; | ||||||
|  |   } | ||||||
|  |   if (pfile == NULL) panic("do_read: invalid fd!\n"); | ||||||
|  |   return pfile; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // open a file named as "pathname" with the permission of "flags".
 | ||||||
|  | // return: -1 on failure; non-zero file-descriptor on success.
 | ||||||
|  | //
 | ||||||
|  | int do_open(char *pathname, int flags) { | ||||||
|  |   struct file *opened_file = NULL; | ||||||
|  |   if ((opened_file = vfs_open(pathname, flags)) == NULL) return -1; | ||||||
|  | 
 | ||||||
|  |   int fd = 0; | ||||||
|  |   if (current->pfiles->nfiles >= MAX_FILES) { | ||||||
|  |     panic("do_open: no file entry for current process!\n"); | ||||||
|  |   } | ||||||
|  |   struct file *pfile; | ||||||
|  |   for (fd = 0; fd < MAX_FILES; ++fd) { | ||||||
|  |     pfile = &(current->pfiles->opened_files[fd]); | ||||||
|  |     if (pfile->status == FD_NONE) break; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // initialize this file structure
 | ||||||
|  |   memcpy(pfile, opened_file, sizeof(struct file)); | ||||||
|  | 
 | ||||||
|  |   ++current->pfiles->nfiles; | ||||||
|  |   return fd; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // read content of a file ("fd") into "buf" for "count".
 | ||||||
|  | // return: actual length of data read from the file.
 | ||||||
|  | //
 | ||||||
|  | int do_read(int fd, char *buf, uint64 count) { | ||||||
|  |   struct file *pfile = get_opened_file(fd); | ||||||
|  | 
 | ||||||
|  |   if (pfile->readable == 0) panic("do_read: no readable file!\n"); | ||||||
|  | 
 | ||||||
|  |   char buffer[count + 1]; | ||||||
|  |   int len = vfs_read(pfile, buffer, count); | ||||||
|  |   buffer[count] = '\0'; | ||||||
|  |   strcpy(buf, buffer); | ||||||
|  |   return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // write content ("buf") whose length is "count" to a file "fd".
 | ||||||
|  | // return: actual length of data written to the file.
 | ||||||
|  | //
 | ||||||
|  | int do_write(int fd, char *buf, uint64 count) { | ||||||
|  |   struct file *pfile = get_opened_file(fd); | ||||||
|  | 
 | ||||||
|  |   if (pfile->writable == 0) panic("do_write: cannot write file!\n"); | ||||||
|  | 
 | ||||||
|  |   int len = vfs_write(pfile, buf, count); | ||||||
|  |   return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // reposition the file offset
 | ||||||
|  | //
 | ||||||
|  | int do_lseek(int fd, int offset, int whence) { | ||||||
|  |   struct file *pfile = get_opened_file(fd); | ||||||
|  |   return vfs_lseek(pfile, offset, whence); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // read the vinode information
 | ||||||
|  | //
 | ||||||
|  | int do_stat(int fd, struct istat *istat) { | ||||||
|  |   struct file *pfile = get_opened_file(fd); | ||||||
|  |   return vfs_stat(pfile, istat); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // read the inode information on the disk
 | ||||||
|  | //
 | ||||||
|  | int do_disk_stat(int fd, struct istat *istat) { | ||||||
|  |   struct file *pfile = get_opened_file(fd); | ||||||
|  |   return vfs_disk_stat(pfile, istat); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // close a file
 | ||||||
|  | //
 | ||||||
|  | int do_close(int fd) { | ||||||
|  |   struct file *pfile = get_opened_file(fd); | ||||||
|  |   return vfs_close(pfile); | ||||||
|  | } | ||||||
| @ -0,0 +1,32 @@ | |||||||
|  | #ifndef _PROC_FILE_H_ | ||||||
|  | #define _PROC_FILE_H_ | ||||||
|  | 
 | ||||||
|  | #include "spike_interface/spike_file.h" | ||||||
|  | #include "util/types.h" | ||||||
|  | #include "vfs.h" | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // file operations
 | ||||||
|  | //
 | ||||||
|  | int do_open(char *pathname, int flags); | ||||||
|  | int do_read(int fd, char *buf, uint64 count); | ||||||
|  | int do_write(int fd, char *buf, uint64 count); | ||||||
|  | int do_lseek(int fd, int offset, int whence); | ||||||
|  | int do_stat(int fd, struct istat *istat); | ||||||
|  | int do_disk_stat(int fd, struct istat *istat); | ||||||
|  | int do_close(int fd); | ||||||
|  | 
 | ||||||
|  | void fs_init(void); | ||||||
|  | 
 | ||||||
|  | // data structure that manages all openned files in a PCB
 | ||||||
|  | typedef struct proc_file_management_t { | ||||||
|  |   struct dentry *cwd;  // vfs dentry of current working directory
 | ||||||
|  |   struct file opened_files[MAX_FILES];  // opened files array
 | ||||||
|  |   int nfiles;  // the number of files opened by a process
 | ||||||
|  | } proc_file_management; | ||||||
|  | 
 | ||||||
|  | proc_file_management *init_proc_file_management(void); | ||||||
|  | 
 | ||||||
|  | void reclaim_proc_file_management(proc_file_management *pfiles); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| @ -0,0 +1,107 @@ | |||||||
|  | /*
 | ||||||
|  |  * Utility functions operating the devices. support only RAM disk device. added @lab4_1. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "ramdev.h" | ||||||
|  | #include "vfs.h" | ||||||
|  | #include "pmm.h" | ||||||
|  | #include "riscv.h" | ||||||
|  | #include "util/types.h" | ||||||
|  | #include "util/string.h" | ||||||
|  | #include "spike_interface/spike_utils.h" | ||||||
|  | #include "rfs.h" | ||||||
|  | 
 | ||||||
|  | struct rfs_device *rfs_device_list[MAX_RAMDISK_COUNT]; | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // write the content stored in "buff" to the "blkno"^th block of disk.
 | ||||||
|  | //
 | ||||||
|  | int ramdisk_write(struct rfs_device *rfs_device, int blkno){ | ||||||
|  |   if ( blkno < 0 || blkno >= RAMDISK_BLOCK_COUNT ) | ||||||
|  |     panic("ramdisk_write: write block No %d out of range!\n", blkno); | ||||||
|  |   void * dst = (void *)((uint64)rfs_device->d_address + blkno * RAMDISK_BLOCK_SIZE); | ||||||
|  |   memcpy(dst, rfs_device->iobuffer, RAMDISK_BLOCK_SIZE); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // read the "blkno"^th block from the RAM disk and store its content into buffer.
 | ||||||
|  | //
 | ||||||
|  | int ramdisk_read(struct rfs_device *rfs_device, int blkno){ | ||||||
|  |   if ( blkno < 0 || blkno >= RAMDISK_BLOCK_COUNT ) | ||||||
|  |     panic("ramdisk_read: read block No out of range!\n"); | ||||||
|  |   void * src = (void *)((uint64)rfs_device->d_address + blkno * RAMDISK_BLOCK_SIZE); | ||||||
|  |   memcpy(rfs_device->iobuffer, src, RAMDISK_BLOCK_SIZE); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // alloc RAMDISK_BLOCK_COUNT continuous pages (blocks) for the RAM Disk
 | ||||||
|  | // setup an vfs node, initialize RAM disk device, and attach the device with the vfs node.
 | ||||||
|  | //
 | ||||||
|  | struct device *init_rfs_device(const char *dev_name) { | ||||||
|  |   // find rfs in registered fs list
 | ||||||
|  |   struct file_system_type *fs_type = NULL; | ||||||
|  |   for (int i = 0; i < MAX_SUPPORTED_FS; i++) { | ||||||
|  |     if (fs_list[i] != NULL && fs_list[i]->type_num == RFS_TYPE) { | ||||||
|  |       fs_type = fs_list[i]; | ||||||
|  |       break;  | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (!fs_type) { | ||||||
|  |     panic("No RFS file system found!\n"); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // alloc blocks for the RAM Disk
 | ||||||
|  |   void *curr_addr = NULL; | ||||||
|  |   void *last_addr = NULL; | ||||||
|  |   void *ramdisk_addr = NULL; | ||||||
|  |   for ( int i = 0; i < RAMDISK_BLOCK_COUNT; ++ i ){ | ||||||
|  |     last_addr = curr_addr; | ||||||
|  |     curr_addr = alloc_page(); | ||||||
|  |     if ( last_addr != NULL && last_addr - curr_addr != PGSIZE ){ | ||||||
|  |       panic("RAM Disk0: address is discontinuous!\n"); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   ramdisk_addr = curr_addr; | ||||||
|  | 
 | ||||||
|  |   // find a free rfs device
 | ||||||
|  |   struct rfs_device **rfs_device = NULL; | ||||||
|  |   int device_id = 0; | ||||||
|  |   for (int i = 0; i < MAX_RAMDISK_COUNT; i++) { | ||||||
|  |     if (rfs_device_list[i] == NULL) { | ||||||
|  |       rfs_device = &rfs_device_list[i]; | ||||||
|  |       device_id = i; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (!rfs_device) { | ||||||
|  |     panic("RAM Disk0: no free device!\n"); | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   *rfs_device = (struct rfs_device *)alloc_page(); | ||||||
|  |   (*rfs_device)->d_blocks = RAMDISK_BLOCK_COUNT; | ||||||
|  |   (*rfs_device)->d_blocksize = RAMDISK_BLOCK_SIZE; | ||||||
|  |   (*rfs_device)->d_write = ramdisk_write; | ||||||
|  |   (*rfs_device)->d_read = ramdisk_read; | ||||||
|  |   (*rfs_device)->d_address = ramdisk_addr; | ||||||
|  |   (*rfs_device)->iobuffer = alloc_page(); | ||||||
|  | 
 | ||||||
|  |   // allocate a vfs device
 | ||||||
|  |   struct device * device = (struct device *)alloc_page(); | ||||||
|  |   // set the device name and index
 | ||||||
|  |   strcpy(device->dev_name, dev_name); | ||||||
|  |   device->dev_id = device_id; | ||||||
|  |   device->fs_type = fs_type; | ||||||
|  | 
 | ||||||
|  |   // add the device to the vfs device list
 | ||||||
|  |   for(int i = 0; i < MAX_VFS_DEV; i++) { | ||||||
|  |     if (vfs_dev_list[i] == NULL) { | ||||||
|  |       vfs_dev_list[i] = device; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   sprint("%s: base address of %s is: %p\n",dev_name, dev_name, ramdisk_addr); | ||||||
|  |   return device; | ||||||
|  | } | ||||||
| @ -0,0 +1,32 @@ | |||||||
|  | #ifndef _RAMDEV_H_ | ||||||
|  | #define _RAMDEV_H_ | ||||||
|  | 
 | ||||||
|  | #include "riscv.h" | ||||||
|  | #include "util/types.h" | ||||||
|  | 
 | ||||||
|  | #define RAMDISK_BLOCK_COUNT 128 | ||||||
|  | #define RAMDISK_BLOCK_SIZE  PGSIZE | ||||||
|  | 
 | ||||||
|  | #define MAX_RAMDISK_COUNT 10 | ||||||
|  | 
 | ||||||
|  | #define RAMDISK_FREE 0 | ||||||
|  | #define RAMDISK_USED 1 | ||||||
|  | 
 | ||||||
|  | struct rfs_device { | ||||||
|  |   void *d_address;  // the ramdisk base address
 | ||||||
|  |   int d_blocks;     // the number of blocks of the device
 | ||||||
|  |   int d_blocksize;  // the blocksize (bytes) per block
 | ||||||
|  |   void *iobuffer;   // iobuffer for write/read
 | ||||||
|  |   int (*d_write)(struct rfs_device *rdev, int blkno);  // device write funtion
 | ||||||
|  |   int (*d_read)(struct rfs_device *rdev, int blkno); // device read funtion
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define dop_write(rdev, blkno) ((rdev)->d_write(rdev, blkno)) | ||||||
|  | #define dop_read(rdev, blkno)  ((rdev)->d_read(rdev, blkno)) | ||||||
|  | 
 | ||||||
|  | struct device *init_rfs_device(const char *dev_name); | ||||||
|  | struct rfs_device *alloc_rfs_device(void); | ||||||
|  | 
 | ||||||
|  | extern struct rfs_device *rfs_device_list[MAX_RAMDISK_COUNT]; | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| @ -0,0 +1,606 @@ | |||||||
|  | /*
 | ||||||
|  |  * RFS (Ramdisk File System) is a customized simple file system installed in the | ||||||
|  |  * RAM disk. added @lab4_1. | ||||||
|  |  * Layout of the file system: | ||||||
|  |  * | ||||||
|  |  * ******** RFS MEM LAYOUT (112 BLOCKS) **************** | ||||||
|  |  *   superblock  |  disk inodes  |  bitmap  |  free blocks  * | ||||||
|  |  *     1 block   |   10 blocks   |     1    |     100       * | ||||||
|  |  * ***************************************************** | ||||||
|  |  * | ||||||
|  |  * The disk layout of rfs is similar to the fs in xv6. | ||||||
|  |  */ | ||||||
|  | #include "rfs.h" | ||||||
|  | 
 | ||||||
|  | #include "pmm.h" | ||||||
|  | #include "ramdev.h" | ||||||
|  | #include "spike_interface/spike_utils.h" | ||||||
|  | #include "util/string.h" | ||||||
|  | #include "vfs.h" | ||||||
|  | 
 | ||||||
|  | /**** vinode inteface ****/ | ||||||
|  | const struct vinode_ops rfs_i_ops = { | ||||||
|  |     .viop_read = rfs_read, | ||||||
|  |     .viop_write = rfs_write, | ||||||
|  |     .viop_create = rfs_create, | ||||||
|  |     .viop_lseek = rfs_lseek, | ||||||
|  |     .viop_disk_stat = rfs_disk_stat, | ||||||
|  |     .viop_lookup = rfs_lookup, | ||||||
|  | 
 | ||||||
|  |     .viop_write_back_vinode = rfs_write_back_vinode, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**** rfs utility functions ****/ | ||||||
|  | //
 | ||||||
|  | // register rfs to the fs list supported by PKE.
 | ||||||
|  | //
 | ||||||
|  | int register_rfs() { | ||||||
|  |   struct file_system_type *fs_type = (struct file_system_type *)alloc_page(); | ||||||
|  |   fs_type->type_num = RFS_TYPE; | ||||||
|  |   fs_type->get_superblock = rfs_get_superblock; | ||||||
|  | 
 | ||||||
|  |   for (int i = 0; i < MAX_SUPPORTED_FS; i++) { | ||||||
|  |     if (fs_list[i] == NULL) { | ||||||
|  |       fs_list[i] = fs_type; | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // format "dev" with rfs. note the "dev" should be a ram disk device.
 | ||||||
|  | //
 | ||||||
|  | int rfs_format_dev(struct device *dev) { | ||||||
|  |   struct rfs_device *rdev = rfs_device_list[dev->dev_id]; | ||||||
|  | 
 | ||||||
|  |   // ** first, format the superblock
 | ||||||
|  |   // build a new superblock
 | ||||||
|  |   struct super_block *super = (struct super_block *)rdev->iobuffer; | ||||||
|  |   super->magic = RFS_MAGIC; | ||||||
|  |   super->size = | ||||||
|  |       1 + RFS_MAX_INODE_BLKNUM + 1 + RFS_MAX_INODE_BLKNUM * RFS_DIRECT_BLKNUM; | ||||||
|  |   // only direct index blocks
 | ||||||
|  |   super->nblocks = RFS_MAX_INODE_BLKNUM * RFS_DIRECT_BLKNUM; | ||||||
|  |   super->ninodes = RFS_BLKSIZE / RFS_INODESIZE * RFS_MAX_INODE_BLKNUM; | ||||||
|  | 
 | ||||||
|  |   // write the superblock to RAM Disk0
 | ||||||
|  |   if (rfs_w1block(rdev, RFS_BLK_OFFSET_SUPER) != 0)  // write to device
 | ||||||
|  |     panic("RFS: failed to write superblock!\n"); | ||||||
|  | 
 | ||||||
|  |   // ** second, set up the inodes and write them to RAM disk
 | ||||||
|  |   // build an empty inode disk block which has RFS_BLKSIZE/RFS_INODESIZE(=32)
 | ||||||
|  |   // disk inodes
 | ||||||
|  |   struct rfs_dinode *p_dinode = (struct rfs_dinode *)rdev->iobuffer; | ||||||
|  |   for (int i = 0; i < RFS_BLKSIZE / RFS_INODESIZE; ++i) { | ||||||
|  |     p_dinode->size = 0; | ||||||
|  |     p_dinode->type = R_FREE; | ||||||
|  |     p_dinode->nlinks = 0; | ||||||
|  |     p_dinode->blocks = 0; | ||||||
|  |     p_dinode = (struct rfs_dinode *)((char *)p_dinode + RFS_INODESIZE); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // write RFS_MAX_INODE_BLKNUM(=10) empty inode disk blocks to RAM Disk0
 | ||||||
|  |   for (int inode_block = 0; inode_block < RFS_MAX_INODE_BLKNUM; ++inode_block) { | ||||||
|  |     if (rfs_w1block(rdev, RFS_BLK_OFFSET_INODE + inode_block) != 0) | ||||||
|  |       panic("RFS: failed to initialize empty inodes!\n"); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // build root directory inode (ino = 0)
 | ||||||
|  |   struct rfs_dinode root_dinode; | ||||||
|  |   root_dinode.size = 0; | ||||||
|  |   root_dinode.type = R_DIR; | ||||||
|  |   root_dinode.nlinks = 1; | ||||||
|  |   root_dinode.blocks = 1; | ||||||
|  |   root_dinode.addrs[0] = RFS_BLK_OFFSET_FREE; | ||||||
|  | 
 | ||||||
|  |   // write root directory inode to RAM Disk0 (ino = 0)
 | ||||||
|  |   if (rfs_write_dinode(rdev, &root_dinode, 0) != 0) { | ||||||
|  |     sprint("RFS: failed to write root inode!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // ** third, write freemap to disk
 | ||||||
|  |   int *freemap = (int *)rdev->iobuffer; | ||||||
|  |   memset(freemap, 0, RFS_BLKSIZE); | ||||||
|  |   freemap[0] = 1;  // the first data block is used for root directory
 | ||||||
|  | 
 | ||||||
|  |   // write the bitmap to RAM Disk0
 | ||||||
|  |   if (rfs_w1block(rdev, RFS_BLK_OFFSET_BITMAP) != 0) {  // write to device
 | ||||||
|  |     sprint("RFS: failed to write bitmap!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   sprint("RFS: format %s done!\n", dev->dev_name); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ** Note: If you use the following four functions interchangeably,
 | ||||||
|  | // ** be sure to watch out for IOBUFFER BEING OVERWRITTEN !!!
 | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // call ramdisk_read via the device structure.
 | ||||||
|  | // read the "n_block"^th block from RAM disk to the iobuffer of rfs_dev.
 | ||||||
|  | //
 | ||||||
|  | int rfs_r1block(struct rfs_device *rfs_dev, int n_block) { | ||||||
|  |   return dop_read(rfs_dev, n_block); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // call ramdisk_write via the device structure.
 | ||||||
|  | // write iobuffer of rfs_dev to RAM disk at the "n_block"^th block.
 | ||||||
|  | //
 | ||||||
|  | int rfs_w1block(struct rfs_device *rfs_dev, int n_block) { | ||||||
|  |   return dop_write(rfs_dev, n_block); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // read disk inode from RAM disk
 | ||||||
|  | //
 | ||||||
|  | struct rfs_dinode *rfs_read_dinode(struct rfs_device *rdev, int n_inode) { | ||||||
|  |   int n_block = n_inode / (RFS_BLKSIZE / RFS_INODESIZE) + RFS_BLK_OFFSET_INODE; | ||||||
|  |   int offset = n_inode % (RFS_BLKSIZE / RFS_INODESIZE); | ||||||
|  | 
 | ||||||
|  |   // call ramdisk_read defined in dev.c
 | ||||||
|  |   if (dop_read(rdev, n_block) != 0) return NULL; | ||||||
|  |   struct rfs_dinode *dinode = (struct rfs_dinode *)alloc_page(); | ||||||
|  |   memcpy(dinode, (char *)rdev->iobuffer + offset * RFS_INODESIZE, | ||||||
|  |          sizeof(struct rfs_dinode)); | ||||||
|  |   return dinode; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // write disk inode to RAM disk.
 | ||||||
|  | // note: we need first read the "disk" block containing the "n_inode"^th inode,
 | ||||||
|  | // modify it, and write the block back to "disk" eventually.
 | ||||||
|  | //
 | ||||||
|  | int rfs_write_dinode(struct rfs_device *rdev, const struct rfs_dinode *dinode, | ||||||
|  |                      int n_inode) { | ||||||
|  |   int n_block = n_inode / (RFS_BLKSIZE / RFS_INODESIZE) + RFS_BLK_OFFSET_INODE; | ||||||
|  |   int offset = n_inode % (RFS_BLKSIZE / RFS_INODESIZE); | ||||||
|  | 
 | ||||||
|  |   // call ramdisk_read defined in dev.c
 | ||||||
|  |   dop_read(rdev, n_block); | ||||||
|  |   memcpy(rdev->iobuffer + offset * RFS_INODESIZE, dinode, | ||||||
|  |          sizeof(struct rfs_dinode)); | ||||||
|  |   // call ramdisk_write defined in dev.c
 | ||||||
|  |   int ret = dop_write(rdev, n_block); | ||||||
|  | 
 | ||||||
|  |   return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // allocate a block from RAM disk
 | ||||||
|  | //
 | ||||||
|  | int rfs_alloc_block(struct super_block *sb) { | ||||||
|  |   int free_block = -1; | ||||||
|  |   // think of s_fs_info as freemap information
 | ||||||
|  |   int *freemap = (int *)sb->s_fs_info; | ||||||
|  |   for (int block = 0; block < sb->nblocks; ++block) { | ||||||
|  |     if (freemap[block] == 0) {  // find a free block
 | ||||||
|  |       freemap[block] = 1; | ||||||
|  |       free_block = RFS_BLK_OFFSET_FREE + block; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (free_block == -1) panic("rfs_alloc_block: no more free block!\n"); | ||||||
|  |   return free_block; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // free a block in RAM disk
 | ||||||
|  | //
 | ||||||
|  | int rfs_free_block(struct super_block *sb, int block_num) { | ||||||
|  |   int *freemap = (int *)sb->s_fs_info; | ||||||
|  |   freemap[block_num - RFS_BLK_OFFSET_FREE] = 0; | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // add a new directory entry to a directory
 | ||||||
|  | //
 | ||||||
|  | int rfs_add_direntry(struct vinode *dir, const char *name, int inum) { | ||||||
|  |   if (dir->type != DIR_I) { | ||||||
|  |     sprint("rfs_add_direntry: not a directory!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   struct rfs_device *rdev = rfs_device_list[dir->sb->s_dev->dev_id]; | ||||||
|  |   int n_block = dir->addrs[dir->size / RFS_BLKSIZE]; | ||||||
|  |   if (rfs_r1block(rdev, n_block) != 0) { | ||||||
|  |     sprint("rfs_add_direntry: failed to read block %d!\n", n_block); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // prepare iobuffer
 | ||||||
|  |   char *addr = (char *)rdev->iobuffer + dir->size % RFS_BLKSIZE; | ||||||
|  |   struct rfs_direntry *p_direntry = (struct rfs_direntry *)addr; | ||||||
|  |   p_direntry->inum = inum; | ||||||
|  |   strcpy(p_direntry->name, name); | ||||||
|  | 
 | ||||||
|  |   // write the modified (parent) directory block back to disk
 | ||||||
|  |   if (rfs_w1block(rdev, n_block) != 0) { | ||||||
|  |     sprint("rfs_add_direntry: failed to write block %d!\n", n_block); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // update its parent dir state
 | ||||||
|  |   dir->size += sizeof(struct rfs_direntry); | ||||||
|  | 
 | ||||||
|  |   // write the parent dir inode back to disk
 | ||||||
|  |   if (rfs_write_back_vinode(dir) != 0) { | ||||||
|  |     sprint("rfs_add_direntry: failed to write back parent dir inode!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // alloc a new (and empty) vinode
 | ||||||
|  | //
 | ||||||
|  | struct vinode *rfs_alloc_vinode(struct super_block *sb) { | ||||||
|  |   struct vinode *vinode = default_alloc_vinode(sb); | ||||||
|  |   vinode->i_ops = &rfs_i_ops; | ||||||
|  |   return vinode; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // convert vfs inode to disk inode, and write it back to disk
 | ||||||
|  | //
 | ||||||
|  | int rfs_write_back_vinode(struct vinode *vinode) { | ||||||
|  |   // copy vinode info to disk inode
 | ||||||
|  |   struct rfs_dinode dinode; | ||||||
|  |   dinode.size = vinode->size; | ||||||
|  |   dinode.nlinks = vinode->nlinks; | ||||||
|  |   dinode.blocks = vinode->blocks; | ||||||
|  |   dinode.type = vinode->type; | ||||||
|  |   for (int i = 0; i < RFS_DIRECT_BLKNUM; ++i) { | ||||||
|  |     dinode.addrs[i] = vinode->addrs[i]; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   struct rfs_device *rdev = rfs_device_list[vinode->sb->s_dev->dev_id]; | ||||||
|  |   if (rfs_write_dinode(rdev, &dinode, vinode->inum) != 0) { | ||||||
|  |     sprint("rfs_free_write_back_inode: failed to write back disk inode!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // update vinode info by reading disk inode
 | ||||||
|  | //
 | ||||||
|  | int rfs_update_vinode(struct vinode *vinode) { | ||||||
|  |   struct rfs_device *rdev = rfs_device_list[vinode->sb->s_dev->dev_id]; | ||||||
|  |   struct rfs_dinode *dinode = rfs_read_dinode(rdev, vinode->inum); | ||||||
|  |   if (dinode == NULL) { | ||||||
|  |     sprint("rfs_update_vinode: failed to read disk inode!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |   vinode->size = dinode->size; | ||||||
|  |   vinode->nlinks = dinode->nlinks; | ||||||
|  |   vinode->blocks = dinode->blocks; | ||||||
|  |   vinode->type = dinode->type; | ||||||
|  |   for (int i = 0; i < RFS_DIRECT_BLKNUM; ++i) { | ||||||
|  |     vinode->addrs[i] = dinode->addrs[i]; | ||||||
|  |   } | ||||||
|  |   free_page(dinode); | ||||||
|  | 
 | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**** vfs-rfs file interface functions ****/ | ||||||
|  | //
 | ||||||
|  | // read the content (for "len") of a file ("f_inode"), and copy the content
 | ||||||
|  | // to "r_buf".
 | ||||||
|  | //
 | ||||||
|  | ssize_t rfs_read(struct vinode *f_inode, char *r_buf, ssize_t len, | ||||||
|  |                  int *offset) { | ||||||
|  |   // obtain disk inode from vfs inode
 | ||||||
|  |   if (f_inode->size < *offset) | ||||||
|  |     panic("rfs_read:offset should less than file size!"); | ||||||
|  | 
 | ||||||
|  |   if (f_inode->size < (*offset + len)) len = f_inode->size - *offset; | ||||||
|  | 
 | ||||||
|  |   char buffer[len + 1]; | ||||||
|  | 
 | ||||||
|  |   // compute how many blocks we need to read
 | ||||||
|  |   int align = *offset % RFS_BLKSIZE; | ||||||
|  |   int block_offset = *offset / RFS_BLKSIZE; | ||||||
|  |   int buf_offset = 0; | ||||||
|  | 
 | ||||||
|  |   int readtimes = (align + len) / RFS_BLKSIZE; | ||||||
|  |   int remain = (align + len) % RFS_BLKSIZE; | ||||||
|  | 
 | ||||||
|  |   struct rfs_device *rdev = rfs_device_list[f_inode->sb->s_dev->dev_id]; | ||||||
|  | 
 | ||||||
|  |   // read first block
 | ||||||
|  |   rfs_r1block(rdev, f_inode->addrs[block_offset]); | ||||||
|  |   int first_block_len = (readtimes == 0 ? len : RFS_BLKSIZE - align); | ||||||
|  |   memcpy(buffer + buf_offset, rdev->iobuffer + align, first_block_len); | ||||||
|  |   buf_offset += first_block_len; | ||||||
|  |   block_offset++; | ||||||
|  |   readtimes--; | ||||||
|  | 
 | ||||||
|  |   // readtimes < 0 means that the file has only one block (and not full),
 | ||||||
|  |   // so our work is done
 | ||||||
|  |   // otherwise...
 | ||||||
|  |   if (readtimes >= 0) { | ||||||
|  |     // read in complete blocks
 | ||||||
|  |     while (readtimes != 0) { | ||||||
|  |       rfs_r1block(rdev, f_inode->addrs[block_offset]); | ||||||
|  |       memcpy(buffer + buf_offset, rdev->iobuffer, RFS_BLKSIZE); | ||||||
|  |       buf_offset += RFS_BLKSIZE; | ||||||
|  |       block_offset++; | ||||||
|  |       readtimes--; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // read in the remaining data
 | ||||||
|  |     if (remain > 0) { | ||||||
|  |       rfs_r1block(rdev, f_inode->addrs[block_offset]); | ||||||
|  |       memcpy(buffer + buf_offset, rdev->iobuffer, remain); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   buffer[len] = '\0'; | ||||||
|  |   strcpy(r_buf, buffer); | ||||||
|  | 
 | ||||||
|  |   *offset += len; | ||||||
|  |   return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // write the content of "w_buf" (lengthed "len") to a file ("f_inode").
 | ||||||
|  | //
 | ||||||
|  | ssize_t rfs_write(struct vinode *f_inode, const char *w_buf, ssize_t len, | ||||||
|  |                   int *offset) { | ||||||
|  |   if (f_inode->size < *offset) { | ||||||
|  |     panic("rfs_write:offset should less than file size!"); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // compute how many blocks we need to write
 | ||||||
|  |   int align = *offset % RFS_BLKSIZE; | ||||||
|  |   int writetimes = (len + align) / RFS_BLKSIZE; | ||||||
|  |   int remain = (len + align) % RFS_BLKSIZE; | ||||||
|  | 
 | ||||||
|  |   int buf_offset = 0; | ||||||
|  |   int block_offset = *offset / RFS_BLKSIZE; | ||||||
|  | 
 | ||||||
|  |   struct rfs_device *rdev = rfs_device_list[f_inode->sb->s_dev->dev_id]; | ||||||
|  | 
 | ||||||
|  |   // write first block
 | ||||||
|  |   if (align != 0) { | ||||||
|  |     rfs_r1block(rdev, f_inode->addrs[block_offset]); | ||||||
|  |     int first_block_len = (writetimes == 0 ? len : RFS_BLKSIZE - align); | ||||||
|  |     memcpy(rdev->iobuffer + align, w_buf, first_block_len); | ||||||
|  |     rfs_w1block(rdev, f_inode->addrs[block_offset]); | ||||||
|  | 
 | ||||||
|  |     buf_offset += first_block_len; | ||||||
|  |     block_offset++; | ||||||
|  |     writetimes--; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // writetimes < 0 means that the file has only one block (and not full),
 | ||||||
|  |   // so our work is done
 | ||||||
|  |   // otherwise...
 | ||||||
|  |   if (writetimes >= 0) { | ||||||
|  |     // write complete blocks
 | ||||||
|  |     while (writetimes != 0) { | ||||||
|  |       if (block_offset == f_inode->blocks) {  // need to create new block
 | ||||||
|  |         // allocate a free block for the file
 | ||||||
|  |         f_inode->addrs[block_offset] = rfs_alloc_block(f_inode->sb); | ||||||
|  |         f_inode->blocks++; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       memcpy(rdev->iobuffer, w_buf + buf_offset, RFS_BLKSIZE); | ||||||
|  |       rfs_w1block(rdev, f_inode->addrs[block_offset]); | ||||||
|  | 
 | ||||||
|  |       buf_offset += RFS_BLKSIZE; | ||||||
|  |       block_offset++; | ||||||
|  |       writetimes--; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // write the remaining data
 | ||||||
|  |     if (remain > 0) { | ||||||
|  |       if (block_offset == f_inode->blocks) { | ||||||
|  |         f_inode->addrs[block_offset] = rfs_alloc_block(f_inode->sb); | ||||||
|  |         ++f_inode->blocks; | ||||||
|  |       } | ||||||
|  |       memcpy(rdev->iobuffer, w_buf + buf_offset, remain); | ||||||
|  |       rfs_w1block(rdev, f_inode->addrs[block_offset]); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // update file size
 | ||||||
|  |   f_inode->size = | ||||||
|  |       (f_inode->size < *offset + len ? *offset + len : f_inode->size); | ||||||
|  | 
 | ||||||
|  |   *offset += len; | ||||||
|  |   return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // lookup a directory entry("sub_dentry") under "parent".
 | ||||||
|  | // note that this is a one level lookup ,and the vfs layer will call this
 | ||||||
|  | // function several times until the final file is found.
 | ||||||
|  | // return: if found, return its vinode, otherwise return NULL
 | ||||||
|  | //
 | ||||||
|  | struct vinode *rfs_lookup(struct vinode *parent, struct dentry *sub_dentry) { | ||||||
|  |   struct rfs_direntry *p_direntry = NULL; | ||||||
|  |   struct vinode *child_vinode = NULL; | ||||||
|  | 
 | ||||||
|  |   int total_direntrys = parent->size / sizeof(struct rfs_direntry); | ||||||
|  |   int one_block_direntrys = RFS_BLKSIZE / sizeof(struct rfs_direntry); | ||||||
|  | 
 | ||||||
|  |   struct rfs_device *rdev = rfs_device_list[parent->sb->s_dev->dev_id]; | ||||||
|  | 
 | ||||||
|  |   // browse the dir entries contained in a directory file
 | ||||||
|  |   for (int i = 0; i < total_direntrys; ++i) { | ||||||
|  |     if (i % one_block_direntrys == 0) {  // read in the disk block at boundary
 | ||||||
|  |       rfs_r1block(rdev, parent->addrs[i / one_block_direntrys]); | ||||||
|  |       p_direntry = (struct rfs_direntry *)rdev->iobuffer; | ||||||
|  |     } | ||||||
|  |     if (strcmp(p_direntry->name, sub_dentry->name) == 0) {  // found
 | ||||||
|  |       child_vinode = rfs_alloc_vinode(parent->sb); | ||||||
|  |       child_vinode->inum = p_direntry->inum; | ||||||
|  |       if (rfs_update_vinode(child_vinode) != 0) | ||||||
|  |         panic("rfs_lookup: read inode failed!"); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     ++p_direntry; | ||||||
|  |   } | ||||||
|  |   return child_vinode; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // create a file with "sub_dentry->name" at directory "parent" in rfs.
 | ||||||
|  | // return the vfs inode of the file being created.
 | ||||||
|  | //
 | ||||||
|  | struct vinode *rfs_create(struct vinode *parent, struct dentry *sub_dentry) { | ||||||
|  |   struct rfs_device *rdev = rfs_device_list[parent->sb->s_dev->dev_id]; | ||||||
|  | 
 | ||||||
|  |   // ** find a free disk inode to store the file that is going to be created
 | ||||||
|  |   struct rfs_dinode *free_dinode = NULL; | ||||||
|  |   int free_inum = 0; | ||||||
|  |   for (int i = 0; i < (RFS_BLKSIZE / RFS_INODESIZE * RFS_MAX_INODE_BLKNUM); | ||||||
|  |        ++i) { | ||||||
|  |     free_dinode = rfs_read_dinode(rdev, i); | ||||||
|  |     if (free_dinode->type == R_FREE) {  // found
 | ||||||
|  |       free_inum = i; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     free_page(free_dinode); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (free_dinode == NULL) | ||||||
|  |     panic("rfs_create: no more free disk inode, we cannot create file.\n" ); | ||||||
|  | 
 | ||||||
|  |   // initialize the states of the file being created
 | ||||||
|  | 
 | ||||||
|  |   // TODO (lab4_1): implement the code for populating the disk inode (free_dinode) 
 | ||||||
|  |   // of a new file being created.
 | ||||||
|  |   // hint:  members of free_dinode to be filled are:
 | ||||||
|  |   // size, should be zero for a new file.
 | ||||||
|  |   // type, see kernel/rfs.h and find the type for a rfs file.
 | ||||||
|  |   // nlinks, i.e., the number of links.
 | ||||||
|  |   // blocks, i.e., its block count.
 | ||||||
|  |   // Note: DO NOT DELETE CODE BELOW PANIC.
 | ||||||
|  |   panic("You need to implement the code of populating a disk inode in lab4_1.\n" ); | ||||||
|  | 
 | ||||||
|  |   // DO NOT REMOVE ANY CODE BELOW.
 | ||||||
|  |   // allocate a free block for the file
 | ||||||
|  |   free_dinode->addrs[0] = rfs_alloc_block(parent->sb); | ||||||
|  | 
 | ||||||
|  |   // **  write the disk inode of file being created to disk
 | ||||||
|  |   rfs_write_dinode(rdev, free_dinode, free_inum); | ||||||
|  |   free_page(free_dinode); | ||||||
|  | 
 | ||||||
|  |   // ** build vfs inode according to dinode
 | ||||||
|  |   struct vinode *new_vinode = rfs_alloc_vinode(parent->sb); | ||||||
|  |   new_vinode->inum = free_inum; | ||||||
|  |   rfs_update_vinode(new_vinode); | ||||||
|  | 
 | ||||||
|  |   // ** append the new file as a direntry to its parent dir
 | ||||||
|  |   int result = rfs_add_direntry(parent, sub_dentry->name, free_inum); | ||||||
|  |   if (result == -1) { | ||||||
|  |     sprint("rfs_create: rfs_add_direntry failed"); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return new_vinode; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // there are two types of seek (specify by whence): LSEEK_SET, SEEK_CUR
 | ||||||
|  | // LSEEK_SET: set the file pointer to the offset
 | ||||||
|  | // LSEEK_CUR: set the file pointer to the current offset plus the offset
 | ||||||
|  | // return 0 if success, otherwise return -1
 | ||||||
|  | //
 | ||||||
|  | int rfs_lseek(struct vinode *f_inode, ssize_t new_offset, int whence, int *offset) { | ||||||
|  |   int file_size = f_inode->size; | ||||||
|  | 
 | ||||||
|  |   switch (whence) { | ||||||
|  |     case LSEEK_SET: | ||||||
|  |       if (new_offset < 0 || new_offset > file_size) { | ||||||
|  |         sprint("rfs_lseek: invalid offset!\n"); | ||||||
|  |         return -1; | ||||||
|  |       } | ||||||
|  |       *offset = new_offset; | ||||||
|  |       break; | ||||||
|  |     case LSEEK_CUR: | ||||||
|  |       if (*offset + new_offset < 0 || *offset + new_offset > file_size) { | ||||||
|  |         sprint("rfs_lseek: invalid offset!\n"); | ||||||
|  |         return -1; | ||||||
|  |       } | ||||||
|  |       *offset += new_offset; | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       sprint("rfs_lseek: invalid whence!\n"); | ||||||
|  |       return -1; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | //  read disk inode information from disk
 | ||||||
|  | //
 | ||||||
|  | int rfs_disk_stat(struct vinode *vinode, struct istat *istat) { | ||||||
|  |   struct rfs_device *rdev = rfs_device_list[vinode->sb->s_dev->dev_id]; | ||||||
|  |   struct rfs_dinode *dinode = rfs_read_dinode(rdev, vinode->inum); | ||||||
|  |   if (dinode == NULL) { | ||||||
|  |     sprint("rfs_disk_stat: read dinode failed!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   istat->st_inum = 1; | ||||||
|  |   istat->st_inum = vinode->inum;  // get inode number from vinode
 | ||||||
|  | 
 | ||||||
|  |   istat->st_size = dinode->size; | ||||||
|  |   istat->st_type = dinode->type; | ||||||
|  |   istat->st_nlinks = dinode->nlinks; | ||||||
|  |   istat->st_blocks = dinode->blocks; | ||||||
|  |   free_page(dinode); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**** vfs-rfs file system type interface functions ****/ | ||||||
|  | struct super_block *rfs_get_superblock(struct device *dev) { | ||||||
|  |   struct rfs_device *rdev = rfs_device_list[dev->dev_id]; | ||||||
|  | 
 | ||||||
|  |   // read super block from ramdisk
 | ||||||
|  |   if (rfs_r1block(rdev, RFS_BLK_OFFSET_SUPER) != 0) | ||||||
|  |     panic("RFS: failed to read superblock!\n"); | ||||||
|  | 
 | ||||||
|  |   struct rfs_superblock d_sb; | ||||||
|  |   memcpy(&d_sb, rdev->iobuffer, sizeof(struct rfs_superblock)); | ||||||
|  | 
 | ||||||
|  |   // set the data for the vfs super block
 | ||||||
|  |   struct super_block *sb = alloc_page(); | ||||||
|  |   sb->magic = d_sb.magic; | ||||||
|  |   sb->size = d_sb.size; | ||||||
|  |   sb->nblocks = d_sb.nblocks; | ||||||
|  |   sb->ninodes = d_sb.ninodes; | ||||||
|  |   sb->s_dev = dev; | ||||||
|  | 
 | ||||||
|  |   if( sb->magic != RFS_MAGIC )  | ||||||
|  |     panic("rfs_get_superblock: wrong ramdisk device!\n"); | ||||||
|  | 
 | ||||||
|  |   // build root dentry and root inode
 | ||||||
|  |   struct vinode *root_inode = rfs_alloc_vinode(sb); | ||||||
|  |   root_inode->inum = 0; | ||||||
|  |   rfs_update_vinode(root_inode); | ||||||
|  | 
 | ||||||
|  |   struct dentry *root_dentry = alloc_vfs_dentry("/", root_inode, NULL); | ||||||
|  |   sb->s_root = root_dentry; | ||||||
|  | 
 | ||||||
|  |   // save the bitmap in the s_fs_info field
 | ||||||
|  |   if (rfs_r1block(rdev, RFS_BLK_OFFSET_BITMAP) != 0) | ||||||
|  |     panic("RFS: failed to read bitmap!\n"); | ||||||
|  |   void *bitmap = alloc_page(); | ||||||
|  |   memcpy(bitmap, rdev->iobuffer, RFS_BLKSIZE); | ||||||
|  |   sb->s_fs_info = bitmap; | ||||||
|  | 
 | ||||||
|  |   return sb; | ||||||
|  | } | ||||||
| @ -0,0 +1,87 @@ | |||||||
|  | #ifndef _RFS_H_ | ||||||
|  | #define _RFS_H_ | ||||||
|  | 
 | ||||||
|  | #include "ramdev.h" | ||||||
|  | #include "riscv.h" | ||||||
|  | #include "util/types.h" | ||||||
|  | #include "vfs.h" | ||||||
|  | 
 | ||||||
|  | #define RFS_TYPE 0 | ||||||
|  | #define RFS_MAGIC 0xBEAF | ||||||
|  | #define RFS_BLKSIZE PGSIZE | ||||||
|  | #define RFS_INODESIZE 128  // block size must be divisible by this value
 | ||||||
|  | #define RFS_MAX_INODE_BLKNUM 10 | ||||||
|  | #define RFS_MAX_FILE_NAME_LEN 28 | ||||||
|  | #define RFS_DIRECT_BLKNUM DIRECT_BLKNUM | ||||||
|  | 
 | ||||||
|  | // rfs block offset
 | ||||||
|  | #define RFS_BLK_OFFSET_SUPER 0 | ||||||
|  | #define RFS_BLK_OFFSET_INODE 1 | ||||||
|  | #define RFS_BLK_OFFSET_BITMAP 11 | ||||||
|  | #define RFS_BLK_OFFSET_FREE 12 | ||||||
|  | 
 | ||||||
|  | // dinode type
 | ||||||
|  | #define R_FILE FILE_I | ||||||
|  | #define R_DIR DIR_I | ||||||
|  | #define R_FREE 2 | ||||||
|  | 
 | ||||||
|  | // file system super block
 | ||||||
|  | struct rfs_superblock { | ||||||
|  |   int magic;    // magic number of the
 | ||||||
|  |   int size;     // size of file system image (blocks)
 | ||||||
|  |   int nblocks;  // number of data blocks
 | ||||||
|  |   int ninodes;  // number of inodes.
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // disk inode
 | ||||||
|  | struct rfs_dinode { | ||||||
|  |   int size;                      // size of the file (in bytes)
 | ||||||
|  |   int type;                      // one of R_FREE, R_FILE, R_DIR
 | ||||||
|  |   int nlinks;                    // number of hard links to this file
 | ||||||
|  |   int blocks;                    // number of blocks
 | ||||||
|  |   int addrs[RFS_DIRECT_BLKNUM];  // direct blocks
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // directory entry
 | ||||||
|  | struct rfs_direntry { | ||||||
|  |   int inum;                          // inode number
 | ||||||
|  |   char name[RFS_MAX_FILE_NAME_LEN];  // file name
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // directory memory cache (used by opendir/readdir/closedir)
 | ||||||
|  | struct rfs_dir_cache { | ||||||
|  |   int block_count; | ||||||
|  |   struct rfs_direntry *dir_base_addr; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // rfs utility functin declarations
 | ||||||
|  | int register_rfs(); | ||||||
|  | int rfs_format_dev(struct device *dev); | ||||||
|  | 
 | ||||||
|  | int rfs_r1block(struct rfs_device *rfs_dev, int n_block); | ||||||
|  | int rfs_w1block(struct rfs_device *rfs_dev, int n_block); | ||||||
|  | struct rfs_dinode *rfs_read_dinode(struct rfs_device *rdev, int n_inode); | ||||||
|  | int rfs_write_dinode(struct rfs_device *rdev, const struct rfs_dinode *dinode, | ||||||
|  |                      int n_inode); | ||||||
|  | int rfs_alloc_block(struct super_block *sb); | ||||||
|  | int rfs_free_block(struct super_block *sb, int block_num); | ||||||
|  | int rfs_add_direntry(struct vinode *dir, const char *name, int inum); | ||||||
|  | 
 | ||||||
|  | struct vinode *rfs_alloc_vinode(struct super_block *sb); | ||||||
|  | int rfs_write_back_vinode(struct vinode *vinode); | ||||||
|  | int rfs_update_vinode(struct vinode *vinode); | ||||||
|  | 
 | ||||||
|  | // rfs interface function declarations
 | ||||||
|  | ssize_t rfs_read(struct vinode *f_inode, char *r_buf, ssize_t len, int *offset); | ||||||
|  | ssize_t rfs_write(struct vinode *f_inode, const char *w_buf, ssize_t len, | ||||||
|  |                   int *offset); | ||||||
|  | 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); | ||||||
|  | 
 | ||||||
|  | struct super_block *rfs_get_superblock(struct device *dev); | ||||||
|  | 
 | ||||||
|  | extern const struct vinode_ops rfs_i_ops; | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| @ -0,0 +1,517 @@ | |||||||
|  | /*
 | ||||||
|  |  * VFS (Virtual File System) interface utilities. added @lab4_1. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "vfs.h" | ||||||
|  | 
 | ||||||
|  | #include "pmm.h" | ||||||
|  | #include "spike_interface/spike_utils.h" | ||||||
|  | #include "util/string.h" | ||||||
|  | #include "util/types.h" | ||||||
|  | #include "util/hash_table.h" | ||||||
|  | 
 | ||||||
|  | struct dentry *vfs_root_dentry;               // system root direntry
 | ||||||
|  | struct super_block *vfs_sb_list[MAX_MOUNTS];  // system superblock list
 | ||||||
|  | struct device *vfs_dev_list[MAX_VFS_DEV];     // system device list in vfs layer
 | ||||||
|  | struct hash_table dentry_hash_table; | ||||||
|  | struct hash_table vinode_hash_table; | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // initializes the dentry hash list and vinode hash list
 | ||||||
|  | //
 | ||||||
|  | int vfs_init() { | ||||||
|  |   int ret; | ||||||
|  |   ret = hash_table_init(&dentry_hash_table, dentry_hash_equal, dentry_hash_func, | ||||||
|  |                             NULL, NULL, NULL); | ||||||
|  |   if (ret != 0) return ret; | ||||||
|  | 
 | ||||||
|  |   ret = hash_table_init(&vinode_hash_table, vinode_hash_equal, vinode_hash_func, | ||||||
|  |                             NULL, NULL, NULL); | ||||||
|  |   if (ret != 0) return ret; | ||||||
|  |   return 0;  | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // mount a file system from the device named "dev_name"
 | ||||||
|  | // PKE does not support mounting a device at an arbitrary directory as in Linux,
 | ||||||
|  | // but can only mount a device in one of the following two ways (according to 
 | ||||||
|  | // the mnt_type parameter) :
 | ||||||
|  | // 1. when mnt_type = MOUNT_AS_ROOT
 | ||||||
|  | //    Mount the device AS the root directory.
 | ||||||
|  | //    that is, mount the device under system root direntry:"/".
 | ||||||
|  | //    In this case, the device specified by parameter dev_name will be used as
 | ||||||
|  | //    the root file system.
 | ||||||
|  | // 2. when mnt_type = MOUNT_DEFAULT
 | ||||||
|  | //    Mount the device UNDER the root directory.
 | ||||||
|  | //    that is, mount the device to "/DEVICE_NAME" (/DEVICE_NAME will be
 | ||||||
|  | //    automatically created) folder.
 | ||||||
|  | //
 | ||||||
|  | struct super_block *vfs_mount(const char *dev_name, int mnt_type) { | ||||||
|  |   // device pointer
 | ||||||
|  |   struct device *p_device = NULL; | ||||||
|  | 
 | ||||||
|  |   // find the device entry in vfs_device_list named as dev_name
 | ||||||
|  |   for (int i = 0; i < MAX_VFS_DEV; ++i) { | ||||||
|  |     p_device = vfs_dev_list[i]; | ||||||
|  |     if (p_device && strcmp(p_device->dev_name, dev_name) == 0) break; | ||||||
|  |   } | ||||||
|  |   if (p_device == NULL) panic("vfs_mount: cannot find the device entry!\n"); | ||||||
|  | 
 | ||||||
|  |   // add the super block into vfs_sb_list
 | ||||||
|  |   struct file_system_type *fs_type = p_device->fs_type; | ||||||
|  |   struct super_block *sb = fs_type->get_superblock(p_device); | ||||||
|  | 
 | ||||||
|  |   // add the root vinode into vinode_hash_table
 | ||||||
|  |   hash_put_vinode(sb->s_root->dentry_inode); | ||||||
|  | 
 | ||||||
|  |   int err = 1; | ||||||
|  |   for (int i = 0; i < MAX_MOUNTS; ++i) { | ||||||
|  |     if (vfs_sb_list[i] == NULL) { | ||||||
|  |       vfs_sb_list[i] = sb; | ||||||
|  |       err = 0; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (err) panic("vfs_mount: too many mounts!\n"); | ||||||
|  | 
 | ||||||
|  |   // mount the root dentry of the file system to right place
 | ||||||
|  |   if (mnt_type == MOUNT_AS_ROOT) { | ||||||
|  |     vfs_root_dentry = sb->s_root; | ||||||
|  | 
 | ||||||
|  |     // insert the mount point into hash table
 | ||||||
|  |     hash_put_dentry(sb->s_root); | ||||||
|  |   } else if (mnt_type == MOUNT_DEFAULT) { | ||||||
|  |     if (!vfs_root_dentry) | ||||||
|  |       panic("vfs_mount: root dentry not found, please mount the root device first!\n"); | ||||||
|  | 
 | ||||||
|  |     struct dentry *mnt_point = sb->s_root; | ||||||
|  | 
 | ||||||
|  |     // set the mount point directory's name to device name
 | ||||||
|  |     char *dev_name = p_device->dev_name; | ||||||
|  |     strcpy(mnt_point->name, dev_name); | ||||||
|  | 
 | ||||||
|  |     // by default, it is mounted under the vfs root directory
 | ||||||
|  |     mnt_point->parent = vfs_root_dentry; | ||||||
|  | 
 | ||||||
|  |     // insert the mount point into hash table
 | ||||||
|  |     hash_put_dentry(sb->s_root); | ||||||
|  |   } else { | ||||||
|  |     panic("vfs_mount: unknown mount type!\n"); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return sb; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // open a file located at "path" with permission of "flags".
 | ||||||
|  | // if the file does not exist, and O_CREAT bit is set in "flags", the file will
 | ||||||
|  | // be created.
 | ||||||
|  | // return: the file pointer to the opened file.
 | ||||||
|  | //
 | ||||||
|  | struct file *vfs_open(const char *path, int flags) { | ||||||
|  |   struct dentry *parent = vfs_root_dentry; // we start the path lookup from root.
 | ||||||
|  |   char miss_name[MAX_PATH_LEN]; | ||||||
|  | 
 | ||||||
|  |   // path lookup.
 | ||||||
|  |   struct dentry *file_dentry = lookup_final_dentry(path, &parent, miss_name); | ||||||
|  | 
 | ||||||
|  |   // file does not exist
 | ||||||
|  |   if (!file_dentry) { | ||||||
|  |     int creatable = flags & O_CREAT; | ||||||
|  | 
 | ||||||
|  |     // create the file if O_CREAT bit is set
 | ||||||
|  |     if (creatable) { | ||||||
|  |       char basename[MAX_PATH_LEN]; | ||||||
|  |       get_base_name(path, basename); | ||||||
|  | 
 | ||||||
|  |       // a missing directory exists in the path
 | ||||||
|  |       if (strcmp(miss_name, basename) != 0) { | ||||||
|  |         sprint("vfs_open: cannot create file in a non-exist directory!\n"); | ||||||
|  |         return NULL; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // create the file
 | ||||||
|  |       file_dentry = alloc_vfs_dentry(basename, NULL, parent); | ||||||
|  |       struct vinode *new_inode = viop_create(parent->dentry_inode, file_dentry); | ||||||
|  |       if (!new_inode) panic("vfs_open: cannot create file!\n"); | ||||||
|  | 
 | ||||||
|  |       file_dentry->dentry_inode = new_inode; | ||||||
|  |       new_inode->ref++; | ||||||
|  |       hash_put_dentry(file_dentry); | ||||||
|  |       hash_put_vinode(new_inode);  | ||||||
|  |     } else { | ||||||
|  |       sprint("vfs_open: cannot find the file!\n"); | ||||||
|  |       return NULL; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (file_dentry->dentry_inode->type != FILE_I) { | ||||||
|  |     sprint("vfs_open: cannot open a directory!\n"); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // get writable and readable flags
 | ||||||
|  |   int writable = 0; | ||||||
|  |   int readable = 0; | ||||||
|  |   switch (flags & MASK_FILEMODE) { | ||||||
|  |     case O_RDONLY: | ||||||
|  |       writable = 0; | ||||||
|  |       readable = 1; | ||||||
|  |       break; | ||||||
|  |     case O_WRONLY: | ||||||
|  |       writable = 1; | ||||||
|  |       readable = 0; | ||||||
|  |       break; | ||||||
|  |     case O_RDWR: | ||||||
|  |       writable = 1; | ||||||
|  |       readable = 1; | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       panic("fs_open: invalid open flags!\n"); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   struct file *file = alloc_vfs_file(file_dentry, readable, writable, 0); | ||||||
|  | 
 | ||||||
|  |   // additional open operations for a specific file system
 | ||||||
|  |   // hostfs needs to conduct actual file open.
 | ||||||
|  |   if (file_dentry->dentry_inode->i_ops->viop_hook_open) { | ||||||
|  |     if (file_dentry->dentry_inode->i_ops-> | ||||||
|  |         viop_hook_open(file_dentry->dentry_inode, file_dentry) < 0) { | ||||||
|  |       sprint("vfs_open: hook_open failed!\n"); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return file; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // read content from "file" starting from file->offset, and store it in "buf".
 | ||||||
|  | // return: the number of bytes actually read
 | ||||||
|  | //
 | ||||||
|  | ssize_t vfs_read(struct file *file, char *buf, size_t count) { | ||||||
|  |   if (!file->readable) { | ||||||
|  |     sprint("vfs_read: file is not readable!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |   if (file->f_dentry->dentry_inode->type != FILE_I) { | ||||||
|  |     sprint("vfs_read: cannot read a directory!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |   // actual reading.
 | ||||||
|  |   return viop_read(file->f_dentry->dentry_inode, buf, count, &(file->offset)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // write content in "buf" to "file", at file->offset.
 | ||||||
|  | // return: the number of bytes actually written
 | ||||||
|  | //
 | ||||||
|  | ssize_t vfs_write(struct file *file, const char *buf, size_t count) { | ||||||
|  |   if (!file->writable) { | ||||||
|  |     sprint("vfs_write: file is not writable!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |   if (file->f_dentry->dentry_inode->type != FILE_I) { | ||||||
|  |     sprint("vfs_read: cannot write a directory!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |   // actual writing.
 | ||||||
|  |   return viop_write(file->f_dentry->dentry_inode, buf, count, &(file->offset)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // reposition read/write file offset
 | ||||||
|  | // return: the new offset on success, -1 on failure.
 | ||||||
|  | //
 | ||||||
|  | ssize_t vfs_lseek(struct file *file, ssize_t offset, int whence) { | ||||||
|  |   if (file->f_dentry->dentry_inode->type != FILE_I) { | ||||||
|  |     sprint("vfs_read: cannot seek a directory!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (viop_lseek(file->f_dentry->dentry_inode, offset, whence, &(file->offset)) != 0) { | ||||||
|  |     sprint("vfs_lseek: lseek failed!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return file->offset; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // read the vinode information
 | ||||||
|  | //
 | ||||||
|  | int vfs_stat(struct file *file, struct istat *istat) { | ||||||
|  |   istat->st_inum = file->f_dentry->dentry_inode->inum; | ||||||
|  |   istat->st_size = file->f_dentry->dentry_inode->size; | ||||||
|  |   istat->st_type = file->f_dentry->dentry_inode->type; | ||||||
|  |   istat->st_nlinks = file->f_dentry->dentry_inode->nlinks; | ||||||
|  |   istat->st_blocks = file->f_dentry->dentry_inode->blocks; | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // read the inode information on the disk
 | ||||||
|  | //
 | ||||||
|  | int vfs_disk_stat(struct file *file, struct istat *istat) { | ||||||
|  |   return viop_disk_stat(file->f_dentry->dentry_inode, istat); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // close a file at vfs layer.
 | ||||||
|  | //
 | ||||||
|  | int vfs_close(struct file *file) { | ||||||
|  |   if (file->f_dentry->dentry_inode->type != FILE_I) { | ||||||
|  |     sprint("vfs_close: cannot close a directory!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   struct dentry *dentry = file->f_dentry; | ||||||
|  |   struct vinode *inode = dentry->dentry_inode; | ||||||
|  | 
 | ||||||
|  |   // additional close operations for a specific file system
 | ||||||
|  |   // hostfs needs to conduct actual file close.
 | ||||||
|  |   if (inode->i_ops->viop_hook_close) { | ||||||
|  |     if (inode->i_ops->viop_hook_close(inode, dentry) != 0) { | ||||||
|  |       sprint("vfs_close: hook_close failed!\n"); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   dentry->d_ref--; | ||||||
|  |   // if the dentry is not pointed by any opened file, free the dentry
 | ||||||
|  |   if (dentry->d_ref == 0) { | ||||||
|  |     // free the dentry
 | ||||||
|  |     hash_erase_dentry(dentry); | ||||||
|  |     free_vfs_dentry(dentry); | ||||||
|  |     inode->ref--; | ||||||
|  |     // no other opened hard link
 | ||||||
|  |     if (inode->ref == 0) { | ||||||
|  |       // write back the inode and free it
 | ||||||
|  |       if (viop_write_back_vinode(inode) != 0) | ||||||
|  |         panic("vfs_close: free inode failed!\n"); | ||||||
|  |       hash_erase_vinode(inode); | ||||||
|  |       free_page(inode); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   file->status = FD_NONE; | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // lookup the "path" and return its dentry (or NULL if not found).
 | ||||||
|  | // the lookup starts from parent, and stop till the full "path" is parsed.
 | ||||||
|  | // return: the final dentry if we find it, NULL for otherwise.
 | ||||||
|  | //
 | ||||||
|  | struct dentry *lookup_final_dentry(const char *path, struct dentry **parent, | ||||||
|  |                                    char *miss_name) { | ||||||
|  |   char path_copy[MAX_PATH_LEN]; | ||||||
|  |   strcpy(path_copy, path); | ||||||
|  | 
 | ||||||
|  |   // split the path, and retrieves a token at a time.
 | ||||||
|  |   // note: strtok() uses a static (local) variable to store the input path
 | ||||||
|  |   // string at the first time it is called. thus it can out a token each time.
 | ||||||
|  |   // for example, when input path is: /RAMDISK0/test_dir/ramfile2
 | ||||||
|  |   // strtok() outputs three tokens: 1)RAMDISK0, 2)test_dir and 3)ramfile2
 | ||||||
|  |   // at its three continuous invocations.
 | ||||||
|  |   char *token = strtok(path_copy, "/"); | ||||||
|  |   struct dentry *this = *parent; | ||||||
|  | 
 | ||||||
|  |   while (token != NULL) { | ||||||
|  |     *parent = this; | ||||||
|  |     this = hash_get_dentry((*parent), token);  // try hash first
 | ||||||
|  |     if (this == NULL) { | ||||||
|  |       // if not found in hash, try to find it in the directory
 | ||||||
|  |       this = alloc_vfs_dentry(token, NULL, *parent); | ||||||
|  |       // lookup subfolder/file in its parent directory. note:
 | ||||||
|  |       // hostfs and rfs will take different procedures for lookup.
 | ||||||
|  |       struct vinode *found_vinode = viop_lookup((*parent)->dentry_inode, this); | ||||||
|  |       if (found_vinode == NULL) { | ||||||
|  |         // not found in both hash table and directory file on disk.
 | ||||||
|  |         free_page(this); | ||||||
|  |         strcpy(miss_name, token); | ||||||
|  |         return NULL; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       struct vinode *same_inode = hash_get_vinode(found_vinode->sb, found_vinode->inum); | ||||||
|  |       if (same_inode != NULL) { | ||||||
|  |         // the vinode is already in the hash table (i.e. we are opening another hard link)
 | ||||||
|  |         this->dentry_inode = same_inode; | ||||||
|  |         same_inode->ref++; | ||||||
|  |         free_page(found_vinode); | ||||||
|  |       } else { | ||||||
|  |         // the vinode is not in the hash table
 | ||||||
|  |         this->dentry_inode = found_vinode; | ||||||
|  |         found_vinode->ref++; | ||||||
|  |         hash_put_vinode(found_vinode); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       hash_put_dentry(this); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // get next token
 | ||||||
|  |     token = strtok(NULL, "/"); | ||||||
|  |   } | ||||||
|  |   return this; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // get the base name of a path
 | ||||||
|  | //
 | ||||||
|  | void get_base_name(const char *path, char *base_name) { | ||||||
|  |   char path_copy[MAX_PATH_LEN]; | ||||||
|  |   strcpy(path_copy, path); | ||||||
|  | 
 | ||||||
|  |   char *token = strtok(path_copy, "/"); | ||||||
|  |   char *last_token = NULL; | ||||||
|  |   while (token != NULL) { | ||||||
|  |     last_token = token; | ||||||
|  |     token = strtok(NULL, "/"); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   strcpy(base_name, last_token); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // alloc a (virtual) file
 | ||||||
|  | //
 | ||||||
|  | struct file *alloc_vfs_file(struct dentry *file_dentry, int readable, int writable, | ||||||
|  |                         int offset) { | ||||||
|  |   struct file *file = alloc_page(); | ||||||
|  |   file->f_dentry = file_dentry; | ||||||
|  |   file_dentry->d_ref += 1; | ||||||
|  | 
 | ||||||
|  |   file->readable = readable; | ||||||
|  |   file->writable = writable; | ||||||
|  |   file->offset = 0; | ||||||
|  |   file->status = FD_OPENED; | ||||||
|  |   return file; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // alloc a (virtual) dir entry
 | ||||||
|  | //
 | ||||||
|  | struct dentry *alloc_vfs_dentry(const char *name, struct vinode *inode, | ||||||
|  |                             struct dentry *parent) { | ||||||
|  |   struct dentry *dentry = (struct dentry *)alloc_page(); | ||||||
|  |   strcpy(dentry->name, name); | ||||||
|  |   dentry->dentry_inode = inode; | ||||||
|  |   if (inode) inode->ref++; | ||||||
|  | 
 | ||||||
|  |   dentry->parent = parent; | ||||||
|  |   dentry->d_ref = 0; | ||||||
|  |   return dentry; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // free a (virtual) dir entry, if it is not referenced by any file
 | ||||||
|  | //
 | ||||||
|  | int free_vfs_dentry(struct dentry *dentry) { | ||||||
|  |   if (dentry->d_ref > 0) { | ||||||
|  |     sprint("free_vfs_dentry: dentry is still in use!\n"); | ||||||
|  |     return -1; | ||||||
|  |   } | ||||||
|  |   free_page((void *)dentry); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // dentry generic hash table method implementation
 | ||||||
|  | int dentry_hash_equal(void *key1, void *key2) { | ||||||
|  |   struct dentry_key *dentry_key1 = key1; | ||||||
|  |   struct dentry_key *dentry_key2 = key2; | ||||||
|  |   if (strcmp(dentry_key1->name, dentry_key2->name) == 0 && | ||||||
|  |       dentry_key1->parent == dentry_key2->parent) { | ||||||
|  |     return 1; | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t dentry_hash_func(void *key) { | ||||||
|  |   struct dentry_key *dentry_key = key; | ||||||
|  |   char *name = dentry_key->name; | ||||||
|  | 
 | ||||||
|  |   size_t hash = 5381; | ||||||
|  |   int c; | ||||||
|  | 
 | ||||||
|  |   while ((c = *name++)) hash = ((hash << 5) + hash) + c;  // hash * 33 + c
 | ||||||
|  | 
 | ||||||
|  |   hash = ((hash << 5) + hash) + (size_t)dentry_key->parent; | ||||||
|  |   return hash % HASH_TABLE_SIZE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // dentry hash table interface
 | ||||||
|  | struct dentry *hash_get_dentry(struct dentry *parent, char *name) { | ||||||
|  |   struct dentry_key key = {.parent = parent, .name = name}; | ||||||
|  |   return (struct dentry *)dentry_hash_table.virtual_hash_get(&dentry_hash_table, | ||||||
|  |                                                              &key); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int hash_put_dentry(struct dentry *dentry) { | ||||||
|  |   struct dentry_key *key = alloc_page(); | ||||||
|  |   key->name = dentry->name; | ||||||
|  |   key->parent = dentry->parent; | ||||||
|  | 
 | ||||||
|  |   int ret = dentry_hash_table.virtual_hash_put(&dentry_hash_table, key, dentry); | ||||||
|  |   if (ret != 0) | ||||||
|  |     free_page(key); | ||||||
|  |   return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int hash_erase_dentry(struct dentry *dentry) { | ||||||
|  |   struct dentry_key key = {.parent = dentry->parent, .name = dentry->name}; | ||||||
|  |   return dentry_hash_table.virtual_hash_erase(&dentry_hash_table, &key); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // vinode generic hash table method implementation
 | ||||||
|  | int vinode_hash_equal(void *key1, void *key2) { | ||||||
|  |   struct vinode_key *vinode_key1 = key1; | ||||||
|  |   struct vinode_key *vinode_key2 = key2; | ||||||
|  |   if (vinode_key1->inum == vinode_key2->inum && vinode_key1->sb == vinode_key2->sb) { | ||||||
|  |     return 1; | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t vinode_hash_func(void *key) { | ||||||
|  |   struct vinode_key *vinode_key = key; | ||||||
|  |   return vinode_key->inum % HASH_TABLE_SIZE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // vinode hash table interface
 | ||||||
|  | struct vinode *hash_get_vinode(struct super_block *sb, int inum) { | ||||||
|  |   if (inum < 0) return NULL; | ||||||
|  |   struct vinode_key key = {.sb = sb, .inum = inum}; | ||||||
|  |   return (struct vinode *)vinode_hash_table.virtual_hash_get(&vinode_hash_table, | ||||||
|  |                                                              &key); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int hash_put_vinode(struct vinode *vinode) { | ||||||
|  |   if (vinode->inum < 0) return -1; | ||||||
|  |   struct vinode_key *key = alloc_page(); | ||||||
|  |   key->sb = vinode->sb; | ||||||
|  |   key->inum = vinode->inum; | ||||||
|  | 
 | ||||||
|  |   int ret = vinode_hash_table.virtual_hash_put(&vinode_hash_table, key, vinode); | ||||||
|  |   if (ret != 0) free_page(key); | ||||||
|  |   return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int hash_erase_vinode(struct vinode *vinode) { | ||||||
|  |   if (vinode->inum < 0) return -1; | ||||||
|  |   struct vinode_key key = {.sb = vinode->sb, .inum = vinode->inum}; | ||||||
|  |   return vinode_hash_table.virtual_hash_erase(&vinode_hash_table, &key); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
|  | // shared (default) actions on allocating a vfs inode.
 | ||||||
|  | //
 | ||||||
|  | struct vinode *default_alloc_vinode(struct super_block *sb) { | ||||||
|  |   struct vinode *vinode = (struct vinode *)alloc_page(); | ||||||
|  |   vinode->blocks = 0; | ||||||
|  |   vinode->inum = 0; | ||||||
|  |   vinode->nlinks = 0; | ||||||
|  |   vinode->ref = 0; | ||||||
|  |   vinode->sb = sb; | ||||||
|  |   vinode->size = 0; | ||||||
|  |   return vinode; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct file_system_type *fs_list[MAX_SUPPORTED_FS]; | ||||||
| @ -0,0 +1,189 @@ | |||||||
|  | #ifndef _VFS_H_ | ||||||
|  | #define _VFS_H_ | ||||||
|  | 
 | ||||||
|  | #include "util/types.h" | ||||||
|  | 
 | ||||||
|  | #define MAX_VFS_DEV 10            // the maximum number of vfs_dev_list
 | ||||||
|  | #define MAX_DENTRY_NAME_LEN 30    // the maximum length of dentry name
 | ||||||
|  | #define MAX_DEVICE_NAME_LEN 30    // the maximum length of device name
 | ||||||
|  | #define MAX_MOUNTS 10             // the maximum number of mounts
 | ||||||
|  | #define MAX_DENTRY_HASH_SIZE 100  // the maximum size of dentry hash table
 | ||||||
|  | #define MAX_PATH_LEN 30           // the maximum length of path
 | ||||||
|  | #define MAX_SUPPORTED_FS 10       // the maximum number of supported file systems
 | ||||||
|  | 
 | ||||||
|  | #define DIRECT_BLKNUM 10          // the number of direct blocks
 | ||||||
|  | 
 | ||||||
|  | /**** vfs initialization function ****/ | ||||||
|  | int vfs_init(); | ||||||
|  | 
 | ||||||
|  | /**** vfs interfaces ****/ | ||||||
|  | 
 | ||||||
|  | // device interfaces
 | ||||||
|  | struct super_block *vfs_mount(const char *dev_name, int mnt_type); | ||||||
|  | 
 | ||||||
|  | // file interfaces
 | ||||||
|  | struct file *vfs_open(const char *path, int flags); | ||||||
|  | ssize_t vfs_read(struct file *file, char *buf, size_t count); | ||||||
|  | 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_close(struct file *file); | ||||||
|  | 
 | ||||||
|  | /**** vfs abstract object types ****/ | ||||||
|  | // system root direntry
 | ||||||
|  | extern struct dentry *vfs_root_dentry; | ||||||
|  | 
 | ||||||
|  | // vfs abstract dentry
 | ||||||
|  | struct dentry { | ||||||
|  |   char name[MAX_DENTRY_NAME_LEN]; | ||||||
|  |   int d_ref; | ||||||
|  |   struct vinode *dentry_inode; | ||||||
|  |   struct dentry *parent; | ||||||
|  |   struct super_block *sb; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // dentry constructor and destructor
 | ||||||
|  | struct dentry *alloc_vfs_dentry(const char *name, struct vinode *inode, | ||||||
|  |                             struct dentry *parent); | ||||||
|  | int free_vfs_dentry(struct dentry *dentry); | ||||||
|  | 
 | ||||||
|  | // ** dentry hash table **
 | ||||||
|  | extern struct hash_table dentry_hash_table; | ||||||
|  | 
 | ||||||
|  | // dentry hash table key type
 | ||||||
|  | struct dentry_key { | ||||||
|  |   struct dentry *parent; | ||||||
|  |   char *name; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // generic hash table method implementation
 | ||||||
|  | int dentry_hash_equal(void *key1, void *key2); | ||||||
|  | size_t dentry_hash_func(void *key); | ||||||
|  | 
 | ||||||
|  | // dentry hash table interface
 | ||||||
|  | struct dentry *hash_get_dentry(struct dentry *parent, char *name); | ||||||
|  | int hash_put_dentry(struct dentry *dentry); | ||||||
|  | int hash_erase_dentry(struct dentry *dentry); | ||||||
|  | 
 | ||||||
|  | // data structure of an openned file
 | ||||||
|  | struct file { | ||||||
|  |   int status; | ||||||
|  |   int readable; | ||||||
|  |   int writable; | ||||||
|  |   int offset; | ||||||
|  |   struct dentry *f_dentry; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // file constructor and destructor(use free_page to destruct)
 | ||||||
|  | struct file *alloc_vfs_file(struct dentry *dentry, int readable, int writable, | ||||||
|  |                         int offset); | ||||||
|  | 
 | ||||||
|  | // abstract device entry in vfs_dev_list
 | ||||||
|  | struct device { | ||||||
|  |   char dev_name[MAX_DEVICE_NAME_LEN];  // the name of the device
 | ||||||
|  |   int dev_id;  // the id of the device (the meaning of an id is interpreted by
 | ||||||
|  |                // the specific file system, all we need to know is that it is
 | ||||||
|  |                // a unique identifier)
 | ||||||
|  |   struct file_system_type *fs_type;  // the file system type in the device
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // device list in vfs layer
 | ||||||
|  | extern struct device *vfs_dev_list[MAX_VFS_DEV]; | ||||||
|  | 
 | ||||||
|  | // supported file system types
 | ||||||
|  | struct file_system_type { | ||||||
|  |   int type_num;  // the number of the file system type
 | ||||||
|  |   struct super_block *(*get_superblock)(struct device *dev); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | extern struct file_system_type *fs_list[MAX_SUPPORTED_FS]; | ||||||
|  | 
 | ||||||
|  | // general-purpose super_block structure
 | ||||||
|  | struct super_block { | ||||||
|  |   int magic;              // magic number of the file system
 | ||||||
|  |   int size;               // size of file system image (blocks)
 | ||||||
|  |   int nblocks;            // number of data blocks
 | ||||||
|  |   int ninodes;            // number of inodes.
 | ||||||
|  |   struct dentry *s_root;  // root dentry of inode
 | ||||||
|  |   struct device *s_dev;   // device of the superblock
 | ||||||
|  |   void *s_fs_info;        // filesystem-specific info. for rfs, it points bitmap
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // abstract vfs inode
 | ||||||
|  | struct vinode { | ||||||
|  |   int inum;                  // inode number of the disk inode
 | ||||||
|  |   int ref;                   // reference count
 | ||||||
|  |   int size;                  // size of the file (in bytes)
 | ||||||
|  |   int type;                  // one of FILE_I, DIR_I
 | ||||||
|  |   int nlinks;                // number of hard links to this file
 | ||||||
|  |   int blocks;                // number of blocks
 | ||||||
|  |   int addrs[DIRECT_BLKNUM];  // direct blocks
 | ||||||
|  |   void *i_fs_info;           // filesystem-specific info (see s_fs_info)
 | ||||||
|  |   struct super_block *sb;          // super block of the vfs inode
 | ||||||
|  |   const struct vinode_ops *i_ops;  // vfs inode operations
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct vinode_ops { | ||||||
|  |   // file operations
 | ||||||
|  |   ssize_t (*viop_read)(struct vinode *node, char *buf, ssize_t len, | ||||||
|  |                        int *offset); | ||||||
|  |   ssize_t (*viop_write)(struct vinode *node, const char *buf, ssize_t len, | ||||||
|  |                         int *offset); | ||||||
|  |   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); | ||||||
|  |   struct vinode *(*viop_lookup)(struct vinode *parent, | ||||||
|  |                                 struct dentry *sub_dentry); | ||||||
|  | 
 | ||||||
|  |   // write back inode to disk
 | ||||||
|  |   int (*viop_write_back_vinode)(struct vinode *node); | ||||||
|  | 
 | ||||||
|  |   // hook functions
 | ||||||
|  |   // In the vfs layer, we do not assume that hook functions will do anything,
 | ||||||
|  |   // but simply call them (when they are defined) at the appropriate time.
 | ||||||
|  |   // Hook functions exist because the fs layer may need to do some additional
 | ||||||
|  |   // operations (such as allocating additional data structures) at some critical
 | ||||||
|  |   // times.
 | ||||||
|  |   int (*viop_hook_open)(struct vinode *node, struct dentry *dentry); | ||||||
|  |   int (*viop_hook_close)(struct vinode *node, struct dentry *dentry); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // vinode operation interface
 | ||||||
|  | // the implementation depends on the vinode type and the specific file system
 | ||||||
|  | 
 | ||||||
|  | // virtual file system inode interfaces
 | ||||||
|  | #define viop_read(node, buf, len, offset)      (node->i_ops->viop_read(node, buf, len, offset)) | ||||||
|  | #define viop_write(node, buf, len, offset)     (node->i_ops->viop_write(node, buf, len, offset)) | ||||||
|  | #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_lookup(parent, sub_dentry)        (parent->i_ops->viop_lookup(parent, sub_dentry)) | ||||||
|  | #define viop_write_back_vinode(node)           (node->i_ops->viop_write_back_vinode(node)) | ||||||
|  | 
 | ||||||
|  | // vinode hash table
 | ||||||
|  | extern struct hash_table vinode_hash_table; | ||||||
|  | 
 | ||||||
|  | // vinode hash table key type
 | ||||||
|  | struct vinode_key { | ||||||
|  |   int inum; | ||||||
|  |   struct super_block *sb; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // generic hash table method implementation
 | ||||||
|  | int vinode_hash_equal(void *key1, void *key2); | ||||||
|  | size_t vinode_hash_func(void *key); | ||||||
|  | 
 | ||||||
|  | // vinode hash table interface
 | ||||||
|  | struct vinode *hash_get_vinode(struct super_block *sb, int inum); | ||||||
|  | int hash_put_vinode(struct vinode *vinode); | ||||||
|  | int hash_erase_vinode(struct vinode *vinode); | ||||||
|  | 
 | ||||||
|  | // other utility functions
 | ||||||
|  | struct vinode *default_alloc_vinode(struct super_block *sb); | ||||||
|  | struct dentry *lookup_final_dentry(const char *path, struct dentry **parent, | ||||||
|  |                                    char *miss_name); | ||||||
|  | void get_base_name(const char *path, char *base_name); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| @ -0,0 +1,63 @@ | |||||||
|  | #include "user_lib.h" | ||||||
|  | #include "util/string.h" | ||||||
|  | #include "util/types.h" | ||||||
|  | 
 | ||||||
|  | int main(int argc, char *argv[]) { | ||||||
|  |   int fd; | ||||||
|  |   int MAXBUF = 512; | ||||||
|  |   char buf[MAXBUF]; | ||||||
|  |   char str[] = "hello world"; | ||||||
|  |   int fd1, fd2; | ||||||
|  | 
 | ||||||
|  |   printu("\n======== Test 1: read host file  ========\n"); | ||||||
|  |   printu("read: /hostfile.txt\n"); | ||||||
|  | 
 | ||||||
|  |   fd = open("/hostfile.txt", O_RDONLY); | ||||||
|  |   printu("file descriptor fd: %d\n", fd); | ||||||
|  | 
 | ||||||
|  |   read_u(fd, buf, MAXBUF); | ||||||
|  |   printu("read content: \n%s\n", buf); | ||||||
|  | 
 | ||||||
|  |   close(fd); | ||||||
|  | 
 | ||||||
|  |   printu("\n======== Test 2: create/write rfs file ========\n"); | ||||||
|  |   printu("write: /RAMDISK0/ramfile\n"); | ||||||
|  | 
 | ||||||
|  |   fd = open("/RAMDISK0/ramfile", O_RDWR | O_CREAT); | ||||||
|  |   printu("file descriptor fd: %d\n", fd); | ||||||
|  | 
 | ||||||
|  |   write_u(fd, buf, strlen(buf)); | ||||||
|  |   printu("write content: \n%s\n", buf); | ||||||
|  |   close(fd); | ||||||
|  | 
 | ||||||
|  |   printu("\n======== Test 3: read rfs file ========\n"); | ||||||
|  |   printu("read: /RAMDISK0/ramfile\n"); | ||||||
|  | 
 | ||||||
|  |   fd = open("/RAMDISK0/ramfile", O_RDWR); | ||||||
|  |   printu("file descriptor fd: %d\n", fd); | ||||||
|  | 
 | ||||||
|  |   read_u(fd, buf, MAXBUF); | ||||||
|  |   printu("read content: \n%s\n", buf); | ||||||
|  |   close(fd); | ||||||
|  | 
 | ||||||
|  |   printu("\n======== Test 4: open twice ========\n"); | ||||||
|  | 
 | ||||||
|  |   fd1 = open("/RAMDISK0/ramfile", O_RDWR | O_CREAT); | ||||||
|  |   fd2 = open("/RAMDISK0/ramfile", O_RDWR | O_CREAT); | ||||||
|  | 
 | ||||||
|  |   printu("file descriptor fd1(ramfile): %d\n", fd1); | ||||||
|  |   printu("file descriptor fd2(ramfile): %d\n", fd2); | ||||||
|  | 
 | ||||||
|  |   write_u(fd1, str, strlen(str)); | ||||||
|  |   printu("write content: \n%s\n", str); | ||||||
|  | 
 | ||||||
|  |   read_u(fd2, buf, MAXBUF); | ||||||
|  |   printu("read content: \n%s\n", buf); | ||||||
|  | 
 | ||||||
|  |   close(fd1); | ||||||
|  |   close(fd2); | ||||||
|  | 
 | ||||||
|  |   printu("\nAll tests passed!\n\n"); | ||||||
|  |   exit(0); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
| @ -1,28 +0,0 @@ | |||||||
| /*
 |  | ||||||
|  * The application of lab3_3. |  | ||||||
|  * parent and child processes never give up their processor during execution. |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| #include "user/user_lib.h" |  | ||||||
| #include "util/types.h" |  | ||||||
| 
 |  | ||||||
| int main(void) { |  | ||||||
|   uint64 pid = fork(); |  | ||||||
|   uint64 rounds = 100000000; |  | ||||||
|   uint64 interval = 10000000; |  | ||||||
|   uint64 a = 0; |  | ||||||
|   if (pid == 0) { |  | ||||||
|     printu("Child: Hello world! \n"); |  | ||||||
|     for (uint64 i = 0; i < rounds; ++i) { |  | ||||||
|       if (i % interval == 0) printu("Child running %ld \n", i); |  | ||||||
|     } |  | ||||||
|   } else { |  | ||||||
|     printu("Parent: Hello world! \n"); |  | ||||||
|     for (uint64 i = 0; i < rounds; ++i) { |  | ||||||
|       if (i % interval == 0) printu("Parent running %ld \n", i); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   exit(0); |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
| @ -0,0 +1,60 @@ | |||||||
|  | #include "util/hash_table.h" | ||||||
|  | #include "util/types.h" | ||||||
|  | #include "kernel/pmm.h" | ||||||
|  | 
 | ||||||
|  | static int default_equal(void *key1, void *key2) { return key1 == key2; } | ||||||
|  | 
 | ||||||
|  | static int default_put(struct hash_table *hash_table, void *key, void *value) { | ||||||
|  |   struct hash_node *node = (struct hash_node *)alloc_page(); | ||||||
|  |   if (hash_table->virtual_hash_get(hash_table, key) != NULL) return -1; | ||||||
|  |   node->key = key; | ||||||
|  |   node->value = value; | ||||||
|  | 
 | ||||||
|  |   size_t index = hash_table->virtual_hash_func(key); | ||||||
|  |   struct hash_node *head = hash_table->head + index; | ||||||
|  | 
 | ||||||
|  |   node->next = head->next; | ||||||
|  |   head->next = node; | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void *defalut_get(struct hash_table *hash_table, void *key) { | ||||||
|  |   size_t index = hash_table->virtual_hash_func(key); | ||||||
|  |   struct hash_node *head = hash_table->head + index; | ||||||
|  |   struct hash_node *node = head->next; | ||||||
|  |   while (node) { | ||||||
|  |     if (hash_table->virtual_hash_equal(node->key, key)) return node->value; | ||||||
|  |     node = node->next; | ||||||
|  |   } | ||||||
|  |   return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int default_erase(struct hash_table *hash_table, void *key) { | ||||||
|  |   size_t index = hash_table->virtual_hash_func(key); | ||||||
|  |   struct hash_node *head = hash_table->head + index; | ||||||
|  |   while (head->next && !hash_table->virtual_hash_equal(head->next->key, key)) | ||||||
|  |     head = head->next; | ||||||
|  |   if (head->next) { | ||||||
|  |     struct hash_node *node = head->next; | ||||||
|  |     head->next = node->next; | ||||||
|  |     free_page(node); | ||||||
|  |     return 0; | ||||||
|  |   } else | ||||||
|  |     return -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int hash_table_init(struct hash_table *list, | ||||||
|  |                    int (*equal)(void *key1, void *key2), | ||||||
|  |                    size_t (*func)(void *key), | ||||||
|  |                    int (*put)(struct hash_table *hash_table, void *key, void *value), | ||||||
|  |                    void *(*get)(struct hash_table *hash_table, void *key), | ||||||
|  |                    int (*erase)(struct hash_table *hash_table, void *key)) { | ||||||
|  |   for (int i = 0; i < HASH_TABLE_SIZE; i++) list->head[i].next = NULL; | ||||||
|  |   if (func == NULL) return -1; | ||||||
|  |   list->virtual_hash_func = func; | ||||||
|  |   list->virtual_hash_equal = equal ? equal : default_equal; | ||||||
|  |   list->virtual_hash_put = put ? put : default_put; | ||||||
|  |   list->virtual_hash_get = get ? get : defalut_get; | ||||||
|  |   list->virtual_hash_erase = erase ? erase : default_erase; | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
| @ -0,0 +1,29 @@ | |||||||
|  | #ifndef _HASH_TABLE_H | ||||||
|  | #define _HASH_TABLE_H | ||||||
|  | #include "util/types.h" | ||||||
|  | 
 | ||||||
|  | #define HASH_TABLE_SIZE 128 | ||||||
|  | 
 | ||||||
|  | struct hash_node { | ||||||
|  |   struct hash_node *next; | ||||||
|  |   void *key; | ||||||
|  |   void *value; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // this is a generic hash linked table for KERNEL SPACE
 | ||||||
|  | struct hash_table { | ||||||
|  |   struct hash_node head[HASH_TABLE_SIZE]; | ||||||
|  |   int (*virtual_hash_equal)(void *key1, void *key2); | ||||||
|  |   size_t (*virtual_hash_func)(void *key); | ||||||
|  |   int (*virtual_hash_put)(struct hash_table *hash_table, void *key, void *value); | ||||||
|  |   void *(*virtual_hash_get)(struct hash_table *hash_table, void *key); | ||||||
|  |   int (*virtual_hash_erase)(struct hash_table *hash_table, void *key); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | int hash_table_init(struct hash_table *list, int (*virtual_hash_equal)(void *key1, void *key2), | ||||||
|  |                    size_t (*virtual_hash_func)(void *key), | ||||||
|  |                    int (*virtual_hash_put)(struct hash_table *hash_table, void *key, void *value), | ||||||
|  |                    void *(*virtual_hash_get)(struct hash_table *hash_table, void *key), | ||||||
|  |                    int (*virtual_hash_erase)(struct hash_table *hash_table, void *key)); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
					Loading…
					
					
				
		Reference in new issue