798989065f11110135fb1200e9aa50cbd7f39477
[pintos-anon] / src / filesys / filesys.c
1 /* This file is derived from source code for the Nachos
2    instructional operating system.  The Nachos copyright notice
3    is reproduced in full below. */
4
5 /* Copyright (c) 1992-1996 The Regents of the University of California.
6    All rights reserved.
7
8    Permission to use, copy, modify, and distribute this software
9    and its documentation for any purpose, without fee, and
10    without written agreement is hereby granted, provided that the
11    above copyright notice and the following two paragraphs appear
12    in all copies of this software.
13
14    IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO
15    ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
16    CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE
17    AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA
18    HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19
20    THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
21    WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23    PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
24    BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
25    PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
26    MODIFICATIONS.
27 */
28
29 #include "filesys/filesys.h"
30 #include <bitmap.h>
31 #include <debug.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include "filesys/file.h"
35 #include "filesys/inode.h"
36 #include "filesys/directory.h"
37 #include "devices/disk.h"
38
39 /* Filesystem.
40
41    For the purposes of the "user processes" and "virtual memory"
42    assignments (projects 2 and 3), please treat all the code in
43    the filesys directory as a black box.  No changes should be
44    needed.  For those projects, a single lock external to the
45    filesystem code suffices.
46
47    The filesystem consists of a set of files.  Each file has a
48    header called an `index node' or `inode', represented by
49    struct inode, that is stored by itself in a single sector (see
50    inode.h).  The header contains the file's length in bytes and
51    an array that lists the sector numbers for the file's
52    contents.
53
54    Two files are special.  The first special file is the free
55    map, whose inode is always stored in sector 0
56    (FREE_MAP_SECTOR).  The free map stores a bitmap (see
57    lib/bitmap.h) that contains one bit for each sector on the
58    disk.  Each bit that corresponds to a sector within a file is
59    set to true, and the other bits, which are not part of any
60    file, are set to false.
61
62    The second special file is the root directory, whose inode is
63    always stored in sector 1 (ROOT_DIR_SECTOR).  The root
64    directory file stores an array of `struct dir_entry' (see
65    directory.h), each of which, if it is in use, associates a
66    filename with the sector of the file's inode.
67
68    The filesystem implemented here has the following limitations:
69
70      - No synchronization.  Concurrent accesses will interfere
71        with one another, so external synchronization is needed.
72
73      - File size is fixed at creation time.  Because the root
74        directory is represented as a file, the number of files
75        that may be created is also limited.
76
77      - File data is allocated as a single extent, so that
78        external fragmentation can become a serious problem as a
79        file system is used over time.
80
81      - No subdirectories.
82
83      - Filenames limited to 14 characters.
84
85      - A system crash mid-operation may corrupt the disk in a way
86        that cannot be repaired automatically.  No `fsck' tool is
87        provided in any case.
88
89    However one important feature is included:
90
91      - Unix-like semantics for filesys_remove() are implemented.
92        That is, if a file is open when it is removed, its blocks
93        are not deallocated and it may still be accessed by the
94        threads that have it open until the last one closes it. */
95
96 /* Sectors of system file inodes. */
97 #define FREE_MAP_SECTOR 0       /* Free map file inode sector. */
98 #define ROOT_DIR_SECTOR 1       /* Root directory file inode sector. */
99
100 /* Root directory. */
101 #define NUM_DIR_ENTRIES 10      /* Maximum number of directory entries. */
102
103 /* The disk that contains the filesystem. */
104 struct disk *filesys_disk;
105
106 /* The free map and root directory files.
107    These files are opened by filesys_init() and never closed. */
108 struct file *free_map_file, *root_dir_file;
109
110 static void do_format (void);
111
112 /* Initializes the filesystem module.
113    If FORMAT is true, reformats the filesystem. */
114 void
115 filesys_init (bool format) 
116 {
117   inode_init ();
118
119   filesys_disk = disk_get (0, 1);
120   if (filesys_disk == NULL)
121     PANIC ("hd0:1 (hdb) not present, filesystem initialization failed");
122
123   if (format) 
124     do_format ();
125   
126   free_map_file = file_open (FREE_MAP_SECTOR);
127   if (free_map_file == NULL)
128     PANIC ("can't open free map file");
129   root_dir_file = file_open (ROOT_DIR_SECTOR);
130   if (root_dir_file == NULL)
131     PANIC ("can't open root dir file");
132 }
133
134 /* Shuts down the filesystem module, writing any unwritten data
135    to disk.
136    Currently there's nothing to do.  You'll need to add code here
137    when you implement write-behind caching. */
138 void
139 filesys_done (void) 
140 {
141 }
142
143 /* Creates a file named NAME with the given INITIAL_SIZE.
144    Returns true if successful, false otherwise.
145    Fails if a file named NAME already exists,
146    or if internal memory allocation fails. */
147 bool
148 filesys_create (const char *name, off_t initial_size) 
149 {
150   struct dir *dir = NULL;
151   struct bitmap *free_map = NULL;
152   disk_sector_t inode_sector;
153   bool success = false;
154
155   /* Read the root directory. */
156   dir = dir_create (NUM_DIR_ENTRIES);
157   if (dir == NULL)
158     goto done;
159   dir_read (dir, root_dir_file);
160   if (dir_lookup (dir, name, NULL)) 
161     goto done;
162
163   /* Allocate a block for the inode. */
164   free_map = bitmap_create (disk_size (filesys_disk));
165   if (free_map == NULL)
166     goto done;
167   bitmap_read (free_map, free_map_file);
168   inode_sector = bitmap_scan_and_flip (free_map, 0, 1, false);
169   if (inode_sector == BITMAP_ERROR)
170     goto done;
171
172   /* Add the file to the directory. */
173   if (!dir_add (dir, name, inode_sector))
174     goto done;
175
176   /* Allocate space for the file. */
177   if (!inode_create (free_map, inode_sector, initial_size))
178     goto done;
179
180   /* Write everything back. */
181   dir_write (dir, root_dir_file);
182   bitmap_write (free_map, free_map_file);
183
184   success = true;
185
186   /* Clean up. */
187  done:
188   bitmap_destroy (free_map);
189   dir_destroy (dir);
190
191   return success;
192 }
193
194 /* Opens a file named NAME and initializes FILE for usage with
195    the file_*() functions declared in file.h.
196    Returns the new file if successful or a null pointer
197    otherwise.
198    Fails if no file named NAME exists,
199    or if an internal memory allocation fails. */
200 struct file *
201 filesys_open (const char *name)
202 {
203   struct dir *dir = NULL;
204   struct file *file = NULL;
205   disk_sector_t inode_sector;
206
207   dir = dir_create (NUM_DIR_ENTRIES);
208   if (dir == NULL)
209     goto done;
210
211   dir_read (dir, root_dir_file);
212   if (dir_lookup (dir, name, &inode_sector))
213     file = file_open (inode_sector);
214
215  done:
216   dir_destroy (dir); 
217
218   return file;
219 }
220
221 /* Deletes the file named NAME.
222    Returns true if successful, false on failure.
223    Fails if no file named NAME exists,
224    or if an internal memory allocation fails. */
225 bool
226 filesys_remove (const char *name) 
227 {
228   struct dir *dir = NULL;
229   struct inode *inode;
230   disk_sector_t inode_sector;
231   bool success = false;
232
233   /* Read the root directory. */
234   dir = dir_create (NUM_DIR_ENTRIES);
235   if (dir == NULL)
236     goto done;
237   dir_read (dir, root_dir_file);
238   if (!dir_lookup (dir, name, &inode_sector))
239     goto done;
240
241   /* Open the inode and delete it. */
242   inode = inode_open (inode_sector);
243   if (inode == NULL)
244     goto done;
245   inode_remove (inode);
246   inode_close (inode);
247
248   /* Remove file from root directory and write directory back to
249      disk. */
250   dir_remove (dir, name);
251   dir_write (dir, root_dir_file);
252
253   success = true;
254
255   /* Clean up. */
256  done:
257   dir_destroy (dir);
258
259   return success;
260 }
261
262 /* Prints a list of files in the filesystem to the system
263    console.
264    Returns true if successful, false on failure,
265    which occurs only if an internal memory allocation fails. */
266 bool
267 filesys_list (void) 
268 {
269   struct dir *dir = dir_create (NUM_DIR_ENTRIES);
270   if (dir == NULL)
271     return false;
272   dir_read (dir, root_dir_file);
273   dir_list (dir);
274   dir_destroy (dir);
275
276   return true;
277 }
278
279 /* Dumps the filesystem state to the system console,
280    including the free map, the list of files, and file contents.
281    Returns true if successful, false on failure,
282    which occurs only if an internal memory allocation fails. */
283 bool
284 filesys_dump (void) 
285 {
286   struct bitmap *free_map;
287   struct dir *dir;  
288
289   printf ("Free map:\n");
290   free_map = bitmap_create (disk_size (filesys_disk));
291   if (free_map == NULL)
292     return false;
293   bitmap_read (free_map, free_map_file);
294   bitmap_dump (free_map);
295   bitmap_destroy (free_map);
296   printf ("\n");
297   
298   dir = dir_create (NUM_DIR_ENTRIES);
299   if (dir == NULL)
300     return false;
301   dir_read (dir, root_dir_file);
302   dir_dump (dir);
303   dir_destroy (dir);
304
305   return true;
306 }
307
308 static void must_succeed_function (int, bool) NO_INLINE;
309 #define MUST_SUCCEED(EXPR) must_succeed_function (__LINE__, EXPR)
310
311 /* Performs basic sanity checks on the filesystem.
312    The filesystem should not contain a file named `foo' when
313    called. */
314 void
315 filesys_self_test (void)
316 {
317   static const char s[] = "This is a test string.";
318   static const char zeros[sizeof s] = {0};
319   struct file *file;
320   char s2[sizeof s];
321   int i;
322
323   filesys_remove ("foo");
324   for (i = 0; i < 2; i++) 
325     {
326       /* Create file and check that it contains zeros
327          throughout the created length. */
328       MUST_SUCCEED (filesys_create ("foo", sizeof s));
329       MUST_SUCCEED ((file = filesys_open ("foo")) != NULL);
330       MUST_SUCCEED (file_read (file, s2, sizeof s2) == sizeof s2);
331       MUST_SUCCEED (memcmp (s2, zeros, sizeof s) == 0);
332       MUST_SUCCEED (file_tell (file) == sizeof s);
333       MUST_SUCCEED (file_length (file) == sizeof s);
334       file_close (file);
335
336       /* Reopen file and write to it. */
337       MUST_SUCCEED ((file = filesys_open ("foo")) != NULL);
338       MUST_SUCCEED (file_write (file, s, sizeof s) == sizeof s);
339       MUST_SUCCEED (file_tell (file) == sizeof s);
340       MUST_SUCCEED (file_length (file) == sizeof s);
341       file_close (file);
342
343       /* Reopen file and verify that it reads back correctly.
344          Delete file while open to check proper semantics. */
345       MUST_SUCCEED ((file = filesys_open ("foo")) != NULL);
346       MUST_SUCCEED (filesys_remove ("foo"));
347       MUST_SUCCEED (filesys_open ("foo") == NULL);
348       MUST_SUCCEED (file_read (file, s2, sizeof s) == sizeof s);
349       MUST_SUCCEED (memcmp (s, s2, sizeof s) == 0);
350       MUST_SUCCEED (file_tell (file) == sizeof s);
351       MUST_SUCCEED (file_length (file) == sizeof s);
352       file_close (file);
353     }
354   
355   printf ("filesys: self test ok\n");
356 }
357 \f
358 /* Formats the filesystem. */
359 static void
360 do_format (void)
361 {
362   struct bitmap *free_map;
363   struct dir *dir;
364
365   printf ("Formatting filesystem...");
366
367   /* Create the initial bitmap and reserve sectors for the
368      free map and root directory inodes. */
369   free_map = bitmap_create (disk_size (filesys_disk));
370   if (free_map == NULL)
371     PANIC ("bitmap creation failed--disk is too large");
372   bitmap_mark (free_map, FREE_MAP_SECTOR);
373   bitmap_mark (free_map, ROOT_DIR_SECTOR);
374
375   /* Allocate free map and root dir files. */
376   if (!inode_create (free_map, FREE_MAP_SECTOR, bitmap_file_size (free_map)))
377     PANIC ("free map creation failed--disk is too large");
378   if (!inode_create (free_map, ROOT_DIR_SECTOR, dir_size (NUM_DIR_ENTRIES)))
379     PANIC ("root directory creation failed");
380
381   /* Write out the free map now that we have space reserved
382      for it. */
383   free_map_file = file_open (FREE_MAP_SECTOR);
384   if (free_map_file == NULL)
385     PANIC ("can't open free map file");
386   bitmap_write (free_map, free_map_file);
387   bitmap_destroy (free_map);
388   file_close (free_map_file);
389
390   /* Write out the root directory in the same way. */
391   root_dir_file = file_open (ROOT_DIR_SECTOR);
392   if (root_dir_file == NULL)
393     PANIC ("can't open root directory");
394   dir = dir_create (NUM_DIR_ENTRIES);
395   if (dir == NULL)
396     PANIC ("can't initialize root directory");
397   dir_write (dir, root_dir_file);
398   dir_destroy (dir);
399   file_close (root_dir_file);
400
401   printf ("done.\n");
402 }
403
404 /* If SUCCESS is false, panics with an error complaining about
405    LINE_NO. */
406 static void 
407 must_succeed_function (int line_no, bool success) 
408 {
409   if (!success)
410     PANIC ("filesys_self_test: operation failed on line %d", line_no);
411 }