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