File system project updates:
authorBen Pfaff <blp@cs.stanford.edu>
Mon, 22 May 2006 18:47:19 +0000 (18:47 +0000)
committerBen Pfaff <blp@cs.stanford.edu>
Mon, 22 May 2006 18:47:19 +0000 (18:47 +0000)
Require support for . and ...
Add inumber system call, so getcwd can be implemented,
and inode_get_inumber() function.
Implement these features in the reference solution.
Add "pwd" example program and require explanation of it in design
document.
Implement "cd" in shell.
Add -l option to "ls" example program.
Describe interpretation of unusual file names.
Remove filesys_self_test().
Update TODO.

16 files changed:
TODO
doc/filesys.texi
doc/filesys.tmpl
solutions/p4.patch
src/examples/.cvsignore
src/examples/Makefile
src/examples/ls.c
src/examples/pwd.c [new file with mode: 0644]
src/filesys/filesys.c
src/filesys/filesys.h
src/filesys/inode.c
src/filesys/inode.h
src/lib/syscall-nr.h
src/lib/user/syscall.c
src/lib/user/syscall.h
tests/.cvsignore

diff --git a/TODO b/TODO
index 38836157fc052fe3a07b91d5d03bea94c64ac104..345973eacb255b35e135e5f1c4c8ef9d6d015ca2 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,78 +1,44 @@
 -*- text -*-
 
-Godmar says:
-
-- In Project 2, we're missing tests that pass arguments to system calls
-that span multiple pages, where some are mapped and some are not. 
-An implementation that only checks the first page, rather than all pages 
-that can be touched during a call to read()/write() passes all tests.
-
-- Need some tests that test that illegal accesses lead to process
-termination. I have written some, will add them. In P2, obviously, 
-this would require that the students break this functionality since 
-the page directory is initialized for them, still it would be good 
-to have.
-
-- There does not appear to be a test that checks that they close all
-fd's on exit.  Idea: add statistics & self-diagnostics code to palloc.c
-and malloc.c.  Self-diagnostics code could be used for debugging.
-The statistics code would report how much kernel memory is free.
-Add a system call "get_kernel_memory_information".  User programs
-could engage in a variety of activities and notice leaks by checking
-the kernel memory statistics.
-
-From: Godmar Back <godmar@gmail.com>
-Subject: on caching in project 4
-To: Ben Pfaff <blp@cs.stanford.edu>
-Date: Mon, 9 Jan 2006 20:58:01 -0500
-
-here's an idea for future semesters.
-
-I'm in the middle of project 4, I've started by implementing a buffer
-cache and plugging it into the existing filesystem.  Along the way I
-was wondering how we could test the cache.
-
-Maybe one could adopt a similar testing strategy as in project 1 for
-the MLQFS scheduler: add a function that reads "get_cache_accesses()"
-and a function "get_cache_hits()".  Then create a version of pintos
-that creates access traces for a to-be-determined workload.  Run an
-off-line analysis that would determine how many hits a perfect cache
-would have (MAX), and how much say an LRU strategy would give (MIN).
-Then add a fudge factor to account for different index strategies and
-test that the reported number of cache hits/accesses is within (MIN,
-MAX) +/- fudge factor.
-
-(As an aside - I am curious why you chose to use a clock-style
-algorithm rather than the more straightforward LRU for your buffer
-cache implementation in your sample solution. Is there a reason for
-that?  I was curious to see if it made a difference, so I implemented
-LRU for your cache implementation and ran the test workload of project
-4 and printed cache hits/accesses.
-I found that for that workload, the clock-based algorithm performs
-almost identical to LRU (within about 1%, but I ran nondeterministally
-with QEMU). I then reduced the cache size to 32 blocks and found again
-the same performance, which raises the suspicion that the test
-workload might not force any cache replacement, so the eviction
-strategy doesn't matter.)
-
-* Get rid of rox--causes more trouble than it's worth
-
 * Reconsider command line arg style--confuses everyone.
 
-* Finish writing tour.
+* Internal tests.
+
+* Userprog project:
 
-via Godmar Back:
+  - Get rid of rox--causes more trouble than it's worth
 
-* Get rid of mmap syscall, add sbrk.
+  - Extra credit: specifics on how to implement sbrk, malloc.
 
-* page-linear, page-shuffle VM tests do not use enough memory to force
-  eviction.  Should increase memory consumption.
+  - Godmar: We're missing tests that pass arguments to system calls
+    that span multiple pages, where some are mapped and some are not.
+    An implementation that only checks the first page, rather than all
+    pages that can be touched during a call to read()/write() passes
+    all tests.
 
-* Add FS persistence test(s).
+  - Godmar: Need some tests that test that illegal accesses lead to
+    process termination. I have written some, will add them. In P2,
+    obviously, this would require that the students break this
+    functionality since the page directory is initialized for them,
+    still it would be good to have.
 
-* process_death test needs improvement
+  - Godmar: There does not appear to be a test that checks that they
+    close all fd's on exit.  Idea: add statistics & self-diagnostics
+    code to palloc.c and malloc.c.  Self-diagnostics code could be
+    used for debugging.  The statistics code would report how much
+    kernel memory is free.  Add a system call
+    "get_kernel_memory_information".  User programs could engage in a
+    variety of activities and notice leaks by checking the kernel
+    memory statistics.
 
-* Internal tests.
+  - process_death test needs improvement
+
+* VM project:
+
+  - Godmar: Get rid of mmap syscall, add sbrk.
+
+  - Godmar: page-linear, page-shuffle VM tests do not use enough
+    memory to force eviction.  Should increase memory consumption.
 
 * Filesys project:
 
@@ -81,6 +47,41 @@ via Godmar Back:
     cache--likely, Bochs doesn't simulate a disk with a realistic
     speed.
 
+  - Do we check that non-empty directories cannot be removed?
+
+  - Need lots more tests.
+
+  - Add FS persistence test(s).
+
+  - Godmar: I'm in the middle of project 4, I've started by
+    implementing a buffer cache and plugging it into the existing
+    filesystem.  Along the way I was wondering how we could test the
+    cache.
+
+    Maybe one could adopt a similar testing strategy as in project 1
+    for the MLQFS scheduler: add a function that reads
+    "get_cache_accesses()" and a function "get_cache_hits()".  Then
+    create a version of pintos that creates access traces for a
+    to-be-determined workload.  Run an off-line analysis that would
+    determine how many hits a perfect cache would have (MAX), and how
+    much say an LRU strategy would give (MIN).  Then add a fudge
+    factor to account for different index strategies and test that the
+    reported number of cache hits/accesses is within (MIN, MAX) +/-
+    fudge factor.
+
+    (As an aside - I am curious why you chose to use a clock-style
+    algorithm rather than the more straightforward LRU for your buffer
+    cache implementation in your sample solution. Is there a reason
+    for that?  I was curious to see if it made a difference, so I
+    implemented LRU for your cache implementation and ran the test
+    workload of project 4 and printed cache hits/accesses.  I found
+    that for that workload, the clock-based algorithm performs almost
+    identical to LRU (within about 1%, but I ran nondeterministally
+    with QEMU). I then reduced the cache size to 32 blocks and found
+    again the same performance, which raises the suspicion that the
+    test workload might not force any cache replacement, so the
+    eviction strategy doesn't matter.)
+
 * Documentation:
 
   - Add "Digging Deeper" sections that describe the nitty-gritty x86
@@ -89,75 +90,68 @@ via Godmar Back:
   - Add explanations of what "real" OSes do to give students some
     perspective.
 
-* Assignments:
-
-  - Add extra credit:
-
-    . Specifics on how to implement sbrk, malloc.
-
-    . Other good ideas.
-
-    . everything needed for getcwd()
-
-To add partition support:
+* To add partition support:
 
-- Find four partition types that are more or less unused and choose to
-  use them for Pintos.  (This is implemented.)
+  - Find four partition types that are more or less unused and choose
+    to use them for Pintos.  (This is implemented.)
 
-- Bootloader reads partition tables of all BIOS devices to find the
-  first that has the "Pintos kernel" partition type.  (This is
-  implemented.)  Ideally the bootloader would make sure there is
-  exactly one such partition, but I didn't implement that yet.
+  - Bootloader reads partition tables of all BIOS devices to find the
+    first that has the "Pintos kernel" partition type.  (This is
+    implemented.)  Ideally the bootloader would make sure there is
+    exactly one such partition, but I didn't implement that yet.
 
-- Bootloader reads kernel into memory at 1 MB using BIOS calls.  (This
-  is implemented.)
+  - Bootloader reads kernel into memory at 1 MB using BIOS calls.
+    (This is implemented.)
 
-- Kernel arguments have to go into a separate sector because the
-  bootloader is otherwise too big to fit now?  (I don't recall if I
-  did anything about this.)
+  - Kernel arguments have to go into a separate sector because the
+    bootloader is otherwise too big to fit now?  (I don't recall if I
+    did anything about this.)
 
-- Kernel at boot also scans partition tables of all the disks it can
-  find to find the ones with the four Pintos partition types (perhaps
-  not all exist).  After that, it makes them available to the rest of
-  the kernel (and doesn't allow access to other devices, for safety).
+  - Kernel at boot also scans partition tables of all the disks it can
+    find to find the ones with the four Pintos partition types
+    (perhaps not all exist).  After that, it makes them available to
+    the rest of the kernel (and doesn't allow access to other devices,
+    for safety).
 
-- "pintos" and "pintos-mkdisk" need to write a partition table to the
-  disks that they create.  "pintos-mkdisk" will need to take a new
-  parameter specifying the type.  (I might have partially implemented
-  this, don't remember.)
+  - "pintos" and "pintos-mkdisk" need to write a partition table to
+    the disks that they create.  "pintos-mkdisk" will need to take a
+    new parameter specifying the type.  (I might have partially
+    implemented this, don't remember.)
 
-- "pintos" should insist on finding a partition header on disks handed
-  to it, for safety.
+  - "pintos" should insist on finding a partition header on disks
+    handed to it, for safety.
 
-- Need some way for "pintos" to assemble multiple disks or partitions
-  into a single image that can be copied directly to a USB block
-  device.  (I don't know whether I came up with a good solution yet or
-  not, or whether I implemented any of it.)
+  - Need some way for "pintos" to assemble multiple disks or
+    partitions into a single image that can be copied directly to a
+    USB block device.  (I don't know whether I came up with a good
+    solution yet or not, or whether I implemented any of it.)
 
-To add USB support:
+To add USB support:
 
-- Needs to be able to scan PCI bus for UHCI controller.  (I
-  implemented this partially.)
+    - Needs to be able to scan PCI bus for UHCI controller.  (I
+      implemented this partially.)
 
-- May want to be able to initialize USB controllers over CardBus
-  bridges.  I don't know whether this requires additional work or if
-  it's useful enough to warrant extra work.  (It's of special interest
-  for me because I have a laptop that only has USB via CardBus.)
+    - May want to be able to initialize USB controllers over CardBus
+      bridges.  I don't know whether this requires additional work or
+      if it's useful enough to warrant extra work.  (It's of special
+      interest for me because I have a laptop that only has USB via
+      CardBus.)
 
-- There are many protocol layers involved: SCSI over USB-Mass Storage
-  over USB over UHCI over PCI.  (I may be forgetting one.)  I don't
-  know yet whether it's best to separate the layers or to merge (some
-  of) them.  I think that a simple and clean organization should be a
-  priority.
+    - There are many protocol layers involved: SCSI over USB-Mass
+      Storage over USB over UHCI over PCI.  (I may be forgetting one.)
+      I don't know yet whether it's best to separate the layers or to
+      merge (some of) them.  I think that a simple and clean
+      organization should be a priority.
 
-- VMware can likely be used for testing because it can expose host USB
-  devices as guest USB devices.  This is safer and more convenient
-  than using real hardware for testing.
+    - VMware can likely be used for testing because it can expose host
+      USB devices as guest USB devices.  This is safer and more
+      convenient than using real hardware for testing.
 
-- Should test with a variety of USB keychain devices because there
-  seems to be wide variation among them, especially in the SCSI
-  protocols they support.  Should try to use a "lowest-common
-  denominator" SCSI protocol if any such thing really exists.
+    - Should test with a variety of USB keychain devices because there
+      seems to be wide variation among them, especially in the SCSI
+      protocols they support.  Should try to use a "lowest-common
+      denominator" SCSI protocol if any such thing really exists.
 
-- Might want to add a feature whereby kernel arguments can be given
-  interactively, rather than passed on-disk.  Needs some though.
+    - Might want to add a feature whereby kernel arguments can be
+      given interactively, rather than passed on-disk.  Needs some
+      though.
index 8e4dc4f964b4f352f7ffc30c6b672f2bda8573c5..49117a502ae1fad37b20f1ba9ff1a77b3cb79c44 100644 (file)
@@ -176,18 +176,17 @@ not an external program.)
 Update the existing system calls so that, anywhere a file name is
 provided by the caller, an absolute or relative path name may used.
 The directory separator character is forward slash (@samp{/}).
-You may support @file{.} and @file{..} for a small amount of extra
-credit.
+You must also support special file names @file{.} and @file{..}, which
+have the same meanings as they do in Unix.
 
 Update the @code{remove} system call so that it can delete empty
-directories in addition to regular files.  Directories can only be
-deleted if they do not contain any files or subdirectories.
+directories in addition to regular files.  Directories may only be
+deleted if they do not contain any files or subdirectories (other than
+@file{.} and @file{..}).
 
 Update the @code{open} system call so that it can also open directories.
-Passing @file{.} as the argument to @code{open} must open the current
-directory, regardless of whether @file{.} and @file{..} are fully
-implemented.  Of the existing system calls, only @code{close} needs to
-accept a file descriptor for a directory.
+Of the existing system calls, only @code{close} needs to accept a file
+descriptor for a directory.
 
 Implement the following new system calls:
 
@@ -213,8 +212,7 @@ name in @var{name}, which must have room for @code{READDIR_MAX_LEN + 1}
 bytes, and returns true.  If no entries are left in the directory,
 returns false.
 
-@file{.} and @file{..} should not be returned by @code{readdir},
-regardless of whether they are implemented.
+@file{.} and @file{..} should not be returned by @code{readdir}.
 
 If the directory changes while it is open, then it is acceptable for
 some entries not to be read at all or to be read multiple times.
@@ -230,14 +228,24 @@ Returns true if @var{fd} represents a directory,
 false if it represents an ordinary file.
 @end deftypefn
 
+@deftypefn {System Call} int inumber (int @var{fd})
+Returns the @dfn{inode number} of the inode associated with @var{fd}.
+Applicable to file descriptors for both files and directories.
+
+An inode number persistently identifies a file or directory.  It is
+unique during the file's existence.  In Pintos, the sector number of the
+inode is suitable for use as an inode number.
+@end deftypefn
+
 We have provided @command{ls} and @command{mkdir} user programs, which
-are straightforward once the above syscalls are implemented.  The
-@command{shell} program implements @command{cd} internally.
+are straightforward once the above syscalls are implemented.  
+We have also provided @command{pwd}, which is not so straightforward.
+The @command{shell} program implements @command{cd} internally.
 
 The @code{pintos} @option{put} and @option{get} commands should now
 accept full path names, assuming that the directories used in the
-paths have already been created.  This should not require any extra
-effort on your part.
+paths have already been created.  This should not require any significant
+extra effort on your part.
 
 @node Buffer Cache
 @subsection Buffer Cache
@@ -385,13 +393,6 @@ modified by the reference solution.
  30 files changed, 2721 insertions(+), 286 deletions(-)
 @end verbatim
 
-@item What extra credit opportunities are available?
-
-You may implement Unix-style support for @file{.} and @file{..} in
-relative paths in their projects.
-
-You may submit with VM enabled.
-
 @item Can @code{DISK_SECTOR_SIZE} change?
 
 No, @code{DISK_SECTOR_SIZE} is fixed at 512.  This is a fixed property
@@ -419,16 +420,20 @@ You'll need to consider this when deciding your inode organization.
 @subsection Subdirectories FAQ
 
 @table @b
-@item How should a file name like @samp{//a//b} be interpreted?
+@item How should a file name like @samp{a//b} be interpreted?
 
 Multiple consecutive slashes are equivalent to a single slash, so this
-file name is the same as @samp{/a/b}.
+file name is the same as @samp{a/b}.
 
 @item How about a file name like @samp{/../x}?
 
-If you don't implement @file{.} and @file{..}, then this is not a
-special case.  If you do, then it is equivalent to @samp{/x}.  That is,
-the root directory is its own parent.
+The root directory is its own parent, so it is equivalent to @samp{/x}.
+
+@item How should a file name that ends in @samp{/} be treated?
+
+Most Unix systems allow a slash at the end of the name for a directory,
+and reject other names that end in slashes.  We will allow this
+behavior, as well as simply rejecting a name that ends in a slash.
 @end table
 
 @node Buffer Cache FAQ
index 1b978e99c24a49c81c753b03669290e8c510134d..63329abea80c7131684b372518e7d2e67b30a489 100644 (file)
@@ -72,6 +72,9 @@ FirstName LastName <email@domain.example>
 >> Describe your code for traversing a user-specified path.  How do
 >> traversals of absolute and relative paths differ?
 
+>> Look over "pwd.c" in src/examples.  Briefly explain how it
+>> determines the present working directory.
+
 ---- SYNCHRONIZATION ----
 
 >> How do you prevent races on directory entries?  For example, only one
index 4dde4d11eb34e4487d13e12721eee21b1b38e618..c43781cb1c4516db4faafaa25245b38c764dcf7c 100644 (file)
@@ -387,7 +387,7 @@ diff -u src/filesys/cache.c~ src/filesys/cache.c
 +
 +  /* Wait for cache contention to die down. */
 +  lock_release (&cache_sync);
-+  timer_sleep (1000);
++  timer_msleep (1000);
 +  goto try_again;
 +}
 +
@@ -627,21 +627,48 @@ Index: src/filesys/directory.c
 diff -u src/filesys/directory.c~ src/filesys/directory.c
 --- src/filesys/directory.c~
 +++ src/filesys/directory.c
-@@ -20,21 +20,13 @@ struct dir_entry 
+@@ -21,12 +21,36 @@ struct dir_entry 
      bool in_use;                        /* In use or free? */
    };
  
 -/* Creates a directory with space for ENTRY_CNT entries in the
 -   given SECTOR.  Returns true if successful, false on failure. */
--bool
++/* Creates a directory in the given SECTOR.
++   The directory's parent is in PARENT_SECTOR. */
+ bool
 -dir_create (disk_sector_t sector, size_t entry_cnt) 
--{
++dir_create (disk_sector_t sector, disk_sector_t parent_sector) 
+ {
 -  return inode_create (sector, entry_cnt * sizeof (struct dir_entry));
--}
--
++  struct inode *inode = inode_create (sector, DIR_INODE);
++  bool success = inode != NULL;
++  if (success) 
++    {
++      struct dir_entry entries[2];
++
++      memset (entries, 0, sizeof entries);
++
++      /* "." entry. */
++      entries[0].inode_sector = sector;
++      strlcpy (entries[0].name, ".", sizeof entries[0].name);
++      entries[0].in_use = true;
++
++      /* ".." entry. */
++      entries[1].inode_sector = parent_sector;
++      strlcpy (entries[1].name, "..", sizeof entries[1].name);
++      entries[1].in_use = true;
++      
++      success = (inode_write_at (inode, entries, sizeof entries, 0)
++                 == sizeof entries);
++      if (!success)
++        inode_remove (inode);
++    }
++  inode_close (inode); 
++  return success;
+ }
  /* Opens and returns the directory for the given INODE, of which
-    it takes ownership.  Returns a null pointer on failure. */
- struct dir *
+@@ -35,7 +59,7 @@ struct dir *
  dir_open (struct inode *inode) 
  {
    struct dir *dir = calloc (1, sizeof *dir);
@@ -649,8 +676,8 @@ diff -u src/filesys/directory.c~ src/filesys/directory.c
 +  if (inode != NULL && dir != NULL && inode_get_type (inode) == DIR_INODE)
      {
        dir->inode = inode;
-       return dir;
-@@ -67,10 +67,8 @@ dir_close (struct dir *dir) 
+       dir->pos = 0;
+@@ -84,10 +108,8 @@ dir_get_inode (struct dir *dir) 
  }
  
  /* Searches DIR for a file with the given NAME.
@@ -663,20 +690,28 @@ diff -u src/filesys/directory.c~ src/filesys/directory.c
  static bool
  lookup (const struct dir *dir, const char *name,
          struct dir_entry *ep, off_t *ofsp) 
-@@ -107,10 +105,12 @@ dir_lookup (const struct dir *dir, const
+@@ -120,15 +142,16 @@ dir_lookup (const struct dir *dir, const
+             struct inode **inode) 
+ {
+   struct dir_entry e;
++  bool ok;
    ASSERT (dir != NULL);
    ASSERT (name != NULL);
  
+-  if (lookup (dir, name, &e, NULL))
+-    *inode = inode_open (e.inode_sector);
+-  else
+-    *inode = NULL;
 +  inode_lock (dir->inode);
-   if (lookup (dir, name, &e, NULL))
-     *inode = inode_open (e.inode_sector);
-   else
-     *inode = NULL;
++  ok = lookup (dir, name, &e, NULL);
 +  inode_unlock (dir->inode);
  
++  *inode = ok ? inode_open (e.inode_sector) : NULL;
    return *inode != NULL;
  }
-@@ -132,10 +132,11 @@ dir_add (struct dir *dir, const char *na
+@@ -149,10 +172,11 @@ dir_add (struct dir *dir, const char *na
    ASSERT (name != NULL);
  
    /* Check NAME for validity. */
@@ -689,7 +724,7 @@ diff -u src/filesys/directory.c~ src/filesys/directory.c
    if (lookup (dir, name, NULL, NULL))
      goto done;
  
-@@ -158,6 +159,7 @@ dir_add (struct dir *dir, const char *na
+@@ -175,6 +199,7 @@ dir_add (struct dir *dir, const char *na
    success = inode_write_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
  
   done:
@@ -697,9 +732,13 @@ diff -u src/filesys/directory.c~ src/filesys/directory.c
    return success;
  }
  
-@@ -176,12 +178,14 @@ dir_remove (struct dir *dir, const char 
+@@ -192,13 +217,18 @@ dir_remove (struct dir *dir, const char 
+   ASSERT (dir != NULL);
    ASSERT (name != NULL);
  
++  if (!strcmp (name, ".") || !strcmp (name, ".."))
++    return false;
++
    /* Find directory entry. */
 +  inode_lock (dir->inode);
    if (!lookup (dir, name, &e, &ofs))
@@ -714,15 +753,15 @@ diff -u src/filesys/directory.c~ src/filesys/directory.c
      goto done;
  
    /* Erase directory entry. */
-@@ -195,6 +199,7 @@ dir_remove (struct dir *dir, const char 
+@@ -211,6 +241,7 @@ dir_remove (struct dir *dir, const char 
+   success = true;
  
   done:
-   inode_close (inode);
 +  inode_unlock (dir->inode);
+   inode_close (inode);
    return success;
  }
-@@ -216,14 +216,17 @@
+@@ -223,14 +254,17 @@ dir_readdir (struct dir *dir, char name[
  {
    struct dir_entry e;
  
@@ -730,10 +769,11 @@ diff -u src/filesys/directory.c~ src/filesys/directory.c
    while (inode_read_at (dir->inode, &e, sizeof e, dir->pos) == sizeof e) 
      {
        dir->pos += sizeof e;
-       if (e.in_use)
+-      if (e.in_use)
++      if (e.in_use && strcmp (e.name, ".") && strcmp (e.name, ".."))
          {
-           strlcpy (name, e.name, NAME_MAX + 1);
 +          inode_unlock (dir->inode);
+           strlcpy (name, e.name, NAME_MAX + 1);
            return true;
          } 
      }
@@ -744,18 +784,45 @@ Index: src/filesys/directory.h
 diff -u src/filesys/directory.h~ src/filesys/directory.h
 --- src/filesys/directory.h~
 +++ src/filesys/directory.h
-@@ -12,6 +11,5 @@
+@@ -14,7 +14,7 @@
  struct inode;
  
  /* Opening and closing directories. */
 -bool dir_create (disk_sector_t sector, size_t entry_cnt);
++bool dir_create (disk_sector_t sector, disk_sector_t parent_sector);
  struct dir *dir_open (struct inode *);
  struct dir *dir_open_root (void);
+ struct dir *dir_reopen (struct dir *);
 Index: src/filesys/file.c
 diff -u src/filesys/file.c~ src/filesys/file.c
 --- src/filesys/file.c~
 +++ src/filesys/file.c
-@@ -18,7 +18,7 @@ struct file *
+@@ -11,6 +11,24 @@ struct file 
+     bool deny_write;            /* Has file_deny_write() been called? */
+   };
++/* Creates a file in the given SECTOR,
++   initially LENGTH bytes long. */
++bool
++file_create (disk_sector_t sector, off_t length) 
++{
++  struct inode *inode = inode_create (sector, FILE_INODE);
++  bool success = inode != NULL;
++  if (success && length != 0) 
++    {
++      ASSERT (length >= 0);
++      success = inode_write_at (inode, "", 1, length - 1) == 1;
++      if (!success)
++        inode_remove (inode); 
++    }
++  inode_close (inode);
++  return success;
++}
++
+ /* Opens a file for the given INODE, of which it takes ownership,
+    and returns the new file.  Returns a null pointer if an
+    allocation fails or if INODE is null. */
+@@ -18,7 +34,7 @@ struct file *
  file_open (struct inode *inode) 
  {
    struct file *file = calloc (1, sizeof *file);
@@ -764,6 +831,25 @@ diff -u src/filesys/file.c~ src/filesys/file.c
      {
        file->inode = inode;
        file->pos = 0;
+Index: src/filesys/file.h
+diff -u src/filesys/file.h~ src/filesys/file.h
+--- src/filesys/file.h~
++++ src/filesys/file.h
+@@ -1,11 +1,14 @@
+ #ifndef FILESYS_FILE_H
+ #define FILESYS_FILE_H
++#include <stdbool.h>
++#include "devices/disk.h"
+ #include "filesys/off_t.h"
+ struct inode;
+ /* Opening and closing files. */
++bool file_create (disk_sector_t sector, off_t length);
+ struct file *file_open (struct inode *);
+ struct file *file_reopen (struct file *);
+ void file_close (struct file *);
 Index: src/filesys/filesys.c
 diff -u src/filesys/filesys.c~ src/filesys/filesys.c
 --- src/filesys/filesys.c~
@@ -790,7 +876,7 @@ diff -u src/filesys/filesys.c~ src/filesys/filesys.c
    free_map_init ();
  
    if (format) 
-@@ -37,6 +40,99 @@ void
+@@ -37,6 +40,130 @@ void
  filesys_done (void) 
  {
    free_map_close ();
@@ -837,8 +923,8 @@ diff -u src/filesys/filesys.c~ src/filesys/filesys.c
 +   Stores the directory corresponding to the name into *DIRP,
 +   and the file name part into BASE_NAME. */
 +static bool
-+resolve_name (const char *name,
-+              struct dir **dirp, char base_name[NAME_MAX + 1]) 
++resolve_name_to_entry (const char *name,
++                       struct dir **dirp, char base_name[NAME_MAX + 1]) 
 +{
 +  struct dir *dir = NULL;
 +  struct inode *inode;
@@ -887,10 +973,41 @@ diff -u src/filesys/filesys.c~ src/filesys/filesys.c
 +  *dirp = NULL;
 +  base_name[0] = '\0';
 +  return false;
++}
++
++/* Resolves relative or absolute file NAME to an inode.
++   Returns an inode if successful, or a null pointer on failure.
++   The caller is responsible for closing the returned inode. */
++static struct inode *
++resolve_name_to_inode (const char *name)
++{
++  if (name[0] == '/' && name[strspn (name, "/")] == '\0') 
++    {
++      /* The name represents the root directory.
++         There's no name part at all, so resolve_name_to_entry()
++         would reject it entirely.
++         Special case it. */
++      return inode_open (ROOT_DIR_SECTOR);
++    }
++  else 
++    {
++      struct dir *dir;
++      char base_name[NAME_MAX + 1];
++
++      if (resolve_name_to_entry (name, &dir, base_name)) 
++        {
++          struct inode *inode;
++          dir_lookup (dir, base_name, &inode);
++          dir_close (dir);
++          return inode; 
++        }
++      else
++        return NULL;
++    }
  }
  \f
  /* Creates a file named NAME with the given INITIAL_SIZE.
-@@ -44,16 +140,17 @@ filesys_done (void) 
+@@ -44,16 +171,24 @@ filesys_done (void) 
     Fails if a file named NAME already exists,
     or if internal memory allocation fails. */
  bool
@@ -905,17 +1022,24 @@ diff -u src/filesys/filesys.c~ src/filesys/filesys.c
 -                  && free_map_allocate (1, &inode_sector)
 -                  && inode_create (inode_sector, initial_size)
 -                  && dir_add (dir, name, inode_sector));
-+  bool success = (resolve_name (name, &dir, base_name)
-+                  && free_map_allocate (&inode_sector)
-+                  && inode_create (inode_sector, initial_size, type)
-+                  && dir_add (dir, base_name, inode_sector));
++  bool success = (resolve_name_to_entry (name, &dir, base_name)
++                  && free_map_allocate (&inode_sector));
++  if (success) 
++    {
++      if (type == FILE_INODE)
++        success = file_create (inode_sector, initial_size);
++      else
++        success = dir_create (inode_sector,
++                              inode_get_inumber (dir_get_inode (dir))); 
++    }
++  success = success && dir_add (dir, base_name, inode_sector);
    if (!success && inode_sector != 0) 
 -    free_map_release (inode_sector, 1);
 +    free_map_release (inode_sector);
    dir_close (dir);
  
    return success;
-@@ -64,17 +161,22 @@ filesys_create (const char *name, off_t 
+@@ -64,17 +199,10 @@ filesys_create (const char *name, off_t 
     otherwise.
     Fails if no file named NAME exists,
     or if an internal memory allocation fails. */
@@ -924,40 +1048,36 @@ diff -u src/filesys/filesys.c~ src/filesys/filesys.c
  filesys_open (const char *name)
  {
 -  struct dir *dir = dir_open_root ();
-+  struct dir *dir = NULL;
-+  char base_name[NAME_MAX + 1];
-   struct inode *inode = NULL;
+-  struct inode *inode = NULL;
+-
 -  if (dir != NULL)
 -    dir_lookup (dir, name, &inode);
-+  if (!strcmp (name, "/"))
-+    inode = inode_open (ROOT_DIR_SECTOR);
-+  else if (!strcmp (name, "."))
-+    inode = inode_reopen (dir_get_inode (thread_current ()->wd));
-+  else if (resolve_name (name, &dir, base_name))
-+    dir_lookup (dir, base_name, &inode);
-   dir_close (dir);
+-  dir_close (dir);
+-
 -  return file_open (inode);
-+  return inode;
++  return resolve_name_to_inode (name);
  }
  
  /* Deletes the file named NAME.
-@@ -84,7 +182,11 @@ filesys_open (const char *name)
+@@ -84,12 +212,35 @@ filesys_open (const char *name)
  bool
  filesys_remove (const char *name) 
  {
 -  struct dir *dir = dir_open_root ();
 -  bool success = dir != NULL && dir_remove (dir, name);
-+  struct dir *dir = NULL;
+-  dir_close (dir); 
++  struct dir *dir;
 +  char base_name[NAME_MAX + 1];
-+  bool success = false;
-+
-+  if (resolve_name (name, &dir, base_name)) 
-+    success = dir_remove (dir, base_name);
-   dir_close (dir); 
++  bool success;
  
-@@ -91,5 +193,44 @@
++  if (resolve_name_to_entry (name, &dir, base_name)) 
++    {
++      success = dir_remove (dir, base_name);
++      dir_close (dir);
++    }
++  else
++    success = false;
++  
    return success;
  }
 +/* Change current directory to NAME.
@@ -965,73 +1085,20 @@ diff -u src/filesys/filesys.c~ src/filesys/filesys.c
 +bool
 +filesys_chdir (const char *name) 
 +{
-+  struct dir *dir;
-+
-+  /* Find new directory. */
-+  if (*name == '\0')
-+    return false;
-+  else if (name[strspn (name, "/")] == '\0')
-+    {
-+      dir = dir_open_root ();
-+      if (dir == NULL)
-+        return false; 
-+    }
-+  else 
++  struct dir *dir = dir_open (resolve_name_to_inode (name));
++  if (dir != NULL) 
 +    {
-+      char base_name[NAME_MAX + 1];
-+      struct inode *base_inode;
-+      struct dir *base_dir;
-+      if (!resolve_name (name, &dir, base_name)
-+          || !dir_lookup (dir, base_name, &base_inode)
-+          || (base_dir = dir_open (base_inode)) == NULL)
-+        {
-+          dir_close (dir);
-+          return false;
-+        }
-+      dir_close (dir);
-+      dir = base_dir;
++      dir_close (thread_current ()->wd);
++      thread_current ()->wd = dir;
++      return true;
 +    }
-+
-+  /* Change current directory. */
-+  dir_close (thread_current ()->wd);
-+  thread_current ()->wd = dir;
-+  
-+  return true;
++  else
++    return false;
 +}
-+
  \f
  static void must_succeed_function (int, bool) NO_INLINE;
  #define MUST_SUCCEED(EXPR) must_succeed_function (__LINE__, EXPR)
-@@ -129,8 +264,8 @@ filesys_self_test (void)
-     {
-       /* Create file and check that it contains zeros
-          throughout the created length. */
--      MUST_SUCCEED (filesys_create ("foo", sizeof s));
--      MUST_SUCCEED ((file = filesys_open ("foo")) != NULL);
-+      MUST_SUCCEED (filesys_create ("foo", sizeof s, FILE_INODE));
-+      MUST_SUCCEED ((file = file_open (filesys_open ("foo"))) != NULL);
-       MUST_SUCCEED (file_read (file, s2, sizeof s2) == sizeof s2);
-       MUST_SUCCEED (memcmp (s2, zeros, sizeof s) == 0);
-       MUST_SUCCEED (file_tell (file) == sizeof s);
-@@ -138,7 +273,7 @@ filesys_self_test (void)
-       file_close (file);
-       /* Reopen file and write to it. */
--      MUST_SUCCEED ((file = filesys_open ("foo")) != NULL);
-+      MUST_SUCCEED ((file = file_open (filesys_open ("foo"))) != NULL);
-       MUST_SUCCEED (file_write (file, s, sizeof s) == sizeof s);
-       MUST_SUCCEED (file_tell (file) == sizeof s);
-       MUST_SUCCEED (file_length (file) == sizeof s);
-@@ -146,7 +281,7 @@ filesys_self_test (void)
-       /* Reopen file and verify that it reads back correctly.
-          Delete file while open to check proper semantics. */
--      MUST_SUCCEED ((file = filesys_open ("foo")) != NULL);
-+      MUST_SUCCEED ((file = file_open (filesys_open ("foo"))) != NULL);
-       MUST_SUCCEED (filesys_remove ("foo"));
-       MUST_SUCCEED (filesys_open ("foo") == NULL);
-       MUST_SUCCEED (file_read (file, s2, sizeof s) == sizeof s);
-@@ -173,9 +308,13 @@ static void
+@@ -155,9 +306,15 @@ static void
  do_format (void)
  {
    printf ("Formatting file system...");
@@ -1041,9 +1108,10 @@ diff -u src/filesys/filesys.c~ src/filesys/filesys.c
 -  if (!dir_create (ROOT_DIR_SECTOR, 16))
 +
 +  /* Set up root directory. */
-+  if (!inode_create (ROOT_DIR_SECTOR, 0, DIR_INODE))
++  if (!dir_create (ROOT_DIR_SECTOR, ROOT_DIR_SECTOR))
      PANIC ("root directory creation failed");
--  free_map_close ();
++
+   free_map_close ();
 +
    printf ("done.\n");
  }
@@ -1161,16 +1229,10 @@ diff -u src/filesys/free-map.c~ src/filesys/free-map.c
  {
    /* Create inode. */
 -  if (!inode_create (FREE_MAP_SECTOR, bitmap_file_size (free_map)))
-+  if (!inode_create (FREE_MAP_SECTOR, bitmap_file_size (free_map), FILE_INODE))
++  if (!file_create (FREE_MAP_SECTOR, 0))
      PANIC ("free map creation failed");
  
    /* Write bitmap to file. */
-@@ -81,4 +85,5 @@ free_map_create (void) 
-     PANIC ("can't open free map");
-   if (!bitmap_write (free_map, free_map_file))
-     PANIC ("can't write free map");
-+  file_close (free_map_file);
- }
 Index: src/filesys/free-map.h
 diff -u src/filesys/free-map.h~ src/filesys/free-map.h
 --- src/filesys/free-map.h~
@@ -1189,7 +1251,7 @@ Index: src/filesys/fsutil.c
 diff -u src/filesys/fsutil.c~ src/filesys/fsutil.c
 --- src/filesys/fsutil.c~
 +++ src/filesys/fsutil.c
-@@ -30,7 +30,7 @@ fsutil_cat (char **argv)
+@@ -38,7 +38,7 @@ fsutil_cat (char **argv)
    char *buffer;
  
    printf ("Printing '%s' to the console...\n", file_name);
@@ -1198,7 +1260,7 @@ diff -u src/filesys/fsutil.c~ src/filesys/fsutil.c
    if (file == NULL)
      PANIC ("%s: open failed", file_name);
    buffer = palloc_get_page (PAL_ASSERT);
-@@ -102,9 +102,9 @@ fsutil_put (char **argv) 
+@@ -110,9 +110,9 @@ fsutil_put (char **argv) 
      PANIC ("%s: invalid file size %d", file_name, size);
    
    /* Create destination file. */
@@ -1210,7 +1272,7 @@ diff -u src/filesys/fsutil.c~ src/filesys/fsutil.c
    if (dst == NULL)
      PANIC ("%s: open failed", file_name);
  
-@@ -154,7 +154,7 @@ fsutil_get (char **argv)
+@@ -162,7 +162,7 @@ fsutil_get (char **argv)
      PANIC ("couldn't allocate buffer");
  
    /* Open source file. */
@@ -1264,7 +1326,7 @@ diff -u src/filesys/inode.c~ src/filesys/inode.c
    };
  
  /* Returns the number of sectors to allocate for an inode SIZE
-@@ -35,33 +50,30 @@ struct inode 
+@@ -35,74 +50,54 @@ struct inode 
      disk_sector_t sector;               /* Sector number of disk location. */
      int open_cnt;                       /* Number of openers. */
      bool removed;                       /* True if deleted, false otherwise. */
@@ -1309,24 +1371,27 @@ diff -u src/filesys/inode.c~ src/filesys/inode.c
 +  lock_init (&open_inodes_lock);
  }
  
- /* Initializes an inode with LENGTH bytes of data and
-@@ -70,38 +82,35 @@ inode_init (void) 
-    Returns true if successful.
-    Returns false if memory or disk allocation fails. */
- bool
+-/* Initializes an inode with LENGTH bytes of data and
+-   writes the new inode to sector SECTOR on the file system
+-   disk.
+-   Returns true if successful.
+-   Returns false if memory or disk allocation fails. */
+-bool
 -inode_create (disk_sector_t sector, off_t length)
-+inode_create (disk_sector_t sector, off_t length, enum inode_type type) 
++/* Initializes an inode of the given TYPE, writes the new inode
++   to sector SECTOR on the file system disk, and returns the
++   inode thus created.  Returns a null pointer if unsuccessful. */   
++struct inode *
++inode_create (disk_sector_t sector, enum inode_type type) 
  {
 -  struct inode_disk *disk_inode = NULL;
 -  bool success = false;
 +  struct cache_block *block;
 +  struct inode_disk *disk_inode;
-+  bool success;
-   ASSERT (length >= 0);
  
+-  ASSERT (length >= 0);
 +  block = cache_lock (sector, EXCLUSIVE);
-+
    /* If this assertion fails, the inode structure is not exactly
       one sector in size, and you should fix that. */
    ASSERT (sizeof *disk_inode == DISK_SECTOR_SIZE);
@@ -1339,8 +1404,7 @@ diff -u src/filesys/inode.c~ src/filesys/inode.c
  
 -  disk_inode = calloc (1, sizeof *disk_inode);
 -  if (disk_inode != NULL)
-+  if (length > 0) 
-     {
+-    {
 -      size_t sectors = bytes_to_sectors (length);
 -      disk_inode->length = length;
 -      disk_inode->magic = INODE_MAGIC;
@@ -1358,17 +1422,13 @@ diff -u src/filesys/inode.c~ src/filesys/inode.c
 -          success = true; 
 -        } 
 -      free (disk_inode);
-+      struct inode *inode = inode_open (sector);
-+      success = inode != NULL && inode_write_at (inode, "", 1, length - 1);
-+      inode_close (inode);
-     }
-+  else
-+    success = true;
-+
-   return success;
+-    }
+-  return success;
++  return inode_open (sector);
  }
  
-@@ -115,6 +124,7 @@ inode_open (disk_sector_t sector) 
+ /* Reads an inode from SECTOR
+@@ -115,29 +110,35 @@ inode_open (disk_sector_t sector) 
    struct inode *inode;
  
    /* Check whether this inode is already open. */
@@ -1376,11 +1436,12 @@ diff -u src/filesys/inode.c~ src/filesys/inode.c
    for (e = list_begin (&open_inodes); e != list_end (&open_inodes);
         e = list_next (e)) 
      {
-@@ -122,22 +132,27 @@ inode_open (disk_sector_t sector) 
+       inode = list_entry (e, struct inode, elem);
        if (inode->sector == sector) 
          {
-           inode_reopen (inode);
+-          inode_reopen (inode);
 -          return inode; 
++          inode->open_cnt++;
 +          goto done; 
          }
      }
@@ -1407,15 +1468,15 @@ diff -u src/filesys/inode.c~ src/filesys/inode.c
    return inode;
  }
  
-@@ -146,10 +161,25 @@ struct inode *
- inode_reopen (struct inode *inode) 
+@@ -146,9 +147,24 @@ struct inode *
+ inode_reopen (struct inode *inode)
  {
-   if (inode != NULL) 
+   if (inode != NULL)
 -    inode->open_cnt++;
 +    {
-+      inode_lock (inode);
++      lock_acquire (&open_inodes_lock);
 +      inode->open_cnt++;
-+      inode_unlock (inode); 
++      lock_release (&open_inodes_lock);
 +    }
    return inode;
  }
@@ -1431,10 +1492,9 @@ diff -u src/filesys/inode.c~ src/filesys/inode.c
 +  return type;
 +}
 +
- /* Closes INODE and writes it to disk.
-    If this was the last reference to INODE, frees its memory.
-    If INODE was also a removed inode, frees its blocks. */
-@@ -161,21 +191,60 @@ inode_close (struct inode *inode) 
+ /* Returns INODE's inode number. */
+ disk_sector_t
+@@ -161,21 +183,60 @@ inode_close (struct inode *inode) 
      return;
  
    /* Release resources if this was the last opener. */
@@ -1500,7 +1560,7 @@ diff -u src/filesys/inode.c~ src/filesys/inode.c
  }
  
  /* Marks INODE to be deleted when it is closed by the last caller who
-@@ -187,6 +256,156 @@ inode_remove (struct inode *inode) 
+@@ -187,6 +248,156 @@ inode_remove (struct inode *inode) 
    inode->removed = true;
  }
  
@@ -1657,7 +1717,7 @@ diff -u src/filesys/inode.c~ src/filesys/inode.c
  /* Reads SIZE bytes from INODE into BUFFER, starting at position OFFSET.
     Returns the number of bytes actually read, which may be less
     than SIZE if an error occurs or end of file is reached. */
-@@ -195,13 +414,12 @@ inode_read_at (struct inode *inode, void
+@@ -195,13 +406,12 @@ inode_read_at (struct inode *inode, void
  {
    uint8_t *buffer = buffer_;
    off_t bytes_read = 0;
@@ -1673,7 +1733,7 @@ diff -u src/filesys/inode.c~ src/filesys/inode.c
  
        /* Bytes left in inode, bytes left in sector, lesser of the two. */
        off_t inode_left = inode_length (inode) - offset;
-@@ -210,26 +428,16 @@ inode_read_at (struct inode *inode, void
+@@ -210,26 +420,16 @@ inode_read_at (struct inode *inode, void
  
        /* Number of bytes to actually copy out of this sector. */
        int chunk_size = size < min_left ? size : min_left;
@@ -1706,7 +1766,7 @@ diff -u src/filesys/inode.c~ src/filesys/inode.c
          }
        
        /* Advance. */
-@@ -237,75 +445,84 @@ inode_read_at (struct inode *inode, void
+@@ -237,75 +437,82 @@ inode_read_at (struct inode *inode, void
        offset += chunk_size;
        bytes_read += chunk_size;
      }
@@ -1734,11 +1794,10 @@ diff -u src/filesys/inode.c~ src/filesys/inode.c
 +
  /* Writes SIZE bytes from BUFFER into INODE, starting at OFFSET.
     Returns the number of bytes actually written, which may be
-    less than SIZE if end of file is reached or an error occurs.
+-   less than SIZE if end of file is reached or an error occurs.
 -   (Normally a write at end of file would extend the inode, but
 -   growth is not yet implemented.) */
-+   (Normally a write at end of file would extend the file, but
-+   file growth is not yet implemented.) */
++   less than SIZE if an error occurs. */
  off_t
  inode_write_at (struct inode *inode, const void *buffer_, off_t size,
                  off_t offset) 
@@ -1828,7 +1887,7 @@ diff -u src/filesys/inode.c~ src/filesys/inode.c
  
    return bytes_written;
  }
-@@ -315,8 +532,12 @@ inode_write_at (struct inode *inode, con
+@@ -315,8 +522,12 @@ inode_write_at (struct inode *inode, con
  void
  inode_deny_write (struct inode *inode) 
  {
@@ -1842,7 +1901,7 @@ diff -u src/filesys/inode.c~ src/filesys/inode.c
  }
  
  /* Re-enables writes to INODE.
-@@ -325,14 +546,47 @@ inode_deny_write (struct inode *inode) 
+@@ -325,14 +536,47 @@ inode_deny_write (struct inode *inode) 
  void
  inode_allow_write (struct inode *inode) 
  {
@@ -1895,27 +1954,28 @@ Index: src/filesys/inode.h
 diff -u src/filesys/inode.h~ src/filesys/inode.h
 --- src/filesys/inode.h~
 +++ src/filesys/inode.h
-@@ -7,10 +7,18 @@
+@@ -7,11 +7,19 @@
  
  struct bitmap;
  
 +/* Type of an inode. */
 +enum inode_type 
 +  {
-+    FILE_INODE,       /* Ordinary file. */
++    FILE_INODE,         /* Ordinary file. */
 +    DIR_INODE         /* Directory. */
 +  };
 +
  void inode_init (void);
 -bool inode_create (disk_sector_t, off_t);
-+bool inode_create (disk_sector_t, off_t, enum inode_type);
++struct inode *inode_create (disk_sector_t, enum inode_type);
  struct inode *inode_open (disk_sector_t);
  struct inode *inode_reopen (struct inode *);
 +enum inode_type inode_get_type (const struct inode *);
+ disk_sector_t inode_get_inumber (const struct inode *);
  void inode_close (struct inode *);
  void inode_remove (struct inode *);
  off_t inode_read_at (struct inode *, void *, off_t size, off_t offset);
-@@ -18,5 +26,8 @@ off_t inode_write_at (struct inode *, co
+@@ -18,5 +27,8 @@ off_t inode_write_at (struct inode *, co
  void inode_deny_write (struct inode *);
  void inode_allow_write (struct inode *);
  off_t inode_length (const struct inode *);
@@ -2016,8 +2076,7 @@ diff -u src/threads/thread.c~ src/threads/thread.c
  #ifdef USERPROG
    process_exit ();
  #endif
--
-+  
    /* Just set our status to dying and schedule another process.
       We will be destroyed during the call to schedule_tail(). */
    intr_disable ();
@@ -2208,7 +2267,7 @@ diff -u src/userprog/process.c~ src/userprog/process.c
 +  };
  
  /* Starts a new thread running a user program loaded from
-    FILE_NAME.  The new thread may be scheduled (and may even exit)
+    FILENAME.  The new thread may be scheduled (and may even exit)
 @@ -28,41 +43,78 @@ static bool load (const char *cmdline, v
  tid_t
  process_execute (const char *file_name) 
@@ -2417,7 +2476,7 @@ diff -u src/userprog/process.c~ src/userprog/process.c
    int i;
  
    /* Allocate and activate page directory. */
-@@ -220,13 +330,28 @@ load (const char *file_name, void (**eip)
+@@ -220,13 +330,28 @@ load (const char *file_name, void (**eip
      goto done;
    process_activate ();
  
@@ -2447,7 +2506,7 @@ diff -u src/userprog/process.c~ src/userprog/process.c
  
    /* Read and verify executable header. */
    if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr
-@@ -301,7 +426,7 @@ load (const char *file_name, void (**eip)
+@@ -301,7 +426,7 @@ load (const char *file_name, void (**eip
      }
  
    /* Set up stack. */
@@ -2456,7 +2515,7 @@ diff -u src/userprog/process.c~ src/userprog/process.c
      goto done;
  
    /* Start address. */
-@@ -311,14 +436,11 @@ load (const char *file_name, void (**eip)
+@@ -311,14 +436,11 @@ load (const char *file_name, void (**eip
  
   done:
    /* We arrive here whether the load is successful or not. */
@@ -2655,7 +2714,7 @@ Index: src/userprog/syscall.c
 diff -u src/userprog/syscall.c~ src/userprog/syscall.c
 --- src/userprog/syscall.c~
 +++ src/userprog/syscall.c
-@@ -1,20 +1,671 @@
+@@ -1,20 +1,684 @@
  #include "userprog/syscall.h"
  #include <stdio.h>
 +#include <string.h>
@@ -2695,6 +2754,7 @@ diff -u src/userprog/syscall.c~ src/userprog/syscall.c
 +static int sys_mkdir (const char *udir);
 +static int sys_readdir (int handle, char *name);
 +static int sys_isdir (int handle);
++static int sys_inumber (int handle);
 + 
  static void syscall_handler (struct intr_frame *);
 -
@@ -2741,12 +2801,13 @@ diff -u src/userprog/syscall.c~ src/userprog/syscall.c
 +      {1, (syscall_function *) sys_mkdir},
 +      {2, (syscall_function *) sys_readdir},
 +      {1, (syscall_function *) sys_isdir},
++      {1, (syscall_function *) sys_inumber},
 +    };
++
 +  const struct syscall *sc;
 +  unsigned call_nr;
 +  int args[3];
-+
 +  /* Get the system call. */
 +  copy_in (&call_nr, f->esp, sizeof call_nr);
 +  if (call_nr >= sizeof syscall_table / sizeof *syscall_table)
@@ -2769,7 +2830,8 @@ diff -u src/userprog/syscall.c~ src/userprog/syscall.c
  static void
 -syscall_handler (struct intr_frame *f UNUSED) 
 +copy_in (void *dst_, const void *usrc_, size_t size) 
-+{
+ {
+-  printf ("system call!\n");
 +  uint8_t *dst = dst_;
 +  const uint8_t *usrc = usrc_;
 +
@@ -2858,8 +2920,8 @@ diff -u src/userprog/syscall.c~ src/userprog/syscall.c
 +  page_unlock (upage);
 + lock_error:
 +  palloc_free_page (ks);
-+  thread_exit ();
-+}
+   thread_exit ();
+ }
 + 
 +/* Halt system call. */
 +static int
@@ -3084,8 +3146,7 @@ diff -u src/userprog/syscall.c~ src/userprog/syscall.c
 +/* Write system call. */
 +static int
 +sys_write (int handle, void *usrc_, unsigned size) 
- {
--  printf ("system call!\n");
++{
 +  uint8_t *usrc = usrc_;
 +  struct file_descriptor *fd = NULL;
 +  int bytes_written = 0;
@@ -3193,8 +3254,8 @@ diff -u src/userprog/syscall.c~ src/userprog/syscall.c
 +        return m;
 +    }
 + 
-   thread_exit ();
- }
++  thread_exit ();
++}
 +
 +/* Remove mapping M from the virtual address space,
 +   writing back any pages that have changed. */
@@ -3304,6 +3365,17 @@ diff -u src/userprog/syscall.c~ src/userprog/syscall.c
 +  struct file_descriptor *fd = lookup_fd (handle);
 +  return fd->dir != NULL;
 +}
++
++/* Inumber system call. */
++static int
++sys_inumber (int handle)
++{
++  struct file_descriptor *fd = lookup_fd (handle);
++  struct inode *inode = (fd->file 
++                         ? file_get_inode (fd->file)
++                         : dir_get_inode (fd->dir));
++  return inode_get_inumber (inode);
++}
 +\f 
 +/* On thread exit, close all open files and unmap all mappings. */
 +void
index a0725c622bf8389c4b54886edada3abc1e746323..a9e09d73308ea73514230cc3b437b5435c85b268 100644 (file)
@@ -8,6 +8,7 @@ ls
 mcat
 mcp
 mkdir
+pwd
 rm
 shell
 bubsort
index 66bc626d40fb3d9abb8d6c0a6908a6115b7be2e7..2128cc22ff88410aa3f44178fd5e071d78f5c235 100644 (file)
@@ -3,27 +3,32 @@ SRCDIR = ..
 # Test programs to compile, and a list of sources for each.
 # To add a new test, put its name on the PROGS list
 # and then add a name_SRC line that lists its source files.
-PROGS = cat cmp cp echo halt hex-dump ls mcat mcp mkdir rm shell \
+PROGS = cat cmp cp echo halt hex-dump ls mcat mcp mkdir pwd rm shell \
        bubsort insult lineup matmult recursor
 
+# Should work from project 2 onward.
 cat_SRC = cat.c
 cmp_SRC = cmp.c
 cp_SRC = cp.c
 echo_SRC = echo.c
 halt_SRC = halt.c
 hex-dump_SRC = hex-dump.c
+insult_SRC = insult.c
+lineup_SRC = lineup.c
 ls_SRC = ls.c
-mcat_SRC = mcat.c
-mcp_SRC = mcp.c
-mkdir_SRC = mkdir.c
+recursor_SRC = recursor.c
 rm_SRC = rm.c
-shell_SRC = shell.c
 
+# Should work in project 3; also in project 4 if VM is included.
 bubsort_SRC = bubsort.c
-insult_SRC = insult.c
-lineup_SRC = lineup.c
 matmult_SRC = matmult.c
-recursor_SRC = recursor.c
+mcat_SRC = mcat.c
+mcp_SRC = mcp.c
+
+# Should work in project 4.
+mkdir_SRC = mkdir.c
+pwd_SRC = pwd.c
+shell_SRC = shell.c
 
 include $(SRCDIR)/Make.config
 include $(SRCDIR)/Makefile.userprog
index 5907a6cedb90ab507e6f4feee4502f385c1bcdd7..d927bc1da696763fae6c2a844d7a4df7c1062eac 100644 (file)
@@ -5,8 +5,8 @@
    named.
 
    By default, only the name of each file is printed.  If "-l" is
-   given as the first argument, the type and size of each file is
-   also printed. */
+   given as the first argument, the type, size, and inumber of
+   each file is also printed.  This won't work until project 4. */
 
 #include <syscall.h>
 #include <stdio.h>
@@ -25,7 +25,12 @@ list_dir (const char *dir, bool verbose)
   if (isdir (dir_fd))
     {
       char name[READDIR_MAX_LEN];
-      printf ("%s:\n", dir);
+
+      printf ("%s", dir);
+      if (verbose)
+        printf (" (inumber %d)", inumber (dir_fd));
+      printf (":\n");
+
       while (readdir (dir_fd, name)) 
         {
           printf ("%s", name); 
@@ -34,14 +39,7 @@ list_dir (const char *dir, bool verbose)
               char full_name[128];
               int entry_fd;
 
-              if (strcmp (dir, "."))
-                snprintf (full_name, sizeof full_name, "%s/%s", dir, name);
-              else 
-                {
-                  /* This is a special case for implementations
-                     that don't fully understand . and .. */
-                  strlcpy (full_name, name, sizeof full_name); 
-                }
+              snprintf (full_name, sizeof full_name, "%s/%s", dir, name);
               entry_fd = open (full_name);
 
               printf (": ");
@@ -51,6 +49,7 @@ list_dir (const char *dir, bool verbose)
                     printf ("directory");
                   else
                     printf ("%d-byte file", filesize (entry_fd));
+                  printf (", inumber %d", inumber (entry_fd));
                 }
               else
                 printf ("open failed");
diff --git a/src/examples/pwd.c b/src/examples/pwd.c
new file mode 100644 (file)
index 0000000..b2441f5
--- /dev/null
@@ -0,0 +1,152 @@
+/* pwd.c
+   
+   Prints the absolute name of the present working directory. */
+
+#include <syscall.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+static bool getcwd (char *cwd, size_t cwd_size);
+
+int
+main (void) 
+{
+  char cwd[128];
+  if (getcwd (cwd, sizeof cwd)) 
+    {
+      printf ("%s\n", cwd);
+      return 0; 
+    }
+  else 
+    {
+      printf ("error\n");
+      return 1; 
+    }
+}
+\f
+/* Stores the inode number for FILE_NAME in *INUM.
+   Returns true if successful, false if the file could not be
+   opened. */
+static bool
+get_inumber (const char *file_name, int *inum) 
+{
+  int fd = open (file_name);
+  if (fd >= 0) 
+    {
+      *inum = inumber (fd);
+      close (fd);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* Prepends PREFIX to the characters stored in the final *DST_LEN
+   bytes of the DST_SIZE-byte buffer that starts at DST.
+   Returns true if successful, false if adding that many
+   characters, plus a null terminator, would overflow the buffer.
+   (No null terminator is actually added or depended upon, but
+   its space is accounted for.) */
+static bool
+prepend (const char *prefix,
+         char *dst, size_t *dst_len, size_t dst_size) 
+{
+  size_t prefix_len = strlen (prefix);
+  if (prefix_len + *dst_len + 1 <= dst_size) 
+    {
+      *dst_len += prefix_len;
+      memcpy ((dst + dst_size) - *dst_len, prefix, prefix_len);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* Stores the current working directory, as a null-terminated
+   string, in the CWD_SIZE bytes in CWD.
+   Returns true if successful, false on error.  Errors include
+   system errors, directory trees deeper than MAX_LEVEL levels,
+   and insufficient space in CWD. */
+static bool
+getcwd (char *cwd, size_t cwd_size) 
+{
+  size_t cwd_len = 0;   
+  
+#define MAX_LEVEL 20
+  char name[MAX_LEVEL * 3 + 1 + READDIR_MAX_LEN + 1];
+  char *namep;
+
+  int child_inum;
+
+  /* Make sure there's enough space for at least "/". */
+  if (cwd_size < 2)
+    return false;
+
+  /* Get inumber for current directory. */
+  if (!get_inumber (".", &child_inum))
+    return false;
+
+  namep = name;
+  for (;;)
+    {
+      int parent_inum, parent_fd;
+
+      /* Compose "../../../..", etc., in NAME. */
+      if ((namep - name) > MAX_LEVEL * 3)
+        return false;
+      *namep++ = '.';
+      *namep++ = '.';
+      *namep = '\0';
+
+      /* Open directory. */
+      parent_fd = open (name);
+      if (parent_fd < 0)
+        return false;
+      *namep++ = '/';
+
+      /* If parent and child have the same inumber,
+         then we've arrived at the root. */
+      parent_inum = inumber (parent_fd);
+      if (parent_inum == child_inum)
+        break;
+
+      /* Find name of file in parent directory with the child's
+         inumber. */
+      for (;;)
+        {
+          int test_inum;
+          if (!readdir (parent_fd, namep) || !get_inumber (name, &test_inum)) 
+            {
+              close (parent_fd);
+              return false; 
+            }
+          if (test_inum == child_inum)
+            break;
+        }
+      close (parent_fd);
+
+      /* Prepend "/name" to CWD. */
+      if (!prepend (namep - 1, cwd, &cwd_len, cwd_size))
+        return false;
+
+      /* Move up. */
+      child_inum = parent_inum;
+    }
+
+  /* Finalize CWD. */
+  if (cwd_len > 0) 
+    {
+      /* Move the string to the beginning of CWD,
+         and null-terminate it. */
+      memmove (cwd, (cwd + cwd_size) - cwd_len, cwd_len);
+      cwd[cwd_len] = '\0';
+    }
+  else 
+    {
+      /* Special case for the root. */
+      strlcpy (cwd, "/", cwd_size); 
+    }
+  
+  return true;
+}
index 5df0d9d6b01145d7b132fab8cfa4577c039097da..fedda08e1d6730421a129e286117298821fd75fb 100644 (file)
@@ -91,65 +91,6 @@ filesys_remove (const char *name)
   return success;
 }
 \f
-static void must_succeed_function (int, bool) NO_INLINE;
-#define MUST_SUCCEED(EXPR) must_succeed_function (__LINE__, EXPR)
-
-/* Performs basic sanity checks on the file system.
-   The file system should not contain a file named `foo' when
-   called. */
-void
-filesys_self_test (void)
-{
-  static const char s[] = "This is a test string.";
-  static const char zeros[sizeof s] = {0};
-  struct file *file;
-  char s2[sizeof s];
-  int i;
-
-  filesys_remove ("foo");
-  for (i = 0; i < 2; i++) 
-    {
-      /* Create file and check that it contains zeros
-         throughout the created length. */
-      MUST_SUCCEED (filesys_create ("foo", sizeof s));
-      MUST_SUCCEED ((file = filesys_open ("foo")) != NULL);
-      MUST_SUCCEED (file_read (file, s2, sizeof s2) == sizeof s2);
-      MUST_SUCCEED (memcmp (s2, zeros, sizeof s) == 0);
-      MUST_SUCCEED (file_tell (file) == sizeof s);
-      MUST_SUCCEED (file_length (file) == sizeof s);
-      file_close (file);
-
-      /* Reopen file and write to it. */
-      MUST_SUCCEED ((file = filesys_open ("foo")) != NULL);
-      MUST_SUCCEED (file_write (file, s, sizeof s) == sizeof s);
-      MUST_SUCCEED (file_tell (file) == sizeof s);
-      MUST_SUCCEED (file_length (file) == sizeof s);
-      file_close (file);
-
-      /* Reopen file and verify that it reads back correctly.
-         Delete file while open to check proper semantics. */
-      MUST_SUCCEED ((file = filesys_open ("foo")) != NULL);
-      MUST_SUCCEED (filesys_remove ("foo"));
-      MUST_SUCCEED (filesys_open ("foo") == NULL);
-      MUST_SUCCEED (file_read (file, s2, sizeof s) == sizeof s);
-      MUST_SUCCEED (memcmp (s, s2, sizeof s) == 0);
-      MUST_SUCCEED (file_tell (file) == sizeof s);
-      MUST_SUCCEED (file_length (file) == sizeof s);
-      file_close (file);
-    }
-  
-  printf ("filesys: self test ok\n");
-}
-
-/* If SUCCESS is false, panics with an error complaining about
-   LINE_NO. */
-static void 
-must_succeed_function (int line_no, bool success) 
-{
-  if (!success)
-    PANIC ("filesys_self_test: operation failed on line %d", line_no);
-}
-\f
 /* Formats the file system. */
 static void
 do_format (void)
index 5a03f81c99d0f5923f2f45f1027272fcab4517b8..caef83c45c17f4986a3fc8838769a64d7a1e7606 100644 (file)
@@ -17,6 +17,4 @@ bool filesys_create (const char *name, off_t initial_size);
 struct file *filesys_open (const char *name);
 bool filesys_remove (const char *name);
 
-void filesys_self_test (void);
-
 #endif /* filesys/filesys.h */
index 17f4b4680f656fdbda2e8344b36366ba478a6315..d890ed8d3c7f0375dc9f3a6a1178254d2c2db011 100644 (file)
@@ -143,13 +143,20 @@ inode_open (disk_sector_t sector)
 
 /* Reopens and returns INODE. */
 struct inode *
-inode_reopen (struct inode *inode) 
+inode_reopen (struct inode *inode)
 {
-  if (inode != NULL) 
+  if (inode != NULL)
     inode->open_cnt++;
   return inode;
 }
 
+/* Returns INODE's inode number. */
+disk_sector_t
+inode_get_inumber (const struct inode *inode)
+{
+  return inode->sector;
+}
+
 /* Closes INODE and writes it to disk.
    If this was the last reference to INODE, frees its memory.
    If INODE was also a removed inode, frees its blocks. */
index e8aac13b838fc6c550725799ed870235416814d2..be7df634ef499bf83390647d75a92edb4532ecc8 100644 (file)
@@ -11,6 +11,7 @@ void inode_init (void);
 bool inode_create (disk_sector_t, off_t);
 struct inode *inode_open (disk_sector_t);
 struct inode *inode_reopen (struct inode *);
+disk_sector_t inode_get_inumber (const struct inode *);
 void inode_close (struct inode *);
 void inode_remove (struct inode *);
 off_t inode_read_at (struct inode *, void *, off_t size, off_t offset);
index eb1e0ee907dd62d22265d90e0e9e38db8b51f067..21a7af952f8728f93b77d6867ebbf60a7699864c 100644 (file)
@@ -27,7 +27,8 @@ enum
     SYS_CHDIR,                  /* Change the current directory. */
     SYS_MKDIR,                  /* Create a directory. */
     SYS_READDIR,                /* Reads a directory entry. */
-    SYS_ISDIR                   /* Tests if a fd represents a directory. */
+    SYS_ISDIR,                  /* Tests if a fd represents a directory. */
+    SYS_INUMBER                 /* Returns the inode number for a fd. */
   };
 
 #endif /* lib/syscall-nr.h */
index 858b77c7d45d47ef7aca231f921ce88f5a257c6f..a9c5bc8500c517f58169a52c1dee5f1b78a3edff 100644 (file)
@@ -176,3 +176,9 @@ isdir (int fd)
 {
   return syscall1 (SYS_ISDIR, fd);
 }
+
+int
+inumber (int fd) 
+{
+  return syscall1 (SYS_INUMBER, fd);
+}
index 340d1b15fea253155bbe30c50eb7bc9cb73ee383..d9487d3df7b73ad67872be69dae90697990095f3 100644 (file)
@@ -31,5 +31,6 @@ bool chdir (const char *dir);
 bool mkdir (const char *dir);
 bool readdir (int fd, char name[READDIR_MAX_LEN + 1]);
 bool isdir (int fd);
+int inumber (int fd);
 
 #endif /* lib/user/syscall.h */
index 4179500890498c98bcff4c6a5331d0ad32ada14a..dfb6a5dc4b323f56e7ee98c9a8ff9dad3278c500 100644 (file)
@@ -1,3 +1,4 @@
+examples
 filesys
 list
 p1