7b164380809f5675f7080584801654a416843c2f
[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      - No indirect blocks.  This limits maximum file size to the
78        number of sector pointers that fit in a single inode
79        times the size of a sector, or 126 * 512 == 63 kB given
80        32-bit sizes and 512-byte sectors.
81
82      - No subdirectories.
83
84      - Filenames limited to 14 characters.
85
86      - A system crash mid-operation may corrupt the disk in a way
87        that cannot be repaired automatically.  No `fsck' tool is
88        provided in any case.
89
90    However one important feature is included:
91
92      - Unix-like semantics for filesys_remove() are implemented.
93        That is, if a file is open when it is removed, its blocks
94        are not deallocated and it may still be accessed by the
95        threads that have it open until the last one closes it. */
96
97 /* Sectors of system file inodes. */
98 #define FREE_MAP_SECTOR 0       /* Free map file inode sector. */
99 #define ROOT_DIR_SECTOR 1       /* Root directory file inode sector. */
100
101 /* Root directory. */
102 #define NUM_DIR_ENTRIES 10      /* Maximum number of directory entries. */
103
104 /* The disk that contains the filesystem. */
105 struct disk *filesys_disk;
106
107 /* The free map and root directory files.
108    These files are opened by filesys_init() and never closed. */
109 struct file *free_map_file, *root_dir_file;
110
111 static void do_format (void);
112
113 /* Initializes the filesystem module.
114    If FORMAT is true, reformats the filesystem. */
115 void
116 filesys_init (bool format) 
117 {
118   inode_init ();
119
120   filesys_disk = disk_get (0, 1);
121   if (filesys_disk == NULL)
122     PANIC ("hd0:1 (hdb) not present, filesystem initialization failed");
123
124   if (format) 
125     do_format ();
126   
127   free_map_file = file_open (FREE_MAP_SECTOR);
128   if (free_map_file == NULL)
129     PANIC ("can't open free map file");
130   root_dir_file = file_open (ROOT_DIR_SECTOR);
131   if (root_dir_file == NULL)
132     PANIC ("can't open root dir file");
133 }
134
135 /* Shuts down the filesystem module, writing any unwritten data
136    to disk.
137    Currently there's nothing to do.  You'll need to add code here
138    when you implement write-behind caching. */
139 void
140 filesys_done (void) 
141 {
142 }
143
144 /* Creates a file named NAME with the given INITIAL_SIZE.
145    Returns true if successful, false otherwise.
146    Fails if a file named NAME already exists,
147    or if internal memory allocation fails. */
148 bool
149 filesys_create (const char *name, off_t initial_size) 
150 {
151   struct dir *dir = NULL;
152   struct bitmap *free_map = NULL;
153   struct inode *inode = NULL;
154   disk_sector_t inode_sector;
155   bool success = false;
156
157   /* Read the root directory. */
158   dir = dir_create (NUM_DIR_ENTRIES);
159   if (dir == NULL)
160     goto done;
161   dir_read (dir, root_dir_file);
162   if (dir_lookup (dir, name, NULL)) 
163     goto done;
164
165   /* Allocate a block for the inode. */
166   free_map = bitmap_create (disk_size (filesys_disk));
167   if (free_map == NULL)
168     goto done;
169   bitmap_read (free_map, free_map_file);
170   inode_sector = bitmap_scan_and_flip (free_map, 0, 1, false);
171   if (inode_sector == BITMAP_ERROR)
172     goto done;
173
174   /* Add the file to the directory. */
175   if (!dir_add (dir, name, inode_sector))
176     goto done;
177
178   /* Allocate space for the file. */
179   inode = inode_create (free_map, inode_sector, initial_size);
180   if (inode == NULL)
181     goto done;
182
183   /* Write everything back. */
184   inode_commit (inode);
185   dir_write (dir, root_dir_file);
186   bitmap_write (free_map, free_map_file);
187
188   success = true;
189
190   /* Clean up. */
191  done:
192   inode_close (inode);
193   bitmap_destroy (free_map);
194   dir_destroy (dir);
195
196   return success;
197 }
198
199 /* Opens a file named NAME and initializes FILE for usage with
200    the file_*() functions declared in file.h.
201    Returns the new file if successful or a null pointer
202    otherwise.
203    Fails if no file named NAME exists,
204    or if an internal memory allocation fails. */
205 struct file *
206 filesys_open (const char *name)
207 {
208   struct dir *dir = NULL;
209   struct file *file = NULL;
210   disk_sector_t inode_sector;
211
212   dir = dir_create (NUM_DIR_ENTRIES);
213   if (dir == NULL)
214     goto done;
215
216   dir_read (dir, root_dir_file);
217   if (dir_lookup (dir, name, &inode_sector))
218     file = file_open (inode_sector);
219
220  done:
221   dir_destroy (dir); 
222
223   return file;
224 }
225
226 /* Deletes the file named NAME.
227    Returns true if successful, false on failure.
228    Fails if no file named NAME exists,
229    or if an internal memory allocation fails. */
230 bool
231 filesys_remove (const char *name) 
232 {
233   struct dir *dir = NULL;
234   struct inode *inode;
235   disk_sector_t inode_sector;
236   bool success = false;
237
238   /* Read the root directory. */
239   dir = dir_create (NUM_DIR_ENTRIES);
240   if (dir == NULL)
241     goto done;
242   dir_read (dir, root_dir_file);
243   if (!dir_lookup (dir, name, &inode_sector))
244     goto done;
245
246   /* Open the inode and delete it. */
247   inode = inode_open (inode_sector);
248   if (inode == NULL)
249     goto done;
250   inode_remove (inode);
251   inode_close (inode);
252
253   /* Remove file from root directory and write directory back to
254      disk. */
255   dir_remove (dir, name);
256   dir_write (dir, root_dir_file);
257
258   success = true;
259
260   /* Clean up. */
261  done:
262   dir_destroy (dir);
263
264   return success;
265 }
266
267 /* Prints a list of files in the filesystem to the system
268    console.
269    Returns true if successful, false on failure,
270    which occurs only if an internal memory allocation fails. */
271 bool
272 filesys_list (void) 
273 {
274   struct dir *dir = dir_create (NUM_DIR_ENTRIES);
275   if (dir == NULL)
276     return false;
277   dir_read (dir, root_dir_file);
278   dir_list (dir);
279   dir_destroy (dir);
280
281   return true;
282 }
283
284 /* Dumps the filesystem state to the system console,
285    including the free map, the list of files, and file contents.
286    Returns true if successful, false on failure,
287    which occurs only if an internal memory allocation fails. */
288 bool
289 filesys_dump (void) 
290 {
291   struct bitmap *free_map;
292   struct dir *dir;  
293
294   printf ("Free map:\n");
295   free_map = bitmap_create (disk_size (filesys_disk));
296   if (free_map == NULL)
297     return false;
298   bitmap_read (free_map, free_map_file);
299   bitmap_dump (free_map);
300   bitmap_destroy (free_map);
301   printf ("\n");
302   
303   dir = dir_create (NUM_DIR_ENTRIES);
304   if (dir == NULL)
305     return false;
306   dir_read (dir, root_dir_file);
307   dir_dump (dir);
308   dir_destroy (dir);
309
310   return true;
311 }
312
313 static void must_succeed_function (int, bool) NO_INLINE;
314 #define MUST_SUCCEED(EXPR) must_succeed_function (__LINE__, EXPR)
315
316 /* Performs basic sanity checks on the filesystem.
317    The filesystem should not contain a file named `foo' when
318    called. */
319 void
320 filesys_self_test (void)
321 {
322   static const char s[] = "This is a test string.";
323   static const char zeros[sizeof s] = {0};
324   struct file *file;
325   char s2[sizeof s];
326   int i;
327
328   filesys_remove ("foo");
329   for (i = 0; i < 2; i++) 
330     {
331       /* Create file and check that it contains zeros
332          throughout the created length. */
333       MUST_SUCCEED (filesys_create ("foo", sizeof s));
334       MUST_SUCCEED ((file = filesys_open ("foo")) != NULL);
335       MUST_SUCCEED (file_read (file, s2, sizeof s2) == sizeof s2);
336       MUST_SUCCEED (memcmp (s2, zeros, sizeof s) == 0);
337       MUST_SUCCEED (file_tell (file) == sizeof s);
338       MUST_SUCCEED (file_length (file) == sizeof s);
339       file_close (file);
340
341       /* Reopen file and write to it. */
342       MUST_SUCCEED ((file = filesys_open ("foo")) != NULL);
343       MUST_SUCCEED (file_write (file, s, sizeof s) == sizeof s);
344       MUST_SUCCEED (file_tell (file) == sizeof s);
345       MUST_SUCCEED (file_length (file) == sizeof s);
346       file_close (file);
347
348       /* Reopen file and verify that it reads back correctly.
349          Delete file while open to check proper semantics. */
350       MUST_SUCCEED ((file = filesys_open ("foo")) != NULL);
351       MUST_SUCCEED (filesys_remove ("foo"));
352       MUST_SUCCEED (file_read (file, s2, sizeof s) == sizeof s);
353       MUST_SUCCEED (memcmp (s, s2, sizeof s) == 0);
354       MUST_SUCCEED (file_tell (file) == sizeof s);
355       MUST_SUCCEED (file_length (file) == sizeof s);
356       file_close (file);
357
358       /* Make sure file is deleted. */
359       MUST_SUCCEED ((file = filesys_open ("foo")) == NULL);
360     }
361   
362   printf ("filesys: self test ok\n");
363 }
364 \f
365 /* Formats the filesystem. */
366 static void
367 do_format (void)
368 {
369   struct bitmap *free_map;
370   struct inode *map_inode, *dir_inode;
371   struct dir *dir;
372
373   printf ("Formatting filesystem...");
374
375   /* Create the initial bitmap and reserve sectors for the
376      free map and root directory inodes. */
377   free_map = bitmap_create (disk_size (filesys_disk));
378   if (free_map == NULL)
379     PANIC ("bitmap creation failed--disk is too large");
380   bitmap_mark (free_map, FREE_MAP_SECTOR);
381   bitmap_mark (free_map, ROOT_DIR_SECTOR);
382
383   /* Allocate data sector(s) for the free map file
384      and write its inode to disk. */
385   map_inode = inode_create (free_map, FREE_MAP_SECTOR,
386                             bitmap_file_size (free_map));
387   if (map_inode == NULL)
388     PANIC ("free map creation failed--disk is too large");
389   inode_commit (map_inode);
390   inode_close (map_inode);
391
392   /* Allocate data sector(s) for the root directory file
393      and write its inodes to disk. */
394   dir_inode = inode_create (free_map, ROOT_DIR_SECTOR,
395                             dir_size (NUM_DIR_ENTRIES));
396   if (dir_inode == NULL)
397     PANIC ("root directory creation failed");
398   inode_commit (dir_inode);
399   inode_close (dir_inode);
400
401   /* Write out the free map now that we have space reserved
402      for it. */
403   free_map_file = file_open (FREE_MAP_SECTOR);
404   if (free_map_file == NULL)
405     PANIC ("can't open free map file");
406   bitmap_write (free_map, free_map_file);
407   bitmap_destroy (free_map);
408   file_close (free_map_file);
409
410   /* Write out the root directory in the same way. */
411   root_dir_file = file_open (ROOT_DIR_SECTOR);
412   if (root_dir_file == NULL)
413     PANIC ("can't open root directory");
414   dir = dir_create (NUM_DIR_ENTRIES);
415   if (dir == NULL)
416     PANIC ("can't initialize root directory");
417   dir_write (dir, root_dir_file);
418   dir_destroy (dir);
419   file_close (root_dir_file);
420
421   printf ("done.\n");
422 }
423
424 /* If SUCCESS is false, panics with an error complaining about
425    LINE_NO. */
426 static void 
427 must_succeed_function (int line_no, bool success) 
428 {
429   if (!success)
430     PANIC ("filesys_self_test: operation failed on line %d", line_no);
431 }