Change list_elem from typedef to struct.
[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 start;                /* Starting sector. */
16     uint32_t unused[126];               /* Unused padding. */
17   };
18
19 /* Returns the number of sectors to allocate for a file SIZE
20    bytes long. */
21 static inline size_t
22 bytes_to_sectors (off_t size)
23 {
24   return DIV_ROUND_UP (size, DISK_SECTOR_SIZE);
25 }
26
27 /* In-memory inode. */
28 struct inode 
29   {
30     struct list_elem elem;              /* Element in inode list. */
31     disk_sector_t sector;               /* Sector number of disk location. */
32     int open_cnt;                       /* Number of openers. */
33     bool removed;                       /* True if deleted, false otherwise. */
34     struct inode_disk data;             /* On-disk data. */
35   };
36
37 /* List of open inodes, so that opening a single inode twice
38    returns the same `struct inode'. */
39 static struct list open_inodes;
40
41 static void deallocate_inode (const struct inode *);
42
43 /* Initializes the inode module. */
44 void
45 inode_init (void) 
46 {
47   list_init (&open_inodes);
48 }
49
50 /* Initializes an inode for a file LENGTH bytes in size and
51    writes the new inode to sector SECTOR on the file system
52    disk.  Allocates sectors for the file from FREE_MAP.
53    Returns true if successful.
54    Returns false if memory or disk allocation fails.  FREE_MAP
55    may be modified regardless. */
56 bool
57 inode_create (struct bitmap *free_map, disk_sector_t sector, off_t length) 
58 {
59   static const char zero_sector[DISK_SECTOR_SIZE];
60   struct inode_disk *idx;
61   size_t start;
62   size_t i;
63
64   ASSERT (free_map != NULL);
65   ASSERT (length >= 0);
66
67   /* Allocate sectors. */
68   start = bitmap_scan_and_flip (free_map, 0, bytes_to_sectors (length), false);
69   if (start == BITMAP_ERROR)
70     return false;
71
72   /* Create inode. */
73   idx = calloc (sizeof *idx, 1);
74   if (idx == NULL)
75     return false;
76   idx->length = length;
77   idx->start = start;
78
79   /* Commit to disk. */
80   disk_write (filesys_disk, sector, idx);
81   for (i = 0; i < bytes_to_sectors (length); i++)
82     disk_write (filesys_disk, idx->start + i, zero_sector);
83
84   free (idx);
85   return true;
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   struct 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 memory. */
113   idx = calloc (1, sizeof *idx);
114   if (idx == NULL)
115     return NULL;
116
117   /* Initialize. */
118   list_push_front (&open_inodes, &idx->elem);
119   idx->sector = sector;
120   idx->open_cnt = 1;
121   idx->removed = false;
122
123   /* Read from disk. */
124   ASSERT (sizeof idx->data == DISK_SECTOR_SIZE);
125   disk_read (filesys_disk, sector, &idx->data);
126
127   return idx;
128 }
129
130 /* Closes inode IDX and writes it to disk.
131    If this was the last reference to IDX, frees its memory.
132    If IDX was also a removed inode, frees its blocks. */
133 void
134 inode_close (struct inode *idx) 
135 {
136   /* Ignore null pointer. */
137   if (idx == NULL)
138     return;
139
140   /* Release resources if this was the last opener. */
141   if (--idx->open_cnt == 0)
142     {
143       /* Deallocate blocks if removed. */
144       if (idx->removed)
145         deallocate_inode (idx);
146
147       /* Remove from inode list and free memory. */
148       list_remove (&idx->elem);
149       free (idx); 
150     }
151 }
152
153 /* Deallocates the blocks allocated for IDX. */
154 static void
155 deallocate_inode (const struct inode *idx)
156 {
157   struct bitmap *free_map = bitmap_create (disk_size (filesys_disk));
158   if (free_map != NULL) 
159     {
160       bitmap_read (free_map, free_map_file);
161       bitmap_reset (free_map, idx->sector);
162       bitmap_set_multiple (free_map, idx->data.start,
163                            bytes_to_sectors (idx->data.length), false);
164       bitmap_write (free_map, free_map_file);
165       bitmap_destroy (free_map);
166     }
167   else
168     printf ("inode_close(): can't free blocks");
169 }
170
171 /* Marks IDX to be deleted when it is closed by the last caller who
172    has it open. */
173 void
174 inode_remove (struct inode *idx) 
175 {
176   ASSERT (idx != NULL);
177   idx->removed = true;
178 }
179
180 /* Returns the disk sector that contains byte offset POS within
181    the file with inode IDX.
182    Returns -1 if IDX does not contain data for a byte at offset
183    POS. */
184 disk_sector_t
185 inode_byte_to_sector (const struct inode *idx, off_t pos) 
186 {
187   ASSERT (idx != NULL);
188   ASSERT (pos < idx->data.length);
189   return idx->data.start + pos / DISK_SECTOR_SIZE;
190 }
191
192 /* Returns the length, in bytes, of the file with inode IDX. */
193 off_t
194 inode_length (const struct inode *idx)
195 {
196   ASSERT (idx != NULL);
197   return idx->data.length;
198 }
199
200 /* Prints a representation of IDX to the system console. */
201 void
202 inode_print (const struct inode *idx) 
203 {
204   ASSERT (idx != NULL);
205   printf ("Inode %"PRDSNu": %"PRDSNu" bytes, "
206           "%zu sectors starting at %"PRDSNu"\n",
207           idx->sector, idx->data.length,
208           (size_t) DIV_ROUND_UP (idx->data.length, DISK_SECTOR_SIZE),
209           idx->data.start);
210 }