#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"
-/* Initializes D as a directory that holds ENTRY_CNT entries. */
+/* A directory. */
+struct dir
+ {
+ struct inode *inode; /* Backing store. */
+ };
+
+/* A single directory entry. */
+struct dir_entry
+ {
+ disk_sector_t inode_sector; /* Sector number of header. */
+ char name[NAME_MAX + 1]; /* Null terminated file name. */
+ 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
-dir_init (struct dir *d, size_t entry_cnt)
+dir_create (disk_sector_t sector, size_t entry_cnt)
{
- ASSERT (d != NULL);
- d->entry_cnt = entry_cnt;
- d->entries = calloc (1, sizeof *d->entries * entry_cnt);
- return d->entries != NULL;
+ return inode_create (sector, entry_cnt * sizeof (struct dir_entry));
}
-/* Destroys D and frees associated resources. */
-void
-dir_destroy (struct dir *d)
+/* Opens the directory in the given INODE, of which it takes
+ ownership, and sets *DIRP to the new directory or a null
+ pointer on failure. Return true if successful, false on
+ failure. */
+bool
+dir_open (struct inode *inode, struct dir **dirp)
{
- if (d != NULL)
- free (d->entries);
-}
+ struct dir *dir = NULL;
+
+ ASSERT (dirp != NULL);
-/* Returns the size of D in bytes. */
-static off_t
-dir_size (struct dir *d)
-{
- ASSERT (d != NULL);
- return d->entry_cnt * sizeof *d->entries;
+ if (inode != NULL)
+ {
+ dir = malloc (sizeof *dir);
+ if (dir != NULL)
+ dir->inode = inode;
+ }
+
+ *dirp = dir;
+ if (dir == NULL)
+ inode_close (inode);
+ return dir != NULL;
}
-/* Reads D from FILE.
- D must have already been initialized, to the correct number of
- entries, with dir_init(). */
-void
-dir_read (struct dir *d, struct file *file)
+/* Opens the root directory and sets *DIRP to it or to a null
+ pointer on failure. Return true if successful, false on
+ failure. */
+bool
+dir_open_root (struct dir **dirp)
{
- ASSERT (d != NULL);
- ASSERT (file != NULL);
- ASSERT (file_length (file) >= dir_size (d));
-
- file_read_at (file, d->entries, dir_size (d), 0);
+ return dir_open (inode_open (ROOT_DIR_SECTOR), dirp);
}
-/* Writes D to FILE.
- D must have already been initialized, to the correct number of
- entries, with dir_init(). */
+/* Destroys DIR and frees associated resources. */
void
-dir_write (struct dir *d, struct file *file)
+dir_close (struct dir *dir)
{
- file_write_at (file, d->entries, dir_size (d), 0);
+ if (dir != NULL)
+ {
+ inode_close (dir->inode);
+ free (dir);
+ }
}
-/* 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 FILEHDR_SECTOR is nonnull, then on success *FILEHDR_SECTOR
- is set to the sector that contains the file's header. */
+ 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 *filehdr_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 (filehdr_sector != NULL)
- *filehdr_sector = e->filehdr_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
- file by that name. The file's header is in sector
- FILEHDR_SECTOR.
+/* 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 filehdr_sector)
+dir_add (struct dir *dir, const char *name, disk_sector_t inode_sector)
{
- size_t i;
+ struct dir_entry e;
+ off_t ofs;
+ bool success = false;
- ASSERT (d != NULL);
+ 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->filehdr_sector = filehdr_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. */
+/* Prints the names of the files in DIR to the system console. */
void
-dir_dump (const struct dir *d)
+dir_list (const struct dir *dir)
{
- struct dir_entry *e;
+ struct dir_entry e;
+ size_t ofs;
- 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");
- }
+ for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
+ ofs += sizeof e)
+ if (e.in_use)
+ printf ("%s\n", e.name);
}