Implement a proper block layer with partition support.
[pintos-anon] / src / filesys / directory.c
index ec6ecd98715d30ba99acff1449e15b0d440c92ea..030c1c9b0dbaf9b3a41a4decaadd7c76793b1d63 100644 (file)
 #include "filesys/directory.h"
 #include <stdio.h>
 #include <string.h>
-#include "filesys/file.h"
-#include "filesys/fsutil.h"
+#include <list.h>
+#include "filesys/filesys.h"
+#include "filesys/inode.h"
 #include "threads/malloc.h"
 
 /* A directory. */
 struct dir 
   {
-    size_t entry_cnt;           /* Number of entries. */
-    struct dir_entry *entries;  /* Array of entries. */
+    struct inode *inode;                /* Backing store. */
+    off_t pos;                          /* Current position. */
   };
 
 /* A single directory entry. */
 struct dir_entry 
   {
-    bool in_use;                        /* In use or free? */
+    block_sector_t inode_sector;        /* Sector number of header. */
     char name[NAME_MAX + 1];            /* Null terminated file name. */
-    disk_sector_t inode_sector;         /* Sector number of header. */
+    bool in_use;                        /* In use or free? */
   };
 
-/* Returns a new directory that holds ENTRY_CNT entries, if
-   successful, or a null pointer if memory is unavailable. */
+/* Creates a directory with space for ENTRY_CNT entries in the
+   given SECTOR.  Returns true if successful, false on failure. */
+bool
+dir_create (block_sector_t sector, size_t entry_cnt)
+{
+  return inode_create (sector, entry_cnt * sizeof (struct dir_entry));
+}
+
+/* Opens and returns the directory for the given INODE, of which
+   it takes ownership.  Returns a null pointer on failure. */
 struct dir *
-dir_create (size_t entry_cnt
+dir_open (struct inode *inode
 {
-  struct dir *d = malloc (sizeof *d);
-  if (d != NULL)
+  struct dir *dir = calloc (1, sizeof *dir);
+  if (inode != NULL && dir != NULL)
     {
-      d->entry_cnt = entry_cnt;
-      d->entries = calloc (1, sizeof *d->entries * entry_cnt);
-      if (d->entries != NULL)
-        return d;
-      free (d); 
+      dir->inode = inode;
+      dir->pos = 0;
+      return dir;
+    }
+  else
+    {
+      inode_close (inode);
+      free (dir);
+      return NULL; 
     }
-  return NULL;
 }
 
-/* Returns the size, in bytes, of a directory with ENTRY_CNT
-   entries. */
-size_t
-dir_size (size_t entry_cnt) 
+/* Opens the root directory and returns a directory for it.
+   Return true if successful, false on failure. */
+struct dir *
+dir_open_root (void)
 {
-  return entry_cnt * sizeof (struct dir_entry);
+  return dir_open (inode_open (ROOT_DIR_SECTOR));
 }
 
-/* Destroys D and frees associated resources. */
-void
-dir_destroy (struct dir *d) 
+/* Opens and returns a new directory for the same inode as DIR.
+   Returns a null pointer on failure. */
+struct dir *
+dir_reopen (struct dir *dir) 
 {
-  if (d != NULL) 
-    {
-      free (d->entries);
-      free (d); 
-    }
+  return dir_open (inode_reopen (dir->inode));
 }
 
-/* Reads D from FILE.
-   D must have already been initialized, to the correct number of
-   entries, with dir_init(). */
+/* Destroys DIR and frees associated resources. */
 void
-dir_read (struct dir *d, struct file *file
+dir_close (struct dir *dir
 {
-  ASSERT (d != NULL);
-  ASSERT (file != NULL);
-  ASSERT (file_length (file) >= (off_t) dir_size (d->entry_cnt));
-
-  file_read_at (file, d->entries, dir_size (d->entry_cnt), 0);
+  if (dir != NULL)
+    {
+      inode_close (dir->inode);
+      free (dir);
+    }
 }
 
-/* Writes D to FILE.
-   D must have already been initialized, to the correct number of
-   entries, with dir_init(). */
-void
-dir_write (struct dir *d, struct file *file) 
+/* Returns the inode encapsulated by DIR. */
+struct inode *
+dir_get_inode (struct dir *dir) 
 {
-  file_write_at (file, d->entries, dir_size (d->entry_cnt), 0);
+  return dir->inode;
 }
 
-/* Searches D for a file named NAME.
-   If successful, returns the file's entry;
-   otherwise, returns a null pointer. */
-static struct dir_entry *
-lookup (const struct dir *d, const char *name) 
+/* Searches DIR for a file with the given NAME.
+   If successful, returns true, sets *EP to the directory entry
+   if EP is non-null, and sets *OFSP to the byte offset of the
+   directory entry if OFSP is non-null.
+   otherwise, returns false and ignores EP and OFSP. */
+static bool
+lookup (const struct dir *dir, const char *name,
+        struct dir_entry *ep, off_t *ofsp) 
 {
-  size_t i;
+  struct dir_entry e;
+  size_t ofs;
   
-  ASSERT (d != NULL);
+  ASSERT (dir != NULL);
   ASSERT (name != NULL);
 
-  if (strlen (name) > NAME_MAX)
-    return NULL;
-
-  for (i = 0; i < d->entry_cnt; i++) 
-    {
-      struct dir_entry *e = &d->entries[i];
-      if (e->in_use && !strcmp (name, e->name))
-        return e;
-    }
-  return NULL;
+  for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
+       ofs += sizeof e) 
+    if (e.in_use && !strcmp (name, e.name)) 
+      {
+        if (ep != NULL)
+          *ep = e;
+        if (ofsp != NULL)
+          *ofsp = ofs;
+        return true;
+      }
+  return false;
 }
 
-/* Searches D for a file named NAME
+/* Searches DIR for a file with the given NAME
    and returns true if one exists, false otherwise.
-   If INODE_SECTOR is nonnull, then on success *INODE_SECTOR
-   is set to the sector that contains the file's inode. */
+   On success, sets *INODE to an inode for the file, otherwise to
+   a null pointer.  The caller must close *INODE. */
 bool
-dir_lookup (const struct dir *d, const char *name,
-            disk_sector_t *inode_sector
+dir_lookup (const struct dir *dir, const char *name,
+            struct inode **inode
 {
-  const struct dir_entry *e;
+  struct dir_entry e;
 
-  ASSERT (d != NULL);
+  ASSERT (dir != NULL);
   ASSERT (name != NULL);
-  
-  e = lookup (d, name);
-  if (e != NULL) 
-    {
-      if (inode_sector != NULL)
-        *inode_sector = e->inode_sector;
-      return true;
-    }
+
+  if (lookup (dir, name, &e, NULL))
+    *inode = inode_open (e.inode_sector);
   else
-    return false;
+    *inode = NULL;
+
+  return *inode != NULL;
 }
 
-/* Adds a file named NAME to D, which must not already contain a
+/* Adds a file named NAME to DIR, which must not already contain a
    file by that name.  The file's inode is in sector
    INODE_SECTOR.
    Returns true if successful, false on failure.
-   Fails if NAME is invalid (i.e. too long) or if D has no free
-   directory entries. */
+   Fails if NAME is invalid (i.e. too long) or a disk or memory
+   error occurs. */
 bool
-dir_add (struct dir *d, const char *name, disk_sector_t inode_sector) 
+dir_add (struct dir *dir, const char *name, block_sector_t inode_sector)
 {
-  size_t i;
-  
-  ASSERT (d != NULL);
+  struct dir_entry e;
+  off_t ofs;
+  bool success = false;
+
+  ASSERT (dir != NULL);
   ASSERT (name != NULL);
-  ASSERT (lookup (d, name) == NULL);
 
-  if (strlen (name) > NAME_MAX)
+  /* Check NAME for validity. */
+  if (*name == '\0' || strlen (name) > NAME_MAX)
     return false;
 
-  for (i = 0; i < d->entry_cnt; i++)
-    {
-      struct dir_entry *e = &d->entries[i];
-      if (!e->in_use) 
-        {
-          e->in_use = true;
-          strlcpy (e->name, name, sizeof e->name);
-          e->inode_sector = inode_sector;
-          return true;
-        }
-    }
-  return false;
+  /* Check that NAME is not in use. */
+  if (lookup (dir, name, NULL, NULL))
+    goto done;
+
+  /* Set OFS to offset of free slot.
+     If there are no free slots, then it will be set to the
+     current end-of-file.
+     
+     inode_read_at() will only return a short read at end of file.
+     Otherwise, we'd need to verify that we didn't get a short
+     read due to something intermittent such as low memory. */
+  for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
+       ofs += sizeof e) 
+    if (!e.in_use)
+      break;
+
+  /* Write slot. */
+  e.in_use = true;
+  strlcpy (e.name, name, sizeof e.name);
+  e.inode_sector = inode_sector;
+  success = inode_write_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
+
+ done:
+  return success;
 }
 
-/* Removes any entry for NAME in D.
+/* Removes any entry for NAME in DIR.
    Returns true if successful, false on failure,
    which occurs only if there is no file with the given NAME. */
 bool
-dir_remove (struct dir *d, const char *name) 
+dir_remove (struct dir *dir, const char *name) 
 {
-  struct dir_entry *e;
+  struct dir_entry e;
+  struct inode *inode = NULL;
+  bool success = false;
+  off_t ofs;
 
-  ASSERT (d != NULL);
+  ASSERT (dir != NULL);
   ASSERT (name != NULL);
 
-  e = lookup (d, name);
-  if (e != NULL) 
-    {
-      e->in_use = false;
-      return true;
-    }
-  else
-    return false;
-}
+  /* Find directory entry. */
+  if (!lookup (dir, name, &e, &ofs))
+    goto done;
 
-/* Prints the names of the files in D to the system console. */
-void
-dir_list (const struct dir *d)
-{
-  struct dir_entry *e;
-  
-  for (e = d->entries; e < d->entries + d->entry_cnt; e++)
-    if (e->in_use)
-      printf ("%s\n", e->name);
+  /* Open inode. */
+  inode = inode_open (e.inode_sector);
+  if (inode == NULL)
+    goto done;
+
+  /* Erase directory entry. */
+  e.in_use = false;
+  if (inode_write_at (dir->inode, &e, sizeof e, ofs) != sizeof e) 
+    goto done;
+
+  /* Remove inode. */
+  inode_remove (inode);
+  success = true;
+
+ done:
+  inode_close (inode);
+  return success;
 }
 
-/* Dumps the contents of D, including its files' names and their
-   contents, to the system console. */
-void
-dir_dump (const struct dir *d) 
+/* Reads the next directory entry in DIR and stores the name in
+   NAME.  Returns true if successful, false if the directory
+   contains no more entries. */
+bool
+dir_readdir (struct dir *dir, char name[NAME_MAX + 1])
 {
-  struct dir_entry *e;
-  
-  for (e = d->entries; e < d->entries + d->entry_cnt; e++)
-    if (e->in_use) 
-      {
-        printf ("Contents of %s:\n", e->name);
-        fsutil_print (e->name);
-        printf ("\n");
-      }
+  struct dir_entry e;
+
+  while (inode_read_at (dir->inode, &e, sizeof e, dir->pos) == sizeof e) 
+    {
+      dir->pos += sizeof e;
+      if (e.in_use)
+        {
+          strlcpy (name, e.name, NAME_MAX + 1);
+          return true;
+        } 
+    }
+  return false;
 }