Make tests public. Rewrite most tests. Add tests.
[pintos-anon] / src / filesys / inode.c
1 #include "filesys/inode.h"
2 #include <list.h>
3 #include <debug.h>
4 #include <round.h>
5 #include <string.h>
6 #include "filesys/filesys.h"
7 #include "filesys/free-map.h"
8 #include "threads/malloc.h"
9
10 /* Identifies an inode. */
11 #define INODE_MAGIC 0x494e4f44
12
13 /* On-disk inode.
14    Must be exactly DISK_SECTOR_SIZE bytes long. */
15 struct inode_disk
16   {
17     disk_sector_t start;                /* First data sector. */
18     off_t length;                       /* File size in bytes. */
19     unsigned magic;                     /* Magic number. */
20     uint32_t unused[125];               /* Not used. */
21   };
22
23 /* Returns the number of sectors to allocate for an inode SIZE
24    bytes long. */
25 static inline size_t
26 bytes_to_sectors (off_t size)
27 {
28   return DIV_ROUND_UP (size, DISK_SECTOR_SIZE);
29 }
30
31 /* In-memory inode. */
32 struct inode 
33   {
34     struct list_elem elem;              /* Element in inode list. */
35     disk_sector_t sector;               /* Sector number of disk location. */
36     int open_cnt;                       /* Number of openers. */
37     bool removed;                       /* True if deleted, false otherwise. */
38     int deny_write_cnt;                 /* 0: writes ok, >0: deny writes. */
39     struct inode_disk data;             /* Inode content. */
40   };
41
42 /* Returns the disk sector that contains byte offset POS within
43    INODE.
44    Returns -1 if INODE does not contain data for a byte at offset
45    POS. */
46 static disk_sector_t
47 byte_to_sector (const struct inode *inode, off_t pos) 
48 {
49   ASSERT (inode != NULL);
50   if (pos < inode->data.length)
51     return inode->data.start + pos / DISK_SECTOR_SIZE;
52   else
53     return -1;
54 }
55
56 /* List of open inodes, so that opening a single inode twice
57    returns the same `struct inode'. */
58 static struct list open_inodes;
59
60 /* Initializes the inode module. */
61 void
62 inode_init (void) 
63 {
64   list_init (&open_inodes);
65 }
66
67 /* Initializes an inode with LENGTH bytes of data and
68    writes the new inode to sector SECTOR on the file system
69    disk.
70    Returns true if successful.
71    Returns false if memory or disk allocation fails. */
72 bool
73 inode_create (disk_sector_t sector, off_t length)
74 {
75   struct inode_disk *disk_inode = NULL;
76   bool success = false;
77
78   ASSERT (length >= 0);
79   ASSERT (sizeof *disk_inode == DISK_SECTOR_SIZE);
80
81   disk_inode = calloc (1, sizeof *disk_inode);
82   if (disk_inode != NULL)
83     {
84       size_t sectors = bytes_to_sectors (length);
85       disk_inode->length = length;
86       disk_inode->magic = INODE_MAGIC;
87       if (free_map_allocate (sectors, &disk_inode->start))
88         {
89           disk_write (filesys_disk, sector, disk_inode);
90           if (sectors > 0) 
91             {
92               static char zeros[DISK_SECTOR_SIZE];
93               size_t i;
94               
95               for (i = 0; i < sectors; i++) 
96                 disk_write (filesys_disk, disk_inode->start + i, zeros); 
97             }
98           success = true; 
99         } 
100       free (disk_inode);
101     }
102   return success;
103 }
104
105 /* Reads an inode from SECTOR
106    and returns a `struct inode' that contains it.
107    Returns a null pointer if memory allocation fails. */
108 struct inode *
109 inode_open (disk_sector_t sector) 
110 {
111   struct list_elem *e;
112   struct inode *inode;
113
114   /* Check whether this inode is already open. */
115   for (e = list_begin (&open_inodes); e != list_end (&open_inodes);
116        e = list_next (e)) 
117     {
118       inode = list_entry (e, struct inode, elem);
119       if (inode->sector == sector) 
120         {
121           inode_reopen (inode);
122           return inode; 
123         }
124     }
125
126   /* Allocate memory. */
127   inode = malloc (sizeof *inode);
128   if (inode == NULL)
129     return NULL;
130
131   /* Initialize. */
132   list_push_front (&open_inodes, &inode->elem);
133   inode->sector = sector;
134   inode->open_cnt = 1;
135   inode->deny_write_cnt = 0;
136   inode->removed = false;
137   disk_read (filesys_disk, inode->sector, &inode->data);
138   return inode;
139 }
140
141 /* Reopens and returns INODE. */
142 struct inode *
143 inode_reopen (struct inode *inode) 
144 {
145   if (inode != NULL) 
146     inode->open_cnt++;
147   return inode;
148 }
149
150 /* Closes INODE and writes it to disk.
151    If this was the last reference to INODE, frees its memory.
152    If INODE was also a removed inode, frees its blocks. */
153 void
154 inode_close (struct inode *inode) 
155 {
156   /* Ignore null pointer. */
157   if (inode == NULL)
158     return;
159
160   /* Release resources if this was the last opener. */
161   if (--inode->open_cnt == 0)
162     {
163       /* Remove from inode list and release lock. */
164       list_remove (&inode->elem);
165
166       /* Deallocate blocks if removed. */
167       if (inode->removed)
168         free_map_release (inode->sector,
169                           bytes_to_sectors (inode->data.length));
170
171       free (inode); 
172     }
173 }
174
175 /* Marks INODE to be deleted when it is closed by the last caller who
176    has it open. */
177 void
178 inode_remove (struct inode *inode) 
179 {
180   ASSERT (inode != NULL);
181   inode->removed = true;
182 }
183
184 /* Reads SIZE bytes from INODE into BUFFER, starting at position OFFSET.
185    Returns the number of bytes actually read, which may be less
186    than SIZE if an error occurs or end of file is reached. */
187 off_t
188 inode_read_at (struct inode *inode, void *buffer_, off_t size, off_t offset) 
189 {
190   uint8_t *buffer = buffer_;
191   off_t bytes_read = 0;
192   uint8_t *bounce = NULL;
193
194   while (size > 0) 
195     {
196       /* Disk sector to read, starting byte offset within sector. */
197       disk_sector_t sector_idx = byte_to_sector (inode, offset);
198       int sector_ofs = offset % DISK_SECTOR_SIZE;
199
200       /* Bytes left in inode, bytes left in sector, lesser of the two. */
201       off_t inode_left = inode_length (inode) - offset;
202       int sector_left = DISK_SECTOR_SIZE - sector_ofs;
203       int min_left = inode_left < sector_left ? inode_left : sector_left;
204
205       /* Number of bytes to actually copy out of this sector. */
206       int chunk_size = size < min_left ? size : min_left;
207       if (chunk_size <= 0)
208         break;
209
210       if (sector_ofs == 0 && chunk_size == DISK_SECTOR_SIZE) 
211         {
212           /* Read full sector directly into caller's buffer. */
213           disk_read (filesys_disk, sector_idx, buffer + bytes_read); 
214         }
215       else 
216         {
217           /* Read sector into bounce buffer, then partially copy
218              into caller's buffer. */
219           if (bounce == NULL) 
220             {
221               bounce = malloc (DISK_SECTOR_SIZE);
222               if (bounce == NULL)
223                 break;
224             }
225           disk_read (filesys_disk, sector_idx, bounce);
226           memcpy (buffer + bytes_read, bounce + sector_ofs, chunk_size);
227         }
228       
229       /* Advance. */
230       size -= chunk_size;
231       offset += chunk_size;
232       bytes_read += chunk_size;
233     }
234   free (bounce);
235
236   return bytes_read;
237 }
238
239 /* Writes SIZE bytes from BUFFER into INODE, starting at OFFSET.
240    Returns the number of bytes actually written, which may be
241    less than SIZE if end of file is reached or an error occurs.
242    (Normally a write at end of file would extend the inode, but
243    growth is not yet implemented.) */
244 off_t
245 inode_write_at (struct inode *inode, const void *buffer_, off_t size,
246                 off_t offset) 
247 {
248   const uint8_t *buffer = buffer_;
249   off_t bytes_written = 0;
250   uint8_t *bounce = NULL;
251
252   if (inode->deny_write_cnt)
253     return 0;
254
255   while (size > 0) 
256     {
257       /* Sector to write, starting byte offset within sector. */
258       off_t sector_idx = byte_to_sector (inode, offset);
259       int sector_ofs = offset % DISK_SECTOR_SIZE;
260
261       /* Bytes left in inode, bytes left in sector, lesser of the two. */
262       off_t inode_left = inode_length (inode) - offset;
263       int sector_left = DISK_SECTOR_SIZE - sector_ofs;
264       int min_left = inode_left < sector_left ? inode_left : sector_left;
265
266       /* Number of bytes to actually write into this sector. */
267       int chunk_size = size < min_left ? size : min_left;
268       if (chunk_size <= 0)
269         break;
270
271       if (sector_ofs == 0 && chunk_size == DISK_SECTOR_SIZE) 
272         {
273           /* Write full sector directly to disk. */
274           disk_write (filesys_disk, sector_idx, buffer + bytes_written); 
275         }
276       else 
277         {
278           /* We need a bounce buffer. */
279           if (bounce == NULL) 
280             {
281               bounce = malloc (DISK_SECTOR_SIZE);
282               if (bounce == NULL)
283                 break;
284             }
285
286           /* If the sector contains data before or after the chunk
287              we're writing, then we need to read in the sector
288              first.  Otherwise we start with a sector of all zeros. */
289           if (sector_ofs > 0 || chunk_size < sector_left) 
290             disk_read (filesys_disk, sector_idx, bounce);
291           else
292             memset (bounce, 0, DISK_SECTOR_SIZE);
293           memcpy (bounce + sector_ofs, buffer + bytes_written, chunk_size);
294           disk_write (filesys_disk, sector_idx, bounce); 
295         }
296
297       /* Advance. */
298       size -= chunk_size;
299       offset += chunk_size;
300       bytes_written += chunk_size;
301     }
302   free (bounce);
303
304   return bytes_written;
305 }
306
307 /* Disables writes to INODE.
308    May be called at most once per inode opener. */
309 void
310 inode_deny_write (struct inode *inode) 
311 {
312   inode->deny_write_cnt++;
313   ASSERT (inode->deny_write_cnt <= inode->open_cnt);
314 }
315
316 /* Re-enables writes to INODE.
317    Must be called once by each inode opener who has called
318    inode_deny_write() on the inode, before closing the inode. */
319 void
320 inode_allow_write (struct inode *inode) 
321 {
322   ASSERT (inode->deny_write_cnt > 0);
323   ASSERT (inode->deny_write_cnt <= inode->open_cnt);
324   inode->deny_write_cnt--;
325 }
326
327 /* Returns the length, in bytes, of INODE's data. */
328 off_t
329 inode_length (const struct inode *inode)
330 {
331   return inode->data.length;
332 }