Project 2 solution.
authorBen Pfaff <blp@cs.stanford.edu>
Wed, 22 Sep 2004 07:38:28 +0000 (07:38 +0000)
committerBen Pfaff <blp@cs.stanford.edu>
Wed, 22 Sep 2004 07:38:28 +0000 (07:38 +0000)
solutions/p2.patch [new file with mode: 0644]

diff --git a/solutions/p2.patch b/solutions/p2.patch
new file mode 100644 (file)
index 0000000..8ff1be7
--- /dev/null
@@ -0,0 +1,850 @@
+diff -urp pintos.orig/src/constants.h pintos/src/constants.h
+--- pintos.orig/src/constants.h        2004-09-21 17:26:39.000000000 -0700
++++ pintos/src/constants.h     2004-09-22 00:34:01.000000000 -0700
+@@ -8,4 +8,4 @@
+ /*#define MACRONAME 1 */
+ /* Uncomment if if you've implemented thread_join(). */
+-/*#define THREAD_JOIN_IMPLEMENTED 1*/
++#define THREAD_JOIN_IMPLEMENTED 1
+Only in pintos/src: constants.h~
+diff -urp pintos.orig/src/threads/synch.c pintos/src/threads/synch.c
+--- pintos.orig/src/threads/synch.c    2004-09-19 21:29:53.000000000 -0700
++++ pintos/src/threads/synch.c 2004-09-22 00:30:05.000000000 -0700
+@@ -330,3 +330,35 @@ cond_name (const struct condition *cond)
+   return cond->name;
+ }
++\f
++void
++latch_init (struct latch *latch, const char *name) 
++{
++  latch->released = false;
++  lock_init (&latch->monitor_lock, name);
++  cond_init (&latch->rel_cond, name);
++}
++
++void
++latch_acquire (struct latch *latch) 
++{
++  lock_acquire (&latch->monitor_lock);
++  if (!latch->released) 
++    {
++      cond_wait (&latch->rel_cond, &latch->monitor_lock);
++      ASSERT (latch->released); 
++    }
++  lock_release (&latch->monitor_lock);
++}
++
++void
++latch_release (struct latch *latch) 
++{
++  lock_acquire (&latch->monitor_lock);
++  if (!latch->released)
++    {
++      latch->released = true;
++      cond_signal (&latch->rel_cond, &latch->monitor_lock);
++    }
++  lock_release (&latch->monitor_lock);
++}
+diff -urp pintos.orig/src/threads/synch.h pintos/src/threads/synch.h
+--- pintos.orig/src/threads/synch.h    2004-09-19 21:29:53.000000000 -0700
++++ pintos/src/threads/synch.h 2004-09-22 00:30:05.000000000 -0700
+@@ -44,4 +44,16 @@ void cond_signal (struct condition *, st
+ void cond_broadcast (struct condition *, struct lock *);
+ const char *cond_name (const struct condition *);
++/* Latch. */
++struct latch 
++  {
++    bool released;              /* Released yet? */
++    struct lock monitor_lock;   /* Monitor lock. */
++    struct condition rel_cond;  /* Signaled when released. */
++  };
++
++void latch_init (struct latch *, const char *);
++void latch_acquire (struct latch *);
++void latch_release (struct latch *);
++
+ #endif /* threads/synch.h */
+diff -urp pintos.orig/src/threads/thread.c pintos/src/threads/thread.c
+--- pintos.orig/src/threads/thread.c   2004-09-21 22:42:17.000000000 -0700
++++ pintos/src/threads/thread.c        2004-09-22 00:32:39.000000000 -0700
+@@ -13,6 +13,7 @@
+ #include "threads/synch.h"
+ #ifdef USERPROG
+ #include "userprog/process.h"
++#include "userprog/syscall.h"
+ #endif
+ /* Random value for struct thread's `magic' member.
+@@ -75,6 +76,7 @@ thread_init (void) 
+   init_thread (initial_thread, "main", PRI_DEFAULT);
+   initial_thread->status = THREAD_RUNNING;
+   initial_thread->tid = allocate_tid ();
++  sema_up (&initial_thread->can_die);
+ }
+ /* Starts preemptive thread scheduling by enabling interrupts.
+@@ -119,6 +121,7 @@ thread_create (const char *name, int pri
+   /* Initialize thread. */
+   init_thread (t, name, priority);
+   tid = t->tid = allocate_tid ();
++  list_push_back (&thread_current ()->children, &t->children_elem);
+   /* Stack frame for kernel_thread(). */
+   kf = alloc_frame (t, sizeof *kf);
+@@ -195,16 +198,36 @@ thread_tid (void) 
+ void
+ thread_exit (void) 
+ {
++  struct thread *t = thread_current ();
++  list_elem *e, *next;
++
+   ASSERT (!intr_context ());
+ #ifdef USERPROG
+   process_exit ();
+ #endif
++  syscall_exit ();
++  
++  /* Notify our parent that we're dying. */
++  latch_release (&t->ready_to_die);
++
++  /* Notify our children that they can die. */
++  for (e = list_begin (&t->children); e != list_end (&t->children);
++       e = next) 
++    {
++      struct thread *child = list_entry (e, struct thread, children_elem);
++      next = list_next (e);
++      list_remove (e);
++      sema_up (&child->can_die); 
++    }
++
++  /* Wait until our parent is ready for us to die. */
++  sema_down (&t->can_die);
+   /* Just set our status to dying and schedule another process.
+      We will be destroyed during the call to schedule_tail(). */
+   intr_disable ();
+-  thread_current ()->status = THREAD_DYING;
++  t->status = THREAD_DYING;
+   schedule ();
+   NOT_REACHED ();
+ }
+@@ -241,6 +264,26 @@ thread_block (void) 
+   thread_current ()->status = THREAD_BLOCKED;
+   schedule ();
+ }
++
++/* Waits for thread with tid CHILD_TID to die. */
++int
++thread_join (tid_t child_tid) 
++{
++  struct thread *cur = thread_current ();
++  list_elem *e;
++
++  for (e = list_begin (&cur->children); e != list_end (&cur->children); ) 
++    {
++      struct thread *child = list_entry (e, struct thread, children_elem);
++      e = list_next (e);
++      if (child->tid == child_tid) 
++        {
++          latch_acquire (&child->ready_to_die);
++          return child->ret_code; 
++        }
++    }
++  return -1;
++}
\f
+ /* Idle thread.  Executes when no other thread is ready to run. */
+ static void
+@@ -306,6 +349,12 @@ init_thread (struct thread *t, const cha
+   strlcpy (t->name, name, sizeof t->name);
+   t->stack = (uint8_t *) t + PGSIZE;
+   t->priority = priority;
++  latch_init (&t->ready_to_die, "ready-to-die");
++  sema_init (&t->can_die, 0, "can-die");
++  list_init (&t->children);
++  t->ret_code = -1;
++  list_init (&t->fds);
++  t->next_handle = 2;
+   t->magic = THREAD_MAGIC;
+ }
+Only in pintos/src/threads: thread.c.orig
+Only in pintos/src/threads: thread.c.rej
+Only in pintos/src/threads: thread.c~
+diff -urp pintos.orig/src/threads/thread.h pintos/src/threads/thread.h
+--- pintos.orig/src/threads/thread.h   2004-09-20 15:29:18.000000000 -0700
++++ pintos/src/threads/thread.h        2004-09-22 00:30:05.000000000 -0700
+@@ -4,6 +4,7 @@
+ #include <debug.h>
+ #include <list.h>
+ #include <stdint.h>
++#include "threads/synch.h"
+ /* States in a thread's life cycle. */
+ enum thread_status
+@@ -89,12 +90,23 @@ struct thread
+     uint8_t *stack;                     /* Saved stack pointer. */
+     int priority;                       /* Priority. */
++    /* Members for implementing thread_join(). */
++    struct latch ready_to_die;          /* Release when thread about to die. */
++    struct semaphore can_die;           /* Up when thread allowed to die. */
++    struct list children;               /* List of child threads. */
++    list_elem children_elem;            /* Element of `children' list. */
++    int ret_code;                       /* Return status. */
++
+     /* Shared between thread.c and synch.c. */
+     list_elem elem;                     /* List element. */
+ #ifdef USERPROG
+     /* Owned by userprog/process.c. */
+     uint32_t *pagedir;                  /* Page directory. */
++
++    /* Owned by syscall.c. */
++    struct list fds;                    /* List of file descriptors. */
++    int next_handle;                    /* Next handle value. */
+ #endif
+     /* Owned by thread.c */
+@@ -117,7 +129,7 @@ void thread_yield (void);
+ void thread_block (void);
+ /* This function will be implemented in problem 1-2. */
+-void thread_join (tid_t);
++int thread_join (tid_t);
+ /* These functions will be implemented in problem 1-3. */
+ void thread_set_priority (int);
+Only in pintos/src/userprog: build
+diff -urp pintos.orig/src/userprog/exception.c pintos/src/userprog/exception.c
+--- pintos.orig/src/userprog/exception.c       2004-09-20 12:06:58.000000000 -0700
++++ pintos/src/userprog/exception.c    2004-09-22 00:30:05.000000000 -0700
+@@ -134,6 +134,13 @@ page_fault (struct intr_frame *f) 
+   write = (f->error_code & PF_W) != 0;
+   user = (f->error_code & PF_U) != 0;
++  if (!user) 
++    {
++      f->eip = (void (*) (void)) f->eax;
++      f->eax = 0;
++      return;
++    }
++
+   /* To implement virtual memory, delete the rest of the function
+      body, and replace it with code that brings in the page to
+      which fault_addr refers. */
+diff -urp pintos.orig/src/userprog/pagedir.c pintos/src/userprog/pagedir.c
+--- pintos.orig/src/userprog/pagedir.c 2004-09-21 22:42:17.000000000 -0700
++++ pintos/src/userprog/pagedir.c      2004-09-22 00:32:23.000000000 -0700
+@@ -99,7 +99,7 @@ pagedir_set_page (uint32_t *pd, void *up
+   ASSERT (pg_ofs (upage) == 0);
+   ASSERT (pg_ofs (kpage) == 0);
+   ASSERT (upage < PHYS_BASE);
+-  ASSERT (lookup_page (pd, upage, false) == NULL);
++  ASSERT (pagedir_get_page (pd, upage) == NULL);
+   pte = lookup_page (pd, upage, true);
+   if (pte != NULL) 
+Only in pintos/src/userprog: pagedir.c.orig
+Only in pintos/src/userprog: pagedir.c.rej
+Only in pintos/src/userprog: pagedir.c.rej~
+Only in pintos/src/userprog: pagedir.c~
+diff -urp pintos.orig/src/userprog/process.c pintos/src/userprog/process.c
+--- pintos.orig/src/userprog/process.c 2004-09-21 22:42:17.000000000 -0700
++++ pintos/src/userprog/process.c      2004-09-22 00:30:05.000000000 -0700
+@@ -182,7 +182,7 @@ struct Elf32_Phdr
+ #define PF_R 4          /* Readable. */
+ static bool load_segment (struct file *, const struct Elf32_Phdr *);
+-static bool setup_stack (void **esp);
++static bool setup_stack (const char *cmdline, void **esp);
+ /* Aborts loading an executable, with an error message. */
+ #define LOAD_ERROR(MSG)                                         \
+@@ -198,13 +198,15 @@ static bool setup_stack (void **esp);
+    and its initial stack pointer into *ESP.
+    Returns true if successful, false otherwise. */
+ bool
+-load (const char *filename, void (**eip) (void), void **esp) 
++load (const char *cmdline, void (**eip) (void), void **esp) 
+ {
+   struct thread *t = thread_current ();
++  char filename[NAME_MAX + 2];
+   struct Elf32_Ehdr ehdr;
+   struct file *file = NULL;
+   off_t file_ofs;
+   bool success = false;
++  char *cp;
+   int i;
+   /* Allocate page directory. */
+@@ -212,6 +214,14 @@ load (const char *filename, void (**eip)
+   if (t->pagedir == NULL)
+     LOAD_ERROR (("page directory allocation failed"));
++  /* Extract filename from command line. */
++  while (*cmdline == ' ')
++    cmdline++;
++  strlcpy (filename, cmdline, sizeof filename);
++  cp = strchr (filename, ' ');
++  if (cp != NULL)
++    *cp = '\0';
++
+   /* Open executable file. */
+   file = filesys_open (filename);
+   if (file == NULL)
+@@ -269,7 +279,7 @@ load (const char *filename, void (**eip)
+     }
+   /* Set up stack. */
+-  if (!setup_stack (esp))
++  if (!setup_stack (cmdline, esp))
+     goto done;
+   /* Start address. */
+@@ -371,10 +381,80 @@ load_segment (struct file *file, const s
+   return true;
+ }
+-/* Create a minimal stack by mapping a zeroed page at the top of
+-   user virtual memory. */
++static void
++reverse (int argc, char **argv) 
++{
++  for (; argc > 1; argc -= 2, argv++) 
++    {
++      char *tmp = argv[0];
++      argv[0] = argv[argc - 1];
++      argv[argc - 1] = tmp;
++    }
++}
++
++static void *
++push (uint8_t *kpage, size_t *ofs, const void *buf, size_t size) 
++{
++  size_t padsize = ROUND_UP (size, sizeof (uint32_t));
++  if (*ofs < padsize)
++    return NULL;
++
++  *ofs -= padsize;
++  memcpy (kpage + *ofs + (padsize - size), buf, size);
++  return kpage + *ofs + (padsize - size);
++}
++
++static bool
++init_cmdline (uint8_t *kpage, uint8_t *upage, const char *cmdline,
++              void **esp) 
++{
++  size_t ofs = PGSIZE;
++  char *const null = NULL;
++  char *cmdline_copy;
++  char *karg, *saveptr;
++  int argc;
++  char **argv;
++
++  /* Push command line string. */
++  cmdline_copy = push (kpage, &ofs, cmdline, strlen (cmdline) + 1);
++  if (cmdline_copy == NULL)
++    return false;
++
++  if (push (kpage, &ofs, &null, sizeof null) == NULL)
++    return false;
++
++  /* Parse command line into arguments
++     and push them in reverse order. */
++  argc = 0;
++  for (karg = strtok_r (cmdline_copy, " ", &saveptr); karg != NULL;
++       karg = strtok_r (NULL, " ", &saveptr))
++    {
++      char *uarg = upage + (karg - (char *) kpage);
++      if (push (kpage, &ofs, &uarg, sizeof uarg) == NULL)
++        return false;
++      argc++;
++    }
++
++  /* Reverse the order of the command line arguments. */
++  argv = (char **) (upage + ofs);
++  reverse (argc, (char **) (kpage + ofs));
++
++  /* Push argv, argc, "return address". */
++  if (push (kpage, &ofs, &argv, sizeof argv) == NULL
++      || push (kpage, &ofs, &argc, sizeof argc) == NULL
++      || push (kpage, &ofs, &null, sizeof null) == NULL)
++    return false;
++
++  /* Set initial stack pointer. */
++  *esp = upage + ofs;
++  return true;
++}
++
++/* Create a minimal stack for T by mapping a page at the
++   top of user virtual memory.  Fills in the page using CMDLINE
++   and sets *ESP to the stack pointer. */
+ static bool
+-setup_stack (void **esp) 
++setup_stack (const char *cmdline, void **esp) 
+ {
+   uint8_t *kpage;
+   bool success = false;
+@@ -382,9 +462,9 @@ setup_stack (void **esp) 
+   kpage = palloc_get (PAL_USER | PAL_ZERO);
+   if (kpage != NULL) 
+     {
+-      success = install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage);
+-      if (success)
+-        *esp = PHYS_BASE;
++      uint8_t *upage = ((uint8_t *) PHYS_BASE) - PGSIZE;
++      if (install_page (upage, kpage))
++        success = init_cmdline (kpage, upage, cmdline, esp);
+       else
+         palloc_free (kpage);
+     }
+Only in pintos/src/userprog: process.c.orig
+diff -urp pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c
+--- pintos.orig/src/userprog/syscall.c 2004-09-05 22:19:19.000000000 -0700
++++ pintos/src/userprog/syscall.c      2004-09-22 00:30:05.000000000 -0700
+@@ -1,20 +1,429 @@
+ #include "userprog/syscall.h"
+ #include <stdio.h>
++#include <string.h>
+ #include <syscall-nr.h>
++#include "userprog/process.h"
++#include "userprog/pagedir.h"
++#include "devices/kbd.h"
++#include "filesys/filesys.h"
++#include "filesys/file.h"
++#include "threads/init.h"
+ #include "threads/interrupt.h"
++#include "threads/malloc.h"
++#include "threads/mmu.h"
++#include "threads/palloc.h"
+ #include "threads/thread.h"
++typedef int syscall_function (int, int, int);
++
++static int sys_halt (void);
++static int sys_exit (int status);
++static int sys_exec (const char *ufile);
++static int sys_join (tid_t);
++static int sys_create (const char *ufile, unsigned initial_size);
++static int sys_remove (const char *ufile);
++static int sys_open (const char *ufile);
++static int sys_filesize (int handle);
++static int sys_read (int handle, void *udst_, unsigned size);
++static int sys_write (int handle, void *usrc_, unsigned size);
++static int sys_seek (int handle, unsigned position);
++static int sys_tell (int handle);
++static int sys_close (int handle);
++
++struct syscall 
++  {
++    size_t arg_cnt;
++    syscall_function *func;
++  };
++
++struct syscall syscall_table[] = 
++  {
++    {0, (syscall_function *) sys_halt},
++    {1, (syscall_function *) sys_exit},
++    {1, (syscall_function *) sys_exec},
++    {1, (syscall_function *) sys_join},
++    {2, (syscall_function *) sys_create},
++    {1, (syscall_function *) sys_remove},
++    {1, (syscall_function *) sys_open},
++    {1, (syscall_function *) sys_filesize},
++    {3, (syscall_function *) sys_read},
++    {3, (syscall_function *) sys_write},
++    {2, (syscall_function *) sys_seek},
++    {1, (syscall_function *) sys_tell},
++    {1, (syscall_function *) sys_close},
++  };
++static const int syscall_cnt = sizeof syscall_table / sizeof *syscall_table;
++
+ static void syscall_handler (struct intr_frame *);
++static void copy_in (void *, const void *, size_t);
++
++static struct lock fs_lock;
+ void
+ syscall_init (void) 
+ {
+   intr_register (0x30, 3, INTR_ON, syscall_handler, "syscall");
++  lock_init (&fs_lock, "fs");
+ }
+ static void
+ syscall_handler (struct intr_frame *f) 
+ {
+-  printf ("system call!\n");
++  struct syscall *s;
++  int call_nr;
++  int args[3];
++
++  copy_in (&call_nr, f->esp, sizeof call_nr);
++  if (call_nr < 0 || call_nr >= syscall_cnt)
++    {
++      printf ("bad syscall number %d\n", call_nr);
++      thread_exit ();
++    }
++
++  s = syscall_table + call_nr;
++  ASSERT (s->arg_cnt <= sizeof args / sizeof *args);
++  memset (args, 0, sizeof args);
++  copy_in (args, (uint32_t *) f->esp + 1, sizeof *args * s->arg_cnt);
++  f->eax = s->func (args[0], args[1], args[2]);
++}
++
++static bool
++verify_user (const void *uaddr) 
++{
++  return pagedir_get_page (thread_current ()->pagedir, uaddr) != NULL;
++}
++
++static inline bool get_user (uint8_t *dst, const uint8_t *usrc) {
++  int eax;
++  asm ("movl $1f, %%eax; movb %2, %%al; movb %%al, %0; 1:"
++       : "=m" (*dst), "=&a" (eax) : "m" (*usrc));
++  return eax != 0;
++}
++
++static inline bool put_user (uint8_t *udst, uint8_t byte) {
++  int eax;
++  asm ("movl $1f, %%eax; movb %b2, %0; 1:"
++       : "=m" (*udst), "=&a" (eax) : "r" (byte));
++  return eax != 0;
++}
++
++static void
++copy_in (void *dst_, const void *usrc_, size_t size) 
++{
++  uint8_t *dst = dst_;
++  const uint8_t *usrc = usrc_;
++
++  for (; size > 0; size--, dst++, usrc++) 
++    if (usrc >= (uint8_t *) PHYS_BASE || !get_user (dst, usrc)) 
++      thread_exit ();
++}
++
++static char *
++copy_in_string (const char *us) 
++{
++  char *ks;
++  size_t length;
++
++  ks = palloc_get (0);
++  if (ks == NULL) 
++    {
++      printf ("copy_in_string: out of memory\n");
++      thread_exit ();
++    }
++
++  for (length = 0; length < PGSIZE; length++)
++    {
++      if (us >= (char *) PHYS_BASE || !get_user (ks + length, us++)) 
++        {
++          printf ("bad user reference (%p)\n", us + length);
++          thread_exit (); 
++        }
++      
++      if (ks[length] == '\0')
++        return ks;
++    }
++
++  printf ("copy_in_string: string too long\n");
++  palloc_free (ks);
++  thread_exit ();
++}
++
++static int
++sys_halt (void)
++{
++  power_off ();
++}
++
++static int
++sys_exit (int ret_code) 
++{
++  thread_current ()->ret_code = ret_code;
++  thread_exit ();
++  NOT_REACHED ();
++}
++
++static int
++sys_exec (const char *ufile) 
++{
++  tid_t tid;
++  char *kfile = copy_in_string (ufile);
++
++  lock_acquire (&fs_lock);
++  tid = process_execute (kfile);
++  lock_release (&fs_lock);
++
++  palloc_free (kfile);
++
++  return tid;
++}
++
++static int
++sys_join (tid_t child) 
++{
++  return thread_join (child);
++}
++
++static int
++sys_create (const char *ufile, unsigned initial_size) 
++{
++  char *kfile = copy_in_string (ufile);
++  bool ok;
++  
++  lock_acquire (&fs_lock);
++  ok = filesys_create (kfile, initial_size);
++  lock_release (&fs_lock);
++
++  palloc_free (kfile);
++
++  return ok;
++}
++
++static int
++sys_remove (const char *ufile) 
++{
++  char *kfile = copy_in_string (ufile);
++  bool ok;
++  
++  lock_acquire (&fs_lock);
++  ok = filesys_remove (kfile);
++  lock_release (&fs_lock);
++
++  palloc_free (kfile);
++
++  return ok;
++}
++
++struct fildes
++  {
++    list_elem elem;
++    struct file *file;
++    int handle;
++  };
++
++static int
++sys_open (const char *ufile) 
++{
++  char *kfile = copy_in_string (ufile);
++  struct fildes *fd;
++  int handle = -1;
++
++  fd = malloc (sizeof *fd);
++  if (fd == NULL)
++    goto exit;
++
++  lock_acquire (&fs_lock);
++  fd->file = filesys_open (kfile);
++  if (fd->file != NULL)
++    {
++      struct thread *cur = thread_current ();
++      handle = fd->handle = cur->next_handle++;
++      list_push_front (&cur->fds, &fd->elem);
++    }
++  else 
++    free (fd);
++  lock_release (&fs_lock);
++
++ exit:
++  palloc_free (kfile);
++  return handle;
++}
++
++static struct fildes *
++lookup_fd (int handle) 
++{
++  struct thread *cur = thread_current ();
++  list_elem *e;
++  
++  for (e = list_begin (&cur->fds); e != list_end (&cur->fds);
++       e = list_next (e))
++    {
++      struct fildes *fd = list_entry (e, struct fildes, elem);
++      if (fd->handle == handle)
++        return fd;
++    }
++
++  printf ("no handle %d\n", handle);
+   thread_exit ();
+ }
++
++static int
++sys_filesize (int handle) 
++{
++  struct fildes *fd = lookup_fd (handle);
++  int size;
++
++  lock_acquire (&fs_lock);
++  size = file_length (fd->file);
++  lock_release (&fs_lock);
++
++  return size;
++}
++
++static int
++sys_read (int handle, void *udst_, unsigned size) 
++{
++  uint8_t *udst = udst_;
++  struct fildes *fd;
++  int bytes_read = 0;
++
++  if (handle == STDIN_FILENO) 
++    {
++      for (bytes_read = 0; (size_t) bytes_read < size; bytes_read++)
++        if (udst >= (uint8_t *) PHYS_BASE || !put_user (udst++, kbd_getc ()))
++          thread_exit ();
++      return bytes_read;
++    }
++
++  lock_acquire (&fs_lock);
++  fd = lookup_fd (handle);
++  while (size > 0) 
++    {
++      size_t page_left = PGSIZE - pg_ofs (udst);
++      size_t read_amt = size < page_left ? size : page_left;
++      off_t retval;
++
++      if (!verify_user (udst)) 
++        {
++          lock_release (&fs_lock);
++          thread_exit ();
++        }
++      
++      retval = file_read (fd->file, udst, read_amt);
++      if (retval < 0)
++        {
++          if (bytes_read == 0)
++            bytes_read = -1; 
++          break;
++        }
++
++      bytes_read += retval;
++      if (retval != (off_t) read_amt)
++        break;
++
++      udst += retval;
++      size -= retval;
++    }
++  lock_release (&fs_lock);
++  
++  return bytes_read;
++}
++
++static int
++sys_write (int handle, void *usrc_, unsigned size) 
++{
++  uint8_t *usrc = usrc_;
++  struct fildes *fd = NULL;
++  int bytes_written = 0;
++
++  lock_acquire (&fs_lock);
++  if (handle != STDOUT_FILENO)
++    fd = lookup_fd (handle);
++  while (size > 0) 
++    {
++      size_t page_left = PGSIZE - pg_ofs (usrc);
++      size_t write_amt = size < page_left ? size : page_left;
++      off_t retval;
++
++      if (!verify_user (usrc)) 
++        {
++          lock_release (&fs_lock);
++          thread_exit ();
++        }
++
++      if (handle == STDOUT_FILENO)
++        {
++          putbuf (usrc, write_amt);
++          retval = write_amt;
++        }
++      else
++        retval = file_write (fd->file, usrc, write_amt);
++      if (retval < 0) 
++        {
++          if (bytes_written == 0)
++            bytes_written = -1;
++          break;
++        }
++
++      bytes_written += retval;
++      if (retval != (off_t) write_amt)
++        break;
++
++      usrc += retval;
++      size -= retval;
++    }
++  lock_release (&fs_lock);
++
++  return bytes_written;
++}
++
++static int
++sys_seek (int handle, unsigned position) 
++{
++  struct fildes *fd = lookup_fd (handle);
++  
++  lock_acquire (&fs_lock);
++  file_seek (fd->file, position);
++  lock_release (&fs_lock);
++
++  return 0;
++}
++
++static int
++sys_tell (int handle) 
++{
++  struct fildes *fd = lookup_fd (handle);
++  unsigned position;
++  
++  lock_acquire (&fs_lock);
++  position = file_tell (fd->file);
++  lock_release (&fs_lock);
++
++  return position;
++}
++
++static int
++sys_close (int handle) 
++{
++  struct fildes *fd = lookup_fd (handle);
++  lock_acquire (&fs_lock);
++  file_close (fd->file);
++  lock_release (&fs_lock);
++  list_remove (&fd->elem);
++  free (fd);
++  return 0;
++}
++
++void
++syscall_exit (void) 
++{
++  struct thread *cur = thread_current ();
++  list_elem *e, *next;
++  
++  for (e = list_begin (&cur->fds); e != list_end (&cur->fds); e = next)
++    {
++      struct fildes *fd = list_entry (e, struct fildes, elem);
++      next = list_next (e);
++      file_close (fd->file);
++      free (fd);
++    }
++}
+diff -urp pintos.orig/src/userprog/syscall.h pintos/src/userprog/syscall.h
+--- pintos.orig/src/userprog/syscall.h 2004-09-05 22:38:45.000000000 -0700
++++ pintos/src/userprog/syscall.h      2004-09-22 00:30:05.000000000 -0700
+@@ -2,5 +2,6 @@
+ #define USERPROG_SYSCALL_H
+ void syscall_init (void);
++void syscall_exit (void);
+ #endif /* userprog/syscall.h */