diff options
Diffstat (limited to 'pintos-progos/filesys/inode.c')
| -rw-r--r-- | pintos-progos/filesys/inode.c | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/pintos-progos/filesys/inode.c b/pintos-progos/filesys/inode.c new file mode 100644 index 0000000..3463563 --- /dev/null +++ b/pintos-progos/filesys/inode.c | |||
| @@ -0,0 +1,345 @@ | |||
| 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 BLOCK_SECTOR_SIZE bytes long. */ | ||
| 15 | struct inode_disk | ||
| 16 | { | ||
| 17 | block_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, BLOCK_SECTOR_SIZE); | ||
| 29 | } | ||
| 30 | |||
| 31 | /* In-memory inode. */ | ||
| 32 | struct inode | ||
| 33 | { | ||
| 34 | struct list_elem elem; /* Element in inode list. */ | ||
| 35 | block_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 block device sector that contains byte offset POS | ||
| 43 | within INODE. | ||
| 44 | Returns -1 if INODE does not contain data for a byte at offset | ||
| 45 | POS. */ | ||
| 46 | static block_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 / BLOCK_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 | device. | ||
| 70 | Returns true if successful. | ||
| 71 | Returns false if memory or disk allocation fails. */ | ||
| 72 | bool | ||
| 73 | inode_create (block_sector_t sector, off_t length) | ||
| 74 | { | ||
| 75 | struct inode_disk *disk_inode = NULL; | ||
| 76 | bool success = false; | ||
| 77 | |||
| 78 | ASSERT (length >= 0); | ||
| 79 | |||
| 80 | /* If this assertion fails, the inode structure is not exactly | ||
| 81 | one sector in size, and you should fix that. */ | ||
| 82 | ASSERT (sizeof *disk_inode == BLOCK_SECTOR_SIZE); | ||
| 83 | |||
| 84 | disk_inode = calloc (1, sizeof *disk_inode); | ||
| 85 | if (disk_inode != NULL) | ||
| 86 | { | ||
| 87 | size_t sectors = bytes_to_sectors (length); | ||
| 88 | disk_inode->length = length; | ||
| 89 | disk_inode->magic = INODE_MAGIC; | ||
| 90 | if (free_map_allocate (sectors, &disk_inode->start)) | ||
| 91 | { | ||
| 92 | block_write (fs_device, sector, disk_inode); | ||
| 93 | if (sectors > 0) | ||
| 94 | { | ||
| 95 | static char zeros[BLOCK_SECTOR_SIZE]; | ||
| 96 | size_t i; | ||
| 97 | |||
| 98 | for (i = 0; i < sectors; i++) | ||
| 99 | block_write (fs_device, disk_inode->start + i, zeros); | ||
| 100 | } | ||
| 101 | success = true; | ||
| 102 | } | ||
| 103 | free (disk_inode); | ||
| 104 | } | ||
| 105 | return success; | ||
| 106 | } | ||
| 107 | |||
| 108 | /* Reads an inode from SECTOR | ||
| 109 | and returns a `struct inode' that contains it. | ||
| 110 | Returns a null pointer if memory allocation fails. */ | ||
| 111 | struct inode * | ||
| 112 | inode_open (block_sector_t sector) | ||
| 113 | { | ||
| 114 | struct list_elem *e; | ||
| 115 | struct inode *inode; | ||
| 116 | |||
| 117 | /* Check whether this inode is already open. */ | ||
| 118 | for (e = list_begin (&open_inodes); e != list_end (&open_inodes); | ||
| 119 | e = list_next (e)) | ||
| 120 | { | ||
| 121 | inode = list_entry (e, struct inode, elem); | ||
| 122 | if (inode->sector == sector) | ||
| 123 | { | ||
| 124 | inode_reopen (inode); | ||
| 125 | return inode; | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | /* Allocate memory. */ | ||
| 130 | inode = malloc (sizeof *inode); | ||
| 131 | if (inode == NULL) | ||
| 132 | return NULL; | ||
| 133 | |||
| 134 | /* Initialize. */ | ||
| 135 | list_push_front (&open_inodes, &inode->elem); | ||
| 136 | inode->sector = sector; | ||
| 137 | inode->open_cnt = 1; | ||
| 138 | inode->deny_write_cnt = 0; | ||
| 139 | inode->removed = false; | ||
| 140 | block_read (fs_device, inode->sector, &inode->data); | ||
| 141 | return inode; | ||
| 142 | } | ||
| 143 | |||
| 144 | /* Reopens and returns INODE. */ | ||
| 145 | struct inode * | ||
| 146 | inode_reopen (struct inode *inode) | ||
| 147 | { | ||
| 148 | if (inode != NULL) | ||
| 149 | inode->open_cnt++; | ||
| 150 | return inode; | ||
| 151 | } | ||
| 152 | |||
| 153 | /* Returns INODE's inode number. */ | ||
| 154 | block_sector_t | ||
| 155 | inode_get_inumber (const struct inode *inode) | ||
| 156 | { | ||
| 157 | return inode->sector; | ||
| 158 | } | ||
| 159 | |||
| 160 | /* Closes INODE and writes it to disk. | ||
| 161 | If this was the last reference to INODE, frees its memory. | ||
| 162 | If INODE was also a removed inode, frees its blocks. */ | ||
| 163 | void | ||
| 164 | inode_close (struct inode *inode) | ||
| 165 | { | ||
| 166 | /* Ignore null pointer. */ | ||
| 167 | if (inode == NULL) | ||
| 168 | return; | ||
| 169 | |||
| 170 | /* Release resources if this was the last opener. */ | ||
| 171 | if (--inode->open_cnt == 0) | ||
| 172 | { | ||
| 173 | /* Remove from inode list and release lock. */ | ||
| 174 | list_remove (&inode->elem); | ||
| 175 | |||
| 176 | /* Deallocate blocks if removed. */ | ||
| 177 | if (inode->removed) | ||
| 178 | { | ||
| 179 | free_map_release (inode->sector, 1); | ||
| 180 | free_map_release (inode->data.start, | ||
| 181 | bytes_to_sectors (inode->data.length)); | ||
| 182 | } | ||
| 183 | |||
| 184 | free (inode); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | /* Marks INODE to be deleted when it is closed by the last caller who | ||
| 189 | has it open. */ | ||
| 190 | void | ||
| 191 | inode_remove (struct inode *inode) | ||
| 192 | { | ||
| 193 | ASSERT (inode != NULL); | ||
| 194 | inode->removed = true; | ||
| 195 | } | ||
| 196 | |||
| 197 | /* Reads SIZE bytes from INODE into BUFFER, starting at position OFFSET. | ||
| 198 | Returns the number of bytes actually read, which may be less | ||
| 199 | than SIZE if an error occurs or end of file is reached. */ | ||
| 200 | off_t | ||
| 201 | inode_read_at (struct inode *inode, void *buffer_, off_t size, off_t offset) | ||
| 202 | { | ||
| 203 | uint8_t *buffer = buffer_; | ||
| 204 | off_t bytes_read = 0; | ||
| 205 | uint8_t *bounce = NULL; | ||
| 206 | |||
| 207 | while (size > 0) | ||
| 208 | { | ||
| 209 | /* Disk sector to read, starting byte offset within sector. */ | ||
| 210 | block_sector_t sector_idx = byte_to_sector (inode, offset); | ||
| 211 | int sector_ofs = offset % BLOCK_SECTOR_SIZE; | ||
| 212 | |||
| 213 | /* Bytes left in inode, bytes left in sector, lesser of the two. */ | ||
| 214 | off_t inode_left = inode_length (inode) - offset; | ||
| 215 | int sector_left = BLOCK_SECTOR_SIZE - sector_ofs; | ||
| 216 | int min_left = inode_left < sector_left ? inode_left : sector_left; | ||
| 217 | |||
| 218 | /* Number of bytes to actually copy out of this sector. */ | ||
| 219 | int chunk_size = size < min_left ? size : min_left; | ||
| 220 | if (chunk_size <= 0) | ||
| 221 | break; | ||
| 222 | |||
| 223 | if (sector_ofs == 0 && chunk_size == BLOCK_SECTOR_SIZE) | ||
| 224 | { | ||
| 225 | /* Read full sector directly into caller's buffer. */ | ||
| 226 | block_read (fs_device, sector_idx, buffer + bytes_read); | ||
| 227 | } | ||
| 228 | else | ||
| 229 | { | ||
| 230 | /* Read sector into bounce buffer, then partially copy | ||
| 231 | into caller's buffer. */ | ||
| 232 | if (bounce == NULL) | ||
| 233 | { | ||
| 234 | bounce = malloc (BLOCK_SECTOR_SIZE); | ||
| 235 | if (bounce == NULL) | ||
| 236 | break; | ||
| 237 | } | ||
| 238 | block_read (fs_device, sector_idx, bounce); | ||
| 239 | memcpy (buffer + bytes_read, bounce + sector_ofs, chunk_size); | ||
| 240 | } | ||
| 241 | |||
| 242 | /* Advance. */ | ||
| 243 | size -= chunk_size; | ||
| 244 | offset += chunk_size; | ||
| 245 | bytes_read += chunk_size; | ||
| 246 | } | ||
| 247 | free (bounce); | ||
| 248 | |||
| 249 | return bytes_read; | ||
| 250 | } | ||
| 251 | |||
| 252 | /* Writes SIZE bytes from BUFFER into INODE, starting at OFFSET. | ||
| 253 | Returns the number of bytes actually written, which may be | ||
| 254 | less than SIZE if end of file is reached or an error occurs. | ||
| 255 | (Normally a write at end of file would extend the inode, but | ||
| 256 | growth is not yet implemented.) */ | ||
| 257 | off_t | ||
| 258 | inode_write_at (struct inode *inode, const void *buffer_, off_t size, | ||
| 259 | off_t offset) | ||
| 260 | { | ||
| 261 | const uint8_t *buffer = buffer_; | ||
| 262 | off_t bytes_written = 0; | ||
| 263 | uint8_t *bounce = NULL; | ||
| 264 | |||
| 265 | if (inode->deny_write_cnt) | ||
| 266 | return 0; | ||
| 267 | |||
| 268 | while (size > 0) | ||
| 269 | { | ||
| 270 | /* Sector to write, starting byte offset within sector. */ | ||
| 271 | block_sector_t sector_idx = byte_to_sector (inode, offset); | ||
| 272 | int sector_ofs = offset % BLOCK_SECTOR_SIZE; | ||
| 273 | |||
| 274 | /* Bytes left in inode, bytes left in sector, lesser of the two. */ | ||
| 275 | off_t inode_left = inode_length (inode) - offset; | ||
| 276 | int sector_left = BLOCK_SECTOR_SIZE - sector_ofs; | ||
| 277 | int min_left = inode_left < sector_left ? inode_left : sector_left; | ||
| 278 | |||
| 279 | /* Number of bytes to actually write into this sector. */ | ||
| 280 | int chunk_size = size < min_left ? size : min_left; | ||
| 281 | if (chunk_size <= 0) | ||
| 282 | break; | ||
| 283 | |||
| 284 | if (sector_ofs == 0 && chunk_size == BLOCK_SECTOR_SIZE) | ||
| 285 | { | ||
| 286 | /* Write full sector directly to disk. */ | ||
| 287 | block_write (fs_device, sector_idx, buffer + bytes_written); | ||
| 288 | } | ||
| 289 | else | ||
| 290 | { | ||
| 291 | /* We need a bounce buffer. */ | ||
| 292 | if (bounce == NULL) | ||
| 293 | { | ||
| 294 | bounce = malloc (BLOCK_SECTOR_SIZE); | ||
| 295 | if (bounce == NULL) | ||
| 296 | break; | ||
| 297 | } | ||
| 298 | |||
| 299 | /* If the sector contains data before or after the chunk | ||
| 300 | we're writing, then we need to read in the sector | ||
| 301 | first. Otherwise we start with a sector of all zeros. */ | ||
| 302 | if (sector_ofs > 0 || chunk_size < sector_left) | ||
| 303 | block_read (fs_device, sector_idx, bounce); | ||
| 304 | else | ||
| 305 | memset (bounce, 0, BLOCK_SECTOR_SIZE); | ||
| 306 | memcpy (bounce + sector_ofs, buffer + bytes_written, chunk_size); | ||
| 307 | block_write (fs_device, sector_idx, bounce); | ||
| 308 | } | ||
| 309 | |||
| 310 | /* Advance. */ | ||
| 311 | size -= chunk_size; | ||
| 312 | offset += chunk_size; | ||
| 313 | bytes_written += chunk_size; | ||
| 314 | } | ||
| 315 | free (bounce); | ||
| 316 | |||
| 317 | return bytes_written; | ||
| 318 | } | ||
| 319 | |||
| 320 | /* Disables writes to INODE. | ||
| 321 | May be called at most once per inode opener. */ | ||
| 322 | void | ||
| 323 | inode_deny_write (struct inode *inode) | ||
| 324 | { | ||
| 325 | inode->deny_write_cnt++; | ||
| 326 | ASSERT (inode->deny_write_cnt <= inode->open_cnt); | ||
| 327 | } | ||
| 328 | |||
| 329 | /* Re-enables writes to INODE. | ||
| 330 | Must be called once by each inode opener who has called | ||
| 331 | inode_deny_write() on the inode, before closing the inode. */ | ||
| 332 | void | ||
| 333 | inode_allow_write (struct inode *inode) | ||
| 334 | { | ||
| 335 | ASSERT (inode->deny_write_cnt > 0); | ||
| 336 | ASSERT (inode->deny_write_cnt <= inode->open_cnt); | ||
| 337 | inode->deny_write_cnt--; | ||
| 338 | } | ||
| 339 | |||
| 340 | /* Returns the length, in bytes, of INODE's data. */ | ||
| 341 | off_t | ||
| 342 | inode_length (const struct inode *inode) | ||
| 343 | { | ||
| 344 | return inode->data.length; | ||
| 345 | } | ||
