diff --git a/defs.h b/defs.h
index 074ad8425d7b65aecdbd40bb2759847001697d3f..f2f8d73ca3dd24bc021c79682b8ea75586c8c5f5 100644
--- a/defs.h
+++ b/defs.h
@@ -128,12 +128,14 @@ void itrunc(struct inode*);
 void idecref(struct inode*);
 struct inode* iincref(struct inode*);
 void iput(struct inode*);
-struct inode* namei(char*, int, uint*, char**, struct inode**);
+struct inode* namei(char*);
+struct inode* nameiparent(char*, char**, int*);
 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);
-struct inode* mknod1(struct inode*, char*, short, short, short);
+struct inode* dircreat(struct inode*, char*, int, short, short, short);
+int dirlookup(struct inode*, char*, int, uint*, uint*);
 int unlink(char*);
 void iupdate(struct inode*);
 int link(char*, char*);
diff --git a/fs.c b/fs.c
index d8e648414339fa99e41a0d558c99cc7d068dd9ac..02ca8e91a9cac73026614bc1d83a74efb1ec53ea 100644
--- a/fs.c
+++ b/fs.c
@@ -465,7 +465,7 @@ writei(struct inode *ip, char *src, uint off, uint n)
 //   set *poff to the byte offset of the directory entry
 //   set *pinum to the inode number
 //   return 0.
-static int
+int
 dirlookup(struct inode *dp, char *name, int namelen, uint *poff, uint *pinum)
 {
   uint off;
@@ -485,8 +485,10 @@ dirlookup(struct inode *dp, char *name, int namelen, uint *poff, uint *pinum)
       if(memcmp(name, de->name, namelen) == 0 &&
          (namelen == DIRSIZ || de->name[namelen]== 0)){
         // entry matches path element
-        *poff = off + (uchar*)de - bp->data;
-        *pinum = de->inum;
+        if(poff)
+          *poff = off + (uchar*)de - bp->data;
+        if(pinum)
+          *pinum = de->inum;
         brelse(bp);
         return 0;
       }
@@ -499,9 +501,9 @@ dirlookup(struct inode *dp, char *name, int namelen, uint *poff, uint *pinum)
 // Write a new directory entry (name, ino) into the directory dp.
 // Caller must have locked dp.
 void
-dirwrite(struct inode *dp, char *name, uint ino)
+dirwrite(struct inode *dp, char *name, int namelen, uint ino)
 {
-  int i, off;
+  int off;
   struct dirent de;
 
   // Look for an empty dirent.
@@ -513,15 +515,37 @@ dirwrite(struct inode *dp, char *name, uint ino)
   }
 
   de.inum = ino;
-  for(i = 0; i < DIRSIZ && name[i]; i++)
-    de.name[i] = name[i];
-  for(; i < DIRSIZ; i++)
-    de.name[i] = '\0';
+  if(namelen > DIRSIZ)
+    namelen = DIRSIZ;
+  memmove(de.name, name, namelen);
+  memset(de.name+namelen, 0, DIRSIZ-namelen);
 
   if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
     panic("dirwrite");
 }
 
+// Create a new inode named name inside dp
+// and return its locked inode structure.
+// If name already exists, return 0.
+struct inode*
+dircreat(struct inode *dp, char *name, int namelen, short type, short major, short minor)
+{
+  struct inode *ip;
+
+  ip = ialloc(dp->dev, type);
+  if(ip == 0)
+    return 0;
+  ip->major = major;
+  ip->minor = minor;
+  ip->size = 0;
+  ip->nlink = 1;
+  iupdate(ip);
+
+  dirwrite(dp, name, namelen, ip->inum);
+
+  return ip;
+}
+
 // Paths
 
 // Skip over the next path element in path, 
@@ -564,21 +588,13 @@ skipelem(char *path, char **name, int *len)
 // NAMEI_DELETE: return locked parent inode, offset of dirent in *ret_off.
 //   return 0 if name doesn't exist.
 struct inode*
-namei(char *path, int mode, uint *ret_off,
-      char **ret_last, struct inode **ret_ip)
+_namei(char *path, int parent, char **pname, int *pnamelen)
 {
   struct inode *dp;
   char *name;
   int namelen;
   uint off, dev, inum;
 
-  if(ret_off)
-    *ret_off = 0xffffffff;
-  if(ret_last)
-    *ret_last = 0;
-  if(ret_ip)
-    *ret_ip = 0;
-
   if(*path == '/')
     dp = igetroot();
   else {
@@ -593,83 +609,63 @@ namei(char *path, int mode, uint *ret_off,
 
     if(dp->type != T_DIR)
       goto fail;
-
-    if(dirlookup(dp, name, namelen, &off, &inum) < 0){
-      if(mode == NAMEI_CREATE && *path == '\0'){
-        *ret_last = name;
-        return dp;
-      }
-      goto fail;
-    }
-
-    if(mode == NAMEI_DELETE && *path == '\0'){
-      // can't unlink . and ..
-      if((namelen == 1 && memcmp(name, ".", 1) == 0) ||
-         (namelen == 2 && memcmp(name, "..", 2) == 0)){
-        goto fail;
-      }
-      *ret_off = off;
+    
+    if(parent && *path == '\0'){
+      // Stop one level early.
+      *pname = name;
+      *pnamelen = namelen;
       return dp;
     }
 
+    if(dirlookup(dp, name, namelen, &off, &inum) < 0)
+      goto fail;
+
     dev = dp->dev;
     iput(dp);
     dp = iget(dev, inum);
     if(dp->type == 0 || dp->nlink < 1)
       panic("namei");
   }
-
-  if(mode == NAMEI_LOOKUP)
-    return dp;
-  if(mode == NAMEI_CREATE && ret_ip){
-    *ret_ip = dp;
+  if(parent)
     return 0;
-  }
-  goto fail;
+  return dp;
 
 fail:
   iput(dp);
   return 0;
 }
 
-// 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)
+namei(char *path)
 {
-  struct inode *ip, *dp;
-  char *last;
-
-  if((dp = namei(path, NAMEI_CREATE, 0, &last, 0)) == 0)
-    return 0;
-
-  ip = mknod1(dp, last, type, major, minor);
-
-  iput(dp);
-
-  return ip;
+  return _namei(path, 0, 0, 0);
 }
 
-// Create a new inode named name inside dp
-// and return its locked inode structure.
-// If name already exists, return 0.
 struct inode*
-mknod1(struct inode *dp, char *name, short type, short major, short minor)
+nameiparent(char *path, char **name, int *namelen)
 {
-  struct inode *ip;
+  return _namei(path, 1, name, namelen);
+}
 
-  ip = ialloc(dp->dev, type);
-  if(ip == 0)
-    return 0;
-  ip->major = major;
-  ip->minor = minor;
-  ip->size = 0;
-  ip->nlink = 1;
 
-  iupdate(ip);  // write new inode to disk
 
-  dirwrite(dp, name, ip->inum);
+// 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;
+  int namelen;
 
+  if((dp = nameiparent(path, &name, &namelen)) == 0)
+    return 0;
+  if(dirlookup(dp, name, namelen, 0, 0) >= 0){
+    iput(dp);
+    return 0;
+  }
+  ip = dircreat(dp, name, namelen, type, major, minor);
+  iput(dp);
   return ip;
 }
 
@@ -680,12 +676,15 @@ unlink(char *path)
   struct inode *ip, *dp;
   struct dirent de;
   uint off, inum, dev;
+  char *name;
+  int namelen;
 
-  dp = namei(path, NAMEI_DELETE, &off, 0, 0);
-  if(dp == 0)
+  if((dp = nameiparent(path, &name, &namelen)) == 0)
     return -1;
-
-  dev = dp->dev;
+  if(dirlookup(dp, name, namelen, &off, 0) < 0){
+    iput(dp);
+    return -1;
+  }
 
   if(readi(dp, (char*)&de, off, sizeof(de)) != sizeof(de) || de.inum == 0)
     panic("unlink no entry");
@@ -702,16 +701,13 @@ unlink(char *path)
   if(writei(dp, (char*)&de, off, sizeof(de)) != sizeof(de))
     panic("unlink dir write");
 
-  iupdate(dp);
+  dev = dp->dev;
   iput(dp);
 
   ip = iget(dev, inum);
-
   if(ip->nlink < 1)
     panic("unlink nlink < 1");
-
   ip->nlink--;
-
   iupdate(ip);
   iput(ip);
 
@@ -720,21 +716,26 @@ unlink(char *path)
 
 // Create the path new as a link to the same inode as old.
 int
-link(char *name1, char *name2)
+link(char *old, char *new)
 {
   struct inode *ip, *dp;
-  char *last;
+  char *name;
+  int namelen;
 
-  if((ip = namei(name1, NAMEI_LOOKUP, 0, 0, 0)) == 0)
+  if((ip = namei(old)) == 0)
     return -1;
   if(ip->type == T_DIR){
     iput(ip);
     return -1;
   }
-
   iunlock(ip);
-
-  if((dp = namei(name2, NAMEI_CREATE, 0, &last, 0)) == 0) {
+  
+  if((dp = nameiparent(new, &name, &namelen)) == 0){
+    idecref(ip);
+    return -1;
+  }
+  if(dirlookup(dp, name, namelen, 0, 0) >= 0){
+    iput(dp);
     idecref(ip);
     return -1;
   }
@@ -744,11 +745,12 @@ link(char *name1, char *name2)
     return -1;
   }
 
+  // LOCKING ERROR HERE!  TWO LOCKS HELD AT ONCE.
   ilock(ip);
   ip->nlink++;
   iupdate(ip);
 
-  dirwrite(dp, last, ip->inum);
+  dirwrite(dp, name, namelen, ip->inum);
   iput(dp);
   iput(ip);
 
diff --git a/proc.c b/proc.c
index 074dc1788b645faaf0a8e619bc3cfd377b7e0636..b09b738c4d1ca301ddf8289045b7d96dbe3de88e 100644
--- a/proc.c
+++ b/proc.c
@@ -203,7 +203,6 @@ scheduler(void)
 void
 sched(void)
 {
-
   if(cp->state == RUNNING)
     panic("sched running");
   if(!holding(&proc_table_lock))
@@ -219,7 +218,6 @@ sched(void)
 void
 yield(void)
 {
-
   acquire(&proc_table_lock);
   cp->state = RUNNABLE;
   sched();
@@ -422,9 +420,10 @@ procdump(void)
   [RUNNING]   "run   ",
   [ZOMBIE]    "zombie"
   };
-  int i;
+  int i, j;
   struct proc *p;
   char *state;
+  uint pc[10];
   
   for(i = 0; i < NPROC; i++) {
     p = &proc[i];
@@ -434,7 +433,13 @@ procdump(void)
       state = states[p->state];
     else
       state = "???";
-    cprintf("%d %s %s\n", p->pid, state, p->name);
+    cprintf("%d %s %s", p->pid, state, p->name);
+    if(p->state == SLEEPING) {
+      getcallerpcs((uint*)p->jmpbuf.ebp+2, pc);
+      for(j=0; j<10 && pc[j] != 0; j++)
+        cprintf(" %p", pc[j]);
+    }
+    cprintf("\n");
   }
 }
 
diff --git a/sysfile.c b/sysfile.c
index e5d4eaec767ee6d0a9d42c5003c2379a7fd019b5..cd00494ea5e721af6309f99c36a14fe32d342897 100644
--- a/sysfile.c
+++ b/sysfile.c
@@ -115,34 +115,41 @@ int
 sys_open(void)
 {
   struct inode *ip, *dp;
-  char *path;
+  char *path, *name;
+  int namelen;
   int omode;
-  int fd;
+  int fd, dev;
+  uint inum;
   struct file *f;
-  char *last;
 
   if(argstr(0, &path) < 0 || argint(1, &omode) < 0)
     return -1;
 
-  if(omode & O_CREATE){
-    dp = namei(path, NAMEI_CREATE, 0, &last, &ip);
-    if(dp){
-      ip = mknod1(dp, last, T_FILE, 0, 0);
-      iput(dp);
-      if(ip == 0)
-        return -1;
-    } else if(ip == 0){
+  switch(omode & O_CREATE){
+  default:
+  case 0: // regular open
+    if((ip = namei(path)) == 0)
       return -1;
-    } else if(ip->type == T_DIR){
-      iput(ip);
+    break;
+  
+  case O_CREATE:
+    if((dp = nameiparent(path, &name, &namelen)) == 0)
       return -1;
+    if(dirlookup(dp, name, namelen, 0, &inum) >= 0){
+      dev = dp->dev;
+      iput(dp);
+      ip = iget(dev, inum);
+    }else{
+      if((ip = dircreat(dp, name, namelen, T_FILE, 0, 0)) == 0){
+        iput(dp);
+        return -1;
+      }
+      iput(dp);
     }
-  } else {
-    ip = namei(path, NAMEI_LOOKUP, 0, 0, 0);
-    if(ip == 0)
-      return -1;
+    break;
   }
-  if(ip->type == T_DIR && ((omode & O_RDWR) || (omode & O_WRONLY))){
+
+  if(ip->type == T_DIR && (omode & (O_RDWR|O_WRONLY|O_CREATE))){
     iput(ip);
     return -1;
   }
@@ -201,18 +208,22 @@ sys_mkdir(void)
 {
   struct inode *nip;
   struct inode *dp;
-  char *path;
+  char *name, *path;
   struct dirent de;
-  char *last;
+  int namelen;
 
   if(argstr(0, &path) < 0)
     return -1;
 
-  dp = namei(path, NAMEI_CREATE, 0, &last, 0);
+  dp = nameiparent(path, &name, &namelen);
   if(dp == 0)
     return -1;
+  if(dirlookup(dp, name, namelen, 0, 0) >= 0){
+    iput(dp);
+    return -1;
+  }
 
-  nip = mknod1(dp, last, T_DIR, 0, 0);
+  nip = dircreat(dp, name, namelen, T_DIR, 0, 0);
   if(nip == 0){
     iput(dp);
     return -1;
@@ -245,22 +256,17 @@ sys_chdir(void)
   if(argstr(0, &path) < 0)
     return -1;
 
-  if((ip = namei(path, NAMEI_LOOKUP, 0, 0, 0)) == 0)
+  if((ip = namei(path)) == 0)
     return -1;
 
-  if(ip == cp->cwd) {
-    iput(ip);
-    return 0;
-  }
-
   if(ip->type != T_DIR) {
     iput(ip);
     return -1;
   }
 
+  iunlock(ip);
   idecref(cp->cwd);
   cp->cwd = ip;
-  iunlock(cp->cwd);
   return 0;
 }
 
@@ -324,8 +330,7 @@ sys_exec(void)
   if(argstr(0, &path) < 0 || argint(1, (int*)&argv) < 0)
     return -1;
 
-  ip = namei(path, NAMEI_LOOKUP, 0, 0, 0);
-  if(ip == 0)
+  if((ip = namei(path)) == 0)
     return -1;
 
   if(readi(ip, (char*)&elf, 0, sizeof(elf)) < sizeof(elf))