Lab 5
This commit is contained in:
parent
da1f8392b1
commit
c67463e23c
@ -141,6 +141,7 @@ include boot/Makefrag
|
||||
include kern/Makefrag
|
||||
include lib/Makefrag
|
||||
include user/Makefrag
|
||||
include fs/Makefrag
|
||||
|
||||
|
||||
CPUS ?= 1
|
||||
@ -149,6 +150,8 @@ QEMUOPTS = -drive file=$(OBJDIR)/kern/kernel.img,index=0,media=disk,format=raw -
|
||||
QEMUOPTS += $(shell if $(QEMU) -nographic -help | grep -q '^-D '; then echo '-D qemu.log'; fi)
|
||||
IMAGES = $(OBJDIR)/kern/kernel.img
|
||||
QEMUOPTS += -smp $(CPUS)
|
||||
QEMUOPTS += -drive file=$(OBJDIR)/fs/fs.img,index=1,media=disk,format=raw
|
||||
IMAGES += $(OBJDIR)/fs/fs.img
|
||||
QEMUOPTS += $(QEMUEXTRA)
|
||||
|
||||
.gdbinit: .gdbinit.tmpl
|
||||
|
@ -1,2 +1,2 @@
|
||||
LAB=4
|
||||
PACKAGEDATE=Mon Oct 8 21:31:51 PDT 2018
|
||||
LAB=5
|
||||
PACKAGEDATE=Wed Oct 24 20:44:37 EDT 2018
|
||||
|
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
|
112
grade-lab5
Executable file
112
grade-lab5
Executable file
@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from gradelib import *
|
||||
|
||||
r = Runner(save("jos.out"),
|
||||
stop_breakpoint("readline"))
|
||||
|
||||
def matchtest(parent, name, *args, **kw):
|
||||
def do_test():
|
||||
r.match(*args, **kw)
|
||||
test(5, name, parent=parent)(do_test)
|
||||
|
||||
@test(5, "internal FS tests [fs/test.c]")
|
||||
def test_fs():
|
||||
r.user_test("hello")
|
||||
matchtest(test_fs, "fs i/o",
|
||||
"FS can do I/O")
|
||||
matchtest(test_fs, "check_bc",
|
||||
"block cache is good")
|
||||
matchtest(test_fs, "check_super",
|
||||
"superblock is good")
|
||||
matchtest(test_fs, "check_bitmap",
|
||||
"bitmap is good")
|
||||
matchtest(test_fs, "alloc_block",
|
||||
"alloc_block is good")
|
||||
matchtest(test_fs, "file_open",
|
||||
"file_open is good")
|
||||
matchtest(test_fs, "file_get_block",
|
||||
"file_get_block is good")
|
||||
matchtest(test_fs, "file_flush/file_truncate/file rewrite",
|
||||
"file_flush is good",
|
||||
"file_truncate is good",
|
||||
"file rewrite is good")
|
||||
|
||||
@test(10, "testfile")
|
||||
def test_testfile():
|
||||
r.user_test("testfile")
|
||||
matchtest(test_testfile, "serve_open/file_stat/file_close",
|
||||
"serve_open is good",
|
||||
"file_stat is good",
|
||||
"file_close is good",
|
||||
"stale fileid is good")
|
||||
matchtest(test_testfile, "file_read",
|
||||
"file_read is good")
|
||||
matchtest(test_testfile, "file_write",
|
||||
"file_write is good")
|
||||
matchtest(test_testfile, "file_read after file_write",
|
||||
"file_read after file_write is good")
|
||||
matchtest(test_testfile, "open",
|
||||
"open is good")
|
||||
matchtest(test_testfile, "large file",
|
||||
"large file is good")
|
||||
|
||||
@test(10, "spawn via spawnhello")
|
||||
def test_spawn():
|
||||
r.user_test("spawnhello")
|
||||
r.match('i am parent environment 00001001',
|
||||
'hello, world',
|
||||
'i am environment 00001002',
|
||||
'No runnable environments in the system!')
|
||||
|
||||
@test(5, "Protection I/O space")
|
||||
def test_faultio():
|
||||
r.user_test("spawnfaultio")
|
||||
r.match('TRAP')
|
||||
|
||||
@test(10, "PTE_SHARE [testpteshare]")
|
||||
def test_pte_share():
|
||||
r.user_test("testpteshare")
|
||||
r.match('fork handles PTE_SHARE right',
|
||||
'spawn handles PTE_SHARE right')
|
||||
|
||||
@test(5, "PTE_SHARE [testfdsharing]")
|
||||
def test_fd_share():
|
||||
r.user_test("testfdsharing")
|
||||
r.match('read in child succeeded',
|
||||
'read in parent succeeded')
|
||||
|
||||
@test(10, "start the shell [icode]")
|
||||
def test_icode():
|
||||
r.user_test("icode")
|
||||
r.match('icode: read /motd',
|
||||
'This is /motd, the message of the day.',
|
||||
'icode: spawn /init',
|
||||
'init: running',
|
||||
'init: data seems okay',
|
||||
'icode: exiting',
|
||||
'init: bss seems okay',
|
||||
"init: args: 'init' 'initarg1' 'initarg2'",
|
||||
'init: running sh',
|
||||
'\$ ')
|
||||
|
||||
@test(15)
|
||||
def test_testshell():
|
||||
r.user_test("testshell", timeout=60)
|
||||
r.match("shell ran correctly")
|
||||
|
||||
def gen_primes(n):
|
||||
rest = range(2, n)
|
||||
while rest:
|
||||
yield rest[0]
|
||||
rest = [n for n in rest if n % rest[0]]
|
||||
|
||||
@test(10)
|
||||
def test_primespipe():
|
||||
r.user_test("primespipe", stop_on_line("[0-9]{4}$"), timeout=120)
|
||||
primes = set(gen_primes(1000))
|
||||
nonprimes = set(range(1000)) - primes
|
||||
r.match(no=["%d$" % np for np in nonprimes],
|
||||
*["%d$" % p for p in primes])
|
||||
|
||||
run_tests()
|
82
inc/args.h
Normal file
82
inc/args.h
Normal file
@ -0,0 +1,82 @@
|
||||
#ifndef JOS_INC_ARGS_H
|
||||
#define JOS_INC_ARGS_H
|
||||
|
||||
struct Argstate;
|
||||
|
||||
// JOS command-line parsing functions.
|
||||
|
||||
// Initializes the Argstate buffer from argc and argv.
|
||||
// (Note: it is safe to use a 'const char **' for argv.)
|
||||
void argstart(int *argc, char **argv, struct Argstate *args);
|
||||
|
||||
// Returns the next flag in the argument list,
|
||||
// or -1 if there are no more flags.
|
||||
//
|
||||
// Flags stop at a non-flag (anything that doesn't start with '-'),
|
||||
// at the end of the argument list, before "-", or after "--",
|
||||
// whichever comes first. Any "--" argument is not returned.
|
||||
//
|
||||
// Consumes arguments from the argc/argv array passed in to argstart.
|
||||
// If you argstart with an argc/argv array of ["sh", "-i", "foo"],
|
||||
// the first call to argnext will return 'i' and change the array to
|
||||
// ["sh", "foo"]. Thus, when argnext returns -1, the argc/argv array
|
||||
// contains just the non-flag arguments.
|
||||
int argnext(struct Argstate *);
|
||||
|
||||
// Returns the next value for the current flag, or 0 if it has no value.
|
||||
// For example, given an argument list ["-fval1", "val2", "val3"],
|
||||
// a call to argnext() will return 'f', after which repeated calls to
|
||||
// argnextvalue will return "val1", "val2", and "val3".
|
||||
// Consumes arguments from the argc/argv array.
|
||||
char *argnextvalue(struct Argstate *);
|
||||
|
||||
// Returns the current flag's value, or 0 if it has no value.
|
||||
// Behaves like argnextvalue, except that repeated calls to argvalue will
|
||||
// return the same value.
|
||||
char *argvalue(struct Argstate *);
|
||||
|
||||
// Example:
|
||||
//
|
||||
// #include <inc/lib.h>
|
||||
//
|
||||
// void
|
||||
// umain(int argc, char **argv)
|
||||
// {
|
||||
// int i;
|
||||
// struct Argstate args;
|
||||
//
|
||||
// argstart(&argc, argv, &args);
|
||||
// while ((i = argnext(&args)) >= 0)
|
||||
// switch (i) {
|
||||
// case 'r':
|
||||
// case 'x':
|
||||
// cprintf("'-%c' flag\n", i);
|
||||
// break;
|
||||
// case 'f':
|
||||
// cprintf("'-f %s' flag\n", argvalue(&args));
|
||||
// break;
|
||||
// default:
|
||||
// cprintf("unknown flag\n");
|
||||
// }
|
||||
//
|
||||
// for (i = 1; i < argc; i++)
|
||||
// cprintf("argument '%s'\n", argv[i]);
|
||||
// }
|
||||
//
|
||||
// If this program is run with the arguments
|
||||
// ["-rx", "-f", "foo", "--", "-r", "duh"]
|
||||
// it will print out
|
||||
// '-r' flag
|
||||
// '-x' flag
|
||||
// '-f foo' flag
|
||||
// argument '-r'
|
||||
// argument 'duh'
|
||||
|
||||
struct Argstate {
|
||||
int *argc;
|
||||
const char **argv;
|
||||
const char *curarg;
|
||||
const char *argvalue;
|
||||
};
|
||||
|
||||
#endif
|
@ -41,6 +41,7 @@ enum {
|
||||
// Special environment types
|
||||
enum EnvType {
|
||||
ENV_TYPE_USER = 0,
|
||||
ENV_TYPE_FS, // File system server
|
||||
};
|
||||
|
||||
struct Env {
|
||||
|
@ -17,6 +17,15 @@ enum {
|
||||
E_IPC_NOT_RECV , // Attempt to send to env that is not recving
|
||||
E_EOF , // Unexpected end of file
|
||||
|
||||
// File system error codes -- only seen in user-level
|
||||
E_NO_DISK , // No free space left on disk
|
||||
E_MAX_OPEN , // Too many files are open
|
||||
E_NOT_FOUND , // File or block not found
|
||||
E_BAD_PATH , // Bad path
|
||||
E_FILE_EXISTS , // File already exists
|
||||
E_NOT_EXEC , // File not a valid executable
|
||||
E_NOT_SUPP , // Operation not supported
|
||||
|
||||
MAXERROR
|
||||
};
|
||||
|
||||
|
58
inc/fd.h
Normal file
58
inc/fd.h
Normal file
@ -0,0 +1,58 @@
|
||||
// Public definitions for the POSIX-like file descriptor emulation layer
|
||||
// that our user-land support library implements for the use of applications.
|
||||
// See the code in the lib directory for the implementation details.
|
||||
|
||||
#ifndef JOS_INC_FD_H
|
||||
#define JOS_INC_FD_H
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/fs.h>
|
||||
|
||||
struct Fd;
|
||||
struct Stat;
|
||||
struct Dev;
|
||||
|
||||
// Per-device-class file descriptor operations
|
||||
struct Dev {
|
||||
int dev_id;
|
||||
const char *dev_name;
|
||||
ssize_t (*dev_read)(struct Fd *fd, void *buf, size_t len);
|
||||
ssize_t (*dev_write)(struct Fd *fd, const void *buf, size_t len);
|
||||
int (*dev_close)(struct Fd *fd);
|
||||
int (*dev_stat)(struct Fd *fd, struct Stat *stat);
|
||||
int (*dev_trunc)(struct Fd *fd, off_t length);
|
||||
};
|
||||
|
||||
struct FdFile {
|
||||
int id;
|
||||
};
|
||||
|
||||
struct Fd {
|
||||
int fd_dev_id;
|
||||
off_t fd_offset;
|
||||
int fd_omode;
|
||||
union {
|
||||
// File server files
|
||||
struct FdFile fd_file;
|
||||
};
|
||||
};
|
||||
|
||||
struct Stat {
|
||||
char st_name[MAXNAMELEN];
|
||||
off_t st_size;
|
||||
int st_isdir;
|
||||
struct Dev *st_dev;
|
||||
};
|
||||
|
||||
char* fd2data(struct Fd *fd);
|
||||
int fd2num(struct Fd *fd);
|
||||
int fd_alloc(struct Fd **fd_store);
|
||||
int fd_close(struct Fd *fd, bool must_exist);
|
||||
int fd_lookup(int fdnum, struct Fd **fd_store);
|
||||
int dev_lookup(int devid, struct Dev **dev_store);
|
||||
|
||||
extern struct Dev devfile;
|
||||
extern struct Dev devcons;
|
||||
extern struct Dev devpipe;
|
||||
|
||||
#endif // not JOS_INC_FD_H
|
116
inc/fs.h
Normal file
116
inc/fs.h
Normal file
@ -0,0 +1,116 @@
|
||||
// See COPYRIGHT for copyright information.
|
||||
|
||||
#ifndef JOS_INC_FS_H
|
||||
#define JOS_INC_FS_H
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/mmu.h>
|
||||
|
||||
// File nodes (both in-memory and on-disk)
|
||||
|
||||
// Bytes per file system block - same as page size
|
||||
#define BLKSIZE PGSIZE
|
||||
#define BLKBITSIZE (BLKSIZE * 8)
|
||||
|
||||
// Maximum size of a filename (a single path component), including null
|
||||
// Must be a multiple of 4
|
||||
#define MAXNAMELEN 128
|
||||
|
||||
// Maximum size of a complete pathname, including null
|
||||
#define MAXPATHLEN 1024
|
||||
|
||||
// Number of block pointers in a File descriptor
|
||||
#define NDIRECT 10
|
||||
// Number of direct block pointers in an indirect block
|
||||
#define NINDIRECT (BLKSIZE / 4)
|
||||
|
||||
#define MAXFILESIZE ((NDIRECT + NINDIRECT) * BLKSIZE)
|
||||
|
||||
struct File {
|
||||
char f_name[MAXNAMELEN]; // filename
|
||||
off_t f_size; // file size in bytes
|
||||
uint32_t f_type; // file type
|
||||
|
||||
// Block pointers.
|
||||
// A block is allocated iff its value is != 0.
|
||||
uint32_t f_direct[NDIRECT]; // direct blocks
|
||||
uint32_t f_indirect; // indirect block
|
||||
|
||||
// Pad out to 256 bytes; must do arithmetic in case we're compiling
|
||||
// fsformat on a 64-bit machine.
|
||||
uint8_t f_pad[256 - MAXNAMELEN - 8 - 4*NDIRECT - 4];
|
||||
} __attribute__((packed)); // required only on some 64-bit machines
|
||||
|
||||
// An inode block contains exactly BLKFILES 'struct File's
|
||||
#define BLKFILES (BLKSIZE / sizeof(struct File))
|
||||
|
||||
// File types
|
||||
#define FTYPE_REG 0 // Regular file
|
||||
#define FTYPE_DIR 1 // Directory
|
||||
|
||||
|
||||
// File system super-block (both in-memory and on-disk)
|
||||
|
||||
#define FS_MAGIC 0x4A0530AE // related vaguely to 'J\0S!'
|
||||
|
||||
struct Super {
|
||||
uint32_t s_magic; // Magic number: FS_MAGIC
|
||||
uint32_t s_nblocks; // Total number of blocks on disk
|
||||
struct File s_root; // Root directory node
|
||||
};
|
||||
|
||||
// Definitions for requests from clients to file system
|
||||
enum {
|
||||
FSREQ_OPEN = 1,
|
||||
FSREQ_SET_SIZE,
|
||||
// Read returns a Fsret_read on the request page
|
||||
FSREQ_READ,
|
||||
FSREQ_WRITE,
|
||||
// Stat returns a Fsret_stat on the request page
|
||||
FSREQ_STAT,
|
||||
FSREQ_FLUSH,
|
||||
FSREQ_REMOVE,
|
||||
FSREQ_SYNC
|
||||
};
|
||||
|
||||
union Fsipc {
|
||||
struct Fsreq_open {
|
||||
char req_path[MAXPATHLEN];
|
||||
int req_omode;
|
||||
} open;
|
||||
struct Fsreq_set_size {
|
||||
int req_fileid;
|
||||
off_t req_size;
|
||||
} set_size;
|
||||
struct Fsreq_read {
|
||||
int req_fileid;
|
||||
size_t req_n;
|
||||
} read;
|
||||
struct Fsret_read {
|
||||
char ret_buf[PGSIZE];
|
||||
} readRet;
|
||||
struct Fsreq_write {
|
||||
int req_fileid;
|
||||
size_t req_n;
|
||||
char req_buf[PGSIZE - (sizeof(int) + sizeof(size_t))];
|
||||
} write;
|
||||
struct Fsreq_stat {
|
||||
int req_fileid;
|
||||
} stat;
|
||||
struct Fsret_stat {
|
||||
char ret_name[MAXNAMELEN];
|
||||
off_t ret_size;
|
||||
int ret_isdir;
|
||||
} statRet;
|
||||
struct Fsreq_flush {
|
||||
int req_fileid;
|
||||
} flush;
|
||||
struct Fsreq_remove {
|
||||
char req_path[MAXPATHLEN];
|
||||
} remove;
|
||||
|
||||
// Ensure Fsipc is one page
|
||||
char _pad[PGSIZE];
|
||||
};
|
||||
|
||||
#endif /* !JOS_INC_FS_H */
|
40
inc/lib.h
40
inc/lib.h
@ -17,6 +17,9 @@
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/syscall.h>
|
||||
#include <inc/trap.h>
|
||||
#include <inc/fs.h>
|
||||
#include <inc/fd.h>
|
||||
#include <inc/args.h>
|
||||
|
||||
#define USED(x) (void)(x)
|
||||
|
||||
@ -46,6 +49,7 @@ int sys_env_destroy(envid_t);
|
||||
void sys_yield(void);
|
||||
static envid_t sys_exofork(void);
|
||||
int sys_env_set_status(envid_t env, int status);
|
||||
int sys_env_set_trapframe(envid_t env, struct Trapframe *tf);
|
||||
int sys_env_set_pgfault_upcall(envid_t env, void *upcall);
|
||||
int sys_page_alloc(envid_t env, void *pg, int perm);
|
||||
int sys_page_map(envid_t src_env, void *src_pg,
|
||||
@ -75,7 +79,43 @@ envid_t ipc_find_env(enum EnvType type);
|
||||
envid_t fork(void);
|
||||
envid_t sfork(void); // Challenge!
|
||||
|
||||
// fd.c
|
||||
int close(int fd);
|
||||
ssize_t read(int fd, void *buf, size_t nbytes);
|
||||
ssize_t write(int fd, const void *buf, size_t nbytes);
|
||||
int seek(int fd, off_t offset);
|
||||
void close_all(void);
|
||||
ssize_t readn(int fd, void *buf, size_t nbytes);
|
||||
int dup(int oldfd, int newfd);
|
||||
int fstat(int fd, struct Stat *statbuf);
|
||||
int stat(const char *path, struct Stat *statbuf);
|
||||
|
||||
// file.c
|
||||
int open(const char *path, int mode);
|
||||
int ftruncate(int fd, off_t size);
|
||||
int remove(const char *path);
|
||||
int sync(void);
|
||||
|
||||
// pageref.c
|
||||
int pageref(void *addr);
|
||||
|
||||
|
||||
// spawn.c
|
||||
envid_t spawn(const char *program, const char **argv);
|
||||
envid_t spawnl(const char *program, const char *arg0, ...);
|
||||
|
||||
// console.c
|
||||
void cputchar(int c);
|
||||
int getchar(void);
|
||||
int iscons(int fd);
|
||||
int opencons(void);
|
||||
|
||||
// pipe.c
|
||||
int pipe(int pipefds[2]);
|
||||
int pipeisclosed(int pipefd);
|
||||
|
||||
// wait.c
|
||||
void wait(envid_t env);
|
||||
|
||||
/* File open modes */
|
||||
#define O_RDONLY 0x0000 /* open for reading only */
|
||||
|
33
inc/partition.h
Normal file
33
inc/partition.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef JOS_INC_PARTITION_H
|
||||
#define JOS_INC_PARTITION_H
|
||||
#include <inc/types.h>
|
||||
|
||||
/*
|
||||
* This file contains definitions for x86 partition tables, and comes from
|
||||
* Mike Mammarella.
|
||||
*/
|
||||
|
||||
// Offset of 1st partition descriptor in a partition sector
|
||||
#define PTABLE_OFFSET 446
|
||||
// 2-byte partition table magic number location and value
|
||||
#define PTABLE_MAGIC_OFFSET 510
|
||||
#define PTABLE_MAGIC "\x55\xAA"
|
||||
|
||||
// Partition type constants
|
||||
#define PTYPE_JOS_KERN 0x27 // JOS kernel
|
||||
#define PTYPE_JOSFS 0x28 // JOS file system
|
||||
// Extended partition identifiers
|
||||
#define PTYPE_DOS_EXTENDED 0x05
|
||||
#define PTYPE_W95_EXTENDED 0x0F
|
||||
#define PTYPE_LINUX_EXTENDED 0x85
|
||||
|
||||
struct Partitiondesc {
|
||||
uint8_t boot;
|
||||
uint8_t chs_begin[3];
|
||||
uint8_t type;
|
||||
uint8_t chs_end[3];
|
||||
uint32_t lba_start;
|
||||
uint32_t lba_length;
|
||||
};
|
||||
|
||||
#endif
|
@ -12,6 +12,7 @@ enum {
|
||||
SYS_page_unmap,
|
||||
SYS_exofork,
|
||||
SYS_env_set_status,
|
||||
SYS_env_set_trapframe,
|
||||
SYS_env_set_pgfault_upcall,
|
||||
SYS_yield,
|
||||
SYS_ipc_try_send,
|
||||
|
@ -76,6 +76,24 @@ KERN_BINFILES += user/idle \
|
||||
user/pingpong \
|
||||
user/pingpongs \
|
||||
user/primes
|
||||
# Binary files for LAB5
|
||||
KERN_BINFILES += user/faultio\
|
||||
user/spawnfaultio\
|
||||
user/testfile \
|
||||
user/spawnhello \
|
||||
user/icode \
|
||||
fs/fs
|
||||
|
||||
# Binary files for LAB5
|
||||
KERN_BINFILES += user/testpteshare \
|
||||
user/testfdsharing \
|
||||
user/testpipe \
|
||||
user/testpiperace \
|
||||
user/testpiperace2 \
|
||||
user/primespipe \
|
||||
user/testkbd \
|
||||
user/testshell
|
||||
|
||||
KERN_OBJFILES := $(patsubst %.c, $(OBJDIR)/%.o, $(KERN_SRCFILES))
|
||||
KERN_OBJFILES := $(patsubst %.S, $(OBJDIR)/%.o, $(KERN_OBJFILES))
|
||||
KERN_OBJFILES := $(patsubst $(OBJDIR)/lib/%, $(OBJDIR)/kern/%, $(KERN_OBJFILES))
|
||||
|
@ -102,6 +102,9 @@ serial_init(void)
|
||||
(void) inb(COM1+COM_IIR);
|
||||
(void) inb(COM1+COM_RX);
|
||||
|
||||
// Enable serial interrupts
|
||||
if (serial_exists)
|
||||
irq_setmask_8259A(irq_mask_8259A & ~(1<<IRQ_SERIAL));
|
||||
}
|
||||
|
||||
|
||||
|
@ -258,7 +258,7 @@ env_alloc(struct Env **newenv_store, envid_t parent_id)
|
||||
env_free_list = e->env_link;
|
||||
*newenv_store = e;
|
||||
|
||||
cprintf("[%08x] new env %08x\n", curenv ? curenv->env_id : 0, e->env_id);
|
||||
// cprintf("[%08x] new env %08x\n", curenv ? curenv->env_id : 0, e->env_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -353,6 +353,9 @@ void
|
||||
env_create(uint8_t *binary, enum EnvType type)
|
||||
{
|
||||
// LAB 3: Your code here.
|
||||
|
||||
// If this is the file server (type == ENV_TYPE_FS) give it I/O privileges.
|
||||
// LAB 5: Your code here.
|
||||
}
|
||||
|
||||
//
|
||||
@ -372,7 +375,7 @@ env_free(struct Env *e)
|
||||
lcr3(PADDR(kern_pgdir));
|
||||
|
||||
// Note the environment's demise.
|
||||
cprintf("[%08x] free env %08x\n", curenv ? curenv->env_id : 0, e->env_id);
|
||||
// cprintf("[%08x] free env %08x\n", curenv ? curenv->env_id : 0, e->env_id);
|
||||
|
||||
// Flush all mapped pages in the user portion of the address space
|
||||
static_assert(UTOP % PTSIZE == 0);
|
||||
|
@ -47,14 +47,20 @@ i386_init(void)
|
||||
// Starting non-boot CPUs
|
||||
boot_aps();
|
||||
|
||||
// Start fs.
|
||||
ENV_CREATE(fs_fs, ENV_TYPE_FS);
|
||||
|
||||
#if defined(TEST)
|
||||
// Don't touch -- used by grading script!
|
||||
ENV_CREATE(TEST, ENV_TYPE_USER);
|
||||
#else
|
||||
// Touch all you want.
|
||||
ENV_CREATE(user_primes, ENV_TYPE_USER);
|
||||
ENV_CREATE(user_icode, ENV_TYPE_USER);
|
||||
#endif // TEST*
|
||||
|
||||
// Should not be necessary - drains keyboard because interrupt has given up.
|
||||
kbd_intr();
|
||||
|
||||
// Schedule and run the first user environment!
|
||||
sched_yield();
|
||||
}
|
||||
|
@ -84,6 +84,8 @@ static void check_page_installed_pgdir(void);
|
||||
// If we're out of memory, boot_alloc should panic.
|
||||
// This function may ONLY be used during initialization,
|
||||
// before the page_free_list list has been set up.
|
||||
// Note that when this function is called, we are still using entry_pgdir,
|
||||
// which only maps the first 4MB of physical memory.
|
||||
static void *
|
||||
boot_alloc(uint32_t n)
|
||||
{
|
||||
|
@ -21,7 +21,8 @@ sched_yield(void)
|
||||
//
|
||||
// If no envs are runnable, but the environment previously
|
||||
// running on this CPU is still ENV_RUNNING, it's okay to
|
||||
// choose that environment.
|
||||
// choose that environment. Make sure curenv is not null before
|
||||
// dereferencing it.
|
||||
//
|
||||
// Never choose an environment that's currently running on
|
||||
// another CPU (env_status == ENV_RUNNING). If there are
|
||||
|
@ -55,10 +55,6 @@ sys_env_destroy(envid_t envid)
|
||||
|
||||
if ((r = envid2env(envid, &e, 1)) < 0)
|
||||
return r;
|
||||
if (e == curenv)
|
||||
cprintf("[%08x] exiting gracefully\n", curenv->env_id);
|
||||
else
|
||||
cprintf("[%08x] destroying %08x\n", curenv->env_id, e->env_id);
|
||||
env_destroy(e);
|
||||
return 0;
|
||||
}
|
||||
@ -107,6 +103,22 @@ sys_env_set_status(envid_t envid, int status)
|
||||
panic("sys_env_set_status not implemented");
|
||||
}
|
||||
|
||||
// Set envid's trap frame to 'tf'.
|
||||
// tf is modified to make sure that user environments always run at code
|
||||
// protection level 3 (CPL 3), interrupts enabled, and IOPL of 0.
|
||||
//
|
||||
// Returns 0 on success, < 0 on error. Errors are:
|
||||
// -E_BAD_ENV if environment envid doesn't currently exist,
|
||||
// or the caller doesn't have permission to change envid.
|
||||
static int
|
||||
sys_env_set_trapframe(envid_t envid, struct Trapframe *tf)
|
||||
{
|
||||
// LAB 5: Your code here.
|
||||
// Remember to check whether the user has supplied us with a good
|
||||
// address!
|
||||
panic("sys_env_set_trapframe not implemented");
|
||||
}
|
||||
|
||||
// Set the page fault upcall for 'envid' by modifying the corresponding struct
|
||||
// Env's 'env_pgfault_upcall' field. When 'envid' causes a page fault, the
|
||||
// kernel will push a fault record onto the exception stack, then branch to
|
||||
|
@ -190,6 +190,9 @@ trap_dispatch(struct Trapframe *tf)
|
||||
// interrupt using lapic_eoi() before calling the scheduler!
|
||||
// LAB 4: Your code here.
|
||||
|
||||
// Handle keyboard and serial interrupts.
|
||||
// LAB 5: Your code here.
|
||||
|
||||
// Unexpected trap: The user process or the kernel has a bug.
|
||||
print_trapframe(tf);
|
||||
if (tf->tf_cs == GD_KT)
|
||||
|
10
lib/Makefrag
10
lib/Makefrag
@ -16,7 +16,17 @@ LIB_SRCFILES := $(LIB_SRCFILES) \
|
||||
lib/fork.c \
|
||||
lib/ipc.c
|
||||
|
||||
LIB_SRCFILES := $(LIB_SRCFILES) \
|
||||
lib/args.c \
|
||||
lib/fd.c \
|
||||
lib/file.c \
|
||||
lib/fprintf.c \
|
||||
lib/pageref.c \
|
||||
lib/spawn.c
|
||||
|
||||
LIB_SRCFILES := $(LIB_SRCFILES) \
|
||||
lib/pipe.c \
|
||||
lib/wait.c
|
||||
|
||||
LIB_OBJFILES := $(patsubst lib/%.c, $(OBJDIR)/lib/%.o, $(LIB_SRCFILES))
|
||||
LIB_OBJFILES := $(patsubst lib/%.S, $(OBJDIR)/lib/%.o, $(LIB_OBJFILES))
|
||||
|
73
lib/args.c
Normal file
73
lib/args.c
Normal file
@ -0,0 +1,73 @@
|
||||
#include <inc/args.h>
|
||||
#include <inc/string.h>
|
||||
|
||||
void
|
||||
argstart(int *argc, char **argv, struct Argstate *args)
|
||||
{
|
||||
args->argc = argc;
|
||||
args->argv = (const char **) argv;
|
||||
args->curarg = (*argc > 1 && argv ? "" : 0);
|
||||
args->argvalue = 0;
|
||||
}
|
||||
|
||||
int
|
||||
argnext(struct Argstate *args)
|
||||
{
|
||||
int arg;
|
||||
|
||||
args->argvalue = 0;
|
||||
|
||||
// Done processing arguments if args->curarg == 0
|
||||
if (args->curarg == 0)
|
||||
return -1;
|
||||
|
||||
if (!*args->curarg) {
|
||||
// Need to process the next argument
|
||||
// Check for end of argument list
|
||||
if (*args->argc == 1
|
||||
|| args->argv[1][0] != '-'
|
||||
|| args->argv[1][1] == '\0')
|
||||
goto endofargs;
|
||||
// Shift arguments down one
|
||||
args->curarg = args->argv[1] + 1;
|
||||
memmove(args->argv + 1, args->argv + 2, sizeof(const char *) * (*args->argc - 1));
|
||||
(*args->argc)--;
|
||||
// Check for "--": end of argument list
|
||||
if (args->curarg[0] == '-' && args->curarg[1] == '\0')
|
||||
goto endofargs;
|
||||
}
|
||||
|
||||
arg = (unsigned char) *args->curarg;
|
||||
args->curarg++;
|
||||
return arg;
|
||||
|
||||
endofargs:
|
||||
args->curarg = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *
|
||||
argvalue(struct Argstate *args)
|
||||
{
|
||||
return (char*) (args->argvalue ? args->argvalue : argnextvalue(args));
|
||||
}
|
||||
|
||||
char *
|
||||
argnextvalue(struct Argstate *args)
|
||||
{
|
||||
if (!args->curarg)
|
||||
return 0;
|
||||
if (*args->curarg) {
|
||||
args->argvalue = args->curarg;
|
||||
args->curarg = "";
|
||||
} else if (*args->argc > 1) {
|
||||
args->argvalue = args->argv[1];
|
||||
memmove(args->argv + 1, args->argv + 2, sizeof(const char *) * (*args->argc - 1));
|
||||
(*args->argc)--;
|
||||
} else {
|
||||
args->argvalue = 0;
|
||||
args->curarg = 0;
|
||||
}
|
||||
return (char*) args->argvalue;
|
||||
}
|
||||
|
111
lib/console.c
111
lib/console.c
@ -15,11 +15,114 @@ cputchar(int ch)
|
||||
int
|
||||
getchar(void)
|
||||
{
|
||||
unsigned char c;
|
||||
int r;
|
||||
// sys_cgetc does not block, but getchar should.
|
||||
while ((r = sys_cgetc()) == 0)
|
||||
sys_yield();
|
||||
return r;
|
||||
|
||||
// JOS does, however, support standard _input_ redirection,
|
||||
// allowing the user to redirect script files to the shell and such.
|
||||
// getchar() reads a character from file descriptor 0.
|
||||
r = read(0, &c, 1);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r < 1)
|
||||
return -E_EOF;
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
// "Real" console file descriptor implementation.
|
||||
// The putchar/getchar functions above will still come here by default,
|
||||
// but now can be redirected to files, pipes, etc., via the fd layer.
|
||||
|
||||
static ssize_t devcons_read(struct Fd*, void*, size_t);
|
||||
static ssize_t devcons_write(struct Fd*, const void*, size_t);
|
||||
static int devcons_close(struct Fd*);
|
||||
static int devcons_stat(struct Fd*, struct Stat*);
|
||||
|
||||
struct Dev devcons =
|
||||
{
|
||||
.dev_id = 'c',
|
||||
.dev_name = "cons",
|
||||
.dev_read = devcons_read,
|
||||
.dev_write = devcons_write,
|
||||
.dev_close = devcons_close,
|
||||
.dev_stat = devcons_stat
|
||||
};
|
||||
|
||||
int
|
||||
iscons(int fdnum)
|
||||
{
|
||||
int r;
|
||||
struct Fd *fd;
|
||||
|
||||
if ((r = fd_lookup(fdnum, &fd)) < 0)
|
||||
return r;
|
||||
return fd->fd_dev_id == devcons.dev_id;
|
||||
}
|
||||
|
||||
int
|
||||
opencons(void)
|
||||
{
|
||||
int r;
|
||||
struct Fd* fd;
|
||||
|
||||
if ((r = fd_alloc(&fd)) < 0)
|
||||
return r;
|
||||
if ((r = sys_page_alloc(0, fd, PTE_P|PTE_U|PTE_W|PTE_SHARE)) < 0)
|
||||
return r;
|
||||
fd->fd_dev_id = devcons.dev_id;
|
||||
fd->fd_omode = O_RDWR;
|
||||
return fd2num(fd);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
devcons_read(struct Fd *fd, void *vbuf, size_t n)
|
||||
{
|
||||
int c;
|
||||
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
while ((c = sys_cgetc()) == 0)
|
||||
sys_yield();
|
||||
if (c < 0)
|
||||
return c;
|
||||
if (c == 0x04) // ctl-d is eof
|
||||
return 0;
|
||||
*(char*)vbuf = c;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
devcons_write(struct Fd *fd, const void *vbuf, size_t n)
|
||||
{
|
||||
int tot, m;
|
||||
char buf[128];
|
||||
|
||||
// mistake: have to nul-terminate arg to sys_cputs,
|
||||
// so we have to copy vbuf into buf in chunks and nul-terminate.
|
||||
for (tot = 0; tot < n; tot += m) {
|
||||
m = n - tot;
|
||||
if (m > sizeof(buf) - 1)
|
||||
m = sizeof(buf) - 1;
|
||||
memmove(buf, (char*)vbuf + tot, m);
|
||||
sys_cputs(buf, m);
|
||||
}
|
||||
return tot;
|
||||
}
|
||||
|
||||
static int
|
||||
devcons_close(struct Fd *fd)
|
||||
{
|
||||
USED(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
devcons_stat(struct Fd *fd, struct Stat *stat)
|
||||
{
|
||||
strcpy(stat->st_name, "<cons>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
void
|
||||
exit(void)
|
||||
{
|
||||
close_all();
|
||||
sys_env_destroy(0);
|
||||
}
|
||||
|
||||
|
320
lib/fd.c
Normal file
320
lib/fd.c
Normal file
@ -0,0 +1,320 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
#define debug 0
|
||||
|
||||
// Maximum number of file descriptors a program may hold open concurrently
|
||||
#define MAXFD 32
|
||||
// Bottom of file descriptor area
|
||||
#define FDTABLE 0xD0000000
|
||||
// Bottom of file data area. We reserve one data page for each FD,
|
||||
// which devices can use if they choose.
|
||||
#define FILEDATA (FDTABLE + MAXFD*PGSIZE)
|
||||
|
||||
// Return the 'struct Fd*' for file descriptor index i
|
||||
#define INDEX2FD(i) ((struct Fd*) (FDTABLE + (i)*PGSIZE))
|
||||
// Return the file data page for file descriptor index i
|
||||
#define INDEX2DATA(i) ((char*) (FILEDATA + (i)*PGSIZE))
|
||||
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// File descriptor manipulators
|
||||
// --------------------------------------------------------------
|
||||
|
||||
int
|
||||
fd2num(struct Fd *fd)
|
||||
{
|
||||
return ((uintptr_t) fd - FDTABLE) / PGSIZE;
|
||||
}
|
||||
|
||||
char*
|
||||
fd2data(struct Fd *fd)
|
||||
{
|
||||
return INDEX2DATA(fd2num(fd));
|
||||
}
|
||||
|
||||
// Finds the smallest i from 0 to MAXFD-1 that doesn't have
|
||||
// its fd page mapped.
|
||||
// Sets *fd_store to the corresponding fd page virtual address.
|
||||
//
|
||||
// fd_alloc does NOT actually allocate an fd page.
|
||||
// It is up to the caller to allocate the page somehow.
|
||||
// This means that if someone calls fd_alloc twice in a row
|
||||
// without allocating the first page we return, we'll return the same
|
||||
// page the second time.
|
||||
//
|
||||
// Hint: Use INDEX2FD.
|
||||
//
|
||||
// Returns 0 on success, < 0 on error. Errors are:
|
||||
// -E_MAX_FD: no more file descriptors
|
||||
// On error, *fd_store is set to 0.
|
||||
int
|
||||
fd_alloc(struct Fd **fd_store)
|
||||
{
|
||||
int i;
|
||||
struct Fd *fd;
|
||||
|
||||
for (i = 0; i < MAXFD; i++) {
|
||||
fd = INDEX2FD(i);
|
||||
if ((uvpd[PDX(fd)] & PTE_P) == 0 || (uvpt[PGNUM(fd)] & PTE_P) == 0) {
|
||||
*fd_store = fd;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
*fd_store = 0;
|
||||
return -E_MAX_OPEN;
|
||||
}
|
||||
|
||||
// Check that fdnum is in range and mapped.
|
||||
// If it is, set *fd_store to the fd page virtual address.
|
||||
//
|
||||
// Returns 0 on success (the page is in range and mapped), < 0 on error.
|
||||
// Errors are:
|
||||
// -E_INVAL: fdnum was either not in range or not mapped.
|
||||
int
|
||||
fd_lookup(int fdnum, struct Fd **fd_store)
|
||||
{
|
||||
struct Fd *fd;
|
||||
|
||||
if (fdnum < 0 || fdnum >= MAXFD) {
|
||||
if (debug)
|
||||
cprintf("[%08x] bad fd %d\n", thisenv->env_id, fdnum);
|
||||
return -E_INVAL;
|
||||
}
|
||||
fd = INDEX2FD(fdnum);
|
||||
if (!(uvpd[PDX(fd)] & PTE_P) || !(uvpt[PGNUM(fd)] & PTE_P)) {
|
||||
if (debug)
|
||||
cprintf("[%08x] closed fd %d\n", thisenv->env_id, fdnum);
|
||||
return -E_INVAL;
|
||||
}
|
||||
*fd_store = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Frees file descriptor 'fd' by closing the corresponding file
|
||||
// and unmapping the file descriptor page.
|
||||
// If 'must_exist' is 0, then fd can be a closed or nonexistent file
|
||||
// descriptor; the function will return 0 and have no other effect.
|
||||
// If 'must_exist' is 1, then fd_close returns -E_INVAL when passed a
|
||||
// closed or nonexistent file descriptor.
|
||||
// Returns 0 on success, < 0 on error.
|
||||
int
|
||||
fd_close(struct Fd *fd, bool must_exist)
|
||||
{
|
||||
struct Fd *fd2;
|
||||
struct Dev *dev;
|
||||
int r;
|
||||
if ((r = fd_lookup(fd2num(fd), &fd2)) < 0
|
||||
|| fd != fd2)
|
||||
return (must_exist ? r : 0);
|
||||
if ((r = dev_lookup(fd->fd_dev_id, &dev)) >= 0) {
|
||||
if (dev->dev_close)
|
||||
r = (*dev->dev_close)(fd);
|
||||
else
|
||||
r = 0;
|
||||
}
|
||||
// Make sure fd is unmapped. Might be a no-op if
|
||||
// (*dev->dev_close)(fd) already unmapped it.
|
||||
(void) sys_page_unmap(0, fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// File functions
|
||||
// --------------------------------------------------------------
|
||||
|
||||
static struct Dev *devtab[] =
|
||||
{
|
||||
&devfile,
|
||||
&devpipe,
|
||||
&devcons,
|
||||
0
|
||||
};
|
||||
|
||||
int
|
||||
dev_lookup(int dev_id, struct Dev **dev)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; devtab[i]; i++)
|
||||
if (devtab[i]->dev_id == dev_id) {
|
||||
*dev = devtab[i];
|
||||
return 0;
|
||||
}
|
||||
cprintf("[%08x] unknown device type %d\n", thisenv->env_id, dev_id);
|
||||
*dev = 0;
|
||||
return -E_INVAL;
|
||||
}
|
||||
|
||||
int
|
||||
close(int fdnum)
|
||||
{
|
||||
struct Fd *fd;
|
||||
int r;
|
||||
|
||||
if ((r = fd_lookup(fdnum, &fd)) < 0)
|
||||
return r;
|
||||
else
|
||||
return fd_close(fd, 1);
|
||||
}
|
||||
|
||||
void
|
||||
close_all(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAXFD; i++)
|
||||
close(i);
|
||||
}
|
||||
|
||||
// Make file descriptor 'newfdnum' a duplicate of file descriptor 'oldfdnum'.
|
||||
// For instance, writing onto either file descriptor will affect the
|
||||
// file and the file offset of the other.
|
||||
// Closes any previously open file descriptor at 'newfdnum'.
|
||||
// This is implemented using virtual memory tricks (of course!).
|
||||
int
|
||||
dup(int oldfdnum, int newfdnum)
|
||||
{
|
||||
int r;
|
||||
char *ova, *nva;
|
||||
pte_t pte;
|
||||
struct Fd *oldfd, *newfd;
|
||||
|
||||
if ((r = fd_lookup(oldfdnum, &oldfd)) < 0)
|
||||
return r;
|
||||
close(newfdnum);
|
||||
|
||||
newfd = INDEX2FD(newfdnum);
|
||||
ova = fd2data(oldfd);
|
||||
nva = fd2data(newfd);
|
||||
|
||||
if ((uvpd[PDX(ova)] & PTE_P) && (uvpt[PGNUM(ova)] & PTE_P))
|
||||
if ((r = sys_page_map(0, ova, 0, nva, uvpt[PGNUM(ova)] & PTE_SYSCALL)) < 0)
|
||||
goto err;
|
||||
if ((r = sys_page_map(0, oldfd, 0, newfd, uvpt[PGNUM(oldfd)] & PTE_SYSCALL)) < 0)
|
||||
goto err;
|
||||
|
||||
return newfdnum;
|
||||
|
||||
err:
|
||||
sys_page_unmap(0, newfd);
|
||||
sys_page_unmap(0, nva);
|
||||
return r;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
read(int fdnum, void *buf, size_t n)
|
||||
{
|
||||
int r;
|
||||
struct Dev *dev;
|
||||
struct Fd *fd;
|
||||
|
||||
if ((r = fd_lookup(fdnum, &fd)) < 0
|
||||
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
|
||||
return r;
|
||||
if ((fd->fd_omode & O_ACCMODE) == O_WRONLY) {
|
||||
cprintf("[%08x] read %d -- bad mode\n", thisenv->env_id, fdnum);
|
||||
return -E_INVAL;
|
||||
}
|
||||
if (!dev->dev_read)
|
||||
return -E_NOT_SUPP;
|
||||
return (*dev->dev_read)(fd, buf, n);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
readn(int fdnum, void *buf, size_t n)
|
||||
{
|
||||
int m, tot;
|
||||
|
||||
for (tot = 0; tot < n; tot += m) {
|
||||
m = read(fdnum, (char*)buf + tot, n - tot);
|
||||
if (m < 0)
|
||||
return m;
|
||||
if (m == 0)
|
||||
break;
|
||||
}
|
||||
return tot;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
write(int fdnum, const void *buf, size_t n)
|
||||
{
|
||||
int r;
|
||||
struct Dev *dev;
|
||||
struct Fd *fd;
|
||||
|
||||
if ((r = fd_lookup(fdnum, &fd)) < 0
|
||||
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
|
||||
return r;
|
||||
if ((fd->fd_omode & O_ACCMODE) == O_RDONLY) {
|
||||
cprintf("[%08x] write %d -- bad mode\n", thisenv->env_id, fdnum);
|
||||
return -E_INVAL;
|
||||
}
|
||||
if (debug)
|
||||
cprintf("write %d %p %d via dev %s\n",
|
||||
fdnum, buf, n, dev->dev_name);
|
||||
if (!dev->dev_write)
|
||||
return -E_NOT_SUPP;
|
||||
return (*dev->dev_write)(fd, buf, n);
|
||||
}
|
||||
|
||||
int
|
||||
seek(int fdnum, off_t offset)
|
||||
{
|
||||
int r;
|
||||
struct Fd *fd;
|
||||
|
||||
if ((r = fd_lookup(fdnum, &fd)) < 0)
|
||||
return r;
|
||||
fd->fd_offset = offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ftruncate(int fdnum, off_t newsize)
|
||||
{
|
||||
int r;
|
||||
struct Dev *dev;
|
||||
struct Fd *fd;
|
||||
if ((r = fd_lookup(fdnum, &fd)) < 0
|
||||
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
|
||||
return r;
|
||||
if ((fd->fd_omode & O_ACCMODE) == O_RDONLY) {
|
||||
cprintf("[%08x] ftruncate %d -- bad mode\n",
|
||||
thisenv->env_id, fdnum);
|
||||
return -E_INVAL;
|
||||
}
|
||||
if (!dev->dev_trunc)
|
||||
return -E_NOT_SUPP;
|
||||
return (*dev->dev_trunc)(fd, newsize);
|
||||
}
|
||||
|
||||
int
|
||||
fstat(int fdnum, struct Stat *stat)
|
||||
{
|
||||
int r;
|
||||
struct Dev *dev;
|
||||
struct Fd *fd;
|
||||
|
||||
if ((r = fd_lookup(fdnum, &fd)) < 0
|
||||
|| (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
|
||||
return r;
|
||||
if (!dev->dev_stat)
|
||||
return -E_NOT_SUPP;
|
||||
stat->st_name[0] = 0;
|
||||
stat->st_size = 0;
|
||||
stat->st_isdir = 0;
|
||||
stat->st_dev = dev;
|
||||
return (*dev->dev_stat)(fd, stat);
|
||||
}
|
||||
|
||||
int
|
||||
stat(const char *path, struct Stat *stat)
|
||||
{
|
||||
int fd, r;
|
||||
|
||||
if ((fd = open(path, O_RDONLY)) < 0)
|
||||
return fd;
|
||||
r = fstat(fd, stat);
|
||||
close(fd);
|
||||
return r;
|
||||
}
|
||||
|
180
lib/file.c
Normal file
180
lib/file.c
Normal file
@ -0,0 +1,180 @@
|
||||
#include <inc/fs.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
#define debug 0
|
||||
|
||||
union Fsipc fsipcbuf __attribute__((aligned(PGSIZE)));
|
||||
|
||||
// Send an inter-environment request to the file server, and wait for
|
||||
// a reply. The request body should be in fsipcbuf, and parts of the
|
||||
// response may be written back to fsipcbuf.
|
||||
// type: request code, passed as the simple integer IPC value.
|
||||
// dstva: virtual address at which to receive reply page, 0 if none.
|
||||
// Returns result from the file server.
|
||||
static int
|
||||
fsipc(unsigned type, void *dstva)
|
||||
{
|
||||
static envid_t fsenv;
|
||||
if (fsenv == 0)
|
||||
fsenv = ipc_find_env(ENV_TYPE_FS);
|
||||
|
||||
static_assert(sizeof(fsipcbuf) == PGSIZE);
|
||||
|
||||
if (debug)
|
||||
cprintf("[%08x] fsipc %d %08x\n", thisenv->env_id, type, *(uint32_t *)&fsipcbuf);
|
||||
|
||||
ipc_send(fsenv, type, &fsipcbuf, PTE_P | PTE_W | PTE_U);
|
||||
return ipc_recv(NULL, dstva, NULL);
|
||||
}
|
||||
|
||||
static int devfile_flush(struct Fd *fd);
|
||||
static ssize_t devfile_read(struct Fd *fd, void *buf, size_t n);
|
||||
static ssize_t devfile_write(struct Fd *fd, const void *buf, size_t n);
|
||||
static int devfile_stat(struct Fd *fd, struct Stat *stat);
|
||||
static int devfile_trunc(struct Fd *fd, off_t newsize);
|
||||
|
||||
struct Dev devfile =
|
||||
{
|
||||
.dev_id = 'f',
|
||||
.dev_name = "file",
|
||||
.dev_read = devfile_read,
|
||||
.dev_close = devfile_flush,
|
||||
.dev_stat = devfile_stat,
|
||||
.dev_write = devfile_write,
|
||||
.dev_trunc = devfile_trunc
|
||||
};
|
||||
|
||||
// Open a file (or directory).
|
||||
//
|
||||
// Returns:
|
||||
// The file descriptor index on success
|
||||
// -E_BAD_PATH if the path is too long (>= MAXPATHLEN)
|
||||
// < 0 for other errors.
|
||||
int
|
||||
open(const char *path, int mode)
|
||||
{
|
||||
// Find an unused file descriptor page using fd_alloc.
|
||||
// Then send a file-open request to the file server.
|
||||
// Include 'path' and 'omode' in request,
|
||||
// and map the returned file descriptor page
|
||||
// at the appropriate fd address.
|
||||
// FSREQ_OPEN returns 0 on success, < 0 on failure.
|
||||
//
|
||||
// (fd_alloc does not allocate a page, it just returns an
|
||||
// unused fd address. Do you need to allocate a page?)
|
||||
//
|
||||
// Return the file descriptor index.
|
||||
// If any step after fd_alloc fails, use fd_close to free the
|
||||
// file descriptor.
|
||||
|
||||
int r;
|
||||
struct Fd *fd;
|
||||
|
||||
if (strlen(path) >= MAXPATHLEN)
|
||||
return -E_BAD_PATH;
|
||||
|
||||
if ((r = fd_alloc(&fd)) < 0)
|
||||
return r;
|
||||
|
||||
strcpy(fsipcbuf.open.req_path, path);
|
||||
fsipcbuf.open.req_omode = mode;
|
||||
|
||||
if ((r = fsipc(FSREQ_OPEN, fd)) < 0) {
|
||||
fd_close(fd, 0);
|
||||
return r;
|
||||
}
|
||||
|
||||
return fd2num(fd);
|
||||
}
|
||||
|
||||
// Flush the file descriptor. After this the fileid is invalid.
|
||||
//
|
||||
// This function is called by fd_close. fd_close will take care of
|
||||
// unmapping the FD page from this environment. Since the server uses
|
||||
// the reference counts on the FD pages to detect which files are
|
||||
// open, unmapping it is enough to free up server-side resources.
|
||||
// Other than that, we just have to make sure our changes are flushed
|
||||
// to disk.
|
||||
static int
|
||||
devfile_flush(struct Fd *fd)
|
||||
{
|
||||
fsipcbuf.flush.req_fileid = fd->fd_file.id;
|
||||
return fsipc(FSREQ_FLUSH, NULL);
|
||||
}
|
||||
|
||||
// Read at most 'n' bytes from 'fd' at the current position into 'buf'.
|
||||
//
|
||||
// Returns:
|
||||
// The number of bytes successfully read.
|
||||
// < 0 on error.
|
||||
static ssize_t
|
||||
devfile_read(struct Fd *fd, void *buf, size_t n)
|
||||
{
|
||||
// Make an FSREQ_READ request to the file system server after
|
||||
// filling fsipcbuf.read with the request arguments. The
|
||||
// bytes read will be written back to fsipcbuf by the file
|
||||
// system server.
|
||||
int r;
|
||||
|
||||
fsipcbuf.read.req_fileid = fd->fd_file.id;
|
||||
fsipcbuf.read.req_n = n;
|
||||
if ((r = fsipc(FSREQ_READ, NULL)) < 0)
|
||||
return r;
|
||||
assert(r <= n);
|
||||
assert(r <= PGSIZE);
|
||||
memmove(buf, fsipcbuf.readRet.ret_buf, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
// Write at most 'n' bytes from 'buf' to 'fd' at the current seek position.
|
||||
//
|
||||
// Returns:
|
||||
// The number of bytes successfully written.
|
||||
// < 0 on error.
|
||||
static ssize_t
|
||||
devfile_write(struct Fd *fd, const void *buf, size_t n)
|
||||
{
|
||||
// Make an FSREQ_WRITE request to the file system server. Be
|
||||
// careful: fsipcbuf.write.req_buf is only so large, but
|
||||
// remember that write is always allowed to write *fewer*
|
||||
// bytes than requested.
|
||||
// LAB 5: Your code here
|
||||
panic("devfile_write not implemented");
|
||||
}
|
||||
|
||||
static int
|
||||
devfile_stat(struct Fd *fd, struct Stat *st)
|
||||
{
|
||||
int r;
|
||||
|
||||
fsipcbuf.stat.req_fileid = fd->fd_file.id;
|
||||
if ((r = fsipc(FSREQ_STAT, NULL)) < 0)
|
||||
return r;
|
||||
strcpy(st->st_name, fsipcbuf.statRet.ret_name);
|
||||
st->st_size = fsipcbuf.statRet.ret_size;
|
||||
st->st_isdir = fsipcbuf.statRet.ret_isdir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Truncate or extend an open file to 'size' bytes
|
||||
static int
|
||||
devfile_trunc(struct Fd *fd, off_t newsize)
|
||||
{
|
||||
fsipcbuf.set_size.req_fileid = fd->fd_file.id;
|
||||
fsipcbuf.set_size.req_size = newsize;
|
||||
return fsipc(FSREQ_SET_SIZE, NULL);
|
||||
}
|
||||
|
||||
|
||||
// Synchronize disk with buffer cache
|
||||
int
|
||||
sync(void)
|
||||
{
|
||||
// Ask the file server to update the disk
|
||||
// by writing any dirty blocks in the buffer cache.
|
||||
|
||||
return fsipc(FSREQ_SYNC, NULL);
|
||||
}
|
||||
|
81
lib/fprintf.c
Normal file
81
lib/fprintf.c
Normal file
@ -0,0 +1,81 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
// Collect up to 256 characters into a buffer
|
||||
// and perform ONE system call to print all of them,
|
||||
// in order to make the lines output to the console atomic
|
||||
// and prevent interrupts from causing context switches
|
||||
// in the middle of a console output line and such.
|
||||
struct printbuf {
|
||||
int fd; // file descriptor
|
||||
int idx; // current buffer index
|
||||
ssize_t result; // accumulated results from write
|
||||
int error; // first error that occurred
|
||||
char buf[256];
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
writebuf(struct printbuf *b)
|
||||
{
|
||||
if (b->error > 0) {
|
||||
ssize_t result = write(b->fd, b->buf, b->idx);
|
||||
if (result > 0)
|
||||
b->result += result;
|
||||
if (result != b->idx) // error, or wrote less than supplied
|
||||
b->error = (result < 0 ? result : 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
putch(int ch, void *thunk)
|
||||
{
|
||||
struct printbuf *b = (struct printbuf *) thunk;
|
||||
b->buf[b->idx++] = ch;
|
||||
if (b->idx == 256) {
|
||||
writebuf(b);
|
||||
b->idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
vfprintf(int fd, const char *fmt, va_list ap)
|
||||
{
|
||||
struct printbuf b;
|
||||
|
||||
b.fd = fd;
|
||||
b.idx = 0;
|
||||
b.result = 0;
|
||||
b.error = 1;
|
||||
vprintfmt(putch, &b, fmt, ap);
|
||||
if (b.idx > 0)
|
||||
writebuf(&b);
|
||||
|
||||
return (b.result ? b.result : b.error);
|
||||
}
|
||||
|
||||
int
|
||||
fprintf(int fd, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int cnt;
|
||||
|
||||
va_start(ap, fmt);
|
||||
cnt = vfprintf(fd, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int
|
||||
printf(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int cnt;
|
||||
|
||||
va_start(ap, fmt);
|
||||
cnt = vfprintf(1, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
14
lib/pageref.c
Normal file
14
lib/pageref.c
Normal file
@ -0,0 +1,14 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
int
|
||||
pageref(void *v)
|
||||
{
|
||||
pte_t pte;
|
||||
|
||||
if (!(uvpd[PDX(v)] & PTE_P))
|
||||
return 0;
|
||||
pte = uvpt[PGNUM(v)];
|
||||
if (!(pte & PTE_P))
|
||||
return 0;
|
||||
return pages[PGNUM(pte)].pp_ref;
|
||||
}
|
191
lib/pipe.c
Normal file
191
lib/pipe.c
Normal file
@ -0,0 +1,191 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
#define debug 0
|
||||
|
||||
static ssize_t devpipe_read(struct Fd *fd, void *buf, size_t n);
|
||||
static ssize_t devpipe_write(struct Fd *fd, const void *buf, size_t n);
|
||||
static int devpipe_stat(struct Fd *fd, struct Stat *stat);
|
||||
static int devpipe_close(struct Fd *fd);
|
||||
|
||||
struct Dev devpipe =
|
||||
{
|
||||
.dev_id = 'p',
|
||||
.dev_name = "pipe",
|
||||
.dev_read = devpipe_read,
|
||||
.dev_write = devpipe_write,
|
||||
.dev_close = devpipe_close,
|
||||
.dev_stat = devpipe_stat,
|
||||
};
|
||||
|
||||
#define PIPEBUFSIZ 32 // small to provoke races
|
||||
|
||||
struct Pipe {
|
||||
off_t p_rpos; // read position
|
||||
off_t p_wpos; // write position
|
||||
uint8_t p_buf[PIPEBUFSIZ]; // data buffer
|
||||
};
|
||||
|
||||
int
|
||||
pipe(int pfd[2])
|
||||
{
|
||||
int r;
|
||||
struct Fd *fd0, *fd1;
|
||||
void *va;
|
||||
|
||||
// allocate the file descriptor table entries
|
||||
if ((r = fd_alloc(&fd0)) < 0
|
||||
|| (r = sys_page_alloc(0, fd0, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
|
||||
goto err;
|
||||
|
||||
if ((r = fd_alloc(&fd1)) < 0
|
||||
|| (r = sys_page_alloc(0, fd1, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
|
||||
goto err1;
|
||||
|
||||
// allocate the pipe structure as first data page in both
|
||||
va = fd2data(fd0);
|
||||
if ((r = sys_page_alloc(0, va, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
|
||||
goto err2;
|
||||
if ((r = sys_page_map(0, va, 0, fd2data(fd1), PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
|
||||
goto err3;
|
||||
|
||||
// set up fd structures
|
||||
fd0->fd_dev_id = devpipe.dev_id;
|
||||
fd0->fd_omode = O_RDONLY;
|
||||
|
||||
fd1->fd_dev_id = devpipe.dev_id;
|
||||
fd1->fd_omode = O_WRONLY;
|
||||
|
||||
if (debug)
|
||||
cprintf("[%08x] pipecreate %08x\n", thisenv->env_id, uvpt[PGNUM(va)]);
|
||||
|
||||
pfd[0] = fd2num(fd0);
|
||||
pfd[1] = fd2num(fd1);
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
sys_page_unmap(0, va);
|
||||
err2:
|
||||
sys_page_unmap(0, fd1);
|
||||
err1:
|
||||
sys_page_unmap(0, fd0);
|
||||
err:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
_pipeisclosed(struct Fd *fd, struct Pipe *p)
|
||||
{
|
||||
int n, nn, ret;
|
||||
|
||||
while (1) {
|
||||
n = thisenv->env_runs;
|
||||
ret = pageref(fd) == pageref(p);
|
||||
nn = thisenv->env_runs;
|
||||
if (n == nn)
|
||||
return ret;
|
||||
if (n != nn && ret == 1)
|
||||
cprintf("pipe race avoided\n", n, thisenv->env_runs, ret);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
pipeisclosed(int fdnum)
|
||||
{
|
||||
struct Fd *fd;
|
||||
struct Pipe *p;
|
||||
int r;
|
||||
|
||||
if ((r = fd_lookup(fdnum, &fd)) < 0)
|
||||
return r;
|
||||
p = (struct Pipe*) fd2data(fd);
|
||||
return _pipeisclosed(fd, p);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
devpipe_read(struct Fd *fd, void *vbuf, size_t n)
|
||||
{
|
||||
uint8_t *buf;
|
||||
size_t i;
|
||||
struct Pipe *p;
|
||||
|
||||
p = (struct Pipe*)fd2data(fd);
|
||||
if (debug)
|
||||
cprintf("[%08x] devpipe_read %08x %d rpos %d wpos %d\n",
|
||||
thisenv->env_id, uvpt[PGNUM(p)], n, p->p_rpos, p->p_wpos);
|
||||
|
||||
buf = vbuf;
|
||||
for (i = 0; i < n; i++) {
|
||||
while (p->p_rpos == p->p_wpos) {
|
||||
// pipe is empty
|
||||
// if we got any data, return it
|
||||
if (i > 0)
|
||||
return i;
|
||||
// if all the writers are gone, note eof
|
||||
if (_pipeisclosed(fd, p))
|
||||
return 0;
|
||||
// yield and see what happens
|
||||
if (debug)
|
||||
cprintf("devpipe_read yield\n");
|
||||
sys_yield();
|
||||
}
|
||||
// there's a byte. take it.
|
||||
// wait to increment rpos until the byte is taken!
|
||||
buf[i] = p->p_buf[p->p_rpos % PIPEBUFSIZ];
|
||||
p->p_rpos++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
devpipe_write(struct Fd *fd, const void *vbuf, size_t n)
|
||||
{
|
||||
const uint8_t *buf;
|
||||
size_t i;
|
||||
struct Pipe *p;
|
||||
|
||||
p = (struct Pipe*) fd2data(fd);
|
||||
if (debug)
|
||||
cprintf("[%08x] devpipe_write %08x %d rpos %d wpos %d\n",
|
||||
thisenv->env_id, uvpt[PGNUM(p)], n, p->p_rpos, p->p_wpos);
|
||||
|
||||
buf = vbuf;
|
||||
for (i = 0; i < n; i++) {
|
||||
while (p->p_wpos >= p->p_rpos + sizeof(p->p_buf)) {
|
||||
// pipe is full
|
||||
// if all the readers are gone
|
||||
// (it's only writers like us now),
|
||||
// note eof
|
||||
if (_pipeisclosed(fd, p))
|
||||
return 0;
|
||||
// yield and see what happens
|
||||
if (debug)
|
||||
cprintf("devpipe_write yield\n");
|
||||
sys_yield();
|
||||
}
|
||||
// there's room for a byte. store it.
|
||||
// wait to increment wpos until the byte is stored!
|
||||
p->p_buf[p->p_wpos % PIPEBUFSIZ] = buf[i];
|
||||
p->p_wpos++;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int
|
||||
devpipe_stat(struct Fd *fd, struct Stat *stat)
|
||||
{
|
||||
struct Pipe *p = (struct Pipe*) fd2data(fd);
|
||||
strcpy(stat->st_name, "<pipe>");
|
||||
stat->st_size = p->p_wpos - p->p_rpos;
|
||||
stat->st_isdir = 0;
|
||||
stat->st_dev = &devpipe;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
devpipe_close(struct Fd *fd)
|
||||
{
|
||||
(void) sys_page_unmap(0, fd);
|
||||
return sys_page_unmap(0, fd2data(fd));
|
||||
}
|
||||
|
@ -28,6 +28,13 @@ static const char * const error_string[MAXERROR] =
|
||||
[E_FAULT] = "segmentation fault",
|
||||
[E_IPC_NOT_RECV]= "env is not recving",
|
||||
[E_EOF] = "unexpected end of file",
|
||||
[E_NO_DISK] = "no free space on disk",
|
||||
[E_MAX_OPEN] = "too many files are open",
|
||||
[E_NOT_FOUND] = "file or block not found",
|
||||
[E_BAD_PATH] = "invalid path",
|
||||
[E_FILE_EXISTS] = "file already exists",
|
||||
[E_NOT_EXEC] = "file is not a valid executable",
|
||||
[E_NOT_SUPP] = "operation not supported",
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -9,15 +9,21 @@ readline(const char *prompt)
|
||||
{
|
||||
int i, c, echoing;
|
||||
|
||||
#if JOS_KERNEL
|
||||
if (prompt != NULL)
|
||||
cprintf("%s", prompt);
|
||||
#else
|
||||
if (prompt != NULL)
|
||||
fprintf(1, "%s", prompt);
|
||||
#endif
|
||||
|
||||
i = 0;
|
||||
echoing = iscons(0);
|
||||
while (1) {
|
||||
c = getchar();
|
||||
if (c < 0) {
|
||||
cprintf("read error: %e\n", c);
|
||||
if (c != -E_EOF)
|
||||
cprintf("read error: %e\n", c);
|
||||
return NULL;
|
||||
} else if ((c == '\b' || c == '\x7f') && i > 0) {
|
||||
if (echoing)
|
||||
|
307
lib/spawn.c
Normal file
307
lib/spawn.c
Normal file
@ -0,0 +1,307 @@
|
||||
#include <inc/lib.h>
|
||||
#include <inc/elf.h>
|
||||
|
||||
#define UTEMP2USTACK(addr) ((void*) (addr) + (USTACKTOP - PGSIZE) - UTEMP)
|
||||
#define UTEMP2 (UTEMP + PGSIZE)
|
||||
#define UTEMP3 (UTEMP2 + PGSIZE)
|
||||
|
||||
// Helper functions for spawn.
|
||||
static int init_stack(envid_t child, const char **argv, uintptr_t *init_esp);
|
||||
static int map_segment(envid_t child, uintptr_t va, size_t memsz,
|
||||
int fd, size_t filesz, off_t fileoffset, int perm);
|
||||
static int copy_shared_pages(envid_t child);
|
||||
|
||||
// Spawn a child process from a program image loaded from the file system.
|
||||
// prog: the pathname of the program to run.
|
||||
// argv: pointer to null-terminated array of pointers to strings,
|
||||
// which will be passed to the child as its command-line arguments.
|
||||
// Returns child envid on success, < 0 on failure.
|
||||
int
|
||||
spawn(const char *prog, const char **argv)
|
||||
{
|
||||
unsigned char elf_buf[512];
|
||||
struct Trapframe child_tf;
|
||||
envid_t child;
|
||||
|
||||
int fd, i, r;
|
||||
struct Elf *elf;
|
||||
struct Proghdr *ph;
|
||||
int perm;
|
||||
|
||||
// This code follows this procedure:
|
||||
//
|
||||
// - Open the program file.
|
||||
//
|
||||
// - Read the ELF header, as you have before, and sanity check its
|
||||
// magic number. (Check out your load_icode!)
|
||||
//
|
||||
// - Use sys_exofork() to create a new environment.
|
||||
//
|
||||
// - Set child_tf to an initial struct Trapframe for the child.
|
||||
//
|
||||
// - Call the init_stack() function above to set up
|
||||
// the initial stack page for the child environment.
|
||||
//
|
||||
// - Map all of the program's segments that are of p_type
|
||||
// ELF_PROG_LOAD into the new environment's address space.
|
||||
// Use the p_flags field in the Proghdr for each segment
|
||||
// to determine how to map the segment:
|
||||
//
|
||||
// * If the ELF flags do not include ELF_PROG_FLAG_WRITE,
|
||||
// then the segment contains text and read-only data.
|
||||
// Use read_map() to read the contents of this segment,
|
||||
// and map the pages it returns directly into the child
|
||||
// so that multiple instances of the same program
|
||||
// will share the same copy of the program text.
|
||||
// Be sure to map the program text read-only in the child.
|
||||
// Read_map is like read but returns a pointer to the data in
|
||||
// *blk rather than copying the data into another buffer.
|
||||
//
|
||||
// * If the ELF segment flags DO include ELF_PROG_FLAG_WRITE,
|
||||
// then the segment contains read/write data and bss.
|
||||
// As with load_icode() in Lab 3, such an ELF segment
|
||||
// occupies p_memsz bytes in memory, but only the FIRST
|
||||
// p_filesz bytes of the segment are actually loaded
|
||||
// from the executable file - you must clear the rest to zero.
|
||||
// For each page to be mapped for a read/write segment,
|
||||
// allocate a page in the parent temporarily at UTEMP,
|
||||
// read() the appropriate portion of the file into that page
|
||||
// and/or use memset() to zero non-loaded portions.
|
||||
// (You can avoid calling memset(), if you like, if
|
||||
// page_alloc() returns zeroed pages already.)
|
||||
// Then insert the page mapping into the child.
|
||||
// Look at init_stack() for inspiration.
|
||||
// Be sure you understand why you can't use read_map() here.
|
||||
//
|
||||
// Note: None of the segment addresses or lengths above
|
||||
// are guaranteed to be page-aligned, so you must deal with
|
||||
// these non-page-aligned values appropriately.
|
||||
// The ELF linker does, however, guarantee that no two segments
|
||||
// will overlap on the same page; and it guarantees that
|
||||
// PGOFF(ph->p_offset) == PGOFF(ph->p_va).
|
||||
//
|
||||
// - Call sys_env_set_trapframe(child, &child_tf) to set up the
|
||||
// correct initial eip and esp values in the child.
|
||||
//
|
||||
// - Start the child process running with sys_env_set_status().
|
||||
|
||||
if ((r = open(prog, O_RDONLY)) < 0)
|
||||
return r;
|
||||
fd = r;
|
||||
|
||||
// Read elf header
|
||||
elf = (struct Elf*) elf_buf;
|
||||
if (readn(fd, elf_buf, sizeof(elf_buf)) != sizeof(elf_buf)
|
||||
|| elf->e_magic != ELF_MAGIC) {
|
||||
close(fd);
|
||||
cprintf("elf magic %08x want %08x\n", elf->e_magic, ELF_MAGIC);
|
||||
return -E_NOT_EXEC;
|
||||
}
|
||||
|
||||
// Create new child environment
|
||||
if ((r = sys_exofork()) < 0)
|
||||
return r;
|
||||
child = r;
|
||||
|
||||
// Set up trap frame, including initial stack.
|
||||
child_tf = envs[ENVX(child)].env_tf;
|
||||
child_tf.tf_eip = elf->e_entry;
|
||||
|
||||
if ((r = init_stack(child, argv, &child_tf.tf_esp)) < 0)
|
||||
return r;
|
||||
|
||||
// Set up program segments as defined in ELF header.
|
||||
ph = (struct Proghdr*) (elf_buf + elf->e_phoff);
|
||||
for (i = 0; i < elf->e_phnum; i++, ph++) {
|
||||
if (ph->p_type != ELF_PROG_LOAD)
|
||||
continue;
|
||||
perm = PTE_P | PTE_U;
|
||||
if (ph->p_flags & ELF_PROG_FLAG_WRITE)
|
||||
perm |= PTE_W;
|
||||
if ((r = map_segment(child, ph->p_va, ph->p_memsz,
|
||||
fd, ph->p_filesz, ph->p_offset, perm)) < 0)
|
||||
goto error;
|
||||
}
|
||||
close(fd);
|
||||
fd = -1;
|
||||
|
||||
// Copy shared library state.
|
||||
if ((r = copy_shared_pages(child)) < 0)
|
||||
panic("copy_shared_pages: %e", r);
|
||||
|
||||
child_tf.tf_eflags |= FL_IOPL_3; // devious: see user/faultio.c
|
||||
if ((r = sys_env_set_trapframe(child, &child_tf)) < 0)
|
||||
panic("sys_env_set_trapframe: %e", r);
|
||||
|
||||
if ((r = sys_env_set_status(child, ENV_RUNNABLE)) < 0)
|
||||
panic("sys_env_set_status: %e", r);
|
||||
|
||||
return child;
|
||||
|
||||
error:
|
||||
sys_env_destroy(child);
|
||||
close(fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
// Spawn, taking command-line arguments array directly on the stack.
|
||||
// NOTE: Must have a sentinal of NULL at the end of the args
|
||||
// (none of the args may be NULL).
|
||||
int
|
||||
spawnl(const char *prog, const char *arg0, ...)
|
||||
{
|
||||
// We calculate argc by advancing the args until we hit NULL.
|
||||
// The contract of the function guarantees that the last
|
||||
// argument will always be NULL, and that none of the other
|
||||
// arguments will be NULL.
|
||||
int argc=0;
|
||||
va_list vl;
|
||||
va_start(vl, arg0);
|
||||
while(va_arg(vl, void *) != NULL)
|
||||
argc++;
|
||||
va_end(vl);
|
||||
|
||||
// Now that we have the size of the args, do a second pass
|
||||
// and store the values in a VLA, which has the format of argv
|
||||
const char *argv[argc+2];
|
||||
argv[0] = arg0;
|
||||
argv[argc+1] = NULL;
|
||||
|
||||
va_start(vl, arg0);
|
||||
unsigned i;
|
||||
for(i=0;i<argc;i++)
|
||||
argv[i+1] = va_arg(vl, const char *);
|
||||
va_end(vl);
|
||||
return spawn(prog, argv);
|
||||
}
|
||||
|
||||
|
||||
// Set up the initial stack page for the new child process with envid 'child'
|
||||
// using the arguments array pointed to by 'argv',
|
||||
// which is a null-terminated array of pointers to null-terminated strings.
|
||||
//
|
||||
// On success, returns 0 and sets *init_esp
|
||||
// to the initial stack pointer with which the child should start.
|
||||
// Returns < 0 on failure.
|
||||
static int
|
||||
init_stack(envid_t child, const char **argv, uintptr_t *init_esp)
|
||||
{
|
||||
size_t string_size;
|
||||
int argc, i, r;
|
||||
char *string_store;
|
||||
uintptr_t *argv_store;
|
||||
|
||||
// Count the number of arguments (argc)
|
||||
// and the total amount of space needed for strings (string_size).
|
||||
string_size = 0;
|
||||
for (argc = 0; argv[argc] != 0; argc++)
|
||||
string_size += strlen(argv[argc]) + 1;
|
||||
|
||||
// Determine where to place the strings and the argv array.
|
||||
// Set up pointers into the temporary page 'UTEMP'; we'll map a page
|
||||
// there later, then remap that page into the child environment
|
||||
// at (USTACKTOP - PGSIZE).
|
||||
// strings is the topmost thing on the stack.
|
||||
string_store = (char*) UTEMP + PGSIZE - string_size;
|
||||
// argv is below that. There's one argument pointer per argument, plus
|
||||
// a null pointer.
|
||||
argv_store = (uintptr_t*) (ROUNDDOWN(string_store, 4) - 4 * (argc + 1));
|
||||
|
||||
// Make sure that argv, strings, and the 2 words that hold 'argc'
|
||||
// and 'argv' themselves will all fit in a single stack page.
|
||||
if ((void*) (argv_store - 2) < (void*) UTEMP)
|
||||
return -E_NO_MEM;
|
||||
|
||||
// Allocate the single stack page at UTEMP.
|
||||
if ((r = sys_page_alloc(0, (void*) UTEMP, PTE_P|PTE_U|PTE_W)) < 0)
|
||||
return r;
|
||||
|
||||
|
||||
// * Initialize 'argv_store[i]' to point to argument string i,
|
||||
// for all 0 <= i < argc.
|
||||
// Also, copy the argument strings from 'argv' into the
|
||||
// newly-allocated stack page.
|
||||
//
|
||||
// * Set 'argv_store[argc]' to 0 to null-terminate the args array.
|
||||
//
|
||||
// * Push two more words onto the child's stack below 'args',
|
||||
// containing the argc and argv parameters to be passed
|
||||
// to the child's umain() function.
|
||||
// argv should be below argc on the stack.
|
||||
// (Again, argv should use an address valid in the child's
|
||||
// environment.)
|
||||
//
|
||||
// * Set *init_esp to the initial stack pointer for the child,
|
||||
// (Again, use an address valid in the child's environment.)
|
||||
for (i = 0; i < argc; i++) {
|
||||
argv_store[i] = UTEMP2USTACK(string_store);
|
||||
strcpy(string_store, argv[i]);
|
||||
string_store += strlen(argv[i]) + 1;
|
||||
}
|
||||
argv_store[argc] = 0;
|
||||
assert(string_store == (char*)UTEMP + PGSIZE);
|
||||
|
||||
argv_store[-1] = UTEMP2USTACK(argv_store);
|
||||
argv_store[-2] = argc;
|
||||
|
||||
*init_esp = UTEMP2USTACK(&argv_store[-2]);
|
||||
|
||||
// After completing the stack, map it into the child's address space
|
||||
// and unmap it from ours!
|
||||
if ((r = sys_page_map(0, UTEMP, child, (void*) (USTACKTOP - PGSIZE), PTE_P | PTE_U | PTE_W)) < 0)
|
||||
goto error;
|
||||
if ((r = sys_page_unmap(0, UTEMP)) < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
sys_page_unmap(0, UTEMP);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int
|
||||
map_segment(envid_t child, uintptr_t va, size_t memsz,
|
||||
int fd, size_t filesz, off_t fileoffset, int perm)
|
||||
{
|
||||
int i, r;
|
||||
void *blk;
|
||||
|
||||
//cprintf("map_segment %x+%x\n", va, memsz);
|
||||
|
||||
if ((i = PGOFF(va))) {
|
||||
va -= i;
|
||||
memsz += i;
|
||||
filesz += i;
|
||||
fileoffset -= i;
|
||||
}
|
||||
|
||||
for (i = 0; i < memsz; i += PGSIZE) {
|
||||
if (i >= filesz) {
|
||||
// allocate a blank page
|
||||
if ((r = sys_page_alloc(child, (void*) (va + i), perm)) < 0)
|
||||
return r;
|
||||
} else {
|
||||
// from file
|
||||
if ((r = sys_page_alloc(0, UTEMP, PTE_P|PTE_U|PTE_W)) < 0)
|
||||
return r;
|
||||
if ((r = seek(fd, fileoffset + i)) < 0)
|
||||
return r;
|
||||
if ((r = readn(fd, UTEMP, MIN(PGSIZE, filesz-i))) < 0)
|
||||
return r;
|
||||
if ((r = sys_page_map(0, UTEMP, child, (void*) (va + i), perm)) < 0)
|
||||
panic("spawn: sys_page_map data: %e", r);
|
||||
sys_page_unmap(0, UTEMP);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy the mappings for shared pages into the child address space.
|
||||
static int
|
||||
copy_shared_pages(envid_t child)
|
||||
{
|
||||
// LAB 5: Your code here.
|
||||
return 0;
|
||||
}
|
||||
|
@ -93,6 +93,12 @@ sys_env_set_status(envid_t envid, int status)
|
||||
return syscall(SYS_env_set_status, 1, envid, status, 0, 0, 0);
|
||||
}
|
||||
|
||||
int
|
||||
sys_env_set_trapframe(envid_t envid, struct Trapframe *tf)
|
||||
{
|
||||
return syscall(SYS_env_set_trapframe, 1, envid, (uint32_t) tf, 0, 0, 0);
|
||||
}
|
||||
|
||||
int
|
||||
sys_env_set_pgfault_upcall(envid_t envid, void *upcall)
|
||||
{
|
||||
|
13
lib/wait.c
Normal file
13
lib/wait.c
Normal file
@ -0,0 +1,13 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
// Waits until 'envid' exits.
|
||||
void
|
||||
wait(envid_t envid)
|
||||
{
|
||||
const volatile struct Env *e;
|
||||
|
||||
assert(envid != 0);
|
||||
e = &envs[ENVX(envid)];
|
||||
while (e->env_id == envid && e->env_status != ENV_FREE)
|
||||
sys_yield();
|
||||
}
|
@ -10,7 +10,8 @@ $(OBJDIR)/user/%.o: user/%.c $(OBJDIR)/.vars.USER_CFLAGS
|
||||
|
||||
$(OBJDIR)/user/%: $(OBJDIR)/user/%.o $(OBJDIR)/lib/entry.o $(USERLIBS:%=$(OBJDIR)/lib/lib%.a) user/user.ld
|
||||
@echo + ld $@
|
||||
$(V)$(LD) -o $@ $(ULDFLAGS) $(LDFLAGS) -nostdlib $(OBJDIR)/lib/entry.o $@.o -L$(OBJDIR)/lib $(USERLIBS:%=-l%) $(GCC_LIB)
|
||||
$(V)$(OBJDUMP) -S $@ > $@.asm
|
||||
$(V)$(NM) -n $@ > $@.sym
|
||||
$(V)$(LD) -o $@.debug $(ULDFLAGS) $(LDFLAGS) -nostdlib $(OBJDIR)/lib/entry.o $@.o -L$(OBJDIR)/lib $(USERLIBS:%=-l%) $(GCC_LIB)
|
||||
$(V)$(OBJDUMP) -S $@.debug > $@.asm
|
||||
$(V)$(NM) -n $@.debug > $@.sym
|
||||
$(V)$(OBJCOPY) -R .stab -R .stabstr --add-gnu-debuglink=$(basename $@.debug) $@.debug $@
|
||||
|
||||
|
36
user/cat.c
Normal file
36
user/cat.c
Normal file
@ -0,0 +1,36 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
char buf[8192];
|
||||
|
||||
void
|
||||
cat(int f, char *s)
|
||||
{
|
||||
long n;
|
||||
int r;
|
||||
|
||||
while ((n = read(f, buf, (long)sizeof(buf))) > 0)
|
||||
if ((r = write(1, buf, n)) != n)
|
||||
panic("write error copying %s: %e", s, r);
|
||||
if (n < 0)
|
||||
panic("error reading %s: %e", s, n);
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int f, i;
|
||||
|
||||
binaryname = "cat";
|
||||
if (argc == 1)
|
||||
cat(0, "<stdin>");
|
||||
else
|
||||
for (i = 1; i < argc; i++) {
|
||||
f = open(argv[i], O_RDONLY);
|
||||
if (f < 0)
|
||||
printf("can't open %s: %e\n", argv[i], f);
|
||||
else {
|
||||
cat(f, argv[i]);
|
||||
close(f);
|
||||
}
|
||||
}
|
||||
}
|
21
user/echo.c
Normal file
21
user/echo.c
Normal file
@ -0,0 +1,21 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int i, nflag;
|
||||
|
||||
nflag = 0;
|
||||
if (argc > 1 && strcmp(argv[1], "-n") == 0) {
|
||||
nflag = 1;
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (i > 1)
|
||||
write(1, " ", 1);
|
||||
write(1, argv[i], strlen(argv[i]));
|
||||
}
|
||||
if (!nflag)
|
||||
write(1, "\n", 1);
|
||||
}
|
22
user/faultio.c
Normal file
22
user/faultio.c
Normal file
@ -0,0 +1,22 @@
|
||||
// test user-level fault handler -- alloc pages to fix faults
|
||||
|
||||
#include <inc/lib.h>
|
||||
#include <inc/x86.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int x, r;
|
||||
int nsecs = 1;
|
||||
int secno = 0;
|
||||
int diskno = 1;
|
||||
|
||||
if (read_eflags() & FL_IOPL_3)
|
||||
cprintf("eflags wrong\n");
|
||||
|
||||
// this outb to select disk 1 should result in a general protection
|
||||
// fault, because user-level code shouldn't be able to use the io space.
|
||||
outb(0x1F6, 0xE0 | (1<<4));
|
||||
|
||||
cprintf("%s: made it here --- bug\n");
|
||||
}
|
29
user/icode.c
Normal file
29
user/icode.c
Normal file
@ -0,0 +1,29 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int fd, n, r;
|
||||
char buf[512+1];
|
||||
|
||||
binaryname = "icode";
|
||||
|
||||
cprintf("icode startup\n");
|
||||
|
||||
cprintf("icode: open /motd\n");
|
||||
if ((fd = open("/motd", O_RDONLY)) < 0)
|
||||
panic("icode: open /motd: %e", fd);
|
||||
|
||||
cprintf("icode: read /motd\n");
|
||||
while ((n = read(fd, buf, sizeof buf-1)) > 0)
|
||||
sys_cputs(buf, n);
|
||||
|
||||
cprintf("icode: close /motd\n");
|
||||
close(fd);
|
||||
|
||||
cprintf("icode: spawn /init\n");
|
||||
if ((r = spawnl("/init", "init", "initarg1", "initarg2", (char*)0)) < 0)
|
||||
panic("icode: spawn /init: %e", r);
|
||||
|
||||
cprintf("icode: exiting\n");
|
||||
}
|
69
user/init.c
Normal file
69
user/init.c
Normal file
@ -0,0 +1,69 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
struct {
|
||||
char msg1[5000];
|
||||
char msg2[1000];
|
||||
} data = {
|
||||
"this is initialized data",
|
||||
"so is this"
|
||||
};
|
||||
|
||||
char bss[6000];
|
||||
|
||||
int
|
||||
sum(const char *s, int n)
|
||||
{
|
||||
int i, tot = 0;
|
||||
for (i = 0; i < n; i++)
|
||||
tot ^= i * s[i];
|
||||
return tot;
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int i, r, x, want;
|
||||
char args[256];
|
||||
|
||||
cprintf("init: running\n");
|
||||
|
||||
want = 0xf989e;
|
||||
if ((x = sum((char*)&data, sizeof data)) != want)
|
||||
cprintf("init: data is not initialized: got sum %08x wanted %08x\n",
|
||||
x, want);
|
||||
else
|
||||
cprintf("init: data seems okay\n");
|
||||
if ((x = sum(bss, sizeof bss)) != 0)
|
||||
cprintf("bss is not initialized: wanted sum 0 got %08x\n", x);
|
||||
else
|
||||
cprintf("init: bss seems okay\n");
|
||||
|
||||
// output in one syscall per line to avoid output interleaving
|
||||
strcat(args, "init: args:");
|
||||
for (i = 0; i < argc; i++) {
|
||||
strcat(args, " '");
|
||||
strcat(args, argv[i]);
|
||||
strcat(args, "'");
|
||||
}
|
||||
cprintf("%s\n", args);
|
||||
|
||||
cprintf("init: running sh\n");
|
||||
|
||||
// being run directly from kernel, so no file descriptors open yet
|
||||
close(0);
|
||||
if ((r = opencons()) < 0)
|
||||
panic("opencons: %e", r);
|
||||
if (r != 0)
|
||||
panic("first opencons used fd %d", r);
|
||||
if ((r = dup(0, 1)) < 0)
|
||||
panic("dup: %e", r);
|
||||
while (1) {
|
||||
cprintf("init: starting sh\n");
|
||||
r = spawnl("/sh", "sh", (char*)0);
|
||||
if (r < 0) {
|
||||
cprintf("init: spawn sh: %e\n", r);
|
||||
continue;
|
||||
}
|
||||
wait(r);
|
||||
}
|
||||
}
|
27
user/initsh.c
Normal file
27
user/initsh.c
Normal file
@ -0,0 +1,27 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int i, r, x, want;
|
||||
|
||||
cprintf("initsh: running sh\n");
|
||||
|
||||
// being run directly from kernel, so no file descriptors open yet
|
||||
close(0);
|
||||
if ((r = opencons()) < 0)
|
||||
panic("opencons: %e", r);
|
||||
if (r != 0)
|
||||
panic("first opencons used fd %d", r);
|
||||
if ((r = dup(0, 1)) < 0)
|
||||
panic("dup: %e", r);
|
||||
while (1) {
|
||||
cprintf("init: starting sh\n");
|
||||
r = spawnl("/sh", "sh", (char*)0);
|
||||
if (r < 0) {
|
||||
cprintf("init: spawn sh: %e\n", r);
|
||||
continue;
|
||||
}
|
||||
wait(r);
|
||||
}
|
||||
}
|
91
user/ls.c
Normal file
91
user/ls.c
Normal file
@ -0,0 +1,91 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
int flag[256];
|
||||
|
||||
void lsdir(const char*, const char*);
|
||||
void ls1(const char*, bool, off_t, const char*);
|
||||
|
||||
void
|
||||
ls(const char *path, const char *prefix)
|
||||
{
|
||||
int r;
|
||||
struct Stat st;
|
||||
|
||||
if ((r = stat(path, &st)) < 0)
|
||||
panic("stat %s: %e", path, r);
|
||||
if (st.st_isdir && !flag['d'])
|
||||
lsdir(path, prefix);
|
||||
else
|
||||
ls1(0, st.st_isdir, st.st_size, path);
|
||||
}
|
||||
|
||||
void
|
||||
lsdir(const char *path, const char *prefix)
|
||||
{
|
||||
int fd, n;
|
||||
struct File f;
|
||||
|
||||
if ((fd = open(path, O_RDONLY)) < 0)
|
||||
panic("open %s: %e", path, fd);
|
||||
while ((n = readn(fd, &f, sizeof f)) == sizeof f)
|
||||
if (f.f_name[0])
|
||||
ls1(prefix, f.f_type==FTYPE_DIR, f.f_size, f.f_name);
|
||||
if (n > 0)
|
||||
panic("short read in directory %s", path);
|
||||
if (n < 0)
|
||||
panic("error reading directory %s: %e", path, n);
|
||||
}
|
||||
|
||||
void
|
||||
ls1(const char *prefix, bool isdir, off_t size, const char *name)
|
||||
{
|
||||
const char *sep;
|
||||
|
||||
if(flag['l'])
|
||||
printf("%11d %c ", size, isdir ? 'd' : '-');
|
||||
if(prefix) {
|
||||
if (prefix[0] && prefix[strlen(prefix)-1] != '/')
|
||||
sep = "/";
|
||||
else
|
||||
sep = "";
|
||||
printf("%s%s", prefix, sep);
|
||||
}
|
||||
printf("%s", name);
|
||||
if(flag['F'] && isdir)
|
||||
printf("/");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
printf("usage: ls [-dFl] [file...]\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
struct Argstate args;
|
||||
|
||||
argstart(&argc, argv, &args);
|
||||
while ((i = argnext(&args)) >= 0)
|
||||
switch (i) {
|
||||
case 'd':
|
||||
case 'F':
|
||||
case 'l':
|
||||
flag[i]++;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
|
||||
if (argc == 1)
|
||||
ls("/", "");
|
||||
else {
|
||||
for (i = 1; i < argc; i++)
|
||||
ls(argv[i], argv[i]);
|
||||
}
|
||||
}
|
||||
|
35
user/lsfd.c
Normal file
35
user/lsfd.c
Normal file
@ -0,0 +1,35 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
cprintf("usage: lsfd [-1]\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int i, usefprint = 0;
|
||||
struct Stat st;
|
||||
struct Argstate args;
|
||||
|
||||
argstart(&argc, argv, &args);
|
||||
while ((i = argnext(&args)) >= 0)
|
||||
if (i == '1')
|
||||
usefprint = 1;
|
||||
else
|
||||
usage();
|
||||
|
||||
for (i = 0; i < 32; i++)
|
||||
if (fstat(i, &st) >= 0) {
|
||||
if (usefprint)
|
||||
fprintf(1, "fd %d: name %s isdir %d size %d dev %s\n",
|
||||
i, st.st_name, st.st_isdir,
|
||||
st.st_size, st.st_dev->dev_name);
|
||||
else
|
||||
cprintf("fd %d: name %s isdir %d size %d dev %s\n",
|
||||
i, st.st_name, st.st_isdir,
|
||||
st.st_size, st.st_dev->dev_name);
|
||||
}
|
||||
}
|
47
user/num.c
Normal file
47
user/num.c
Normal file
@ -0,0 +1,47 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
int bol = 1;
|
||||
int line = 0;
|
||||
|
||||
void
|
||||
num(int f, const char *s)
|
||||
{
|
||||
long n;
|
||||
int r;
|
||||
char c;
|
||||
|
||||
while ((n = read(f, &c, 1)) > 0) {
|
||||
if (bol) {
|
||||
printf("%5d ", ++line);
|
||||
bol = 0;
|
||||
}
|
||||
if ((r = write(1, &c, 1)) != 1)
|
||||
panic("write error copying %s: %e", s, r);
|
||||
if (c == '\n')
|
||||
bol = 1;
|
||||
}
|
||||
if (n < 0)
|
||||
panic("error reading %s: %e", s, n);
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int f, i;
|
||||
|
||||
binaryname = "num";
|
||||
if (argc == 1)
|
||||
num(0, "<stdin>");
|
||||
else
|
||||
for (i = 1; i < argc; i++) {
|
||||
f = open(argv[i], O_RDONLY);
|
||||
if (f < 0)
|
||||
panic("can't open %s: %e", argv[i], f);
|
||||
else {
|
||||
num(f, argv[i]);
|
||||
close(f);
|
||||
}
|
||||
}
|
||||
exit();
|
||||
}
|
||||
|
76
user/primespipe.c
Normal file
76
user/primespipe.c
Normal file
@ -0,0 +1,76 @@
|
||||
// Concurrent version of prime sieve of Eratosthenes.
|
||||
// Invented by Doug McIlroy, inventor of Unix pipes.
|
||||
// See http://swtch.com/~rsc/thread/.
|
||||
// The picture halfway down the page and the text surrounding it
|
||||
// explain what's going on here.
|
||||
//
|
||||
// Since NENV is 1024, we can print 1022 primes before running out.
|
||||
// The remaining two environments are the integer generator at the bottom
|
||||
// of main and user/idle.
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
unsigned
|
||||
primeproc(int fd)
|
||||
{
|
||||
int i, id, p, pfd[2], wfd, r;
|
||||
|
||||
// fetch a prime from our left neighbor
|
||||
top:
|
||||
if ((r = readn(fd, &p, 4)) != 4)
|
||||
panic("primeproc could not read initial prime: %d, %e", r, r >= 0 ? 0 : r);
|
||||
|
||||
cprintf("%d\n", p);
|
||||
|
||||
// fork a right neighbor to continue the chain
|
||||
if ((i=pipe(pfd)) < 0)
|
||||
panic("pipe: %e", i);
|
||||
if ((id = fork()) < 0)
|
||||
panic("fork: %e", id);
|
||||
if (id == 0) {
|
||||
close(fd);
|
||||
close(pfd[1]);
|
||||
fd = pfd[0];
|
||||
goto top;
|
||||
}
|
||||
|
||||
close(pfd[0]);
|
||||
wfd = pfd[1];
|
||||
|
||||
// filter out multiples of our prime
|
||||
for (;;) {
|
||||
if ((r=readn(fd, &i, 4)) != 4)
|
||||
panic("primeproc %d readn %d %d %e", p, fd, r, r >= 0 ? 0 : r);
|
||||
if (i%p)
|
||||
if ((r=write(wfd, &i, 4)) != 4)
|
||||
panic("primeproc %d write: %d %e", p, r, r >= 0 ? 0 : r);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int i, id, p[2], r;
|
||||
|
||||
binaryname = "primespipe";
|
||||
|
||||
if ((i=pipe(p)) < 0)
|
||||
panic("pipe: %e", i);
|
||||
|
||||
// fork the first prime process in the chain
|
||||
if ((id=fork()) < 0)
|
||||
panic("fork: %e", id);
|
||||
|
||||
if (id == 0) {
|
||||
close(p[1]);
|
||||
primeproc(p[0]);
|
||||
}
|
||||
|
||||
close(p[0]);
|
||||
|
||||
// feed all the integers through
|
||||
for (i=2;; i++)
|
||||
if ((r=write(p[1], &i, 4)) != 4)
|
||||
panic("generator write: %d, %e", r, r >= 0 ? 0 : r);
|
||||
}
|
||||
|
322
user/sh.c
Normal file
322
user/sh.c
Normal file
@ -0,0 +1,322 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
#define BUFSIZ 1024 /* Find the buffer overrun bug! */
|
||||
int debug = 0;
|
||||
|
||||
|
||||
// gettoken(s, 0) prepares gettoken for subsequent calls and returns 0.
|
||||
// gettoken(0, token) parses a shell token from the previously set string,
|
||||
// null-terminates that token, stores the token pointer in '*token',
|
||||
// and returns a token ID (0, '<', '>', '|', or 'w').
|
||||
// Subsequent calls to 'gettoken(0, token)' will return subsequent
|
||||
// tokens from the string.
|
||||
int gettoken(char *s, char **token);
|
||||
|
||||
|
||||
// Parse a shell command from string 's' and execute it.
|
||||
// Do not return until the shell command is finished.
|
||||
// runcmd() is called in a forked child,
|
||||
// so it's OK to manipulate file descriptor state.
|
||||
#define MAXARGS 16
|
||||
void
|
||||
runcmd(char* s)
|
||||
{
|
||||
char *argv[MAXARGS], *t, argv0buf[BUFSIZ];
|
||||
int argc, c, i, r, p[2], fd, pipe_child;
|
||||
|
||||
pipe_child = 0;
|
||||
gettoken(s, 0);
|
||||
|
||||
again:
|
||||
argc = 0;
|
||||
while (1) {
|
||||
switch ((c = gettoken(0, &t))) {
|
||||
|
||||
case 'w': // Add an argument
|
||||
if (argc == MAXARGS) {
|
||||
cprintf("too many arguments\n");
|
||||
exit();
|
||||
}
|
||||
argv[argc++] = t;
|
||||
break;
|
||||
|
||||
case '<': // Input redirection
|
||||
// Grab the filename from the argument list
|
||||
if (gettoken(0, &t) != 'w') {
|
||||
cprintf("syntax error: < not followed by word\n");
|
||||
exit();
|
||||
}
|
||||
// Open 't' for reading as file descriptor 0
|
||||
// (which environments use as standard input).
|
||||
// We can't open a file onto a particular descriptor,
|
||||
// so open the file as 'fd',
|
||||
// then check whether 'fd' is 0.
|
||||
// If not, dup 'fd' onto file descriptor 0,
|
||||
// then close the original 'fd'.
|
||||
|
||||
// LAB 5: Your code here.
|
||||
panic("< redirection not implemented");
|
||||
break;
|
||||
|
||||
case '>': // Output redirection
|
||||
// Grab the filename from the argument list
|
||||
if (gettoken(0, &t) != 'w') {
|
||||
cprintf("syntax error: > not followed by word\n");
|
||||
exit();
|
||||
}
|
||||
if ((fd = open(t, O_WRONLY|O_CREAT|O_TRUNC)) < 0) {
|
||||
cprintf("open %s for write: %e", t, fd);
|
||||
exit();
|
||||
}
|
||||
if (fd != 1) {
|
||||
dup(fd, 1);
|
||||
close(fd);
|
||||
}
|
||||
break;
|
||||
|
||||
case '|': // Pipe
|
||||
if ((r = pipe(p)) < 0) {
|
||||
cprintf("pipe: %e", r);
|
||||
exit();
|
||||
}
|
||||
if (debug)
|
||||
cprintf("PIPE: %d %d\n", p[0], p[1]);
|
||||
if ((r = fork()) < 0) {
|
||||
cprintf("fork: %e", r);
|
||||
exit();
|
||||
}
|
||||
if (r == 0) {
|
||||
if (p[0] != 0) {
|
||||
dup(p[0], 0);
|
||||
close(p[0]);
|
||||
}
|
||||
close(p[1]);
|
||||
goto again;
|
||||
} else {
|
||||
pipe_child = r;
|
||||
if (p[1] != 1) {
|
||||
dup(p[1], 1);
|
||||
close(p[1]);
|
||||
}
|
||||
close(p[0]);
|
||||
goto runit;
|
||||
}
|
||||
panic("| not implemented");
|
||||
break;
|
||||
|
||||
case 0: // String is complete
|
||||
// Run the current command!
|
||||
goto runit;
|
||||
|
||||
default:
|
||||
panic("bad return %d from gettoken", c);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
runit:
|
||||
// Return immediately if command line was empty.
|
||||
if(argc == 0) {
|
||||
if (debug)
|
||||
cprintf("EMPTY COMMAND\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up command line.
|
||||
// Read all commands from the filesystem: add an initial '/' to
|
||||
// the command name.
|
||||
// This essentially acts like 'PATH=/'.
|
||||
if (argv[0][0] != '/') {
|
||||
argv0buf[0] = '/';
|
||||
strcpy(argv0buf + 1, argv[0]);
|
||||
argv[0] = argv0buf;
|
||||
}
|
||||
argv[argc] = 0;
|
||||
|
||||
// Print the command.
|
||||
if (debug) {
|
||||
cprintf("[%08x] SPAWN:", thisenv->env_id);
|
||||
for (i = 0; argv[i]; i++)
|
||||
cprintf(" %s", argv[i]);
|
||||
cprintf("\n");
|
||||
}
|
||||
|
||||
// Spawn the command!
|
||||
if ((r = spawn(argv[0], (const char**) argv)) < 0)
|
||||
cprintf("spawn %s: %e\n", argv[0], r);
|
||||
|
||||
// In the parent, close all file descriptors and wait for the
|
||||
// spawned command to exit.
|
||||
close_all();
|
||||
if (r >= 0) {
|
||||
if (debug)
|
||||
cprintf("[%08x] WAIT %s %08x\n", thisenv->env_id, argv[0], r);
|
||||
wait(r);
|
||||
if (debug)
|
||||
cprintf("[%08x] wait finished\n", thisenv->env_id);
|
||||
}
|
||||
|
||||
// If we were the left-hand part of a pipe,
|
||||
// wait for the right-hand part to finish.
|
||||
if (pipe_child) {
|
||||
if (debug)
|
||||
cprintf("[%08x] WAIT pipe_child %08x\n", thisenv->env_id, pipe_child);
|
||||
wait(pipe_child);
|
||||
if (debug)
|
||||
cprintf("[%08x] wait finished\n", thisenv->env_id);
|
||||
}
|
||||
|
||||
// Done!
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
// Get the next token from string s.
|
||||
// Set *p1 to the beginning of the token and *p2 just past the token.
|
||||
// Returns
|
||||
// 0 for end-of-string;
|
||||
// < for <;
|
||||
// > for >;
|
||||
// | for |;
|
||||
// w for a word.
|
||||
//
|
||||
// Eventually (once we parse the space where the \0 will go),
|
||||
// words get nul-terminated.
|
||||
#define WHITESPACE " \t\r\n"
|
||||
#define SYMBOLS "<|>&;()"
|
||||
|
||||
int
|
||||
_gettoken(char *s, char **p1, char **p2)
|
||||
{
|
||||
int t;
|
||||
|
||||
if (s == 0) {
|
||||
if (debug > 1)
|
||||
cprintf("GETTOKEN NULL\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (debug > 1)
|
||||
cprintf("GETTOKEN: %s\n", s);
|
||||
|
||||
*p1 = 0;
|
||||
*p2 = 0;
|
||||
|
||||
while (strchr(WHITESPACE, *s))
|
||||
*s++ = 0;
|
||||
if (*s == 0) {
|
||||
if (debug > 1)
|
||||
cprintf("EOL\n");
|
||||
return 0;
|
||||
}
|
||||
if (strchr(SYMBOLS, *s)) {
|
||||
t = *s;
|
||||
*p1 = s;
|
||||
*s++ = 0;
|
||||
*p2 = s;
|
||||
if (debug > 1)
|
||||
cprintf("TOK %c\n", t);
|
||||
return t;
|
||||
}
|
||||
*p1 = s;
|
||||
while (*s && !strchr(WHITESPACE SYMBOLS, *s))
|
||||
s++;
|
||||
*p2 = s;
|
||||
if (debug > 1) {
|
||||
t = **p2;
|
||||
**p2 = 0;
|
||||
cprintf("WORD: %s\n", *p1);
|
||||
**p2 = t;
|
||||
}
|
||||
return 'w';
|
||||
}
|
||||
|
||||
int
|
||||
gettoken(char *s, char **p1)
|
||||
{
|
||||
static int c, nc;
|
||||
static char* np1, *np2;
|
||||
|
||||
if (s) {
|
||||
nc = _gettoken(s, &np1, &np2);
|
||||
return 0;
|
||||
}
|
||||
c = nc;
|
||||
*p1 = np1;
|
||||
nc = _gettoken(np2, &np1, &np2);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
cprintf("usage: sh [-dix] [command-file]\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int r, interactive, echocmds;
|
||||
struct Argstate args;
|
||||
|
||||
interactive = '?';
|
||||
echocmds = 0;
|
||||
argstart(&argc, argv, &args);
|
||||
while ((r = argnext(&args)) >= 0)
|
||||
switch (r) {
|
||||
case 'd':
|
||||
debug++;
|
||||
break;
|
||||
case 'i':
|
||||
interactive = 1;
|
||||
break;
|
||||
case 'x':
|
||||
echocmds = 1;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
|
||||
if (argc > 2)
|
||||
usage();
|
||||
if (argc == 2) {
|
||||
close(0);
|
||||
if ((r = open(argv[1], O_RDONLY)) < 0)
|
||||
panic("open %s: %e", argv[1], r);
|
||||
assert(r == 0);
|
||||
}
|
||||
if (interactive == '?')
|
||||
interactive = iscons(0);
|
||||
|
||||
while (1) {
|
||||
char *buf;
|
||||
|
||||
buf = readline(interactive ? "$ " : NULL);
|
||||
if (buf == NULL) {
|
||||
if (debug)
|
||||
cprintf("EXITING\n");
|
||||
exit(); // end of file
|
||||
}
|
||||
if (debug)
|
||||
cprintf("LINE: %s\n", buf);
|
||||
if (buf[0] == '#')
|
||||
continue;
|
||||
if (echocmds)
|
||||
printf("# %s\n", buf);
|
||||
if (debug)
|
||||
cprintf("BEFORE FORK\n");
|
||||
if ((r = fork()) < 0)
|
||||
panic("fork: %e", r);
|
||||
if (debug)
|
||||
cprintf("FORK: %d\n", r);
|
||||
if (r == 0) {
|
||||
runcmd(buf);
|
||||
exit();
|
||||
} else
|
||||
wait(r);
|
||||
}
|
||||
}
|
||||
|
10
user/spawnfaultio.c
Normal file
10
user/spawnfaultio.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int r;
|
||||
cprintf("i am parent environment %08x\n", thisenv->env_id);
|
||||
if ((r = spawnl("faultio", "faultio", 0)) < 0)
|
||||
panic("spawn(faultio) failed: %e", r);
|
||||
}
|
10
user/spawnhello.c
Normal file
10
user/spawnhello.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int r;
|
||||
cprintf("i am parent environment %08x\n", thisenv->env_id);
|
||||
if ((r = spawnl("hello", "hello", 0)) < 0)
|
||||
panic("spawn(hello) failed: %e", r);
|
||||
}
|
10
user/spawninit.c
Normal file
10
user/spawninit.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int r;
|
||||
cprintf("i am parent environment %08x\n", thisenv->env_id);
|
||||
if ((r = spawnl("init", "init", "one", "two", 0)) < 0)
|
||||
panic("spawnl(init) failed: %e", r);
|
||||
}
|
38
user/testfdsharing.c
Normal file
38
user/testfdsharing.c
Normal file
@ -0,0 +1,38 @@
|
||||
#include <inc/x86.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
char buf[512], buf2[512];
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int fd, r, n, n2;
|
||||
|
||||
if ((fd = open("motd", O_RDONLY)) < 0)
|
||||
panic("open motd: %e", fd);
|
||||
seek(fd, 0);
|
||||
if ((n = readn(fd, buf, sizeof buf)) <= 0)
|
||||
panic("readn: %e", n);
|
||||
|
||||
if ((r = fork()) < 0)
|
||||
panic("fork: %e", r);
|
||||
if (r == 0) {
|
||||
seek(fd, 0);
|
||||
cprintf("going to read in child (might page fault if your sharing is buggy)\n");
|
||||
if ((n2 = readn(fd, buf2, sizeof buf2)) != n)
|
||||
panic("read in parent got %d, read in child got %d", n, n2);
|
||||
if (memcmp(buf, buf2, n) != 0)
|
||||
panic("read in parent got different bytes from read in child");
|
||||
cprintf("read in child succeeded\n");
|
||||
seek(fd, 0);
|
||||
close(fd);
|
||||
exit();
|
||||
}
|
||||
wait(r);
|
||||
if ((n2 = readn(fd, buf2, sizeof buf2)) != n)
|
||||
panic("read in parent got %d, then got %d", n, n2);
|
||||
cprintf("read in parent succeeded\n");
|
||||
close(fd);
|
||||
|
||||
breakpoint();
|
||||
}
|
128
user/testfile.c
Normal file
128
user/testfile.c
Normal file
@ -0,0 +1,128 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
const char *msg = "This is the NEW message of the day!\n\n";
|
||||
|
||||
#define FVA ((struct Fd*)0xCCCCC000)
|
||||
|
||||
static int
|
||||
xopen(const char *path, int mode)
|
||||
{
|
||||
extern union Fsipc fsipcbuf;
|
||||
envid_t fsenv;
|
||||
|
||||
strcpy(fsipcbuf.open.req_path, path);
|
||||
fsipcbuf.open.req_omode = mode;
|
||||
|
||||
fsenv = ipc_find_env(ENV_TYPE_FS);
|
||||
ipc_send(fsenv, FSREQ_OPEN, &fsipcbuf, PTE_P | PTE_W | PTE_U);
|
||||
return ipc_recv(NULL, FVA, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int r, f, i;
|
||||
struct Fd *fd;
|
||||
struct Fd fdcopy;
|
||||
struct Stat st;
|
||||
char buf[512];
|
||||
|
||||
// We open files manually first, to avoid the FD layer
|
||||
if ((r = xopen("/not-found", O_RDONLY)) < 0 && r != -E_NOT_FOUND)
|
||||
panic("serve_open /not-found: %e", r);
|
||||
else if (r >= 0)
|
||||
panic("serve_open /not-found succeeded!");
|
||||
|
||||
if ((r = xopen("/newmotd", O_RDONLY)) < 0)
|
||||
panic("serve_open /newmotd: %e", r);
|
||||
if (FVA->fd_dev_id != 'f' || FVA->fd_offset != 0 || FVA->fd_omode != O_RDONLY)
|
||||
panic("serve_open did not fill struct Fd correctly\n");
|
||||
cprintf("serve_open is good\n");
|
||||
|
||||
if ((r = devfile.dev_stat(FVA, &st)) < 0)
|
||||
panic("file_stat: %e", r);
|
||||
if (strlen(msg) != st.st_size)
|
||||
panic("file_stat returned size %d wanted %d\n", st.st_size, strlen(msg));
|
||||
cprintf("file_stat is good\n");
|
||||
|
||||
memset(buf, 0, sizeof buf);
|
||||
if ((r = devfile.dev_read(FVA, buf, sizeof buf)) < 0)
|
||||
panic("file_read: %e", r);
|
||||
if (strcmp(buf, msg) != 0)
|
||||
panic("file_read returned wrong data");
|
||||
cprintf("file_read is good\n");
|
||||
|
||||
if ((r = devfile.dev_close(FVA)) < 0)
|
||||
panic("file_close: %e", r);
|
||||
cprintf("file_close is good\n");
|
||||
|
||||
// We're about to unmap the FD, but still need a way to get
|
||||
// the stale filenum to serve_read, so we make a local copy.
|
||||
// The file server won't think it's stale until we unmap the
|
||||
// FD page.
|
||||
fdcopy = *FVA;
|
||||
sys_page_unmap(0, FVA);
|
||||
|
||||
if ((r = devfile.dev_read(&fdcopy, buf, sizeof buf)) != -E_INVAL)
|
||||
panic("serve_read does not handle stale fileids correctly: %e", r);
|
||||
cprintf("stale fileid is good\n");
|
||||
|
||||
// Try writing
|
||||
if ((r = xopen("/new-file", O_RDWR|O_CREAT)) < 0)
|
||||
panic("serve_open /new-file: %e", r);
|
||||
|
||||
if ((r = devfile.dev_write(FVA, msg, strlen(msg))) != strlen(msg))
|
||||
panic("file_write: %e", r);
|
||||
cprintf("file_write is good\n");
|
||||
|
||||
FVA->fd_offset = 0;
|
||||
memset(buf, 0, sizeof buf);
|
||||
if ((r = devfile.dev_read(FVA, buf, sizeof buf)) < 0)
|
||||
panic("file_read after file_write: %e", r);
|
||||
if (r != strlen(msg))
|
||||
panic("file_read after file_write returned wrong length: %d", r);
|
||||
if (strcmp(buf, msg) != 0)
|
||||
panic("file_read after file_write returned wrong data");
|
||||
cprintf("file_read after file_write is good\n");
|
||||
|
||||
// Now we'll try out open
|
||||
if ((r = open("/not-found", O_RDONLY)) < 0 && r != -E_NOT_FOUND)
|
||||
panic("open /not-found: %e", r);
|
||||
else if (r >= 0)
|
||||
panic("open /not-found succeeded!");
|
||||
|
||||
if ((r = open("/newmotd", O_RDONLY)) < 0)
|
||||
panic("open /newmotd: %e", r);
|
||||
fd = (struct Fd*) (0xD0000000 + r*PGSIZE);
|
||||
if (fd->fd_dev_id != 'f' || fd->fd_offset != 0 || fd->fd_omode != O_RDONLY)
|
||||
panic("open did not fill struct Fd correctly\n");
|
||||
cprintf("open is good\n");
|
||||
|
||||
// Try files with indirect blocks
|
||||
if ((f = open("/big", O_WRONLY|O_CREAT)) < 0)
|
||||
panic("creat /big: %e", f);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
for (i = 0; i < (NDIRECT*3)*BLKSIZE; i += sizeof(buf)) {
|
||||
*(int*)buf = i;
|
||||
if ((r = write(f, buf, sizeof(buf))) < 0)
|
||||
panic("write /big@%d: %e", i, r);
|
||||
}
|
||||
close(f);
|
||||
|
||||
if ((f = open("/big", O_RDONLY)) < 0)
|
||||
panic("open /big: %e", f);
|
||||
for (i = 0; i < (NDIRECT*3)*BLKSIZE; i += sizeof(buf)) {
|
||||
*(int*)buf = i;
|
||||
if ((r = readn(f, buf, sizeof(buf))) < 0)
|
||||
panic("read /big@%d: %e", i, r);
|
||||
if (r != sizeof(buf))
|
||||
panic("read /big from %d returned %d < %d bytes",
|
||||
i, r, sizeof(buf));
|
||||
if (*(int*)buf != i)
|
||||
panic("read /big from %d returned bad data %d",
|
||||
i, *(int*)buf);
|
||||
}
|
||||
close(f);
|
||||
cprintf("large file is good\n");
|
||||
}
|
||||
|
30
user/testkbd.c
Normal file
30
user/testkbd.c
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int i, r;
|
||||
|
||||
// Spin for a bit to let the console quiet
|
||||
for (i = 0; i < 10; ++i)
|
||||
sys_yield();
|
||||
|
||||
close(0);
|
||||
if ((r = opencons()) < 0)
|
||||
panic("opencons: %e", r);
|
||||
if (r != 0)
|
||||
panic("first opencons used fd %d", r);
|
||||
if ((r = dup(0, 1)) < 0)
|
||||
panic("dup: %e", r);
|
||||
|
||||
for(;;){
|
||||
char *buf;
|
||||
|
||||
buf = readline("Type a line: ");
|
||||
if (buf != NULL)
|
||||
fprintf(1, "%s\n", buf);
|
||||
else
|
||||
fprintf(1, "(end of file received)\n");
|
||||
}
|
||||
}
|
24
user/testmalloc.c
Normal file
24
user/testmalloc.c
Normal file
@ -0,0 +1,24 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
char *buf;
|
||||
int n;
|
||||
void *v;
|
||||
|
||||
while (1) {
|
||||
buf = readline("> ");
|
||||
if (buf == 0)
|
||||
exit();
|
||||
if (memcmp(buf, "free ", 5) == 0) {
|
||||
v = (void*) strtol(buf + 5, 0, 0);
|
||||
free(v);
|
||||
} else if (memcmp(buf, "malloc ", 7) == 0) {
|
||||
n = strtol(buf + 7, 0, 0);
|
||||
v = malloc(n);
|
||||
printf("\t0x%x\n", (uintptr_t) v);
|
||||
} else
|
||||
printf("?unknown command\n");
|
||||
}
|
||||
}
|
64
user/testpipe.c
Normal file
64
user/testpipe.c
Normal file
@ -0,0 +1,64 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
char *msg = "Now is the time for all good men to come to the aid of their party.";
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
char buf[100];
|
||||
int i, pid, p[2];
|
||||
|
||||
binaryname = "pipereadeof";
|
||||
|
||||
if ((i = pipe(p)) < 0)
|
||||
panic("pipe: %e", i);
|
||||
|
||||
if ((pid = fork()) < 0)
|
||||
panic("fork: %e", i);
|
||||
|
||||
if (pid == 0) {
|
||||
cprintf("[%08x] pipereadeof close %d\n", thisenv->env_id, p[1]);
|
||||
close(p[1]);
|
||||
cprintf("[%08x] pipereadeof readn %d\n", thisenv->env_id, p[0]);
|
||||
i = readn(p[0], buf, sizeof buf-1);
|
||||
if (i < 0)
|
||||
panic("read: %e", i);
|
||||
buf[i] = 0;
|
||||
if (strcmp(buf, msg) == 0)
|
||||
cprintf("\npipe read closed properly\n");
|
||||
else
|
||||
cprintf("\ngot %d bytes: %s\n", i, buf);
|
||||
exit();
|
||||
} else {
|
||||
cprintf("[%08x] pipereadeof close %d\n", thisenv->env_id, p[0]);
|
||||
close(p[0]);
|
||||
cprintf("[%08x] pipereadeof write %d\n", thisenv->env_id, p[1]);
|
||||
if ((i = write(p[1], msg, strlen(msg))) != strlen(msg))
|
||||
panic("write: %e", i);
|
||||
close(p[1]);
|
||||
}
|
||||
wait(pid);
|
||||
|
||||
binaryname = "pipewriteeof";
|
||||
if ((i = pipe(p)) < 0)
|
||||
panic("pipe: %e", i);
|
||||
|
||||
if ((pid = fork()) < 0)
|
||||
panic("fork: %e", i);
|
||||
|
||||
if (pid == 0) {
|
||||
close(p[0]);
|
||||
while (1) {
|
||||
cprintf(".");
|
||||
if (write(p[1], "x", 1) != 1)
|
||||
break;
|
||||
}
|
||||
cprintf("\npipe write closed properly\n");
|
||||
exit();
|
||||
}
|
||||
close(p[0]);
|
||||
close(p[1]);
|
||||
wait(pid);
|
||||
|
||||
cprintf("pipe tests passed\n");
|
||||
}
|
66
user/testpiperace.c
Normal file
66
user/testpiperace.c
Normal file
@ -0,0 +1,66 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int p[2], r, pid, i, max;
|
||||
void *va;
|
||||
struct Fd *fd;
|
||||
const volatile struct Env *kid;
|
||||
|
||||
cprintf("testing for dup race...\n");
|
||||
if ((r = pipe(p)) < 0)
|
||||
panic("pipe: %e", r);
|
||||
max = 200;
|
||||
if ((r = fork()) < 0)
|
||||
panic("fork: %e", r);
|
||||
if (r == 0) {
|
||||
close(p[1]);
|
||||
//
|
||||
// Now the ref count for p[0] will toggle between 2 and 3
|
||||
// as the parent dups and closes it (there's a close implicit in dup).
|
||||
//
|
||||
// The ref count for p[1] is 1.
|
||||
// Thus the ref count for the underlying pipe structure
|
||||
// will toggle between 3 and 4.
|
||||
//
|
||||
// If a clock interrupt catches close between unmapping
|
||||
// the pipe structure and unmapping the fd, we'll have
|
||||
// a ref count for p[0] of 3, a ref count for p[1] of 1,
|
||||
// and a ref count for the pipe structure of 3, which is
|
||||
// a no-no.
|
||||
//
|
||||
// If a clock interrupt catches dup between mapping the
|
||||
// fd and mapping the pipe structure, we'll have the same
|
||||
// ref counts, still a no-no.
|
||||
//
|
||||
for (i=0; i<max; i++) {
|
||||
if(pipeisclosed(p[0])){
|
||||
cprintf("RACE: pipe appears closed\n");
|
||||
exit();
|
||||
}
|
||||
sys_yield();
|
||||
}
|
||||
// do something to be not runnable besides exiting
|
||||
ipc_recv(0,0,0);
|
||||
}
|
||||
pid = r;
|
||||
cprintf("pid is %d\n", pid);
|
||||
va = 0;
|
||||
kid = &envs[ENVX(pid)];
|
||||
cprintf("kid is %d\n", kid-envs);
|
||||
dup(p[0], 10);
|
||||
while (kid->env_status == ENV_RUNNABLE)
|
||||
dup(p[0], 10);
|
||||
|
||||
cprintf("child done with loop\n");
|
||||
if (pipeisclosed(p[0]))
|
||||
panic("somehow the other end of p[0] got closed!");
|
||||
if ((r = fd_lookup(p[0], &fd)) < 0)
|
||||
panic("cannot look up p[0]: %e", r);
|
||||
va = fd2data(fd);
|
||||
if (pageref(va) != 3+1)
|
||||
cprintf("\nchild detected race\n");
|
||||
else
|
||||
cprintf("\nrace didn't happen\n", max);
|
||||
}
|
69
user/testpiperace2.c
Normal file
69
user/testpiperace2.c
Normal file
@ -0,0 +1,69 @@
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int p[2], r, i;
|
||||
struct Fd *fd;
|
||||
const volatile struct Env *kid;
|
||||
|
||||
cprintf("testing for pipeisclosed race...\n");
|
||||
if ((r = pipe(p)) < 0)
|
||||
panic("pipe: %e", r);
|
||||
if ((r = fork()) < 0)
|
||||
panic("fork: %e", r);
|
||||
if (r == 0) {
|
||||
// child just dups and closes repeatedly,
|
||||
// yielding so the parent can see
|
||||
// the fd state between the two.
|
||||
close(p[1]);
|
||||
for (i = 0; i < 200; i++) {
|
||||
if (i % 10 == 0)
|
||||
cprintf("%d.", i);
|
||||
// dup, then close. yield so that other guy will
|
||||
// see us while we're between them.
|
||||
dup(p[0], 10);
|
||||
sys_yield();
|
||||
close(10);
|
||||
sys_yield();
|
||||
}
|
||||
exit();
|
||||
}
|
||||
|
||||
// We hold both p[0] and p[1] open, so pipeisclosed should
|
||||
// never return false.
|
||||
//
|
||||
// Now the ref count for p[0] will toggle between 2 and 3
|
||||
// as the child dups and closes it.
|
||||
// The ref count for p[1] is 1.
|
||||
// Thus the ref count for the underlying pipe structure
|
||||
// will toggle between 3 and 4.
|
||||
//
|
||||
// If pipeisclosed checks pageref(p[0]) and gets 3, and
|
||||
// then the child closes, and then pipeisclosed checks
|
||||
// pageref(pipe structure) and gets 3, then it will return true
|
||||
// when it shouldn't.
|
||||
//
|
||||
// If pipeisclosed checks pageref(pipe structure) and gets 3,
|
||||
// and then the child dups, and then pipeisclosed checks
|
||||
// pageref(p[0]) and gets 3, then it will return true when
|
||||
// it shouldn't.
|
||||
//
|
||||
// So either way, pipeisclosed is going give a wrong answer.
|
||||
//
|
||||
kid = &envs[ENVX(r)];
|
||||
while (kid->env_status == ENV_RUNNABLE)
|
||||
if (pipeisclosed(p[0]) != 0) {
|
||||
cprintf("\nRACE: pipe appears closed\n");
|
||||
sys_env_destroy(r);
|
||||
exit();
|
||||
}
|
||||
cprintf("child done with loop\n");
|
||||
if (pipeisclosed(p[0]))
|
||||
panic("somehow the other end of p[0] got closed!");
|
||||
if ((r = fd_lookup(p[0], &fd)) < 0)
|
||||
panic("cannot look up p[0]: %e", r);
|
||||
(void) fd2data(fd);
|
||||
cprintf("race didn't happen\n");
|
||||
}
|
42
user/testptelibrary.c
Normal file
42
user/testptelibrary.c
Normal file
@ -0,0 +1,42 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
#define VA ((char *) 0xA0000000)
|
||||
const char *msg = "hello, world\n";
|
||||
const char *msg2 = "goodbye, world\n";
|
||||
|
||||
void childofspawn(void);
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (argc != 0)
|
||||
childofspawn();
|
||||
|
||||
if ((r = sys_page_alloc(0, VA, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
|
||||
panic("sys_page_alloc: %e", r);
|
||||
|
||||
// check fork
|
||||
if ((r = fork()) < 0)
|
||||
panic("fork: %e", r);
|
||||
if (r == 0) {
|
||||
strcpy(VA, msg);
|
||||
exit();
|
||||
}
|
||||
wait(r);
|
||||
cprintf("fork handles PTE_SHARE %s\n", strcmp(VA, msg) == 0 ? "right" : "wrong");
|
||||
|
||||
// check spawn
|
||||
if ((r = spawnl("/testptelibrary", "testptelibrary", "arg", 0)) < 0)
|
||||
panic("spawn: %e", r);
|
||||
wait(r);
|
||||
cprintf("spawn handles PTE_SHARE %s\n", strcmp(VA, msg2) == 0 ? "right" : "wrong");
|
||||
}
|
||||
|
||||
void
|
||||
childofspawn(void)
|
||||
{
|
||||
strcpy(VA, msg2);
|
||||
exit();
|
||||
}
|
45
user/testpteshare.c
Normal file
45
user/testpteshare.c
Normal file
@ -0,0 +1,45 @@
|
||||
#include <inc/x86.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
#define VA ((char *) 0xA0000000)
|
||||
const char *msg = "hello, world\n";
|
||||
const char *msg2 = "goodbye, world\n";
|
||||
|
||||
void childofspawn(void);
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (argc != 0)
|
||||
childofspawn();
|
||||
|
||||
if ((r = sys_page_alloc(0, VA, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
|
||||
panic("sys_page_alloc: %e", r);
|
||||
|
||||
// check fork
|
||||
if ((r = fork()) < 0)
|
||||
panic("fork: %e", r);
|
||||
if (r == 0) {
|
||||
strcpy(VA, msg);
|
||||
exit();
|
||||
}
|
||||
wait(r);
|
||||
cprintf("fork handles PTE_SHARE %s\n", strcmp(VA, msg) == 0 ? "right" : "wrong");
|
||||
|
||||
// check spawn
|
||||
if ((r = spawnl("/testpteshare", "testpteshare", "arg", 0)) < 0)
|
||||
panic("spawn: %e", r);
|
||||
wait(r);
|
||||
cprintf("spawn handles PTE_SHARE %s\n", strcmp(VA, msg2) == 0 ? "right" : "wrong");
|
||||
|
||||
breakpoint();
|
||||
}
|
||||
|
||||
void
|
||||
childofspawn(void)
|
||||
{
|
||||
strcpy(VA, msg2);
|
||||
exit();
|
||||
}
|
85
user/testshell.c
Normal file
85
user/testshell.c
Normal file
@ -0,0 +1,85 @@
|
||||
#include <inc/x86.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
void wrong(int, int, int);
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
char c1, c2;
|
||||
int r, rfd, wfd, kfd, n1, n2, off, nloff;
|
||||
int pfds[2];
|
||||
|
||||
close(0);
|
||||
close(1);
|
||||
opencons();
|
||||
opencons();
|
||||
|
||||
if ((rfd = open("testshell.sh", O_RDONLY)) < 0)
|
||||
panic("open testshell.sh: %e", rfd);
|
||||
if ((wfd = pipe(pfds)) < 0)
|
||||
panic("pipe: %e", wfd);
|
||||
wfd = pfds[1];
|
||||
|
||||
cprintf("running sh -x < testshell.sh | cat\n");
|
||||
if ((r = fork()) < 0)
|
||||
panic("fork: %e", r);
|
||||
if (r == 0) {
|
||||
dup(rfd, 0);
|
||||
dup(wfd, 1);
|
||||
close(rfd);
|
||||
close(wfd);
|
||||
if ((r = spawnl("/sh", "sh", "-x", 0)) < 0)
|
||||
panic("spawn: %e", r);
|
||||
close(0);
|
||||
close(1);
|
||||
wait(r);
|
||||
exit();
|
||||
}
|
||||
close(rfd);
|
||||
close(wfd);
|
||||
|
||||
rfd = pfds[0];
|
||||
if ((kfd = open("testshell.key", O_RDONLY)) < 0)
|
||||
panic("open testshell.key for reading: %e", kfd);
|
||||
|
||||
nloff = 0;
|
||||
for (off=0;; off++) {
|
||||
n1 = read(rfd, &c1, 1);
|
||||
n2 = read(kfd, &c2, 1);
|
||||
if (n1 < 0)
|
||||
panic("reading testshell.out: %e", n1);
|
||||
if (n2 < 0)
|
||||
panic("reading testshell.key: %e", n2);
|
||||
if (n1 == 0 && n2 == 0)
|
||||
break;
|
||||
if (n1 != 1 || n2 != 1 || c1 != c2)
|
||||
wrong(rfd, kfd, nloff);
|
||||
if (c1 == '\n')
|
||||
nloff = off+1;
|
||||
}
|
||||
cprintf("shell ran correctly\n");
|
||||
|
||||
breakpoint();
|
||||
}
|
||||
|
||||
void
|
||||
wrong(int rfd, int kfd, int off)
|
||||
{
|
||||
char buf[100];
|
||||
int n;
|
||||
|
||||
seek(rfd, off);
|
||||
seek(kfd, off);
|
||||
|
||||
cprintf("shell produced incorrect output.\n");
|
||||
cprintf("expected:\n===\n");
|
||||
while ((n = read(kfd, buf, sizeof buf-1)) > 0)
|
||||
sys_cputs(buf, n);
|
||||
cprintf("===\ngot:\n===\n");
|
||||
while ((n = read(rfd, buf, sizeof buf-1)) > 0)
|
||||
sys_cputs(buf, n);
|
||||
cprintf("===\n");
|
||||
exit();
|
||||
}
|
||||
|
40
user/writemotd.c
Normal file
40
user/writemotd.c
Normal file
@ -0,0 +1,40 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int rfd, wfd;
|
||||
char buf[512];
|
||||
int n, r;
|
||||
|
||||
if ((rfd = open("/newmotd", O_RDONLY)) < 0)
|
||||
panic("open /newmotd: %e", rfd);
|
||||
if ((wfd = open("/motd", O_RDWR)) < 0)
|
||||
panic("open /motd: %e", wfd);
|
||||
cprintf("file descriptors %d %d\n", rfd, wfd);
|
||||
if (rfd == wfd)
|
||||
panic("open /newmotd and /motd give same file descriptor");
|
||||
|
||||
cprintf("OLD MOTD\n===\n");
|
||||
while ((n = read(wfd, buf, sizeof buf-1)) > 0)
|
||||
sys_cputs(buf, n);
|
||||
cprintf("===\n");
|
||||
seek(wfd, 0);
|
||||
|
||||
if ((r = ftruncate(wfd, 0)) < 0)
|
||||
panic("truncate /motd: %e", r);
|
||||
|
||||
cprintf("NEW MOTD\n===\n");
|
||||
while ((n = read(rfd, buf, sizeof buf-1)) > 0) {
|
||||
sys_cputs(buf, n);
|
||||
if ((r = write(wfd, buf, n)) != n)
|
||||
panic("write /motd: %e", r);
|
||||
}
|
||||
cprintf("===\n");
|
||||
|
||||
if (n < 0)
|
||||
panic("read /newmotd: %e", n);
|
||||
|
||||
close(rfd);
|
||||
close(wfd);
|
||||
}
|
Loading…
Reference in New Issue
Block a user