|
|
|
/*
|
|
|
|
* 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_link = rfs_link,
|
|
|
|
.viop_unlink = rfs_unlink,
|
|
|
|
.viop_lookup = rfs_lookup,
|
|
|
|
|
|
|
|
.viop_readdir = rfs_readdir,
|
|
|
|
.viop_mkdir = rfs_mkdir,
|
|
|
|
|
|
|
|
.viop_write_back_vinode = rfs_write_back_vinode,
|
|
|
|
|
|
|
|
.viop_hook_opendir = rfs_hook_opendir,
|
|
|
|
.viop_hook_closedir = rfs_hook_closedir,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**** 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// create a hard link under a direntry "parent" for an existing file of "link_node"
|
|
|
|
//
|
|
|
|
int rfs_link(struct vinode *parent, struct dentry *sub_dentry, struct vinode *link_node) {
|
|
|
|
// TODO (lab4_3): we now need to establish a hard link to an existing file whose vfs
|
|
|
|
// inode is "link_node". To do that, we need first to know the name of the new (link)
|
|
|
|
// file, and then, we need to increase the link count of the existing file. Lastly,
|
|
|
|
// we need to make the changes persistent to disk. To know the name of the new (link)
|
|
|
|
// file, you need to stuty the structure of dentry, that contains the name member;
|
|
|
|
// To incease the link count of the existing file, you need to study the structure of
|
|
|
|
// vfs inode, since it contains the inode information of the existing file.
|
|
|
|
//
|
|
|
|
// hint: to accomplish this experiment, you need to:
|
|
|
|
// 1) increase the link count of the file to be hard-linked;
|
|
|
|
// 2) append the new (link) file as a dentry to its parent directory; you can use
|
|
|
|
// rfs_add_direntry here.
|
|
|
|
// 3) persistent the changes to disk. you can use rfs_write_back_vinode here.
|
|
|
|
//
|
|
|
|
panic("You need to implement the code for creating a hard link in lab4_3.\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// remove a hard link with "name" under a direntry "parent"
|
|
|
|
//
|
|
|
|
int rfs_unlink(struct vinode *parent, struct dentry *sub_dentry, struct vinode *unlink_vinode) {
|
|
|
|
struct rfs_device *rdev = rfs_device_list[parent->sb->s_dev->dev_id];
|
|
|
|
|
|
|
|
// ** find the direntry in the directory file
|
|
|
|
int total_direntrys = parent->size / sizeof(struct rfs_direntry);
|
|
|
|
int one_block_direntrys = RFS_BLKSIZE / sizeof(struct rfs_direntry);
|
|
|
|
|
|
|
|
struct rfs_direntry *p_direntry = NULL;
|
|
|
|
int delete_index;
|
|
|
|
for (delete_index = 0; delete_index < total_direntrys; ++delete_index) {
|
|
|
|
// read in the disk block at boundary
|
|
|
|
if (delete_index % one_block_direntrys == 0) {
|
|
|
|
rfs_r1block(rdev, parent->addrs[delete_index / one_block_direntrys]);
|
|
|
|
p_direntry = (struct rfs_direntry *)rdev->iobuffer;
|
|
|
|
}
|
|
|
|
if (strcmp(p_direntry->name, sub_dentry->name) == 0) { // found
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++p_direntry;
|
|
|
|
}
|
|
|
|
|
|
|
|
int inum = p_direntry->inum;
|
|
|
|
|
|
|
|
if (delete_index == total_direntrys) {
|
|
|
|
sprint("unlink: file %s not found.\n", sub_dentry->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ** read the disk inode of the file to be unlinked
|
|
|
|
struct rfs_dinode *unlink_dinode = rfs_read_dinode(rdev, inum);
|
|
|
|
|
|
|
|
// if this assertion fails, it indicates that the previous modification to nlinks
|
|
|
|
// was not written back to disk, which is not allowed
|
|
|
|
assert(unlink_vinode->nlinks == unlink_dinode->nlinks);
|
|
|
|
|
|
|
|
// ** decrease vinode nlinks by 1
|
|
|
|
unlink_vinode->nlinks--;
|
|
|
|
|
|
|
|
// ** update disk inode nlinks
|
|
|
|
unlink_dinode->nlinks = unlink_vinode->nlinks;
|
|
|
|
|
|
|
|
// ** if nlinks == 0, free the disk inode and disk blocks
|
|
|
|
if (unlink_dinode->nlinks == 0) {
|
|
|
|
// free disk blocks
|
|
|
|
for (int i = 0; i < unlink_dinode->blocks; ++i) {
|
|
|
|
rfs_free_block(parent->sb, unlink_dinode->addrs[i]);
|
|
|
|
}
|
|
|
|
// free disk inode
|
|
|
|
unlink_dinode->type = R_FREE;
|
|
|
|
}
|
|
|
|
// ** write the disk inode back to disk
|
|
|
|
rfs_write_dinode(rdev, unlink_dinode, inum);
|
|
|
|
free_page(unlink_dinode);
|
|
|
|
|
|
|
|
// ** remove the direntry from the directory
|
|
|
|
|
|
|
|
// handle the first block
|
|
|
|
int delete_block_index = delete_index / one_block_direntrys;
|
|
|
|
rfs_r1block(rdev, parent->addrs[delete_block_index]);
|
|
|
|
|
|
|
|
int offset = delete_index % one_block_direntrys;
|
|
|
|
memmove(rdev->iobuffer + offset * sizeof(struct rfs_direntry),
|
|
|
|
rdev->iobuffer + (offset + 1) * sizeof(struct rfs_direntry),
|
|
|
|
(one_block_direntrys - offset - 1) * sizeof(struct rfs_direntry));
|
|
|
|
|
|
|
|
struct rfs_direntry *previous_block = (struct rfs_direntry *)alloc_page();
|
|
|
|
memcpy(previous_block, rdev->iobuffer, RFS_BLKSIZE);
|
|
|
|
|
|
|
|
for (int i = delete_block_index + 1; i < parent->blocks; i++) {
|
|
|
|
rfs_r1block(rdev, parent->addrs[i]);
|
|
|
|
struct rfs_direntry *this_block = (struct rfs_direntry *)alloc_page();
|
|
|
|
memcpy(this_block, rdev->iobuffer, RFS_BLKSIZE);
|
|
|
|
|
|
|
|
// copy the first direntry of this block to the last direntry
|
|
|
|
// of previous block
|
|
|
|
memcpy(previous_block + one_block_direntrys - 1, rdev->iobuffer,
|
|
|
|
sizeof(struct rfs_direntry));
|
|
|
|
|
|
|
|
// move the direntry in this block forward by one
|
|
|
|
memmove(this_block, this_block + 1,
|
|
|
|
(one_block_direntrys - 1) * sizeof(struct rfs_direntry));
|
|
|
|
|
|
|
|
// write the previous block back to disk
|
|
|
|
memcpy(rdev->iobuffer, previous_block, RFS_BLKSIZE);
|
|
|
|
rfs_w1block(rdev, parent->addrs[i - 1]);
|
|
|
|
|
|
|
|
// update previous block
|
|
|
|
free_page(previous_block);
|
|
|
|
previous_block = this_block;
|
|
|
|
}
|
|
|
|
|
|
|
|
// write the last block back to disk
|
|
|
|
memcpy(rdev->iobuffer, previous_block, RFS_BLKSIZE);
|
|
|
|
rfs_w1block(rdev, parent->addrs[parent->blocks - 1]);
|
|
|
|
free_page(previous_block);
|
|
|
|
|
|
|
|
// if the last block is empty, free it
|
|
|
|
total_direntrys--;
|
|
|
|
if (total_direntrys % one_block_direntrys == 0 && parent->blocks > 1) {
|
|
|
|
rfs_free_block(parent->sb, parent->addrs[parent->blocks - 1]);
|
|
|
|
parent->blocks--;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ** update the directory file's size
|
|
|
|
parent->size -= sizeof(struct rfs_direntry);
|
|
|
|
|
|
|
|
// ** write the directory file's inode back to disk
|
|
|
|
if (rfs_write_back_vinode(parent) != 0) {
|
|
|
|
sprint("rfs_unlink: rfs_write_back_vinode failed");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// when a directory is opened, the contents of the directory file are read
|
|
|
|
// into the memory for directory read operations
|
|
|
|
//
|
|
|
|
int rfs_hook_opendir(struct vinode *dir_vinode, struct dentry *dentry) {
|
|
|
|
// allocate space and read the contents of the dir block into memory
|
|
|
|
void *pdire = NULL;
|
|
|
|
void *previous = NULL;
|
|
|
|
struct rfs_device *rdev = rfs_device_list[dir_vinode->sb->s_dev->dev_id];
|
|
|
|
|
|
|
|
// read-in the directory file, store all direntries in dir cache.
|
|
|
|
for (int i = dir_vinode->blocks - 1; i >= 0; i--) {
|
|
|
|
previous = pdire;
|
|
|
|
pdire = alloc_page();
|
|
|
|
|
|
|
|
if (previous != NULL && previous - pdire != RFS_BLKSIZE)
|
|
|
|
panic("rfs_hook_opendir: memory discontinuity");
|
|
|
|
|
|
|
|
rfs_r1block(rdev, dir_vinode->addrs[i]);
|
|
|
|
memcpy(pdire, rdev->iobuffer, RFS_BLKSIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// save the pointer to the directory block in the vinode
|
|
|
|
struct rfs_dir_cache *dir_cache = (struct rfs_dir_cache *)alloc_page();
|
|
|
|
dir_cache->block_count = dir_vinode->blocks;
|
|
|
|
dir_cache->dir_base_addr = (struct rfs_direntry *)pdire;
|
|
|
|
|
|
|
|
dir_vinode->i_fs_info = dir_cache;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// when a directory is closed, the memory space allocated for the directory
|
|
|
|
// block is freed
|
|
|
|
//
|
|
|
|
int rfs_hook_closedir(struct vinode *dir_vinode, struct dentry *dentry) {
|
|
|
|
struct rfs_dir_cache *dir_cache =
|
|
|
|
(struct rfs_dir_cache *)dir_vinode->i_fs_info;
|
|
|
|
|
|
|
|
// reclaim the dir cache
|
|
|
|
for (int i = 0; i < dir_cache->block_count; ++i) {
|
|
|
|
free_page((char *)dir_cache->dir_base_addr + i * RFS_BLKSIZE);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// read a directory entry from the directory "dir", and the "offset" indicate
|
|
|
|
// the position of the entry to be read. if offset is 0, the first entry is read,
|
|
|
|
// if offset is 1, the second entry is read, and so on.
|
|
|
|
// return: 0 on success, -1 when there are no more entry (end of the list).
|
|
|
|
//
|
|
|
|
int rfs_readdir(struct vinode *dir_vinode, struct dir *dir, int *offset) {
|
|
|
|
int total_direntrys = dir_vinode->size / sizeof(struct rfs_direntry);
|
|
|
|
int one_block_direntrys = RFS_BLKSIZE / sizeof(struct rfs_direntry);
|
|
|
|
|
|
|
|
int direntry_index = *offset;
|
|
|
|
if (direntry_index >= total_direntrys) {
|
|
|
|
// no more direntry
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// reads a directory entry from the directory cache stored in vfs inode.
|
|
|
|
struct rfs_dir_cache *dir_cache =
|
|
|
|
(struct rfs_dir_cache *)dir_vinode->i_fs_info;
|
|
|
|
struct rfs_direntry *p_direntry = dir_cache->dir_base_addr + direntry_index;
|
|
|
|
|
|
|
|
// TODO (lab4_2): implement the code to read a directory entry.
|
|
|
|
// hint: in the above code, we had found the directory entry that located at the
|
|
|
|
// *offset, and used p_direntry to point it.
|
|
|
|
// in the remaining processing, we need to return our discovery.
|
|
|
|
// the method of returning is to popular proper members of "dir", more specifically,
|
|
|
|
// dir->name and dir->inum.
|
|
|
|
// note: DO NOT DELETE CODE BELOW PANIC.
|
|
|
|
panic("You need to implement the code for reading a directory entry of rfs in lab4_2.\n" );
|
|
|
|
|
|
|
|
// DO NOT DELETE CODE BELOW.
|
|
|
|
(*offset)++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// make a new direntry named "sub_dentry->name" under the directory "parent",
|
|
|
|
// return the vfs inode of subdir being created.
|
|
|
|
//
|
|
|
|
struct vinode *rfs_mkdir(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_mkdir: no more free disk inode, we cannot create directory.\n" );
|
|
|
|
|
|
|
|
// initialize the states of the file being created
|
|
|
|
free_dinode->size = 0;
|
|
|
|
free_dinode->type = R_DIR;
|
|
|
|
free_dinode->nlinks = 1;
|
|
|
|
free_dinode->blocks = 1;
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
// ** add a direntry to the directory
|
|
|
|
int result = rfs_add_direntry(parent, sub_dentry->name, free_inum);
|
|
|
|
if (result == -1) {
|
|
|
|
sprint("rfs_mkdir: rfs_add_direntry failed");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ** allocate a new vinode
|
|
|
|
struct vinode *sub_vinode = rfs_alloc_vinode(parent->sb);
|
|
|
|
sub_vinode->inum = free_inum;
|
|
|
|
rfs_update_vinode(sub_vinode);
|
|
|
|
|
|
|
|
return sub_vinode;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**** 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;
|
|
|
|
}
|