diff --git a/fs.c b/fs.c
index 530ef039ca5e6afcaa8c84e7a612f5552f050587..ed6297035f267c7563a8773c02197fe5e265cb8d 100644
--- a/fs.c
+++ b/fs.c
@@ -385,6 +385,7 @@ struct inode *
 namei(char *path, int mode, uint *ret_off)
 {
   struct inode *dp;
+  struct proc *p = curproc[cpu()];
   char *cp = path, *cp1;
   uint off, dev;
   struct buf *bp;
@@ -392,7 +393,12 @@ namei(char *path, int mode, uint *ret_off)
   int i, atend;
   unsigned ninum;
   
-  dp = iget(rootdev, 1);
+  if (*cp == '/') dp = iget(rootdev, 1);
+  else {
+    dp = p->cwd;
+    iincref(dp);
+    ilock(dp);
+  }
 
   while(*cp == '/')
     cp++;
diff --git a/fstests.c b/fstests.c
index d6f630e512a5745881563b063b12c5ce33b7f40f..e42851964de31441bc10c3759b1891ed192908b7 100644
--- a/fstests.c
+++ b/fstests.c
@@ -1,3 +1,5 @@
+#include "types.h"
+#include "stat.h"
 #include "user.h"
 #include "fcntl.h"
 
diff --git a/ls.c b/ls.c
index 607a857d15abfae19158d6bdbe6071c6d6b110f7..3441eba9343be7b8aa8a10891c14cd36b6f1d926 100644
--- a/ls.c
+++ b/ls.c
@@ -15,22 +15,31 @@ main(int argc, char *argv[])
   uint sz;
   int i;
 
-  if(argc > 1){
-    puts("Usage: ls\n");
+  if(argc > 2){
+    puts("Usage: ls [dir]\n");
     exit();
   }
 
-  fd = open(".", 0);
-  if(fd < 0){
-    printf(2, "ls: cannot open .\n");
-    exit();
+  if (argc == 2) {
+    fd = open(argv[1], 0);
+    if(fd < 0){
+      printf(2, "ls: cannot open dir %s\n", argv[1]);
+      exit();
+    } 
+  } else {
+    fd = open(".", 0);
+    if(fd < 0){
+      printf(2, "ls: cannot open .\n");
+      exit();
+    }
   }
+
   if (fstat(fd, &st) < 0) {
-    printf(2, "ls: cannot open .\n");
+    printf(2, "ls: cannot stat dir\n");
     exit();
   }
   if (st.st_type != T_DIR) {
-    printf(2, "ls: . is not a dir\n");
+    printf(2, "ls: dir is not a directory\n");
   }
   sz = st.st_size;
   for(off = 0; off < sz; off += sizeof(struct dirent)) {
@@ -39,9 +48,10 @@ main(int argc, char *argv[])
       break;
     }
     if (dirent.inum != 0) {
+      // xxx prepend to name the pathname supplied to ls (e.g. .. in ls ..)
       if (stat (dirent.name, &st) < 0)  {
-	printf(1, "stat: failed\n");
-	break;
+	printf(1, "stat: failed %s\n", dirent.name);
+	continue;
       }
       for (i = 0; i < DIRSIZ; i++) {
 	if (dirent.name[i] != '\0')
diff --git a/proc.c b/proc.c
index a7908e230593fc2f0f0d319e6716e15d69e22643..9f7064f668d1578c1c750757445a251948323d17 100644
--- a/proc.c
+++ b/proc.c
@@ -132,8 +132,8 @@ copyproc(struct proc* p)
       fd_incref(np->fds[i]);
   }
 
-  // np->cwd = p->cwd;
-  // iincref(p->cwd);
+  np->cwd = p->cwd;
+  iincref(p->cwd);
 
   return np;
 }
diff --git a/sh.c b/sh.c
index e2b8959a8269951b50d8046a8c72765534b54c36..9e0cb3f73bca2b2c93f1d1ca2ddcfa025c75ee15 100644
--- a/sh.c
+++ b/sh.c
@@ -15,15 +15,20 @@ main(void)
 
   while(1){
     puts("$ ");
+    memset (buf, '\0', sizeof(buf));
     gets(buf, sizeof(buf));
     if(buf[0] == '\0')
       continue;
     pid = fork();
     if(pid == 0){
       parse(buf);
-      exec(buf, args);
-      printf(1, "%s: not found\n", buf);
-      exit();
+      if (buf[0] == 'c' && buf[1] == 'd' && buf[2] == '\0') {  // cd
+	chdir(&buf[3]);
+      } else {
+	exec(buf, args);
+	printf(1, "%s: not found\n", buf);
+	exit();
+      }
     }
     if(pid > 0)
       wait();
@@ -39,11 +44,12 @@ parse(char buf[])
   for (i = 0; buf[i] != '\0'; i++) {
     if (buf[i] == ' ') {
       buf[i] = '\0';
-      args[j++] = buf + i+1;
+      args[j++] = buf + i + 1;
       if (j >= 100) {
 	printf(2, "too many args\n");
 	exit();
       }
     }
   }
+  args[j] = '\0';
 }
diff --git a/syscall.c b/syscall.c
index 0a017c76e77d204544656a4b0529266257cddc9b..31b61892e543b110c2feca46bdf3a8b69909b2c2 100644
--- a/syscall.c
+++ b/syscall.c
@@ -309,7 +309,8 @@ sys_mkdir(void)
     return -1;
 
   nip = mknod (cp->mem + arg0, T_DIR, 0, 0);
-
+  
+  memset (de.name, '\0', DIRSIZ);
   de.name[0] = '.';
   de.inum = nip->inum;
   writei (nip, (char *) &de, 0, sizeof(de));
@@ -324,6 +325,43 @@ sys_mkdir(void)
   return (nip == 0) ? -1 : 0;
 }
 
+
+int
+sys_chdir(void)
+{
+  struct proc *cp = curproc[cpu()];
+  struct inode *ip;
+    uint arg0;
+  int l;
+  
+  if(fetcharg(0, &arg0) < 0) 
+    return -1;
+
+  if((l = checkstring(arg0)) < 0)
+    return -1;
+
+  if(l >= DIRSIZ)
+    return -1;
+
+  if ((ip = namei(cp->mem + arg0, NAMEI_LOOKUP, 0)) == 0)
+    return -1;
+  
+  if (ip == cp->cwd) {
+    iput (ip);
+    return 0;
+  }
+
+  if (ip->type != T_DIR) {
+    iput(ip);
+    return 0;
+  }
+
+  idecref(cp->cwd);
+  cp->cwd = ip;
+  iunlock(cp->cwd);
+  return 0;
+}
+
 int
 sys_unlink(void)
 {
@@ -599,6 +637,9 @@ syscall(void)
   case SYS_mkdir:
     ret = sys_mkdir();
     break;
+  case SYS_chdir:
+    ret = sys_chdir();
+    break;
   default:
     cprintf("unknown sys call %d\n", num);
     // XXX fault
diff --git a/syscall.h b/syscall.h
index d5e2dbe25b5bcc940f3a11b863a4c4a7b8f52337..2209cf24090949748e9fae4bf490a35e03d1f0ce 100644
--- a/syscall.h
+++ b/syscall.h
@@ -14,4 +14,5 @@
 #define SYS_fstat 17
 #define SYS_link 18
 #define SYS_mkdir 19
+#define SYS_chdir 20
 
diff --git a/trap.c b/trap.c
index 9d1482f580b569e879408b4771fd6236fc9da701..d20199093b1eac760848d0f3c6c18372001b1e9c 100644
--- a/trap.c
+++ b/trap.c
@@ -128,7 +128,7 @@ trap(struct trapframe *tf)
   cprintf("trap %d from cpu %d eip %x\n", v, cpu(), tf->eip);
   if(curproc[cpu()])
     cprintf("pid %d\n", curproc[cpu()]->pid);
-  panic("trap");
+  //  panic("trap");
 
   return;
 }
diff --git a/user.h b/user.h
index 6022bc9e6da85dec478e3c0dc7dfc45a9d90b7e1..27f2cfb56b8f58d6d784ed830640da527a0118e5 100644
--- a/user.h
+++ b/user.h
@@ -17,8 +17,9 @@ int unlink (char*);
 int fstat (int fd, struct stat *stat);
 int link(char *, char *);
 int mkdir(char *);
-int stat(char *, struct stat *stat);
+int chdir(char *);
 
+int stat(char *, struct stat *stat);
 int puts(char*);
 char* strcpy(char*, char*);
 void printf(int fd, char *fmt, ...);
diff --git a/usertests.c b/usertests.c
index 20155c2f2b43b86052582126cc960dc374261438..0d3e2bcde46b4f90eb2495c80427ff4a8233b4bc 100644
--- a/usertests.c
+++ b/usertests.c
@@ -1,3 +1,5 @@
+#include "types.h"
+#include "stat.h"
 #include "user.h"
 #include "fcntl.h"
 
diff --git a/usys.S b/usys.S
index 8f937130a0f51382370f821b2479a47b156946d6..16d84e74a3a2b63111cb6ac8093e84ca7a9e8e23 100644
--- a/usys.S
+++ b/usys.S
@@ -24,3 +24,4 @@ STUB(unlink)
 STUB(fstat)
 STUB(link)
 STUB(mkdir)
+STUB(chdir)