Lab 5
This commit is contained in:
77
fs/Makefrag
Normal file
77
fs/Makefrag
Normal file
@@ -0,0 +1,77 @@
|
||||
|
||||
OBJDIRS += fs
|
||||
|
||||
FSOFILES := $(OBJDIR)/fs/ide.o \
|
||||
$(OBJDIR)/fs/bc.o \
|
||||
$(OBJDIR)/fs/fs.o \
|
||||
$(OBJDIR)/fs/serv.o \
|
||||
$(OBJDIR)/fs/test.o \
|
||||
|
||||
USERAPPS := $(OBJDIR)/user/init
|
||||
|
||||
FSIMGTXTFILES := fs/newmotd \
|
||||
fs/motd
|
||||
|
||||
|
||||
USERAPPS := $(USERAPPS) \
|
||||
$(OBJDIR)/user/cat \
|
||||
$(OBJDIR)/user/echo \
|
||||
$(OBJDIR)/user/init \
|
||||
$(OBJDIR)/user/ls \
|
||||
$(OBJDIR)/user/lsfd \
|
||||
$(OBJDIR)/user/num \
|
||||
$(OBJDIR)/user/forktree \
|
||||
$(OBJDIR)/user/primes \
|
||||
$(OBJDIR)/user/primespipe \
|
||||
$(OBJDIR)/user/sh \
|
||||
$(OBJDIR)/user/testfdsharing \
|
||||
$(OBJDIR)/user/testkbd \
|
||||
$(OBJDIR)/user/testpipe \
|
||||
$(OBJDIR)/user/testpteshare \
|
||||
$(OBJDIR)/user/testshell \
|
||||
$(OBJDIR)/user/hello \
|
||||
$(OBJDIR)/user/faultio \
|
||||
|
||||
FSIMGTXTFILES := $(FSIMGTXTFILES) \
|
||||
fs/lorem \
|
||||
fs/script \
|
||||
fs/testshell.key \
|
||||
fs/testshell.sh
|
||||
|
||||
|
||||
FSIMGFILES := $(FSIMGTXTFILES) $(USERAPPS)
|
||||
|
||||
$(OBJDIR)/fs/%.o: fs/%.c fs/fs.h inc/lib.h $(OBJDIR)/.vars.USER_CFLAGS
|
||||
@echo + cc[USER] $<
|
||||
@mkdir -p $(@D)
|
||||
$(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/fs/fs: $(FSOFILES) $(OBJDIR)/lib/entry.o $(OBJDIR)/lib/libjos.a user/user.ld
|
||||
@echo + ld $@
|
||||
$(V)mkdir -p $(@D)
|
||||
$(V)$(LD) -o $@ $(ULDFLAGS) $(LDFLAGS) -nostdlib \
|
||||
$(OBJDIR)/lib/entry.o $(FSOFILES) \
|
||||
-L$(OBJDIR)/lib -ljos $(GCC_LIB)
|
||||
$(V)$(OBJDUMP) -S $@ >$@.asm
|
||||
|
||||
# How to build the file system image
|
||||
$(OBJDIR)/fs/fsformat: fs/fsformat.c
|
||||
@echo + mk $(OBJDIR)/fs/fsformat
|
||||
$(V)mkdir -p $(@D)
|
||||
$(V)$(NCC) $(NATIVE_CFLAGS) -o $(OBJDIR)/fs/fsformat fs/fsformat.c
|
||||
|
||||
$(OBJDIR)/fs/clean-fs.img: $(OBJDIR)/fs/fsformat $(FSIMGFILES)
|
||||
@echo + mk $(OBJDIR)/fs/clean-fs.img
|
||||
$(V)mkdir -p $(@D)
|
||||
$(V)$(OBJDIR)/fs/fsformat $(OBJDIR)/fs/clean-fs.img 1024 $(FSIMGFILES)
|
||||
|
||||
$(OBJDIR)/fs/fs.img: $(OBJDIR)/fs/clean-fs.img
|
||||
@echo + cp $(OBJDIR)/fs/clean-fs.img $@
|
||||
$(V)cp $(OBJDIR)/fs/clean-fs.img $@
|
||||
|
||||
all: $(OBJDIR)/fs/fs.img
|
||||
|
||||
#all: $(addsuffix .sym, $(USERAPPS))
|
||||
|
||||
#all: $(addsuffix .asm, $(USERAPPS))
|
||||
|
||||
151
fs/bc.c
Normal file
151
fs/bc.c
Normal file
@@ -0,0 +1,151 @@
|
||||
|
||||
#include "fs.h"
|
||||
|
||||
// Return the virtual address of this disk block.
|
||||
void*
|
||||
diskaddr(uint32_t blockno)
|
||||
{
|
||||
if (blockno == 0 || (super && blockno >= super->s_nblocks))
|
||||
panic("bad block number %08x in diskaddr", blockno);
|
||||
return (char*) (DISKMAP + blockno * BLKSIZE);
|
||||
}
|
||||
|
||||
// Is this virtual address mapped?
|
||||
bool
|
||||
va_is_mapped(void *va)
|
||||
{
|
||||
return (uvpd[PDX(va)] & PTE_P) && (uvpt[PGNUM(va)] & PTE_P);
|
||||
}
|
||||
|
||||
// Is this virtual address dirty?
|
||||
bool
|
||||
va_is_dirty(void *va)
|
||||
{
|
||||
return (uvpt[PGNUM(va)] & PTE_D) != 0;
|
||||
}
|
||||
|
||||
// Fault any disk block that is read in to memory by
|
||||
// loading it from disk.
|
||||
static void
|
||||
bc_pgfault(struct UTrapframe *utf)
|
||||
{
|
||||
void *addr = (void *) utf->utf_fault_va;
|
||||
uint32_t blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE;
|
||||
int r;
|
||||
|
||||
// Check that the fault was within the block cache region
|
||||
if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE))
|
||||
panic("page fault in FS: eip %08x, va %08x, err %04x",
|
||||
utf->utf_eip, addr, utf->utf_err);
|
||||
|
||||
// Sanity check the block number.
|
||||
if (super && blockno >= super->s_nblocks)
|
||||
panic("reading non-existent block %08x\n", blockno);
|
||||
|
||||
// Allocate a page in the disk map region, read the contents
|
||||
// of the block from the disk into that page.
|
||||
// Hint: first round addr to page boundary. fs/ide.c has code to read
|
||||
// the disk.
|
||||
//
|
||||
// LAB 5: you code here:
|
||||
|
||||
// Clear the dirty bit for the disk block page since we just read the
|
||||
// block from disk
|
||||
if ((r = sys_page_map(0, addr, 0, addr, uvpt[PGNUM(addr)] & PTE_SYSCALL)) < 0)
|
||||
panic("in bc_pgfault, sys_page_map: %e", r);
|
||||
|
||||
// Check that the block we read was allocated. (exercise for
|
||||
// the reader: why do we do this *after* reading the block
|
||||
// in?)
|
||||
if (bitmap && block_is_free(blockno))
|
||||
panic("reading free block %08x\n", blockno);
|
||||
}
|
||||
|
||||
// Flush the contents of the block containing VA out to disk if
|
||||
// necessary, then clear the PTE_D bit using sys_page_map.
|
||||
// If the block is not in the block cache or is not dirty, does
|
||||
// nothing.
|
||||
// Hint: Use va_is_mapped, va_is_dirty, and ide_write.
|
||||
// Hint: Use the PTE_SYSCALL constant when calling sys_page_map.
|
||||
// Hint: Don't forget to round addr down.
|
||||
void
|
||||
flush_block(void *addr)
|
||||
{
|
||||
uint32_t blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE;
|
||||
|
||||
if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE))
|
||||
panic("flush_block of bad va %08x", addr);
|
||||
|
||||
// LAB 5: Your code here.
|
||||
panic("flush_block not implemented");
|
||||
}
|
||||
|
||||
// Test that the block cache works, by smashing the superblock and
|
||||
// reading it back.
|
||||
static void
|
||||
check_bc(void)
|
||||
{
|
||||
struct Super backup;
|
||||
|
||||
// back up super block
|
||||
memmove(&backup, diskaddr(1), sizeof backup);
|
||||
|
||||
// smash it
|
||||
strcpy(diskaddr(1), "OOPS!\n");
|
||||
flush_block(diskaddr(1));
|
||||
assert(va_is_mapped(diskaddr(1)));
|
||||
assert(!va_is_dirty(diskaddr(1)));
|
||||
|
||||
// clear it out
|
||||
sys_page_unmap(0, diskaddr(1));
|
||||
assert(!va_is_mapped(diskaddr(1)));
|
||||
|
||||
// read it back in
|
||||
assert(strcmp(diskaddr(1), "OOPS!\n") == 0);
|
||||
|
||||
// fix it
|
||||
memmove(diskaddr(1), &backup, sizeof backup);
|
||||
flush_block(diskaddr(1));
|
||||
|
||||
// Now repeat the same experiment, but pass an unaligned address to
|
||||
// flush_block.
|
||||
|
||||
// back up super block
|
||||
memmove(&backup, diskaddr(1), sizeof backup);
|
||||
|
||||
// smash it
|
||||
strcpy(diskaddr(1), "OOPS!\n");
|
||||
|
||||
// Pass an unaligned address to flush_block.
|
||||
flush_block(diskaddr(1) + 20);
|
||||
assert(va_is_mapped(diskaddr(1)));
|
||||
|
||||
// Skip the !va_is_dirty() check because it makes the bug somewhat
|
||||
// obscure and hence harder to debug.
|
||||
//assert(!va_is_dirty(diskaddr(1)));
|
||||
|
||||
// clear it out
|
||||
sys_page_unmap(0, diskaddr(1));
|
||||
assert(!va_is_mapped(diskaddr(1)));
|
||||
|
||||
// read it back in
|
||||
assert(strcmp(diskaddr(1), "OOPS!\n") == 0);
|
||||
|
||||
// fix it
|
||||
memmove(diskaddr(1), &backup, sizeof backup);
|
||||
flush_block(diskaddr(1));
|
||||
|
||||
cprintf("block cache is good\n");
|
||||
}
|
||||
|
||||
void
|
||||
bc_init(void)
|
||||
{
|
||||
struct Super super;
|
||||
set_pgfault_handler(bc_pgfault);
|
||||
check_bc();
|
||||
|
||||
// cache the super block by reading it once
|
||||
memmove(&super, diskaddr(1), sizeof super);
|
||||
}
|
||||
|
||||
456
fs/fs.c
Normal file
456
fs/fs.c
Normal file
@@ -0,0 +1,456 @@
|
||||
#include <inc/string.h>
|
||||
#include <inc/partition.h>
|
||||
|
||||
#include "fs.h"
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Super block
|
||||
// --------------------------------------------------------------
|
||||
|
||||
// Validate the file system super-block.
|
||||
void
|
||||
check_super(void)
|
||||
{
|
||||
if (super->s_magic != FS_MAGIC)
|
||||
panic("bad file system magic number");
|
||||
|
||||
if (super->s_nblocks > DISKSIZE/BLKSIZE)
|
||||
panic("file system is too large");
|
||||
|
||||
cprintf("superblock is good\n");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Free block bitmap
|
||||
// --------------------------------------------------------------
|
||||
|
||||
// Check to see if the block bitmap indicates that block 'blockno' is free.
|
||||
// Return 1 if the block is free, 0 if not.
|
||||
bool
|
||||
block_is_free(uint32_t blockno)
|
||||
{
|
||||
if (super == 0 || blockno >= super->s_nblocks)
|
||||
return 0;
|
||||
if (bitmap[blockno / 32] & (1 << (blockno % 32)))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Mark a block free in the bitmap
|
||||
void
|
||||
free_block(uint32_t blockno)
|
||||
{
|
||||
// Blockno zero is the null pointer of block numbers.
|
||||
if (blockno == 0)
|
||||
panic("attempt to free zero block");
|
||||
bitmap[blockno/32] |= 1<<(blockno%32);
|
||||
}
|
||||
|
||||
// Search the bitmap for a free block and allocate it. When you
|
||||
// allocate a block, immediately flush the changed bitmap block
|
||||
// to disk.
|
||||
//
|
||||
// Return block number allocated on success,
|
||||
// -E_NO_DISK if we are out of blocks.
|
||||
//
|
||||
// Hint: use free_block as an example for manipulating the bitmap.
|
||||
int
|
||||
alloc_block(void)
|
||||
{
|
||||
// The bitmap consists of one or more blocks. A single bitmap block
|
||||
// contains the in-use bits for BLKBITSIZE blocks. There are
|
||||
// super->s_nblocks blocks in the disk altogether.
|
||||
|
||||
// LAB 5: Your code here.
|
||||
panic("alloc_block not implemented");
|
||||
return -E_NO_DISK;
|
||||
}
|
||||
|
||||
// Validate the file system bitmap.
|
||||
//
|
||||
// Check that all reserved blocks -- 0, 1, and the bitmap blocks themselves --
|
||||
// are all marked as in-use.
|
||||
void
|
||||
check_bitmap(void)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
// Make sure all bitmap blocks are marked in-use
|
||||
for (i = 0; i * BLKBITSIZE < super->s_nblocks; i++)
|
||||
assert(!block_is_free(2+i));
|
||||
|
||||
// Make sure the reserved and root blocks are marked in-use.
|
||||
assert(!block_is_free(0));
|
||||
assert(!block_is_free(1));
|
||||
|
||||
cprintf("bitmap is good\n");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// File system structures
|
||||
// --------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
// Initialize the file system
|
||||
void
|
||||
fs_init(void)
|
||||
{
|
||||
static_assert(sizeof(struct File) == 256);
|
||||
|
||||
// Find a JOS disk. Use the second IDE disk (number 1) if available
|
||||
if (ide_probe_disk1())
|
||||
ide_set_disk(1);
|
||||
else
|
||||
ide_set_disk(0);
|
||||
bc_init();
|
||||
|
||||
// Set "super" to point to the super block.
|
||||
super = diskaddr(1);
|
||||
check_super();
|
||||
|
||||
// Set "bitmap" to the beginning of the first bitmap block.
|
||||
bitmap = diskaddr(2);
|
||||
check_bitmap();
|
||||
|
||||
}
|
||||
|
||||
// Find the disk block number slot for the 'filebno'th block in file 'f'.
|
||||
// Set '*ppdiskbno' to point to that slot.
|
||||
// The slot will be one of the f->f_direct[] entries,
|
||||
// or an entry in the indirect block.
|
||||
// When 'alloc' is set, this function will allocate an indirect block
|
||||
// if necessary.
|
||||
//
|
||||
// Returns:
|
||||
// 0 on success (but note that *ppdiskbno might equal 0).
|
||||
// -E_NOT_FOUND if the function needed to allocate an indirect block, but
|
||||
// alloc was 0.
|
||||
// -E_NO_DISK if there's no space on the disk for an indirect block.
|
||||
// -E_INVAL if filebno is out of range (it's >= NDIRECT + NINDIRECT).
|
||||
//
|
||||
// Analogy: This is like pgdir_walk for files.
|
||||
// Hint: Don't forget to clear any block you allocate.
|
||||
static int
|
||||
file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool alloc)
|
||||
{
|
||||
// LAB 5: Your code here.
|
||||
panic("file_block_walk not implemented");
|
||||
}
|
||||
|
||||
// Set *blk to the address in memory where the filebno'th
|
||||
// block of file 'f' would be mapped.
|
||||
//
|
||||
// Returns 0 on success, < 0 on error. Errors are:
|
||||
// -E_NO_DISK if a block needed to be allocated but the disk is full.
|
||||
// -E_INVAL if filebno is out of range.
|
||||
//
|
||||
// Hint: Use file_block_walk and alloc_block.
|
||||
int
|
||||
file_get_block(struct File *f, uint32_t filebno, char **blk)
|
||||
{
|
||||
// LAB 5: Your code here.
|
||||
panic("file_get_block not implemented");
|
||||
}
|
||||
|
||||
// Try to find a file named "name" in dir. If so, set *file to it.
|
||||
//
|
||||
// Returns 0 and sets *file on success, < 0 on error. Errors are:
|
||||
// -E_NOT_FOUND if the file is not found
|
||||
static int
|
||||
dir_lookup(struct File *dir, const char *name, struct File **file)
|
||||
{
|
||||
int r;
|
||||
uint32_t i, j, nblock;
|
||||
char *blk;
|
||||
struct File *f;
|
||||
|
||||
// Search dir for name.
|
||||
// We maintain the invariant that the size of a directory-file
|
||||
// is always a multiple of the file system's block size.
|
||||
assert((dir->f_size % BLKSIZE) == 0);
|
||||
nblock = dir->f_size / BLKSIZE;
|
||||
for (i = 0; i < nblock; i++) {
|
||||
if ((r = file_get_block(dir, i, &blk)) < 0)
|
||||
return r;
|
||||
f = (struct File*) blk;
|
||||
for (j = 0; j < BLKFILES; j++)
|
||||
if (strcmp(f[j].f_name, name) == 0) {
|
||||
*file = &f[j];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -E_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Set *file to point at a free File structure in dir. The caller is
|
||||
// responsible for filling in the File fields.
|
||||
static int
|
||||
dir_alloc_file(struct File *dir, struct File **file)
|
||||
{
|
||||
int r;
|
||||
uint32_t nblock, i, j;
|
||||
char *blk;
|
||||
struct File *f;
|
||||
|
||||
assert((dir->f_size % BLKSIZE) == 0);
|
||||
nblock = dir->f_size / BLKSIZE;
|
||||
for (i = 0; i < nblock; i++) {
|
||||
if ((r = file_get_block(dir, i, &blk)) < 0)
|
||||
return r;
|
||||
f = (struct File*) blk;
|
||||
for (j = 0; j < BLKFILES; j++)
|
||||
if (f[j].f_name[0] == '\0') {
|
||||
*file = &f[j];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
dir->f_size += BLKSIZE;
|
||||
if ((r = file_get_block(dir, i, &blk)) < 0)
|
||||
return r;
|
||||
f = (struct File*) blk;
|
||||
*file = &f[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Skip over slashes.
|
||||
static const char*
|
||||
skip_slash(const char *p)
|
||||
{
|
||||
while (*p == '/')
|
||||
p++;
|
||||
return p;
|
||||
}
|
||||
|
||||
// Evaluate a path name, starting at the root.
|
||||
// On success, set *pf to the file we found
|
||||
// and set *pdir to the directory the file is in.
|
||||
// If we cannot find the file but find the directory
|
||||
// it should be in, set *pdir and copy the final path
|
||||
// element into lastelem.
|
||||
static int
|
||||
walk_path(const char *path, struct File **pdir, struct File **pf, char *lastelem)
|
||||
{
|
||||
const char *p;
|
||||
char name[MAXNAMELEN];
|
||||
struct File *dir, *f;
|
||||
int r;
|
||||
|
||||
// if (*path != '/')
|
||||
// return -E_BAD_PATH;
|
||||
path = skip_slash(path);
|
||||
f = &super->s_root;
|
||||
dir = 0;
|
||||
name[0] = 0;
|
||||
|
||||
if (pdir)
|
||||
*pdir = 0;
|
||||
*pf = 0;
|
||||
while (*path != '\0') {
|
||||
dir = f;
|
||||
p = path;
|
||||
while (*path != '/' && *path != '\0')
|
||||
path++;
|
||||
if (path - p >= MAXNAMELEN)
|
||||
return -E_BAD_PATH;
|
||||
memmove(name, p, path - p);
|
||||
name[path - p] = '\0';
|
||||
path = skip_slash(path);
|
||||
|
||||
if (dir->f_type != FTYPE_DIR)
|
||||
return -E_NOT_FOUND;
|
||||
|
||||
if ((r = dir_lookup(dir, name, &f)) < 0) {
|
||||
if (r == -E_NOT_FOUND && *path == '\0') {
|
||||
if (pdir)
|
||||
*pdir = dir;
|
||||
if (lastelem)
|
||||
strcpy(lastelem, name);
|
||||
*pf = 0;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdir)
|
||||
*pdir = dir;
|
||||
*pf = f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// File operations
|
||||
// --------------------------------------------------------------
|
||||
|
||||
// Create "path". On success set *pf to point at the file and return 0.
|
||||
// On error return < 0.
|
||||
int
|
||||
file_create(const char *path, struct File **pf)
|
||||
{
|
||||
char name[MAXNAMELEN];
|
||||
int r;
|
||||
struct File *dir, *f;
|
||||
|
||||
if ((r = walk_path(path, &dir, &f, name)) == 0)
|
||||
return -E_FILE_EXISTS;
|
||||
if (r != -E_NOT_FOUND || dir == 0)
|
||||
return r;
|
||||
if ((r = dir_alloc_file(dir, &f)) < 0)
|
||||
return r;
|
||||
|
||||
strcpy(f->f_name, name);
|
||||
*pf = f;
|
||||
file_flush(dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Open "path". On success set *pf to point at the file and return 0.
|
||||
// On error return < 0.
|
||||
int
|
||||
file_open(const char *path, struct File **pf)
|
||||
{
|
||||
return walk_path(path, 0, pf, 0);
|
||||
}
|
||||
|
||||
// Read count bytes from f into buf, starting from seek position
|
||||
// offset. This meant to mimic the standard pread function.
|
||||
// Returns the number of bytes read, < 0 on error.
|
||||
ssize_t
|
||||
file_read(struct File *f, void *buf, size_t count, off_t offset)
|
||||
{
|
||||
int r, bn;
|
||||
off_t pos;
|
||||
char *blk;
|
||||
|
||||
if (offset >= f->f_size)
|
||||
return 0;
|
||||
|
||||
count = MIN(count, f->f_size - offset);
|
||||
|
||||
for (pos = offset; pos < offset + count; ) {
|
||||
if ((r = file_get_block(f, pos / BLKSIZE, &blk)) < 0)
|
||||
return r;
|
||||
bn = MIN(BLKSIZE - pos % BLKSIZE, offset + count - pos);
|
||||
memmove(buf, blk + pos % BLKSIZE, bn);
|
||||
pos += bn;
|
||||
buf += bn;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
// Write count bytes from buf into f, starting at seek position
|
||||
// offset. This is meant to mimic the standard pwrite function.
|
||||
// Extends the file if necessary.
|
||||
// Returns the number of bytes written, < 0 on error.
|
||||
int
|
||||
file_write(struct File *f, const void *buf, size_t count, off_t offset)
|
||||
{
|
||||
int r, bn;
|
||||
off_t pos;
|
||||
char *blk;
|
||||
|
||||
// Extend file if necessary
|
||||
if (offset + count > f->f_size)
|
||||
if ((r = file_set_size(f, offset + count)) < 0)
|
||||
return r;
|
||||
|
||||
for (pos = offset; pos < offset + count; ) {
|
||||
if ((r = file_get_block(f, pos / BLKSIZE, &blk)) < 0)
|
||||
return r;
|
||||
bn = MIN(BLKSIZE - pos % BLKSIZE, offset + count - pos);
|
||||
memmove(blk + pos % BLKSIZE, buf, bn);
|
||||
pos += bn;
|
||||
buf += bn;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// Remove a block from file f. If it's not there, just silently succeed.
|
||||
// Returns 0 on success, < 0 on error.
|
||||
static int
|
||||
file_free_block(struct File *f, uint32_t filebno)
|
||||
{
|
||||
int r;
|
||||
uint32_t *ptr;
|
||||
|
||||
if ((r = file_block_walk(f, filebno, &ptr, 0)) < 0)
|
||||
return r;
|
||||
if (*ptr) {
|
||||
free_block(*ptr);
|
||||
*ptr = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Remove any blocks currently used by file 'f',
|
||||
// but not necessary for a file of size 'newsize'.
|
||||
// For both the old and new sizes, figure out the number of blocks required,
|
||||
// and then clear the blocks from new_nblocks to old_nblocks.
|
||||
// If the new_nblocks is no more than NDIRECT, and the indirect block has
|
||||
// been allocated (f->f_indirect != 0), then free the indirect block too.
|
||||
// (Remember to clear the f->f_indirect pointer so you'll know
|
||||
// whether it's valid!)
|
||||
// Do not change f->f_size.
|
||||
static void
|
||||
file_truncate_blocks(struct File *f, off_t newsize)
|
||||
{
|
||||
int r;
|
||||
uint32_t bno, old_nblocks, new_nblocks;
|
||||
|
||||
old_nblocks = (f->f_size + BLKSIZE - 1) / BLKSIZE;
|
||||
new_nblocks = (newsize + BLKSIZE - 1) / BLKSIZE;
|
||||
for (bno = new_nblocks; bno < old_nblocks; bno++)
|
||||
if ((r = file_free_block(f, bno)) < 0)
|
||||
cprintf("warning: file_free_block: %e", r);
|
||||
|
||||
if (new_nblocks <= NDIRECT && f->f_indirect) {
|
||||
free_block(f->f_indirect);
|
||||
f->f_indirect = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the size of file f, truncating or extending as necessary.
|
||||
int
|
||||
file_set_size(struct File *f, off_t newsize)
|
||||
{
|
||||
if (f->f_size > newsize)
|
||||
file_truncate_blocks(f, newsize);
|
||||
f->f_size = newsize;
|
||||
flush_block(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Flush the contents and metadata of file f out to disk.
|
||||
// Loop over all the blocks in file.
|
||||
// Translate the file block number into a disk block number
|
||||
// and then check whether that disk block is dirty. If so, write it out.
|
||||
void
|
||||
file_flush(struct File *f)
|
||||
{
|
||||
int i;
|
||||
uint32_t *pdiskbno;
|
||||
|
||||
for (i = 0; i < (f->f_size + BLKSIZE - 1) / BLKSIZE; i++) {
|
||||
if (file_block_walk(f, i, &pdiskbno, 0) < 0 ||
|
||||
pdiskbno == NULL || *pdiskbno == 0)
|
||||
continue;
|
||||
flush_block(diskaddr(*pdiskbno));
|
||||
}
|
||||
flush_block(f);
|
||||
if (f->f_indirect)
|
||||
flush_block(diskaddr(f->f_indirect));
|
||||
}
|
||||
|
||||
|
||||
// Sync the entire file system. A big hammer.
|
||||
void
|
||||
fs_sync(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i < super->s_nblocks; i++)
|
||||
flush_block(diskaddr(i));
|
||||
}
|
||||
|
||||
49
fs/fs.h
Normal file
49
fs/fs.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <inc/fs.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
#define SECTSIZE 512 // bytes per disk sector
|
||||
#define BLKSECTS (BLKSIZE / SECTSIZE) // sectors per block
|
||||
|
||||
/* Disk block n, when in memory, is mapped into the file system
|
||||
* server's address space at DISKMAP + (n*BLKSIZE). */
|
||||
#define DISKMAP 0x10000000
|
||||
|
||||
/* Maximum disk size we can handle (3GB) */
|
||||
#define DISKSIZE 0xC0000000
|
||||
|
||||
struct Super *super; // superblock
|
||||
uint32_t *bitmap; // bitmap blocks mapped in memory
|
||||
|
||||
/* ide.c */
|
||||
bool ide_probe_disk1(void);
|
||||
void ide_set_disk(int diskno);
|
||||
void ide_set_partition(uint32_t first_sect, uint32_t nsect);
|
||||
int ide_read(uint32_t secno, void *dst, size_t nsecs);
|
||||
int ide_write(uint32_t secno, const void *src, size_t nsecs);
|
||||
|
||||
/* bc.c */
|
||||
void* diskaddr(uint32_t blockno);
|
||||
bool va_is_mapped(void *va);
|
||||
bool va_is_dirty(void *va);
|
||||
void flush_block(void *addr);
|
||||
void bc_init(void);
|
||||
|
||||
/* fs.c */
|
||||
void fs_init(void);
|
||||
int file_get_block(struct File *f, uint32_t file_blockno, char **pblk);
|
||||
int file_create(const char *path, struct File **f);
|
||||
int file_open(const char *path, struct File **f);
|
||||
ssize_t file_read(struct File *f, void *buf, size_t count, off_t offset);
|
||||
int file_write(struct File *f, const void *buf, size_t count, off_t offset);
|
||||
int file_set_size(struct File *f, off_t newsize);
|
||||
void file_flush(struct File *f);
|
||||
int file_remove(const char *path);
|
||||
void fs_sync(void);
|
||||
|
||||
/* int map_block(uint32_t); */
|
||||
bool block_is_free(uint32_t blockno);
|
||||
int alloc_block(void);
|
||||
|
||||
/* test.c */
|
||||
void fs_test(void);
|
||||
|
||||
244
fs/fsformat.c
Normal file
244
fs/fsformat.c
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* JOS file system format
|
||||
*/
|
||||
|
||||
// We don't actually want to define off_t!
|
||||
#define off_t xxx_off_t
|
||||
#define bool xxx_bool
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#undef off_t
|
||||
#undef bool
|
||||
|
||||
// Prevent inc/types.h, included from inc/fs.h,
|
||||
// from attempting to redefine types defined in the host's inttypes.h.
|
||||
#define JOS_INC_TYPES_H
|
||||
// Typedef the types that inc/mmu.h needs.
|
||||
typedef uint32_t physaddr_t;
|
||||
typedef uint32_t off_t;
|
||||
typedef int bool;
|
||||
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/fs.h>
|
||||
|
||||
#define ROUNDUP(n, v) ((n) - 1 + (v) - ((n) - 1) % (v))
|
||||
#define MAX_DIR_ENTS 128
|
||||
|
||||
struct Dir
|
||||
{
|
||||
struct File *f;
|
||||
struct File *ents;
|
||||
int n;
|
||||
};
|
||||
|
||||
uint32_t nblocks;
|
||||
char *diskmap, *diskpos;
|
||||
struct Super *super;
|
||||
uint32_t *bitmap;
|
||||
|
||||
void
|
||||
panic(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
fputc('\n', stderr);
|
||||
abort();
|
||||
}
|
||||
|
||||
void
|
||||
readn(int f, void *out, size_t n)
|
||||
{
|
||||
size_t p = 0;
|
||||
while (p < n) {
|
||||
ssize_t m = read(f, out + p, n - p);
|
||||
if (m < 0)
|
||||
panic("read: %s", strerror(errno));
|
||||
if (m == 0)
|
||||
panic("read: Unexpected EOF");
|
||||
p += m;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
blockof(void *pos)
|
||||
{
|
||||
return ((char*)pos - diskmap) / BLKSIZE;
|
||||
}
|
||||
|
||||
void *
|
||||
alloc(uint32_t bytes)
|
||||
{
|
||||
void *start = diskpos;
|
||||
diskpos += ROUNDUP(bytes, BLKSIZE);
|
||||
if (blockof(diskpos) >= nblocks)
|
||||
panic("out of disk blocks");
|
||||
return start;
|
||||
}
|
||||
|
||||
void
|
||||
opendisk(const char *name)
|
||||
{
|
||||
int r, diskfd, nbitblocks;
|
||||
|
||||
if ((diskfd = open(name, O_RDWR | O_CREAT, 0666)) < 0)
|
||||
panic("open %s: %s", name, strerror(errno));
|
||||
|
||||
if ((r = ftruncate(diskfd, 0)) < 0
|
||||
|| (r = ftruncate(diskfd, nblocks * BLKSIZE)) < 0)
|
||||
panic("truncate %s: %s", name, strerror(errno));
|
||||
|
||||
if ((diskmap = mmap(NULL, nblocks * BLKSIZE, PROT_READ|PROT_WRITE,
|
||||
MAP_SHARED, diskfd, 0)) == MAP_FAILED)
|
||||
panic("mmap %s: %s", name, strerror(errno));
|
||||
|
||||
close(diskfd);
|
||||
|
||||
diskpos = diskmap;
|
||||
alloc(BLKSIZE);
|
||||
super = alloc(BLKSIZE);
|
||||
super->s_magic = FS_MAGIC;
|
||||
super->s_nblocks = nblocks;
|
||||
super->s_root.f_type = FTYPE_DIR;
|
||||
strcpy(super->s_root.f_name, "/");
|
||||
|
||||
nbitblocks = (nblocks + BLKBITSIZE - 1) / BLKBITSIZE;
|
||||
bitmap = alloc(nbitblocks * BLKSIZE);
|
||||
memset(bitmap, 0xFF, nbitblocks * BLKSIZE);
|
||||
}
|
||||
|
||||
void
|
||||
finishdisk(void)
|
||||
{
|
||||
int r, i;
|
||||
|
||||
for (i = 0; i < blockof(diskpos); ++i)
|
||||
bitmap[i/32] &= ~(1<<(i%32));
|
||||
|
||||
if ((r = msync(diskmap, nblocks * BLKSIZE, MS_SYNC)) < 0)
|
||||
panic("msync: %s", strerror(errno));
|
||||
}
|
||||
|
||||
void
|
||||
finishfile(struct File *f, uint32_t start, uint32_t len)
|
||||
{
|
||||
int i;
|
||||
f->f_size = len;
|
||||
len = ROUNDUP(len, BLKSIZE);
|
||||
for (i = 0; i < len / BLKSIZE && i < NDIRECT; ++i)
|
||||
f->f_direct[i] = start + i;
|
||||
if (i == NDIRECT) {
|
||||
uint32_t *ind = alloc(BLKSIZE);
|
||||
f->f_indirect = blockof(ind);
|
||||
for (; i < len / BLKSIZE; ++i)
|
||||
ind[i - NDIRECT] = start + i;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
startdir(struct File *f, struct Dir *dout)
|
||||
{
|
||||
dout->f = f;
|
||||
dout->ents = malloc(MAX_DIR_ENTS * sizeof *dout->ents);
|
||||
dout->n = 0;
|
||||
}
|
||||
|
||||
struct File *
|
||||
diradd(struct Dir *d, uint32_t type, const char *name)
|
||||
{
|
||||
struct File *out = &d->ents[d->n++];
|
||||
if (d->n > MAX_DIR_ENTS)
|
||||
panic("too many directory entries");
|
||||
strcpy(out->f_name, name);
|
||||
out->f_type = type;
|
||||
return out;
|
||||
}
|
||||
|
||||
void
|
||||
finishdir(struct Dir *d)
|
||||
{
|
||||
int size = d->n * sizeof(struct File);
|
||||
struct File *start = alloc(size);
|
||||
memmove(start, d->ents, size);
|
||||
finishfile(d->f, blockof(start), ROUNDUP(size, BLKSIZE));
|
||||
free(d->ents);
|
||||
d->ents = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
writefile(struct Dir *dir, const char *name)
|
||||
{
|
||||
int r, fd;
|
||||
struct File *f;
|
||||
struct stat st;
|
||||
const char *last;
|
||||
char *start;
|
||||
|
||||
if ((fd = open(name, O_RDONLY)) < 0)
|
||||
panic("open %s: %s", name, strerror(errno));
|
||||
if ((r = fstat(fd, &st)) < 0)
|
||||
panic("stat %s: %s", name, strerror(errno));
|
||||
if (!S_ISREG(st.st_mode))
|
||||
panic("%s is not a regular file", name);
|
||||
if (st.st_size >= MAXFILESIZE)
|
||||
panic("%s too large", name);
|
||||
|
||||
last = strrchr(name, '/');
|
||||
if (last)
|
||||
last++;
|
||||
else
|
||||
last = name;
|
||||
|
||||
f = diradd(dir, FTYPE_REG, last);
|
||||
start = alloc(st.st_size);
|
||||
readn(fd, start, st.st_size);
|
||||
finishfile(f, blockof(start), st.st_size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprintf(stderr, "Usage: fsformat fs.img NBLOCKS files...\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
char *s;
|
||||
struct Dir root;
|
||||
|
||||
assert(BLKSIZE % sizeof(struct File) == 0);
|
||||
|
||||
if (argc < 3)
|
||||
usage();
|
||||
|
||||
nblocks = strtol(argv[2], &s, 0);
|
||||
if (*s || s == argv[2] || nblocks < 2 || nblocks > 1024)
|
||||
usage();
|
||||
|
||||
opendisk(argv[1]);
|
||||
|
||||
startdir(&super->s_root, &root);
|
||||
for (i = 3; i < argc; i++)
|
||||
writefile(&root, argv[i]);
|
||||
finishdir(&root);
|
||||
|
||||
finishdisk();
|
||||
return 0;
|
||||
}
|
||||
|
||||
112
fs/ide.c
Normal file
112
fs/ide.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Minimal PIO-based (non-interrupt-driven) IDE driver code.
|
||||
* For information about what all this IDE/ATA magic means,
|
||||
* see the materials available on the class references page.
|
||||
*/
|
||||
|
||||
#include "fs.h"
|
||||
#include <inc/x86.h>
|
||||
|
||||
#define IDE_BSY 0x80
|
||||
#define IDE_DRDY 0x40
|
||||
#define IDE_DF 0x20
|
||||
#define IDE_ERR 0x01
|
||||
|
||||
static int diskno = 1;
|
||||
|
||||
static int
|
||||
ide_wait_ready(bool check_error)
|
||||
{
|
||||
int r;
|
||||
|
||||
while (((r = inb(0x1F7)) & (IDE_BSY|IDE_DRDY)) != IDE_DRDY)
|
||||
/* do nothing */;
|
||||
|
||||
if (check_error && (r & (IDE_DF|IDE_ERR)) != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
ide_probe_disk1(void)
|
||||
{
|
||||
int r, x;
|
||||
|
||||
// wait for Device 0 to be ready
|
||||
ide_wait_ready(0);
|
||||
|
||||
// switch to Device 1
|
||||
outb(0x1F6, 0xE0 | (1<<4));
|
||||
|
||||
// check for Device 1 to be ready for a while
|
||||
for (x = 0;
|
||||
x < 1000 && ((r = inb(0x1F7)) & (IDE_BSY|IDE_DF|IDE_ERR)) != 0;
|
||||
x++)
|
||||
/* do nothing */;
|
||||
|
||||
// switch back to Device 0
|
||||
outb(0x1F6, 0xE0 | (0<<4));
|
||||
|
||||
cprintf("Device 1 presence: %d\n", (x < 1000));
|
||||
return (x < 1000);
|
||||
}
|
||||
|
||||
void
|
||||
ide_set_disk(int d)
|
||||
{
|
||||
if (d != 0 && d != 1)
|
||||
panic("bad disk number");
|
||||
diskno = d;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ide_read(uint32_t secno, void *dst, size_t nsecs)
|
||||
{
|
||||
int r;
|
||||
|
||||
assert(nsecs <= 256);
|
||||
|
||||
ide_wait_ready(0);
|
||||
|
||||
outb(0x1F2, nsecs);
|
||||
outb(0x1F3, secno & 0xFF);
|
||||
outb(0x1F4, (secno >> 8) & 0xFF);
|
||||
outb(0x1F5, (secno >> 16) & 0xFF);
|
||||
outb(0x1F6, 0xE0 | ((diskno&1)<<4) | ((secno>>24)&0x0F));
|
||||
outb(0x1F7, 0x20); // CMD 0x20 means read sector
|
||||
|
||||
for (; nsecs > 0; nsecs--, dst += SECTSIZE) {
|
||||
if ((r = ide_wait_ready(1)) < 0)
|
||||
return r;
|
||||
insl(0x1F0, dst, SECTSIZE/4);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ide_write(uint32_t secno, const void *src, size_t nsecs)
|
||||
{
|
||||
int r;
|
||||
|
||||
assert(nsecs <= 256);
|
||||
|
||||
ide_wait_ready(0);
|
||||
|
||||
outb(0x1F2, nsecs);
|
||||
outb(0x1F3, secno & 0xFF);
|
||||
outb(0x1F4, (secno >> 8) & 0xFF);
|
||||
outb(0x1F5, (secno >> 16) & 0xFF);
|
||||
outb(0x1F6, 0xE0 | ((diskno&1)<<4) | ((secno>>24)&0x0F));
|
||||
outb(0x1F7, 0x30); // CMD 0x30 means write sector
|
||||
|
||||
for (; nsecs > 0; nsecs--, src += SECTSIZE) {
|
||||
if ((r = ide_wait_ready(1)) < 0)
|
||||
return r;
|
||||
outsl(0x1F0, src, SECTSIZE/4);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
12
fs/lorem
Normal file
12
fs/lorem
Normal file
@@ -0,0 +1,12 @@
|
||||
Lorem ipsum dolor sit amet, consectetur
|
||||
adipisicing elit, sed do eiusmod tempor
|
||||
incididunt ut labore et dolore magna
|
||||
aliqua. Ut enim ad minim veniam, quis
|
||||
nostrud exercitation ullamco laboris
|
||||
nisi ut aliquip ex ea commodo consequat.
|
||||
Duis aute irure dolor in reprehenderit
|
||||
in voluptate velit esse cillum dolore eu
|
||||
fugiat nulla pariatur. Excepteur sint
|
||||
occaecat cupidatat non proident, sunt in
|
||||
culpa qui officia deserunt mollit anim
|
||||
id est laborum.
|
||||
4
fs/motd
Normal file
4
fs/motd
Normal file
@@ -0,0 +1,4 @@
|
||||
This is /motd, the message of the day.
|
||||
|
||||
Welcome to the JOS kernel, now with a file system!
|
||||
|
||||
2
fs/newmotd
Normal file
2
fs/newmotd
Normal file
@@ -0,0 +1,2 @@
|
||||
This is the NEW message of the day!
|
||||
|
||||
5
fs/script
Normal file
5
fs/script
Normal file
@@ -0,0 +1,5 @@
|
||||
echo This is from the script.
|
||||
cat lorem | num | cat
|
||||
echo These are my file descriptors.
|
||||
lsfd -1
|
||||
echo This is the end of the script.
|
||||
345
fs/serv.c
Normal file
345
fs/serv.c
Normal file
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* File system server main loop -
|
||||
* serves IPC requests from other environments.
|
||||
*/
|
||||
|
||||
#include <inc/x86.h>
|
||||
#include <inc/string.h>
|
||||
|
||||
#include "fs.h"
|
||||
|
||||
|
||||
#define debug 0
|
||||
|
||||
// The file system server maintains three structures
|
||||
// for each open file.
|
||||
//
|
||||
// 1. The on-disk 'struct File' is mapped into the part of memory
|
||||
// that maps the disk. This memory is kept private to the file
|
||||
// server.
|
||||
// 2. Each open file has a 'struct Fd' as well, which sort of
|
||||
// corresponds to a Unix file descriptor. This 'struct Fd' is kept
|
||||
// on *its own page* in memory, and it is shared with any
|
||||
// environments that have the file open.
|
||||
// 3. 'struct OpenFile' links these other two structures, and is kept
|
||||
// private to the file server. The server maintains an array of
|
||||
// all open files, indexed by "file ID". (There can be at most
|
||||
// MAXOPEN files open concurrently.) The client uses file IDs to
|
||||
// communicate with the server. File IDs are a lot like
|
||||
// environment IDs in the kernel. Use openfile_lookup to translate
|
||||
// file IDs to struct OpenFile.
|
||||
|
||||
struct OpenFile {
|
||||
uint32_t o_fileid; // file id
|
||||
struct File *o_file; // mapped descriptor for open file
|
||||
int o_mode; // open mode
|
||||
struct Fd *o_fd; // Fd page
|
||||
};
|
||||
|
||||
// Max number of open files in the file system at once
|
||||
#define MAXOPEN 1024
|
||||
#define FILEVA 0xD0000000
|
||||
|
||||
// initialize to force into data section
|
||||
struct OpenFile opentab[MAXOPEN] = {
|
||||
{ 0, 0, 1, 0 }
|
||||
};
|
||||
|
||||
// Virtual address at which to receive page mappings containing client requests.
|
||||
union Fsipc *fsreq = (union Fsipc *)0x0ffff000;
|
||||
|
||||
void
|
||||
serve_init(void)
|
||||
{
|
||||
int i;
|
||||
uintptr_t va = FILEVA;
|
||||
for (i = 0; i < MAXOPEN; i++) {
|
||||
opentab[i].o_fileid = i;
|
||||
opentab[i].o_fd = (struct Fd*) va;
|
||||
va += PGSIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate an open file.
|
||||
int
|
||||
openfile_alloc(struct OpenFile **o)
|
||||
{
|
||||
int i, r;
|
||||
|
||||
// Find an available open-file table entry
|
||||
for (i = 0; i < MAXOPEN; i++) {
|
||||
switch (pageref(opentab[i].o_fd)) {
|
||||
case 0:
|
||||
if ((r = sys_page_alloc(0, opentab[i].o_fd, PTE_P|PTE_U|PTE_W)) < 0)
|
||||
return r;
|
||||
/* fall through */
|
||||
case 1:
|
||||
opentab[i].o_fileid += MAXOPEN;
|
||||
*o = &opentab[i];
|
||||
memset(opentab[i].o_fd, 0, PGSIZE);
|
||||
return (*o)->o_fileid;
|
||||
}
|
||||
}
|
||||
return -E_MAX_OPEN;
|
||||
}
|
||||
|
||||
// Look up an open file for envid.
|
||||
int
|
||||
openfile_lookup(envid_t envid, uint32_t fileid, struct OpenFile **po)
|
||||
{
|
||||
struct OpenFile *o;
|
||||
|
||||
o = &opentab[fileid % MAXOPEN];
|
||||
if (pageref(o->o_fd) <= 1 || o->o_fileid != fileid)
|
||||
return -E_INVAL;
|
||||
*po = o;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Open req->req_path in mode req->req_omode, storing the Fd page and
|
||||
// permissions to return to the calling environment in *pg_store and
|
||||
// *perm_store respectively.
|
||||
int
|
||||
serve_open(envid_t envid, struct Fsreq_open *req,
|
||||
void **pg_store, int *perm_store)
|
||||
{
|
||||
char path[MAXPATHLEN];
|
||||
struct File *f;
|
||||
int fileid;
|
||||
int r;
|
||||
struct OpenFile *o;
|
||||
|
||||
if (debug)
|
||||
cprintf("serve_open %08x %s 0x%x\n", envid, req->req_path, req->req_omode);
|
||||
|
||||
// Copy in the path, making sure it's null-terminated
|
||||
memmove(path, req->req_path, MAXPATHLEN);
|
||||
path[MAXPATHLEN-1] = 0;
|
||||
|
||||
// Find an open file ID
|
||||
if ((r = openfile_alloc(&o)) < 0) {
|
||||
if (debug)
|
||||
cprintf("openfile_alloc failed: %e", r);
|
||||
return r;
|
||||
}
|
||||
fileid = r;
|
||||
|
||||
// Open the file
|
||||
if (req->req_omode & O_CREAT) {
|
||||
if ((r = file_create(path, &f)) < 0) {
|
||||
if (!(req->req_omode & O_EXCL) && r == -E_FILE_EXISTS)
|
||||
goto try_open;
|
||||
if (debug)
|
||||
cprintf("file_create failed: %e", r);
|
||||
return r;
|
||||
}
|
||||
} else {
|
||||
try_open:
|
||||
if ((r = file_open(path, &f)) < 0) {
|
||||
if (debug)
|
||||
cprintf("file_open failed: %e", r);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
// Truncate
|
||||
if (req->req_omode & O_TRUNC) {
|
||||
if ((r = file_set_size(f, 0)) < 0) {
|
||||
if (debug)
|
||||
cprintf("file_set_size failed: %e", r);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
if ((r = file_open(path, &f)) < 0) {
|
||||
if (debug)
|
||||
cprintf("file_open failed: %e", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
// Save the file pointer
|
||||
o->o_file = f;
|
||||
|
||||
// Fill out the Fd structure
|
||||
o->o_fd->fd_file.id = o->o_fileid;
|
||||
o->o_fd->fd_omode = req->req_omode & O_ACCMODE;
|
||||
o->o_fd->fd_dev_id = devfile.dev_id;
|
||||
o->o_mode = req->req_omode;
|
||||
|
||||
if (debug)
|
||||
cprintf("sending success, page %08x\n", (uintptr_t) o->o_fd);
|
||||
|
||||
// Share the FD page with the caller by setting *pg_store,
|
||||
// store its permission in *perm_store
|
||||
*pg_store = o->o_fd;
|
||||
*perm_store = PTE_P|PTE_U|PTE_W|PTE_SHARE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set the size of req->req_fileid to req->req_size bytes, truncating
|
||||
// or extending the file as necessary.
|
||||
int
|
||||
serve_set_size(envid_t envid, struct Fsreq_set_size *req)
|
||||
{
|
||||
struct OpenFile *o;
|
||||
int r;
|
||||
|
||||
if (debug)
|
||||
cprintf("serve_set_size %08x %08x %08x\n", envid, req->req_fileid, req->req_size);
|
||||
|
||||
// Every file system IPC call has the same general structure.
|
||||
// Here's how it goes.
|
||||
|
||||
// First, use openfile_lookup to find the relevant open file.
|
||||
// On failure, return the error code to the client with ipc_send.
|
||||
if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0)
|
||||
return r;
|
||||
|
||||
// Second, call the relevant file system function (from fs/fs.c).
|
||||
// On failure, return the error code to the client.
|
||||
return file_set_size(o->o_file, req->req_size);
|
||||
}
|
||||
|
||||
// Read at most ipc->read.req_n bytes from the current seek position
|
||||
// in ipc->read.req_fileid. Return the bytes read from the file to
|
||||
// the caller in ipc->readRet, then update the seek position. Returns
|
||||
// the number of bytes successfully read, or < 0 on error.
|
||||
int
|
||||
serve_read(envid_t envid, union Fsipc *ipc)
|
||||
{
|
||||
struct Fsreq_read *req = &ipc->read;
|
||||
struct Fsret_read *ret = &ipc->readRet;
|
||||
|
||||
if (debug)
|
||||
cprintf("serve_read %08x %08x %08x\n", envid, req->req_fileid, req->req_n);
|
||||
|
||||
// Lab 5: Your code here:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Write req->req_n bytes from req->req_buf to req_fileid, starting at
|
||||
// the current seek position, and update the seek position
|
||||
// accordingly. Extend the file if necessary. Returns the number of
|
||||
// bytes written, or < 0 on error.
|
||||
int
|
||||
serve_write(envid_t envid, struct Fsreq_write *req)
|
||||
{
|
||||
if (debug)
|
||||
cprintf("serve_write %08x %08x %08x\n", envid, req->req_fileid, req->req_n);
|
||||
|
||||
// LAB 5: Your code here.
|
||||
panic("serve_write not implemented");
|
||||
}
|
||||
|
||||
// Stat ipc->stat.req_fileid. Return the file's struct Stat to the
|
||||
// caller in ipc->statRet.
|
||||
int
|
||||
serve_stat(envid_t envid, union Fsipc *ipc)
|
||||
{
|
||||
struct Fsreq_stat *req = &ipc->stat;
|
||||
struct Fsret_stat *ret = &ipc->statRet;
|
||||
struct OpenFile *o;
|
||||
int r;
|
||||
|
||||
if (debug)
|
||||
cprintf("serve_stat %08x %08x\n", envid, req->req_fileid);
|
||||
|
||||
if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0)
|
||||
return r;
|
||||
|
||||
strcpy(ret->ret_name, o->o_file->f_name);
|
||||
ret->ret_size = o->o_file->f_size;
|
||||
ret->ret_isdir = (o->o_file->f_type == FTYPE_DIR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Flush all data and metadata of req->req_fileid to disk.
|
||||
int
|
||||
serve_flush(envid_t envid, struct Fsreq_flush *req)
|
||||
{
|
||||
struct OpenFile *o;
|
||||
int r;
|
||||
|
||||
if (debug)
|
||||
cprintf("serve_flush %08x %08x\n", envid, req->req_fileid);
|
||||
|
||||
if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0)
|
||||
return r;
|
||||
file_flush(o->o_file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
serve_sync(envid_t envid, union Fsipc *req)
|
||||
{
|
||||
fs_sync();
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int (*fshandler)(envid_t envid, union Fsipc *req);
|
||||
|
||||
fshandler handlers[] = {
|
||||
// Open is handled specially because it passes pages
|
||||
/* [FSREQ_OPEN] = (fshandler)serve_open, */
|
||||
[FSREQ_READ] = serve_read,
|
||||
[FSREQ_STAT] = serve_stat,
|
||||
[FSREQ_FLUSH] = (fshandler)serve_flush,
|
||||
[FSREQ_WRITE] = (fshandler)serve_write,
|
||||
[FSREQ_SET_SIZE] = (fshandler)serve_set_size,
|
||||
[FSREQ_SYNC] = serve_sync
|
||||
};
|
||||
|
||||
void
|
||||
serve(void)
|
||||
{
|
||||
uint32_t req, whom;
|
||||
int perm, r;
|
||||
void *pg;
|
||||
|
||||
while (1) {
|
||||
perm = 0;
|
||||
req = ipc_recv((int32_t *) &whom, fsreq, &perm);
|
||||
if (debug)
|
||||
cprintf("fs req %d from %08x [page %08x: %s]\n",
|
||||
req, whom, uvpt[PGNUM(fsreq)], fsreq);
|
||||
|
||||
// All requests must contain an argument page
|
||||
if (!(perm & PTE_P)) {
|
||||
cprintf("Invalid request from %08x: no argument page\n",
|
||||
whom);
|
||||
continue; // just leave it hanging...
|
||||
}
|
||||
|
||||
pg = NULL;
|
||||
if (req == FSREQ_OPEN) {
|
||||
r = serve_open(whom, (struct Fsreq_open*)fsreq, &pg, &perm);
|
||||
} else if (req < ARRAY_SIZE(handlers) && handlers[req]) {
|
||||
r = handlers[req](whom, fsreq);
|
||||
} else {
|
||||
cprintf("Invalid request code %d from %08x\n", req, whom);
|
||||
r = -E_INVAL;
|
||||
}
|
||||
ipc_send(whom, r, pg, perm);
|
||||
sys_page_unmap(0, fsreq);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
static_assert(sizeof(struct File) == 256);
|
||||
binaryname = "fs";
|
||||
cprintf("FS is running\n");
|
||||
|
||||
// Check that we are able to do I/O
|
||||
outw(0x8A00, 0x8A00);
|
||||
cprintf("FS can do I/O\n");
|
||||
|
||||
serve_init();
|
||||
fs_init();
|
||||
fs_test();
|
||||
serve();
|
||||
}
|
||||
|
||||
7
fs/testshell.sh
Normal file
7
fs/testshell.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
echo hello world | cat
|
||||
cat lorem
|
||||
cat lorem |num
|
||||
cat lorem |num |num |num |num |num
|
||||
lsfd -1
|
||||
cat script
|
||||
sh <script
|
||||
Reference in New Issue
Block a user