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