init commit of lab4_1

lab4_1_file
Zhiyuan Shao 2 years ago
parent 448d994bba
commit bbcac5cf62

@ -4,9 +4,12 @@ Copyright License
The PKE software is:
Copyright (c) 2021, Zhiyuan Shao (zyshao@hust.edu.cn),
Yi Gui (gy163email@163.com),
Yan Jiao (773709579@qq.com),
Guo Li (2925441676@qq.com),
Huazhong University of Science and Technology
Liang Shi (lshi@cs.ecnu.edu.cn),
Longshan Xu (1981888213@qq.com),
Yangxue Ou (3386215144@qq.com),
East China Normal University
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

@ -68,9 +68,7 @@ USER_CPPS := user/*.c
USER_CPPS := $(wildcard $(USER_CPPS))
USER_OBJS := $(addprefix $(OBJ_DIR)/, $(patsubst %.c,%.o,$(USER_CPPS)))
USER_TARGET := $(OBJ_DIR)/app_two_long_loops
USER_TARGET := $(OBJ_DIR)/app_file
#------------------------targets------------------------
$(OBJ_DIR):
@-mkdir -p $(OBJ_DIR)

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

@ -11,6 +11,10 @@
#include "sched.h"
#include "memlayout.h"
#include "spike_interface/spike_utils.h"
#include "util/types.h"
#include "vfs.h"
#include "rfs.h"
#include "ramdev.h"
//
// trap_sec_start points to the beginning of S-mode trap segment (i.e., the entry point of
@ -67,6 +71,9 @@ int s_start(void) {
// added @lab3_1
init_proc_pool();
// init file system, added @lab4_1
fs_init();
sprint("Switch to user mode...\n");
// the application code (elf) is first loaded into memory, and then put into execution
// added @lab3_1

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

@ -151,6 +151,10 @@ process* alloc_process() {
procs[i].total_mapped_region = 4;
// initialize files_struct
procs[i].pfiles = init_proc_file_management();
sprint("in alloc_proc. build proc_file_management successfully.\n");
// return after initialization.
return &procs[i];
}

@ -2,6 +2,7 @@
#define _PROC_H_
#include "riscv.h"
#include "proc_file.h"
typedef struct trapframe_t {
// space to store context (all common registers)
@ -89,6 +90,9 @@ typedef struct process_t {
// accounting. added @lab3_3
int tick_count;
// file system. added @lab4_1
proc_file_management *pfiles;
}process;
// switch to run user app

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

@ -13,6 +13,7 @@
#include "pmm.h"
#include "vmm.h"
#include "sched.h"
#include "proc_file.h"
#include "spike_interface/spike_utils.h"
@ -94,6 +95,76 @@ ssize_t sys_user_yield() {
return 0;
}
//
// open file
//
ssize_t sys_user_open(char *pathva, int flags) {
char* pathpa = (char*)user_va_to_pa((pagetable_t)(current->pagetable), pathva);
return do_open(pathpa, flags);
}
//
// read file
//
ssize_t sys_user_read(int fd, char *bufva, uint64 count) {
int i = 0;
while (i < count) { // count can be greater than page size
uint64 addr = (uint64)bufva + i;
uint64 pa = lookup_pa((pagetable_t)current->pagetable, addr);
uint64 off = addr - ROUNDDOWN(addr, PGSIZE);
uint64 len = count - i < PGSIZE - off ? count - i : PGSIZE - off;
uint64 r = do_read(fd, (char *)pa + off, len);
i += r; if (r < len) return i;
}
return count;
}
//
// write file
//
ssize_t sys_user_write(int fd, char *bufva, uint64 count) {
int i = 0;
while (i < count) { // count can be greater than page size
uint64 addr = (uint64)bufva + i;
uint64 pa = lookup_pa((pagetable_t)current->pagetable, addr);
uint64 off = addr - ROUNDDOWN(addr, PGSIZE);
uint64 len = count - i < PGSIZE - off ? count - i : PGSIZE - off;
uint64 r = do_write(fd, (char *)pa + off, len);
i += r; if (r < len) return i;
}
return count;
}
//
// lseek file
//
ssize_t sys_user_lseek(int fd, int offset, int whence) {
return do_lseek(fd, offset, whence);
}
//
// read vinode
//
ssize_t sys_user_stat(int fd, struct istat *istat) {
struct istat * pistat = (struct istat *)user_va_to_pa((pagetable_t)(current->pagetable), istat);
return do_stat(fd, pistat);
}
//
// read disk inode
//
ssize_t sys_user_disk_stat(int fd, struct istat *istat) {
struct istat * pistat = (struct istat *)user_va_to_pa((pagetable_t)(current->pagetable), istat);
return do_disk_stat(fd, pistat);
}
//
// close file
//
ssize_t sys_user_close(int fd) {
return do_close(fd);
}
//
// [a0]: the syscall number; [a1] ... [a7]: arguments to the syscalls.
// returns the code of success, (e.g., 0 means success, fail for otherwise)
@ -113,6 +184,21 @@ long do_syscall(long a0, long a1, long a2, long a3, long a4, long a5, long a6, l
return sys_user_fork();
case SYS_user_yield:
return sys_user_yield();
// added @lab4_1
case SYS_user_open:
return sys_user_open((char *)a1, a2);
case SYS_user_read:
return sys_user_read(a1, (char *)a2, a3);
case SYS_user_write:
return sys_user_write(a1, (char *)a2, a3);
case SYS_user_lseek:
return sys_user_lseek(a1, a2, a3);
case SYS_user_stat:
return sys_user_stat(a1, (struct istat *)a2);
case SYS_user_disk_stat:
return sys_user_disk_stat(a1, (struct istat *)a2);
case SYS_user_close:
return sys_user_close(a1);
default:
panic("Unknown syscall %ld \n", a0);
}

@ -14,6 +14,14 @@
// added @lab3_1
#define SYS_user_fork (SYS_user_base + 4)
#define SYS_user_yield (SYS_user_base + 5)
// added @lab4_1
#define SYS_user_open (SYS_user_base + 17)
#define SYS_user_read (SYS_user_base + 18)
#define SYS_user_write (SYS_user_base + 19)
#define SYS_user_lseek (SYS_user_base + 20)
#define SYS_user_stat (SYS_user_base + 21)
#define SYS_user_disk_stat (SYS_user_base + 22)
#define SYS_user_close (SYS_user_base + 23)
long do_syscall(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7);

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

@ -14,8 +14,6 @@
#include "spike_interface/spike_utils.h"
//#include "../kernel/config.h"
#define MAX_FILES 128
#define MAX_FDS 128
static spike_file_t* spike_fds[MAX_FDS];
spike_file_t spike_files[MAX_FILES] = {[0 ... MAX_FILES - 1] = {-1, 0}};
@ -128,3 +126,18 @@ ssize_t spike_file_read(spike_file_t* f, void* buf, size_t size) {
ssize_t spike_file_lseek(spike_file_t* f, size_t ptr, int dir) {
return frontend_syscall(HTIFSYS_lseek, f->kfd, ptr, dir, 0, 0, 0, 0);
}
spike_file_t* spike_file_get(int fd) {
spike_file_t* f;
if (fd < 0 || fd >= MAX_FDS || (f = atomic_read(&spike_fds[fd])) == NULL)
return 0;
long old_cnt;
do {
old_cnt = atomic_read(&f->refcnt);
if (old_cnt == 0)
return 0;
} while (atomic_cas(&f->refcnt, old_cnt, old_cnt+1) != old_cnt);
return f;
}

@ -6,16 +6,16 @@
#include "util/types.h"
typedef struct file {
typedef struct file_t {
int kfd; // file descriptor of the host file
uint32 refcnt;
} spike_file_t;
extern spike_file_t spike_files[];
#define O_RDONLY 00
#define O_WRONLY 01
#define O_RDWR 02
#define MAX_FILES 128
#define MAX_FDS 128
#define ENOMEM 12 /* Out of memory */
#define stdin (spike_files + 0)
@ -60,5 +60,6 @@ void spike_file_init(void);
int spike_file_dup(spike_file_t* f);
int spike_file_truncate(spike_file_t* f, off_t len);
int spike_file_stat(spike_file_t* f, struct stat* s);
spike_file_t* spike_file_get(int fd);
#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;
}

@ -76,3 +76,52 @@ int fork() {
void yield() {
do_user_call(SYS_user_yield, 0, 0, 0, 0, 0, 0, 0);
}
//
// lib call to open
//
int open(const char *pathname, int flags) {
return do_user_call(SYS_user_open, (uint64)pathname, flags, 0, 0, 0, 0, 0);
}
//
// lib call to read
//
int read_u(int fd, void * buf, uint64 count){
return do_user_call(SYS_user_read, fd, (uint64)buf, count, 0, 0, 0, 0);
}
//
// lib call to write
//
int write_u(int fd, void *buf, uint64 count) {
return do_user_call(SYS_user_write, fd, (uint64)buf, count, 0, 0, 0, 0);
}
//
// lib call to seek
//
int lseek_u(int fd, int offset, int whence) {
return do_user_call(SYS_user_lseek, fd, offset, whence, 0, 0, 0, 0);
}
//
// lib call to read file information
//
int stat_u(int fd, struct istat *istat) {
return do_user_call(SYS_user_stat, fd, (uint64)istat, 0, 0, 0, 0, 0);
}
//
// lib call to read file information from disk
//
int disk_stat_u(int fd, struct istat *istat) {
return do_user_call(SYS_user_disk_stat, fd, (uint64)istat, 0, 0, 0, 0, 0);
}
//
// lib call to close
//
int close(int fd) {
return do_user_call(SYS_user_close, fd, 0, 0, 0, 0, 0, 0);
}

@ -2,9 +2,25 @@
* header file to be used by applications.
*/
#ifndef _USER_LIB_H_
#define _USER_LIB_H_
#include "util/types.h"
#include "kernel/proc_file.h"
int printu(const char *s, ...);
int exit(int code);
void* naive_malloc();
void naive_free(void* va);
int fork();
void yield();
// added @ lab4_1
int open(const char *pathname, int flags);
int read_u(int fd, void *buf, uint64 count);
int write_u(int fd, void *buf, uint64 count);
int lseek_u(int fd, int offset, int whence);
int stat_u(int fd, struct istat *istat);
int disk_stat_u(int fd, struct istat *istat);
int close(int fd);
#endif

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

@ -1,10 +1,10 @@
// See LICENSE for license details.
#include <stdint.h>
#include <ctype.h>
#include "string.h"
#include <ctype.h>
#include <stdint.h>
void* memcpy(void* dest, const void* src, size_t len) {
const char* s = src;
char* d = dest;
@ -62,6 +62,47 @@ char* strcpy(char* dest, const char* src) {
return dest;
}
char *strchr(const char *p, int ch)
{
char c;
c = ch;
for (;; ++p) {
if (*p == c)
return ((char *)p);
if (*p == '\0')
return (NULL);
}
}
char* strtok(char* str, const char* delim) {
static char* current;
if (str != NULL) current = str;
if (current == NULL) return NULL;
char* start = current;
while (*start != '\0' && strchr(delim, *start) != NULL) start++;
if (*start == '\0') {
current = NULL;
return current;
}
char* end = start;
while (*end != '\0' && strchr(delim, *end) == NULL) end++;
if (*end != '\0') {
*end = '\0';
current = end + 1;
} else
current = NULL;
return start;
}
char *strcat(char *dst, const char *src) {
strcpy(dst + strlen(dst), src);
return dst;
}
long atol(const char* str) {
long res = 0;
int sign = 0;
@ -107,4 +148,4 @@ char* safestrcpy(char* s, const char* t, int n) {
;
*s = 0;
return os;
}
}

@ -3,13 +3,16 @@
#include <stddef.h>
void* memcpy(void* dest, const void* src, size_t len);
void* memset(void* dest, int byte, size_t len);
void *memcpy(void* dest, const void* src, size_t len);
void *memset(void* dest, int byte, size_t len);
size_t strlen(const char* s);
int strcmp(const char* s1, const char* s2);
char* strcpy(char* dest, const char* src);
char *strcpy(char* dest, const char* src);
char *strchr(const char *p, int ch);
char *strtok(char* str, const char* delim);
char *strcat(char *dst, const char *src);
long atol(const char* str);
void* memmove(void* dst, const void* src, size_t n);
char* safestrcpy(char* s, const char* t, int n);
void *memmove(void* dst, const void* src, size_t n);
char *safestrcpy(char* s, const char* t, int n);
#endif
#endif

@ -16,8 +16,38 @@ typedef int bool;
typedef signed long ssize_t;
typedef unsigned long size_t;
#define NULL ((void *)0)
#define TRUE 1
#define FALSE 0
#define LSEEK_SET 0
#define LSEEK_CUR 1
#define MOUNT_DEFAULT 0
#define MOUNT_AS_ROOT 1
#define FILE_I 0
#define DIR_I 1
#define MASK_FILEMODE 0x003
#define O_RDONLY 00 // read-only access
#define O_WRONLY 01 // write-only access
#define O_RDWR 02 // read-write
#define O_CREAT 0100 // create
#define FD_NONE 0
#define FD_OPENED 1
#define MAX_FILE_NAME_LEN 32
struct istat {
int st_inum;
int st_size;
int st_type;
int st_nlinks;
int st_blocks;
};
#endif

Loading…
Cancel
Save