summaryrefslogtreecommitdiffstats
path: root/src/filesys/directory.c
diff options
context:
space:
mode:
authorFelipe Boeira <felipe.boeira@liu.se>2019-01-08 18:39:03 +0100
committerFelipe Boeira <felipe.boeira@liu.se>2019-01-08 18:39:03 +0100
commitd4522b8e9854178473adcea0fbb84f23f6e744bd (patch)
treefbcf620617c5023154eba3f965b3a982daa64a47 /src/filesys/directory.c
downloadpintos-d4522b8e9854178473adcea0fbb84f23f6e744bd.tar.gz
Initial commit
Diffstat (limited to 'src/filesys/directory.c')
-rw-r--r--src/filesys/directory.c236
1 files changed, 236 insertions, 0 deletions
diff --git a/src/filesys/directory.c b/src/filesys/directory.c
new file mode 100644
index 0000000..0d265d5
--- /dev/null
+++ b/src/filesys/directory.c
@@ -0,0 +1,236 @@
+#include "filesys/directory.h"
+#include <stdio.h>
+#include <string.h>
+#include <list.h>
+#include "filesys/filesys.h"
+#include "filesys/inode.h"
+#include "threads/malloc.h"
+
+/* A directory. */
+struct dir
+ {
+ struct inode *inode; /* Backing store. */
+ off_t pos; /* Current position. */
+ };
+
+/* 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_create (disk_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_open (struct inode *inode)
+{
+ struct dir *dir = calloc (1, sizeof *dir);
+ if (inode != NULL && dir != NULL)
+ {
+ dir->inode = inode;
+ dir->pos = 0;
+ return dir;
+ }
+ else
+ {
+ inode_close (inode);
+ free (dir);
+ return NULL;
+ }
+}
+
+/* Opens the root directory and returns a directory for it.
+ Return true if successful, false on failure. */
+struct dir *
+dir_open_root (void)
+{
+ return dir_open (inode_open (ROOT_DIR_SECTOR));
+}
+
+/* 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)
+{
+ return dir_open (inode_reopen (dir->inode));
+}
+
+/* Destroys DIR and frees associated resources. */
+void
+dir_close (struct dir *dir)
+{
+ if (dir != NULL)
+ {
+ inode_close (dir->inode);
+ free (dir);
+ }
+}
+
+/* Returns the inode encapsulated by DIR. */
+struct inode *
+dir_get_inode (struct dir *dir)
+{
+ return dir->inode;
+}
+
+/* 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)
+{
+ struct dir_entry e;
+ size_t ofs;
+
+ ASSERT (dir != NULL);
+ ASSERT (name != 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 DIR for a file with the given NAME
+ and returns true if one exists, false otherwise.
+ 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 *dir, const char *name,
+ struct inode **inode)
+{
+ struct dir_entry e;
+
+ ASSERT (dir != NULL);
+ ASSERT (name != NULL);
+
+ if (lookup (dir, name, &e, NULL))
+ *inode = inode_open (e.inode_sector);
+ else
+ *inode = NULL;
+
+ return *inode != NULL;
+}
+
+/* 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 a disk or memory
+ error occurs. */
+bool
+dir_add (struct dir *dir, const char *name, disk_sector_t inode_sector)
+{
+ struct dir_entry e;
+ off_t ofs;
+ bool success = false;
+
+ ASSERT (dir != NULL);
+ ASSERT (name != NULL);
+
+ /* Check NAME for validity. */
+ if (*name == '\0' || strlen (name) > NAME_MAX)
+ 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 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 *dir, const char *name)
+{
+ struct dir_entry e;
+ struct inode *inode = NULL;
+ bool success = false;
+ off_t ofs;
+
+ ASSERT (dir != NULL);
+ ASSERT (name != NULL);
+
+ /* Find directory entry. */
+ if (!lookup (dir, name, &e, &ofs))
+ goto done;
+
+ /* 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;
+}
+
+/* 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;
+
+ 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;
+}