Make directory interface more like file interface.
[pintos-anon] / src / filesys / directory.c
1 #include "filesys/directory.h"
2 #include <stdio.h>
3 #include <string.h>
4 #include <list.h>
5 #include "filesys/filesys.h"
6 #include "filesys/inode.h"
7 #include "threads/malloc.h"
8
9 /* A directory. */
10 struct dir 
11   {
12     struct inode *inode;                /* Backing store. */
13   };
14
15 /* A single directory entry. */
16 struct dir_entry 
17   {
18     disk_sector_t inode_sector;         /* Sector number of header. */
19     char name[NAME_MAX + 1];            /* Null terminated file name. */
20     bool in_use;                        /* In use or free? */
21   };
22
23 /* Creates a directory with space for ENTRY_CNT entries in the
24    given SECTOR.  Returns true if successful, false on failure. */
25 bool
26 dir_create (disk_sector_t sector, size_t entry_cnt) 
27 {
28   return inode_create (sector, entry_cnt * sizeof (struct dir_entry));
29 }
30
31 /* Opens and returns the directory for the given INODE, of which
32    it takes ownership.  Returns a null pointer on failure. */
33 struct dir *
34 dir_open (struct inode *inode) 
35 {
36   struct dir *dir = calloc (1, sizeof *dir);
37   if (inode != NULL && dir != NULL)
38     {
39       dir->inode = inode;
40       return dir;
41     }
42   else
43     {
44       inode_close (inode);
45       free (dir);
46       return NULL; 
47     }
48 }
49
50 /* Opens the root directory and returns a directory for it.
51    Return true if successful, false on failure. */
52 struct dir *
53 dir_open_root (void)
54 {
55   return dir_open (inode_open (ROOT_DIR_SECTOR));
56 }
57
58 /* Opens and returns a new directory for the same inode as DIR.
59    Returns a null pointer on failure. */
60 struct dir *
61 dir_reopen (struct dir *dir) 
62 {
63   return dir_open (inode_reopen (dir->inode));
64 }
65
66 /* Destroys DIR and frees associated resources. */
67 void
68 dir_close (struct dir *dir) 
69 {
70   if (dir != NULL)
71     {
72       inode_close (dir->inode);
73       free (dir);
74     }
75 }
76
77 /* Searches DIR for a file with the given NAME.
78    If successful, returns true, sets *EP to the directory entry
79    if EP is non-null, and sets *OFSP to the byte offset of the
80    directory entry if OFSP is non-null.
81    otherwise, returns false and ignores EP and OFSP. */
82 static bool
83 lookup (const struct dir *dir, const char *name,
84         struct dir_entry *ep, off_t *ofsp) 
85 {
86   struct dir_entry e;
87   size_t ofs;
88   
89   ASSERT (dir != NULL);
90   ASSERT (name != NULL);
91
92   for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
93        ofs += sizeof e) 
94     if (e.in_use && !strcmp (name, e.name)) 
95       {
96         if (ep != NULL)
97           *ep = e;
98         if (ofsp != NULL)
99           *ofsp = ofs;
100         return true;
101       }
102   return false;
103 }
104
105 /* Searches DIR for a file with the given NAME
106    and returns true if one exists, false otherwise.
107    On success, sets *INODE to an inode for the file, otherwise to
108    a null pointer.  The caller must close *INODE. */
109 bool
110 dir_lookup (const struct dir *dir, const char *name,
111             struct inode **inode) 
112 {
113   struct dir_entry e;
114
115   ASSERT (dir != NULL);
116   ASSERT (name != NULL);
117
118   if (lookup (dir, name, &e, NULL))
119     *inode = inode_open (e.inode_sector);
120   else
121     *inode = NULL;
122
123   return *inode != NULL;
124 }
125
126 /* Adds a file named NAME to DIR, which must not already contain a
127    file by that name.  The file's inode is in sector
128    INODE_SECTOR.
129    Returns true if successful, false on failure.
130    Fails if NAME is invalid (i.e. too long) or a disk or memory
131    error occurs. */
132 bool
133 dir_add (struct dir *dir, const char *name, disk_sector_t inode_sector) 
134 {
135   struct dir_entry e;
136   off_t ofs;
137   bool success = false;
138   
139   ASSERT (dir != NULL);
140   ASSERT (name != NULL);
141
142   /* Check NAME for validity. */
143   if (*name == '\0' || strlen (name) > NAME_MAX)
144     return false;
145
146   /* Check that NAME is not in use. */
147   if (lookup (dir, name, NULL, NULL))
148     goto done;
149
150   /* Set OFS to offset of free slot.
151      If there are no free slots, then it will be set to the
152      current end-of-file.
153      
154      inode_read_at() will only return a short read at end of file.
155      Otherwise, we'd need to verify that we didn't get a short
156      read due to something intermittent such as low memory. */
157   for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
158        ofs += sizeof e) 
159     if (!e.in_use)
160       break;
161
162   /* Write slot. */
163   e.in_use = true;
164   strlcpy (e.name, name, sizeof e.name);
165   e.inode_sector = inode_sector;
166   success = inode_write_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
167
168  done:
169   return success;
170 }
171
172 /* Removes any entry for NAME in DIR.
173    Returns true if successful, false on failure,
174    which occurs only if there is no file with the given NAME. */
175 bool
176 dir_remove (struct dir *dir, const char *name) 
177 {
178   struct dir_entry e;
179   struct inode *inode = NULL;
180   bool success = false;
181   off_t ofs;
182
183   ASSERT (dir != NULL);
184   ASSERT (name != NULL);
185
186   /* Find directory entry. */
187   if (!lookup (dir, name, &e, &ofs))
188     goto done;
189
190   /* Open inode. */
191   inode = inode_open (e.inode_sector);
192   if (inode == NULL)
193     goto done;
194
195   /* Erase directory entry. */
196   e.in_use = false;
197   if (inode_write_at (dir->inode, &e, sizeof e, ofs) != sizeof e) 
198     goto done;
199
200   /* Remove inode. */
201   inode_remove (inode);
202   success = true;
203
204  done:
205   inode_close (inode);
206   return success;
207 }
208
209 /* Prints the names of the files in DIR to the system console. */
210 void
211 dir_list (const struct dir *dir)
212 {
213   struct dir_entry e;
214   size_t ofs;
215   
216   for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
217        ofs += sizeof e) 
218     if (e.in_use)
219       printf ("%s\n", e.name);
220 }