From eaea18cb9cbb86018dae8f1decfa217ecbe85fa5 Mon Sep 17 00:00:00 2001
From: rsc <rsc>
Date: Wed, 22 Aug 2007 06:01:32 +0000
Subject: [PATCH] PDF at http://am.lcs.mit.edu/~rsc/xv6.pdf

Various changes made while offline.

 + bwrite sector argument is redundant; use b->sector.
 + reformatting of files for nicer PDF page breaks
 + distinguish between locked, unlocked inodes in type signatures
 + change FD_FILE to FD_INODE
 + move userinit (nee proc0init) to proc.c
 + move ROOTDEV to param.h
 + always parenthesize sizeof argument
---
 BUGS        |  46 +-----
 bio.c       |   5 +-
 bootmain.c  |  55 +++----
 defs.h      |  28 ++--
 exec.c      |  22 ++-
 file.c      |  38 ++---
 file.h      |   4 +-
 fs.c        | 411 +++++++++++++++-------------------------------------
 fsvar.h     |   6 +-
 main.c      |  82 +++--------
 param.h     |   1 +
 proc.c      | 141 ++++++++++--------
 proc.h      |   4 +-
 runoff.list |  12 +-
 runoff.spec |  59 ++++++--
 runoff1     |   6 +
 sh.c        |   2 +-
 show1       |   2 +-
 spinlock.c  |  15 +-
 sysfile.c   | 314 ++++++++++++++++++++++++++-------------
 trap.c      |   8 +-
 trapasm.S   |   3 -
 ulib.c      |  12 ++
 user.h      |   1 +
 vectors.pl  |  21 +++
 25 files changed, 633 insertions(+), 665 deletions(-)

diff --git a/BUGS b/BUGS
index 1d2bd37..16e23ae 100644
--- a/BUGS
+++ b/BUGS
@@ -4,47 +4,11 @@ proc.c:
 	and be able to break out with an error return.
 	it is better if you check *before* sleep.
 
-	can swap procdump up after proc_kill
-	and then have proc_exit and proc_wait on same sheet
-
-	sched ->  switch2scheduler?  or just switch?
-
-	factor out switching and scheduling code from process code
-
-	shuffle for formatting
-
 syscall.c:
-	cannot convince runoff1 to split the extern lists to fill previous page completely.
-
-fs.c: split all name operations off in name.c?  (starting with namei but
-      wdir keep in fs.c)
-	locking?
-	shuffle for formatting
-
-pipe.c:
-	more comments?
-	comment how functions get called?
-
-sysfile.c:
-	is the sys_exec picture upside down?
-	can sys_open and sys_exec be simplified any?
-
-general:
-	sizeof parens?
-
-bio.c:
-	decide odd or even
-	bwrite doesn't need a second argument
-
-file.c:
-	move fileincref onto page 1?
-
-L=$HOME/mit/l
-(for i in *.c; do xoc -x xgnu -x ./nodecleq.zeta --typesonly $i; done) 2>&1 | grep warning
-
-saw random sharedfd failure.
-
-why does fdalloc consume reference?
+	cannot convince runoff1 to split the extern lists 
+	  to fill previous page completely.
 
-why mkdir and create?
+formatting:
+	file.c filewrite leaks onto next page
+	need to fix PAGEBREAK mechanism
 
diff --git a/bio.c b/bio.c
index feeda5f..c819b6b 100644
--- a/bio.c
+++ b/bio.c
@@ -117,12 +117,11 @@ bread(uint dev, uint sector)
 // Write buf's contents to disk.
 // Must be locked.
 void
-bwrite(struct buf *b, uint sector)
+bwrite(struct buf *b)
 {
   if((b->flags & B_BUSY) == 0)
     panic("bwrite");
-
-  ide_rw(b->dev & 0xff, sector, b->data, 1, 0);
+  ide_rw(b->dev & 0xff, b->sector, b->data, 1, 0);
   b->flags |= B_VALID;
 }
 
diff --git a/bootmain.c b/bootmain.c
index 3a6f5b3..1882aa8 100644
--- a/bootmain.c
+++ b/bootmain.c
@@ -25,6 +25,7 @@
 //  * cmain() in this file takes over, 
 //    reads in the kernel and jumps to it.
 
+//PAGEBREAK!
 #include "types.h"
 #include "elf.h"
 #include "x86.h"
@@ -32,7 +33,6 @@
 #define SECTSIZE  512
 #define ELFHDR    ((struct elfhdr*) 0x10000) // scratch space
 
-void readsect(void*, uint);
 void readseg(uint, uint, uint);
 
 void
@@ -64,32 +64,6 @@ bad:
     ;
 }
 
-// Read 'count' bytes at 'offset' from kernel into virtual address 'va'.
-// Might copy more than asked
-void
-readseg(uint va, uint count, uint offset)
-{
-  uint end_va;
-
-  va &= 0xFFFFFF;
-  end_va = va + count;
-
-  // round down to sector boundary
-  va &= ~(SECTSIZE - 1);
-
-  // translate from bytes to sectors, and kernel starts at sector 1
-  offset = (offset / SECTSIZE) + 1;
-
-  // If this is too slow, we could read lots of sectors at a time.
-  // We'd write more to memory than asked, but it doesn't matter --
-  // we load in increasing order.
-  while(va < end_va) {
-    readsect((uchar*) va, offset);
-    va += SECTSIZE;
-    offset++;
-  }
-}
-
 void
 waitdisk(void)
 {
@@ -98,6 +72,7 @@ waitdisk(void)
     ;
 }
 
+// Read a single sector at offset into dst.
 void
 readsect(void *dst, uint offset)
 {
@@ -118,3 +93,29 @@ readsect(void *dst, uint offset)
   insl(0x1F0, dst, SECTSIZE/4);
 }
 
+// Read 'count' bytes at 'offset' from kernel into virtual address 'va'.
+// Might copy more than asked.
+void
+readseg(uint va, uint count, uint offset)
+{
+  uint end_va;
+
+  va &= 0xFFFFFF;
+  end_va = va + count;
+
+  // round down to sector boundary
+  va &= ~(SECTSIZE - 1);
+
+  // translate from bytes to sectors, and kernel starts at sector 1
+  offset = (offset / SECTSIZE) + 1;
+
+  // If this is too slow, we could read lots of sectors at a time.
+  // We'd write more to memory than asked, but it doesn't matter --
+  // we load in increasing order.
+  while(va < end_va) {
+    readsect((uchar*) va, offset);
+    va += SECTSIZE;
+    offset++;
+  }
+}
+
diff --git a/defs.h b/defs.h
index acf7b7c..49c0296 100644
--- a/defs.h
+++ b/defs.h
@@ -24,6 +24,7 @@ int proc_kill(int);
 int proc_wait(void);
 void yield(void);
 void procdump(void);
+void userinit(void);
 
 // setjmp.S
 struct jmpbuf;
@@ -117,30 +118,31 @@ void ide_rw(int, uint, void*, uint, int);
 void binit(void);
 struct buf;
 struct buf* bread(uint, uint);
-void bwrite(struct buf*, uint);
+void bwrite(struct buf*);
 void brelse(struct buf*);
 
 // fs.c
 struct inode;
+struct uinode;
 void iinit(void);
-void ilock(struct inode*);
-void iunlock(struct inode*);
-void idecref(struct inode*);
-struct inode* iincref(struct inode*);
-void iput(struct inode*);
-struct inode* namei(char*);
+struct inode* ilock(struct uinode*);
+struct uinode* iunlock(struct inode*);
+void iput(struct uinode*);
+struct uinode* idup(struct uinode*);
+struct uinode* namei(char*);
 void stati(struct inode*, struct stat*);
 int readi(struct inode*, char*, uint, uint);
 int writei(struct inode*, char*, uint, uint);
-struct inode* mknod(char*, short, short, short);
-int unlink(char*);
-int link(char*, char*);
-struct inode* igetroot(void);
-int mkdir(char *path);
-struct inode* create(char *path);
+int dirlink(struct inode *dp, char *name, uint ino);
+struct uinode* dirlookup(struct inode *dp, char *name, uint *poff);
+void iupdate(struct inode *ip);
+int namecmp(const char *s, const char *t);
+struct uinode* ialloc(uint, short);
+struct uinode* nameiparent(char *path, char *name);
 
 // exec.c
 int exec(char*, char**);
 
 // number of elements in fixed-size array
 #define NELEM(x) (sizeof(x)/sizeof((x)[0]))
+
diff --git a/exec.c b/exec.c
index 1f8b1af..de1175c 100644
--- a/exec.c
+++ b/exec.c
@@ -19,7 +19,7 @@ int
 exec(char *path, char **argv)
 {
   uint sz, sp, p1, p2;
-  int i, nargs, argbytes, len;
+  int i, nargs, argbytes, len, off;
   struct inode *ip;
   struct elfhdr elf;
   struct proghdr ph;
@@ -29,7 +29,7 @@ exec(char *path, char **argv)
   sz = 0;
   mem = 0;
 
-  if((ip = namei(path)) == 0)
+  if((ip = ilock(namei(path))) == 0)
     return -1;
 
   if(readi(ip, (char*)&elf, 0, sizeof(elf)) < sizeof(elf))
@@ -38,9 +38,8 @@ exec(char *path, char **argv)
   if(elf.magic != ELF_MAGIC)
     goto bad;
 
-  for(i = 0; i < elf.phnum; i++){
-    if(readi(ip, (char*)&ph, elf.phoff + i * sizeof(ph),
-             sizeof(ph)) != sizeof(ph))
+  for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
+    if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph))
       goto bad;
     if(ph.type != ELF_PROG_LOAD)
       continue;
@@ -94,7 +93,7 @@ exec(char *path, char **argv)
   for(last=s=path; *s; s++)
     if(*s == '/')
       last = s+1;
-  safestrcpy(cp->name, last, sizeof cp->name);
+  safestrcpy(cp->name, last, sizeof(cp->name));
 
   // commit to the new image.
   kfree(cp->mem, cp->sz);
@@ -102,9 +101,8 @@ exec(char *path, char **argv)
   cp->mem = mem;
   mem = 0;
 
-  for(i = 0; i < elf.phnum; i++){
-    if(readi(ip, (char*)&ph, elf.phoff + i * sizeof(ph),
-             sizeof(ph)) != sizeof(ph))
+  for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
+    if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph))
       goto bad2;
     if(ph.type != ELF_PROG_LOAD)
       continue;
@@ -115,7 +113,7 @@ exec(char *path, char **argv)
     memset(cp->mem + ph.va + ph.filesz, 0, ph.memsz - ph.filesz);
   }
 
-  iput(ip);
+  iput(iunlock(ip));
   
   cp->tf->eip = elf.entry;
   cp->tf->esp = sp;
@@ -126,11 +124,11 @@ exec(char *path, char **argv)
  bad:
   if(mem)
     kfree(mem, sz);
-  iput(ip);
+  iput(iunlock(ip));
   return -1;
 
  bad2:
-  iput(ip);
+  iput(iunlock(ip));
   proc_exit();
   return 0;
 }
diff --git a/file.c b/file.c
index 47e4629..981d474 100644
--- a/file.c
+++ b/file.c
@@ -11,9 +11,8 @@
 #include "fs.h"
 #include "fsvar.h"
 
-struct spinlock file_table_lock;
 struct devsw devsw[NDEV];
-
+struct spinlock file_table_lock;
 struct file file[NFILE];
 
 void
@@ -22,7 +21,7 @@ fileinit(void)
   initlock(&file_table_lock, "file_table");
 }
 
-// Allocate a file structure
+// Allocate a file structure.
 struct file*
 filealloc(void)
 {
@@ -57,16 +56,17 @@ int
 fileread(struct file *f, char *addr, int n)
 {
   int r;
+  struct inode *ip;
 
   if(f->readable == 0)
     return -1;
   if(f->type == FD_PIPE)
     return pipe_read(f->pipe, addr, n);
-  if(f->type == FD_FILE){
-    ilock(f->ip);
-    if((r = readi(f->ip, addr, f->off, n)) > 0)
+  if(f->type == FD_INODE){
+    ip = ilock(f->ip);
+    if((r = readi(ip, addr, f->off, n)) > 0)
       f->off += r;
-    iunlock(f->ip);
+    iunlock(ip);
     return r;
   }
   panic("fileread");
@@ -77,16 +77,17 @@ int
 filewrite(struct file *f, char *addr, int n)
 {
   int r;
+  struct inode *ip;
 
   if(f->writable == 0)
     return -1;
   if(f->type == FD_PIPE)
     return pipe_write(f->pipe, addr, n);
-  if(f->type == FD_FILE){
-    ilock(f->ip);
-    if((r = writei(f->ip, addr, f->off, n)) > 0)
+  if(f->type == FD_INODE){
+    ip = ilock(f->ip);
+    if((r = writei(ip, addr, f->off, n)) > 0)
       f->off += r;
-    iunlock(f->ip);
+    iunlock(ip);
     return r;
   }
   panic("filewrite");
@@ -96,10 +97,12 @@ filewrite(struct file *f, char *addr, int n)
 int
 filestat(struct file *f, struct stat *st)
 {
-  if(f->type == FD_FILE){
-    ilock(f->ip);
-    stati(f->ip, st);
-    iunlock(f->ip);
+  struct inode *ip;
+
+  if(f->type == FD_INODE){
+    ip = ilock(f->ip);
+    stati(ip, st);
+    iunlock(ip);
     return 0;
   }
   return -1;
@@ -110,6 +113,7 @@ void
 fileclose(struct file *f)
 {
   struct file ff;
+
   acquire(&file_table_lock);
 
   if(f->ref < 1 || f->type == FD_CLOSED)
@@ -127,8 +131,8 @@ fileclose(struct file *f)
   
   if(ff.type == FD_PIPE)
     pipe_close(ff.pipe, ff.writable);
-  else if(ff.type == FD_FILE)
-    idecref(ff.ip);
+  else if(ff.type == FD_INODE)
+    iput(ff.ip);
   else
     panic("fileclose");
 }
diff --git a/file.h b/file.h
index 15d6b78..d864793 100644
--- a/file.h
+++ b/file.h
@@ -1,9 +1,9 @@
 struct file {
-  enum { FD_CLOSED, FD_NONE, FD_PIPE, FD_FILE } type;
+  enum { FD_CLOSED, FD_NONE, FD_PIPE, FD_INODE } type;
   int ref; // reference count
   char readable;
   char writable;
   struct pipe *pipe;
-  struct inode *ip;
+  struct uinode *ip;
   uint off;
 };
diff --git a/fs.c b/fs.c
index 8c65f35..69bfd8f 100644
--- a/fs.c
+++ b/fs.c
@@ -7,8 +7,10 @@
 //   + Names: paths like /usr/rtm/xv6/fs.c for convenient naming.
 //
 // Disk layout is: superblock, inodes, disk bitmap, data blocks.
-
-// TODO: Check locking!
+//
+// This file contains the low-level file system manipulation 
+// routines.  The (higher-level) system call implementations
+// are in sysfile.c.
 
 #include "types.h"
 #include "stat.h"
@@ -25,7 +27,6 @@
 
 #define min(a, b) ((a) < (b) ? (a) : (b))
 static void itrunc(struct inode*);
-static void iupdate(struct inode*);
 
 // Blocks. 
 
@@ -51,7 +52,7 @@ balloc(uint dev)
     m = 0x1 << (bi % 8);
     if((bp->data[bi/8] & m) == 0) {  // is block free?
       bp->data[bi/8] |= 0x1 << (bi % 8);
-      bwrite(bp, BBLOCK(b, ninodes));  // mark it allocated on disk
+      bwrite(bp);  // mark it allocated on disk
       brelse(bp);
       return b;
     }
@@ -74,14 +75,14 @@ bfree(int dev, uint b)
 
   bp = bread(dev, b);
   memset(bp->data, 0, BSIZE);
-  bwrite(bp, b);
+  bwrite(bp);
   brelse(bp);
 
   bp = bread(dev, BBLOCK(b, ninodes));
   bi = b % BPB;
   m = 0x1 << (bi % 8);
   bp->data[bi/8] &= ~m;
-  bwrite(bp, BBLOCK(b, ninodes));  // mark it free on disk
+  bwrite(bp);  // mark it free on disk
   brelse(bp);
 }
 
@@ -98,11 +99,20 @@ bfree(int dev, uint b)
 // It is an error to use an inode without holding a reference to it.
 //
 // Inodes can be marked busy, just like bufs, meaning
-// that some process has logically locked the inode, and other processes
-// are not allowed to look at it.  Because the locking can last for 
-// a long time (for example, during a disk access), we use a flag
-// like in buffer cache, not spin locks.  The inode should always be
-// locked during modifications to it.
+// that some process has exclusive use of the inode.
+// Processes are only allowed to read and write inode
+// metadata and contents when holding the inode's lock.
+// Because inodes locks are held during disk accesses, 
+// they are implemented using a flag, as in the buffer cache,
+// not using spin locks.  Callers are responsible for locking
+// inodes before passing them to routines in this file; leaving
+// this responsibility with the caller makes it possible for them
+// to create arbitrarily-sized atomic operations.
+//
+// To give maximum control over locking to the callers, 
+// the routines in this file that return inode pointers 
+// return pointers to *unlocked* inodes.  It is the callers'
+// responsibility to lock them before using them.
 
 struct {
   struct spinlock lock;
@@ -116,14 +126,8 @@ iinit(void)
 }
 
 // Find the inode with number inum on device dev
-// and return the in-memory copy.  The returned inode
-// has its reference count incremented (and thus must be
-// idecref'ed), but is *unlocked*, meaning that none of the fields
-// except dev and inum are guaranteed to be initialized.
-// This convention gives the caller maximum control over blocking;
-// it also guarantees that iget will not sleep, which is useful in 
-// the early igetroot and when holding other locked inodes.
-struct inode*
+// and return the in-memory copy. h
+static struct uinode*
 iget(uint dev, uint inum)
 {
   struct inode *ip, *empty;
@@ -136,7 +140,7 @@ iget(uint dev, uint inum)
     if(ip->ref > 0 && ip->dev == dev && ip->inum == inum){
       ip->ref++;
       release(&icache.lock);
-      return ip;
+      return (struct uinode*)ip;
     }
     if(empty == 0 && ip->ref == 0)    // Remember empty slot.
       empty = ip;
@@ -153,28 +157,37 @@ iget(uint dev, uint inum)
   ip->flags = 0;
   release(&icache.lock);
 
-  return ip;
+  return (struct uinode*)ip;
 }
 
-// Iget the inode for the file system root (/).
-// This gets called before there is a current process: it cannot sleep!
-struct inode*
-igetroot(void)
+// Increment reference count for ip.
+// Returns ip to enable ip = idup(ip1) idiom.
+struct uinode*
+idup(struct uinode *uip)
 {
   struct inode *ip;
-  ip = iget(ROOTDEV, 1);
-  return ip;
+
+  ip = (struct inode*)uip;
+  acquire(&icache.lock);
+  ip->ref++;
+  release(&icache.lock);
+  return uip;
 }
 
 // Lock the given inode.
-void
-ilock(struct inode *ip)
+struct inode*
+ilock(struct uinode *uip)
 {
   struct buf *bp;
   struct dinode *dip;
+  struct inode *ip;
+
+  ip = (struct inode*)uip;
+  if(ip == 0)
+    return 0;
 
   if(ip->ref < 1)
-    panic("ilock");
+    panic("ilock: no refs");
 
   acquire(&icache.lock);
   while(ip->flags & I_BUSY)
@@ -193,13 +206,19 @@ ilock(struct inode *ip)
     memmove(ip->addrs, dip->addrs, sizeof(ip->addrs));
     brelse(bp);
     ip->flags |= I_VALID;
+    if(ip->type == 0)
+      panic("ilock: no type");
   }
+  return ip;
 }
 
 // Unlock the given inode.
-void
+struct uinode*
 iunlock(struct inode *ip)
 {
+  if(ip == 0)
+    return 0;
+
   if(!(ip->flags & I_BUSY) || ip->ref < 1)
     panic("iunlock");
 
@@ -207,36 +226,21 @@ iunlock(struct inode *ip)
   ip->flags &= ~I_BUSY;
   wakeup(ip);
   release(&icache.lock);
-}
-
-// Unlock inode and drop reference.
-void
-iput(struct inode *ip)
-{
-  iunlock(ip);
-  idecref(ip);
-}
-
-// Increment reference count for ip.
-// Returns ip to enable ip = iincref(ip1) idiom.
-struct inode*
-iincref(struct inode *ip)
-{
-  acquire(&icache.lock);
-  ip->ref++;
-  release(&icache.lock);
-  return ip;
+  return (struct uinode*)ip;
 }
 
 // Caller holds reference to unlocked ip.  Drop reference.
 void
-idecref(struct inode *ip)
+iput(struct uinode *uip)
 {
+  struct inode *ip;
+  
+  ip = (struct inode*)uip;
   acquire(&icache.lock);
   if(ip->ref == 1 && (ip->flags & I_VALID) && ip->nlink == 0) {
     // inode is no longer used: truncate and free inode.
     if(ip->flags & I_BUSY)
-      panic("idecref busy");
+      panic("iput busy");
     ip->flags |= I_BUSY;
     release(&icache.lock);
     // XXX convince rsc that no one will come find this inode.
@@ -251,7 +255,7 @@ idecref(struct inode *ip)
 }
 
 // Allocate a new inode with the given type on device dev.
-struct inode*
+struct uinode*
 ialloc(uint dev, short type)
 {
   int inum, ninodes;
@@ -270,7 +274,7 @@ ialloc(uint dev, short type)
     if(dip->type == 0) {  // a free inode
       memset(dip, 0, sizeof(*dip));
       dip->type = type;
-      bwrite(bp, IBLOCK(inum));   // mark it allocated on the disk
+      bwrite(bp);   // mark it allocated on the disk
       brelse(bp);
       return iget(dev, inum);
     }
@@ -280,7 +284,7 @@ ialloc(uint dev, short type)
 }
 
 // Copy inode, which has changed, from memory to disk.
-static void
+void
 iupdate(struct inode *ip)
 {
   struct buf *bp;
@@ -294,7 +298,7 @@ iupdate(struct inode *ip)
   dip->nlink = ip->nlink;
   dip->size = ip->size;
   memmove(dip->addrs, ip->addrs, sizeof(ip->addrs));
-  bwrite(bp, IBLOCK(ip->inum));
+  bwrite(bp);
   brelse(bp);
 }
 
@@ -306,8 +310,8 @@ iupdate(struct inode *ip)
 // listed in the block ip->addrs[INDIRECT].
 
 // Return the disk block address of the nth block in inode ip.
-// If there is no such block: if alloc is set, allocate one, else return -1.
-uint
+// If there is no such block, alloc controls whether one is allocated.
+static uint
 bmap(struct inode *ip, uint bn, int alloc)
 {
   uint addr, *a;
@@ -339,7 +343,7 @@ bmap(struct inode *ip, uint bn, int alloc)
         return -1;
       }
       a[bn] = addr = balloc(ip->dev);
-      bwrite(bp, ip->addrs[INDIRECT]);
+      bwrite(bp);
     }
     brelse(bp);
     return addr;
@@ -348,6 +352,7 @@ bmap(struct inode *ip, uint bn, int alloc)
   panic("bmap: out of range");
 }
 
+// PAGEBREAK: 30
 // Truncate inode (discard contents).
 static void
 itrunc(struct inode *ip)
@@ -389,6 +394,7 @@ stati(struct inode *ip, struct stat *st)
   st->size = ip->size;
 }
 
+//PAGEBREAK!
 // Read data from inode.
 int
 readi(struct inode *ip, char *dst, uint off, uint n)
@@ -416,6 +422,7 @@ readi(struct inode *ip, char *dst, uint off, uint n)
   return n;
 }
 
+// PAGEBREAK!
 // Write data to inode.
 int
 writei(struct inode *ip, char *src, uint off, uint n)
@@ -438,7 +445,7 @@ writei(struct inode *ip, char *src, uint off, uint n)
     bp = bread(ip->dev, bmap(ip, off/BSIZE, 1));
     m = min(n - tot, BSIZE - off%BSIZE);
     memmove(bp->data + off%BSIZE, src, m);
-    bwrite(bp, bmap(ip, off/BSIZE, 0));
+    bwrite(bp);
     brelse(bp);
   }
 
@@ -449,12 +456,10 @@ writei(struct inode *ip, char *src, uint off, uint n)
   return n;
 }
 
+//PAGEBREAK!
 // Directories
-//
-// Directories are just inodes (files) filled with dirent structures.
 
-// Compare two names, which are strings with a max length of DIRSIZ.
-static int
+int
 namecmp(const char *s, const char *t)
 {
   int i;
@@ -468,25 +473,9 @@ namecmp(const char *s, const char *t)
   return 0;
 }
 
-// Copy one name to another.
-static void
-namecpy(char *s, const char *t)
-{
-  int i;
-  
-  for(i=0; i<DIRSIZ && t[i]; i++)
-    s[i] = t[i];
-  for(; i<DIRSIZ; i++)
-    s[i] = 0;
-}
-
 // Look for a directory entry in a directory.
-// If not found, return -1.
-// If found:
-//   set *poff to the byte offset of the directory entry
-//   set *pinum to the inode number
-//   return 0.
-static struct inode*
+// If found, set *poff to byte offset of entry.
+struct uinode*
 dirlookup(struct inode *dp, char *name, uint *poff)
 {
   uint off, inum;
@@ -517,18 +506,29 @@ dirlookup(struct inode *dp, char *name, uint *poff)
   return 0;
 }
 
+// Copy one name to another.
+static void
+namecpy(char *s, const char *t)
+{
+  int i;
+  
+  for(i=0; i<DIRSIZ && t[i]; i++)
+    s[i] = t[i];
+  for(; i<DIRSIZ; i++)
+    s[i] = 0;
+}
+
 // Write a new directory entry (name, ino) into the directory dp.
-// Caller must have locked dp.
-static int
+int
 dirlink(struct inode *dp, char *name, uint ino)
 {
   int off;
   struct dirent de;
-  struct inode *ip;
+  struct uinode *ip;
 
-  // Double-check that name is not present.
+  // Check that name is not present.
   if((ip = dirlookup(dp, name, 0)) != 0){
-    idecref(ip);
+    iput(ip);
     return -1;
   }
 
@@ -548,49 +548,18 @@ dirlink(struct inode *dp, char *name, uint ino)
   return 0;
 }
 
-// Create a new inode named name inside dp
-// and return its locked inode structure.
-// If name already exists, return 0.
-static struct inode*
-dircreat(struct inode *dp, char *name, short type, short major, short minor)
-{
-  struct inode *ip;
-
-  ip = ialloc(dp->dev, type);
-  if(ip == 0)
-    return 0;
-  ilock(ip);
-  ip->major = major;
-  ip->minor = minor;
-  ip->size = 0;
-  ip->nlink = 1;
-  iupdate(ip);
-  
-  if(dirlink(dp, name, ip->inum) < 0){
-    ip->nlink = 0;
-    iupdate(ip);
-    iput(ip);
-    return 0;
-  }
-
-  return ip;
-}
-
 // Paths
 
-// Skip over the next path element in path, 
-// saving it in *name and its length in *len.
-// Return a pointer to the element after that
-// (after any trailing slashes).
-// Thus the caller can check whether *path=='\0'
-// to see whether the name just removed was
-// the last one.  
-// If there is no name to remove, return 0.
+// Copy the next path element from path into name.
+// Return a pointer to the element following the copied one.
+// The returned path has no leading slashes,
+// so the caller can check *path=='\0' to see if the name is the last one.
+// If no name to remove, return 0.
 //
 // Examples:
-//   skipelem("a/bb/c") = "bb/c", with *name = "a/bb/c", len=1
-//   skipelem("///a/bb") = "b", with *name="a/bb", len=1
-//   skipelem("") = skipelem("////") = 0
+//   skipelem("a/bb/c", name) = "bb/c", setting name = "a"
+//   skipelem("///a/bb", name) = "b", setting name="a"
+//   skipelem("", name) = skipelem("////", name) = 0
 //
 static char*
 skipelem(char *path, char *name)
@@ -617,201 +586,61 @@ skipelem(char *path, char *name)
   return path;
 }
 
-// look up a path name, in one of three modes.
-// NAMEI_LOOKUP: return locked target inode.
-// NAMEI_CREATE: return locked parent inode.
-//   return 0 if name does exist.
-//   *ret_last points to last path component (i.e. new file name).
-//   *ret_ip points to the the name that did exist, if it did.
-//   *ret_ip and *ret_last may be zero even if return value is zero.
-// NAMEI_DELETE: return locked parent inode, offset of dirent in *ret_off.
-//   return 0 if name doesn't exist.
-struct inode*
+// Look up and return the inode for a path name.
+// If parent is set, return the inode for the parent
+// and write the final path element to name, which
+// should have room for DIRSIZ bytes.
+static struct uinode*
 _namei(char *path, int parent, char *name)
 {
-  struct inode *dp, *ip;
+  struct uinode *dp, *ip;
+  struct inode *dpl;
   uint off;
 
   if(*path == '/')
-    dp = igetroot();
+    dp = iget(ROOTDEV, 1);
   else
-    dp = iincref(cp->cwd);
-  ilock(dp);
+    dp = idup(cp->cwd);
 
   while((path = skipelem(path, name)) != 0){
-    if(dp->type != T_DIR)
-      goto fail;
+    dpl = ilock(dp);
+    if(dpl->type != T_DIR){
+      iunlock(dpl);
+      iput(dp);
+      return 0;
+    }
     
     if(parent && *path == '\0'){
       // Stop one level early.
+      iunlock(dpl);
       return dp;
     }
 
-    if((ip = dirlookup(dp, name, &off)) == 0)
-      goto fail;
+    if((ip = dirlookup(dpl, name, &off)) == 0){
+      iunlock(dpl);
+      iput(dp);
+      iput(ip);
+      return 0;
+    }
 
+    iunlock(dpl);
     iput(dp);
-    ilock(ip);
     dp = ip;
-    if(dp->type == 0 || dp->nlink < 1)
-      panic("namei");
   }
   if(parent)
     return 0;
   return dp;
-
-fail:
-  iput(dp);
-  return 0;
 }
 
-struct inode*
+struct uinode*
 namei(char *path)
 {
   char name[DIRSIZ];
   return _namei(path, 0, name);
 }
 
-static struct inode*
+struct uinode*
 nameiparent(char *path, char *name)
 {
   return _namei(path, 1, name);
 }
-
-// Create the path and return its locked inode structure.
-// If cp already exists, return 0.
-struct inode*
-mknod(char *path, short type, short major, short minor)
-{
-  struct inode *ip, *dp;
-  char name[DIRSIZ];
-
-  if((dp = nameiparent(path, name)) == 0)
-    return 0;
-  ip = dircreat(dp, name, type, major, minor);
-  iput(dp);
-  return ip;
-}
-
-// Unlink the inode named cp.
-int
-unlink(char *path)
-{
-  struct inode *ip, *dp;
-  struct dirent de;
-  uint off;
-  char name[DIRSIZ];
-
-  if((dp = nameiparent(path, name)) == 0)
-    return -1;
-
-  // Cannot unlink "." or "..".
-  if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0){
-    iput(dp);
-    return -1;
-  }
-
-  if((ip = dirlookup(dp, name, &off)) == 0){
-    iput(dp);
-    return -1;
-  }
-  memset(&de, 0, sizeof(de));
-  if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
-    panic("unlink dir write");
-  iput(dp);
-
-  ilock(ip);
-  if(ip->nlink < 1)
-    panic("unlink nlink < 1");
-  ip->nlink--;
-  iupdate(ip);
-  iput(ip);
-
-  return 0;
-}
-
-// Create the path new as a link to the same inode as old.
-int
-link(char *old, char *new)
-{
-  struct inode *ip, *dp;
-  char name[DIRSIZ];
-
-  if((ip = namei(old)) == 0)
-    return -1;
-  if(ip->type == T_DIR){
-    iput(ip);
-    return -1;
-  }
-  iunlock(ip);
-
-  if((dp = nameiparent(new, name)) == 0){
-    idecref(ip);
-    return -1;
-  }
-  if(dp->dev != ip->dev || dirlink(dp, name, ip->inum) < 0){
-    idecref(ip);
-    iput(dp);
-    return -1;
-  }
-  iput(dp);
-
-  // XXX write ordering wrong here too.
-  ilock(ip);
-  ip->nlink++;
-  iupdate(ip);
-  iput(ip);
-  return 0;
-}
-
-int
-mkdir(char *path)
-{
-  struct inode *dp, *ip;
-  char name[DIRSIZ];
-  
-  // XXX write ordering is screwy here- do we care?
-  if((dp = nameiparent(path, name)) == 0)
-    return -1;
-  
-  if((ip = dircreat(dp, name, T_DIR, 0, 0)) == 0){
-    iput(dp);
-    return -1;
-  }
-  dp->nlink++;
-  iupdate(dp);
-
-  if(dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0)
-    panic("mkdir");
-  iput(dp);
-  iput(ip);
-
-  return 0;
-}
-
-struct inode*
-create(char *path)
-{
-  struct inode *dp, *ip;
-  char name[DIRSIZ];
-  
-  if((dp = nameiparent(path, name)) == 0)
-    return 0;
-  
-  if((ip = dirlookup(dp, name, 0)) != 0){
-    iput(dp);
-    ilock(ip);
-    if(ip->type == T_DIR){
-      iput(ip);
-      return 0;
-    }
-    return ip;
-  }
-  if((ip = dircreat(dp, name, T_FILE, 0, 0)) == 0){
-    iput(dp);
-    return 0;
-  }
-  iput(dp);
-  return ip;
-}
-
diff --git a/fsvar.h b/fsvar.h
index 8609c2a..f823c66 100644
--- a/fsvar.h
+++ b/fsvar.h
@@ -14,7 +14,11 @@ struct inode {
   uint addrs[NADDRS];
 };
 
-#define ROOTDEV  1   // Device number of root file system
+// unlocked inode - only dev and inum are available
+struct uinode {
+  uint dev;
+  uint inum;
+};
 
 #define I_BUSY 0x1
 #define I_VALID 0x2
diff --git a/main.c b/main.c
index aea5d80..6ef8da1 100644
--- a/main.c
+++ b/main.c
@@ -12,8 +12,6 @@
 
 extern char edata[], end[];
 
-void proc0init();
-
 // Bootstrap processor starts running C code here.
 // This is called main0 not main so that it can have
 // a void return type.  Gcc can't handle functions named
@@ -35,49 +33,37 @@ main0(void)
   bcpu = mp_bcpu();
 
   // switch to bootstrap processor's stack
-  asm volatile("movl %0, %%esp" : : "r" (cpus[bcpu].mpstack + MPSTACK - 32));
-  asm volatile("movl %0, %%ebp" : : "r" (cpus[bcpu].mpstack + MPSTACK));
+  asm volatile("movl %0, %%esp" : : "r" (cpus[bcpu].mpstack+MPSTACK-32));
+  asm volatile("movl %0, %%ebp" : : "r" (cpus[bcpu].mpstack+MPSTACK));
 
   lapic_init(bcpu);
 
   cprintf("\ncpu%d: starting xv6\n\n", cpu());
 
-  pinit(); // process table
-  binit(); // buffer cache
-  pic_init();
-  ioapic_init();
-  kinit(); // physical memory allocator
-  tvinit(); // trap vectors
-  idtinit(); // this CPU's interrupt descriptor table
-  fileinit();
-  iinit(); // i-node table
-
-  // make sure there's a TSS
-  setupsegs(0);
-
-  // initialize I/O devices, let them enable interrupts
-  console_init();
-  ide_init();
-
-  // start other CPUs
-  mp_startthem();
-
-  // turn on timer
-  if(ismp)
-    lapic_timerinit();
-  else
-    pit8253_timerinit();
-
-  // enable interrupts on the local APIC
-  lapic_enableintr();
+  pinit();         // process table
+  binit();         // buffer cache
+  pic_init();      // interrupt controller
+  ioapic_init();   // another interrupt controller
+  kinit();         // physical memory allocator
+  tvinit();        // trap vectors
+  idtinit();       // interrupt descriptor table
+  fileinit();      // file table
+  iinit();         // inode cache
+  setupsegs(0);    // segments & TSS
+  console_init();  // I/O devices & their interrupts
+  ide_init();      // disk
+  mp_startthem();  // other CPUs
+  if(ismp){
+    lapic_timerinit();   // smp timer
+    lapic_enableintr();  // local interrupts
+  }else
+    pit8253_timerinit(); // uniprocessor timer
+  userinit();      // first user process
 
   // enable interrupts on this processor.
   cpus[cpu()].nlock--;
   sti();
 
-  // initialize process 0
-  proc0init();
-
   scheduler();
 }
 
@@ -106,29 +92,3 @@ mpmain(void)
   scheduler();
 }
 
-void
-proc0init(void)
-{
-  struct proc *p;
-  extern uchar _binary_initcode_start[], _binary_initcode_size[];
-  
-  p = copyproc(0);
-  p->sz = PAGE;
-  p->mem = kalloc(p->sz);
-  p->cwd = igetroot();
-  memset(&p->tf, 0, sizeof p->tf);
-  p->tf->es = p->tf->ds = p->tf->ss = (SEG_UDATA << 3) | DPL_USER;
-  p->tf->cs = (SEG_UCODE << 3) | DPL_USER;
-  p->tf->eflags = FL_IF;
-  p->tf->esp = p->sz;
-  
-  // Push dummy return address to placate gcc.
-  p->tf->esp -= 4;
-  *(uint*)(p->mem + p->tf->esp) = 0xefefefef;
-
-  p->tf->eip = 0;
-  memmove(p->mem, _binary_initcode_start, (int)_binary_initcode_size);
-  safestrcpy(p->name, "initcode", sizeof p->name);
-  p->state = RUNNABLE;
-}
-
diff --git a/param.h b/param.h
index d80ef75..b3a2473 100644
--- a/param.h
+++ b/param.h
@@ -8,3 +8,4 @@
 #define NBUF         10  // size of disk block cache
 #define NINODE      100  // maximum number of active i-nodes
 #define NDEV         10  // maximum major device number
+#define ROOTDEV       1  // device number of file system root disk
diff --git a/proc.c b/proc.c
index c86f88f..fd58dec 100644
--- a/proc.c
+++ b/proc.c
@@ -11,7 +11,7 @@ struct spinlock proc_table_lock;
 
 struct proc proc[NPROC];
 struct proc *curproc[NCPU];
-int next_pid = 1;
+int nextpid = 1;
 extern void forkret(void);
 extern void forkret1(struct trapframe*);
 
@@ -21,37 +21,27 @@ pinit(void)
   initlock(&proc_table_lock, "proc_table");
 }
 
-// Set up CPU's segment descriptors and task state for a
-// given process.
-// If p==0, set up for "idle" state for when scheduler()
-// is idling, not running any process.
-void
-setupsegs(struct proc *p)
+// Look in the process table for an UNUSED proc.
+// If found, change state to EMBRYO and return it.
+// Otherwise return 0.
+static struct proc*
+allocproc(void)
 {
-  struct cpu *c = &cpus[cpu()];
-
-  c->ts.ss0 = SEG_KDATA << 3;
-  if(p){
-    c->ts.esp0 = (uint)(p->kstack + KSTACKSIZE);
-  } else {
-    c->ts.esp0 = 0xffffffff;
-  }
+  int i;
+  struct proc *p;
 
-  c->gdt[0] = SEG_NULL;
-  c->gdt[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0x100000 + 64*1024-1, 0);
-  c->gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
-  c->gdt[SEG_TSS] = SEG16(STS_T32A, (uint)&c->ts, sizeof(c->ts)-1, 0);
-  c->gdt[SEG_TSS].s = 0;
-  if(p){
-    c->gdt[SEG_UCODE] = SEG(STA_X|STA_R, (uint)p->mem, p->sz-1, DPL_USER);
-    c->gdt[SEG_UDATA] = SEG(STA_W, (uint)p->mem, p->sz-1, DPL_USER);
-  } else {
-    c->gdt[SEG_UCODE] = SEG_NULL;
-    c->gdt[SEG_UDATA] = SEG_NULL;
+  acquire(&proc_table_lock);
+  for(i = 0; i < NPROC; i++){
+    p = &proc[i];
+    if(p->state == UNUSED){
+      p->state = EMBRYO;
+      p->pid = nextpid++;
+      release(&proc_table_lock);
+      return p;
+    }
   }
-
-  lgdt(c->gdt, sizeof c->gdt);
-  ltr(SEG_TSS << 3);
+  release(&proc_table_lock);
+  return 0;
 }
 
 // Grow current process's memory by n bytes.
@@ -73,29 +63,41 @@ growproc(int n)
   return cp->sz - n;
 }
 
-// Look in the process table for an UNUSED proc.
-// If found, change state to EMBRYO and return it.
-// Otherwise return 0.
-struct proc*
-allocproc(void)
+// Set up CPU's segment descriptors and task state for a
+// given process.
+// If p==0, set up for "idle" state for when scheduler()
+// is idling, not running any process.
+void
+setupsegs(struct proc *p)
 {
-  int i;
-  struct proc *p;
+  struct cpu *c = &cpus[cpu()];
 
-  for(i = 0; i < NPROC; i++){
-    p = &proc[i];
-    if(p->state == UNUSED){
-      p->state = EMBRYO;
-      return p;
-    }
+  c->ts.ss0 = SEG_KDATA << 3;
+  if(p)
+    c->ts.esp0 = (uint)(p->kstack + KSTACKSIZE);
+  else
+    c->ts.esp0 = 0xffffffff;
+
+  c->gdt[0] = SEG_NULL;
+  c->gdt[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0x100000 + 64*1024-1, 0);
+  c->gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
+  c->gdt[SEG_TSS] = SEG16(STS_T32A, (uint)&c->ts, sizeof(c->ts)-1, 0);
+  c->gdt[SEG_TSS].s = 0;
+  if(p){
+    c->gdt[SEG_UCODE] = SEG(STA_X|STA_R, (uint)p->mem, p->sz-1, DPL_USER);
+    c->gdt[SEG_UDATA] = SEG(STA_W, (uint)p->mem, p->sz-1, DPL_USER);
+  } else {
+    c->gdt[SEG_UCODE] = SEG_NULL;
+    c->gdt[SEG_UDATA] = SEG_NULL;
   }
-  return 0;
+
+  lgdt(c->gdt, sizeof(c->gdt));
+  ltr(SEG_TSS << 3);
 }
 
 // Create a new process copying p as the parent.
-// Does not copy the kernel stack.
-// Instead, sets up stack to return as if from system call.
-// Caller must arrange for process to run (set state to RUNNABLE).
+// Sets up stack to return as if from system call.
+// Caller must set state of returned proc to RUNNABLE.
 struct proc*
 copyproc(struct proc *p)
 {
@@ -103,13 +105,8 @@ copyproc(struct proc *p)
   struct proc *np;
 
   // Allocate process.
-  acquire(&proc_table_lock);
-  if((np = allocproc()) == 0){
-    release(&proc_table_lock);
+  if((np = allocproc()) == 0)
     return 0;
-  }
-  np->pid = next_pid++;
-  release(&proc_table_lock);
 
   // Allocate kernel stack.
   if((np->kstack = kalloc(KSTACKSIZE)) == 0){
@@ -120,7 +117,7 @@ copyproc(struct proc *p)
 
   if(p){  // Copy process state from p.
     np->ppid = p->pid;
-    memmove(np->tf, p->tf, sizeof *np->tf);
+    memmove(np->tf, p->tf, sizeof(*np->tf));
   
     np->sz = p->sz;
     if((np->mem = kalloc(np->sz)) == 0){
@@ -132,24 +129,49 @@ copyproc(struct proc *p)
     memmove(np->mem, p->mem, np->sz);
 
     for(i = 0; i < NOFILE; i++){
-      np->ofile[i] = p->ofile[i];
-      if(np->ofile[i])
+      if((np->ofile[i] = p->ofile[i]) != 0)
         fileincref(np->ofile[i]);
     }
-    np->cwd = iincref(p->cwd);
+    np->cwd = idup(p->cwd);
   }
 
   // Set up new jmpbuf to start executing at forkret (see below).
-  memset(&np->jmpbuf, 0, sizeof np->jmpbuf);
+  memset(&np->jmpbuf, 0, sizeof(np->jmpbuf));
   np->jmpbuf.eip = (uint)forkret;
   np->jmpbuf.esp = (uint)np->tf - 4;
 
   // Clear %eax so that fork system call returns 0 in child.
   np->tf->eax = 0;
-
   return np;
 }
 
+// Set up first user process.
+void
+userinit(void)
+{
+  struct proc *p;
+  extern uchar _binary_initcode_start[], _binary_initcode_size[];
+  
+  p = copyproc(0);
+  p->sz = PAGE;
+  p->mem = kalloc(p->sz);
+  p->cwd = namei("/");
+  memset(&p->tf, 0, sizeof(p->tf));
+  p->tf->es = p->tf->ds = p->tf->ss = (SEG_UDATA << 3) | DPL_USER;
+  p->tf->cs = (SEG_UCODE << 3) | DPL_USER;
+  p->tf->eflags = FL_IF;
+  p->tf->esp = p->sz;
+  
+  // Push dummy return address to placate gcc.
+  p->tf->esp -= 4;
+  *(uint*)(p->mem + p->tf->esp) = 0xefefefef;
+
+  p->tf->eip = 0;
+  memmove(p->mem, _binary_initcode_start, (int)_binary_initcode_size);
+  safestrcpy(p->name, "initcode", sizeof(p->name));
+  p->state = RUNNABLE;
+}
+
 //PAGEBREAK: 42
 // Per-CPU process scheduler.
 // Each CPU calls scheduler() after setting itself up.
@@ -269,6 +291,7 @@ sleep(void *chan, struct spinlock *lk)
   }
 }
 
+//PAGEBREAK!
 // Wake up all processes sleeping on chan.
 // Proc_table_lock must be held.
 void
@@ -334,7 +357,7 @@ proc_exit(void)
     }
   }
 
-  idecref(cp->cwd);
+  iput(cp->cwd);
   cp->cwd = 0;
 
   acquire(&proc_table_lock);
diff --git a/proc.h b/proc.h
index 7a04cd5..01bff4a 100644
--- a/proc.h
+++ b/proc.h
@@ -37,7 +37,7 @@ struct proc {
   void *chan;               // If non-zero, sleeping on chan
   int killed;               // If non-zero, have been killed
   struct file *ofile[NOFILE];  // Open files
-  struct inode *cwd;        // Current directory
+  struct uinode *cwd;       // Current directory
   struct jmpbuf jmpbuf;     // Jump here to run process
   struct trapframe *tf;     // Trap frame for current interrupt
   char name[16];            // Process name (debugging)
@@ -49,8 +49,6 @@ struct proc {
 //   fixed-size stack
 //   expandable heap
 
-extern struct proc proc[];
-
 // If xv6 was only for uniprocessors, this could be
 //   struct proc *cp;
 // Instead we have an array curproc, one per
diff --git a/runoff.list b/runoff.list
index 6c7cc21..5aa1d96 100644
--- a/runoff.list
+++ b/runoff.list
@@ -13,7 +13,7 @@ bootasm.S
 bootother.S
 bootmain.c
 main.c
-mp.c
+initcode.S
 init.c
 
 # locks
@@ -27,11 +27,11 @@ setjmp.S
 kalloc.c
 
 # system calls
-syscall.h
-trapasm.S
 traps.h
-trap.c
 vectors.pl
+trapasm.S
+trap.c
+syscall.h
 syscall.c
 sysproc.c
 
@@ -46,6 +46,7 @@ fsvar.h
 ide.c
 bio.c
 fs.c
+exec.c
 file.c
 sysfile.c
 
@@ -56,10 +57,11 @@ pipe.c
 string.c
 
 # low-level PC
+mp.c
 ioapic.h
 lapic.c
 ioapic.c
 picirq.c
 kbd.h
 console.c
-8253pit.c
\ No newline at end of file
+8253pit.c
diff --git a/runoff.spec b/runoff.spec
index 53e98ee..9d0ad31 100644
--- a/runoff.spec
+++ b/runoff.spec
@@ -1,11 +1,50 @@
-even: mmu.h
-even: bootasm.S
-even: bootother.S
-even: bootmain.c
+# types.h either
+# param.h either
+# defs.h either
+# x86.h either
+# asm.h either
+# mmu.h either
+# elf.h either
+# mp.h either
+
+even: bootasm.S  # mild preference
+even: bootother.S  # mild preference
+# bootmain.c either
 even: main.c
-even: spinlock.c
-even: proc.h
-even: proc.c
-odd: kalloc.c
-even: trap.c
-odd: bio.c
+# mp.c don't care at all
+even: initcode.S
+odd: init.c
+
+# spinlock.h either
+# spinlock.c either
+even: proc.h  # mild preference
+even: proc.c  # VERY important
+# setjmp.S either
+# kalloc.c either
+
+# syscall.h either
+# trapasm.S either
+# traps.h either
+even: trap.c  # important
+# vectors.pl either
+# syscall.c either
+# sysproc.c either
+
+# buf.h either
+# dev.h either
+# fcntl.h either
+# stat.h either
+# file.h either
+# fs.h either
+# fsvar.h either
+# even: ide.c
+# odd: bio.c
+odd: fs.c   # VERY important
+# file.c either
+# exec.c either
+# sysfile.c either
+
+even: pipe.c  # mild preference
+# string.c either
+# even: console.c
+
diff --git a/runoff1 b/runoff1
index 381a67f..ba42e8f 100755
--- a/runoff1
+++ b/runoff1
@@ -45,6 +45,12 @@ for($i=0; $i<@lines; ){
 		$sawbrace = 0;
 		$breaksize = 15;  # 15 lines to get to function
 		for($j=$i; $j<$i+50 && $j < @lines; $j++){
+			if($lines[$j] =~ /PAGEBREAK!/){
+				$lines[$j] = "";
+				$breakbefore = $j;
+				$breaksize = 100;
+				last;
+			}
 			if($lines[$j] =~ /PAGEBREAK:\s*([0-9]+)/){
 				$breaksize = $1;
 				$breakbefore = $j;
diff --git a/sh.c b/sh.c
index 382db8f..6aa8824 100644
--- a/sh.c
+++ b/sh.c
@@ -41,7 +41,7 @@ int _gettoken(char *s, char **p1, char **p2);
 int
 main(void)
 {
-  while(getcmd(buf, sizeof buf) >= 0) {
+  while(getcmd(buf, sizeof(buf)) >= 0) {
     if(parse(buf) >= 0)
       runcmd();
   }
diff --git a/show1 b/show1
index 3637c72..e0d3d83 100755
--- a/show1
+++ b/show1
@@ -1,3 +1,3 @@
 #!/bin/sh
 
-runoff1 "$@" | pr.pl -h "xv6/$@" | mpage -m50t50b -o -bLetter -T -t -2 -FCourier -L60 >x.ps; gv --swap x.ps
+runoff1 "$@" | pr.pl -h "xv6/$@" | mpage -m50t50b -o -bLetter -T -t -2 -FLucidaSans-Typewriter83 -L60 >x.ps; gv --swap x.ps
diff --git a/spinlock.c b/spinlock.c
index a30fc58..b194211 100644
--- a/spinlock.c
+++ b/spinlock.c
@@ -36,6 +36,13 @@ getcallerpcs(void *v, uint pcs[])
     pcs[i] = 0;
 }
 
+// Check whether this cpu is holding the lock.
+int
+holding(struct spinlock *lock)
+{
+  return lock->locked && lock->cpu == cpu() + 10;
+}
+
 // Acquire the lock.
 // Loops (spins) until the lock is acquired.
 // (Because contention is handled by spinning,
@@ -83,11 +90,3 @@ release(struct spinlock *lock)
   if(--cpus[cpu()].nlock == 0)
     sti();
 }
-
-// Check whether this cpu is holding the lock.
-int
-holding(struct spinlock *lock)
-{
-  return lock->locked && lock->cpu == cpu() + 10;
-}
-
diff --git a/sysfile.c b/sysfile.c
index 87d163c..625d466 100644
--- a/sysfile.c
+++ b/sysfile.c
@@ -11,7 +11,6 @@
 #include "buf.h"
 #include "fs.h"
 #include "fsvar.h"
-#include "elf.h"
 #include "file.h"
 #include "fcntl.h"
 
@@ -51,27 +50,15 @@ fdalloc(struct file *f)
 }
 
 int
-sys_pipe(void)
+sys_read(void)
 {
-  int *fd;
-  struct file *rf, *wf;
-  int fd0, fd1;
+  struct file *f;
+  int n;
+  char *cp;
 
-  if(argptr(0, (void*)&fd, 2*sizeof fd[0]) < 0)
-    return -1;
-  if(pipe_alloc(&rf, &wf) < 0)
-    return -1;
-  fd0 = -1;
-  if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){
-    if(fd0 >= 0)
-      cp->ofile[fd0] = 0;
-    fileclose(rf);
-    fileclose(wf);
+  if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &cp, n) < 0)
     return -1;
-  }
-  fd[0] = fd0;
-  fd[1] = fd1;
-  return 0;
+  return fileread(f, cp, n);
 }
 
 int
@@ -87,15 +74,14 @@ sys_write(void)
 }
 
 int
-sys_read(void)
+sys_fstat(void)
 {
   struct file *f;
-  int n;
-  char *cp;
-
-  if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argptr(1, &cp, n) < 0)
+  struct stat *st;
+  
+  if(argfd(0, 0, &f) < 0 || argptr(1, (void*)&st, sizeof(*st)) < 0)
     return -1;
-  return fileread(f, cp, n);
+  return filestat(f, st);
 }
 
 int
@@ -111,6 +97,150 @@ sys_close(void)
   return 0;
 }
 
+// Create the path new as a link to the same inode as old.
+int
+sys_link(void)
+{
+  char name[DIRSIZ], *new, *old;
+  struct inode *dp, *ip;
+  struct uinode *ipu;
+
+  if(argstr(0, &old) < 0 || argstr(1, &new) < 0)
+    return -1;
+  if((ip = ilock(namei(old))) == 0)
+    return -1;
+  if(ip->type == T_DIR){
+    iput(iunlock(ip));
+    return -1;
+  }
+  ip->nlink++;
+  iupdate(ip);
+  ipu = iunlock(ip);  ip = 0;
+
+  if((dp = ilock(nameiparent(new, name))) == 0 ||
+     dp->dev != ipu->dev || dirlink(dp, name, ipu->inum) < 0){
+    if(dp)
+      iput(iunlock(dp));
+    ip = ilock(ipu);
+    ip->nlink--;
+    iupdate(ip);
+    iput(iunlock(ip));
+    return -1;
+  }
+  iput(iunlock(dp));
+  iput(ipu);
+  return 0;
+}
+
+// Is the directory dp empty except for "." and ".." ?
+static int
+isdirempty(struct inode *dp)
+{
+  int off;
+  struct dirent de;
+
+  for(off=2*sizeof(de); off<dp->size; off+=sizeof(de)){
+    if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
+      panic("isdirempty: readi");
+    if(de.inum != 0)
+      return 0;
+  }
+  return 1;
+}
+
+int
+sys_unlink(void)
+{
+  struct inode *ip, *dp;
+  struct dirent de;
+  char name[DIRSIZ], *path;
+  uint off;
+
+  if(argstr(0, &path) < 0)
+    return -1;
+  if((dp = ilock(nameiparent(path, name))) == 0)
+    return -1;
+
+  // Cannot unlink "." or "..".
+  if(namecmp(name, ".") == 0 || namecmp(name, "..") == 0){
+    iput(iunlock(dp));
+    return -1;
+  }
+
+  if((ip = ilock(dirlookup(dp, name, &off))) == 0){
+    iput(iunlock(dp));
+    return -1;
+  }
+
+  if(ip->nlink < 1)
+    panic("unlink: nlink < 1");
+  if(ip->type == T_DIR && !isdirempty(ip)){
+    iput(iunlock(ip));
+    iput(iunlock(dp));
+    return -1;
+  }
+
+  memset(&de, 0, sizeof(de));
+  if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
+    panic("unlink: writei");
+  iput(iunlock(dp));
+  
+  ip->nlink--;
+  iupdate(ip);
+  iput(iunlock(ip));
+  return 0;
+}
+
+// Create the path and return its unlocked inode structure.
+static struct inode*
+mkpath(char *path, int canexist, short type, short major, short minor)
+{
+  uint off;
+  struct inode *ip, *dp;
+  struct uinode *ipu;
+  char name[DIRSIZ];
+
+  if((dp = ilock(nameiparent(path, name))) == 0)
+    return 0;
+
+  if(canexist && (ipu = dirlookup(dp, name, &off)) != 0){
+    iput(iunlock(dp));
+    ip = ilock(ipu);
+    if(ip->type != type || ip->major != major || ip->minor != minor){
+      iput(iunlock(ip));
+      return 0;
+    }
+    return ip;
+  }
+
+  if((ip = ilock(ialloc(dp->dev, type))) == 0){
+    iput(iunlock(dp));
+    return 0;
+  }
+  ip->major = major;
+  ip->minor = minor;
+  ip->size = 0;
+  ip->nlink = 1;
+  iupdate(ip);
+  
+  if(dirlink(dp, name, ip->inum) < 0){
+    ip->nlink = 0;
+    iput(iunlock(ip));
+    iput(iunlock(dp));
+    return 0;
+  }
+
+  if(type == T_DIR){  // Create . and .. entries.
+    dp->nlink++;  // for ".."
+    iupdate(dp);
+    // No ip->nlink++ for ".": avoid cyclic ref count.
+    if(dirlink(ip, ".", ip->inum) < 0 || dirlink(ip, "..", dp->inum) < 0)
+      panic("mkpath dots");
+  }
+  iput(iunlock(dp));
+  return ip;
+}
+
 int
 sys_open(void)
 {
@@ -122,30 +252,28 @@ sys_open(void)
   if(argstr(0, &path) < 0 || argint(1, &omode) < 0)
     return -1;
 
-  if(omode & O_CREATE)
-    ip = create(path);
-  else
-    ip = namei(path);
-  if(ip == 0)
-    return -1;
-
-  if(ip->type == T_DIR && (omode & (O_RDWR|O_WRONLY))){
-    iput(ip);
-    return -1;
+  if(omode & O_CREATE){
+    if((ip = mkpath(path, 1, T_FILE, 0, 0)) == 0)
+      return -1;
+  }else{
+    if((ip = ilock(namei(path))) == 0)
+      return -1;
+    if(ip->type == T_DIR && (omode & (O_RDWR|O_WRONLY))){
+      iput(iunlock(ip));
+      return -1;
+    }
   }
 
-  if((f = filealloc()) == 0){
-    iput(ip);
-    return -1;
-  }
-  if((fd = fdalloc(f)) < 0){
-    iput(ip);
-    fileclose(f);
+  if((f = filealloc()) == 0 || (fd = fdalloc(f)) < 0){
+    if(f)
+      fileclose(f);
+    iput(iunlock(ip));
     return -1;
   }
 
-  iunlock(ip);
-  f->type = FD_FILE;
+  f->type = FD_INODE;
+  f->ip = iunlock(ip);
+  f->off = 0;
   if(omode & O_RDWR) {
     f->readable = 1;
     f->writable = 1;
@@ -156,8 +284,6 @@ sys_open(void)
     f->readable = 1;
     f->writable = 0;
   }
-  f->ip = ip;
-  f->off = 0;
 
   return fd;
 }
@@ -165,7 +291,7 @@ sys_open(void)
 int
 sys_mknod(void)
 {
-  struct inode *nip;
+  struct inode *ip;
   char *path;
   int len;
   int type, major, minor;
@@ -173,14 +299,10 @@ sys_mknod(void)
   if((len=argstr(0, &path)) < 0 || argint(1, &type) < 0 ||
      argint(2, &major) < 0 || argint(3, &minor) < 0)
     return -1;
-
-  // XXX why this check?
-  if(len >= DIRSIZ)
+  // XXX check that type == T_DEV or eliminate type arg?
+  if((ip = mkpath(path, 0, type, major, minor)) == 0)
     return -1;
-
-  if((nip = mknod(path, type, major, minor)) == 0)
-    return -1;
-  iput(nip);
+  iput(iunlock(ip));
   return 0;
 }
 
@@ -188,56 +310,31 @@ int
 sys_mkdir(void)
 {
   char *path;
+  struct inode *ip;
 
-  if(argstr(0, &path) < 0)
+  if(argstr(0, &path) < 0 || (ip = mkpath(path, 0, T_DIR, 0, 0)) == 0)
     return -1;
-  return mkdir(path);
+  iput(iunlock(ip));
+  return 0;
 }
 
 int
 sys_chdir(void)
 {
-  struct inode *ip;
   char *path;
+  struct inode *ip;
 
-  if(argstr(0, &path) < 0)
-    return -1;
-
-  if((ip = namei(path)) == 0)
+  if(argstr(0, &path) < 0 || (ip = ilock(namei(path))) == 0)
     return -1;
-
   if(ip->type != T_DIR) {
-    iput(ip);
+    iput(iunlock(ip));
     return -1;
   }
-
-  iunlock(ip);
-  idecref(cp->cwd);
-  cp->cwd = ip;
+  iput(cp->cwd);
+  cp->cwd = iunlock(ip);
   return 0;
 }
 
-int
-sys_unlink(void)
-{
-  char *path;
-  
-  if(argstr(0, &path) < 0)
-    return -1;
-  return unlink(path);
-}
-
-int
-sys_fstat(void)
-{
-  struct file *f;
-  struct stat *st;
-  
-  if(argfd(0, 0, &f) < 0 || argptr(1, (void*)&st, sizeof *st) < 0)
-    return -1;
-  return filestat(f, st);
-}
-
 int
 sys_dup(void)
 {
@@ -252,30 +349,18 @@ sys_dup(void)
   return fd;
 }
 
-int
-sys_link(void)
-{
-  char *old, *new;
-  
-  if(argstr(0, &old) < 0 || argstr(1, &new) < 0)
-    return -1;
-  return link(old, new);
-}
-
-#define MAXARGS 20
-
 int
 sys_exec(void)
 {
-  char *path, *argv[MAXARGS];
+  char *path, *argv[20];
   int i;
   uint uargv, uarg;
 
   if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0)
     return -1;
-  memset(argv, 0, sizeof argv);
+  memset(argv, 0, sizeof(argv));
   for(i=0;; i++){
-    if(i >= MAXARGS)
+    if(i >= NELEM(argv))
       return -1;
     if(fetchint(cp, uargv+4*i, (int*)&uarg) < 0)
       return -1;
@@ -289,3 +374,26 @@ sys_exec(void)
   return exec(path, argv);
 }
 
+int
+sys_pipe(void)
+{
+  int *fd;
+  struct file *rf, *wf;
+  int fd0, fd1;
+
+  if(argptr(0, (void*)&fd, 2*sizeof(fd[0])) < 0)
+    return -1;
+  if(pipe_alloc(&rf, &wf) < 0)
+    return -1;
+  fd0 = -1;
+  if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){
+    if(fd0 >= 0)
+      cp->ofile[fd0] = 0;
+    fileclose(rf);
+    fileclose(wf);
+    return -1;
+  }
+  fd[0] = fd0;
+  fd[1] = fd1;
+  return 0;
+}
diff --git a/trap.c b/trap.c
index 43b5a42..217e79d 100644
--- a/trap.c
+++ b/trap.c
@@ -17,14 +17,14 @@ tvinit(void)
   int i;
 
   for(i = 0; i < 256; i++)
-    SETGATE(idt[i], 0, SEG_KCODE << 3, vectors[i], 0);
-  SETGATE(idt[T_SYSCALL], 0, SEG_KCODE << 3, vectors[T_SYSCALL], DPL_USER);
+    SETGATE(idt[i], 0, SEG_KCODE<<3, vectors[i], 0);
+  SETGATE(idt[T_SYSCALL], 0, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER);
 }
 
 void
 idtinit(void)
 {
-  lidt(idt, sizeof idt);
+  lidt(idt, sizeof(idt));
 }
 
 void
@@ -80,7 +80,7 @@ trap(struct trapframe *tf)
   default:
     if(cp) {
       // Assume process divided by zero or dereferenced null, etc.
-      cprintf("pid %d %s: unhandled trap %d err %d on cpu %d eip %x -- kill proc\n",
+      cprintf("pid %d %s: trap %d err %d on cpu %d eip %x -- kill proc\n",
               cp->pid, cp->name, tf->trapno, tf->err, cpu(), tf->eip);
       proc_exit();
     }
diff --git a/trapasm.S b/trapasm.S
index 215a5a7..6eaa57d 100644
--- a/trapasm.S
+++ b/trapasm.S
@@ -33,6 +33,3 @@ forkret1:
   movl 4(%esp), %esp
   jmp trapret
 
-.globl  acpu
-acpu:
-  .long 0
diff --git a/ulib.c b/ulib.c
index 6c57b2d..29aa644 100644
--- a/ulib.c
+++ b/ulib.c
@@ -100,3 +100,15 @@ atoi(const char *s)
     n = n*10 + *s++ - '0';
   return n;
 }
+
+void*
+memmove(void *vdst, void *vsrc, int n)
+{
+  char *dst, *src;
+  
+  dst = vdst;
+  src = vsrc;
+  while(n-- > 0)
+    *dst++ = *src++;
+  return vdst;
+}
diff --git a/user.h b/user.h
index 75687b6..7d2e596 100644
--- a/user.h
+++ b/user.h
@@ -23,6 +23,7 @@ char* sbrk(int);
 int stat(char*, struct stat*);
 int puts(char*);
 char* strcpy(char*, char*);
+void *memmove(void*, void*, int);
 char* strchr(const char*, char c);
 int strcmp(const char*, const char*);
 void printf(int, char*, ...);
diff --git a/vectors.pl b/vectors.pl
index a6e4de8..499aa74 100755
--- a/vectors.pl
+++ b/vectors.pl
@@ -26,3 +26,24 @@ print "vectors:\n";
 for(my $i = 0; $i < 256; $i++){
     print "  .long vector$i\n";
 }
+
+# sample output:
+#   # handlers
+#   .text
+#   .globl alltraps
+#   .globl vector0
+#   vector0:
+#     pushl $0
+#     pushl $0
+#     jmp alltraps
+#   ...
+#   
+#   # vector table
+#   .data
+#   .globl vectors
+#   vectors:
+#     .long vector0
+#     .long vector1
+#     .long vector2
+#   ...
+
-- 
GitLab