update name of code to labcodes
This commit is contained in:
167
labcodes/lab8/kern/fs/devs/dev.c
Normal file
167
labcodes/lab8/kern/fs/devs/dev.c
Normal file
@@ -0,0 +1,167 @@
|
||||
#include <defs.h>
|
||||
#include <string.h>
|
||||
#include <stat.h>
|
||||
#include <dev.h>
|
||||
#include <inode.h>
|
||||
#include <unistd.h>
|
||||
#include <error.h>
|
||||
|
||||
/*
|
||||
* dev_open - Called for each open().
|
||||
*/
|
||||
static int
|
||||
dev_open(struct inode *node, uint32_t open_flags) {
|
||||
if (open_flags & (O_CREAT | O_TRUNC | O_EXCL | O_APPEND)) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
struct device *dev = vop_info(node, device);
|
||||
return dop_open(dev, open_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* dev_close - Called on the last close(). Just pass through.
|
||||
*/
|
||||
static int
|
||||
dev_close(struct inode *node) {
|
||||
struct device *dev = vop_info(node, device);
|
||||
return dop_close(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* dev_read -Called for read. Hand off to iobuf.
|
||||
*/
|
||||
static int
|
||||
dev_read(struct inode *node, struct iobuf *iob) {
|
||||
struct device *dev = vop_info(node, device);
|
||||
return dop_io(dev, iob, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* dev_write -Called for write. Hand off to iobuf.
|
||||
*/
|
||||
static int
|
||||
dev_write(struct inode *node, struct iobuf *iob) {
|
||||
struct device *dev = vop_info(node, device);
|
||||
return dop_io(dev, iob, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* dev_ioctl - Called for ioctl(). Just pass through.
|
||||
*/
|
||||
static int
|
||||
dev_ioctl(struct inode *node, int op, void *data) {
|
||||
struct device *dev = vop_info(node, device);
|
||||
return dop_ioctl(dev, op, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* dev_fstat - Called for stat().
|
||||
* Set the type and the size (block devices only).
|
||||
* The link count for a device is always 1.
|
||||
*/
|
||||
static int
|
||||
dev_fstat(struct inode *node, struct stat *stat) {
|
||||
int ret;
|
||||
memset(stat, 0, sizeof(struct stat));
|
||||
if ((ret = vop_gettype(node, &(stat->st_mode))) != 0) {
|
||||
return ret;
|
||||
}
|
||||
struct device *dev = vop_info(node, device);
|
||||
stat->st_nlinks = 1;
|
||||
stat->st_blocks = dev->d_blocks;
|
||||
stat->st_size = stat->st_blocks * dev->d_blocksize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* dev_gettype - Return the type. A device is a "block device" if it has a known
|
||||
* length. A device that generates data in a stream is a "character
|
||||
* device".
|
||||
*/
|
||||
static int
|
||||
dev_gettype(struct inode *node, uint32_t *type_store) {
|
||||
struct device *dev = vop_info(node, device);
|
||||
*type_store = (dev->d_blocks > 0) ? S_IFBLK : S_IFCHR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* dev_tryseek - Attempt a seek.
|
||||
* For block devices, require block alignment.
|
||||
* For character devices, prohibit seeking entirely.
|
||||
*/
|
||||
static int
|
||||
dev_tryseek(struct inode *node, off_t pos) {
|
||||
struct device *dev = vop_info(node, device);
|
||||
if (dev->d_blocks > 0) {
|
||||
if ((pos % dev->d_blocksize) == 0) {
|
||||
if (pos >= 0 && pos < dev->d_blocks * dev->d_blocksize) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -E_INVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* dev_lookup - Name lookup.
|
||||
*
|
||||
* One interesting feature of device:name pathname syntax is that you
|
||||
* can implement pathnames on arbitrary devices. For instance, if you
|
||||
* had a graphics device that supported multiple resolutions (which we
|
||||
* don't), you might arrange things so that you could open it with
|
||||
* pathnames like "video:800x600/24bpp" in order to select the operating
|
||||
* mode.
|
||||
*
|
||||
* However, we have no support for this in the base system.
|
||||
*/
|
||||
static int
|
||||
dev_lookup(struct inode *node, char *path, struct inode **node_store) {
|
||||
if (*path != '\0') {
|
||||
return -E_NOENT;
|
||||
}
|
||||
vop_ref_inc(node);
|
||||
*node_store = node;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function table for device inodes.
|
||||
*/
|
||||
static const struct inode_ops dev_node_ops = {
|
||||
.vop_magic = VOP_MAGIC,
|
||||
.vop_open = dev_open,
|
||||
.vop_close = dev_close,
|
||||
.vop_read = dev_read,
|
||||
.vop_write = dev_write,
|
||||
.vop_fstat = dev_fstat,
|
||||
.vop_ioctl = dev_ioctl,
|
||||
.vop_gettype = dev_gettype,
|
||||
.vop_tryseek = dev_tryseek,
|
||||
.vop_lookup = dev_lookup,
|
||||
};
|
||||
|
||||
#define init_device(x) \
|
||||
do { \
|
||||
extern void dev_init_##x(void); \
|
||||
dev_init_##x(); \
|
||||
} while (0)
|
||||
|
||||
/* dev_init - Initialization functions for builtin vfs-level devices. */
|
||||
void
|
||||
dev_init(void) {
|
||||
// init_device(null);
|
||||
init_device(stdin);
|
||||
init_device(stdout);
|
||||
init_device(disk0);
|
||||
}
|
||||
/* dev_create_inode - Create inode for a vfs-level device. */
|
||||
struct inode *
|
||||
dev_create_inode(void) {
|
||||
struct inode *node;
|
||||
if ((node = alloc_inode(device)) != NULL) {
|
||||
vop_init(node, &dev_node_ops, NULL);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
31
labcodes/lab8/kern/fs/devs/dev.h
Normal file
31
labcodes/lab8/kern/fs/devs/dev.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef __KERN_FS_DEVS_DEV_H__
|
||||
#define __KERN_FS_DEVS_DEV_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
struct inode;
|
||||
struct iobuf;
|
||||
|
||||
/*
|
||||
* Filesystem-namespace-accessible device.
|
||||
* d_io is for both reads and writes; the iobuf will indicates the direction.
|
||||
*/
|
||||
struct device {
|
||||
size_t d_blocks;
|
||||
size_t d_blocksize;
|
||||
int (*d_open)(struct device *dev, uint32_t open_flags);
|
||||
int (*d_close)(struct device *dev);
|
||||
int (*d_io)(struct device *dev, struct iobuf *iob, bool write);
|
||||
int (*d_ioctl)(struct device *dev, int op, void *data);
|
||||
};
|
||||
|
||||
#define dop_open(dev, open_flags) ((dev)->d_open(dev, open_flags))
|
||||
#define dop_close(dev) ((dev)->d_close(dev))
|
||||
#define dop_io(dev, iob, write) ((dev)->d_io(dev, iob, write))
|
||||
#define dop_ioctl(dev, op, data) ((dev)->d_ioctl(dev, op, data))
|
||||
|
||||
void dev_init(void);
|
||||
struct inode *dev_create_inode(void);
|
||||
|
||||
#endif /* !__KERN_FS_DEVS_DEV_H__ */
|
||||
|
||||
144
labcodes/lab8/kern/fs/devs/dev_disk0.c
Normal file
144
labcodes/lab8/kern/fs/devs/dev_disk0.c
Normal file
@@ -0,0 +1,144 @@
|
||||
#include <defs.h>
|
||||
#include <mmu.h>
|
||||
#include <sem.h>
|
||||
#include <ide.h>
|
||||
#include <inode.h>
|
||||
#include <kmalloc.h>
|
||||
#include <dev.h>
|
||||
#include <vfs.h>
|
||||
#include <iobuf.h>
|
||||
#include <error.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define DISK0_BLKSIZE PGSIZE
|
||||
#define DISK0_BUFSIZE (4 * DISK0_BLKSIZE)
|
||||
#define DISK0_BLK_NSECT (DISK0_BLKSIZE / SECTSIZE)
|
||||
|
||||
static char *disk0_buffer;
|
||||
static semaphore_t disk0_sem;
|
||||
|
||||
static void
|
||||
lock_disk0(void) {
|
||||
down(&(disk0_sem));
|
||||
}
|
||||
|
||||
static void
|
||||
unlock_disk0(void) {
|
||||
up(&(disk0_sem));
|
||||
}
|
||||
|
||||
static int
|
||||
disk0_open(struct device *dev, uint32_t open_flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
disk0_close(struct device *dev) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
disk0_read_blks_nolock(uint32_t blkno, uint32_t nblks) {
|
||||
int ret;
|
||||
uint32_t sectno = blkno * DISK0_BLK_NSECT, nsecs = nblks * DISK0_BLK_NSECT;
|
||||
if ((ret = ide_read_secs(DISK0_DEV_NO, sectno, disk0_buffer, nsecs)) != 0) {
|
||||
panic("disk0: read blkno = %d (sectno = %d), nblks = %d (nsecs = %d): 0x%08x.\n",
|
||||
blkno, sectno, nblks, nsecs, ret);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
disk0_write_blks_nolock(uint32_t blkno, uint32_t nblks) {
|
||||
int ret;
|
||||
uint32_t sectno = blkno * DISK0_BLK_NSECT, nsecs = nblks * DISK0_BLK_NSECT;
|
||||
if ((ret = ide_write_secs(DISK0_DEV_NO, sectno, disk0_buffer, nsecs)) != 0) {
|
||||
panic("disk0: write blkno = %d (sectno = %d), nblks = %d (nsecs = %d): 0x%08x.\n",
|
||||
blkno, sectno, nblks, nsecs, ret);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
disk0_io(struct device *dev, struct iobuf *iob, bool write) {
|
||||
off_t offset = iob->io_offset;
|
||||
size_t resid = iob->io_resid;
|
||||
uint32_t blkno = offset / DISK0_BLKSIZE;
|
||||
uint32_t nblks = resid / DISK0_BLKSIZE;
|
||||
|
||||
/* don't allow I/O that isn't block-aligned */
|
||||
if ((offset % DISK0_BLKSIZE) != 0 || (resid % DISK0_BLKSIZE) != 0) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
|
||||
/* don't allow I/O past the end of disk0 */
|
||||
if (blkno + nblks > dev->d_blocks) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
|
||||
/* read/write nothing ? */
|
||||
if (nblks == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
lock_disk0();
|
||||
while (resid != 0) {
|
||||
size_t copied, alen = DISK0_BUFSIZE;
|
||||
if (write) {
|
||||
iobuf_move(iob, disk0_buffer, alen, 0, &copied);
|
||||
assert(copied != 0 && copied <= resid && copied % DISK0_BLKSIZE == 0);
|
||||
nblks = copied / DISK0_BLKSIZE;
|
||||
disk0_write_blks_nolock(blkno, nblks);
|
||||
}
|
||||
else {
|
||||
if (alen > resid) {
|
||||
alen = resid;
|
||||
}
|
||||
nblks = alen / DISK0_BLKSIZE;
|
||||
disk0_read_blks_nolock(blkno, nblks);
|
||||
iobuf_move(iob, disk0_buffer, alen, 1, &copied);
|
||||
assert(copied == alen && copied % DISK0_BLKSIZE == 0);
|
||||
}
|
||||
resid -= copied, blkno += nblks;
|
||||
}
|
||||
unlock_disk0();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
disk0_ioctl(struct device *dev, int op, void *data) {
|
||||
return -E_UNIMP;
|
||||
}
|
||||
|
||||
static void
|
||||
disk0_device_init(struct device *dev) {
|
||||
static_assert(DISK0_BLKSIZE % SECTSIZE == 0);
|
||||
if (!ide_device_valid(DISK0_DEV_NO)) {
|
||||
panic("disk0 device isn't available.\n");
|
||||
}
|
||||
dev->d_blocks = ide_device_size(DISK0_DEV_NO) / DISK0_BLK_NSECT;
|
||||
dev->d_blocksize = DISK0_BLKSIZE;
|
||||
dev->d_open = disk0_open;
|
||||
dev->d_close = disk0_close;
|
||||
dev->d_io = disk0_io;
|
||||
dev->d_ioctl = disk0_ioctl;
|
||||
sem_init(&(disk0_sem), 1);
|
||||
|
||||
static_assert(DISK0_BUFSIZE % DISK0_BLKSIZE == 0);
|
||||
if ((disk0_buffer = kmalloc(DISK0_BUFSIZE)) == NULL) {
|
||||
panic("disk0 alloc buffer failed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dev_init_disk0(void) {
|
||||
struct inode *node;
|
||||
if ((node = dev_create_inode()) == NULL) {
|
||||
panic("disk0: dev_create_node.\n");
|
||||
}
|
||||
disk0_device_init(vop_info(node, device));
|
||||
|
||||
int ret;
|
||||
if ((ret = vfs_add_dev("disk0", node, 1)) != 0) {
|
||||
panic("disk0: vfs_add_dev: %e.\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
126
labcodes/lab8/kern/fs/devs/dev_stdin.c
Normal file
126
labcodes/lab8/kern/fs/devs/dev_stdin.c
Normal file
@@ -0,0 +1,126 @@
|
||||
#include <defs.h>
|
||||
#include <stdio.h>
|
||||
#include <wait.h>
|
||||
#include <sync.h>
|
||||
#include <proc.h>
|
||||
#include <sched.h>
|
||||
#include <dev.h>
|
||||
#include <vfs.h>
|
||||
#include <iobuf.h>
|
||||
#include <inode.h>
|
||||
#include <unistd.h>
|
||||
#include <error.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define STDIN_BUFSIZE 4096
|
||||
|
||||
static char stdin_buffer[STDIN_BUFSIZE];
|
||||
static off_t p_rpos, p_wpos;
|
||||
static wait_queue_t __wait_queue, *wait_queue = &__wait_queue;
|
||||
|
||||
void
|
||||
dev_stdin_write(char c) {
|
||||
bool intr_flag;
|
||||
if (c != '\0') {
|
||||
local_intr_save(intr_flag);
|
||||
{
|
||||
stdin_buffer[p_wpos % STDIN_BUFSIZE] = c;
|
||||
if (p_wpos - p_rpos < STDIN_BUFSIZE) {
|
||||
p_wpos ++;
|
||||
}
|
||||
if (!wait_queue_empty(wait_queue)) {
|
||||
wakeup_queue(wait_queue, WT_KBD, 1);
|
||||
}
|
||||
}
|
||||
local_intr_restore(intr_flag);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
dev_stdin_read(char *buf, size_t len) {
|
||||
int ret = 0;
|
||||
bool intr_flag;
|
||||
local_intr_save(intr_flag);
|
||||
{
|
||||
for (; ret < len; ret ++, p_rpos ++) {
|
||||
try_again:
|
||||
if (p_rpos < p_wpos) {
|
||||
*buf ++ = stdin_buffer[p_rpos % STDIN_BUFSIZE];
|
||||
}
|
||||
else {
|
||||
wait_t __wait, *wait = &__wait;
|
||||
wait_current_set(wait_queue, wait, WT_KBD);
|
||||
local_intr_restore(intr_flag);
|
||||
|
||||
schedule();
|
||||
|
||||
local_intr_save(intr_flag);
|
||||
wait_current_del(wait_queue, wait);
|
||||
if (wait->wakeup_flags == WT_KBD) {
|
||||
goto try_again;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
local_intr_restore(intr_flag);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
stdin_open(struct device *dev, uint32_t open_flags) {
|
||||
if (open_flags != O_RDONLY) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
stdin_close(struct device *dev) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
stdin_io(struct device *dev, struct iobuf *iob, bool write) {
|
||||
if (!write) {
|
||||
int ret;
|
||||
if ((ret = dev_stdin_read(iob->io_base, iob->io_resid)) > 0) {
|
||||
iob->io_resid -= ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return -E_INVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
stdin_ioctl(struct device *dev, int op, void *data) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
stdin_device_init(struct device *dev) {
|
||||
dev->d_blocks = 0;
|
||||
dev->d_blocksize = 1;
|
||||
dev->d_open = stdin_open;
|
||||
dev->d_close = stdin_close;
|
||||
dev->d_io = stdin_io;
|
||||
dev->d_ioctl = stdin_ioctl;
|
||||
|
||||
p_rpos = p_wpos = 0;
|
||||
wait_queue_init(wait_queue);
|
||||
}
|
||||
|
||||
void
|
||||
dev_init_stdin(void) {
|
||||
struct inode *node;
|
||||
if ((node = dev_create_inode()) == NULL) {
|
||||
panic("stdin: dev_create_node.\n");
|
||||
}
|
||||
stdin_device_init(vop_info(node, device));
|
||||
|
||||
int ret;
|
||||
if ((ret = vfs_add_dev("stdin", node, 0)) != 0) {
|
||||
panic("stdin: vfs_add_dev: %e.\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
64
labcodes/lab8/kern/fs/devs/dev_stdout.c
Normal file
64
labcodes/lab8/kern/fs/devs/dev_stdout.c
Normal file
@@ -0,0 +1,64 @@
|
||||
#include <defs.h>
|
||||
#include <stdio.h>
|
||||
#include <dev.h>
|
||||
#include <vfs.h>
|
||||
#include <iobuf.h>
|
||||
#include <inode.h>
|
||||
#include <unistd.h>
|
||||
#include <error.h>
|
||||
#include <assert.h>
|
||||
|
||||
static int
|
||||
stdout_open(struct device *dev, uint32_t open_flags) {
|
||||
if (open_flags != O_WRONLY) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
stdout_close(struct device *dev) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
stdout_io(struct device *dev, struct iobuf *iob, bool write) {
|
||||
if (write) {
|
||||
char *data = iob->io_base;
|
||||
for (; iob->io_resid != 0; iob->io_resid --) {
|
||||
cputchar(*data ++);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -E_INVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
stdout_ioctl(struct device *dev, int op, void *data) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
|
||||
static void
|
||||
stdout_device_init(struct device *dev) {
|
||||
dev->d_blocks = 0;
|
||||
dev->d_blocksize = 1;
|
||||
dev->d_open = stdout_open;
|
||||
dev->d_close = stdout_close;
|
||||
dev->d_io = stdout_io;
|
||||
dev->d_ioctl = stdout_ioctl;
|
||||
}
|
||||
|
||||
void
|
||||
dev_init_stdout(void) {
|
||||
struct inode *node;
|
||||
if ((node = dev_create_inode()) == NULL) {
|
||||
panic("stdout: dev_create_node.\n");
|
||||
}
|
||||
stdout_device_init(vop_info(node, device));
|
||||
|
||||
int ret;
|
||||
if ((ret = vfs_add_dev("stdout", node, 0)) != 0) {
|
||||
panic("stdout: vfs_add_dev: %e.\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
356
labcodes/lab8/kern/fs/file.c
Normal file
356
labcodes/lab8/kern/fs/file.c
Normal file
@@ -0,0 +1,356 @@
|
||||
#include <defs.h>
|
||||
#include <string.h>
|
||||
#include <vfs.h>
|
||||
#include <proc.h>
|
||||
#include <file.h>
|
||||
#include <unistd.h>
|
||||
#include <iobuf.h>
|
||||
#include <inode.h>
|
||||
#include <stat.h>
|
||||
#include <dirent.h>
|
||||
#include <error.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define testfd(fd) ((fd) >= 0 && (fd) < FILES_STRUCT_NENTRY)
|
||||
|
||||
// get_fd_array - get current process's open files table
|
||||
static struct file *
|
||||
get_fd_array(void) {
|
||||
struct files_struct *filesp = current->filesp;
|
||||
assert(filesp != NULL && files_count(filesp) > 0);
|
||||
return filesp->fd_array;
|
||||
}
|
||||
|
||||
// fd_array_init - initialize the open files table
|
||||
void
|
||||
fd_array_init(struct file *fd_array) {
|
||||
int fd;
|
||||
struct file *file = fd_array;
|
||||
for (fd = 0; fd < FILES_STRUCT_NENTRY; fd ++, file ++) {
|
||||
file->open_count = 0;
|
||||
file->status = FD_NONE, file->fd = fd;
|
||||
}
|
||||
}
|
||||
|
||||
// fs_array_alloc - allocate a free file item (with FD_NONE status) in open files table
|
||||
static int
|
||||
fd_array_alloc(int fd, struct file **file_store) {
|
||||
// panic("debug");
|
||||
struct file *file = get_fd_array();
|
||||
if (fd == NO_FD) {
|
||||
for (fd = 0; fd < FILES_STRUCT_NENTRY; fd ++, file ++) {
|
||||
if (file->status == FD_NONE) {
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
return -E_MAX_OPEN;
|
||||
}
|
||||
else {
|
||||
if (testfd(fd)) {
|
||||
file += fd;
|
||||
if (file->status == FD_NONE) {
|
||||
goto found;
|
||||
}
|
||||
return -E_BUSY;
|
||||
}
|
||||
return -E_INVAL;
|
||||
}
|
||||
found:
|
||||
assert(fopen_count(file) == 0);
|
||||
file->status = FD_INIT, file->node = NULL;
|
||||
*file_store = file;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// fd_array_free - free a file item in open files table
|
||||
static void
|
||||
fd_array_free(struct file *file) {
|
||||
assert(file->status == FD_INIT || file->status == FD_CLOSED);
|
||||
assert(fopen_count(file) == 0);
|
||||
if (file->status == FD_CLOSED) {
|
||||
vfs_close(file->node);
|
||||
}
|
||||
file->status = FD_NONE;
|
||||
}
|
||||
|
||||
static void
|
||||
fd_array_acquire(struct file *file) {
|
||||
assert(file->status == FD_OPENED);
|
||||
fopen_count_inc(file);
|
||||
}
|
||||
|
||||
// fd_array_release - file's open_count--; if file's open_count-- == 0 , then call fd_array_free to free this file item
|
||||
static void
|
||||
fd_array_release(struct file *file) {
|
||||
assert(file->status == FD_OPENED || file->status == FD_CLOSED);
|
||||
assert(fopen_count(file) > 0);
|
||||
if (fopen_count_dec(file) == 0) {
|
||||
fd_array_free(file);
|
||||
}
|
||||
}
|
||||
|
||||
// fd_array_open - file's open_count++, set status to FD_OPENED
|
||||
void
|
||||
fd_array_open(struct file *file) {
|
||||
assert(file->status == FD_INIT && file->node != NULL);
|
||||
file->status = FD_OPENED;
|
||||
fopen_count_inc(file);
|
||||
}
|
||||
|
||||
// fd_array_close - file's open_count--; if file's open_count-- == 0 , then call fd_array_free to free this file item
|
||||
void
|
||||
fd_array_close(struct file *file) {
|
||||
assert(file->status == FD_OPENED);
|
||||
assert(fopen_count(file) > 0);
|
||||
file->status = FD_CLOSED;
|
||||
if (fopen_count_dec(file) == 0) {
|
||||
fd_array_free(file);
|
||||
}
|
||||
}
|
||||
|
||||
//fs_array_dup - duplicate file 'from' to file 'to'
|
||||
void
|
||||
fd_array_dup(struct file *to, struct file *from) {
|
||||
//cprintf("[fd_array_dup]from fd=%d, to fd=%d\n",from->fd, to->fd);
|
||||
assert(to->status == FD_INIT && from->status == FD_OPENED);
|
||||
to->pos = from->pos;
|
||||
to->readable = from->readable;
|
||||
to->writable = from->writable;
|
||||
struct inode *node = from->node;
|
||||
vop_ref_inc(node), vop_open_inc(node);
|
||||
to->node = node;
|
||||
fd_array_open(to);
|
||||
}
|
||||
|
||||
// fd2file - use fd as index of fd_array, return the array item (file)
|
||||
static inline int
|
||||
fd2file(int fd, struct file **file_store) {
|
||||
if (testfd(fd)) {
|
||||
struct file *file = get_fd_array() + fd;
|
||||
if (file->status == FD_OPENED && file->fd == fd) {
|
||||
*file_store = file;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -E_INVAL;
|
||||
}
|
||||
|
||||
// file_testfd - test file is readble or writable?
|
||||
bool
|
||||
file_testfd(int fd, bool readable, bool writable) {
|
||||
int ret;
|
||||
struct file *file;
|
||||
if ((ret = fd2file(fd, &file)) != 0) {
|
||||
return 0;
|
||||
}
|
||||
if (readable && !file->readable) {
|
||||
return 0;
|
||||
}
|
||||
if (writable && !file->writable) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// open file
|
||||
int
|
||||
file_open(char *path, uint32_t open_flags) {
|
||||
bool readable = 0, writable = 0;
|
||||
switch (open_flags & O_ACCMODE) {
|
||||
case O_RDONLY: readable = 1; break;
|
||||
case O_WRONLY: writable = 1; break;
|
||||
case O_RDWR:
|
||||
readable = writable = 1;
|
||||
break;
|
||||
default:
|
||||
return -E_INVAL;
|
||||
}
|
||||
|
||||
int ret;
|
||||
struct file *file;
|
||||
if ((ret = fd_array_alloc(NO_FD, &file)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct inode *node;
|
||||
if ((ret = vfs_open(path, open_flags, &node)) != 0) {
|
||||
fd_array_free(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
file->pos = 0;
|
||||
if (open_flags & O_APPEND) {
|
||||
struct stat __stat, *stat = &__stat;
|
||||
if ((ret = vop_fstat(node, stat)) != 0) {
|
||||
vfs_close(node);
|
||||
fd_array_free(file);
|
||||
return ret;
|
||||
}
|
||||
file->pos = stat->st_size;
|
||||
}
|
||||
|
||||
file->node = node;
|
||||
file->readable = readable;
|
||||
file->writable = writable;
|
||||
fd_array_open(file);
|
||||
return file->fd;
|
||||
}
|
||||
|
||||
// close file
|
||||
int
|
||||
file_close(int fd) {
|
||||
int ret;
|
||||
struct file *file;
|
||||
if ((ret = fd2file(fd, &file)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
fd_array_close(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// read file
|
||||
int
|
||||
file_read(int fd, void *base, size_t len, size_t *copied_store) {
|
||||
int ret;
|
||||
struct file *file;
|
||||
*copied_store = 0;
|
||||
if ((ret = fd2file(fd, &file)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
if (!file->readable) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
fd_array_acquire(file);
|
||||
|
||||
struct iobuf __iob, *iob = iobuf_init(&__iob, base, len, file->pos);
|
||||
ret = vop_read(file->node, iob);
|
||||
|
||||
size_t copied = iobuf_used(iob);
|
||||
if (file->status == FD_OPENED) {
|
||||
file->pos += copied;
|
||||
}
|
||||
*copied_store = copied;
|
||||
fd_array_release(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// write file
|
||||
int
|
||||
file_write(int fd, void *base, size_t len, size_t *copied_store) {
|
||||
int ret;
|
||||
struct file *file;
|
||||
*copied_store = 0;
|
||||
if ((ret = fd2file(fd, &file)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
if (!file->writable) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
fd_array_acquire(file);
|
||||
|
||||
struct iobuf __iob, *iob = iobuf_init(&__iob, base, len, file->pos);
|
||||
ret = vop_write(file->node, iob);
|
||||
|
||||
size_t copied = iobuf_used(iob);
|
||||
if (file->status == FD_OPENED) {
|
||||
file->pos += copied;
|
||||
}
|
||||
*copied_store = copied;
|
||||
fd_array_release(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// seek file
|
||||
int
|
||||
file_seek(int fd, off_t pos, int whence) {
|
||||
struct stat __stat, *stat = &__stat;
|
||||
int ret;
|
||||
struct file *file;
|
||||
if ((ret = fd2file(fd, &file)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
fd_array_acquire(file);
|
||||
|
||||
switch (whence) {
|
||||
case LSEEK_SET: break;
|
||||
case LSEEK_CUR: pos += file->pos; break;
|
||||
case LSEEK_END:
|
||||
if ((ret = vop_fstat(file->node, stat)) == 0) {
|
||||
pos += stat->st_size;
|
||||
}
|
||||
break;
|
||||
default: ret = -E_INVAL;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
if ((ret = vop_tryseek(file->node, pos)) == 0) {
|
||||
file->pos = pos;
|
||||
}
|
||||
// cprintf("file_seek, pos=%d, whence=%d, ret=%d\n", pos, whence, ret);
|
||||
}
|
||||
fd_array_release(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// stat file
|
||||
int
|
||||
file_fstat(int fd, struct stat *stat) {
|
||||
int ret;
|
||||
struct file *file;
|
||||
if ((ret = fd2file(fd, &file)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
fd_array_acquire(file);
|
||||
ret = vop_fstat(file->node, stat);
|
||||
fd_array_release(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// sync file
|
||||
int
|
||||
file_fsync(int fd) {
|
||||
int ret;
|
||||
struct file *file;
|
||||
if ((ret = fd2file(fd, &file)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
fd_array_acquire(file);
|
||||
ret = vop_fsync(file->node);
|
||||
fd_array_release(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// get file entry in DIR
|
||||
int
|
||||
file_getdirentry(int fd, struct dirent *direntp) {
|
||||
int ret;
|
||||
struct file *file;
|
||||
if ((ret = fd2file(fd, &file)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
fd_array_acquire(file);
|
||||
|
||||
struct iobuf __iob, *iob = iobuf_init(&__iob, direntp->name, sizeof(direntp->name), direntp->offset);
|
||||
if ((ret = vop_getdirentry(file->node, iob)) == 0) {
|
||||
direntp->offset += iobuf_used(iob);
|
||||
}
|
||||
fd_array_release(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// duplicate file
|
||||
int
|
||||
file_dup(int fd1, int fd2) {
|
||||
int ret;
|
||||
struct file *file1, *file2;
|
||||
if ((ret = fd2file(fd1, &file1)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
if ((ret = fd_array_alloc(fd2, &file2)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
fd_array_dup(file2, file1);
|
||||
return file2->fd;
|
||||
}
|
||||
|
||||
|
||||
62
labcodes/lab8/kern/fs/file.h
Normal file
62
labcodes/lab8/kern/fs/file.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef __KERN_FS_FILE_H__
|
||||
#define __KERN_FS_FILE_H__
|
||||
|
||||
//#include <types.h>
|
||||
#include <fs.h>
|
||||
#include <proc.h>
|
||||
#include <atomic.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct inode;
|
||||
struct stat;
|
||||
struct dirent;
|
||||
|
||||
struct file {
|
||||
enum {
|
||||
FD_NONE, FD_INIT, FD_OPENED, FD_CLOSED,
|
||||
} status;
|
||||
bool readable;
|
||||
bool writable;
|
||||
int fd;
|
||||
off_t pos;
|
||||
struct inode *node;
|
||||
int open_count;
|
||||
};
|
||||
|
||||
void fd_array_init(struct file *fd_array);
|
||||
void fd_array_open(struct file *file);
|
||||
void fd_array_close(struct file *file);
|
||||
void fd_array_dup(struct file *to, struct file *from);
|
||||
bool file_testfd(int fd, bool readable, bool writable);
|
||||
|
||||
int file_open(char *path, uint32_t open_flags);
|
||||
int file_close(int fd);
|
||||
int file_read(int fd, void *base, size_t len, size_t *copied_store);
|
||||
int file_write(int fd, void *base, size_t len, size_t *copied_store);
|
||||
int file_seek(int fd, off_t pos, int whence);
|
||||
int file_fstat(int fd, struct stat *stat);
|
||||
int file_fsync(int fd);
|
||||
int file_getdirentry(int fd, struct dirent *dirent);
|
||||
int file_dup(int fd1, int fd2);
|
||||
int file_pipe(int fd[]);
|
||||
int file_mkfifo(const char *name, uint32_t open_flags);
|
||||
|
||||
static inline int
|
||||
fopen_count(struct file *file) {
|
||||
return file->open_count;
|
||||
}
|
||||
|
||||
static inline int
|
||||
fopen_count_inc(struct file *file) {
|
||||
file->open_count += 1;
|
||||
return file->open_count;
|
||||
}
|
||||
|
||||
static inline int
|
||||
fopen_count_dec(struct file *file) {
|
||||
file->open_count -= 1;
|
||||
return file->open_count;
|
||||
}
|
||||
|
||||
#endif /* !__KERN_FS_FILE_H__ */
|
||||
|
||||
99
labcodes/lab8/kern/fs/fs.c
Normal file
99
labcodes/lab8/kern/fs/fs.c
Normal file
@@ -0,0 +1,99 @@
|
||||
#include <defs.h>
|
||||
#include <kmalloc.h>
|
||||
#include <sem.h>
|
||||
#include <vfs.h>
|
||||
#include <dev.h>
|
||||
#include <file.h>
|
||||
#include <sfs.h>
|
||||
#include <inode.h>
|
||||
#include <assert.h>
|
||||
//called when init_main proc start
|
||||
void
|
||||
fs_init(void) {
|
||||
vfs_init();
|
||||
dev_init();
|
||||
sfs_init();
|
||||
}
|
||||
|
||||
void
|
||||
fs_cleanup(void) {
|
||||
vfs_cleanup();
|
||||
}
|
||||
|
||||
void
|
||||
lock_files(struct files_struct *filesp) {
|
||||
down(&(filesp->files_sem));
|
||||
}
|
||||
|
||||
void
|
||||
unlock_files(struct files_struct *filesp) {
|
||||
up(&(filesp->files_sem));
|
||||
}
|
||||
//Called when a new proc init
|
||||
struct files_struct *
|
||||
files_create(void) {
|
||||
//cprintf("[files_create]\n");
|
||||
static_assert((int)FILES_STRUCT_NENTRY > 128);
|
||||
struct files_struct *filesp;
|
||||
if ((filesp = kmalloc(sizeof(struct files_struct) + FILES_STRUCT_BUFSIZE)) != NULL) {
|
||||
filesp->pwd = NULL;
|
||||
filesp->fd_array = (void *)(filesp + 1);
|
||||
filesp->files_count = 0;
|
||||
sem_init(&(filesp->files_sem), 1);
|
||||
fd_array_init(filesp->fd_array);
|
||||
}
|
||||
return filesp;
|
||||
}
|
||||
//Called when a proc exit
|
||||
void
|
||||
files_destroy(struct files_struct *filesp) {
|
||||
// cprintf("[files_destroy]\n");
|
||||
assert(filesp != NULL && files_count(filesp) == 0);
|
||||
if (filesp->pwd != NULL) {
|
||||
vop_ref_dec(filesp->pwd);
|
||||
}
|
||||
int i;
|
||||
struct file *file = filesp->fd_array;
|
||||
for (i = 0; i < FILES_STRUCT_NENTRY; i ++, file ++) {
|
||||
if (file->status == FD_OPENED) {
|
||||
fd_array_close(file);
|
||||
}
|
||||
assert(file->status == FD_NONE);
|
||||
}
|
||||
kfree(filesp);
|
||||
}
|
||||
|
||||
void
|
||||
files_closeall(struct files_struct *filesp) {
|
||||
// cprintf("[files_closeall]\n");
|
||||
assert(filesp != NULL && files_count(filesp) > 0);
|
||||
int i;
|
||||
struct file *file = filesp->fd_array;
|
||||
//skip the stdin & stdout
|
||||
for (i = 2, file += 2; i < FILES_STRUCT_NENTRY; i ++, file ++) {
|
||||
if (file->status == FD_OPENED) {
|
||||
fd_array_close(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
dup_files(struct files_struct *to, struct files_struct *from) {
|
||||
// cprintf("[dup_fs]\n");
|
||||
assert(to != NULL && from != NULL);
|
||||
assert(files_count(to) == 0 && files_count(from) > 0);
|
||||
if ((to->pwd = from->pwd) != NULL) {
|
||||
vop_ref_inc(to->pwd);
|
||||
}
|
||||
int i;
|
||||
struct file *to_file = to->fd_array, *from_file = from->fd_array;
|
||||
for (i = 0; i < FILES_STRUCT_NENTRY; i ++, to_file ++, from_file ++) {
|
||||
if (from_file->status == FD_OPENED) {
|
||||
/* alloc_fd first */
|
||||
to_file->status = FD_INIT;
|
||||
fd_array_dup(to_file, from_file);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
61
labcodes/lab8/kern/fs/fs.h
Normal file
61
labcodes/lab8/kern/fs/fs.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef __KERN_FS_FS_H__
|
||||
#define __KERN_FS_FS_H__
|
||||
|
||||
#include <defs.h>
|
||||
#include <mmu.h>
|
||||
#include <sem.h>
|
||||
#include <atomic.h>
|
||||
|
||||
#define SECTSIZE 512
|
||||
#define PAGE_NSECT (PGSIZE / SECTSIZE)
|
||||
|
||||
#define SWAP_DEV_NO 1
|
||||
#define DISK0_DEV_NO 2
|
||||
#define DISK1_DEV_NO 3
|
||||
|
||||
void fs_init(void);
|
||||
void fs_cleanup(void);
|
||||
|
||||
struct inode;
|
||||
struct file;
|
||||
|
||||
/*
|
||||
* process's file related informaction
|
||||
*/
|
||||
struct files_struct {
|
||||
struct inode *pwd; // inode of present working directory
|
||||
struct file *fd_array; // opened files array
|
||||
int files_count; // the number of opened files
|
||||
semaphore_t files_sem; // lock protect sem
|
||||
};
|
||||
|
||||
#define FILES_STRUCT_BUFSIZE (PGSIZE - sizeof(struct files_struct))
|
||||
#define FILES_STRUCT_NENTRY (FILES_STRUCT_BUFSIZE / sizeof(struct file))
|
||||
|
||||
void lock_files(struct files_struct *filesp);
|
||||
void unlock_files(struct files_struct *filesp);
|
||||
|
||||
struct files_struct *files_create(void);
|
||||
void files_destroy(struct files_struct *filesp);
|
||||
void files_closeall(struct files_struct *filesp);
|
||||
int dup_files(struct files_struct *to, struct files_struct *from);
|
||||
|
||||
static inline int
|
||||
files_count(struct files_struct *filesp) {
|
||||
return filesp->files_count;
|
||||
}
|
||||
|
||||
static inline int
|
||||
files_count_inc(struct files_struct *filesp) {
|
||||
filesp->files_count += 1;
|
||||
return filesp->files_count;
|
||||
}
|
||||
|
||||
static inline int
|
||||
files_count_dec(struct files_struct *filesp) {
|
||||
filesp->files_count -= 1;
|
||||
return filesp->files_count;
|
||||
}
|
||||
|
||||
#endif /* !__KERN_FS_FS_H__ */
|
||||
|
||||
77
labcodes/lab8/kern/fs/iobuf.c
Normal file
77
labcodes/lab8/kern/fs/iobuf.c
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <defs.h>
|
||||
#include <string.h>
|
||||
#include <iobuf.h>
|
||||
#include <error.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
* iobuf_init - init io buffer struct.
|
||||
* set up io_base to point to the buffer you want to transfer to, and set io_len to the length of buffer;
|
||||
* initialize io_offset as desired;
|
||||
* initialize io_resid to the total amount of data that can be transferred through this io.
|
||||
*/
|
||||
struct iobuf *
|
||||
iobuf_init(struct iobuf *iob, void *base, size_t len, off_t offset) {
|
||||
iob->io_base = base;
|
||||
iob->io_offset = offset;
|
||||
iob->io_len = iob->io_resid = len;
|
||||
return iob;
|
||||
}
|
||||
|
||||
/* iobuf_move - move data (iob->io_base ---> data OR data --> iob->io.base) in memory
|
||||
* @copiedp: the size of data memcopied
|
||||
*
|
||||
* iobuf_move may be called repeatedly on the same io to transfer
|
||||
* additional data until the available buffer space the io refers to
|
||||
* is exhausted.
|
||||
*/
|
||||
int
|
||||
iobuf_move(struct iobuf *iob, void *data, size_t len, bool m2b, size_t *copiedp) {
|
||||
size_t alen;
|
||||
if ((alen = iob->io_resid) > len) {
|
||||
alen = len;
|
||||
}
|
||||
if (alen > 0) {
|
||||
void *src = iob->io_base, *dst = data;
|
||||
if (m2b) {
|
||||
void *tmp = src;
|
||||
src = dst, dst = tmp;
|
||||
}
|
||||
memmove(dst, src, alen);
|
||||
iobuf_skip(iob, alen), len -= alen;
|
||||
}
|
||||
if (copiedp != NULL) {
|
||||
*copiedp = alen;
|
||||
}
|
||||
return (len == 0) ? 0 : -E_NO_MEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* iobuf_move_zeros - set io buffer zero
|
||||
* @copiedp: the size of data memcopied
|
||||
*/
|
||||
int
|
||||
iobuf_move_zeros(struct iobuf *iob, size_t len, size_t *copiedp) {
|
||||
size_t alen;
|
||||
if ((alen = iob->io_resid) > len) {
|
||||
alen = len;
|
||||
}
|
||||
if (alen > 0) {
|
||||
memset(iob->io_base, 0, alen);
|
||||
iobuf_skip(iob, alen), len -= alen;
|
||||
}
|
||||
if (copiedp != NULL) {
|
||||
*copiedp = alen;
|
||||
}
|
||||
return (len == 0) ? 0 : -E_NO_MEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* iobuf_skip - change the current position of io buffer
|
||||
*/
|
||||
void
|
||||
iobuf_skip(struct iobuf *iob, size_t n) {
|
||||
assert(iob->io_resid >= n);
|
||||
iob->io_base += n, iob->io_offset += n, iob->io_resid -= n;
|
||||
}
|
||||
|
||||
24
labcodes/lab8/kern/fs/iobuf.h
Normal file
24
labcodes/lab8/kern/fs/iobuf.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef __KERN_FS_IOBUF_H__
|
||||
#define __KERN_FS_IOBUF_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
/*
|
||||
* iobuf is a buffer Rd/Wr status record
|
||||
*/
|
||||
struct iobuf {
|
||||
void *io_base; // the base addr of buffer (used for Rd/Wr)
|
||||
off_t io_offset; // current Rd/Wr position in buffer, will have been incremented by the amount transferred
|
||||
size_t io_len; // the length of buffer (used for Rd/Wr)
|
||||
size_t io_resid; // current resident length need to Rd/Wr, will have been decremented by the amount transferred.
|
||||
};
|
||||
|
||||
#define iobuf_used(iob) ((size_t)((iob)->io_len - (iob)->io_resid))
|
||||
|
||||
struct iobuf *iobuf_init(struct iobuf *iob, void *base, size_t len, off_t offset);
|
||||
int iobuf_move(struct iobuf *iob, void *data, size_t len, bool m2b, size_t *copiedp);
|
||||
int iobuf_move_zeros(struct iobuf *iob, size_t len, size_t *copiedp);
|
||||
void iobuf_skip(struct iobuf *iob, size_t n);
|
||||
|
||||
#endif /* !__KERN_FS_IOBUF_H__ */
|
||||
|
||||
114
labcodes/lab8/kern/fs/sfs/bitmap.c
Normal file
114
labcodes/lab8/kern/fs/sfs/bitmap.c
Normal file
@@ -0,0 +1,114 @@
|
||||
#include <defs.h>
|
||||
#include <string.h>
|
||||
#include <bitmap.h>
|
||||
#include <kmalloc.h>
|
||||
#include <error.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define WORD_TYPE uint32_t
|
||||
#define WORD_BITS (sizeof(WORD_TYPE) * CHAR_BIT)
|
||||
|
||||
struct bitmap {
|
||||
uint32_t nbits;
|
||||
uint32_t nwords;
|
||||
WORD_TYPE *map;
|
||||
};
|
||||
|
||||
// bitmap_create - allocate a new bitmap object.
|
||||
struct bitmap *
|
||||
bitmap_create(uint32_t nbits) {
|
||||
static_assert(WORD_BITS != 0);
|
||||
assert(nbits != 0 && nbits + WORD_BITS > nbits);
|
||||
|
||||
struct bitmap *bitmap;
|
||||
if ((bitmap = kmalloc(sizeof(struct bitmap))) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t nwords = ROUNDUP_DIV(nbits, WORD_BITS);
|
||||
WORD_TYPE *map;
|
||||
if ((map = kmalloc(sizeof(WORD_TYPE) * nwords)) == NULL) {
|
||||
kfree(bitmap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bitmap->nbits = nbits, bitmap->nwords = nwords;
|
||||
bitmap->map = memset(map, 0xFF, sizeof(WORD_TYPE) * nwords);
|
||||
|
||||
/* mark any leftover bits at the end in use(0) */
|
||||
if (nbits != nwords * WORD_BITS) {
|
||||
uint32_t ix = nwords - 1, overbits = nbits - ix * WORD_BITS;
|
||||
|
||||
assert(nbits / WORD_BITS == ix);
|
||||
assert(overbits > 0 && overbits < WORD_BITS);
|
||||
|
||||
for (; overbits < WORD_BITS; overbits ++) {
|
||||
bitmap->map[ix] ^= (1 << overbits);
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
// bitmap_alloc - locate a cleared bit, set it, and return its index.
|
||||
int
|
||||
bitmap_alloc(struct bitmap *bitmap, uint32_t *index_store) {
|
||||
WORD_TYPE *map = bitmap->map;
|
||||
uint32_t ix, offset, nwords = bitmap->nwords;
|
||||
for (ix = 0; ix < nwords; ix ++) {
|
||||
if (map[ix] != 0) {
|
||||
for (offset = 0; offset < WORD_BITS; offset ++) {
|
||||
WORD_TYPE mask = (1 << offset);
|
||||
if (map[ix] & mask) {
|
||||
map[ix] ^= mask;
|
||||
*index_store = ix * WORD_BITS + offset;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
return -E_NO_MEM;
|
||||
}
|
||||
|
||||
// bitmap_translate - according index, get the related word and mask
|
||||
static void
|
||||
bitmap_translate(struct bitmap *bitmap, uint32_t index, WORD_TYPE **word, WORD_TYPE *mask) {
|
||||
assert(index < bitmap->nbits);
|
||||
uint32_t ix = index / WORD_BITS, offset = index % WORD_BITS;
|
||||
*word = bitmap->map + ix;
|
||||
*mask = (1 << offset);
|
||||
}
|
||||
|
||||
// bitmap_test - according index, get the related value (0 OR 1) in the bitmap
|
||||
bool
|
||||
bitmap_test(struct bitmap *bitmap, uint32_t index) {
|
||||
WORD_TYPE *word, mask;
|
||||
bitmap_translate(bitmap, index, &word, &mask);
|
||||
return (*word & mask);
|
||||
}
|
||||
|
||||
// bitmap_free - according index, set related bit to 1
|
||||
void
|
||||
bitmap_free(struct bitmap *bitmap, uint32_t index) {
|
||||
WORD_TYPE *word, mask;
|
||||
bitmap_translate(bitmap, index, &word, &mask);
|
||||
assert(!(*word & mask));
|
||||
*word |= mask;
|
||||
}
|
||||
|
||||
// bitmap_destroy - free memory contains bitmap
|
||||
void
|
||||
bitmap_destroy(struct bitmap *bitmap) {
|
||||
kfree(bitmap->map);
|
||||
kfree(bitmap);
|
||||
}
|
||||
|
||||
// bitmap_getdata - return bitmap->map, return the length of bits to len_store
|
||||
void *
|
||||
bitmap_getdata(struct bitmap *bitmap, size_t *len_store) {
|
||||
if (len_store != NULL) {
|
||||
*len_store = sizeof(WORD_TYPE) * bitmap->nwords;
|
||||
}
|
||||
return bitmap->map;
|
||||
}
|
||||
|
||||
32
labcodes/lab8/kern/fs/sfs/bitmap.h
Normal file
32
labcodes/lab8/kern/fs/sfs/bitmap.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef __KERN_FS_SFS_BITMAP_H__
|
||||
#define __KERN_FS_SFS_BITMAP_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
|
||||
/*
|
||||
* Fixed-size array of bits. (Intended for storage management.)
|
||||
*
|
||||
* Functions:
|
||||
* bitmap_create - allocate a new bitmap object.
|
||||
* Returns NULL on error.
|
||||
* bitmap_getdata - return pointer to raw bit data (for I/O).
|
||||
* bitmap_alloc - locate a cleared bit, set it, and return its index.
|
||||
* bitmap_mark - set a clear bit by its index.
|
||||
* bitmap_unmark - clear a set bit by its index.
|
||||
* bitmap_isset - return whether a particular bit is set or not.
|
||||
* bitmap_destroy - destroy bitmap.
|
||||
*/
|
||||
|
||||
|
||||
struct bitmap;
|
||||
|
||||
struct bitmap *bitmap_create(uint32_t nbits); // allocate a new bitmap object.
|
||||
int bitmap_alloc(struct bitmap *bitmap, uint32_t *index_store); // locate a cleared bit, set it, and return its index.
|
||||
bool bitmap_test(struct bitmap *bitmap, uint32_t index); // return whether a particular bit is set or not.
|
||||
void bitmap_free(struct bitmap *bitmap, uint32_t index); // according index, set related bit to 1
|
||||
void bitmap_destroy(struct bitmap *bitmap); // free memory contains bitmap
|
||||
void *bitmap_getdata(struct bitmap *bitmap, size_t *len_store); // return pointer to raw bit data (for I/O)
|
||||
|
||||
#endif /* !__KERN_FS_SFS_BITMAP_H__ */
|
||||
|
||||
19
labcodes/lab8/kern/fs/sfs/sfs.c
Normal file
19
labcodes/lab8/kern/fs/sfs/sfs.c
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <defs.h>
|
||||
#include <sfs.h>
|
||||
#include <error.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
* sfs_init - mount sfs on disk0
|
||||
*
|
||||
* CALL GRAPH:
|
||||
* kern_init-->fs_init-->sfs_init
|
||||
*/
|
||||
void
|
||||
sfs_init(void) {
|
||||
int ret;
|
||||
if ((ret = sfs_mount("disk0")) != 0) {
|
||||
panic("failed: sfs: sfs_mount: %e.\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
129
labcodes/lab8/kern/fs/sfs/sfs.h
Normal file
129
labcodes/lab8/kern/fs/sfs/sfs.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#ifndef __KERN_FS_SFS_SFS_H__
|
||||
#define __KERN_FS_SFS_SFS_H__
|
||||
|
||||
#include <defs.h>
|
||||
#include <mmu.h>
|
||||
#include <list.h>
|
||||
#include <sem.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* Simple FS (SFS) definitions visible to ucore. This covers the on-disk format
|
||||
* and is used by tools that work on SFS volumes, such as mksfs.
|
||||
*/
|
||||
|
||||
#define SFS_MAGIC 0x2f8dbe2a /* magic number for sfs */
|
||||
#define SFS_BLKSIZE PGSIZE /* size of block */
|
||||
#define SFS_NDIRECT 12 /* # of direct blocks in inode */
|
||||
#define SFS_MAX_INFO_LEN 31 /* max length of infomation */
|
||||
#define SFS_MAX_FNAME_LEN FS_MAX_FNAME_LEN /* max length of filename */
|
||||
#define SFS_MAX_FILE_SIZE (1024UL * 1024 * 128) /* max file size (128M) */
|
||||
#define SFS_BLKN_SUPER 0 /* block the superblock lives in */
|
||||
#define SFS_BLKN_ROOT 1 /* location of the root dir inode */
|
||||
#define SFS_BLKN_FREEMAP 2 /* 1st block of the freemap */
|
||||
|
||||
/* # of bits in a block */
|
||||
#define SFS_BLKBITS (SFS_BLKSIZE * CHAR_BIT)
|
||||
|
||||
/* # of entries in a block */
|
||||
#define SFS_BLK_NENTRY (SFS_BLKSIZE / sizeof(uint32_t))
|
||||
|
||||
/* file types */
|
||||
#define SFS_TYPE_INVAL 0 /* Should not appear on disk */
|
||||
#define SFS_TYPE_FILE 1
|
||||
#define SFS_TYPE_DIR 2
|
||||
#define SFS_TYPE_LINK 3
|
||||
|
||||
/*
|
||||
* On-disk superblock
|
||||
*/
|
||||
struct sfs_super {
|
||||
uint32_t magic; /* magic number, should be SFS_MAGIC */
|
||||
uint32_t blocks; /* # of blocks in fs */
|
||||
uint32_t unused_blocks; /* # of unused blocks in fs */
|
||||
char info[SFS_MAX_INFO_LEN + 1]; /* infomation for sfs */
|
||||
};
|
||||
|
||||
/* inode (on disk) */
|
||||
struct sfs_disk_inode {
|
||||
uint32_t size; /* size of the file (in bytes) */
|
||||
uint16_t type; /* one of SYS_TYPE_* above */
|
||||
uint16_t nlinks; /* # of hard links to this file */
|
||||
uint32_t blocks; /* # of blocks */
|
||||
uint32_t direct[SFS_NDIRECT]; /* direct blocks */
|
||||
uint32_t indirect; /* indirect blocks */
|
||||
// uint32_t db_indirect; /* double indirect blocks */
|
||||
// unused
|
||||
};
|
||||
|
||||
/* file entry (on disk) */
|
||||
struct sfs_disk_entry {
|
||||
uint32_t ino; /* inode number */
|
||||
char name[SFS_MAX_FNAME_LEN + 1]; /* file name */
|
||||
};
|
||||
|
||||
#define sfs_dentry_size \
|
||||
sizeof(((struct sfs_disk_entry *)0)->name)
|
||||
|
||||
/* inode for sfs */
|
||||
struct sfs_inode {
|
||||
struct sfs_disk_inode *din; /* on-disk inode */
|
||||
uint32_t ino; /* inode number */
|
||||
bool dirty; /* true if inode modified */
|
||||
int reclaim_count; /* kill inode if it hits zero */
|
||||
semaphore_t sem; /* semaphore for din */
|
||||
list_entry_t inode_link; /* entry for linked-list in sfs_fs */
|
||||
list_entry_t hash_link; /* entry for hash linked-list in sfs_fs */
|
||||
};
|
||||
|
||||
#define le2sin(le, member) \
|
||||
to_struct((le), struct sfs_inode, member)
|
||||
|
||||
/* filesystem for sfs */
|
||||
struct sfs_fs {
|
||||
struct sfs_super super; /* on-disk superblock */
|
||||
struct device *dev; /* device mounted on */
|
||||
struct bitmap *freemap; /* blocks in use are mared 0 */
|
||||
bool super_dirty; /* true if super/freemap modified */
|
||||
void *sfs_buffer; /* buffer for non-block aligned io */
|
||||
semaphore_t fs_sem; /* semaphore for fs */
|
||||
semaphore_t io_sem; /* semaphore for io */
|
||||
semaphore_t mutex_sem; /* semaphore for link/unlink and rename */
|
||||
list_entry_t inode_list; /* inode linked-list */
|
||||
list_entry_t *hash_list; /* inode hash linked-list */
|
||||
};
|
||||
|
||||
/* hash for sfs */
|
||||
#define SFS_HLIST_SHIFT 10
|
||||
#define SFS_HLIST_SIZE (1 << SFS_HLIST_SHIFT)
|
||||
#define sin_hashfn(x) (hash32(x, SFS_HLIST_SHIFT))
|
||||
|
||||
/* size of freemap (in bits) */
|
||||
#define sfs_freemap_bits(super) ROUNDUP((super)->blocks, SFS_BLKBITS)
|
||||
|
||||
/* size of freemap (in blocks) */
|
||||
#define sfs_freemap_blocks(super) ROUNDUP_DIV((super)->blocks, SFS_BLKBITS)
|
||||
|
||||
struct fs;
|
||||
struct inode;
|
||||
|
||||
void sfs_init(void);
|
||||
int sfs_mount(const char *devname);
|
||||
|
||||
void lock_sfs_fs(struct sfs_fs *sfs);
|
||||
void lock_sfs_io(struct sfs_fs *sfs);
|
||||
void unlock_sfs_fs(struct sfs_fs *sfs);
|
||||
void unlock_sfs_io(struct sfs_fs *sfs);
|
||||
|
||||
int sfs_rblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks);
|
||||
int sfs_wblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks);
|
||||
int sfs_rbuf(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset);
|
||||
int sfs_wbuf(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset);
|
||||
int sfs_sync_super(struct sfs_fs *sfs);
|
||||
int sfs_sync_freemap(struct sfs_fs *sfs);
|
||||
int sfs_clear_block(struct sfs_fs *sfs, uint32_t blkno, uint32_t nblks);
|
||||
|
||||
int sfs_load_inode(struct sfs_fs *sfs, struct inode **node_store, uint32_t ino);
|
||||
|
||||
#endif /* !__KERN_FS_SFS_SFS_H__ */
|
||||
|
||||
258
labcodes/lab8/kern/fs/sfs/sfs_fs.c
Normal file
258
labcodes/lab8/kern/fs/sfs/sfs_fs.c
Normal file
@@ -0,0 +1,258 @@
|
||||
#include <defs.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <kmalloc.h>
|
||||
#include <list.h>
|
||||
#include <fs.h>
|
||||
#include <vfs.h>
|
||||
#include <dev.h>
|
||||
#include <sfs.h>
|
||||
#include <inode.h>
|
||||
#include <iobuf.h>
|
||||
#include <bitmap.h>
|
||||
#include <error.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
* sfs_sync - sync sfs's superblock and freemap in memroy into disk
|
||||
*/
|
||||
static int
|
||||
sfs_sync(struct fs *fs) {
|
||||
struct sfs_fs *sfs = fsop_info(fs, sfs);
|
||||
lock_sfs_fs(sfs);
|
||||
{
|
||||
list_entry_t *list = &(sfs->inode_list), *le = list;
|
||||
while ((le = list_next(le)) != list) {
|
||||
struct sfs_inode *sin = le2sin(le, inode_link);
|
||||
vop_fsync(info2node(sin, sfs_inode));
|
||||
}
|
||||
}
|
||||
unlock_sfs_fs(sfs);
|
||||
|
||||
int ret;
|
||||
if (sfs->super_dirty) {
|
||||
sfs->super_dirty = 0;
|
||||
if ((ret = sfs_sync_super(sfs)) != 0) {
|
||||
sfs->super_dirty = 1;
|
||||
return ret;
|
||||
}
|
||||
if ((ret = sfs_sync_freemap(sfs)) != 0) {
|
||||
sfs->super_dirty = 1;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_get_root - get the root directory inode from disk (SFS_BLKN_ROOT,1)
|
||||
*/
|
||||
static struct inode *
|
||||
sfs_get_root(struct fs *fs) {
|
||||
struct inode *node;
|
||||
int ret;
|
||||
if ((ret = sfs_load_inode(fsop_info(fs, sfs), &node, SFS_BLKN_ROOT)) != 0) {
|
||||
panic("load sfs root failed: %e", ret);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_unmount - unmount sfs, and free the memorys contain sfs->freemap/sfs_buffer/hash_liskt and sfs itself.
|
||||
*/
|
||||
static int
|
||||
sfs_unmount(struct fs *fs) {
|
||||
struct sfs_fs *sfs = fsop_info(fs, sfs);
|
||||
if (!list_empty(&(sfs->inode_list))) {
|
||||
return -E_BUSY;
|
||||
}
|
||||
assert(!sfs->super_dirty);
|
||||
bitmap_destroy(sfs->freemap);
|
||||
kfree(sfs->sfs_buffer);
|
||||
kfree(sfs->hash_list);
|
||||
kfree(sfs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_cleanup - when sfs failed, then should call this function to sync sfs by calling sfs_sync
|
||||
*
|
||||
* NOTICE: nouse now.
|
||||
*/
|
||||
static void
|
||||
sfs_cleanup(struct fs *fs) {
|
||||
struct sfs_fs *sfs = fsop_info(fs, sfs);
|
||||
uint32_t blocks = sfs->super.blocks, unused_blocks = sfs->super.unused_blocks;
|
||||
cprintf("sfs: cleanup: '%s' (%d/%d/%d)\n", sfs->super.info,
|
||||
blocks - unused_blocks, unused_blocks, blocks);
|
||||
int i, ret;
|
||||
for (i = 0; i < 32; i ++) {
|
||||
if ((ret = fsop_sync(fs)) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret != 0) {
|
||||
warn("sfs: sync error: '%s': %e.\n", sfs->super.info, ret);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_init_read - used in sfs_do_mount to read disk block(blkno, 1) directly.
|
||||
*
|
||||
* @dev: the block device
|
||||
* @blkno: the NO. of disk block
|
||||
* @blk_buffer: the buffer used for read
|
||||
*
|
||||
* (1) init iobuf
|
||||
* (2) read dev into iobuf
|
||||
*/
|
||||
static int
|
||||
sfs_init_read(struct device *dev, uint32_t blkno, void *blk_buffer) {
|
||||
struct iobuf __iob, *iob = iobuf_init(&__iob, blk_buffer, SFS_BLKSIZE, blkno * SFS_BLKSIZE);
|
||||
return dop_io(dev, iob, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_init_freemap - used in sfs_do_mount to read freemap data info in disk block(blkno, nblks) directly.
|
||||
*
|
||||
* @dev: the block device
|
||||
* @bitmap: the bitmap in memroy
|
||||
* @blkno: the NO. of disk block
|
||||
* @nblks: Rd number of disk block
|
||||
* @blk_buffer: the buffer used for read
|
||||
*
|
||||
* (1) get data addr in bitmap
|
||||
* (2) read dev into iobuf
|
||||
*/
|
||||
static int
|
||||
sfs_init_freemap(struct device *dev, struct bitmap *freemap, uint32_t blkno, uint32_t nblks, void *blk_buffer) {
|
||||
size_t len;
|
||||
void *data = bitmap_getdata(freemap, &len);
|
||||
assert(data != NULL && len == nblks * SFS_BLKSIZE);
|
||||
while (nblks != 0) {
|
||||
int ret;
|
||||
if ((ret = sfs_init_read(dev, blkno, data)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
blkno ++, nblks --, data += SFS_BLKSIZE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_do_mount - mount sfs file system.
|
||||
*
|
||||
* @dev: the block device contains sfs file system
|
||||
* @fs_store: the fs struct in memroy
|
||||
*/
|
||||
static int
|
||||
sfs_do_mount(struct device *dev, struct fs **fs_store) {
|
||||
static_assert(SFS_BLKSIZE >= sizeof(struct sfs_super));
|
||||
static_assert(SFS_BLKSIZE >= sizeof(struct sfs_disk_inode));
|
||||
static_assert(SFS_BLKSIZE >= sizeof(struct sfs_disk_entry));
|
||||
|
||||
if (dev->d_blocksize != SFS_BLKSIZE) {
|
||||
return -E_NA_DEV;
|
||||
}
|
||||
|
||||
/* allocate fs structure */
|
||||
struct fs *fs;
|
||||
if ((fs = alloc_fs(sfs)) == NULL) {
|
||||
return -E_NO_MEM;
|
||||
}
|
||||
struct sfs_fs *sfs = fsop_info(fs, sfs);
|
||||
sfs->dev = dev;
|
||||
|
||||
int ret = -E_NO_MEM;
|
||||
|
||||
void *sfs_buffer;
|
||||
if ((sfs->sfs_buffer = sfs_buffer = kmalloc(SFS_BLKSIZE)) == NULL) {
|
||||
goto failed_cleanup_fs;
|
||||
}
|
||||
|
||||
/* load and check superblock */
|
||||
if ((ret = sfs_init_read(dev, SFS_BLKN_SUPER, sfs_buffer)) != 0) {
|
||||
goto failed_cleanup_sfs_buffer;
|
||||
}
|
||||
|
||||
ret = -E_INVAL;
|
||||
|
||||
struct sfs_super *super = sfs_buffer;
|
||||
if (super->magic != SFS_MAGIC) {
|
||||
cprintf("sfs: wrong magic in superblock. (%08x should be %08x).\n",
|
||||
super->magic, SFS_MAGIC);
|
||||
goto failed_cleanup_sfs_buffer;
|
||||
}
|
||||
if (super->blocks > dev->d_blocks) {
|
||||
cprintf("sfs: fs has %u blocks, device has %u blocks.\n",
|
||||
super->blocks, dev->d_blocks);
|
||||
goto failed_cleanup_sfs_buffer;
|
||||
}
|
||||
super->info[SFS_MAX_INFO_LEN] = '\0';
|
||||
sfs->super = *super;
|
||||
|
||||
ret = -E_NO_MEM;
|
||||
|
||||
uint32_t i;
|
||||
|
||||
/* alloc and initialize hash list */
|
||||
list_entry_t *hash_list;
|
||||
if ((sfs->hash_list = hash_list = kmalloc(sizeof(list_entry_t) * SFS_HLIST_SIZE)) == NULL) {
|
||||
goto failed_cleanup_sfs_buffer;
|
||||
}
|
||||
for (i = 0; i < SFS_HLIST_SIZE; i ++) {
|
||||
list_init(hash_list + i);
|
||||
}
|
||||
|
||||
/* load and check freemap */
|
||||
struct bitmap *freemap;
|
||||
uint32_t freemap_size_nbits = sfs_freemap_bits(super);
|
||||
if ((sfs->freemap = freemap = bitmap_create(freemap_size_nbits)) == NULL) {
|
||||
goto failed_cleanup_hash_list;
|
||||
}
|
||||
uint32_t freemap_size_nblks = sfs_freemap_blocks(super);
|
||||
if ((ret = sfs_init_freemap(dev, freemap, SFS_BLKN_FREEMAP, freemap_size_nblks, sfs_buffer)) != 0) {
|
||||
goto failed_cleanup_freemap;
|
||||
}
|
||||
|
||||
uint32_t blocks = sfs->super.blocks, unused_blocks = 0;
|
||||
for (i = 0; i < freemap_size_nbits; i ++) {
|
||||
if (bitmap_test(freemap, i)) {
|
||||
unused_blocks ++;
|
||||
}
|
||||
}
|
||||
assert(unused_blocks == sfs->super.unused_blocks);
|
||||
|
||||
/* and other fields */
|
||||
sfs->super_dirty = 0;
|
||||
sem_init(&(sfs->fs_sem), 1);
|
||||
sem_init(&(sfs->io_sem), 1);
|
||||
sem_init(&(sfs->mutex_sem), 1);
|
||||
list_init(&(sfs->inode_list));
|
||||
cprintf("sfs: mount: '%s' (%d/%d/%d)\n", sfs->super.info,
|
||||
blocks - unused_blocks, unused_blocks, blocks);
|
||||
|
||||
/* link addr of sync/get_root/unmount/cleanup funciton fs's function pointers*/
|
||||
fs->fs_sync = sfs_sync;
|
||||
fs->fs_get_root = sfs_get_root;
|
||||
fs->fs_unmount = sfs_unmount;
|
||||
fs->fs_cleanup = sfs_cleanup;
|
||||
*fs_store = fs;
|
||||
return 0;
|
||||
|
||||
failed_cleanup_freemap:
|
||||
bitmap_destroy(freemap);
|
||||
failed_cleanup_hash_list:
|
||||
kfree(hash_list);
|
||||
failed_cleanup_sfs_buffer:
|
||||
kfree(sfs_buffer);
|
||||
failed_cleanup_fs:
|
||||
kfree(fs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
sfs_mount(const char *devname) {
|
||||
return vfs_mount(devname, sfs_do_mount);
|
||||
}
|
||||
|
||||
987
labcodes/lab8/kern/fs/sfs/sfs_inode.c
Normal file
987
labcodes/lab8/kern/fs/sfs/sfs_inode.c
Normal file
@@ -0,0 +1,987 @@
|
||||
#include <defs.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <list.h>
|
||||
#include <stat.h>
|
||||
#include <kmalloc.h>
|
||||
#include <vfs.h>
|
||||
#include <dev.h>
|
||||
#include <sfs.h>
|
||||
#include <inode.h>
|
||||
#include <iobuf.h>
|
||||
#include <bitmap.h>
|
||||
#include <error.h>
|
||||
#include <assert.h>
|
||||
|
||||
static const struct inode_ops sfs_node_dirops; // dir operations
|
||||
static const struct inode_ops sfs_node_fileops; // file operations
|
||||
|
||||
/*
|
||||
* lock_sin - lock the process of inode Rd/Wr
|
||||
*/
|
||||
static void
|
||||
lock_sin(struct sfs_inode *sin) {
|
||||
down(&(sin->sem));
|
||||
}
|
||||
|
||||
/*
|
||||
* unlock_sin - unlock the process of inode Rd/Wr
|
||||
*/
|
||||
static void
|
||||
unlock_sin(struct sfs_inode *sin) {
|
||||
up(&(sin->sem));
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_get_ops - return function addr of fs_node_dirops/sfs_node_fileops
|
||||
*/
|
||||
static const struct inode_ops *
|
||||
sfs_get_ops(uint16_t type) {
|
||||
switch (type) {
|
||||
case SFS_TYPE_DIR:
|
||||
return &sfs_node_dirops;
|
||||
case SFS_TYPE_FILE:
|
||||
return &sfs_node_fileops;
|
||||
}
|
||||
panic("invalid file type %d.\n", type);
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_hash_list - return inode entry in sfs->hash_list
|
||||
*/
|
||||
static list_entry_t *
|
||||
sfs_hash_list(struct sfs_fs *sfs, uint32_t ino) {
|
||||
return sfs->hash_list + sin_hashfn(ino);
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_set_links - link inode sin in sfs->linked-list AND sfs->hash_link
|
||||
*/
|
||||
static void
|
||||
sfs_set_links(struct sfs_fs *sfs, struct sfs_inode *sin) {
|
||||
list_add(&(sfs->inode_list), &(sin->inode_link));
|
||||
list_add(sfs_hash_list(sfs, sin->ino), &(sin->hash_link));
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_remove_links - unlink inode sin in sfs->linked-list AND sfs->hash_link
|
||||
*/
|
||||
static void
|
||||
sfs_remove_links(struct sfs_inode *sin) {
|
||||
list_del(&(sin->inode_link));
|
||||
list_del(&(sin->hash_link));
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_block_inuse - check the inode with NO. ino inuse info in bitmap
|
||||
*/
|
||||
static bool
|
||||
sfs_block_inuse(struct sfs_fs *sfs, uint32_t ino) {
|
||||
if (ino != 0 && ino < sfs->super.blocks) {
|
||||
return !bitmap_test(sfs->freemap, ino);
|
||||
}
|
||||
panic("sfs_block_inuse: called out of range (0, %u) %u.\n", sfs->super.blocks, ino);
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_block_alloc - check and get a free disk block
|
||||
*/
|
||||
static int
|
||||
sfs_block_alloc(struct sfs_fs *sfs, uint32_t *ino_store) {
|
||||
int ret;
|
||||
if ((ret = bitmap_alloc(sfs->freemap, ino_store)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
assert(sfs->super.unused_blocks > 0);
|
||||
sfs->super.unused_blocks --, sfs->super_dirty = 1;
|
||||
assert(sfs_block_inuse(sfs, *ino_store));
|
||||
return sfs_clear_block(sfs, *ino_store, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_block_free - set related bits for ino block to 1(means free) in bitmap, add sfs->super.unused_blocks, set superblock dirty *
|
||||
*/
|
||||
static void
|
||||
sfs_block_free(struct sfs_fs *sfs, uint32_t ino) {
|
||||
assert(sfs_block_inuse(sfs, ino));
|
||||
bitmap_free(sfs->freemap, ino);
|
||||
sfs->super.unused_blocks ++, sfs->super_dirty = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_create_inode - alloc a inode in memroy, and init din/ino/dirty/reclian_count/sem fields in sfs_inode in inode
|
||||
*/
|
||||
static int
|
||||
sfs_create_inode(struct sfs_fs *sfs, struct sfs_disk_inode *din, uint32_t ino, struct inode **node_store) {
|
||||
struct inode *node;
|
||||
if ((node = alloc_inode(sfs_inode)) != NULL) {
|
||||
vop_init(node, sfs_get_ops(din->type), info2fs(sfs, sfs));
|
||||
struct sfs_inode *sin = vop_info(node, sfs_inode);
|
||||
sin->din = din, sin->ino = ino, sin->dirty = 0, sin->reclaim_count = 1;
|
||||
sem_init(&(sin->sem), 1);
|
||||
*node_store = node;
|
||||
return 0;
|
||||
}
|
||||
return -E_NO_MEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* lookup_sfs_nolock - according ino, find related inode
|
||||
*
|
||||
* NOTICE: le2sin, info2node MACRO
|
||||
*/
|
||||
static struct inode *
|
||||
lookup_sfs_nolock(struct sfs_fs *sfs, uint32_t ino) {
|
||||
struct inode *node;
|
||||
list_entry_t *list = sfs_hash_list(sfs, ino), *le = list;
|
||||
while ((le = list_next(le)) != list) {
|
||||
struct sfs_inode *sin = le2sin(le, hash_link);
|
||||
if (sin->ino == ino) {
|
||||
node = info2node(sin, sfs_inode);
|
||||
if (vop_ref_inc(node) == 1) {
|
||||
sin->reclaim_count ++;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_load_inode - If the inode isn't existed, load inode related ino disk block data into a new created inode.
|
||||
* If the inode is in memory alreadily, then do nothing
|
||||
*/
|
||||
int
|
||||
sfs_load_inode(struct sfs_fs *sfs, struct inode **node_store, uint32_t ino) {
|
||||
lock_sfs_fs(sfs);
|
||||
struct inode *node;
|
||||
if ((node = lookup_sfs_nolock(sfs, ino)) != NULL) {
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
int ret = -E_NO_MEM;
|
||||
struct sfs_disk_inode *din;
|
||||
if ((din = kmalloc(sizeof(struct sfs_disk_inode))) == NULL) {
|
||||
goto failed_unlock;
|
||||
}
|
||||
|
||||
assert(sfs_block_inuse(sfs, ino));
|
||||
if ((ret = sfs_rbuf(sfs, din, sizeof(struct sfs_disk_inode), ino, 0)) != 0) {
|
||||
goto failed_cleanup_din;
|
||||
}
|
||||
|
||||
assert(din->nlinks != 0);
|
||||
if ((ret = sfs_create_inode(sfs, din, ino, &node)) != 0) {
|
||||
goto failed_cleanup_din;
|
||||
}
|
||||
sfs_set_links(sfs, vop_info(node, sfs_inode));
|
||||
|
||||
out_unlock:
|
||||
unlock_sfs_fs(sfs);
|
||||
*node_store = node;
|
||||
return 0;
|
||||
|
||||
failed_cleanup_din:
|
||||
kfree(din);
|
||||
failed_unlock:
|
||||
unlock_sfs_fs(sfs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_bmap_get_sub_nolock - according entry pointer entp and index, find the index of indrect disk block
|
||||
* return the index of indrect disk block to ino_store. no lock protect
|
||||
* @sfs: sfs file system
|
||||
* @entp: the pointer of index of entry disk block
|
||||
* @index: the index of block in indrect block
|
||||
* @create: BOOL, if the block isn't allocated, if create = 1 the alloc a block, otherwise just do nothing
|
||||
* @ino_store: 0 OR the index of already inused block or new allocated block.
|
||||
*/
|
||||
static int
|
||||
sfs_bmap_get_sub_nolock(struct sfs_fs *sfs, uint32_t *entp, uint32_t index, bool create, uint32_t *ino_store) {
|
||||
assert(index < SFS_BLK_NENTRY);
|
||||
int ret;
|
||||
uint32_t ent, ino = 0;
|
||||
off_t offset = index * sizeof(uint32_t); // the offset of entry in entry block
|
||||
// if entry block is existd, read the content of entry block into sfs->sfs_buffer
|
||||
if ((ent = *entp) != 0) {
|
||||
if ((ret = sfs_rbuf(sfs, &ino, sizeof(uint32_t), ent, offset)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ino != 0 || !create) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!create) {
|
||||
goto out;
|
||||
}
|
||||
//if entry block isn't existd, allocated a entry block (for indrect block)
|
||||
if ((ret = sfs_block_alloc(sfs, &ent)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if ((ret = sfs_block_alloc(sfs, &ino)) != 0) {
|
||||
goto failed_cleanup;
|
||||
}
|
||||
if ((ret = sfs_wbuf(sfs, &ino, sizeof(uint32_t), ent, offset)) != 0) {
|
||||
sfs_block_free(sfs, ino);
|
||||
goto failed_cleanup;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ent != *entp) {
|
||||
*entp = ent;
|
||||
}
|
||||
*ino_store = ino;
|
||||
return 0;
|
||||
|
||||
failed_cleanup:
|
||||
if (ent != *entp) {
|
||||
sfs_block_free(sfs, ent);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_bmap_get_nolock - according sfs_inode and index of block, find the NO. of disk block
|
||||
* no lock protect
|
||||
* @sfs: sfs file system
|
||||
* @sin: sfs inode in memory
|
||||
* @index: the index of block in inode
|
||||
* @create: BOOL, if the block isn't allocated, if create = 1 the alloc a block, otherwise just do nothing
|
||||
* @ino_store: 0 OR the index of already inused block or new allocated block.
|
||||
*/
|
||||
static int
|
||||
sfs_bmap_get_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, uint32_t index, bool create, uint32_t *ino_store) {
|
||||
struct sfs_disk_inode *din = sin->din;
|
||||
int ret;
|
||||
uint32_t ent, ino;
|
||||
// the index of disk block is in the fist SFS_NDIRECT direct blocks
|
||||
if (index < SFS_NDIRECT) {
|
||||
if ((ino = din->direct[index]) == 0 && create) {
|
||||
if ((ret = sfs_block_alloc(sfs, &ino)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
din->direct[index] = ino;
|
||||
sin->dirty = 1;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
// the index of disk block is in the indirect blocks.
|
||||
index -= SFS_NDIRECT;
|
||||
if (index < SFS_BLK_NENTRY) {
|
||||
ent = din->indirect;
|
||||
if ((ret = sfs_bmap_get_sub_nolock(sfs, &ent, index, create, &ino)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ent != din->indirect) {
|
||||
assert(din->indirect == 0);
|
||||
din->indirect = ent;
|
||||
sin->dirty = 1;
|
||||
}
|
||||
goto out;
|
||||
} else {
|
||||
panic ("sfs_bmap_get_nolock - index out of range");
|
||||
}
|
||||
out:
|
||||
assert(ino == 0 || sfs_block_inuse(sfs, ino));
|
||||
*ino_store = ino;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_bmap_free_sub_nolock - set the entry item to 0 (free) in the indirect block
|
||||
*/
|
||||
static int
|
||||
sfs_bmap_free_sub_nolock(struct sfs_fs *sfs, uint32_t ent, uint32_t index) {
|
||||
assert(sfs_block_inuse(sfs, ent) && index < SFS_BLK_NENTRY);
|
||||
int ret;
|
||||
uint32_t ino, zero = 0;
|
||||
off_t offset = index * sizeof(uint32_t);
|
||||
if ((ret = sfs_rbuf(sfs, &ino, sizeof(uint32_t), ent, offset)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ino != 0) {
|
||||
if ((ret = sfs_wbuf(sfs, &zero, sizeof(uint32_t), ent, offset)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
sfs_block_free(sfs, ino);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_bmap_free_nolock - free a block with logical index in inode and reset the inode's fields
|
||||
*/
|
||||
static int
|
||||
sfs_bmap_free_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, uint32_t index) {
|
||||
struct sfs_disk_inode *din = sin->din;
|
||||
int ret;
|
||||
uint32_t ent, ino;
|
||||
if (index < SFS_NDIRECT) {
|
||||
if ((ino = din->direct[index]) != 0) {
|
||||
// free the block
|
||||
sfs_block_free(sfs, ino);
|
||||
din->direct[index] = 0;
|
||||
sin->dirty = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
index -= SFS_NDIRECT;
|
||||
if (index < SFS_BLK_NENTRY) {
|
||||
if ((ent = din->indirect) != 0) {
|
||||
// set the entry item to 0 in the indirect block
|
||||
if ((ret = sfs_bmap_free_sub_nolock(sfs, ent, index)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_bmap_load_nolock - according to the DIR's inode and the logical index of block in inode, find the NO. of disk block.
|
||||
* @sfs: sfs file system
|
||||
* @sin: sfs inode in memory
|
||||
* @index: the logical index of disk block in inode
|
||||
* @ino_store:the NO. of disk block
|
||||
*/
|
||||
static int
|
||||
sfs_bmap_load_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, uint32_t index, uint32_t *ino_store) {
|
||||
struct sfs_disk_inode *din = sin->din;
|
||||
assert(index <= din->blocks);
|
||||
int ret;
|
||||
uint32_t ino;
|
||||
bool create = (index == din->blocks);
|
||||
if ((ret = sfs_bmap_get_nolock(sfs, sin, index, create, &ino)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
assert(sfs_block_inuse(sfs, ino));
|
||||
if (create) {
|
||||
din->blocks ++;
|
||||
}
|
||||
if (ino_store != NULL) {
|
||||
*ino_store = ino;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_bmap_truncate_nolock - free the disk block at the end of file
|
||||
*/
|
||||
static int
|
||||
sfs_bmap_truncate_nolock(struct sfs_fs *sfs, struct sfs_inode *sin) {
|
||||
struct sfs_disk_inode *din = sin->din;
|
||||
assert(din->blocks != 0);
|
||||
int ret;
|
||||
if ((ret = sfs_bmap_free_nolock(sfs, sin, din->blocks - 1)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
din->blocks --;
|
||||
sin->dirty = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_dirent_read_nolock - read the file entry from disk block which contains this entry
|
||||
* @sfs: sfs file system
|
||||
* @sin: sfs inode in memory
|
||||
* @slot: the index of file entry
|
||||
* @entry: file entry
|
||||
*/
|
||||
static int
|
||||
sfs_dirent_read_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, int slot, struct sfs_disk_entry *entry) {
|
||||
assert(sin->din->type == SFS_TYPE_DIR && (slot >= 0 && slot < sin->din->blocks));
|
||||
int ret;
|
||||
uint32_t ino;
|
||||
// according to the DIR's inode and the slot of file entry, find the index of disk block which contains this file entry
|
||||
if ((ret = sfs_bmap_load_nolock(sfs, sin, slot, &ino)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
assert(sfs_block_inuse(sfs, ino));
|
||||
// read the content of file entry in the disk block
|
||||
if ((ret = sfs_rbuf(sfs, entry, sizeof(struct sfs_disk_entry), ino, 0)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
entry->name[SFS_MAX_FNAME_LEN] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define sfs_dirent_link_nolock_check(sfs, sin, slot, lnksin, name) \
|
||||
do { \
|
||||
int err; \
|
||||
if ((err = sfs_dirent_link_nolock(sfs, sin, slot, lnksin, name)) != 0) { \
|
||||
warn("sfs_dirent_link error: %e.\n", err); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define sfs_dirent_unlink_nolock_check(sfs, sin, slot, lnksin) \
|
||||
do { \
|
||||
int err; \
|
||||
if ((err = sfs_dirent_unlink_nolock(sfs, sin, slot, lnksin)) != 0) { \
|
||||
warn("sfs_dirent_unlink error: %e.\n", err); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* sfs_dirent_search_nolock - read every file entry in the DIR, compare file name with each entry->name
|
||||
* If equal, then return slot and NO. of disk of this file's inode
|
||||
* @sfs: sfs file system
|
||||
* @sin: sfs inode in memory
|
||||
* @name: the filename
|
||||
* @ino_store: NO. of disk of this file (with the filename)'s inode
|
||||
* @slot: logical index of file entry (NOTICE: each file entry ocupied one disk block)
|
||||
* @empty_slot: the empty logical index of file entry.
|
||||
*/
|
||||
static int
|
||||
sfs_dirent_search_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, const char *name, uint32_t *ino_store, int *slot, int *empty_slot) {
|
||||
assert(strlen(name) <= SFS_MAX_FNAME_LEN);
|
||||
struct sfs_disk_entry *entry;
|
||||
if ((entry = kmalloc(sizeof(struct sfs_disk_entry))) == NULL) {
|
||||
return -E_NO_MEM;
|
||||
}
|
||||
|
||||
#define set_pvalue(x, v) do { if ((x) != NULL) { *(x) = (v); } } while (0)
|
||||
int ret, i, nslots = sin->din->blocks;
|
||||
set_pvalue(empty_slot, nslots);
|
||||
for (i = 0; i < nslots; i ++) {
|
||||
if ((ret = sfs_dirent_read_nolock(sfs, sin, i, entry)) != 0) {
|
||||
goto out;
|
||||
}
|
||||
if (entry->ino == 0) {
|
||||
set_pvalue(empty_slot, i);
|
||||
continue ;
|
||||
}
|
||||
if (strcmp(name, entry->name) == 0) {
|
||||
set_pvalue(slot, i);
|
||||
set_pvalue(ino_store, entry->ino);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
#undef set_pvalue
|
||||
ret = -E_NOENT;
|
||||
out:
|
||||
kfree(entry);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_dirent_findino_nolock - read all file entries in DIR's inode and find a entry->ino == ino
|
||||
*/
|
||||
|
||||
static int
|
||||
sfs_dirent_findino_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, uint32_t ino, struct sfs_disk_entry *entry) {
|
||||
int ret, i, nslots = sin->din->blocks;
|
||||
for (i = 0; i < nslots; i ++) {
|
||||
if ((ret = sfs_dirent_read_nolock(sfs, sin, i, entry)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
if (entry->ino == ino) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -E_NOENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_lookup_once - find inode corresponding the file name in DIR's sin inode
|
||||
* @sfs: sfs file system
|
||||
* @sin: DIR sfs inode in memory
|
||||
* @name: the file name in DIR
|
||||
* @node_store: the inode corresponding the file name in DIR
|
||||
* @slot: the logical index of file entry
|
||||
*/
|
||||
static int
|
||||
sfs_lookup_once(struct sfs_fs *sfs, struct sfs_inode *sin, const char *name, struct inode **node_store, int *slot) {
|
||||
int ret;
|
||||
uint32_t ino;
|
||||
lock_sin(sin);
|
||||
{ // find the NO. of disk block and logical index of file entry
|
||||
ret = sfs_dirent_search_nolock(sfs, sin, name, &ino, slot, NULL);
|
||||
}
|
||||
unlock_sin(sin);
|
||||
if (ret == 0) {
|
||||
// load the content of inode with the the NO. of disk block
|
||||
ret = sfs_load_inode(sfs, node_store, ino);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// sfs_opendir - just check the opne_flags, now support readonly
|
||||
static int
|
||||
sfs_opendir(struct inode *node, uint32_t open_flags) {
|
||||
switch (open_flags & O_ACCMODE) {
|
||||
case O_RDONLY:
|
||||
break;
|
||||
case O_WRONLY:
|
||||
case O_RDWR:
|
||||
default:
|
||||
return -E_ISDIR;
|
||||
}
|
||||
if (open_flags & O_APPEND) {
|
||||
return -E_ISDIR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// sfs_openfile - open file (no use)
|
||||
static int
|
||||
sfs_openfile(struct inode *node, uint32_t open_flags) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// sfs_close - close file
|
||||
static int
|
||||
sfs_close(struct inode *node) {
|
||||
return vop_fsync(node);
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_io_nolock - Rd/Wr a file contentfrom offset position to offset+ length disk blocks<-->buffer (in memroy)
|
||||
* @sfs: sfs file system
|
||||
* @sin: sfs inode in memory
|
||||
* @buf: the buffer Rd/Wr
|
||||
* @offset: the offset of file
|
||||
* @alenp: the length need to read (is a pointer). and will RETURN the really Rd/Wr lenght
|
||||
* @write: BOOL, 0 read, 1 write
|
||||
*/
|
||||
static int
|
||||
sfs_io_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, void *buf, off_t offset, size_t *alenp, bool write) {
|
||||
struct sfs_disk_inode *din = sin->din;
|
||||
assert(din->type != SFS_TYPE_DIR);
|
||||
off_t endpos = offset + *alenp, blkoff;
|
||||
*alenp = 0;
|
||||
// calculate the Rd/Wr end position
|
||||
if (offset < 0 || offset >= SFS_MAX_FILE_SIZE || offset > endpos) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
if (offset == endpos) {
|
||||
return 0;
|
||||
}
|
||||
if (endpos > SFS_MAX_FILE_SIZE) {
|
||||
endpos = SFS_MAX_FILE_SIZE;
|
||||
}
|
||||
if (!write) {
|
||||
if (offset >= din->size) {
|
||||
return 0;
|
||||
}
|
||||
if (endpos > din->size) {
|
||||
endpos = din->size;
|
||||
}
|
||||
}
|
||||
|
||||
int (*sfs_buf_op)(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset);
|
||||
int (*sfs_block_op)(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks);
|
||||
if (write) {
|
||||
sfs_buf_op = sfs_wbuf, sfs_block_op = sfs_wblock;
|
||||
}
|
||||
else {
|
||||
sfs_buf_op = sfs_rbuf, sfs_block_op = sfs_rblock;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
size_t size, alen = 0;
|
||||
uint32_t ino;
|
||||
uint32_t blkno = offset / SFS_BLKSIZE; // The NO. of Rd/Wr begin block
|
||||
uint32_t nblks = endpos / SFS_BLKSIZE - blkno; // The size of Rd/Wr blocks
|
||||
|
||||
//LAB8:EXERCISE1 YOUR CODE HINT: call sfs_bmap_load_nolock, sfs_rbuf, sfs_rblock,etc. read different kind of blocks in file
|
||||
/*
|
||||
* (1) If offset isn't aligned with the first block, Rd/Wr some content from offset to the end of the first block
|
||||
* NOTICE: useful function: sfs_bmap_load_nolock, sfs_buf_op
|
||||
* Rd/Wr size = (nblks != 0) ? (SFS_BLKSIZE - blkoff) : (endpos - offset)
|
||||
* (2) Rd/Wr aligned blocks
|
||||
* NOTICE: useful function: sfs_bmap_load_nolock, sfs_block_op
|
||||
* (3) If end position isn't aligned with the last block, Rd/Wr some content from begin to the (endpos % SFS_BLKSIZE) of the last block
|
||||
* NOTICE: useful function: sfs_bmap_load_nolock, sfs_buf_op
|
||||
*/
|
||||
out:
|
||||
*alenp = alen;
|
||||
if (offset + alen > sin->din->size) {
|
||||
sin->din->size = offset + alen;
|
||||
sin->dirty = 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_io - Rd/Wr file. the wrapper of sfs_io_nolock
|
||||
with lock protect
|
||||
*/
|
||||
static inline int
|
||||
sfs_io(struct inode *node, struct iobuf *iob, bool write) {
|
||||
struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs);
|
||||
struct sfs_inode *sin = vop_info(node, sfs_inode);
|
||||
int ret;
|
||||
lock_sin(sin);
|
||||
{
|
||||
size_t alen = iob->io_resid;
|
||||
ret = sfs_io_nolock(sfs, sin, iob->io_base, iob->io_offset, &alen, write);
|
||||
if (alen != 0) {
|
||||
iobuf_skip(iob, alen);
|
||||
}
|
||||
}
|
||||
unlock_sin(sin);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// sfs_read - read file
|
||||
static int
|
||||
sfs_read(struct inode *node, struct iobuf *iob) {
|
||||
return sfs_io(node, iob, 0);
|
||||
}
|
||||
|
||||
// sfs_write - write file
|
||||
static int
|
||||
sfs_write(struct inode *node, struct iobuf *iob) {
|
||||
return sfs_io(node, iob, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_fstat - Return nlinks/block/size, etc. info about a file. The pointer is a pointer to struct stat;
|
||||
*/
|
||||
static int
|
||||
sfs_fstat(struct inode *node, struct stat *stat) {
|
||||
int ret;
|
||||
memset(stat, 0, sizeof(struct stat));
|
||||
if ((ret = vop_gettype(node, &(stat->st_mode))) != 0) {
|
||||
return ret;
|
||||
}
|
||||
struct sfs_disk_inode *din = vop_info(node, sfs_inode)->din;
|
||||
stat->st_nlinks = din->nlinks;
|
||||
stat->st_blocks = din->blocks;
|
||||
stat->st_size = din->size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_fsync - Force any dirty inode info associated with this file to stable storage.
|
||||
*/
|
||||
static int
|
||||
sfs_fsync(struct inode *node) {
|
||||
struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs);
|
||||
struct sfs_inode *sin = vop_info(node, sfs_inode);
|
||||
int ret = 0;
|
||||
if (sin->dirty) {
|
||||
lock_sin(sin);
|
||||
{
|
||||
if (sin->dirty) {
|
||||
sin->dirty = 0;
|
||||
if ((ret = sfs_wbuf(sfs, sin->din, sizeof(struct sfs_disk_inode), sin->ino, 0)) != 0) {
|
||||
sin->dirty = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
unlock_sin(sin);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*sfs_namefile -Compute pathname relative to filesystem root of the file and copy to the specified io buffer.
|
||||
*
|
||||
*/
|
||||
static int
|
||||
sfs_namefile(struct inode *node, struct iobuf *iob) {
|
||||
struct sfs_disk_entry *entry;
|
||||
if (iob->io_resid <= 2 || (entry = kmalloc(sizeof(struct sfs_disk_entry))) == NULL) {
|
||||
return -E_NO_MEM;
|
||||
}
|
||||
|
||||
struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs);
|
||||
struct sfs_inode *sin = vop_info(node, sfs_inode);
|
||||
|
||||
int ret;
|
||||
char *ptr = iob->io_base + iob->io_resid;
|
||||
size_t alen, resid = iob->io_resid - 2;
|
||||
vop_ref_inc(node);
|
||||
while (1) {
|
||||
struct inode *parent;
|
||||
if ((ret = sfs_lookup_once(sfs, sin, "..", &parent, NULL)) != 0) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
uint32_t ino = sin->ino;
|
||||
vop_ref_dec(node);
|
||||
if (node == parent) {
|
||||
vop_ref_dec(node);
|
||||
break;
|
||||
}
|
||||
|
||||
node = parent, sin = vop_info(node, sfs_inode);
|
||||
assert(ino != sin->ino && sin->din->type == SFS_TYPE_DIR);
|
||||
|
||||
lock_sin(sin);
|
||||
{
|
||||
ret = sfs_dirent_findino_nolock(sfs, sin, ino, entry);
|
||||
}
|
||||
unlock_sin(sin);
|
||||
|
||||
if (ret != 0) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if ((alen = strlen(entry->name) + 1) > resid) {
|
||||
goto failed_nomem;
|
||||
}
|
||||
resid -= alen, ptr -= alen;
|
||||
memcpy(ptr, entry->name, alen - 1);
|
||||
ptr[alen - 1] = '/';
|
||||
}
|
||||
alen = iob->io_resid - resid - 2;
|
||||
ptr = memmove(iob->io_base + 1, ptr, alen);
|
||||
ptr[-1] = '/', ptr[alen] = '\0';
|
||||
iobuf_skip(iob, alen);
|
||||
kfree(entry);
|
||||
return 0;
|
||||
|
||||
failed_nomem:
|
||||
ret = -E_NO_MEM;
|
||||
failed:
|
||||
vop_ref_dec(node);
|
||||
kfree(entry);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_getdirentry_sub_noblock - get the content of file entry in DIR
|
||||
*/
|
||||
static int
|
||||
sfs_getdirentry_sub_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, int slot, struct sfs_disk_entry *entry) {
|
||||
int ret, i, nslots = sin->din->blocks;
|
||||
for (i = 0; i < nslots; i ++) {
|
||||
if ((ret = sfs_dirent_read_nolock(sfs, sin, i, entry)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
if (entry->ino != 0) {
|
||||
if (slot == 0) {
|
||||
return 0;
|
||||
}
|
||||
slot --;
|
||||
}
|
||||
}
|
||||
return -E_NOENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_getdirentry - according to the iob->io_offset, calculate the dir entry's slot in disk block,
|
||||
get dir entry content from the disk
|
||||
*/
|
||||
static int
|
||||
sfs_getdirentry(struct inode *node, struct iobuf *iob) {
|
||||
struct sfs_disk_entry *entry;
|
||||
if ((entry = kmalloc(sizeof(struct sfs_disk_entry))) == NULL) {
|
||||
return -E_NO_MEM;
|
||||
}
|
||||
|
||||
struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs);
|
||||
struct sfs_inode *sin = vop_info(node, sfs_inode);
|
||||
|
||||
int ret, slot;
|
||||
off_t offset = iob->io_offset;
|
||||
if (offset < 0 || offset % sfs_dentry_size != 0) {
|
||||
kfree(entry);
|
||||
return -E_INVAL;
|
||||
}
|
||||
if ((slot = offset / sfs_dentry_size) > sin->din->blocks) {
|
||||
kfree(entry);
|
||||
return -E_NOENT;
|
||||
}
|
||||
lock_sin(sin);
|
||||
if ((ret = sfs_getdirentry_sub_nolock(sfs, sin, slot, entry)) != 0) {
|
||||
unlock_sin(sin);
|
||||
goto out;
|
||||
}
|
||||
unlock_sin(sin);
|
||||
ret = iobuf_move(iob, entry->name, sfs_dentry_size, 1, NULL);
|
||||
out:
|
||||
kfree(entry);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_reclaim - Free all resources inode occupied . Called when inode is no longer in use.
|
||||
*/
|
||||
static int
|
||||
sfs_reclaim(struct inode *node) {
|
||||
struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs);
|
||||
struct sfs_inode *sin = vop_info(node, sfs_inode);
|
||||
|
||||
int ret = -E_BUSY;
|
||||
uint32_t ent;
|
||||
lock_sfs_fs(sfs);
|
||||
assert(sin->reclaim_count > 0);
|
||||
if ((-- sin->reclaim_count) != 0 || inode_ref_count(node) != 0) {
|
||||
goto failed_unlock;
|
||||
}
|
||||
if (sin->din->nlinks == 0) {
|
||||
if ((ret = vop_truncate(node, 0)) != 0) {
|
||||
goto failed_unlock;
|
||||
}
|
||||
}
|
||||
if (sin->dirty) {
|
||||
if ((ret = vop_fsync(node)) != 0) {
|
||||
goto failed_unlock;
|
||||
}
|
||||
}
|
||||
sfs_remove_links(sin);
|
||||
unlock_sfs_fs(sfs);
|
||||
|
||||
if (sin->din->nlinks == 0) {
|
||||
sfs_block_free(sfs, sin->ino);
|
||||
if ((ent = sin->din->indirect) != 0) {
|
||||
sfs_block_free(sfs, ent);
|
||||
}
|
||||
}
|
||||
kfree(sin->din);
|
||||
vop_kill(node);
|
||||
return 0;
|
||||
|
||||
failed_unlock:
|
||||
unlock_sfs_fs(sfs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_gettype - Return type of file. The values for file types are in sfs.h.
|
||||
*/
|
||||
static int
|
||||
sfs_gettype(struct inode *node, uint32_t *type_store) {
|
||||
struct sfs_disk_inode *din = vop_info(node, sfs_inode)->din;
|
||||
switch (din->type) {
|
||||
case SFS_TYPE_DIR:
|
||||
*type_store = S_IFDIR;
|
||||
return 0;
|
||||
case SFS_TYPE_FILE:
|
||||
*type_store = S_IFREG;
|
||||
return 0;
|
||||
case SFS_TYPE_LINK:
|
||||
*type_store = S_IFLNK;
|
||||
return 0;
|
||||
}
|
||||
panic("invalid file type %d.\n", din->type);
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_tryseek - Check if seeking to the specified position within the file is legal.
|
||||
*/
|
||||
static int
|
||||
sfs_tryseek(struct inode *node, off_t pos) {
|
||||
if (pos < 0 || pos >= SFS_MAX_FILE_SIZE) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
struct sfs_inode *sin = vop_info(node, sfs_inode);
|
||||
if (pos > sin->din->size) {
|
||||
return vop_truncate(node, pos);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_truncfile : reszie the file with new length
|
||||
*/
|
||||
static int
|
||||
sfs_truncfile(struct inode *node, off_t len) {
|
||||
if (len < 0 || len > SFS_MAX_FILE_SIZE) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs);
|
||||
struct sfs_inode *sin = vop_info(node, sfs_inode);
|
||||
struct sfs_disk_inode *din = sin->din;
|
||||
|
||||
int ret = 0;
|
||||
//new number of disk blocks of file
|
||||
uint32_t nblks, tblks = ROUNDUP_DIV(len, SFS_BLKSIZE);
|
||||
if (din->size == len) {
|
||||
assert(tblks == din->blocks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lock_sin(sin);
|
||||
// old number of disk blocks of file
|
||||
nblks = din->blocks;
|
||||
if (nblks < tblks) {
|
||||
// try to enlarge the file size by add new disk block at the end of file
|
||||
while (nblks != tblks) {
|
||||
if ((ret = sfs_bmap_load_nolock(sfs, sin, nblks, NULL)) != 0) {
|
||||
goto out_unlock;
|
||||
}
|
||||
nblks ++;
|
||||
}
|
||||
}
|
||||
else if (tblks < nblks) {
|
||||
// try to reduce the file size
|
||||
while (tblks != nblks) {
|
||||
if ((ret = sfs_bmap_truncate_nolock(sfs, sin)) != 0) {
|
||||
goto out_unlock;
|
||||
}
|
||||
nblks --;
|
||||
}
|
||||
}
|
||||
assert(din->blocks == tblks);
|
||||
din->size = len;
|
||||
sin->dirty = 1;
|
||||
|
||||
out_unlock:
|
||||
unlock_sin(sin);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_lookup - Parse path relative to the passed directory
|
||||
* DIR, and hand back the inode for the file it
|
||||
* refers to.
|
||||
*/
|
||||
static int
|
||||
sfs_lookup(struct inode *node, char *path, struct inode **node_store) {
|
||||
struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs);
|
||||
assert(*path != '\0' && *path != '/');
|
||||
vop_ref_inc(node);
|
||||
struct sfs_inode *sin = vop_info(node, sfs_inode);
|
||||
if (sin->din->type != SFS_TYPE_DIR) {
|
||||
vop_ref_dec(node);
|
||||
return -E_NOTDIR;
|
||||
}
|
||||
struct inode *subnode;
|
||||
int ret = sfs_lookup_once(sfs, sin, path, &subnode, NULL);
|
||||
|
||||
vop_ref_dec(node);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
*node_store = subnode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The sfs specific DIR operations correspond to the abstract operations on a inode.
|
||||
static const struct inode_ops sfs_node_dirops = {
|
||||
.vop_magic = VOP_MAGIC,
|
||||
.vop_open = sfs_opendir,
|
||||
.vop_close = sfs_close,
|
||||
.vop_fstat = sfs_fstat,
|
||||
.vop_fsync = sfs_fsync,
|
||||
.vop_namefile = sfs_namefile,
|
||||
.vop_getdirentry = sfs_getdirentry,
|
||||
.vop_reclaim = sfs_reclaim,
|
||||
.vop_gettype = sfs_gettype,
|
||||
.vop_lookup = sfs_lookup,
|
||||
};
|
||||
/// The sfs specific FILE operations correspond to the abstract operations on a inode.
|
||||
static const struct inode_ops sfs_node_fileops = {
|
||||
.vop_magic = VOP_MAGIC,
|
||||
.vop_open = sfs_openfile,
|
||||
.vop_close = sfs_close,
|
||||
.vop_read = sfs_read,
|
||||
.vop_write = sfs_write,
|
||||
.vop_fstat = sfs_fstat,
|
||||
.vop_fsync = sfs_fsync,
|
||||
.vop_reclaim = sfs_reclaim,
|
||||
.vop_gettype = sfs_gettype,
|
||||
.vop_tryseek = sfs_tryseek,
|
||||
.vop_truncate = sfs_truncfile,
|
||||
};
|
||||
|
||||
167
labcodes/lab8/kern/fs/sfs/sfs_io.c
Normal file
167
labcodes/lab8/kern/fs/sfs/sfs_io.c
Normal file
@@ -0,0 +1,167 @@
|
||||
#include <defs.h>
|
||||
#include <string.h>
|
||||
#include <dev.h>
|
||||
#include <sfs.h>
|
||||
#include <iobuf.h>
|
||||
#include <bitmap.h>
|
||||
#include <assert.h>
|
||||
|
||||
//Basic block-level I/O routines
|
||||
|
||||
/* sfs_rwblock_nolock - Basic block-level I/O routine for Rd/Wr one disk block,
|
||||
* without lock protect for mutex process on Rd/Wr disk block
|
||||
* @sfs: sfs_fs which will be process
|
||||
* @buf: the buffer uesed for Rd/Wr
|
||||
* @blkno: the NO. of disk block
|
||||
* @write: BOOL: Read or Write
|
||||
* @check: BOOL: if check (blono < sfs super.blocks)
|
||||
*/
|
||||
static int
|
||||
sfs_rwblock_nolock(struct sfs_fs *sfs, void *buf, uint32_t blkno, bool write, bool check) {
|
||||
assert((blkno != 0 || !check) && blkno < sfs->super.blocks);
|
||||
struct iobuf __iob, *iob = iobuf_init(&__iob, buf, SFS_BLKSIZE, blkno * SFS_BLKSIZE);
|
||||
return dop_io(sfs->dev, iob, write);
|
||||
}
|
||||
|
||||
/* sfs_rwblock - Basic block-level I/O routine for Rd/Wr N disk blocks ,
|
||||
* with lock protect for mutex process on Rd/Wr disk block
|
||||
* @sfs: sfs_fs which will be process
|
||||
* @buf: the buffer uesed for Rd/Wr
|
||||
* @blkno: the NO. of disk block
|
||||
* @nblks: Rd/Wr number of disk block
|
||||
* @write: BOOL: Read - 0 or Write - 1
|
||||
*/
|
||||
static int
|
||||
sfs_rwblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks, bool write) {
|
||||
int ret = 0;
|
||||
lock_sfs_io(sfs);
|
||||
{
|
||||
while (nblks != 0) {
|
||||
if ((ret = sfs_rwblock_nolock(sfs, buf, blkno, write, 1)) != 0) {
|
||||
break;
|
||||
}
|
||||
blkno ++, nblks --;
|
||||
buf += SFS_BLKSIZE;
|
||||
}
|
||||
}
|
||||
unlock_sfs_io(sfs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* sfs_rblock - The Wrap of sfs_rwblock function for Rd N disk blocks ,
|
||||
*
|
||||
* @sfs: sfs_fs which will be process
|
||||
* @buf: the buffer uesed for Rd/Wr
|
||||
* @blkno: the NO. of disk block
|
||||
* @nblks: Rd/Wr number of disk block
|
||||
*/
|
||||
int
|
||||
sfs_rblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks) {
|
||||
return sfs_rwblock(sfs, buf, blkno, nblks, 0);
|
||||
}
|
||||
|
||||
/* sfs_wblock - The Wrap of sfs_rwblock function for Wr N disk blocks ,
|
||||
*
|
||||
* @sfs: sfs_fs which will be process
|
||||
* @buf: the buffer uesed for Rd/Wr
|
||||
* @blkno: the NO. of disk block
|
||||
* @nblks: Rd/Wr number of disk block
|
||||
*/
|
||||
int
|
||||
sfs_wblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks) {
|
||||
return sfs_rwblock(sfs, buf, blkno, nblks, 1);
|
||||
}
|
||||
|
||||
/* sfs_rbuf - The Basic block-level I/O routine for Rd( non-block & non-aligned io) one disk block(using sfs->sfs_buffer)
|
||||
* with lock protect for mutex process on Rd/Wr disk block
|
||||
* @sfs: sfs_fs which will be process
|
||||
* @buf: the buffer uesed for Rd
|
||||
* @len: the length need to Rd
|
||||
* @blkno: the NO. of disk block
|
||||
* @offset: the offset in the content of disk block
|
||||
*/
|
||||
int
|
||||
sfs_rbuf(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset) {
|
||||
assert(offset >= 0 && offset < SFS_BLKSIZE && offset + len <= SFS_BLKSIZE);
|
||||
int ret;
|
||||
lock_sfs_io(sfs);
|
||||
{
|
||||
if ((ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, blkno, 0, 1)) == 0) {
|
||||
memcpy(buf, sfs->sfs_buffer + offset, len);
|
||||
}
|
||||
}
|
||||
unlock_sfs_io(sfs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* sfs_wbuf - The Basic block-level I/O routine for Wr( non-block & non-aligned io) one disk block(using sfs->sfs_buffer)
|
||||
* with lock protect for mutex process on Rd/Wr disk block
|
||||
* @sfs: sfs_fs which will be process
|
||||
* @buf: the buffer uesed for Wr
|
||||
* @len: the length need to Wr
|
||||
* @blkno: the NO. of disk block
|
||||
* @offset: the offset in the content of disk block
|
||||
*/
|
||||
int
|
||||
sfs_wbuf(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset) {
|
||||
assert(offset >= 0 && offset < SFS_BLKSIZE && offset + len <= SFS_BLKSIZE);
|
||||
int ret;
|
||||
lock_sfs_io(sfs);
|
||||
{
|
||||
if ((ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, blkno, 0, 1)) == 0) {
|
||||
memcpy(sfs->sfs_buffer + offset, buf, len);
|
||||
ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, blkno, 1, 1);
|
||||
}
|
||||
}
|
||||
unlock_sfs_io(sfs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_sync_super - write sfs->super (in memory) into disk (SFS_BLKN_SUPER, 1) with lock protect.
|
||||
*/
|
||||
int
|
||||
sfs_sync_super(struct sfs_fs *sfs) {
|
||||
int ret;
|
||||
lock_sfs_io(sfs);
|
||||
{
|
||||
memset(sfs->sfs_buffer, 0, SFS_BLKSIZE);
|
||||
memcpy(sfs->sfs_buffer, &(sfs->super), sizeof(sfs->super));
|
||||
ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, SFS_BLKN_SUPER, 1, 0);
|
||||
}
|
||||
unlock_sfs_io(sfs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_sync_freemap - write sfs bitmap into disk (SFS_BLKN_FREEMAP, nblks) without lock protect.
|
||||
*/
|
||||
int
|
||||
sfs_sync_freemap(struct sfs_fs *sfs) {
|
||||
uint32_t nblks = sfs_freemap_blocks(&(sfs->super));
|
||||
return sfs_wblock(sfs, bitmap_getdata(sfs->freemap, NULL), SFS_BLKN_FREEMAP, nblks);
|
||||
}
|
||||
|
||||
/*
|
||||
* sfs_clear_block - write zero info into disk (blkno, nblks) with lock protect.
|
||||
* @sfs: sfs_fs which will be process
|
||||
* @blkno: the NO. of disk block
|
||||
* @nblks: Rd/Wr number of disk block
|
||||
*/
|
||||
int
|
||||
sfs_clear_block(struct sfs_fs *sfs, uint32_t blkno, uint32_t nblks) {
|
||||
int ret;
|
||||
lock_sfs_io(sfs);
|
||||
{
|
||||
memset(sfs->sfs_buffer, 0, SFS_BLKSIZE);
|
||||
while (nblks != 0) {
|
||||
if ((ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, blkno, 1, 1)) != 0) {
|
||||
break;
|
||||
}
|
||||
blkno ++, nblks --;
|
||||
}
|
||||
}
|
||||
unlock_sfs_io(sfs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
44
labcodes/lab8/kern/fs/sfs/sfs_lock.c
Normal file
44
labcodes/lab8/kern/fs/sfs/sfs_lock.c
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <defs.h>
|
||||
#include <sem.h>
|
||||
#include <sfs.h>
|
||||
|
||||
|
||||
/*
|
||||
* lock_sfs_fs - lock the process of SFS Filesystem Rd/Wr Disk Block
|
||||
*
|
||||
* called by: sfs_load_inode, sfs_sync, sfs_reclaim
|
||||
*/
|
||||
void
|
||||
lock_sfs_fs(struct sfs_fs *sfs) {
|
||||
down(&(sfs->fs_sem));
|
||||
}
|
||||
|
||||
/*
|
||||
* lock_sfs_io - lock the process of SFS File Rd/Wr Disk Block
|
||||
*
|
||||
* called by: sfs_rwblock, sfs_clear_block, sfs_sync_super
|
||||
*/
|
||||
void
|
||||
lock_sfs_io(struct sfs_fs *sfs) {
|
||||
down(&(sfs->io_sem));
|
||||
}
|
||||
|
||||
/*
|
||||
* unlock_sfs_fs - unlock the process of SFS Filesystem Rd/Wr Disk Block
|
||||
*
|
||||
* called by: sfs_load_inode, sfs_sync, sfs_reclaim
|
||||
*/
|
||||
void
|
||||
unlock_sfs_fs(struct sfs_fs *sfs) {
|
||||
up(&(sfs->fs_sem));
|
||||
}
|
||||
|
||||
/*
|
||||
* unlock_sfs_io - unlock the process of sfs Rd/Wr Disk Block
|
||||
*
|
||||
* called by: sfs_rwblock sfs_clear_block sfs_sync_super
|
||||
*/
|
||||
void
|
||||
unlock_sfs_io(struct sfs_fs *sfs) {
|
||||
up(&(sfs->io_sem));
|
||||
}
|
||||
27
labcodes/lab8/kern/fs/swap/swapfs.c
Normal file
27
labcodes/lab8/kern/fs/swap/swapfs.c
Normal file
@@ -0,0 +1,27 @@
|
||||
#include <swap.h>
|
||||
#include <swapfs.h>
|
||||
#include <mmu.h>
|
||||
#include <fs.h>
|
||||
#include <ide.h>
|
||||
#include <pmm.h>
|
||||
#include <assert.h>
|
||||
|
||||
void
|
||||
swapfs_init(void) {
|
||||
static_assert((PGSIZE % SECTSIZE) == 0);
|
||||
if (!ide_device_valid(SWAP_DEV_NO)) {
|
||||
panic("swap fs isn't available.\n");
|
||||
}
|
||||
max_swap_offset = ide_device_size(SWAP_DEV_NO) / (PGSIZE / SECTSIZE);
|
||||
}
|
||||
|
||||
int
|
||||
swapfs_read(swap_entry_t entry, struct Page *page) {
|
||||
return ide_read_secs(SWAP_DEV_NO, swap_offset(entry) * PAGE_NSECT, page2kva(page), PAGE_NSECT);
|
||||
}
|
||||
|
||||
int
|
||||
swapfs_write(swap_entry_t entry, struct Page *page) {
|
||||
return ide_write_secs(SWAP_DEV_NO, swap_offset(entry) * PAGE_NSECT, page2kva(page), PAGE_NSECT);
|
||||
}
|
||||
|
||||
12
labcodes/lab8/kern/fs/swap/swapfs.h
Normal file
12
labcodes/lab8/kern/fs/swap/swapfs.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef __KERN_FS_SWAP_SWAPFS_H__
|
||||
#define __KERN_FS_SWAP_SWAPFS_H__
|
||||
|
||||
#include <memlayout.h>
|
||||
#include <swap.h>
|
||||
|
||||
void swapfs_init(void);
|
||||
int swapfs_read(swap_entry_t entry, struct Page *page);
|
||||
int swapfs_write(swap_entry_t entry, struct Page *page);
|
||||
|
||||
#endif /* !__KERN_FS_SWAP_SWAPFS_H__ */
|
||||
|
||||
317
labcodes/lab8/kern/fs/sysfile.c
Normal file
317
labcodes/lab8/kern/fs/sysfile.c
Normal file
@@ -0,0 +1,317 @@
|
||||
#include <defs.h>
|
||||
#include <string.h>
|
||||
#include <vmm.h>
|
||||
#include <proc.h>
|
||||
#include <kmalloc.h>
|
||||
#include <vfs.h>
|
||||
#include <file.h>
|
||||
#include <iobuf.h>
|
||||
#include <sysfile.h>
|
||||
#include <stat.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <error.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define IOBUF_SIZE 4096
|
||||
|
||||
/* copy_path - copy path name */
|
||||
static int
|
||||
copy_path(char **to, const char *from) {
|
||||
struct mm_struct *mm = current->mm;
|
||||
char *buffer;
|
||||
if ((buffer = kmalloc(FS_MAX_FPATH_LEN + 1)) == NULL) {
|
||||
return -E_NO_MEM;
|
||||
}
|
||||
lock_mm(mm);
|
||||
if (!copy_string(mm, buffer, from, FS_MAX_FPATH_LEN + 1)) {
|
||||
unlock_mm(mm);
|
||||
goto failed_cleanup;
|
||||
}
|
||||
unlock_mm(mm);
|
||||
*to = buffer;
|
||||
return 0;
|
||||
|
||||
failed_cleanup:
|
||||
kfree(buffer);
|
||||
return -E_INVAL;
|
||||
}
|
||||
|
||||
/* sysfile_open - open file */
|
||||
int
|
||||
sysfile_open(const char *__path, uint32_t open_flags) {
|
||||
int ret;
|
||||
char *path;
|
||||
if ((ret = copy_path(&path, __path)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
ret = file_open(path, open_flags);
|
||||
kfree(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* sysfile_close - close file */
|
||||
int
|
||||
sysfile_close(int fd) {
|
||||
return file_close(fd);
|
||||
}
|
||||
|
||||
/* sysfile_read - read file */
|
||||
int
|
||||
sysfile_read(int fd, void *base, size_t len) {
|
||||
struct mm_struct *mm = current->mm;
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (!file_testfd(fd, 1, 0)) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
void *buffer;
|
||||
if ((buffer = kmalloc(IOBUF_SIZE)) == NULL) {
|
||||
return -E_NO_MEM;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
size_t copied = 0, alen;
|
||||
while (len != 0) {
|
||||
if ((alen = IOBUF_SIZE) > len) {
|
||||
alen = len;
|
||||
}
|
||||
ret = file_read(fd, buffer, alen, &alen);
|
||||
if (alen != 0) {
|
||||
lock_mm(mm);
|
||||
{
|
||||
if (copy_to_user(mm, base, buffer, alen)) {
|
||||
assert(len >= alen);
|
||||
base += alen, len -= alen, copied += alen;
|
||||
}
|
||||
else if (ret == 0) {
|
||||
ret = -E_INVAL;
|
||||
}
|
||||
}
|
||||
unlock_mm(mm);
|
||||
}
|
||||
if (ret != 0 || alen == 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(buffer);
|
||||
if (copied != 0) {
|
||||
return copied;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* sysfile_write - write file */
|
||||
int
|
||||
sysfile_write(int fd, void *base, size_t len) {
|
||||
struct mm_struct *mm = current->mm;
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (!file_testfd(fd, 0, 1)) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
void *buffer;
|
||||
if ((buffer = kmalloc(IOBUF_SIZE)) == NULL) {
|
||||
return -E_NO_MEM;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
size_t copied = 0, alen;
|
||||
while (len != 0) {
|
||||
if ((alen = IOBUF_SIZE) > len) {
|
||||
alen = len;
|
||||
}
|
||||
lock_mm(mm);
|
||||
{
|
||||
if (!copy_from_user(mm, buffer, base, alen, 0)) {
|
||||
ret = -E_INVAL;
|
||||
}
|
||||
}
|
||||
unlock_mm(mm);
|
||||
if (ret == 0) {
|
||||
ret = file_write(fd, buffer, alen, &alen);
|
||||
if (alen != 0) {
|
||||
assert(len >= alen);
|
||||
base += alen, len -= alen, copied += alen;
|
||||
}
|
||||
}
|
||||
if (ret != 0 || alen == 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(buffer);
|
||||
if (copied != 0) {
|
||||
return copied;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* sysfile_seek - seek file */
|
||||
int
|
||||
sysfile_seek(int fd, off_t pos, int whence) {
|
||||
return file_seek(fd, pos, whence);
|
||||
}
|
||||
|
||||
/* sysfile_fstat - stat file */
|
||||
int
|
||||
sysfile_fstat(int fd, struct stat *__stat) {
|
||||
struct mm_struct *mm = current->mm;
|
||||
int ret;
|
||||
struct stat __local_stat, *stat = &__local_stat;
|
||||
if ((ret = file_fstat(fd, stat)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
lock_mm(mm);
|
||||
{
|
||||
if (!copy_to_user(mm, __stat, stat, sizeof(struct stat))) {
|
||||
ret = -E_INVAL;
|
||||
}
|
||||
}
|
||||
unlock_mm(mm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* sysfile_fsync - sync file */
|
||||
int
|
||||
sysfile_fsync(int fd) {
|
||||
return file_fsync(fd);
|
||||
}
|
||||
|
||||
/* sysfile_chdir - change dir */
|
||||
int
|
||||
sysfile_chdir(const char *__path) {
|
||||
int ret;
|
||||
char *path;
|
||||
if ((ret = copy_path(&path, __path)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
ret = vfs_chdir(path);
|
||||
kfree(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* sysfile_link - link file */
|
||||
int
|
||||
sysfile_link(const char *__path1, const char *__path2) {
|
||||
int ret;
|
||||
char *old_path, *new_path;
|
||||
if ((ret = copy_path(&old_path, __path1)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
if ((ret = copy_path(&new_path, __path2)) != 0) {
|
||||
kfree(old_path);
|
||||
return ret;
|
||||
}
|
||||
ret = vfs_link(old_path, new_path);
|
||||
kfree(old_path), kfree(new_path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* sysfile_rename - rename file */
|
||||
int
|
||||
sysfile_rename(const char *__path1, const char *__path2) {
|
||||
int ret;
|
||||
char *old_path, *new_path;
|
||||
if ((ret = copy_path(&old_path, __path1)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
if ((ret = copy_path(&new_path, __path2)) != 0) {
|
||||
kfree(old_path);
|
||||
return ret;
|
||||
}
|
||||
ret = vfs_rename(old_path, new_path);
|
||||
kfree(old_path), kfree(new_path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* sysfile_unlink - unlink file */
|
||||
int
|
||||
sysfile_unlink(const char *__path) {
|
||||
int ret;
|
||||
char *path;
|
||||
if ((ret = copy_path(&path, __path)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
ret = vfs_unlink(path);
|
||||
kfree(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* sysfile_get cwd - get current working directory */
|
||||
int
|
||||
sysfile_getcwd(char *buf, size_t len) {
|
||||
struct mm_struct *mm = current->mm;
|
||||
if (len == 0) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
|
||||
int ret = -E_INVAL;
|
||||
lock_mm(mm);
|
||||
{
|
||||
if (user_mem_check(mm, (uintptr_t)buf, len, 1)) {
|
||||
struct iobuf __iob, *iob = iobuf_init(&__iob, buf, len, 0);
|
||||
ret = vfs_getcwd(iob);
|
||||
}
|
||||
}
|
||||
unlock_mm(mm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* sysfile_getdirentry - get the file entry in DIR */
|
||||
int
|
||||
sysfile_getdirentry(int fd, struct dirent *__direntp) {
|
||||
struct mm_struct *mm = current->mm;
|
||||
struct dirent *direntp;
|
||||
if ((direntp = kmalloc(sizeof(struct dirent))) == NULL) {
|
||||
return -E_NO_MEM;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
lock_mm(mm);
|
||||
{
|
||||
if (!copy_from_user(mm, &(direntp->offset), &(__direntp->offset), sizeof(direntp->offset), 1)) {
|
||||
ret = -E_INVAL;
|
||||
}
|
||||
}
|
||||
unlock_mm(mm);
|
||||
|
||||
if (ret != 0 || (ret = file_getdirentry(fd, direntp)) != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
lock_mm(mm);
|
||||
{
|
||||
if (!copy_to_user(mm, __direntp, direntp, sizeof(struct dirent))) {
|
||||
ret = -E_INVAL;
|
||||
}
|
||||
}
|
||||
unlock_mm(mm);
|
||||
|
||||
out:
|
||||
kfree(direntp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* sysfile_dup - duplicate fd1 to fd2 */
|
||||
int
|
||||
sysfile_dup(int fd1, int fd2) {
|
||||
return file_dup(fd1, fd2);
|
||||
}
|
||||
|
||||
int
|
||||
sysfile_pipe(int *fd_store) {
|
||||
return -E_UNIMP;
|
||||
}
|
||||
|
||||
int
|
||||
sysfile_mkfifo(const char *__name, uint32_t open_flags) {
|
||||
return -E_UNIMP;
|
||||
}
|
||||
|
||||
28
labcodes/lab8/kern/fs/sysfile.h
Normal file
28
labcodes/lab8/kern/fs/sysfile.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef __KERN_FS_SYSFILE_H__
|
||||
#define __KERN_FS_SYSFILE_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
struct stat;
|
||||
struct dirent;
|
||||
|
||||
int sysfile_open(const char *path, uint32_t open_flags); // Open or create a file. FLAGS/MODE per the syscall.
|
||||
int sysfile_close(int fd); // Close a vnode opened
|
||||
int sysfile_read(int fd, void *base, size_t len); // Read file
|
||||
int sysfile_write(int fd, void *base, size_t len); // Write file
|
||||
int sysfile_seek(int fd, off_t pos, int whence); // Seek file
|
||||
int sysfile_fstat(int fd, struct stat *stat); // Stat file
|
||||
int sysfile_fsync(int fd); // Sync file
|
||||
int sysfile_chdir(const char *path); // change DIR
|
||||
int sysfile_mkdir(const char *path); // create DIR
|
||||
int sysfile_link(const char *path1, const char *path2); // set a path1's link as path2
|
||||
int sysfile_rename(const char *path1, const char *path2); // rename file
|
||||
int sysfile_unlink(const char *path); // unlink a path
|
||||
int sysfile_getcwd(char *buf, size_t len); // get current working directory
|
||||
int sysfile_getdirentry(int fd, struct dirent *direntp); // get the file entry in DIR
|
||||
int sysfile_dup(int fd1, int fd2); // duplicate file
|
||||
int sysfile_pipe(int *fd_store); // build PIPE
|
||||
int sysfile_mkfifo(const char *name, uint32_t open_flags); // build named PIPE
|
||||
|
||||
#endif /* !__KERN_FS_SYSFILE_H__ */
|
||||
|
||||
116
labcodes/lab8/kern/fs/vfs/inode.c
Normal file
116
labcodes/lab8/kern/fs/vfs/inode.c
Normal file
@@ -0,0 +1,116 @@
|
||||
#include <defs.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <atomic.h>
|
||||
#include <vfs.h>
|
||||
#include <inode.h>
|
||||
#include <error.h>
|
||||
#include <assert.h>
|
||||
#include <kmalloc.h>
|
||||
|
||||
/* *
|
||||
* __alloc_inode - alloc a inode structure and initialize in_type
|
||||
* */
|
||||
struct inode *
|
||||
__alloc_inode(int type) {
|
||||
struct inode *node;
|
||||
if ((node = kmalloc(sizeof(struct inode))) != NULL) {
|
||||
node->in_type = type;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/* *
|
||||
* inode_init - initialize a inode structure
|
||||
* invoked by vop_init
|
||||
* */
|
||||
void
|
||||
inode_init(struct inode *node, const struct inode_ops *ops, struct fs *fs) {
|
||||
node->ref_count = 0;
|
||||
node->open_count = 0;
|
||||
node->in_ops = ops, node->in_fs = fs;
|
||||
vop_ref_inc(node);
|
||||
}
|
||||
|
||||
/* *
|
||||
* inode_kill - kill a inode structure
|
||||
* invoked by vop_kill
|
||||
* */
|
||||
void
|
||||
inode_kill(struct inode *node) {
|
||||
assert(inode_ref_count(node) == 0);
|
||||
assert(inode_open_count(node) == 0);
|
||||
kfree(node);
|
||||
}
|
||||
|
||||
/* *
|
||||
* inode_ref_inc - increment ref_count
|
||||
* invoked by vop_ref_inc
|
||||
* */
|
||||
int
|
||||
inode_ref_inc(struct inode *node) {
|
||||
node->ref_count += 1;
|
||||
return node->ref_count;
|
||||
}
|
||||
|
||||
/* *
|
||||
* inode_ref_dec - decrement ref_count
|
||||
* invoked by vop_ref_dec
|
||||
* calls vop_reclaim if the ref_count hits zero
|
||||
* */
|
||||
int
|
||||
inode_ref_dec(struct inode *node) {
|
||||
assert(inode_ref_count(node) > 0);
|
||||
int ref_count, ret;
|
||||
node->ref_count-= 1;
|
||||
ref_count = node->ref_count;
|
||||
if (ref_count == 0) {
|
||||
if ((ret = vop_reclaim(node)) != 0 && ret != -E_BUSY) {
|
||||
cprintf("vfs: warning: vop_reclaim: %e.\n", ret);
|
||||
}
|
||||
}
|
||||
return ref_count;
|
||||
}
|
||||
|
||||
/* *
|
||||
* inode_open_inc - increment the open_count
|
||||
* invoked by vop_open_inc
|
||||
* */
|
||||
int
|
||||
inode_open_inc(struct inode *node) {
|
||||
node->open_count += 1;
|
||||
return node->open_count;
|
||||
}
|
||||
|
||||
/* *
|
||||
* inode_open_dec - decrement the open_count
|
||||
* invoked by vop_open_dec
|
||||
* calls vop_close if the open_count hits zero
|
||||
* */
|
||||
int
|
||||
inode_open_dec(struct inode *node) {
|
||||
assert(inode_open_count(node) > 0);
|
||||
int open_count, ret;
|
||||
node->open_count -= 1;
|
||||
open_count = node->open_count;
|
||||
if (open_count == 0) {
|
||||
if ((ret = vop_close(node)) != 0) {
|
||||
cprintf("vfs: warning: vop_close: %e.\n", ret);
|
||||
}
|
||||
}
|
||||
return open_count;
|
||||
}
|
||||
|
||||
/* *
|
||||
* inode_check - check the various things being valid
|
||||
* called before all vop_* calls
|
||||
* */
|
||||
void
|
||||
inode_check(struct inode *node, const char *opstr) {
|
||||
assert(node != NULL && node->in_ops != NULL);
|
||||
assert(node->in_ops->vop_magic == VOP_MAGIC);
|
||||
int ref_count = inode_ref_count(node), open_count = inode_open_count(node);
|
||||
assert(ref_count >= open_count && open_count >= 0);
|
||||
assert(ref_count < MAX_INODE_COUNT && open_count < MAX_INODE_COUNT);
|
||||
}
|
||||
|
||||
248
labcodes/lab8/kern/fs/vfs/inode.h
Normal file
248
labcodes/lab8/kern/fs/vfs/inode.h
Normal file
@@ -0,0 +1,248 @@
|
||||
#ifndef __KERN_FS_VFS_INODE_H__
|
||||
#define __KERN_FS_VFS_INODE_H__
|
||||
|
||||
#include <defs.h>
|
||||
#include <dev.h>
|
||||
#include <sfs.h>
|
||||
#include <atomic.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct stat;
|
||||
struct iobuf;
|
||||
|
||||
/*
|
||||
* A struct inode is an abstract representation of a file.
|
||||
*
|
||||
* It is an interface that allows the kernel's filesystem-independent
|
||||
* code to interact usefully with multiple sets of filesystem code.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Abstract low-level file.
|
||||
*
|
||||
* Note: in_info is Filesystem-specific data, in_type is the inode type
|
||||
*
|
||||
* open_count is managed using VOP_INCOPEN and VOP_DECOPEN by
|
||||
* vfs_open() and vfs_close(). Code above the VFS layer should not
|
||||
* need to worry about it.
|
||||
*/
|
||||
struct inode {
|
||||
union {
|
||||
struct device __device_info;
|
||||
struct sfs_inode __sfs_inode_info;
|
||||
} in_info;
|
||||
enum {
|
||||
inode_type_device_info = 0x1234,
|
||||
inode_type_sfs_inode_info,
|
||||
} in_type;
|
||||
int ref_count;
|
||||
int open_count;
|
||||
struct fs *in_fs;
|
||||
const struct inode_ops *in_ops;
|
||||
};
|
||||
|
||||
#define __in_type(type) inode_type_##type##_info
|
||||
|
||||
#define check_inode_type(node, type) ((node)->in_type == __in_type(type))
|
||||
|
||||
#define __vop_info(node, type) \
|
||||
({ \
|
||||
struct inode *__node = (node); \
|
||||
assert(__node != NULL && check_inode_type(__node, type)); \
|
||||
&(__node->in_info.__##type##_info); \
|
||||
})
|
||||
|
||||
#define vop_info(node, type) __vop_info(node, type)
|
||||
|
||||
#define info2node(info, type) \
|
||||
to_struct((info), struct inode, in_info.__##type##_info)
|
||||
|
||||
struct inode *__alloc_inode(int type);
|
||||
|
||||
#define alloc_inode(type) __alloc_inode(__in_type(type))
|
||||
|
||||
#define MAX_INODE_COUNT 0x10000
|
||||
|
||||
int inode_ref_inc(struct inode *node);
|
||||
int inode_ref_dec(struct inode *node);
|
||||
int inode_open_inc(struct inode *node);
|
||||
int inode_open_dec(struct inode *node);
|
||||
|
||||
void inode_init(struct inode *node, const struct inode_ops *ops, struct fs *fs);
|
||||
void inode_kill(struct inode *node);
|
||||
|
||||
#define VOP_MAGIC 0x8c4ba476
|
||||
|
||||
/*
|
||||
* Abstract operations on a inode.
|
||||
*
|
||||
* These are used in the form VOP_FOO(inode, args), which are macros
|
||||
* that expands to inode->inode_ops->vop_foo(inode, args). The operations
|
||||
* "foo" are:
|
||||
*
|
||||
* vop_open - Called on open() of a file. Can be used to
|
||||
* reject illegal or undesired open modes. Note that
|
||||
* various operations can be performed without the
|
||||
* file actually being opened.
|
||||
* The inode need not look at O_CREAT, O_EXCL, or
|
||||
* O_TRUNC, as these are handled in the VFS layer.
|
||||
*
|
||||
* VOP_EACHOPEN should not be called directly from
|
||||
* above the VFS layer - use vfs_open() to open inodes.
|
||||
* This maintains the open count so VOP_LASTCLOSE can
|
||||
* be called at the right time.
|
||||
*
|
||||
* vop_close - To be called on *last* close() of a file.
|
||||
*
|
||||
* VOP_LASTCLOSE should not be called directly from
|
||||
* above the VFS layer - use vfs_close() to close
|
||||
* inodes opened with vfs_open().
|
||||
*
|
||||
* vop_reclaim - Called when inode is no longer in use. Note that
|
||||
* this may be substantially after vop_lastclose is
|
||||
* called.
|
||||
*
|
||||
*****************************************
|
||||
*
|
||||
* vop_read - Read data from file to uio, at offset specified
|
||||
* in the uio, updating uio_resid to reflect the
|
||||
* amount read, and updating uio_offset to match.
|
||||
* Not allowed on directories or symlinks.
|
||||
*
|
||||
* vop_getdirentry - Read a single filename from a directory into a
|
||||
* uio, choosing what name based on the offset
|
||||
* field in the uio, and updating that field.
|
||||
* Unlike with I/O on regular files, the value of
|
||||
* the offset field is not interpreted outside
|
||||
* the filesystem and thus need not be a byte
|
||||
* count. However, the uio_resid field should be
|
||||
* handled in the normal fashion.
|
||||
* On non-directory objects, return ENOTDIR.
|
||||
*
|
||||
* vop_write - Write data from uio to file at offset specified
|
||||
* in the uio, updating uio_resid to reflect the
|
||||
* amount written, and updating uio_offset to match.
|
||||
* Not allowed on directories or symlinks.
|
||||
*
|
||||
* vop_ioctl - Perform ioctl operation OP on file using data
|
||||
* DATA. The interpretation of the data is specific
|
||||
* to each ioctl.
|
||||
*
|
||||
* vop_fstat -Return info about a file. The pointer is a
|
||||
* pointer to struct stat; see stat.h.
|
||||
*
|
||||
* vop_gettype - Return type of file. The values for file types
|
||||
* are in sfs.h.
|
||||
*
|
||||
* vop_tryseek - Check if seeking to the specified position within
|
||||
* the file is legal. (For instance, all seeks
|
||||
* are illegal on serial port devices, and seeks
|
||||
* past EOF on files whose sizes are fixed may be
|
||||
* as well.)
|
||||
*
|
||||
* vop_fsync - Force any dirty buffers associated with this file
|
||||
* to stable storage.
|
||||
*
|
||||
* vop_truncate - Forcibly set size of file to the length passed
|
||||
* in, discarding any excess blocks.
|
||||
*
|
||||
* vop_namefile - Compute pathname relative to filesystem root
|
||||
* of the file and copy to the specified io buffer.
|
||||
* Need not work on objects that are not
|
||||
* directories.
|
||||
*
|
||||
*****************************************
|
||||
*
|
||||
* vop_creat - Create a regular file named NAME in the passed
|
||||
* directory DIR. If boolean EXCL is true, fail if
|
||||
* the file already exists; otherwise, use the
|
||||
* existing file if there is one. Hand back the
|
||||
* inode for the file as per vop_lookup.
|
||||
*
|
||||
*****************************************
|
||||
*
|
||||
* vop_lookup - Parse PATHNAME relative to the passed directory
|
||||
* DIR, and hand back the inode for the file it
|
||||
* refers to. May destroy PATHNAME. Should increment
|
||||
* refcount on inode handed back.
|
||||
*/
|
||||
struct inode_ops {
|
||||
unsigned long vop_magic;
|
||||
int (*vop_open)(struct inode *node, uint32_t open_flags);
|
||||
int (*vop_close)(struct inode *node);
|
||||
int (*vop_read)(struct inode *node, struct iobuf *iob);
|
||||
int (*vop_write)(struct inode *node, struct iobuf *iob);
|
||||
int (*vop_fstat)(struct inode *node, struct stat *stat);
|
||||
int (*vop_fsync)(struct inode *node);
|
||||
int (*vop_namefile)(struct inode *node, struct iobuf *iob);
|
||||
int (*vop_getdirentry)(struct inode *node, struct iobuf *iob);
|
||||
int (*vop_reclaim)(struct inode *node);
|
||||
int (*vop_gettype)(struct inode *node, uint32_t *type_store);
|
||||
int (*vop_tryseek)(struct inode *node, off_t pos);
|
||||
int (*vop_truncate)(struct inode *node, off_t len);
|
||||
int (*vop_create)(struct inode *node, const char *name, bool excl, struct inode **node_store);
|
||||
int (*vop_lookup)(struct inode *node, char *path, struct inode **node_store);
|
||||
int (*vop_ioctl)(struct inode *node, int op, void *data);
|
||||
};
|
||||
|
||||
/*
|
||||
* Consistency check
|
||||
*/
|
||||
void inode_check(struct inode *node, const char *opstr);
|
||||
|
||||
#define __vop_op(node, sym) \
|
||||
({ \
|
||||
struct inode *__node = (node); \
|
||||
assert(__node != NULL && __node->in_ops != NULL && __node->in_ops->vop_##sym != NULL); \
|
||||
inode_check(__node, #sym); \
|
||||
__node->in_ops->vop_##sym; \
|
||||
})
|
||||
|
||||
#define vop_open(node, open_flags) (__vop_op(node, open)(node, open_flags))
|
||||
#define vop_close(node) (__vop_op(node, close)(node))
|
||||
#define vop_read(node, iob) (__vop_op(node, read)(node, iob))
|
||||
#define vop_write(node, iob) (__vop_op(node, write)(node, iob))
|
||||
#define vop_fstat(node, stat) (__vop_op(node, fstat)(node, stat))
|
||||
#define vop_fsync(node) (__vop_op(node, fsync)(node))
|
||||
#define vop_namefile(node, iob) (__vop_op(node, namefile)(node, iob))
|
||||
#define vop_getdirentry(node, iob) (__vop_op(node, getdirentry)(node, iob))
|
||||
#define vop_reclaim(node) (__vop_op(node, reclaim)(node))
|
||||
#define vop_ioctl(node, op, data) (__vop_op(node, ioctl)(node, op, data))
|
||||
#define vop_gettype(node, type_store) (__vop_op(node, gettype)(node, type_store))
|
||||
#define vop_tryseek(node, pos) (__vop_op(node, tryseek)(node, pos))
|
||||
#define vop_truncate(node, len) (__vop_op(node, truncate)(node, len))
|
||||
#define vop_create(node, name, excl, node_store) (__vop_op(node, create)(node, name, excl, node_store))
|
||||
#define vop_lookup(node, path, node_store) (__vop_op(node, lookup)(node, path, node_store))
|
||||
|
||||
|
||||
#define vop_fs(node) ((node)->in_fs)
|
||||
#define vop_init(node, ops, fs) inode_init(node, ops, fs)
|
||||
#define vop_kill(node) inode_kill(node)
|
||||
|
||||
/*
|
||||
* Reference count manipulation (handled above filesystem level)
|
||||
*/
|
||||
#define vop_ref_inc(node) inode_ref_inc(node)
|
||||
#define vop_ref_dec(node) inode_ref_dec(node)
|
||||
/*
|
||||
* Open count manipulation (handled above filesystem level)
|
||||
*
|
||||
* VOP_INCOPEN is called by vfs_open. VOP_DECOPEN is called by vfs_close.
|
||||
* Neither of these should need to be called from above the vfs layer.
|
||||
*/
|
||||
#define vop_open_inc(node) inode_open_inc(node)
|
||||
#define vop_open_dec(node) inode_open_dec(node)
|
||||
|
||||
|
||||
static inline int
|
||||
inode_ref_count(struct inode *node) {
|
||||
return node->ref_count;
|
||||
}
|
||||
|
||||
static inline int
|
||||
inode_open_count(struct inode *node) {
|
||||
return node->open_count;
|
||||
}
|
||||
|
||||
#endif /* !__KERN_FS_VFS_INODE_H__ */
|
||||
|
||||
97
labcodes/lab8/kern/fs/vfs/vfs.c
Normal file
97
labcodes/lab8/kern/fs/vfs/vfs.c
Normal file
@@ -0,0 +1,97 @@
|
||||
#include <defs.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <vfs.h>
|
||||
#include <inode.h>
|
||||
#include <sem.h>
|
||||
#include <kmalloc.h>
|
||||
#include <error.h>
|
||||
|
||||
static semaphore_t bootfs_sem;
|
||||
static struct inode *bootfs_node = NULL;
|
||||
|
||||
extern void vfs_devlist_init(void);
|
||||
|
||||
// __alloc_fs - allocate memory for fs, and set fs type
|
||||
struct fs *
|
||||
__alloc_fs(int type) {
|
||||
struct fs *fs;
|
||||
if ((fs = kmalloc(sizeof(struct fs))) != NULL) {
|
||||
fs->fs_type = type;
|
||||
}
|
||||
return fs;
|
||||
}
|
||||
|
||||
// vfs_init - vfs initialize
|
||||
void
|
||||
vfs_init(void) {
|
||||
sem_init(&bootfs_sem, 1);
|
||||
vfs_devlist_init();
|
||||
}
|
||||
|
||||
// lock_bootfs - lock for bootfs
|
||||
static void
|
||||
lock_bootfs(void) {
|
||||
down(&bootfs_sem);
|
||||
}
|
||||
// ulock_bootfs - ulock for bootfs
|
||||
static void
|
||||
unlock_bootfs(void) {
|
||||
up(&bootfs_sem);
|
||||
}
|
||||
|
||||
// change_bootfs - set the new fs inode
|
||||
static void
|
||||
change_bootfs(struct inode *node) {
|
||||
struct inode *old;
|
||||
lock_bootfs();
|
||||
{
|
||||
old = bootfs_node, bootfs_node = node;
|
||||
}
|
||||
unlock_bootfs();
|
||||
if (old != NULL) {
|
||||
vop_ref_dec(old);
|
||||
}
|
||||
}
|
||||
|
||||
// vfs_set_bootfs - change the dir of file system
|
||||
int
|
||||
vfs_set_bootfs(char *fsname) {
|
||||
struct inode *node = NULL;
|
||||
if (fsname != NULL) {
|
||||
char *s;
|
||||
if ((s = strchr(fsname, ':')) == NULL || s[1] != '\0') {
|
||||
return -E_INVAL;
|
||||
}
|
||||
int ret;
|
||||
if ((ret = vfs_chdir(fsname)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
if ((ret = vfs_get_curdir(&node)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
change_bootfs(node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// vfs_get_bootfs - get the inode of bootfs
|
||||
int
|
||||
vfs_get_bootfs(struct inode **node_store) {
|
||||
struct inode *node = NULL;
|
||||
if (bootfs_node != NULL) {
|
||||
lock_bootfs();
|
||||
{
|
||||
if ((node = bootfs_node) != NULL) {
|
||||
vop_ref_inc(bootfs_node);
|
||||
}
|
||||
}
|
||||
unlock_bootfs();
|
||||
}
|
||||
if (node == NULL) {
|
||||
return -E_NOENT;
|
||||
}
|
||||
*node_store = node;
|
||||
return 0;
|
||||
}
|
||||
|
||||
191
labcodes/lab8/kern/fs/vfs/vfs.h
Normal file
191
labcodes/lab8/kern/fs/vfs/vfs.h
Normal file
@@ -0,0 +1,191 @@
|
||||
#ifndef __KERN_FS_VFS_VFS_H__
|
||||
#define __KERN_FS_VFS_VFS_H__
|
||||
|
||||
#include <defs.h>
|
||||
#include <fs.h>
|
||||
#include <sfs.h>
|
||||
|
||||
struct inode; // abstract structure for an on-disk file (inode.h)
|
||||
struct device; // abstract structure for a device (dev.h)
|
||||
struct iobuf; // kernel or userspace I/O buffer (iobuf.h)
|
||||
|
||||
/*
|
||||
* Abstract filesystem. (Or device accessible as a file.)
|
||||
*
|
||||
* Information:
|
||||
* fs_info : filesystem-specific data (sfs_fs)
|
||||
* fs_type : filesystem type
|
||||
* Operations:
|
||||
*
|
||||
* fs_sync - Flush all dirty buffers to disk.
|
||||
* fs_get_root - Return root inode of filesystem.
|
||||
* fs_unmount - Attempt unmount of filesystem.
|
||||
* fs_cleanup - Cleanup of filesystem.???
|
||||
*
|
||||
*
|
||||
* fs_get_root should increment the refcount of the inode returned.
|
||||
* It should not ever return NULL.
|
||||
*
|
||||
* If fs_unmount returns an error, the filesystem stays mounted, and
|
||||
* consequently the struct fs instance should remain valid. On success,
|
||||
* however, the filesystem object and all storage associated with the
|
||||
* filesystem should have been discarded/released.
|
||||
*
|
||||
*/
|
||||
struct fs {
|
||||
union {
|
||||
struct sfs_fs __sfs_info;
|
||||
} fs_info; // filesystem-specific data
|
||||
enum {
|
||||
fs_type_sfs_info,
|
||||
} fs_type; // filesystem type
|
||||
int (*fs_sync)(struct fs *fs); // Flush all dirty buffers to disk
|
||||
struct inode *(*fs_get_root)(struct fs *fs); // Return root inode of filesystem.
|
||||
int (*fs_unmount)(struct fs *fs); // Attempt unmount of filesystem.
|
||||
void (*fs_cleanup)(struct fs *fs); // Cleanup of filesystem.???
|
||||
};
|
||||
|
||||
#define __fs_type(type) fs_type_##type##_info
|
||||
|
||||
#define check_fs_type(fs, type) ((fs)->fs_type == __fs_type(type))
|
||||
|
||||
#define __fsop_info(_fs, type) ({ \
|
||||
struct fs *__fs = (_fs); \
|
||||
assert(__fs != NULL && check_fs_type(__fs, type)); \
|
||||
&(__fs->fs_info.__##type##_info); \
|
||||
})
|
||||
|
||||
#define fsop_info(fs, type) __fsop_info(fs, type)
|
||||
|
||||
#define info2fs(info, type) \
|
||||
to_struct((info), struct fs, fs_info.__##type##_info)
|
||||
|
||||
struct fs *__alloc_fs(int type);
|
||||
|
||||
#define alloc_fs(type) __alloc_fs(__fs_type(type))
|
||||
|
||||
// Macros to shorten the calling sequences.
|
||||
#define fsop_sync(fs) ((fs)->fs_sync(fs))
|
||||
#define fsop_get_root(fs) ((fs)->fs_get_root(fs))
|
||||
#define fsop_unmount(fs) ((fs)->fs_unmount(fs))
|
||||
#define fsop_cleanup(fs) ((fs)->fs_cleanup(fs))
|
||||
|
||||
/*
|
||||
* Virtual File System layer functions.
|
||||
*
|
||||
* The VFS layer translates operations on abstract on-disk files or
|
||||
* pathnames to operations on specific files on specific filesystems.
|
||||
*/
|
||||
void vfs_init(void);
|
||||
void vfs_cleanup(void);
|
||||
void vfs_devlist_init(void);
|
||||
|
||||
/*
|
||||
* VFS layer low-level operations.
|
||||
* See inode.h for direct operations on inodes.
|
||||
* See fs.h for direct operations on filesystems/devices.
|
||||
*
|
||||
* vfs_set_curdir - change current directory of current thread by inode
|
||||
* vfs_get_curdir - retrieve inode of current directory of current thread
|
||||
* vfs_get_root - get root inode for the filesystem named DEVNAME
|
||||
* vfs_get_devname - get mounted device name for the filesystem passed in
|
||||
*/
|
||||
int vfs_set_curdir(struct inode *dir);
|
||||
int vfs_get_curdir(struct inode **dir_store);
|
||||
int vfs_get_root(const char *devname, struct inode **root_store);
|
||||
const char *vfs_get_devname(struct fs *fs);
|
||||
|
||||
|
||||
/*
|
||||
* VFS layer high-level operations on pathnames
|
||||
* Because namei may destroy pathnames, these all may too.
|
||||
*
|
||||
* vfs_open - Open or create a file. FLAGS/MODE per the syscall.
|
||||
* vfs_close - Close a inode opened with vfs_open. Does not fail.
|
||||
* (See vfspath.c for a discussion of why.)
|
||||
* vfs_link - Create a hard link to a file.
|
||||
* vfs_symlink - Create a symlink PATH containing contents CONTENTS.
|
||||
* vfs_readlink - Read contents of a symlink into a uio.
|
||||
* vfs_mkdir - Create a directory. MODE per the syscall.
|
||||
* vfs_unlink - Delete a file/directory.
|
||||
* vfs_rename - rename a file.
|
||||
* vfs_chdir - Change current directory of current thread by name.
|
||||
* vfs_getcwd - Retrieve name of current directory of current thread.
|
||||
*
|
||||
*/
|
||||
int vfs_open(char *path, uint32_t open_flags, struct inode **inode_store);
|
||||
int vfs_close(struct inode *node);
|
||||
int vfs_link(char *old_path, char *new_path);
|
||||
int vfs_symlink(char *old_path, char *new_path);
|
||||
int vfs_readlink(char *path, struct iobuf *iob);
|
||||
int vfs_mkdir(char *path);
|
||||
int vfs_unlink(char *path);
|
||||
int vfs_rename(char *old_path, char *new_path);
|
||||
int vfs_chdir(char *path);
|
||||
int vfs_getcwd(struct iobuf *iob);
|
||||
|
||||
|
||||
/*
|
||||
* VFS layer mid-level operations.
|
||||
*
|
||||
* vfs_lookup - Like VOP_LOOKUP, but takes a full device:path name,
|
||||
* or a name relative to the current directory, and
|
||||
* goes to the correct filesystem.
|
||||
* vfs_lookparent - Likewise, for VOP_LOOKPARENT.
|
||||
*
|
||||
* Both of these may destroy the path passed in.
|
||||
*/
|
||||
int vfs_lookup(char *path, struct inode **node_store);
|
||||
int vfs_lookup_parent(char *path, struct inode **node_store, char **endp);
|
||||
|
||||
/*
|
||||
* Misc
|
||||
*
|
||||
* vfs_set_bootfs - Set the filesystem that paths beginning with a
|
||||
* slash are sent to. If not set, these paths fail
|
||||
* with ENOENT. The argument should be the device
|
||||
* name or volume name for the filesystem (such as
|
||||
* "lhd0:") but need not have the trailing colon.
|
||||
*
|
||||
* vfs_get_bootfs - return the inode of the bootfs filesystem.
|
||||
*
|
||||
* vfs_add_fs - Add a hardwired filesystem to the VFS named device
|
||||
* list. It will be accessible as "devname:". This is
|
||||
* intended for filesystem-devices like emufs, and
|
||||
* gizmos like Linux procfs or BSD kernfs, not for
|
||||
* mounting filesystems on disk devices.
|
||||
*
|
||||
* vfs_add_dev - Add a device to the VFS named device list. If
|
||||
* MOUNTABLE is zero, the device will be accessible
|
||||
* as "DEVNAME:". If the mountable flag is set, the
|
||||
* device will be accessible as "DEVNAMEraw:" and
|
||||
* mountable under the name "DEVNAME". Thus, the
|
||||
* console, added with MOUNTABLE not set, would be
|
||||
* accessed by pathname as "con:", and lhd0, added
|
||||
* with mountable set, would be accessed by
|
||||
* pathname as "lhd0raw:" and mounted by passing
|
||||
* "lhd0" to vfs_mount.
|
||||
*
|
||||
* vfs_mount - Attempt to mount a filesystem on a device. The
|
||||
* device named by DEVNAME will be looked up and
|
||||
* passed, along with DATA, to the supplied function
|
||||
* MOUNTFUNC, which should create a struct fs and
|
||||
* return it in RESULT.
|
||||
*
|
||||
* vfs_unmount - Unmount the filesystem presently mounted on the
|
||||
* specified device.
|
||||
*
|
||||
* vfs_unmountall - Unmount all mounted filesystems.
|
||||
*/
|
||||
int vfs_set_bootfs(char *fsname);
|
||||
int vfs_get_bootfs(struct inode **node_store);
|
||||
|
||||
int vfs_add_fs(const char *devname, struct fs *fs);
|
||||
int vfs_add_dev(const char *devname, struct inode *devnode, bool mountable);
|
||||
|
||||
int vfs_mount(const char *devname, int (*mountfunc)(struct device *dev, struct fs **fs_store));
|
||||
int vfs_unmount(const char *devname);
|
||||
int vfs_unmount_all(void);
|
||||
|
||||
#endif /* !__KERN_FS_VFS_VFS_H__ */
|
||||
|
||||
309
labcodes/lab8/kern/fs/vfs/vfsdev.c
Normal file
309
labcodes/lab8/kern/fs/vfs/vfsdev.c
Normal file
@@ -0,0 +1,309 @@
|
||||
#include <defs.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <vfs.h>
|
||||
#include <dev.h>
|
||||
#include <inode.h>
|
||||
#include <sem.h>
|
||||
#include <list.h>
|
||||
#include <kmalloc.h>
|
||||
#include <unistd.h>
|
||||
#include <error.h>
|
||||
#include <assert.h>
|
||||
|
||||
// device info entry in vdev_list
|
||||
typedef struct {
|
||||
const char *devname;
|
||||
struct inode *devnode;
|
||||
struct fs *fs;
|
||||
bool mountable;
|
||||
list_entry_t vdev_link;
|
||||
} vfs_dev_t;
|
||||
|
||||
#define le2vdev(le, member) \
|
||||
to_struct((le), vfs_dev_t, member)
|
||||
|
||||
static list_entry_t vdev_list; // device info list in vfs layer
|
||||
static semaphore_t vdev_list_sem;
|
||||
|
||||
static void
|
||||
lock_vdev_list(void) {
|
||||
down(&vdev_list_sem);
|
||||
}
|
||||
|
||||
static void
|
||||
unlock_vdev_list(void) {
|
||||
up(&vdev_list_sem);
|
||||
}
|
||||
|
||||
void
|
||||
vfs_devlist_init(void) {
|
||||
list_init(&vdev_list);
|
||||
sem_init(&vdev_list_sem, 1);
|
||||
}
|
||||
|
||||
// vfs_cleanup - finally clean (or sync) fs
|
||||
void
|
||||
vfs_cleanup(void) {
|
||||
if (!list_empty(&vdev_list)) {
|
||||
lock_vdev_list();
|
||||
{
|
||||
list_entry_t *list = &vdev_list, *le = list;
|
||||
while ((le = list_next(le)) != list) {
|
||||
vfs_dev_t *vdev = le2vdev(le, vdev_link);
|
||||
if (vdev->fs != NULL) {
|
||||
fsop_cleanup(vdev->fs);
|
||||
}
|
||||
}
|
||||
}
|
||||
unlock_vdev_list();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* vfs_get_root - Given a device name (stdin, stdout, etc.), hand
|
||||
* back an appropriate inode.
|
||||
*/
|
||||
int
|
||||
vfs_get_root(const char *devname, struct inode **node_store) {
|
||||
assert(devname != NULL);
|
||||
int ret = -E_NO_DEV;
|
||||
if (!list_empty(&vdev_list)) {
|
||||
lock_vdev_list();
|
||||
{
|
||||
list_entry_t *list = &vdev_list, *le = list;
|
||||
while ((le = list_next(le)) != list) {
|
||||
vfs_dev_t *vdev = le2vdev(le, vdev_link);
|
||||
if (strcmp(devname, vdev->devname) == 0) {
|
||||
struct inode *found = NULL;
|
||||
if (vdev->fs != NULL) {
|
||||
found = fsop_get_root(vdev->fs);
|
||||
}
|
||||
else if (!vdev->mountable) {
|
||||
vop_ref_inc(vdev->devnode);
|
||||
found = vdev->devnode;
|
||||
}
|
||||
if (found != NULL) {
|
||||
ret = 0, *node_store = found;
|
||||
}
|
||||
else {
|
||||
ret = -E_NA_DEV;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
unlock_vdev_list();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* vfs_get_devname - Given a filesystem, hand back the name of the device it's mounted on.
|
||||
*/
|
||||
const char *
|
||||
vfs_get_devname(struct fs *fs) {
|
||||
assert(fs != NULL);
|
||||
list_entry_t *list = &vdev_list, *le = list;
|
||||
while ((le = list_next(le)) != list) {
|
||||
vfs_dev_t *vdev = le2vdev(le, vdev_link);
|
||||
if (vdev->fs == fs) {
|
||||
return vdev->devname;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* check_devname_confilct - Is there alreadily device which has the same name?
|
||||
*/
|
||||
static bool
|
||||
check_devname_conflict(const char *devname) {
|
||||
list_entry_t *list = &vdev_list, *le = list;
|
||||
while ((le = list_next(le)) != list) {
|
||||
vfs_dev_t *vdev = le2vdev(le, vdev_link);
|
||||
if (strcmp(vdev->devname, devname) == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* vfs_do_add - Add a new device to the VFS layer's device table.
|
||||
*
|
||||
* If "mountable" is set, the device will be treated as one that expects
|
||||
* to have a filesystem mounted on it, and a raw device will be created
|
||||
* for direct access.
|
||||
*/
|
||||
static int
|
||||
vfs_do_add(const char *devname, struct inode *devnode, struct fs *fs, bool mountable) {
|
||||
assert(devname != NULL);
|
||||
assert((devnode == NULL && !mountable) || (devnode != NULL && check_inode_type(devnode, device)));
|
||||
if (strlen(devname) > FS_MAX_DNAME_LEN) {
|
||||
return -E_TOO_BIG;
|
||||
}
|
||||
|
||||
int ret = -E_NO_MEM;
|
||||
char *s_devname;
|
||||
if ((s_devname = strdup(devname)) == NULL) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
vfs_dev_t *vdev;
|
||||
if ((vdev = kmalloc(sizeof(vfs_dev_t))) == NULL) {
|
||||
goto failed_cleanup_name;
|
||||
}
|
||||
|
||||
ret = -E_EXISTS;
|
||||
lock_vdev_list();
|
||||
if (!check_devname_conflict(s_devname)) {
|
||||
unlock_vdev_list();
|
||||
goto failed_cleanup_vdev;
|
||||
}
|
||||
vdev->devname = s_devname;
|
||||
vdev->devnode = devnode;
|
||||
vdev->mountable = mountable;
|
||||
vdev->fs = fs;
|
||||
|
||||
list_add(&vdev_list, &(vdev->vdev_link));
|
||||
unlock_vdev_list();
|
||||
return 0;
|
||||
|
||||
failed_cleanup_vdev:
|
||||
kfree(vdev);
|
||||
failed_cleanup_name:
|
||||
kfree(s_devname);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* vfs_add_fs - Add a new fs, by name. See vfs_do_add information for the description of
|
||||
* mountable.
|
||||
*/
|
||||
int
|
||||
vfs_add_fs(const char *devname, struct fs *fs) {
|
||||
return vfs_do_add(devname, NULL, fs, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* vfs_add_dev - Add a new device, by name. See vfs_do_add information for the description of
|
||||
* mountable.
|
||||
*/
|
||||
int
|
||||
vfs_add_dev(const char *devname, struct inode *devnode, bool mountable) {
|
||||
return vfs_do_add(devname, devnode, NULL, mountable);
|
||||
}
|
||||
|
||||
/*
|
||||
* find_mount - Look for a mountable device named DEVNAME.
|
||||
* Should already hold vdev_list lock.
|
||||
*/
|
||||
static int
|
||||
find_mount(const char *devname, vfs_dev_t **vdev_store) {
|
||||
assert(devname != NULL);
|
||||
list_entry_t *list = &vdev_list, *le = list;
|
||||
while ((le = list_next(le)) != list) {
|
||||
vfs_dev_t *vdev = le2vdev(le, vdev_link);
|
||||
if (vdev->mountable && strcmp(vdev->devname, devname) == 0) {
|
||||
*vdev_store = vdev;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -E_NO_DEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* vfs_mount - Mount a filesystem. Once we've found the device, call MOUNTFUNC to
|
||||
* set up the filesystem and hand back a struct fs.
|
||||
*
|
||||
* The DATA argument is passed through unchanged to MOUNTFUNC.
|
||||
*/
|
||||
int
|
||||
vfs_mount(const char *devname, int (*mountfunc)(struct device *dev, struct fs **fs_store)) {
|
||||
int ret;
|
||||
lock_vdev_list();
|
||||
vfs_dev_t *vdev;
|
||||
if ((ret = find_mount(devname, &vdev)) != 0) {
|
||||
goto out;
|
||||
}
|
||||
if (vdev->fs != NULL) {
|
||||
ret = -E_BUSY;
|
||||
goto out;
|
||||
}
|
||||
assert(vdev->devname != NULL && vdev->mountable);
|
||||
|
||||
struct device *dev = vop_info(vdev->devnode, device);
|
||||
if ((ret = mountfunc(dev, &(vdev->fs))) == 0) {
|
||||
assert(vdev->fs != NULL);
|
||||
cprintf("vfs: mount %s.\n", vdev->devname);
|
||||
}
|
||||
|
||||
out:
|
||||
unlock_vdev_list();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* vfs_unmount - Unmount a filesystem/device by name.
|
||||
* First calls FSOP_SYNC on the filesystem; then calls FSOP_UNMOUNT.
|
||||
*/
|
||||
int
|
||||
vfs_unmount(const char *devname) {
|
||||
int ret;
|
||||
lock_vdev_list();
|
||||
vfs_dev_t *vdev;
|
||||
if ((ret = find_mount(devname, &vdev)) != 0) {
|
||||
goto out;
|
||||
}
|
||||
if (vdev->fs == NULL) {
|
||||
ret = -E_INVAL;
|
||||
goto out;
|
||||
}
|
||||
assert(vdev->devname != NULL && vdev->mountable);
|
||||
|
||||
if ((ret = fsop_sync(vdev->fs)) != 0) {
|
||||
goto out;
|
||||
}
|
||||
if ((ret = fsop_unmount(vdev->fs)) == 0) {
|
||||
vdev->fs = NULL;
|
||||
cprintf("vfs: unmount %s.\n", vdev->devname);
|
||||
}
|
||||
|
||||
out:
|
||||
unlock_vdev_list();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* vfs_unmount_all - Global unmount function.
|
||||
*/
|
||||
int
|
||||
vfs_unmount_all(void) {
|
||||
if (!list_empty(&vdev_list)) {
|
||||
lock_vdev_list();
|
||||
{
|
||||
list_entry_t *list = &vdev_list, *le = list;
|
||||
while ((le = list_next(le)) != list) {
|
||||
vfs_dev_t *vdev = le2vdev(le, vdev_link);
|
||||
if (vdev->mountable && vdev->fs != NULL) {
|
||||
int ret;
|
||||
if ((ret = fsop_sync(vdev->fs)) != 0) {
|
||||
cprintf("vfs: warning: sync failed for %s: %e.\n", vdev->devname, ret);
|
||||
continue ;
|
||||
}
|
||||
if ((ret = fsop_unmount(vdev->fs)) != 0) {
|
||||
cprintf("vfs: warning: unmount failed for %s: %e.\n", vdev->devname, ret);
|
||||
continue ;
|
||||
}
|
||||
vdev->fs = NULL;
|
||||
cprintf("vfs: unmount %s.\n", vdev->devname);
|
||||
}
|
||||
}
|
||||
}
|
||||
unlock_vdev_list();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
110
labcodes/lab8/kern/fs/vfs/vfsfile.c
Normal file
110
labcodes/lab8/kern/fs/vfs/vfsfile.c
Normal file
@@ -0,0 +1,110 @@
|
||||
#include <defs.h>
|
||||
#include <string.h>
|
||||
#include <vfs.h>
|
||||
#include <inode.h>
|
||||
#include <unistd.h>
|
||||
#include <error.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
// open file in vfs, get/create inode for file with filename path.
|
||||
int
|
||||
vfs_open(char *path, uint32_t open_flags, struct inode **node_store) {
|
||||
bool can_write = 0;
|
||||
switch (open_flags & O_ACCMODE) {
|
||||
case O_RDONLY:
|
||||
break;
|
||||
case O_WRONLY:
|
||||
case O_RDWR:
|
||||
can_write = 1;
|
||||
break;
|
||||
default:
|
||||
return -E_INVAL;
|
||||
}
|
||||
|
||||
if (open_flags & O_TRUNC) {
|
||||
if (!can_write) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int ret;
|
||||
struct inode *node;
|
||||
bool excl = (open_flags & O_EXCL) != 0;
|
||||
bool create = (open_flags & O_CREAT) != 0;
|
||||
ret = vfs_lookup(path, &node);
|
||||
|
||||
if (ret != 0) {
|
||||
if (ret == -16 && (create)) {
|
||||
char *name;
|
||||
struct inode *dir;
|
||||
if ((ret = vfs_lookup_parent(path, &dir, &name)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
ret = vop_create(dir, name, excl, &node);
|
||||
} else return ret;
|
||||
} else if (excl && create) {
|
||||
return -E_EXISTS;
|
||||
}
|
||||
assert(node != NULL);
|
||||
|
||||
if ((ret = vop_open(node, open_flags)) != 0) {
|
||||
vop_ref_dec(node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
vop_open_inc(node);
|
||||
if (open_flags & O_TRUNC || create) {
|
||||
if ((ret = vop_truncate(node, 0)) != 0) {
|
||||
vop_open_dec(node);
|
||||
vop_ref_dec(node);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
*node_store = node;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// close file in vfs
|
||||
int
|
||||
vfs_close(struct inode *node) {
|
||||
vop_open_dec(node);
|
||||
vop_ref_dec(node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// unimplement
|
||||
int
|
||||
vfs_unlink(char *path) {
|
||||
return -E_UNIMP;
|
||||
}
|
||||
|
||||
// unimplement
|
||||
int
|
||||
vfs_rename(char *old_path, char *new_path) {
|
||||
return -E_UNIMP;
|
||||
}
|
||||
|
||||
// unimplement
|
||||
int
|
||||
vfs_link(char *old_path, char *new_path) {
|
||||
return -E_UNIMP;
|
||||
}
|
||||
|
||||
// unimplement
|
||||
int
|
||||
vfs_symlink(char *old_path, char *new_path) {
|
||||
return -E_UNIMP;
|
||||
}
|
||||
|
||||
// unimplement
|
||||
int
|
||||
vfs_readlink(char *path, struct iobuf *iob) {
|
||||
return -E_UNIMP;
|
||||
}
|
||||
|
||||
// unimplement
|
||||
int
|
||||
vfs_mkdir(char *path){
|
||||
return -E_UNIMP;
|
||||
}
|
||||
101
labcodes/lab8/kern/fs/vfs/vfslookup.c
Normal file
101
labcodes/lab8/kern/fs/vfs/vfslookup.c
Normal file
@@ -0,0 +1,101 @@
|
||||
#include <defs.h>
|
||||
#include <string.h>
|
||||
#include <vfs.h>
|
||||
#include <inode.h>
|
||||
#include <error.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
* get_device- Common code to pull the device name, if any, off the front of a
|
||||
* path and choose the inode to begin the name lookup relative to.
|
||||
*/
|
||||
|
||||
static int
|
||||
get_device(char *path, char **subpath, struct inode **node_store) {
|
||||
int i, slash = -1, colon = -1;
|
||||
for (i = 0; path[i] != '\0'; i ++) {
|
||||
if (path[i] == ':') { colon = i; break; }
|
||||
if (path[i] == '/') { slash = i; break; }
|
||||
}
|
||||
if (colon < 0 && slash != 0) {
|
||||
/* *
|
||||
* No colon before a slash, so no device name specified, and the slash isn't leading
|
||||
* or is also absent, so this is a relative path or just a bare filename. Start from
|
||||
* the current directory, and use the whole thing as the subpath.
|
||||
* */
|
||||
*subpath = path;
|
||||
return vfs_get_curdir(node_store);
|
||||
}
|
||||
if (colon > 0) {
|
||||
/* device:path - get root of device's filesystem */
|
||||
path[colon] = '\0';
|
||||
|
||||
/* device:/path - skip slash, treat as device:path */
|
||||
while (path[++ colon] == '/');
|
||||
*subpath = path + colon;
|
||||
return vfs_get_root(path, node_store);
|
||||
}
|
||||
|
||||
/* *
|
||||
* we have either /path or :path
|
||||
* /path is a path relative to the root of the "boot filesystem"
|
||||
* :path is a path relative to the root of the current filesystem
|
||||
* */
|
||||
int ret;
|
||||
if (*path == '/') {
|
||||
if ((ret = vfs_get_bootfs(node_store)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(*path == ':');
|
||||
struct inode *node;
|
||||
if ((ret = vfs_get_curdir(&node)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
/* The current directory may not be a device, so it must have a fs. */
|
||||
assert(node->in_fs != NULL);
|
||||
*node_store = fsop_get_root(node->in_fs);
|
||||
vop_ref_dec(node);
|
||||
}
|
||||
|
||||
/* ///... or :/... */
|
||||
while (*(++ path) == '/');
|
||||
*subpath = path;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* vfs_lookup - get the inode according to the path filename
|
||||
*/
|
||||
int
|
||||
vfs_lookup(char *path, struct inode **node_store) {
|
||||
int ret;
|
||||
struct inode *node;
|
||||
if ((ret = get_device(path, &path, &node)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
if (*path != '\0') {
|
||||
ret = vop_lookup(node, path, node_store);
|
||||
vop_ref_dec(node);
|
||||
return ret;
|
||||
}
|
||||
*node_store = node;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* vfs_lookup_parent - Name-to-vnode translation.
|
||||
* (In BSD, both of these are subsumed by namei().)
|
||||
*/
|
||||
int
|
||||
vfs_lookup_parent(char *path, struct inode **node_store, char **endp){
|
||||
int ret;
|
||||
struct inode *node;
|
||||
if ((ret = get_device(path, &path, &node)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
*endp = path;
|
||||
*node_store = node;
|
||||
return 0;
|
||||
}
|
||||
126
labcodes/lab8/kern/fs/vfs/vfspath.c
Normal file
126
labcodes/lab8/kern/fs/vfs/vfspath.c
Normal file
@@ -0,0 +1,126 @@
|
||||
#include <defs.h>
|
||||
#include <string.h>
|
||||
#include <vfs.h>
|
||||
#include <inode.h>
|
||||
#include <iobuf.h>
|
||||
#include <stat.h>
|
||||
#include <proc.h>
|
||||
#include <error.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
* get_cwd_nolock - retrieve current process's working directory. without lock protect
|
||||
*/
|
||||
static struct inode *
|
||||
get_cwd_nolock(void) {
|
||||
return current->filesp->pwd;
|
||||
}
|
||||
/*
|
||||
* set_cwd_nolock - set current working directory.
|
||||
*/
|
||||
static void
|
||||
set_cwd_nolock(struct inode *pwd) {
|
||||
current->filesp->pwd = pwd;
|
||||
}
|
||||
|
||||
/*
|
||||
* lock_cfs - lock the fs related process on current process
|
||||
*/
|
||||
static void
|
||||
lock_cfs(void) {
|
||||
lock_files(current->filesp);
|
||||
}
|
||||
/*
|
||||
* unlock_cfs - unlock the fs related process on current process
|
||||
*/
|
||||
static void
|
||||
unlock_cfs(void) {
|
||||
unlock_files(current->filesp);
|
||||
}
|
||||
|
||||
/*
|
||||
* vfs_get_curdir - Get current directory as a inode.
|
||||
*/
|
||||
int
|
||||
vfs_get_curdir(struct inode **dir_store) {
|
||||
struct inode *node;
|
||||
if ((node = get_cwd_nolock()) != NULL) {
|
||||
vop_ref_inc(node);
|
||||
*dir_store = node;
|
||||
return 0;
|
||||
}
|
||||
return -E_NOENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* vfs_set_curdir - Set current directory as a inode.
|
||||
* The passed inode must in fact be a directory.
|
||||
*/
|
||||
int
|
||||
vfs_set_curdir(struct inode *dir) {
|
||||
int ret = 0;
|
||||
lock_cfs();
|
||||
struct inode *old_dir;
|
||||
if ((old_dir = get_cwd_nolock()) != dir) {
|
||||
if (dir != NULL) {
|
||||
uint32_t type;
|
||||
if ((ret = vop_gettype(dir, &type)) != 0) {
|
||||
goto out;
|
||||
}
|
||||
if (!S_ISDIR(type)) {
|
||||
ret = -E_NOTDIR;
|
||||
goto out;
|
||||
}
|
||||
vop_ref_inc(dir);
|
||||
}
|
||||
set_cwd_nolock(dir);
|
||||
if (old_dir != NULL) {
|
||||
vop_ref_dec(old_dir);
|
||||
}
|
||||
}
|
||||
out:
|
||||
unlock_cfs();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* vfs_chdir - Set current directory, as a pathname. Use vfs_lookup to translate
|
||||
* it to a inode.
|
||||
*/
|
||||
int
|
||||
vfs_chdir(char *path) {
|
||||
int ret;
|
||||
struct inode *node;
|
||||
if ((ret = vfs_lookup(path, &node)) == 0) {
|
||||
ret = vfs_set_curdir(node);
|
||||
vop_ref_dec(node);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* vfs_getcwd - retrieve current working directory(cwd).
|
||||
*/
|
||||
int
|
||||
vfs_getcwd(struct iobuf *iob) {
|
||||
int ret;
|
||||
struct inode *node;
|
||||
if ((ret = vfs_get_curdir(&node)) != 0) {
|
||||
return ret;
|
||||
}
|
||||
assert(node->in_fs != NULL);
|
||||
|
||||
const char *devname = vfs_get_devname(node->in_fs);
|
||||
if ((ret = iobuf_move(iob, (char *)devname, strlen(devname), 1, NULL)) != 0) {
|
||||
goto out;
|
||||
}
|
||||
char colon = ':';
|
||||
if ((ret = iobuf_move(iob, &colon, sizeof(colon), 1, NULL)) != 0) {
|
||||
goto out;
|
||||
}
|
||||
ret = vop_namefile(node, iob);
|
||||
|
||||
out:
|
||||
vop_ref_dec(node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user