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