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