#include <bitmap.h>
#include <list.h>
#include <debug.h>
+#include <round.h>
#include <stdio.h>
#include "filesys/filesys.h"
#include "threads/malloc.h"
-/* Number of direct sector pointers in an inode. */
-#define DIRECT_CNT ((DISK_SECTOR_SIZE - sizeof (off_t) - sizeof (size_t)) \
- / sizeof (disk_sector_t))
-
/* On-disk inode.
- It is exactly DISK_SECTOR_SIZE bytes long. */
+ Must be exactly DISK_SECTOR_SIZE bytes long. */
struct inode_disk
{
off_t length; /* File size in bytes. */
- size_t sector_cnt; /* File size in sectors. */
- disk_sector_t sectors[DIRECT_CNT]; /* Sectors allocated for file. */
+ disk_sector_t start; /* Starting sector. */
+ uint32_t unused[126]; /* Unused padding. */
};
+/* Returns the number of sectors to allocate for a file SIZE
+ bytes long. */
+static inline size_t
+bytes_to_sectors (off_t size)
+{
+ return DIV_ROUND_UP (size, DISK_SECTOR_SIZE);
+}
+
/* In-memory inode. */
struct inode
{
/* List of open inodes, so that opening a single inode twice
returns the same `struct inode'. */
-struct list open_inodes;
+static struct list open_inodes;
-static struct inode *alloc_inode (disk_sector_t);
+static void deallocate_inode (const struct inode *);
/* Initializes the inode module. */
void
list_init (&open_inodes);
}
-/* Allocates sectors from bitmap B for the content of a file
- whose size is LENGTH bytes, and returns a new `struct inode'
- properly initialized for the file.
- It is the caller's responsible to write the inode to disk with
- inode_commit(), as well as the bitmap.
- If memory or disk allocation fails, returns a null pointer,
- leaving bitmap B is unchanged. */
-struct inode *
-inode_create (struct bitmap *b, disk_sector_t sector, off_t length)
+/* Initializes an inode for a file LENGTH bytes in size and
+ writes the new inode to sector SECTOR on the file system
+ disk. Allocates sectors for the file from FREE_MAP.
+ Returns true if successful.
+ Returns false if memory or disk allocation fails. FREE_MAP
+ may be modified regardless. */
+bool
+inode_create (struct bitmap *free_map, disk_sector_t sector, off_t length)
{
- struct inode *idx;
- size_t sector_cnt;
+ static const char zero_sector[DISK_SECTOR_SIZE];
+ struct inode_disk *idx;
+ size_t start;
+ size_t i;
- ASSERT (b != NULL);
+ ASSERT (free_map != NULL);
ASSERT (length >= 0);
- /* Calculate number of sectors to allocate for file data. */
- sector_cnt = (length / DISK_SECTOR_SIZE) + (length % DISK_SECTOR_SIZE > 0);
- if (sector_cnt > DIRECT_CNT)
- return NULL;
+ /* Allocate sectors. */
+ start = bitmap_scan_and_flip (free_map, 0, bytes_to_sectors (length), false);
+ if (start == BITMAP_ERROR)
+ return false;
- /* Allocate inode. */
- idx = alloc_inode (sector);
+ /* Create inode. */
+ idx = calloc (sizeof *idx, 1);
if (idx == NULL)
- return NULL;
+ return false;
+ idx->length = length;
+ idx->start = start;
- /* Allocate disk sectors. */
- idx->data.length = length;
- while (idx->data.sector_cnt < sector_cnt)
- {
- size_t sector = bitmap_find_and_set (b);
- if (sector == BITMAP_ERROR)
- goto error;
+ /* Commit to disk. */
+ disk_write (filesys_disk, sector, idx);
+ for (i = 0; i < bytes_to_sectors (length); i++)
+ disk_write (filesys_disk, idx->start + i, zero_sector);
- idx->data.sectors[idx->data.sector_cnt++] = sector;
- }
-
- /* Zero out the file contents. */
- if (sector_cnt > 0)
- {
- void *zero_sector;
- size_t i;
-
- zero_sector = calloc (1, DISK_SECTOR_SIZE);
- if (zero_sector == NULL)
- goto error;
- for (i = 0; i < sector_cnt; i++)
- disk_write (filesys_disk, idx->data.sectors[i], zero_sector);
- free (zero_sector);
- }
-
- return idx;
-
- error:
- inode_remove (idx);
- inode_close (idx);
- return NULL;
+ free (idx);
+ return true;
}
/* Reads an inode from SECTOR
list_elem *e;
struct inode *idx;
- /* Check whether this inode is already open. */
+ /* Check whether this inode is already open.
+ (A hash table would be better, but the Pintos base code
+ avoids using the hash table so that users are free to modify
+ it at will.) */
for (e = list_begin (&open_inodes); e != list_end (&open_inodes);
e = list_next (e))
{
}
}
- /* Allocate inode. */
- idx = alloc_inode (sector);
+ /* Allocate memory. */
+ idx = calloc (1, sizeof *idx);
if (idx == NULL)
return NULL;
+ /* Initialize. */
+ list_push_front (&open_inodes, &idx->elem);
+ idx->sector = sector;
+ idx->open_cnt = 1;
+ idx->removed = false;
+
/* Read from disk. */
ASSERT (sizeof idx->data == DISK_SECTOR_SIZE);
disk_read (filesys_disk, sector, &idx->data);
void
inode_close (struct inode *idx)
{
+ /* Ignore null pointer. */
if (idx == NULL)
return;
+ /* Release resources if this was the last opener. */
if (--idx->open_cnt == 0)
{
- if (idx->removed)
- {
- struct bitmap *free_map;
- size_t i;
-
- free_map = bitmap_create (disk_size (filesys_disk));
- if (free_map != NULL)
- {
- bitmap_read (free_map, free_map_file);
-
- bitmap_reset (free_map, idx->sector);
- for (i = 0; i < idx->data.sector_cnt; i++)
- bitmap_reset (free_map, idx->data.sectors[i]);
-
- bitmap_write (free_map, free_map_file);
- bitmap_destroy (free_map);
- }
- else
- printf ("inode_close(): can't free blocks");
- }
+ /* Deallocate blocks if removed. */
+ if (idx->removed)
+ deallocate_inode (idx);
+
+ /* Remove from inode list and free memory. */
list_remove (&idx->elem);
free (idx);
}
}
-/* Writes IDX to disk. */
-void
-inode_commit (const struct inode *idx)
+/* Deallocates the blocks allocated for IDX. */
+static void
+deallocate_inode (const struct inode *idx)
{
- ASSERT (idx != NULL);
- ASSERT (sizeof idx->data == DISK_SECTOR_SIZE);
- disk_write (filesys_disk, idx->sector, &idx->data);
+ struct bitmap *free_map = bitmap_create (disk_size (filesys_disk));
+ if (free_map != NULL)
+ {
+ bitmap_read (free_map, free_map_file);
+ bitmap_reset (free_map, idx->sector);
+ bitmap_set_multiple (free_map, idx->data.start,
+ bytes_to_sectors (idx->data.length), false);
+ bitmap_write (free_map, free_map_file);
+ bitmap_destroy (free_map);
+ }
+ else
+ printf ("inode_close(): can't free blocks");
}
/* Marks IDX to be deleted when it is closed by the last caller who
disk_sector_t
inode_byte_to_sector (const struct inode *idx, off_t pos)
{
- size_t i;
-
ASSERT (idx != NULL);
-
- i = pos / DISK_SECTOR_SIZE;
- return i < idx->data.sector_cnt ? idx->data.sectors[i] : (disk_sector_t) -1;
+ ASSERT (pos < idx->data.length);
+ return idx->data.start + pos / DISK_SECTOR_SIZE;
}
/* Returns the length, in bytes, of the file with inode IDX. */
void
inode_print (const struct inode *idx)
{
- size_t i;
-
- printf ("Inode %"PRDSNu": %"PRDSNu" bytes, %zd sectors (",
- idx->sector, idx->data.length, idx->data.sector_cnt);
-
- /* This loop could be unsafe for large idx->data.sector_cnt, can
- you see why? */
- for (i = 0; i < idx->data.sector_cnt; i++)
- {
- if (i != 0)
- printf (", ");
- printf ("%"PRDSNu, idx->data.sectors[i]);
- }
- printf (")\n");
-}
-\f
-/* Returns a newly allocated and initialized inode. */
-static struct inode *
-alloc_inode (disk_sector_t sector)
-{
- /* Allocate memory. */
- struct inode *idx = calloc (1, sizeof *idx);
- if (idx == NULL)
- return NULL;
-
- /* Initialize. */
- list_push_front (&open_inodes, &idx->elem);
- idx->sector = sector;
- idx->open_cnt = 1;
- idx->removed = false;
-
- return idx;
+ ASSERT (idx != NULL);
+ printf ("Inode %"PRDSNu": %"PRDSNu" bytes, "
+ "%zu sectors starting at %"PRDSNu"\n",
+ idx->sector, idx->data.length,
+ (size_t) DIV_ROUND_UP (idx->data.length, DISK_SECTOR_SIZE),
+ idx->data.start);
}