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