Start work on partition support.
[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 "devices/partition.h"
9 #include "threads/malloc.h"
10
11 /* On-disk inode.
12    Must be exactly DISK_SECTOR_SIZE bytes long. */
13 struct inode_disk
14   {
15     off_t length;                       /* File size in bytes. */
16     disk_sector_t start;                /* Starting sector. */
17     uint32_t unused[126];               /* Unused padding. */
18   };
19
20 /* Returns the number of sectors to allocate for a file SIZE
21    bytes long. */
22 static inline size_t
23 bytes_to_sectors (off_t size)
24 {
25   return DIV_ROUND_UP (size, DISK_SECTOR_SIZE);
26 }
27
28 /* In-memory inode. */
29 struct inode 
30   {
31     list_elem elem;                     /* Element in inode list. */
32     disk_sector_t sector;               /* Sector number of disk location. */
33     int open_cnt;                       /* Number of openers. */
34     bool removed;                       /* True if deleted, false otherwise. */
35     struct inode_disk data;             /* On-disk data. */
36   };
37
38 /* List of open inodes, so that opening a single inode twice
39    returns the same `struct inode'. */
40 static struct list open_inodes;
41
42 static void deallocate_inode (const struct inode *);
43
44 /* Initializes the inode module. */
45 void
46 inode_init (void) 
47 {
48   list_init (&open_inodes);
49 }
50
51 /* Initializes an inode for a file LENGTH bytes in size and
52    writes the new inode to sector SECTOR on the file system
53    disk.  Allocates sectors for the file from FREE_MAP.
54    Returns true if successful.
55    Returns false if memory or disk allocation fails.  FREE_MAP
56    may be modified regardless. */
57 bool
58 inode_create (struct bitmap *free_map, disk_sector_t sector, off_t length) 
59 {
60   static const char zero_sector[DISK_SECTOR_SIZE];
61   struct inode_disk *idx;
62   size_t start;
63   size_t i;
64
65   ASSERT (free_map != NULL);
66   ASSERT (length >= 0);
67
68   /* Allocate sectors. */
69   start = bitmap_scan_and_flip (free_map, 0, bytes_to_sectors (length), false);
70   if (start == BITMAP_ERROR)
71     return false;
72
73   /* Create inode. */
74   idx = calloc (sizeof *idx, 1);
75   if (idx == NULL)
76     return false;
77   idx->length = length;
78   idx->start = start;
79
80   /* Commit to disk. */
81   partition_write (filesys_partition, sector, idx);
82   for (i = 0; i < bytes_to_sectors (length); i++)
83     partition_write (filesys_partition, idx->start + i, zero_sector);
84
85   free (idx);
86   return true;
87 }
88
89 /* Reads an inode from SECTOR
90    and returns a `struct inode' that contains it.
91    Returns a null pointer if memory allocation fails. */
92 struct inode *
93 inode_open (disk_sector_t sector) 
94 {
95   list_elem *e;
96   struct inode *idx;
97
98   /* Check whether this inode is already open.
99      (A hash table would be better, but the Pintos base code
100      avoids using the hash table so that users are free to modify
101      it at will.) */
102   for (e = list_begin (&open_inodes); e != list_end (&open_inodes);
103        e = list_next (e)) 
104     {
105       idx = list_entry (e, struct inode, elem);
106       if (idx->sector == sector) 
107         {
108           idx->open_cnt++;
109           return idx;
110         }
111     }
112
113   /* Allocate memory. */
114   idx = calloc (1, sizeof *idx);
115   if (idx == NULL)
116     return NULL;
117
118   /* Initialize. */
119   list_push_front (&open_inodes, &idx->elem);
120   idx->sector = sector;
121   idx->open_cnt = 1;
122   idx->removed = false;
123
124   /* Read from disk. */
125   ASSERT (sizeof idx->data == DISK_SECTOR_SIZE);
126   partition_read (filesys_partition, sector, &idx->data);
127
128   return idx;
129 }
130
131 /* Closes inode IDX and writes it to disk.
132    If this was the last reference to IDX, frees its memory.
133    If IDX was also a removed inode, frees its blocks. */
134 void
135 inode_close (struct inode *idx) 
136 {
137   /* Ignore null pointer. */
138   if (idx == NULL)
139     return;
140
141   /* Release resources if this was the last opener. */
142   if (--idx->open_cnt == 0)
143     {
144       /* Deallocate blocks if removed. */
145       if (idx->removed)
146         deallocate_inode (idx);
147
148       /* Remove from inode list and free memory. */
149       list_remove (&idx->elem);
150       free (idx); 
151     }
152 }
153
154 /* Deallocates the blocks allocated for IDX. */
155 static void
156 deallocate_inode (const struct inode *idx)
157 {
158   struct bitmap *free_map = bitmap_create (partition_size (filesys_partition));
159   if (free_map != NULL) 
160     {
161       bitmap_read (free_map, free_map_file);
162       bitmap_reset (free_map, idx->sector);
163       bitmap_set_multiple (free_map, idx->data.start,
164                            bytes_to_sectors (idx->data.length), false);
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
172 /* Marks IDX to be deleted when it is closed by the last caller who
173    has it open. */
174 void
175 inode_remove (struct inode *idx) 
176 {
177   ASSERT (idx != NULL);
178   idx->removed = true;
179 }
180
181 /* Returns the disk sector that contains byte offset POS within
182    the file with inode IDX.
183    Returns -1 if IDX does not contain data for a byte at offset
184    POS. */
185 disk_sector_t
186 inode_byte_to_sector (const struct inode *idx, off_t pos) 
187 {
188   ASSERT (idx != NULL);
189   ASSERT (pos < idx->data.length);
190   return idx->data.start + pos / DISK_SECTOR_SIZE;
191 }
192
193 /* Returns the length, in bytes, of the file with inode IDX. */
194 off_t
195 inode_length (const struct inode *idx)
196 {
197   ASSERT (idx != NULL);
198   return idx->data.length;
199 }
200
201 /* Prints a representation of IDX to the system console. */
202 void
203 inode_print (const struct inode *idx) 
204 {
205   ASSERT (idx != NULL);
206   printf ("Inode %"PRDSNu": %"PRDSNu" bytes, "
207           "%zu sectors starting at %"PRDSNu"\n",
208           idx->sector, idx->data.length,
209           (size_t) DIV_ROUND_UP (idx->data.length, DISK_SECTOR_SIZE),
210           idx->data.start);
211 }