Start work on partition support.
[pintos-anon] / src / filesys / filesys.c
index c0e3938d7dcac457be1d5f54afb5b2e83311a39b..847bfb85c93b81b8846fd2908e110532db1b0672 100644 (file)
 #include <stdio.h>
 #include <string.h>
 #include "filesys/file.h"
-#include "filesys/filehdr.h"
+#include "filesys/inode.h"
 #include "filesys/directory.h"
 #include "devices/disk.h"
+#include "devices/partition.h"
 
 /* Filesystem.
 
-   The filesystem consists of a set of files.  Each file has a
-   header, represented by struct filehdr, that is stored by
-   itself in a single sector (see filehdr.h).  The header
-   contains the file's length in bytes and an array that lists
-   the sector numbers for the file's contents.
+   For the purposes of the "user processes" and "virtual memory"
+   assignments (projects 2 and 3), please treat all the code in
+   the filesys directory as a black box.  No changes should be
+   needed.  For those projects, a single lock external to the
+   file system code suffices.
+
+   The file system consists of a set of files.  Each file has a
+   header called an `index node' or `inode', represented by
+   struct inode, that is stored by itself in a single sector (see
+   inode.h).  The header contains the file's length in bytes and
+   an array that lists the sector numbers for the file's
+   contents.
 
    Two files are special.  The first special file is the free
-   map, whose header is always stored in sector 0
+   map, whose inode is always stored in sector 0
    (FREE_MAP_SECTOR).  The free map stores a bitmap (see
    lib/bitmap.h) that contains one bit for each sector on the
    disk.  Each bit that corresponds to a sector within a file is
    set to true, and the other bits, which are not part of any
    file, are set to false.
 
-   The second special file is the root directory, whose header is
+   The second special file is the root directory, whose inode is
    always stored in sector 1 (ROOT_DIR_SECTOR).  The root
    directory file stores an array of `struct dir_entry' (see
    directory.h), each of which, if it is in use, associates a
-   filename with the sector of the file's header.
+   filename with the sector of the file's inode.
 
-   The filesystem implemented here has the following limitations:
+   The file system implemented here has the following limitations:
 
      - No synchronization.  Concurrent accesses will interfere
-       with one another.
+       with one another, so external synchronization is needed.
 
      - File size is fixed at creation time.  Because the root
        directory is represented as a file, the number of files
        that may be created is also limited.
 
-     - No indirect blocks.  This limits maximum file size to the
-       number of sector pointers that fit in a single sector
-       times the size of a sector, or 126 * 512 == 63 kB given
-       32-bit sizes and 512-byte sectors.
+     - File data is allocated as a single extent, so that
+       external fragmentation can become a serious problem as a
+       file system is used over time.
 
-     - No nested subdirectories.
+     - No subdirectories.
 
      - Filenames limited to 14 characters.
 
        that cannot be repaired automatically.  No `fsck' tool is
        provided in any case.
 
-   Note: for the purposes of the "user processes" assignment
-   (project 2), please treat all the code in the filesys
-   directory as a black box.  No changes should be needed.  For
-   that project, a single lock external to the filesystem code
-   suffices. */
+   However one important feature is included:
+
+     - Unix-like semantics for filesys_remove() are implemented.
+       That is, if a file is open when it is removed, its blocks
+       are not deallocated and it may still be accessed by the
+       threads that have it open until the last one closes it. */
 
-/* File header sectors for system files. */
-#define FREE_MAP_SECTOR 0       /* Free map file header sector. */
-#define ROOT_DIR_SECTOR 1       /* Root directory file header sector. */
+/* Sectors of system file inodes. */
+#define FREE_MAP_SECTOR 0       /* Free map file inode sector. */
+#define ROOT_DIR_SECTOR 1       /* Root directory file inode sector. */
 
 /* Root directory. */
 #define NUM_DIR_ENTRIES 10      /* Maximum number of directory entries. */
-#define ROOT_DIR_FILE_SIZE      /* Root directory file size in bytes. */ \
-         (sizeof (struct dir_entry) * NUM_DIR_ENTRIES)
 
-/* The disk that contains the filesystem. */
-struct disk *filesys_disk;
+/* The partition that contains the file system. */
+struct partition *filesys_partition;
 
 /* The free map and root directory files.
    These files are opened by filesys_init() and never closed. */
-static struct file free_map_file, root_dir_file;
+struct file *free_map_file, *root_dir_file;
 
 static void do_format (void);
 
-/* Initializes the filesystem module.
-   If FORMAT is true, reformats the filesystem. */
+/* Initializes the file system module.
+   If FORMAT is true, reformats the file system. */
 void
 filesys_init (bool format) 
 {
-  filesys_disk = disk_get (0, 1);
-  if (filesys_disk == NULL)
-    PANIC ("hd0:1 (hdb) not present, filesystem initialization failed");
+  inode_init ();
+
+  filesys_partition = partition_get (PARTITION_FILESYS);
+  if (filesys_partition == NULL)
+    PANIC ("missing file system partition");
 
   if (format) 
     do_format ();
   
-  if (!file_open (&free_map_file, FREE_MAP_SECTOR))
+  free_map_file = file_open (FREE_MAP_SECTOR);
+  if (free_map_file == NULL)
     PANIC ("can't open free map file");
-  if (!file_open (&root_dir_file, ROOT_DIR_SECTOR))
+  root_dir_file = file_open (ROOT_DIR_SECTOR);
+  if (root_dir_file == NULL)
     PANIC ("can't open root dir file");
 }
 
+/* Shuts down the file system module, writing any unwritten data
+   to disk.
+   Currently there's nothing to do.  You'll need to add code here
+   when you implement write-behind caching. */
+void
+filesys_done (void) 
+{
+}
+
 /* Creates a file named NAME with the given INITIAL_SIZE.
    Returns true if successful, false otherwise.
    Fails if a file named NAME already exists,
@@ -129,73 +148,75 @@ filesys_init (bool format)
 bool
 filesys_create (const char *name, off_t initial_size) 
 {
-  struct dir dir;
-  struct bitmap free_map;
-  disk_sector_t hdr_sector;
-  struct filehdr *filehdr;
+  struct dir *dir = NULL;
+  struct bitmap *free_map = NULL;
+  disk_sector_t inode_sector;
   bool success = false;
 
   /* Read the root directory. */
-  if (!dir_init (&dir, NUM_DIR_ENTRIES))
-    return false;
-  dir_read (&dir, &root_dir_file);
-  if (dir_lookup (&dir, name, NULL)) 
-    goto exit1;
-
-  /* Allocate a block for the file header. */
-  if (!bitmap_init (&free_map, disk_size (filesys_disk)))
-    goto exit1;
-  bitmap_read (&free_map, &free_map_file);
-  hdr_sector = bitmap_find_and_set (&free_map);
-  if (hdr_sector == BITMAP_ERROR)
-    goto exit2;
+  dir = dir_create (NUM_DIR_ENTRIES);
+  if (dir == NULL)
+    goto done;
+  dir_read (dir, root_dir_file);
+  if (dir_lookup (dir, name, NULL)) 
+    goto done;
+
+  /* Allocate a block for the inode. */
+  free_map = bitmap_create (partition_size (filesys_partition));
+  if (free_map == NULL)
+    goto done;
+  bitmap_read (free_map, free_map_file);
+  inode_sector = bitmap_scan_and_flip (free_map, 0, 1, false);
+  if (inode_sector == BITMAP_ERROR)
+    goto done;
 
   /* Add the file to the directory. */
-  if (!dir_add (&dir, name, hdr_sector))
-    goto exit2;
+  if (!dir_add (dir, name, inode_sector))
+    goto done;
 
   /* Allocate space for the file. */
-  filehdr = filehdr_allocate (&free_map, initial_size);
-  if (filehdr == NULL)
-    goto exit2;
+  if (!inode_create (free_map, inode_sector, initial_size))
+    goto done;
 
   /* Write everything back. */
-  filehdr_write (filehdr, hdr_sector);
-  dir_write (&dir, &root_dir_file);
-  bitmap_write (&free_map, &free_map_file);
+  dir_write (dir, root_dir_file);
+  bitmap_write (free_map, free_map_file);
 
   success = true;
 
   /* Clean up. */
-  filehdr_destroy (filehdr);
- exit2:
-  bitmap_destroy (&free_map);
- exit1:
-  dir_destroy (&dir);
+ done:
+  bitmap_destroy (free_map);
+  dir_destroy (dir);
 
   return success;
 }
 
 /* Opens a file named NAME and initializes FILE for usage with
    the file_*() functions declared in file.h.
-   Returns true if successful, false on failure.
+   Returns the new file if successful or a null pointer
+   otherwise.
    Fails if no file named NAME exists,
    or if an internal memory allocation fails. */
-bool
-filesys_open (const char *name, struct file *file)
+struct file *
+filesys_open (const char *name)
 {
-  struct dir dir;
-  disk_sector_t hdr_sector;
-  bool success = false;
+  struct dir *dir = NULL;
+  struct file *file = NULL;
+  disk_sector_t inode_sector;
 
-  if (!dir_init (&dir, NUM_DIR_ENTRIES))
-    return false;
-  dir_read (&dir, &root_dir_file);
-  if (dir_lookup (&dir, name, &hdr_sector))
-    success = file_open (file, hdr_sector);
-  
-  dir_destroy (&dir);
-  return success;
+  dir = dir_create (NUM_DIR_ENTRIES);
+  if (dir == NULL)
+    goto done;
+
+  dir_read (dir, root_dir_file);
+  if (dir_lookup (dir, name, &inode_sector))
+    file = file_open (inode_sector);
+
+ done:
+  dir_destroy (dir); 
+
+  return file;
 }
 
 /* Deletes the file named NAME.
@@ -205,91 +226,82 @@ filesys_open (const char *name, struct file *file)
 bool
 filesys_remove (const char *name) 
 {
-  struct dir dir;
-  disk_sector_t hdr_sector;
-  struct filehdr *filehdr;
-  struct bitmap free_map;
+  struct dir *dir = NULL;
+  struct inode *inode;
+  disk_sector_t inode_sector;
   bool success = false;
 
   /* Read the root directory. */
-  if (!dir_init (&dir, NUM_DIR_ENTRIES))
-    return false;
-  dir_read (&dir, &root_dir_file);
-  if (!dir_lookup (&dir, name, &hdr_sector))
-    goto exit1;
-
-  /* Read the file header. */
-  filehdr = filehdr_read (hdr_sector);
-  if (filehdr == NULL)
-    goto exit1;
-
-  /* Allocate a block for the file header. */
-  if (!bitmap_init (&free_map, disk_size (filesys_disk)))
-    goto exit2;
-  bitmap_read (&free_map, &free_map_file);
-
-  /* Deallocate. */
-  filehdr_deallocate (filehdr, &free_map);
-  bitmap_reset (&free_map, hdr_sector);
-  dir_remove (&dir, name);
-
-  /* Write everything back. */
-  bitmap_write (&free_map, &free_map_file);
-  dir_write (&dir, &root_dir_file);
+  dir = dir_create (NUM_DIR_ENTRIES);
+  if (dir == NULL)
+    goto done;
+  dir_read (dir, root_dir_file);
+  if (!dir_lookup (dir, name, &inode_sector))
+    goto done;
+
+  /* Open the inode and delete it. */
+  inode = inode_open (inode_sector);
+  if (inode == NULL)
+    goto done;
+  inode_remove (inode);
+  inode_close (inode);
+
+  /* Remove file from root directory and write directory back to
+     disk. */
+  dir_remove (dir, name);
+  dir_write (dir, root_dir_file);
 
   success = true;
 
   /* Clean up. */
-  bitmap_destroy (&free_map);
- exit2:
-  filehdr_destroy (filehdr);
- exit1:
-  dir_destroy (&dir);
+ done:
+  dir_destroy (dir);
 
   return success;
 }
 
-/* Prints a list of files in the filesystem to the system
+/* Prints a list of files in the file system to the system
    console.
    Returns true if successful, false on failure,
    which occurs only if an internal memory allocation fails. */
 bool
 filesys_list (void) 
 {
-  struct dir dir;
-
-  if (!dir_init (&dir, NUM_DIR_ENTRIES))
+  struct dir *dir = dir_create (NUM_DIR_ENTRIES);
+  if (dir == NULL)
     return false;
-  dir_read (&dir, &root_dir_file);
-  dir_list (&dir);
-  dir_destroy (&dir);
+  dir_read (dir, root_dir_file);
+  dir_list (dir);
+  dir_destroy (dir);
 
   return true;
 }
 
-/* Dumps the filesystem state to the system console,
+/* Dumps the file system state to the system console,
    including the free map, the list of files, and file contents.
    Returns true if successful, false on failure,
    which occurs only if an internal memory allocation fails. */
 bool
 filesys_dump (void) 
 {
-  struct bitmap free_map;
-  struct dir dir;  
+  struct bitmap *free_map;
+  struct dir *dir;  
 
   printf ("Free map:\n");
-  if (!bitmap_init (&free_map, disk_size (filesys_disk)))
+  free_map = bitmap_create (partition_size (filesys_partition));
+  if (free_map == NULL)
     return false;
-  bitmap_read (&free_map, &free_map_file);
-  bitmap_dump (&free_map);
-  bitmap_destroy (&free_map);
+  bitmap_read (free_map, free_map_file);
+  bitmap_dump (free_map);
+  bitmap_destroy (free_map);
   printf ("\n");
   
-  if (!dir_init (&dir, NUM_DIR_ENTRIES))
+  dir = dir_create (NUM_DIR_ENTRIES);
+  if (dir == NULL)
     return false;
-  dir_read (&dir, &root_dir_file);
-  dir_dump (&dir);
-  dir_destroy (&dir);
+  dir_read (dir, root_dir_file);
+  dir_dump (dir);
+  dir_destroy (dir);
 
   return true;
 }
@@ -297,84 +309,95 @@ filesys_dump (void)
 static void must_succeed_function (int, bool) NO_INLINE;
 #define MUST_SUCCEED(EXPR) must_succeed_function (__LINE__, EXPR)
 
-/* Performs basic sanity checks on the filesystem.
-   The filesystem should not contain a file named `foo' when
+/* 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.";
-  struct file file;
+  static const char zeros[sizeof s] = {0};
+  struct file *file;
   char s2[sizeof s];
-
-  MUST_SUCCEED (filesys_create ("foo", sizeof s));
-  MUST_SUCCEED (filesys_open ("foo", &file));
-  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);
-
-  MUST_SUCCEED (filesys_open ("foo", &file));
-  MUST_SUCCEED (file_read (&file, s2, sizeof s2) == sizeof s2);
-  MUST_SUCCEED (memcmp (s, s2, sizeof s) == 0);
-  MUST_SUCCEED (file_tell (&file) == sizeof s2);
-  MUST_SUCCEED (file_length (&file) == sizeof s2);
-  file_close (&file);
-
-  MUST_SUCCEED (filesys_remove ("foo"));
-
+  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");
 }
 \f
-/* Formats the filesystem. */
+/* Formats the file system. */
 static void
 do_format (void)
 {
-  struct bitmap free_map;
-  struct filehdr *map_hdr, *dir_hdr;
-  struct dir dir;
+  struct bitmap *free_map;
+  struct dir *dir;
 
-  printf ("Formatting filesystem...");
+  printf ("Formatting file system...");
 
   /* Create the initial bitmap and reserve sectors for the
-     free map and root directory file headers. */
-  if (!bitmap_init (&free_map, disk_size (filesys_disk)))
-    PANIC ("bitmap creation failed--disk is too large");
-  bitmap_mark (&free_map, FREE_MAP_SECTOR);
-  bitmap_mark (&free_map, ROOT_DIR_SECTOR);
-
-  /* Allocate data sector(s) for the free map file
-     and write its file header to disk. */
-  map_hdr = filehdr_allocate (&free_map, bitmap_file_size (&free_map));
-  if (map_hdr == NULL)
-    PANIC ("free map creation failed--disk is too large");
-  filehdr_write (map_hdr, FREE_MAP_SECTOR);
-  filehdr_destroy (map_hdr);
-
-  /* Allocate data sector(s) for the root directory file
-     and write its file header to disk. */
-  dir_hdr = filehdr_allocate (&free_map, ROOT_DIR_FILE_SIZE);
-  if (dir_hdr == NULL)
+     free map and root directory inodes. */
+  free_map = bitmap_create (partition_size (filesys_partition));
+  if (free_map == NULL)
+    PANIC ("bitmap creation failed--file system partition is too large");
+  bitmap_mark (free_map, FREE_MAP_SECTOR);
+  bitmap_mark (free_map, ROOT_DIR_SECTOR);
+
+  /* Allocate free map and root dir files. */
+  if (!inode_create (free_map, FREE_MAP_SECTOR, bitmap_file_size (free_map)))
+    PANIC ("free map creation failed--file system partition is too large");
+  if (!inode_create (free_map, ROOT_DIR_SECTOR, dir_size (NUM_DIR_ENTRIES)))
     PANIC ("root directory creation failed");
-  filehdr_write (dir_hdr, ROOT_DIR_SECTOR);
-  filehdr_destroy (dir_hdr);
 
   /* Write out the free map now that we have space reserved
      for it. */
-  if (!file_open (&free_map_file, FREE_MAP_SECTOR))
+  free_map_file = file_open (FREE_MAP_SECTOR);
+  if (free_map_file == NULL)
     PANIC ("can't open free map file");
-  bitmap_write (&free_map, &free_map_file);
-  bitmap_destroy (&free_map);
-  file_close (&free_map_file);
+  bitmap_write (free_map, free_map_file);
+  bitmap_destroy (free_map);
+  file_close (free_map_file);
 
   /* Write out the root directory in the same way. */
-  if (!file_open (&root_dir_file, ROOT_DIR_SECTOR))
+  root_dir_file = file_open (ROOT_DIR_SECTOR);
+  if (root_dir_file == NULL)
     PANIC ("can't open root directory");
-  if (!dir_init (&dir, NUM_DIR_ENTRIES))
+  dir = dir_create (NUM_DIR_ENTRIES);
+  if (dir == NULL)
     PANIC ("can't initialize root directory");
-  dir_write (&dir, &root_dir_file);
-  dir_destroy (&dir);
-  file_close (&free_map_file);
+  dir_write (dir, root_dir_file);
+  dir_destroy (dir);
+  file_close (root_dir_file);
 
   printf ("done.\n");
 }