This commit is contained in:
Anish Athalye
2018-10-24 20:44:45 -04:00
parent da1f8392b1
commit c67463e23c
71 changed files with 4734 additions and 18 deletions

77
fs/Makefrag Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,2 @@
This is the NEW message of the day!

5
fs/script Normal file
View 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
View 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
View 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