Fsutil_ls must close the directory after reading it
[pintos-anon] / src / filesys / fsutil.c
index 3876ee9775bb88fc7a8181a71d4daf7c10be9336..5f045d64a5ad076f02d686007687d0a93a082f90 100644 (file)
 #include "filesys/fsutil.h"
 #include <debug.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <ustar.h>
+#include "filesys/directory.h"
 #include "filesys/file.h"
 #include "filesys/filesys.h"
-#include "threads/mmu.h"
+#include "threads/malloc.h"
 #include "threads/palloc.h"
+#include "threads/vaddr.h"
 
-/* Filename and file size to use for copy operations,
-   as "filename:size". */
-char *fsutil_copy_arg;
-
-/* Name of a file print to print to console. */
-char *fsutil_print_file;
-
-/* Name of a file to delete. */
-char *fsutil_remove_file;
-
-/* List all files in the filesystem to the system console? */
-bool fsutil_list_files;
-
-/* Dump full contents of filesystem to the system console? */
-bool fsutil_dump_filesys;
-
-/* Copies from the "scratch" disk, hdc or hd1:0,
-   to a file named FILENAME in the filesystem.
-   The file will be SIZE bytes in length. */
-static void
-copy (const char *filename, off_t size) 
+/* List files in the root directory. */
+void
+fsutil_ls (char **argv UNUSED) 
 {
-  struct disk *src;
-  struct file *dst;
-  disk_sector_t sector;
-  void *buffer;
+  struct dir *dir;
+  char name[NAME_MAX + 1];
+  
+  printf ("Files in the root directory:\n");
+  dir = dir_open_root ();
+  if (dir == NULL)
+    PANIC ("root dir open failed");
+  while (dir_readdir (dir, name))
+    printf ("%s\n", name);
+  dir_close (dir);
+  printf ("End of listing.\n");
+}
 
-  /* Open source disk. */
-  src = disk_get (1, 0);
-  if (src == NULL)
-    PANIC ("couldn't open source disk (hdc or hd1:0)");
-  if (size > (off_t) disk_size (src) * DISK_SECTOR_SIZE)
-    PANIC ("source disk is too small for %lld-byte file",
-           (unsigned long long) size);
+/* Prints the contents of file ARGV[1] to the system console as
+   hex and ASCII. */
+void
+fsutil_cat (char **argv)
+{
+  const char *file_name = argv[1];
   
-  /* Create destination file. */
-  if (!filesys_create (filename, size))
-    PANIC ("%s: create failed", filename);
-  dst = filesys_open (filename);
-  if (dst == NULL)
-    PANIC ("%s: open failed", filename);
+  struct file *file;
+  char *buffer;
 
-  /* Do copy. */
-  buffer = palloc_get (PAL_ASSERT);
-  sector = 0;
-  while (size > 0)
+  printf ("Printing '%s' to the console...\n", file_name);
+  file = filesys_open (file_name);
+  if (file == NULL)
+    PANIC ("%s: open failed", file_name);
+  buffer = palloc_get_page (PAL_ASSERT);
+  for (;;) 
     {
-      int chunk_size = size > DISK_SECTOR_SIZE ? DISK_SECTOR_SIZE : size;
-      disk_read (src, sector++, buffer);
-      if (file_write (dst, buffer, chunk_size) != chunk_size)
-        PANIC ("%s: write failed with %lld bytes unwritten",
-               filename, (unsigned long long) size);
-      size -= chunk_size;
+      off_t pos = file_tell (file);
+      off_t n = file_read (file, buffer, PGSIZE);
+      if (n == 0)
+        break;
+
+      hex_dump (pos, buffer, n, true); 
     }
-  palloc_free (buffer);
+  palloc_free_page (buffer);
+  file_close (file);
+}
 
-  file_close (dst);
+/* Deletes file ARGV[1]. */
+void
+fsutil_rm (char **argv) 
+{
+  const char *file_name = argv[1];
+  
+  printf ("Deleting '%s'...\n", file_name);
+  if (!filesys_remove (file_name))
+    PANIC ("%s: delete failed\n", file_name);
 }
 
-/* Executes the filesystem operations described by the variables
-   declared in fsutil.h. */
+/* Extracts a ustar-format tar archive from the scratch block
+   device into the Pintos file system. */
 void
-fsutil_run (void
+fsutil_extract (char **argv UNUSED
 {
-  if (fsutil_copy_arg != NULL) 
-    {
-      char *save;
-      char *filename = strtok_r (fsutil_copy_arg, ":", &save);
-      char *size = strtok_r (NULL, "", &save);
+  static block_sector_t sector = 0;
 
-      if (filename == NULL || size == NULL)
-        PANIC ("bad format for -cp option; use -u for usage");
+  struct block *src;
+  void *header, *data;
 
-      copy (filename, atoi (size));
-    }
+  /* Allocate buffers. */
+  header = malloc (BLOCK_SECTOR_SIZE);
+  data = malloc (BLOCK_SECTOR_SIZE);
+  if (header == NULL || data == NULL)
+    PANIC ("couldn't allocate buffers");
+
+  /* Open source block device. */
+  src = block_get_role (BLOCK_SCRATCH);
+  if (src == NULL)
+    PANIC ("couldn't open scratch device");
 
-  if (fsutil_print_file != NULL)
-    fsutil_print (fsutil_print_file);
+  printf ("Extracting ustar archive from scratch device "
+          "into file system...\n");
 
-  if (fsutil_remove_file != NULL) 
+  for (;;)
     {
-      if (filesys_remove (fsutil_remove_file))
-        printf ("%s: removed\n", fsutil_remove_file);
-      else
-        PANIC ("%s: remove failed\n", fsutil_remove_file);
+      const char *file_name;
+      const char *error;
+      enum ustar_type type;
+      int size;
+
+      /* Read and parse ustar header. */
+      block_read (src, sector++, header);
+      error = ustar_parse_header (header, &file_name, &type, &size);
+      if (error != NULL)
+        PANIC ("bad ustar header in sector %"PRDSNu" (%s)", sector - 1, error);
+
+      if (type == USTAR_EOF)
+        {
+          /* End of archive. */
+          break;
+        }
+      else if (type == USTAR_DIRECTORY)
+        printf ("ignoring directory %s\n", file_name);
+      else if (type == USTAR_REGULAR)
+        {
+          struct file *dst;
+
+          printf ("Putting '%s' into the file system...\n", file_name);
+
+          /* Create destination file. */
+          if (!filesys_create (file_name, size))
+            PANIC ("%s: create failed", file_name);
+          dst = filesys_open (file_name);
+          if (dst == NULL)
+            PANIC ("%s: open failed", file_name);
+
+          /* Do copy. */
+          while (size > 0)
+            {
+              int chunk_size = (size > BLOCK_SECTOR_SIZE
+                                ? BLOCK_SECTOR_SIZE
+                                : size);
+              block_read (src, sector++, data);
+              if (file_write (dst, data, chunk_size) != chunk_size)
+                PANIC ("%s: write failed with %d bytes unwritten",
+                       file_name, size);
+              size -= chunk_size;
+            }
+
+          /* Finish up. */
+          file_close (dst);
+        }
     }
 
-  if (fsutil_list_files)
-    filesys_list ();
-
-  if (fsutil_dump_filesys)
-    filesys_dump ();
+  /* Erase the ustar header from the start of the block device,
+     so that the extraction operation is idempotent.  We erase
+     two blocks because two blocks of zeros are the ustar
+     end-of-archive marker. */
+  printf ("Erasing ustar archive...\n");
+  memset (header, 0, BLOCK_SECTOR_SIZE);
+  block_write (src, 0, header);
+  block_write (src, 1, header);
+
+  free (data);
+  free (header);
 }
 
-/* Prints the contents of file FILENAME to the system console as
-   hex and ASCII. */
+/* Copies file FILE_NAME from the file system to the scratch
+   device, in ustar format.
+
+   The first call to this function will write starting at the
+   beginning of the scratch device.  Later calls advance across
+   the device.  This position is independent of that used for
+   fsutil_extract(), so `extract' should precede all
+   `append's. */
 void
-fsutil_print (const char *filename) 
+fsutil_append (char **argv)
 {
-  struct file *file;
-  char *buffer;
+  static block_sector_t sector = 0;
 
-  file = filesys_open (filename);
-  if (file == NULL)
-    PANIC ("%s: open failed", filename);
-  buffer = palloc_get (PAL_ASSERT);
-  for (;;) 
-    {
-      off_t n = file_read (file, buffer, PGSIZE);
-      if (n == 0)
-        break;
+  const char *file_name = argv[1];
+  void *buffer;
+  struct file *src;
+  struct block *dst;
+  off_t size;
+
+  printf ("Appending '%s' to ustar archive on scratch device...\n", file_name);
+
+  /* Allocate buffer. */
+  buffer = malloc (BLOCK_SECTOR_SIZE);
+  if (buffer == NULL)
+    PANIC ("couldn't allocate buffer");
+
+  /* Open source file. */
+  src = filesys_open (file_name);
+  if (src == NULL)
+    PANIC ("%s: open failed", file_name);
+  size = file_length (src);
+
+  /* Open target block device. */
+  dst = block_get_role (BLOCK_SCRATCH);
+  if (dst == NULL)
+    PANIC ("couldn't open scratch device");
+  
+  /* Write ustar header to first sector. */
+  if (!ustar_make_header (file_name, USTAR_REGULAR, size, buffer))
+    PANIC ("%s: name too long for ustar format", file_name);
+  block_write (dst, sector++, buffer);
 
-      hex_dump (buffer, n, true);
+  /* Do copy. */
+  while (size > 0) 
+    {
+      int chunk_size = size > BLOCK_SECTOR_SIZE ? BLOCK_SECTOR_SIZE : size;
+      if (sector >= block_size (dst))
+        PANIC ("%s: out of space on scratch device", file_name);
+      if (file_read (src, buffer, chunk_size) != chunk_size)
+        PANIC ("%s: read failed with %"PROTd" bytes unread", file_name, size);
+      memset (buffer + chunk_size, 0, BLOCK_SECTOR_SIZE - chunk_size);
+      block_write (dst, sector++, buffer);
+      size -= chunk_size;
     }
-  palloc_free (buffer);
-  file_close (file);
+
+  /* Write ustar end-of-archive marker, which is two consecutive
+     sectors full of zeros.  Don't advance our position past
+     them, though, in case we have more files to append. */
+  memset (buffer, 0, BLOCK_SECTOR_SIZE);
+  block_write (dst, sector, buffer);
+  block_write (dst, sector, buffer + 1);
+
+  /* Finish up. */
+  file_close (src);
+  free (buffer);
 }