Fix bitmap_scan_and_flip() calls.
[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   for (e = list_begin (&open_inodes); e != list_end (&open_inodes);
115        e = list_next (e)) 
116     {
117       idx = list_entry (e, struct inode, elem);
118       if (idx->sector == sector) 
119         {
120           idx->open_cnt++;
121           return idx;
122         }
123     }
124
125   /* Allocate inode. */
126   idx = alloc_inode (sector);
127   if (idx == NULL)
128     return NULL;
129
130   /* Read from disk. */
131   ASSERT (sizeof idx->data == DISK_SECTOR_SIZE);
132   disk_read (filesys_disk, sector, &idx->data);
133
134   return idx;
135 }
136
137 /* Closes inode IDX and writes it to disk.
138    If this was the last reference to IDX, frees its memory.
139    If IDX was also a removed inode, frees its blocks. */
140 void
141 inode_close (struct inode *idx) 
142 {
143   if (idx == NULL)
144     return;
145
146   if (--idx->open_cnt == 0)
147     {
148       if (idx->removed) 
149         {
150           struct bitmap *free_map;
151           size_t i;
152
153           free_map = bitmap_create (disk_size (filesys_disk));
154           if (free_map != NULL)
155             {
156               bitmap_read (free_map, free_map_file);
157               
158               bitmap_reset (free_map, idx->sector);
159               for (i = 0; i < idx->data.sector_cnt; i++)
160                 bitmap_reset (free_map, idx->data.sectors[i]);
161
162               bitmap_write (free_map, free_map_file);
163               bitmap_destroy (free_map);
164             }
165           else
166             printf ("inode_close(): can't free blocks");
167         }
168       list_remove (&idx->elem);
169       free (idx); 
170     }
171 }
172
173 /* Writes IDX to disk. */
174 void
175 inode_commit (const struct inode *idx) 
176 {
177   ASSERT (idx != NULL);
178   ASSERT (sizeof idx->data == DISK_SECTOR_SIZE);
179   disk_write (filesys_disk, idx->sector, &idx->data);
180 }
181
182 /* Marks IDX to be deleted when it is closed by the last caller who
183    has it open. */
184 void
185 inode_remove (struct inode *idx) 
186 {
187   ASSERT (idx != NULL);
188   idx->removed = true;
189 }
190
191 /* Returns the disk sector that contains byte offset POS within
192    the file with inode IDX.
193    Returns -1 if IDX does not contain data for a byte at offset
194    POS. */
195 disk_sector_t
196 inode_byte_to_sector (const struct inode *idx, off_t pos) 
197 {
198   size_t i;
199
200   ASSERT (idx != NULL);
201
202   i = pos / DISK_SECTOR_SIZE;
203   return i < idx->data.sector_cnt ? idx->data.sectors[i] : (disk_sector_t) -1;
204 }
205
206 /* Returns the length, in bytes, of the file with inode IDX. */
207 off_t
208 inode_length (const struct inode *idx)
209 {
210   ASSERT (idx != NULL);
211   return idx->data.length;
212 }
213
214 /* Prints a representation of IDX to the system console. */
215 void
216 inode_print (const struct inode *idx) 
217 {
218   size_t i;
219   
220   printf ("Inode %"PRDSNu": %"PRDSNu" bytes, %zd sectors (",
221           idx->sector, idx->data.length, idx->data.sector_cnt);
222
223   /* This loop could be unsafe for large idx->data.sector_cnt, can
224      you see why? */
225   for (i = 0; i < idx->data.sector_cnt; i++) 
226     {
227       if (i != 0)
228         printf (", ");
229       printf ("%"PRDSNu, idx->data.sectors[i]); 
230     }
231   printf (")\n");
232 }
233 \f
234 /* Returns a newly allocated and initialized inode. */
235 static struct inode *
236 alloc_inode (disk_sector_t sector) 
237 {
238   /* Allocate memory. */
239   struct inode *idx = calloc (1, sizeof *idx);
240   if (idx == NULL)
241     return NULL;
242
243   /* Initialize. */
244   list_push_front (&open_inodes, &idx->elem);
245   idx->sector = sector;
246   idx->open_cnt = 1;
247   idx->removed = false;
248
249   return idx;
250 }