Switch the base file system from direct-indexed inodes to extents.
[pintos-anon] / src / filesys / inode.c
1 #include "filesys/inode.h"
2 #include <bitmap.h>
3 #include <list.h>
4 #include <debug.h>
5 #include <round.h>
6 #include <stdio.h>
7 #include "filesys/filesys.h"
8 #include "threads/malloc.h"
9
10 /* On-disk inode.
11    Must be exactly DISK_SECTOR_SIZE bytes long. */
12 struct inode_disk
13   {
14     off_t length;                       /* File size in bytes. */
15     disk_sector_t first_sector;         /* Starting sector. */
16     uint32_t unused[126];               /* Unused padding. */
17   };
18
19 /* In-memory inode. */
20 struct inode 
21   {
22     list_elem elem;                     /* Element in inode list. */
23     disk_sector_t sector;               /* Sector number of disk location. */
24     int open_cnt;                       /* Number of openers. */
25     bool removed;                       /* True if deleted, false otherwise. */
26     struct inode_disk data;             /* On-disk data. */
27   };
28
29 /* List of open inodes, so that opening a single inode twice
30    returns the same `struct inode'. */
31 struct list open_inodes;
32
33 static struct inode *alloc_inode (disk_sector_t);
34
35 /* Initializes the inode module. */
36 void
37 inode_init (void) 
38 {
39   list_init (&open_inodes);
40 }
41
42 /* Allocates sectors from bitmap B for the content of a file
43    whose size is LENGTH bytes, and returns a new `struct inode'
44    properly initialized for the file.
45    It is the caller's responsible to write the inode to disk with
46    inode_commit(), as well as the bitmap.
47    If memory or disk allocation fails, returns a null pointer,
48    leaving bitmap B is unchanged. */
49 struct inode *
50 inode_create (struct bitmap *b, disk_sector_t sector, off_t length) 
51 {
52   struct inode *idx;
53   size_t sector_cnt;
54
55   ASSERT (b != NULL);
56   ASSERT (length >= 0);
57
58   /* Allocate inode. */
59   idx = alloc_inode (sector);
60   if (idx == NULL)
61     return NULL;
62
63   /* Allocate disk sectors. */
64   sector_cnt = DIV_ROUND_UP (length, DISK_SECTOR_SIZE);
65   idx->data.length = length;
66   idx->data.first_sector = bitmap_scan_and_flip (b, 0, sector_cnt, false);
67   if (idx->data.first_sector == BITMAP_ERROR)
68     return NULL;
69
70   /* Zero out the file contents. */
71   if (sector_cnt > 0) 
72     {
73       static const char zero_sector[DISK_SECTOR_SIZE];
74       disk_sector_t i;
75       
76       for (i = 0; i < sector_cnt; i++)
77         disk_write (filesys_disk, idx->data.first_sector + i, zero_sector);
78     }
79
80   return idx;
81
82  error:
83   inode_remove (idx);
84   inode_close (idx);
85   return NULL;
86 }
87
88 /* Reads an inode from SECTOR
89    and returns a `struct inode' that contains it.
90    Returns a null pointer if memory allocation fails. */
91 struct inode *
92 inode_open (disk_sector_t sector) 
93 {
94   list_elem *e;
95   struct inode *idx;
96
97   /* Check whether this inode is already open.
98      (A hash table would be better, but the Pintos base code
99      avoids using the hash table so that users are free to modify
100      it at will.) */
101   for (e = list_begin (&open_inodes); e != list_end (&open_inodes);
102        e = list_next (e)) 
103     {
104       idx = list_entry (e, struct inode, elem);
105       if (idx->sector == sector) 
106         {
107           idx->open_cnt++;
108           return idx;
109         }
110     }
111
112   /* Allocate inode. */
113   idx = alloc_inode (sector);
114   if (idx == NULL)
115     return NULL;
116
117   /* Read from disk. */
118   ASSERT (sizeof idx->data == DISK_SECTOR_SIZE);
119   disk_read (filesys_disk, sector, &idx->data);
120
121   return idx;
122 }
123
124 /* Closes inode IDX and writes it to disk.
125    If this was the last reference to IDX, frees its memory.
126    If IDX was also a removed inode, frees its blocks. */
127 void
128 inode_close (struct inode *idx) 
129 {
130   if (idx == NULL)
131     return;
132
133   if (--idx->open_cnt == 0)
134     {
135       if (idx->removed) 
136         {
137           struct bitmap *free_map;
138
139           free_map = bitmap_create (disk_size (filesys_disk));
140           if (free_map != NULL)
141             {
142               disk_sector_t start, end;
143               
144               bitmap_read (free_map, free_map_file);
145
146               /* Reset inode sector bit. */
147               bitmap_reset (free_map, idx->sector);
148
149               /* Reset inode data sector bits. */
150               start = idx->data.first_sector;
151               end = start + DIV_ROUND_UP (idx->data.length, DISK_SECTOR_SIZE);
152               bitmap_set_multiple (free_map, start, end, false);
153
154               bitmap_write (free_map, free_map_file);
155               bitmap_destroy (free_map);
156             }
157           else
158             printf ("inode_close(): can't free blocks");
159         }
160       list_remove (&idx->elem);
161       free (idx); 
162     }
163 }
164
165 /* Writes IDX to disk. */
166 void
167 inode_commit (const struct inode *idx) 
168 {
169   ASSERT (idx != NULL);
170   ASSERT (sizeof idx->data == DISK_SECTOR_SIZE);
171   disk_write (filesys_disk, idx->sector, &idx->data);
172 }
173
174 /* Marks IDX to be deleted when it is closed by the last caller who
175    has it open. */
176 void
177 inode_remove (struct inode *idx) 
178 {
179   ASSERT (idx != NULL);
180   idx->removed = true;
181 }
182
183 /* Returns the disk sector that contains byte offset POS within
184    the file with inode IDX.
185    Returns -1 if IDX does not contain data for a byte at offset
186    POS. */
187 disk_sector_t
188 inode_byte_to_sector (const struct inode *idx, off_t pos) 
189 {
190   ASSERT (idx != NULL);
191
192   if (pos < idx->data.length)
193     return idx->data.first_sector + pos / DISK_SECTOR_SIZE;
194   else
195     return (disk_sector_t) -1;
196 }
197
198 /* Returns the length, in bytes, of the file with inode IDX. */
199 off_t
200 inode_length (const struct inode *idx)
201 {
202   ASSERT (idx != NULL);
203   return idx->data.length;
204 }
205
206 /* Prints a representation of IDX to the system console. */
207 void
208 inode_print (const struct inode *idx) 
209 {
210   printf ("Inode %"PRDSNu": %"PRDSNu" bytes, "
211           "%zu sectors starting at %"PRDSNu"\n",
212           idx->sector, idx->data.length,
213           (size_t) DIV_ROUND_UP (idx->data.length, DISK_SECTOR_SIZE),
214           idx->data.first_sector);
215 }
216 \f
217 /* Returns a newly allocated and initialized inode. */
218 static struct inode *
219 alloc_inode (disk_sector_t sector) 
220 {
221   /* Allocate memory. */
222   struct inode *idx = calloc (1, sizeof *idx);
223   if (idx == NULL)
224     return NULL;
225
226   /* Initialize. */
227   list_push_front (&open_inodes, &idx->elem);
228   idx->sector = sector;
229   idx->open_cnt = 1;
230   idx->removed = false;
231
232   return idx;
233 }