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

View File

@ -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

View File

@ -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
View File

@ -0,0 +1,77 @@
OBJDIRS += fs
FSOFILES := $(OBJDIR)/fs/ide.o \
$(OBJDIR)/fs/bc.o \
$(OBJDIR)/fs/fs.o \
$(OBJDIR)/fs/serv.o \
$(OBJDIR)/fs/test.o \
USERAPPS := $(OBJDIR)/user/init
FSIMGTXTFILES := fs/newmotd \
fs/motd
USERAPPS := $(USERAPPS) \
$(OBJDIR)/user/cat \
$(OBJDIR)/user/echo \
$(OBJDIR)/user/init \
$(OBJDIR)/user/ls \
$(OBJDIR)/user/lsfd \
$(OBJDIR)/user/num \
$(OBJDIR)/user/forktree \
$(OBJDIR)/user/primes \
$(OBJDIR)/user/primespipe \
$(OBJDIR)/user/sh \
$(OBJDIR)/user/testfdsharing \
$(OBJDIR)/user/testkbd \
$(OBJDIR)/user/testpipe \
$(OBJDIR)/user/testpteshare \
$(OBJDIR)/user/testshell \
$(OBJDIR)/user/hello \
$(OBJDIR)/user/faultio \
FSIMGTXTFILES := $(FSIMGTXTFILES) \
fs/lorem \
fs/script \
fs/testshell.key \
fs/testshell.sh
FSIMGFILES := $(FSIMGTXTFILES) $(USERAPPS)
$(OBJDIR)/fs/%.o: fs/%.c fs/fs.h inc/lib.h $(OBJDIR)/.vars.USER_CFLAGS
@echo + cc[USER] $<
@mkdir -p $(@D)
$(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $<
$(OBJDIR)/fs/fs: $(FSOFILES) $(OBJDIR)/lib/entry.o $(OBJDIR)/lib/libjos.a user/user.ld
@echo + ld $@
$(V)mkdir -p $(@D)
$(V)$(LD) -o $@ $(ULDFLAGS) $(LDFLAGS) -nostdlib \
$(OBJDIR)/lib/entry.o $(FSOFILES) \
-L$(OBJDIR)/lib -ljos $(GCC_LIB)
$(V)$(OBJDUMP) -S $@ >$@.asm
# How to build the file system image
$(OBJDIR)/fs/fsformat: fs/fsformat.c
@echo + mk $(OBJDIR)/fs/fsformat
$(V)mkdir -p $(@D)
$(V)$(NCC) $(NATIVE_CFLAGS) -o $(OBJDIR)/fs/fsformat fs/fsformat.c
$(OBJDIR)/fs/clean-fs.img: $(OBJDIR)/fs/fsformat $(FSIMGFILES)
@echo + mk $(OBJDIR)/fs/clean-fs.img
$(V)mkdir -p $(@D)
$(V)$(OBJDIR)/fs/fsformat $(OBJDIR)/fs/clean-fs.img 1024 $(FSIMGFILES)
$(OBJDIR)/fs/fs.img: $(OBJDIR)/fs/clean-fs.img
@echo + cp $(OBJDIR)/fs/clean-fs.img $@
$(V)cp $(OBJDIR)/fs/clean-fs.img $@
all: $(OBJDIR)/fs/fs.img
#all: $(addsuffix .sym, $(USERAPPS))
#all: $(addsuffix .asm, $(USERAPPS))

151
fs/bc.c Normal file
View File

@ -0,0 +1,151 @@
#include "fs.h"
// Return the virtual address of this disk block.
void*
diskaddr(uint32_t blockno)
{
if (blockno == 0 || (super && blockno >= super->s_nblocks))
panic("bad block number %08x in diskaddr", blockno);
return (char*) (DISKMAP + blockno * BLKSIZE);
}
// Is this virtual address mapped?
bool
va_is_mapped(void *va)
{
return (uvpd[PDX(va)] & PTE_P) && (uvpt[PGNUM(va)] & PTE_P);
}
// Is this virtual address dirty?
bool
va_is_dirty(void *va)
{
return (uvpt[PGNUM(va)] & PTE_D) != 0;
}
// Fault any disk block that is read in to memory by
// loading it from disk.
static void
bc_pgfault(struct UTrapframe *utf)
{
void *addr = (void *) utf->utf_fault_va;
uint32_t blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE;
int r;
// Check that the fault was within the block cache region
if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE))
panic("page fault in FS: eip %08x, va %08x, err %04x",
utf->utf_eip, addr, utf->utf_err);
// Sanity check the block number.
if (super && blockno >= super->s_nblocks)
panic("reading non-existent block %08x\n", blockno);
// Allocate a page in the disk map region, read the contents
// of the block from the disk into that page.
// Hint: first round addr to page boundary. fs/ide.c has code to read
// the disk.
//
// LAB 5: you code here:
// Clear the dirty bit for the disk block page since we just read the
// block from disk
if ((r = sys_page_map(0, addr, 0, addr, uvpt[PGNUM(addr)] & PTE_SYSCALL)) < 0)
panic("in bc_pgfault, sys_page_map: %e", r);
// Check that the block we read was allocated. (exercise for
// the reader: why do we do this *after* reading the block
// in?)
if (bitmap && block_is_free(blockno))
panic("reading free block %08x\n", blockno);
}
// Flush the contents of the block containing VA out to disk if
// necessary, then clear the PTE_D bit using sys_page_map.
// If the block is not in the block cache or is not dirty, does
// nothing.
// Hint: Use va_is_mapped, va_is_dirty, and ide_write.
// Hint: Use the PTE_SYSCALL constant when calling sys_page_map.
// Hint: Don't forget to round addr down.
void
flush_block(void *addr)
{
uint32_t blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE;
if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE))
panic("flush_block of bad va %08x", addr);
// LAB 5: Your code here.
panic("flush_block not implemented");
}
// Test that the block cache works, by smashing the superblock and
// reading it back.
static void
check_bc(void)
{
struct Super backup;
// back up super block
memmove(&backup, diskaddr(1), sizeof backup);
// smash it
strcpy(diskaddr(1), "OOPS!\n");
flush_block(diskaddr(1));
assert(va_is_mapped(diskaddr(1)));
assert(!va_is_dirty(diskaddr(1)));
// clear it out
sys_page_unmap(0, diskaddr(1));
assert(!va_is_mapped(diskaddr(1)));
// read it back in
assert(strcmp(diskaddr(1), "OOPS!\n") == 0);
// fix it
memmove(diskaddr(1), &backup, sizeof backup);
flush_block(diskaddr(1));
// Now repeat the same experiment, but pass an unaligned address to
// flush_block.
// back up super block
memmove(&backup, diskaddr(1), sizeof backup);
// smash it
strcpy(diskaddr(1), "OOPS!\n");
// Pass an unaligned address to flush_block.
flush_block(diskaddr(1) + 20);
assert(va_is_mapped(diskaddr(1)));
// Skip the !va_is_dirty() check because it makes the bug somewhat
// obscure and hence harder to debug.
//assert(!va_is_dirty(diskaddr(1)));
// clear it out
sys_page_unmap(0, diskaddr(1));
assert(!va_is_mapped(diskaddr(1)));
// read it back in
assert(strcmp(diskaddr(1), "OOPS!\n") == 0);
// fix it
memmove(diskaddr(1), &backup, sizeof backup);
flush_block(diskaddr(1));
cprintf("block cache is good\n");
}
void
bc_init(void)
{
struct Super super;
set_pgfault_handler(bc_pgfault);
check_bc();
// cache the super block by reading it once
memmove(&super, diskaddr(1), sizeof super);
}

456
fs/fs.c Normal file
View File

@ -0,0 +1,456 @@
#include <inc/string.h>
#include <inc/partition.h>
#include "fs.h"
// --------------------------------------------------------------
// Super block
// --------------------------------------------------------------
// Validate the file system super-block.
void
check_super(void)
{
if (super->s_magic != FS_MAGIC)
panic("bad file system magic number");
if (super->s_nblocks > DISKSIZE/BLKSIZE)
panic("file system is too large");
cprintf("superblock is good\n");
}
// --------------------------------------------------------------
// Free block bitmap
// --------------------------------------------------------------
// Check to see if the block bitmap indicates that block 'blockno' is free.
// Return 1 if the block is free, 0 if not.
bool
block_is_free(uint32_t blockno)
{
if (super == 0 || blockno >= super->s_nblocks)
return 0;
if (bitmap[blockno / 32] & (1 << (blockno % 32)))
return 1;
return 0;
}
// Mark a block free in the bitmap
void
free_block(uint32_t blockno)
{
// Blockno zero is the null pointer of block numbers.
if (blockno == 0)
panic("attempt to free zero block");
bitmap[blockno/32] |= 1<<(blockno%32);
}
// Search the bitmap for a free block and allocate it. When you
// allocate a block, immediately flush the changed bitmap block
// to disk.
//
// Return block number allocated on success,
// -E_NO_DISK if we are out of blocks.
//
// Hint: use free_block as an example for manipulating the bitmap.
int
alloc_block(void)
{
// The bitmap consists of one or more blocks. A single bitmap block
// contains the in-use bits for BLKBITSIZE blocks. There are
// super->s_nblocks blocks in the disk altogether.
// LAB 5: Your code here.
panic("alloc_block not implemented");
return -E_NO_DISK;
}
// Validate the file system bitmap.
//
// Check that all reserved blocks -- 0, 1, and the bitmap blocks themselves --
// are all marked as in-use.
void
check_bitmap(void)
{
uint32_t i;
// Make sure all bitmap blocks are marked in-use
for (i = 0; i * BLKBITSIZE < super->s_nblocks; i++)
assert(!block_is_free(2+i));
// Make sure the reserved and root blocks are marked in-use.
assert(!block_is_free(0));
assert(!block_is_free(1));
cprintf("bitmap is good\n");
}
// --------------------------------------------------------------
// File system structures
// --------------------------------------------------------------
// Initialize the file system
void
fs_init(void)
{
static_assert(sizeof(struct File) == 256);
// Find a JOS disk. Use the second IDE disk (number 1) if available
if (ide_probe_disk1())
ide_set_disk(1);
else
ide_set_disk(0);
bc_init();
// Set "super" to point to the super block.
super = diskaddr(1);
check_super();
// Set "bitmap" to the beginning of the first bitmap block.
bitmap = diskaddr(2);
check_bitmap();
}
// Find the disk block number slot for the 'filebno'th block in file 'f'.
// Set '*ppdiskbno' to point to that slot.
// The slot will be one of the f->f_direct[] entries,
// or an entry in the indirect block.
// When 'alloc' is set, this function will allocate an indirect block
// if necessary.
//
// Returns:
// 0 on success (but note that *ppdiskbno might equal 0).
// -E_NOT_FOUND if the function needed to allocate an indirect block, but
// alloc was 0.
// -E_NO_DISK if there's no space on the disk for an indirect block.
// -E_INVAL if filebno is out of range (it's >= NDIRECT + NINDIRECT).
//
// Analogy: This is like pgdir_walk for files.
// Hint: Don't forget to clear any block you allocate.
static int
file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool alloc)
{
// LAB 5: Your code here.
panic("file_block_walk not implemented");
}
// Set *blk to the address in memory where the filebno'th
// block of file 'f' would be mapped.
//
// Returns 0 on success, < 0 on error. Errors are:
// -E_NO_DISK if a block needed to be allocated but the disk is full.
// -E_INVAL if filebno is out of range.
//
// Hint: Use file_block_walk and alloc_block.
int
file_get_block(struct File *f, uint32_t filebno, char **blk)
{
// LAB 5: Your code here.
panic("file_get_block not implemented");
}
// Try to find a file named "name" in dir. If so, set *file to it.
//
// Returns 0 and sets *file on success, < 0 on error. Errors are:
// -E_NOT_FOUND if the file is not found
static int
dir_lookup(struct File *dir, const char *name, struct File **file)
{
int r;
uint32_t i, j, nblock;
char *blk;
struct File *f;
// Search dir for name.
// We maintain the invariant that the size of a directory-file
// is always a multiple of the file system's block size.
assert((dir->f_size % BLKSIZE) == 0);
nblock = dir->f_size / BLKSIZE;
for (i = 0; i < nblock; i++) {
if ((r = file_get_block(dir, i, &blk)) < 0)
return r;
f = (struct File*) blk;
for (j = 0; j < BLKFILES; j++)
if (strcmp(f[j].f_name, name) == 0) {
*file = &f[j];
return 0;
}
}
return -E_NOT_FOUND;
}
// Set *file to point at a free File structure in dir. The caller is
// responsible for filling in the File fields.
static int
dir_alloc_file(struct File *dir, struct File **file)
{
int r;
uint32_t nblock, i, j;
char *blk;
struct File *f;
assert((dir->f_size % BLKSIZE) == 0);
nblock = dir->f_size / BLKSIZE;
for (i = 0; i < nblock; i++) {
if ((r = file_get_block(dir, i, &blk)) < 0)
return r;
f = (struct File*) blk;
for (j = 0; j < BLKFILES; j++)
if (f[j].f_name[0] == '\0') {
*file = &f[j];
return 0;
}
}
dir->f_size += BLKSIZE;
if ((r = file_get_block(dir, i, &blk)) < 0)
return r;
f = (struct File*) blk;
*file = &f[0];
return 0;
}
// Skip over slashes.
static const char*
skip_slash(const char *p)
{
while (*p == '/')
p++;
return p;
}
// Evaluate a path name, starting at the root.
// On success, set *pf to the file we found
// and set *pdir to the directory the file is in.
// If we cannot find the file but find the directory
// it should be in, set *pdir and copy the final path
// element into lastelem.
static int
walk_path(const char *path, struct File **pdir, struct File **pf, char *lastelem)
{
const char *p;
char name[MAXNAMELEN];
struct File *dir, *f;
int r;
// if (*path != '/')
// return -E_BAD_PATH;
path = skip_slash(path);
f = &super->s_root;
dir = 0;
name[0] = 0;
if (pdir)
*pdir = 0;
*pf = 0;
while (*path != '\0') {
dir = f;
p = path;
while (*path != '/' && *path != '\0')
path++;
if (path - p >= MAXNAMELEN)
return -E_BAD_PATH;
memmove(name, p, path - p);
name[path - p] = '\0';
path = skip_slash(path);
if (dir->f_type != FTYPE_DIR)
return -E_NOT_FOUND;
if ((r = dir_lookup(dir, name, &f)) < 0) {
if (r == -E_NOT_FOUND && *path == '\0') {
if (pdir)
*pdir = dir;
if (lastelem)
strcpy(lastelem, name);
*pf = 0;
}
return r;
}
}
if (pdir)
*pdir = dir;
*pf = f;
return 0;
}
// --------------------------------------------------------------
// File operations
// --------------------------------------------------------------
// Create "path". On success set *pf to point at the file and return 0.
// On error return < 0.
int
file_create(const char *path, struct File **pf)
{
char name[MAXNAMELEN];
int r;
struct File *dir, *f;
if ((r = walk_path(path, &dir, &f, name)) == 0)
return -E_FILE_EXISTS;
if (r != -E_NOT_FOUND || dir == 0)
return r;
if ((r = dir_alloc_file(dir, &f)) < 0)
return r;
strcpy(f->f_name, name);
*pf = f;
file_flush(dir);
return 0;
}
// Open "path". On success set *pf to point at the file and return 0.
// On error return < 0.
int
file_open(const char *path, struct File **pf)
{
return walk_path(path, 0, pf, 0);
}
// Read count bytes from f into buf, starting from seek position
// offset. This meant to mimic the standard pread function.
// Returns the number of bytes read, < 0 on error.
ssize_t
file_read(struct File *f, void *buf, size_t count, off_t offset)
{
int r, bn;
off_t pos;
char *blk;
if (offset >= f->f_size)
return 0;
count = MIN(count, f->f_size - offset);
for (pos = offset; pos < offset + count; ) {
if ((r = file_get_block(f, pos / BLKSIZE, &blk)) < 0)
return r;
bn = MIN(BLKSIZE - pos % BLKSIZE, offset + count - pos);
memmove(buf, blk + pos % BLKSIZE, bn);
pos += bn;
buf += bn;
}
return count;
}
// Write count bytes from buf into f, starting at seek position
// offset. This is meant to mimic the standard pwrite function.
// Extends the file if necessary.
// Returns the number of bytes written, < 0 on error.
int
file_write(struct File *f, const void *buf, size_t count, off_t offset)
{
int r, bn;
off_t pos;
char *blk;
// Extend file if necessary
if (offset + count > f->f_size)
if ((r = file_set_size(f, offset + count)) < 0)
return r;
for (pos = offset; pos < offset + count; ) {
if ((r = file_get_block(f, pos / BLKSIZE, &blk)) < 0)
return r;
bn = MIN(BLKSIZE - pos % BLKSIZE, offset + count - pos);
memmove(blk + pos % BLKSIZE, buf, bn);
pos += bn;
buf += bn;
}
return count;
}
// Remove a block from file f. If it's not there, just silently succeed.
// Returns 0 on success, < 0 on error.
static int
file_free_block(struct File *f, uint32_t filebno)
{
int r;
uint32_t *ptr;
if ((r = file_block_walk(f, filebno, &ptr, 0)) < 0)
return r;
if (*ptr) {
free_block(*ptr);
*ptr = 0;
}
return 0;
}
// Remove any blocks currently used by file 'f',
// but not necessary for a file of size 'newsize'.
// For both the old and new sizes, figure out the number of blocks required,
// and then clear the blocks from new_nblocks to old_nblocks.
// If the new_nblocks is no more than NDIRECT, and the indirect block has
// been allocated (f->f_indirect != 0), then free the indirect block too.
// (Remember to clear the f->f_indirect pointer so you'll know
// whether it's valid!)
// Do not change f->f_size.
static void
file_truncate_blocks(struct File *f, off_t newsize)
{
int r;
uint32_t bno, old_nblocks, new_nblocks;
old_nblocks = (f->f_size + BLKSIZE - 1) / BLKSIZE;
new_nblocks = (newsize + BLKSIZE - 1) / BLKSIZE;
for (bno = new_nblocks; bno < old_nblocks; bno++)
if ((r = file_free_block(f, bno)) < 0)
cprintf("warning: file_free_block: %e", r);
if (new_nblocks <= NDIRECT && f->f_indirect) {
free_block(f->f_indirect);
f->f_indirect = 0;
}
}
// Set the size of file f, truncating or extending as necessary.
int
file_set_size(struct File *f, off_t newsize)
{
if (f->f_size > newsize)
file_truncate_blocks(f, newsize);
f->f_size = newsize;
flush_block(f);
return 0;
}
// Flush the contents and metadata of file f out to disk.
// Loop over all the blocks in file.
// Translate the file block number into a disk block number
// and then check whether that disk block is dirty. If so, write it out.
void
file_flush(struct File *f)
{
int i;
uint32_t *pdiskbno;
for (i = 0; i < (f->f_size + BLKSIZE - 1) / BLKSIZE; i++) {
if (file_block_walk(f, i, &pdiskbno, 0) < 0 ||
pdiskbno == NULL || *pdiskbno == 0)
continue;
flush_block(diskaddr(*pdiskbno));
}
flush_block(f);
if (f->f_indirect)
flush_block(diskaddr(f->f_indirect));
}
// Sync the entire file system. A big hammer.
void
fs_sync(void)
{
int i;
for (i = 1; i < super->s_nblocks; i++)
flush_block(diskaddr(i));
}

49
fs/fs.h Normal file
View File

@ -0,0 +1,49 @@
#include <inc/fs.h>
#include <inc/lib.h>
#define SECTSIZE 512 // bytes per disk sector
#define BLKSECTS (BLKSIZE / SECTSIZE) // sectors per block
/* Disk block n, when in memory, is mapped into the file system
* server's address space at DISKMAP + (n*BLKSIZE). */
#define DISKMAP 0x10000000
/* Maximum disk size we can handle (3GB) */
#define DISKSIZE 0xC0000000
struct Super *super; // superblock
uint32_t *bitmap; // bitmap blocks mapped in memory
/* ide.c */
bool ide_probe_disk1(void);
void ide_set_disk(int diskno);
void ide_set_partition(uint32_t first_sect, uint32_t nsect);
int ide_read(uint32_t secno, void *dst, size_t nsecs);
int ide_write(uint32_t secno, const void *src, size_t nsecs);
/* bc.c */
void* diskaddr(uint32_t blockno);
bool va_is_mapped(void *va);
bool va_is_dirty(void *va);
void flush_block(void *addr);
void bc_init(void);
/* fs.c */
void fs_init(void);
int file_get_block(struct File *f, uint32_t file_blockno, char **pblk);
int file_create(const char *path, struct File **f);
int file_open(const char *path, struct File **f);
ssize_t file_read(struct File *f, void *buf, size_t count, off_t offset);
int file_write(struct File *f, const void *buf, size_t count, off_t offset);
int file_set_size(struct File *f, off_t newsize);
void file_flush(struct File *f);
int file_remove(const char *path);
void fs_sync(void);
/* int map_block(uint32_t); */
bool block_is_free(uint32_t blockno);
int alloc_block(void);
/* test.c */
void fs_test(void);

244
fs/fsformat.c Normal file
View File

@ -0,0 +1,244 @@
/*
* JOS file system format
*/
// We don't actually want to define off_t!
#define off_t xxx_off_t
#define bool xxx_bool
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#undef off_t
#undef bool
// Prevent inc/types.h, included from inc/fs.h,
// from attempting to redefine types defined in the host's inttypes.h.
#define JOS_INC_TYPES_H
// Typedef the types that inc/mmu.h needs.
typedef uint32_t physaddr_t;
typedef uint32_t off_t;
typedef int bool;
#include <inc/mmu.h>
#include <inc/fs.h>
#define ROUNDUP(n, v) ((n) - 1 + (v) - ((n) - 1) % (v))
#define MAX_DIR_ENTS 128
struct Dir
{
struct File *f;
struct File *ents;
int n;
};
uint32_t nblocks;
char *diskmap, *diskpos;
struct Super *super;
uint32_t *bitmap;
void
panic(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputc('\n', stderr);
abort();
}
void
readn(int f, void *out, size_t n)
{
size_t p = 0;
while (p < n) {
ssize_t m = read(f, out + p, n - p);
if (m < 0)
panic("read: %s", strerror(errno));
if (m == 0)
panic("read: Unexpected EOF");
p += m;
}
}
uint32_t
blockof(void *pos)
{
return ((char*)pos - diskmap) / BLKSIZE;
}
void *
alloc(uint32_t bytes)
{
void *start = diskpos;
diskpos += ROUNDUP(bytes, BLKSIZE);
if (blockof(diskpos) >= nblocks)
panic("out of disk blocks");
return start;
}
void
opendisk(const char *name)
{
int r, diskfd, nbitblocks;
if ((diskfd = open(name, O_RDWR | O_CREAT, 0666)) < 0)
panic("open %s: %s", name, strerror(errno));
if ((r = ftruncate(diskfd, 0)) < 0
|| (r = ftruncate(diskfd, nblocks * BLKSIZE)) < 0)
panic("truncate %s: %s", name, strerror(errno));
if ((diskmap = mmap(NULL, nblocks * BLKSIZE, PROT_READ|PROT_WRITE,
MAP_SHARED, diskfd, 0)) == MAP_FAILED)
panic("mmap %s: %s", name, strerror(errno));
close(diskfd);
diskpos = diskmap;
alloc(BLKSIZE);
super = alloc(BLKSIZE);
super->s_magic = FS_MAGIC;
super->s_nblocks = nblocks;
super->s_root.f_type = FTYPE_DIR;
strcpy(super->s_root.f_name, "/");
nbitblocks = (nblocks + BLKBITSIZE - 1) / BLKBITSIZE;
bitmap = alloc(nbitblocks * BLKSIZE);
memset(bitmap, 0xFF, nbitblocks * BLKSIZE);
}
void
finishdisk(void)
{
int r, i;
for (i = 0; i < blockof(diskpos); ++i)
bitmap[i/32] &= ~(1<<(i%32));
if ((r = msync(diskmap, nblocks * BLKSIZE, MS_SYNC)) < 0)
panic("msync: %s", strerror(errno));
}
void
finishfile(struct File *f, uint32_t start, uint32_t len)
{
int i;
f->f_size = len;
len = ROUNDUP(len, BLKSIZE);
for (i = 0; i < len / BLKSIZE && i < NDIRECT; ++i)
f->f_direct[i] = start + i;
if (i == NDIRECT) {
uint32_t *ind = alloc(BLKSIZE);
f->f_indirect = blockof(ind);
for (; i < len / BLKSIZE; ++i)
ind[i - NDIRECT] = start + i;
}
}
void
startdir(struct File *f, struct Dir *dout)
{
dout->f = f;
dout->ents = malloc(MAX_DIR_ENTS * sizeof *dout->ents);
dout->n = 0;
}
struct File *
diradd(struct Dir *d, uint32_t type, const char *name)
{
struct File *out = &d->ents[d->n++];
if (d->n > MAX_DIR_ENTS)
panic("too many directory entries");
strcpy(out->f_name, name);
out->f_type = type;
return out;
}
void
finishdir(struct Dir *d)
{
int size = d->n * sizeof(struct File);
struct File *start = alloc(size);
memmove(start, d->ents, size);
finishfile(d->f, blockof(start), ROUNDUP(size, BLKSIZE));
free(d->ents);
d->ents = NULL;
}
void
writefile(struct Dir *dir, const char *name)
{
int r, fd;
struct File *f;
struct stat st;
const char *last;
char *start;
if ((fd = open(name, O_RDONLY)) < 0)
panic("open %s: %s", name, strerror(errno));
if ((r = fstat(fd, &st)) < 0)
panic("stat %s: %s", name, strerror(errno));
if (!S_ISREG(st.st_mode))
panic("%s is not a regular file", name);
if (st.st_size >= MAXFILESIZE)
panic("%s too large", name);
last = strrchr(name, '/');
if (last)
last++;
else
last = name;
f = diradd(dir, FTYPE_REG, last);
start = alloc(st.st_size);
readn(fd, start, st.st_size);
finishfile(f, blockof(start), st.st_size);
close(fd);
}
void
usage(void)
{
fprintf(stderr, "Usage: fsformat fs.img NBLOCKS files...\n");
exit(2);
}
int
main(int argc, char **argv)
{
int i;
char *s;
struct Dir root;
assert(BLKSIZE % sizeof(struct File) == 0);
if (argc < 3)
usage();
nblocks = strtol(argv[2], &s, 0);
if (*s || s == argv[2] || nblocks < 2 || nblocks > 1024)
usage();
opendisk(argv[1]);
startdir(&super->s_root, &root);
for (i = 3; i < argc; i++)
writefile(&root, argv[i]);
finishdir(&root);
finishdisk();
return 0;
}

112
fs/ide.c Normal file
View File

@ -0,0 +1,112 @@
/*
* Minimal PIO-based (non-interrupt-driven) IDE driver code.
* For information about what all this IDE/ATA magic means,
* see the materials available on the class references page.
*/
#include "fs.h"
#include <inc/x86.h>
#define IDE_BSY 0x80
#define IDE_DRDY 0x40
#define IDE_DF 0x20
#define IDE_ERR 0x01
static int diskno = 1;
static int
ide_wait_ready(bool check_error)
{
int r;
while (((r = inb(0x1F7)) & (IDE_BSY|IDE_DRDY)) != IDE_DRDY)
/* do nothing */;
if (check_error && (r & (IDE_DF|IDE_ERR)) != 0)
return -1;
return 0;
}
bool
ide_probe_disk1(void)
{
int r, x;
// wait for Device 0 to be ready
ide_wait_ready(0);
// switch to Device 1
outb(0x1F6, 0xE0 | (1<<4));
// check for Device 1 to be ready for a while
for (x = 0;
x < 1000 && ((r = inb(0x1F7)) & (IDE_BSY|IDE_DF|IDE_ERR)) != 0;
x++)
/* do nothing */;
// switch back to Device 0
outb(0x1F6, 0xE0 | (0<<4));
cprintf("Device 1 presence: %d\n", (x < 1000));
return (x < 1000);
}
void
ide_set_disk(int d)
{
if (d != 0 && d != 1)
panic("bad disk number");
diskno = d;
}
int
ide_read(uint32_t secno, void *dst, size_t nsecs)
{
int r;
assert(nsecs <= 256);
ide_wait_ready(0);
outb(0x1F2, nsecs);
outb(0x1F3, secno & 0xFF);
outb(0x1F4, (secno >> 8) & 0xFF);
outb(0x1F5, (secno >> 16) & 0xFF);
outb(0x1F6, 0xE0 | ((diskno&1)<<4) | ((secno>>24)&0x0F));
outb(0x1F7, 0x20); // CMD 0x20 means read sector
for (; nsecs > 0; nsecs--, dst += SECTSIZE) {
if ((r = ide_wait_ready(1)) < 0)
return r;
insl(0x1F0, dst, SECTSIZE/4);
}
return 0;
}
int
ide_write(uint32_t secno, const void *src, size_t nsecs)
{
int r;
assert(nsecs <= 256);
ide_wait_ready(0);
outb(0x1F2, nsecs);
outb(0x1F3, secno & 0xFF);
outb(0x1F4, (secno >> 8) & 0xFF);
outb(0x1F5, (secno >> 16) & 0xFF);
outb(0x1F6, 0xE0 | ((diskno&1)<<4) | ((secno>>24)&0x0F));
outb(0x1F7, 0x30); // CMD 0x30 means write sector
for (; nsecs > 0; nsecs--, src += SECTSIZE) {
if ((r = ide_wait_ready(1)) < 0)
return r;
outsl(0x1F0, src, SECTSIZE/4);
}
return 0;
}

12
fs/lorem Normal file
View File

@ -0,0 +1,12 @@
Lorem ipsum dolor sit amet, consectetur
adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit
in voluptate velit esse cillum dolore eu
fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim
id est laborum.

4
fs/motd Normal file
View File

@ -0,0 +1,4 @@
This is /motd, the message of the day.
Welcome to the JOS kernel, now with a file system!

2
fs/newmotd Normal file
View File

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

5
fs/script Normal file
View File

@ -0,0 +1,5 @@
echo This is from the script.
cat lorem | num | cat
echo These are my file descriptors.
lsfd -1
echo This is the end of the script.

345
fs/serv.c Normal file
View File

@ -0,0 +1,345 @@
/*
* File system server main loop -
* serves IPC requests from other environments.
*/
#include <inc/x86.h>
#include <inc/string.h>
#include "fs.h"
#define debug 0
// The file system server maintains three structures
// for each open file.
//
// 1. The on-disk 'struct File' is mapped into the part of memory
// that maps the disk. This memory is kept private to the file
// server.
// 2. Each open file has a 'struct Fd' as well, which sort of
// corresponds to a Unix file descriptor. This 'struct Fd' is kept
// on *its own page* in memory, and it is shared with any
// environments that have the file open.
// 3. 'struct OpenFile' links these other two structures, and is kept
// private to the file server. The server maintains an array of
// all open files, indexed by "file ID". (There can be at most
// MAXOPEN files open concurrently.) The client uses file IDs to
// communicate with the server. File IDs are a lot like
// environment IDs in the kernel. Use openfile_lookup to translate
// file IDs to struct OpenFile.
struct OpenFile {
uint32_t o_fileid; // file id
struct File *o_file; // mapped descriptor for open file
int o_mode; // open mode
struct Fd *o_fd; // Fd page
};
// Max number of open files in the file system at once
#define MAXOPEN 1024
#define FILEVA 0xD0000000
// initialize to force into data section
struct OpenFile opentab[MAXOPEN] = {
{ 0, 0, 1, 0 }
};
// Virtual address at which to receive page mappings containing client requests.
union Fsipc *fsreq = (union Fsipc *)0x0ffff000;
void
serve_init(void)
{
int i;
uintptr_t va = FILEVA;
for (i = 0; i < MAXOPEN; i++) {
opentab[i].o_fileid = i;
opentab[i].o_fd = (struct Fd*) va;
va += PGSIZE;
}
}
// Allocate an open file.
int
openfile_alloc(struct OpenFile **o)
{
int i, r;
// Find an available open-file table entry
for (i = 0; i < MAXOPEN; i++) {
switch (pageref(opentab[i].o_fd)) {
case 0:
if ((r = sys_page_alloc(0, opentab[i].o_fd, PTE_P|PTE_U|PTE_W)) < 0)
return r;
/* fall through */
case 1:
opentab[i].o_fileid += MAXOPEN;
*o = &opentab[i];
memset(opentab[i].o_fd, 0, PGSIZE);
return (*o)->o_fileid;
}
}
return -E_MAX_OPEN;
}
// Look up an open file for envid.
int
openfile_lookup(envid_t envid, uint32_t fileid, struct OpenFile **po)
{
struct OpenFile *o;
o = &opentab[fileid % MAXOPEN];
if (pageref(o->o_fd) <= 1 || o->o_fileid != fileid)
return -E_INVAL;
*po = o;
return 0;
}
// Open req->req_path in mode req->req_omode, storing the Fd page and
// permissions to return to the calling environment in *pg_store and
// *perm_store respectively.
int
serve_open(envid_t envid, struct Fsreq_open *req,
void **pg_store, int *perm_store)
{
char path[MAXPATHLEN];
struct File *f;
int fileid;
int r;
struct OpenFile *o;
if (debug)
cprintf("serve_open %08x %s 0x%x\n", envid, req->req_path, req->req_omode);
// Copy in the path, making sure it's null-terminated
memmove(path, req->req_path, MAXPATHLEN);
path[MAXPATHLEN-1] = 0;
// Find an open file ID
if ((r = openfile_alloc(&o)) < 0) {
if (debug)
cprintf("openfile_alloc failed: %e", r);
return r;
}
fileid = r;
// Open the file
if (req->req_omode & O_CREAT) {
if ((r = file_create(path, &f)) < 0) {
if (!(req->req_omode & O_EXCL) && r == -E_FILE_EXISTS)
goto try_open;
if (debug)
cprintf("file_create failed: %e", r);
return r;
}
} else {
try_open:
if ((r = file_open(path, &f)) < 0) {
if (debug)
cprintf("file_open failed: %e", r);
return r;
}
}
// Truncate
if (req->req_omode & O_TRUNC) {
if ((r = file_set_size(f, 0)) < 0) {
if (debug)
cprintf("file_set_size failed: %e", r);
return r;
}
}
if ((r = file_open(path, &f)) < 0) {
if (debug)
cprintf("file_open failed: %e", r);
return r;
}
// Save the file pointer
o->o_file = f;
// Fill out the Fd structure
o->o_fd->fd_file.id = o->o_fileid;
o->o_fd->fd_omode = req->req_omode & O_ACCMODE;
o->o_fd->fd_dev_id = devfile.dev_id;
o->o_mode = req->req_omode;
if (debug)
cprintf("sending success, page %08x\n", (uintptr_t) o->o_fd);
// Share the FD page with the caller by setting *pg_store,
// store its permission in *perm_store
*pg_store = o->o_fd;
*perm_store = PTE_P|PTE_U|PTE_W|PTE_SHARE;
return 0;
}
// Set the size of req->req_fileid to req->req_size bytes, truncating
// or extending the file as necessary.
int
serve_set_size(envid_t envid, struct Fsreq_set_size *req)
{
struct OpenFile *o;
int r;
if (debug)
cprintf("serve_set_size %08x %08x %08x\n", envid, req->req_fileid, req->req_size);
// Every file system IPC call has the same general structure.
// Here's how it goes.
// First, use openfile_lookup to find the relevant open file.
// On failure, return the error code to the client with ipc_send.
if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0)
return r;
// Second, call the relevant file system function (from fs/fs.c).
// On failure, return the error code to the client.
return file_set_size(o->o_file, req->req_size);
}
// Read at most ipc->read.req_n bytes from the current seek position
// in ipc->read.req_fileid. Return the bytes read from the file to
// the caller in ipc->readRet, then update the seek position. Returns
// the number of bytes successfully read, or < 0 on error.
int
serve_read(envid_t envid, union Fsipc *ipc)
{
struct Fsreq_read *req = &ipc->read;
struct Fsret_read *ret = &ipc->readRet;
if (debug)
cprintf("serve_read %08x %08x %08x\n", envid, req->req_fileid, req->req_n);
// Lab 5: Your code here:
return 0;
}
// Write req->req_n bytes from req->req_buf to req_fileid, starting at
// the current seek position, and update the seek position
// accordingly. Extend the file if necessary. Returns the number of
// bytes written, or < 0 on error.
int
serve_write(envid_t envid, struct Fsreq_write *req)
{
if (debug)
cprintf("serve_write %08x %08x %08x\n", envid, req->req_fileid, req->req_n);
// LAB 5: Your code here.
panic("serve_write not implemented");
}
// Stat ipc->stat.req_fileid. Return the file's struct Stat to the
// caller in ipc->statRet.
int
serve_stat(envid_t envid, union Fsipc *ipc)
{
struct Fsreq_stat *req = &ipc->stat;
struct Fsret_stat *ret = &ipc->statRet;
struct OpenFile *o;
int r;
if (debug)
cprintf("serve_stat %08x %08x\n", envid, req->req_fileid);
if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0)
return r;
strcpy(ret->ret_name, o->o_file->f_name);
ret->ret_size = o->o_file->f_size;
ret->ret_isdir = (o->o_file->f_type == FTYPE_DIR);
return 0;
}
// Flush all data and metadata of req->req_fileid to disk.
int
serve_flush(envid_t envid, struct Fsreq_flush *req)
{
struct OpenFile *o;
int r;
if (debug)
cprintf("serve_flush %08x %08x\n", envid, req->req_fileid);
if ((r = openfile_lookup(envid, req->req_fileid, &o)) < 0)
return r;
file_flush(o->o_file);
return 0;
}
int
serve_sync(envid_t envid, union Fsipc *req)
{
fs_sync();
return 0;
}
typedef int (*fshandler)(envid_t envid, union Fsipc *req);
fshandler handlers[] = {
// Open is handled specially because it passes pages
/* [FSREQ_OPEN] = (fshandler)serve_open, */
[FSREQ_READ] = serve_read,
[FSREQ_STAT] = serve_stat,
[FSREQ_FLUSH] = (fshandler)serve_flush,
[FSREQ_WRITE] = (fshandler)serve_write,
[FSREQ_SET_SIZE] = (fshandler)serve_set_size,
[FSREQ_SYNC] = serve_sync
};
void
serve(void)
{
uint32_t req, whom;
int perm, r;
void *pg;
while (1) {
perm = 0;
req = ipc_recv((int32_t *) &whom, fsreq, &perm);
if (debug)
cprintf("fs req %d from %08x [page %08x: %s]\n",
req, whom, uvpt[PGNUM(fsreq)], fsreq);
// All requests must contain an argument page
if (!(perm & PTE_P)) {
cprintf("Invalid request from %08x: no argument page\n",
whom);
continue; // just leave it hanging...
}
pg = NULL;
if (req == FSREQ_OPEN) {
r = serve_open(whom, (struct Fsreq_open*)fsreq, &pg, &perm);
} else if (req < ARRAY_SIZE(handlers) && handlers[req]) {
r = handlers[req](whom, fsreq);
} else {
cprintf("Invalid request code %d from %08x\n", req, whom);
r = -E_INVAL;
}
ipc_send(whom, r, pg, perm);
sys_page_unmap(0, fsreq);
}
}
void
umain(int argc, char **argv)
{
static_assert(sizeof(struct File) == 256);
binaryname = "fs";
cprintf("FS is running\n");
// Check that we are able to do I/O
outw(0x8A00, 0x8A00);
cprintf("FS can do I/O\n");
serve_init();
fs_init();
fs_test();
serve();
}

7
fs/testshell.sh Normal file
View File

@ -0,0 +1,7 @@
echo hello world | cat
cat lorem
cat lorem |num
cat lorem |num |num |num |num |num
lsfd -1
cat script
sh <script

112
grade-lab5 Executable file
View 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
View 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

View File

@ -41,6 +41,7 @@ enum {
// Special environment types
enum EnvType {
ENV_TYPE_USER = 0,
ENV_TYPE_FS, // File system server
};
struct Env {

View File

@ -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
View 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
View 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 */

View File

@ -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
View 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

View File

@ -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,

View File

@ -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))

View File

@ -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));
}

View File

@ -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);

View File

@ -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();
}

View File

@ -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)
{

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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
View 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;
}

View File

@ -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;
}

View File

@ -4,6 +4,7 @@
void
exit(void)
{
close_all();
sys_env_destroy(0);
}

320
lib/fd.c Normal file
View 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
View 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
View 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
View 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
View 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));
}

View File

@ -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",
};
/*

View File

@ -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
View 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;
}

View File

@ -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
View 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();
}

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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);
}