diff options
Diffstat (limited to 'filesys')
| -rw-r--r-- | filesys/.gitignore | 3 | ||||
| -rw-r--r-- | filesys/Make.vars | 13 | ||||
| -rw-r--r-- | filesys/Makefile | 1 | ||||
| -rw-r--r-- | filesys/directory.c | 236 | ||||
| -rw-r--r-- | filesys/directory.h | 30 | ||||
| -rw-r--r-- | filesys/file.c | 168 | ||||
| -rw-r--r-- | filesys/file.h | 29 | ||||
| -rw-r--r-- | filesys/filesys.c | 103 | ||||
| -rw-r--r-- | filesys/filesys.h | 20 | ||||
| -rw-r--r-- | filesys/free-map.c | 85 | ||||
| -rw-r--r-- | filesys/free-map.h | 17 | ||||
| -rw-r--r-- | filesys/fsutil.c | 222 | ||||
| -rw-r--r-- | filesys/fsutil.h | 10 | ||||
| -rw-r--r-- | filesys/inode.c | 345 | ||||
| -rw-r--r-- | filesys/inode.h | 23 | ||||
| -rw-r--r-- | filesys/off_t.h | 15 |
16 files changed, 1320 insertions, 0 deletions
diff --git a/filesys/.gitignore b/filesys/.gitignore new file mode 100644 index 0000000..6d5357c --- /dev/null +++ b/filesys/.gitignore | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | build | ||
| 2 | bochsrc.txt | ||
| 3 | bochsout.txt | ||
diff --git a/filesys/Make.vars b/filesys/Make.vars new file mode 100644 index 0000000..b3aa005 --- /dev/null +++ b/filesys/Make.vars | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | kernel.bin: DEFINES = -DUSERPROG -DFILESYS | ||
| 4 | KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys | ||
| 5 | TEST_SUBDIRS = tests/userprog tests/filesys/base tests/filesys/extended | ||
| 6 | GRADING_FILE = $(SRCDIR)/tests/filesys/Grading.no-vm | ||
| 7 | SIMULATOR = --qemu | ||
| 8 | |||
| 9 | # Uncomment the lines below to enable VM. | ||
| 10 | #kernel.bin: DEFINES += -DVM | ||
| 11 | #KERNEL_SUBDIRS += vm | ||
| 12 | #TEST_SUBDIRS += tests/vm | ||
| 13 | #GRADING_FILE = $(SRCDIR)/tests/filesys/Grading.with-vm | ||
diff --git a/filesys/Makefile b/filesys/Makefile new file mode 100644 index 0000000..34c10aa --- /dev/null +++ b/filesys/Makefile | |||
| @@ -0,0 +1 @@ | |||
| include ../Makefile.kernel | |||
diff --git a/filesys/directory.c b/filesys/directory.c new file mode 100644 index 0000000..030c1c9 --- /dev/null +++ b/filesys/directory.c | |||
| @@ -0,0 +1,236 @@ | |||
| 1 | #include "filesys/directory.h" | ||
| 2 | #include <stdio.h> | ||
| 3 | #include <string.h> | ||
| 4 | #include <list.h> | ||
| 5 | #include "filesys/filesys.h" | ||
| 6 | #include "filesys/inode.h" | ||
| 7 | #include "threads/malloc.h" | ||
| 8 | |||
| 9 | /* A directory. */ | ||
| 10 | struct dir | ||
| 11 | { | ||
| 12 | struct inode *inode; /* Backing store. */ | ||
| 13 | off_t pos; /* Current position. */ | ||
| 14 | }; | ||
| 15 | |||
| 16 | /* A single directory entry. */ | ||
| 17 | struct dir_entry | ||
| 18 | { | ||
| 19 | block_sector_t inode_sector; /* Sector number of header. */ | ||
| 20 | char name[NAME_MAX + 1]; /* Null terminated file name. */ | ||
| 21 | bool in_use; /* In use or free? */ | ||
| 22 | }; | ||
| 23 | |||
| 24 | /* Creates a directory with space for ENTRY_CNT entries in the | ||
| 25 | given SECTOR. Returns true if successful, false on failure. */ | ||
| 26 | bool | ||
| 27 | dir_create (block_sector_t sector, size_t entry_cnt) | ||
| 28 | { | ||
| 29 | return inode_create (sector, entry_cnt * sizeof (struct dir_entry)); | ||
| 30 | } | ||
| 31 | |||
| 32 | /* Opens and returns the directory for the given INODE, of which | ||
| 33 | it takes ownership. Returns a null pointer on failure. */ | ||
| 34 | struct dir * | ||
| 35 | dir_open (struct inode *inode) | ||
| 36 | { | ||
| 37 | struct dir *dir = calloc (1, sizeof *dir); | ||
| 38 | if (inode != NULL && dir != NULL) | ||
| 39 | { | ||
| 40 | dir->inode = inode; | ||
| 41 | dir->pos = 0; | ||
| 42 | return dir; | ||
| 43 | } | ||
| 44 | else | ||
| 45 | { | ||
| 46 | inode_close (inode); | ||
| 47 | free (dir); | ||
| 48 | return NULL; | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | /* Opens the root directory and returns a directory for it. | ||
| 53 | Return true if successful, false on failure. */ | ||
| 54 | struct dir * | ||
| 55 | dir_open_root (void) | ||
| 56 | { | ||
| 57 | return dir_open (inode_open (ROOT_DIR_SECTOR)); | ||
| 58 | } | ||
| 59 | |||
| 60 | /* Opens and returns a new directory for the same inode as DIR. | ||
| 61 | Returns a null pointer on failure. */ | ||
| 62 | struct dir * | ||
| 63 | dir_reopen (struct dir *dir) | ||
| 64 | { | ||
| 65 | return dir_open (inode_reopen (dir->inode)); | ||
| 66 | } | ||
| 67 | |||
| 68 | /* Destroys DIR and frees associated resources. */ | ||
| 69 | void | ||
| 70 | dir_close (struct dir *dir) | ||
| 71 | { | ||
| 72 | if (dir != NULL) | ||
| 73 | { | ||
| 74 | inode_close (dir->inode); | ||
| 75 | free (dir); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | /* Returns the inode encapsulated by DIR. */ | ||
| 80 | struct inode * | ||
| 81 | dir_get_inode (struct dir *dir) | ||
| 82 | { | ||
| 83 | return dir->inode; | ||
| 84 | } | ||
| 85 | |||
| 86 | /* Searches DIR for a file with the given NAME. | ||
| 87 | If successful, returns true, sets *EP to the directory entry | ||
| 88 | if EP is non-null, and sets *OFSP to the byte offset of the | ||
| 89 | directory entry if OFSP is non-null. | ||
| 90 | otherwise, returns false and ignores EP and OFSP. */ | ||
| 91 | static bool | ||
| 92 | lookup (const struct dir *dir, const char *name, | ||
| 93 | struct dir_entry *ep, off_t *ofsp) | ||
| 94 | { | ||
| 95 | struct dir_entry e; | ||
| 96 | size_t ofs; | ||
| 97 | |||
| 98 | ASSERT (dir != NULL); | ||
| 99 | ASSERT (name != NULL); | ||
| 100 | |||
| 101 | for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e; | ||
| 102 | ofs += sizeof e) | ||
| 103 | if (e.in_use && !strcmp (name, e.name)) | ||
| 104 | { | ||
| 105 | if (ep != NULL) | ||
| 106 | *ep = e; | ||
| 107 | if (ofsp != NULL) | ||
| 108 | *ofsp = ofs; | ||
| 109 | return true; | ||
| 110 | } | ||
| 111 | return false; | ||
| 112 | } | ||
| 113 | |||
| 114 | /* Searches DIR for a file with the given NAME | ||
| 115 | and returns true if one exists, false otherwise. | ||
| 116 | On success, sets *INODE to an inode for the file, otherwise to | ||
| 117 | a null pointer. The caller must close *INODE. */ | ||
| 118 | bool | ||
| 119 | dir_lookup (const struct dir *dir, const char *name, | ||
| 120 | struct inode **inode) | ||
| 121 | { | ||
| 122 | struct dir_entry e; | ||
| 123 | |||
| 124 | ASSERT (dir != NULL); | ||
| 125 | ASSERT (name != NULL); | ||
| 126 | |||
| 127 | if (lookup (dir, name, &e, NULL)) | ||
| 128 | *inode = inode_open (e.inode_sector); | ||
| 129 | else | ||
| 130 | *inode = NULL; | ||
| 131 | |||
| 132 | return *inode != NULL; | ||
| 133 | } | ||
| 134 | |||
| 135 | /* Adds a file named NAME to DIR, which must not already contain a | ||
| 136 | file by that name. The file's inode is in sector | ||
| 137 | INODE_SECTOR. | ||
| 138 | Returns true if successful, false on failure. | ||
| 139 | Fails if NAME is invalid (i.e. too long) or a disk or memory | ||
| 140 | error occurs. */ | ||
| 141 | bool | ||
| 142 | dir_add (struct dir *dir, const char *name, block_sector_t inode_sector) | ||
| 143 | { | ||
| 144 | struct dir_entry e; | ||
| 145 | off_t ofs; | ||
| 146 | bool success = false; | ||
| 147 | |||
| 148 | ASSERT (dir != NULL); | ||
| 149 | ASSERT (name != NULL); | ||
| 150 | |||
| 151 | /* Check NAME for validity. */ | ||
| 152 | if (*name == '\0' || strlen (name) > NAME_MAX) | ||
| 153 | return false; | ||
| 154 | |||
| 155 | /* Check that NAME is not in use. */ | ||
| 156 | if (lookup (dir, name, NULL, NULL)) | ||
| 157 | goto done; | ||
| 158 | |||
| 159 | /* Set OFS to offset of free slot. | ||
| 160 | If there are no free slots, then it will be set to the | ||
| 161 | current end-of-file. | ||
| 162 | |||
| 163 | inode_read_at() will only return a short read at end of file. | ||
| 164 | Otherwise, we'd need to verify that we didn't get a short | ||
| 165 | read due to something intermittent such as low memory. */ | ||
| 166 | for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e; | ||
| 167 | ofs += sizeof e) | ||
| 168 | if (!e.in_use) | ||
| 169 | break; | ||
| 170 | |||
| 171 | /* Write slot. */ | ||
| 172 | e.in_use = true; | ||
| 173 | strlcpy (e.name, name, sizeof e.name); | ||
| 174 | e.inode_sector = inode_sector; | ||
| 175 | success = inode_write_at (dir->inode, &e, sizeof e, ofs) == sizeof e; | ||
| 176 | |||
| 177 | done: | ||
| 178 | return success; | ||
| 179 | } | ||
| 180 | |||
| 181 | /* Removes any entry for NAME in DIR. | ||
| 182 | Returns true if successful, false on failure, | ||
| 183 | which occurs only if there is no file with the given NAME. */ | ||
| 184 | bool | ||
| 185 | dir_remove (struct dir *dir, const char *name) | ||
| 186 | { | ||
| 187 | struct dir_entry e; | ||
| 188 | struct inode *inode = NULL; | ||
| 189 | bool success = false; | ||
| 190 | off_t ofs; | ||
| 191 | |||
| 192 | ASSERT (dir != NULL); | ||
| 193 | ASSERT (name != NULL); | ||
| 194 | |||
| 195 | /* Find directory entry. */ | ||
| 196 | if (!lookup (dir, name, &e, &ofs)) | ||
| 197 | goto done; | ||
| 198 | |||
| 199 | /* Open inode. */ | ||
| 200 | inode = inode_open (e.inode_sector); | ||
| 201 | if (inode == NULL) | ||
| 202 | goto done; | ||
| 203 | |||
| 204 | /* Erase directory entry. */ | ||
| 205 | e.in_use = false; | ||
| 206 | if (inode_write_at (dir->inode, &e, sizeof e, ofs) != sizeof e) | ||
| 207 | goto done; | ||
| 208 | |||
| 209 | /* Remove inode. */ | ||
| 210 | inode_remove (inode); | ||
| 211 | success = true; | ||
| 212 | |||
| 213 | done: | ||
| 214 | inode_close (inode); | ||
| 215 | return success; | ||
| 216 | } | ||
| 217 | |||
| 218 | /* Reads the next directory entry in DIR and stores the name in | ||
| 219 | NAME. Returns true if successful, false if the directory | ||
| 220 | contains no more entries. */ | ||
| 221 | bool | ||
| 222 | dir_readdir (struct dir *dir, char name[NAME_MAX + 1]) | ||
| 223 | { | ||
| 224 | struct dir_entry e; | ||
| 225 | |||
| 226 | while (inode_read_at (dir->inode, &e, sizeof e, dir->pos) == sizeof e) | ||
| 227 | { | ||
| 228 | dir->pos += sizeof e; | ||
| 229 | if (e.in_use) | ||
| 230 | { | ||
| 231 | strlcpy (name, e.name, NAME_MAX + 1); | ||
| 232 | return true; | ||
| 233 | } | ||
| 234 | } | ||
| 235 | return false; | ||
| 236 | } | ||
diff --git a/filesys/directory.h b/filesys/directory.h new file mode 100644 index 0000000..930acf9 --- /dev/null +++ b/filesys/directory.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #ifndef FILESYS_DIRECTORY_H | ||
| 2 | #define FILESYS_DIRECTORY_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include <stddef.h> | ||
| 6 | #include "devices/block.h" | ||
| 7 | |||
| 8 | /* Maximum length of a file name component. | ||
| 9 | This is the traditional UNIX maximum length. | ||
| 10 | After directories are implemented, this maximum length may be | ||
| 11 | retained, but much longer full path names must be allowed. */ | ||
| 12 | #define NAME_MAX 14 | ||
| 13 | |||
| 14 | struct inode; | ||
| 15 | |||
| 16 | /* Opening and closing directories. */ | ||
| 17 | bool dir_create (block_sector_t sector, size_t entry_cnt); | ||
| 18 | struct dir *dir_open (struct inode *); | ||
| 19 | struct dir *dir_open_root (void); | ||
| 20 | struct dir *dir_reopen (struct dir *); | ||
| 21 | void dir_close (struct dir *); | ||
| 22 | struct inode *dir_get_inode (struct dir *); | ||
| 23 | |||
| 24 | /* Reading and writing. */ | ||
| 25 | bool dir_lookup (const struct dir *, const char *name, struct inode **); | ||
| 26 | bool dir_add (struct dir *, const char *name, block_sector_t); | ||
| 27 | bool dir_remove (struct dir *, const char *name); | ||
| 28 | bool dir_readdir (struct dir *, char name[NAME_MAX + 1]); | ||
| 29 | |||
| 30 | #endif /* filesys/directory.h */ | ||
diff --git a/filesys/file.c b/filesys/file.c new file mode 100644 index 0000000..d5fc10d --- /dev/null +++ b/filesys/file.c | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | #include "filesys/file.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include "filesys/inode.h" | ||
| 4 | #include "threads/malloc.h" | ||
| 5 | |||
| 6 | /* An open file. */ | ||
| 7 | struct file | ||
| 8 | { | ||
| 9 | struct inode *inode; /* File's inode. */ | ||
| 10 | off_t pos; /* Current position. */ | ||
| 11 | bool deny_write; /* Has file_deny_write() been called? */ | ||
| 12 | }; | ||
| 13 | |||
| 14 | /* Opens a file for the given INODE, of which it takes ownership, | ||
| 15 | and returns the new file. Returns a null pointer if an | ||
| 16 | allocation fails or if INODE is null. */ | ||
| 17 | struct file * | ||
| 18 | file_open (struct inode *inode) | ||
| 19 | { | ||
| 20 | struct file *file = calloc (1, sizeof *file); | ||
| 21 | if (inode != NULL && file != NULL) | ||
| 22 | { | ||
| 23 | file->inode = inode; | ||
| 24 | file->pos = 0; | ||
| 25 | file->deny_write = false; | ||
| 26 | return file; | ||
| 27 | } | ||
| 28 | else | ||
| 29 | { | ||
| 30 | inode_close (inode); | ||
| 31 | free (file); | ||
| 32 | return NULL; | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | /* Opens and returns a new file for the same inode as FILE. | ||
| 37 | Returns a null pointer if unsuccessful. */ | ||
| 38 | struct file * | ||
| 39 | file_reopen (struct file *file) | ||
| 40 | { | ||
| 41 | return file_open (inode_reopen (file->inode)); | ||
| 42 | } | ||
| 43 | |||
| 44 | /* Closes FILE. */ | ||
| 45 | void | ||
| 46 | file_close (struct file *file) | ||
| 47 | { | ||
| 48 | if (file != NULL) | ||
| 49 | { | ||
| 50 | file_allow_write (file); | ||
| 51 | inode_close (file->inode); | ||
| 52 | free (file); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | /* Returns the inode encapsulated by FILE. */ | ||
| 57 | struct inode * | ||
| 58 | file_get_inode (struct file *file) | ||
| 59 | { | ||
| 60 | return file->inode; | ||
| 61 | } | ||
| 62 | |||
| 63 | /* Reads SIZE bytes from FILE into BUFFER, | ||
| 64 | starting at the file's current position. | ||
| 65 | Returns the number of bytes actually read, | ||
| 66 | which may be less than SIZE if end of file is reached. | ||
| 67 | Advances FILE's position by the number of bytes read. */ | ||
| 68 | off_t | ||
| 69 | file_read (struct file *file, void *buffer, off_t size) | ||
| 70 | { | ||
| 71 | off_t bytes_read = inode_read_at (file->inode, buffer, size, file->pos); | ||
| 72 | file->pos += bytes_read; | ||
| 73 | return bytes_read; | ||
| 74 | } | ||
| 75 | |||
| 76 | /* Reads SIZE bytes from FILE into BUFFER, | ||
| 77 | starting at offset FILE_OFS in the file. | ||
| 78 | Returns the number of bytes actually read, | ||
| 79 | which may be less than SIZE if end of file is reached. | ||
| 80 | The file's current position is unaffected. */ | ||
| 81 | off_t | ||
| 82 | file_read_at (struct file *file, void *buffer, off_t size, off_t file_ofs) | ||
| 83 | { | ||
| 84 | return inode_read_at (file->inode, buffer, size, file_ofs); | ||
| 85 | } | ||
| 86 | |||
| 87 | /* Writes SIZE bytes from BUFFER into FILE, | ||
| 88 | starting at the file's current position. | ||
| 89 | Returns the number of bytes actually written, | ||
| 90 | which may be less than SIZE if end of file is reached. | ||
| 91 | (Normally we'd grow the file in that case, but file growth is | ||
| 92 | not yet implemented.) | ||
| 93 | Advances FILE's position by the number of bytes read. */ | ||
| 94 | off_t | ||
| 95 | file_write (struct file *file, const void *buffer, off_t size) | ||
| 96 | { | ||
| 97 | off_t bytes_written = inode_write_at (file->inode, buffer, size, file->pos); | ||
| 98 | file->pos += bytes_written; | ||
| 99 | return bytes_written; | ||
| 100 | } | ||
| 101 | |||
| 102 | /* Writes SIZE bytes from BUFFER into FILE, | ||
| 103 | starting at offset FILE_OFS in the file. | ||
| 104 | Returns the number of bytes actually written, | ||
| 105 | which may be less than SIZE if end of file is reached. | ||
| 106 | (Normally we'd grow the file in that case, but file growth is | ||
| 107 | not yet implemented.) | ||
| 108 | The file's current position is unaffected. */ | ||
| 109 | off_t | ||
| 110 | file_write_at (struct file *file, const void *buffer, off_t size, | ||
| 111 | off_t file_ofs) | ||
| 112 | { | ||
| 113 | return inode_write_at (file->inode, buffer, size, file_ofs); | ||
| 114 | } | ||
| 115 | |||
| 116 | /* Prevents write operations on FILE's underlying inode | ||
| 117 | until file_allow_write() is called or FILE is closed. */ | ||
| 118 | void | ||
| 119 | file_deny_write (struct file *file) | ||
| 120 | { | ||
| 121 | ASSERT (file != NULL); | ||
| 122 | if (!file->deny_write) | ||
| 123 | { | ||
| 124 | file->deny_write = true; | ||
| 125 | inode_deny_write (file->inode); | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | /* Re-enables write operations on FILE's underlying inode. | ||
| 130 | (Writes might still be denied by some other file that has the | ||
| 131 | same inode open.) */ | ||
| 132 | void | ||
| 133 | file_allow_write (struct file *file) | ||
| 134 | { | ||
| 135 | ASSERT (file != NULL); | ||
| 136 | if (file->deny_write) | ||
| 137 | { | ||
| 138 | file->deny_write = false; | ||
| 139 | inode_allow_write (file->inode); | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | /* Returns the size of FILE in bytes. */ | ||
| 144 | off_t | ||
| 145 | file_length (struct file *file) | ||
| 146 | { | ||
| 147 | ASSERT (file != NULL); | ||
| 148 | return inode_length (file->inode); | ||
| 149 | } | ||
| 150 | |||
| 151 | /* Sets the current position in FILE to NEW_POS bytes from the | ||
| 152 | start of the file. */ | ||
| 153 | void | ||
| 154 | file_seek (struct file *file, off_t new_pos) | ||
| 155 | { | ||
| 156 | ASSERT (file != NULL); | ||
| 157 | ASSERT (new_pos >= 0); | ||
| 158 | file->pos = new_pos; | ||
| 159 | } | ||
| 160 | |||
| 161 | /* Returns the current position in FILE as a byte offset from the | ||
| 162 | start of the file. */ | ||
| 163 | off_t | ||
| 164 | file_tell (struct file *file) | ||
| 165 | { | ||
| 166 | ASSERT (file != NULL); | ||
| 167 | return file->pos; | ||
| 168 | } | ||
diff --git a/filesys/file.h b/filesys/file.h new file mode 100644 index 0000000..a33c5af --- /dev/null +++ b/filesys/file.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | #ifndef FILESYS_FILE_H | ||
| 2 | #define FILESYS_FILE_H | ||
| 3 | |||
| 4 | #include "filesys/off_t.h" | ||
| 5 | |||
| 6 | struct inode; | ||
| 7 | |||
| 8 | /* Opening and closing files. */ | ||
| 9 | struct file *file_open (struct inode *); | ||
| 10 | struct file *file_reopen (struct file *); | ||
| 11 | void file_close (struct file *); | ||
| 12 | struct inode *file_get_inode (struct file *); | ||
| 13 | |||
| 14 | /* Reading and writing. */ | ||
| 15 | off_t file_read (struct file *, void *, off_t); | ||
| 16 | off_t file_read_at (struct file *, void *, off_t size, off_t start); | ||
| 17 | off_t file_write (struct file *, const void *, off_t); | ||
| 18 | off_t file_write_at (struct file *, const void *, off_t size, off_t start); | ||
| 19 | |||
| 20 | /* Preventing writes. */ | ||
| 21 | void file_deny_write (struct file *); | ||
| 22 | void file_allow_write (struct file *); | ||
| 23 | |||
| 24 | /* File position. */ | ||
| 25 | void file_seek (struct file *, off_t); | ||
| 26 | off_t file_tell (struct file *); | ||
| 27 | off_t file_length (struct file *); | ||
| 28 | |||
| 29 | #endif /* filesys/file.h */ | ||
diff --git a/filesys/filesys.c b/filesys/filesys.c new file mode 100644 index 0000000..7a53f5f --- /dev/null +++ b/filesys/filesys.c | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | #include "filesys/filesys.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | #include <string.h> | ||
| 5 | #include "filesys/file.h" | ||
| 6 | #include "filesys/free-map.h" | ||
| 7 | #include "filesys/inode.h" | ||
| 8 | #include "filesys/directory.h" | ||
| 9 | |||
| 10 | /* Partition that contains the file system. */ | ||
| 11 | struct block *fs_device; | ||
| 12 | |||
| 13 | static void do_format (void); | ||
| 14 | |||
| 15 | /* Initializes the file system module. | ||
| 16 | If FORMAT is true, reformats the file system. */ | ||
| 17 | void | ||
| 18 | filesys_init (bool format) | ||
| 19 | { | ||
| 20 | fs_device = block_get_role (BLOCK_FILESYS); | ||
| 21 | if (fs_device == NULL) | ||
| 22 | PANIC ("No file system device found, can't initialize file system."); | ||
| 23 | |||
| 24 | inode_init (); | ||
| 25 | free_map_init (); | ||
| 26 | |||
| 27 | if (format) | ||
| 28 | do_format (); | ||
| 29 | |||
| 30 | free_map_open (); | ||
| 31 | } | ||
| 32 | |||
| 33 | /* Shuts down the file system module, writing any unwritten data | ||
| 34 | to disk. */ | ||
| 35 | void | ||
| 36 | filesys_done (void) | ||
| 37 | { | ||
| 38 | free_map_close (); | ||
| 39 | } | ||
| 40 | |||
| 41 | /* Creates a file named NAME with the given INITIAL_SIZE. | ||
| 42 | Returns true if successful, false otherwise. | ||
| 43 | Fails if a file named NAME already exists, | ||
| 44 | or if internal memory allocation fails. */ | ||
| 45 | bool | ||
| 46 | filesys_create (const char *name, off_t initial_size) | ||
| 47 | { | ||
| 48 | block_sector_t inode_sector = 0; | ||
| 49 | struct dir *dir = dir_open_root (); | ||
| 50 | bool success = (dir != NULL | ||
| 51 | && free_map_allocate (1, &inode_sector) | ||
| 52 | && inode_create (inode_sector, initial_size) | ||
| 53 | && dir_add (dir, name, inode_sector)); | ||
| 54 | if (!success && inode_sector != 0) | ||
| 55 | free_map_release (inode_sector, 1); | ||
| 56 | dir_close (dir); | ||
| 57 | |||
| 58 | return success; | ||
| 59 | } | ||
| 60 | |||
| 61 | /* Opens the file with the given NAME. | ||
| 62 | Returns the new file if successful or a null pointer | ||
| 63 | otherwise. | ||
| 64 | Fails if no file named NAME exists, | ||
| 65 | or if an internal memory allocation fails. */ | ||
| 66 | struct file * | ||
| 67 | filesys_open (const char *name) | ||
| 68 | { | ||
| 69 | struct dir *dir = dir_open_root (); | ||
| 70 | struct inode *inode = NULL; | ||
| 71 | |||
| 72 | if (dir != NULL) | ||
| 73 | dir_lookup (dir, name, &inode); | ||
| 74 | dir_close (dir); | ||
| 75 | |||
| 76 | return file_open (inode); | ||
| 77 | } | ||
| 78 | |||
| 79 | /* Deletes the file named NAME. | ||
| 80 | Returns true if successful, false on failure. | ||
| 81 | Fails if no file named NAME exists, | ||
| 82 | or if an internal memory allocation fails. */ | ||
| 83 | bool | ||
| 84 | filesys_remove (const char *name) | ||
| 85 | { | ||
| 86 | struct dir *dir = dir_open_root (); | ||
| 87 | bool success = dir != NULL && dir_remove (dir, name); | ||
| 88 | dir_close (dir); | ||
| 89 | |||
| 90 | return success; | ||
| 91 | } | ||
| 92 | |||
| 93 | /* Formats the file system. */ | ||
| 94 | static void | ||
| 95 | do_format (void) | ||
| 96 | { | ||
| 97 | printf ("Formatting file system..."); | ||
| 98 | free_map_create (); | ||
| 99 | if (!dir_create (ROOT_DIR_SECTOR, 16)) | ||
| 100 | PANIC ("root directory creation failed"); | ||
| 101 | free_map_close (); | ||
| 102 | printf ("done.\n"); | ||
| 103 | } | ||
diff --git a/filesys/filesys.h b/filesys/filesys.h new file mode 100644 index 0000000..c1cda84 --- /dev/null +++ b/filesys/filesys.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | #ifndef FILESYS_FILESYS_H | ||
| 2 | #define FILESYS_FILESYS_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include "filesys/off_t.h" | ||
| 6 | |||
| 7 | /* Sectors of system file inodes. */ | ||
| 8 | #define FREE_MAP_SECTOR 0 /* Free map file inode sector. */ | ||
| 9 | #define ROOT_DIR_SECTOR 1 /* Root directory file inode sector. */ | ||
| 10 | |||
| 11 | /* Block device that contains the file system. */ | ||
| 12 | struct block *fs_device; | ||
| 13 | |||
| 14 | void filesys_init (bool format); | ||
| 15 | void filesys_done (void); | ||
| 16 | bool filesys_create (const char *name, off_t initial_size); | ||
| 17 | struct file *filesys_open (const char *name); | ||
| 18 | bool filesys_remove (const char *name); | ||
| 19 | |||
| 20 | #endif /* filesys/filesys.h */ | ||
diff --git a/filesys/free-map.c b/filesys/free-map.c new file mode 100644 index 0000000..29ea4df --- /dev/null +++ b/filesys/free-map.c | |||
| @@ -0,0 +1,85 @@ | |||
| 1 | #include "filesys/free-map.h" | ||
| 2 | #include <bitmap.h> | ||
| 3 | #include <debug.h> | ||
| 4 | #include "filesys/file.h" | ||
| 5 | #include "filesys/filesys.h" | ||
| 6 | #include "filesys/inode.h" | ||
| 7 | |||
| 8 | static struct file *free_map_file; /* Free map file. */ | ||
| 9 | static struct bitmap *free_map; /* Free map, one bit per sector. */ | ||
| 10 | |||
| 11 | /* Initializes the free map. */ | ||
| 12 | void | ||
| 13 | free_map_init (void) | ||
| 14 | { | ||
| 15 | free_map = bitmap_create (block_size (fs_device)); | ||
| 16 | if (free_map == NULL) | ||
| 17 | PANIC ("bitmap creation failed--file system device is too large"); | ||
| 18 | bitmap_mark (free_map, FREE_MAP_SECTOR); | ||
| 19 | bitmap_mark (free_map, ROOT_DIR_SECTOR); | ||
| 20 | } | ||
| 21 | |||
| 22 | /* Allocates CNT consecutive sectors from the free map and stores | ||
| 23 | the first into *SECTORP. | ||
| 24 | Returns true if successful, false if not enough consecutive | ||
| 25 | sectors were available or if the free_map file could not be | ||
| 26 | written. */ | ||
| 27 | bool | ||
| 28 | free_map_allocate (size_t cnt, block_sector_t *sectorp) | ||
| 29 | { | ||
| 30 | block_sector_t sector = bitmap_scan_and_flip (free_map, 0, cnt, false); | ||
| 31 | if (sector != BITMAP_ERROR | ||
| 32 | && free_map_file != NULL | ||
| 33 | && !bitmap_write (free_map, free_map_file)) | ||
| 34 | { | ||
| 35 | bitmap_set_multiple (free_map, sector, cnt, false); | ||
| 36 | sector = BITMAP_ERROR; | ||
| 37 | } | ||
| 38 | if (sector != BITMAP_ERROR) | ||
| 39 | *sectorp = sector; | ||
| 40 | return sector != BITMAP_ERROR; | ||
| 41 | } | ||
| 42 | |||
| 43 | /* Makes CNT sectors starting at SECTOR available for use. */ | ||
| 44 | void | ||
| 45 | free_map_release (block_sector_t sector, size_t cnt) | ||
| 46 | { | ||
| 47 | ASSERT (bitmap_all (free_map, sector, cnt)); | ||
| 48 | bitmap_set_multiple (free_map, sector, cnt, false); | ||
| 49 | bitmap_write (free_map, free_map_file); | ||
| 50 | } | ||
| 51 | |||
| 52 | /* Opens the free map file and reads it from disk. */ | ||
| 53 | void | ||
| 54 | free_map_open (void) | ||
| 55 | { | ||
| 56 | free_map_file = file_open (inode_open (FREE_MAP_SECTOR)); | ||
| 57 | if (free_map_file == NULL) | ||
| 58 | PANIC ("can't open free map"); | ||
| 59 | if (!bitmap_read (free_map, free_map_file)) | ||
| 60 | PANIC ("can't read free map"); | ||
| 61 | } | ||
| 62 | |||
| 63 | /* Writes the free map to disk and closes the free map file. */ | ||
| 64 | void | ||
| 65 | free_map_close (void) | ||
| 66 | { | ||
| 67 | file_close (free_map_file); | ||
| 68 | } | ||
| 69 | |||
| 70 | /* Creates a new free map file on disk and writes the free map to | ||
| 71 | it. */ | ||
| 72 | void | ||
| 73 | free_map_create (void) | ||
| 74 | { | ||
| 75 | /* Create inode. */ | ||
| 76 | if (!inode_create (FREE_MAP_SECTOR, bitmap_file_size (free_map))) | ||
| 77 | PANIC ("free map creation failed"); | ||
| 78 | |||
| 79 | /* Write bitmap to file. */ | ||
| 80 | free_map_file = file_open (inode_open (FREE_MAP_SECTOR)); | ||
| 81 | if (free_map_file == NULL) | ||
| 82 | PANIC ("can't open free map"); | ||
| 83 | if (!bitmap_write (free_map, free_map_file)) | ||
| 84 | PANIC ("can't write free map"); | ||
| 85 | } | ||
diff --git a/filesys/free-map.h b/filesys/free-map.h new file mode 100644 index 0000000..316cd1c --- /dev/null +++ b/filesys/free-map.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | #ifndef FILESYS_FREE_MAP_H | ||
| 2 | #define FILESYS_FREE_MAP_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include <stddef.h> | ||
| 6 | #include "devices/block.h" | ||
| 7 | |||
| 8 | void free_map_init (void); | ||
| 9 | void free_map_read (void); | ||
| 10 | void free_map_create (void); | ||
| 11 | void free_map_open (void); | ||
| 12 | void free_map_close (void); | ||
| 13 | |||
| 14 | bool free_map_allocate (size_t, block_sector_t *); | ||
| 15 | void free_map_release (block_sector_t, size_t); | ||
| 16 | |||
| 17 | #endif /* filesys/free-map.h */ | ||
diff --git a/filesys/fsutil.c b/filesys/fsutil.c new file mode 100644 index 0000000..447f291 --- /dev/null +++ b/filesys/fsutil.c | |||
| @@ -0,0 +1,222 @@ | |||
| 1 | #include "filesys/fsutil.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | #include <stdlib.h> | ||
| 5 | #include <string.h> | ||
| 6 | #include <ustar.h> | ||
| 7 | #include "filesys/directory.h" | ||
| 8 | #include "filesys/file.h" | ||
| 9 | #include "filesys/filesys.h" | ||
| 10 | #include "threads/malloc.h" | ||
| 11 | #include "threads/palloc.h" | ||
| 12 | #include "threads/vaddr.h" | ||
| 13 | |||
| 14 | /* List files in the root directory. */ | ||
| 15 | void | ||
| 16 | fsutil_ls (char **argv UNUSED) | ||
| 17 | { | ||
| 18 | struct dir *dir; | ||
| 19 | char name[NAME_MAX + 1]; | ||
| 20 | |||
| 21 | printf ("Files in the root directory:\n"); | ||
| 22 | dir = dir_open_root (); | ||
| 23 | if (dir == NULL) | ||
| 24 | PANIC ("root dir open failed"); | ||
| 25 | while (dir_readdir (dir, name)) | ||
| 26 | printf ("%s\n", name); | ||
| 27 | printf ("End of listing.\n"); | ||
| 28 | } | ||
| 29 | |||
| 30 | /* Prints the contents of file ARGV[1] to the system console as | ||
| 31 | hex and ASCII. */ | ||
| 32 | void | ||
| 33 | fsutil_cat (char **argv) | ||
| 34 | { | ||
| 35 | const char *file_name = argv[1]; | ||
| 36 | |||
| 37 | struct file *file; | ||
| 38 | char *buffer; | ||
| 39 | |||
| 40 | printf ("Printing '%s' to the console...\n", file_name); | ||
| 41 | file = filesys_open (file_name); | ||
| 42 | if (file == NULL) | ||
| 43 | PANIC ("%s: open failed", file_name); | ||
| 44 | buffer = palloc_get_page (PAL_ASSERT); | ||
| 45 | for (;;) | ||
| 46 | { | ||
| 47 | off_t pos = file_tell (file); | ||
| 48 | off_t n = file_read (file, buffer, PGSIZE); | ||
| 49 | if (n == 0) | ||
| 50 | break; | ||
| 51 | |||
| 52 | hex_dump (pos, buffer, n, true); | ||
| 53 | } | ||
| 54 | palloc_free_page (buffer); | ||
| 55 | file_close (file); | ||
| 56 | } | ||
| 57 | |||
| 58 | /* Deletes file ARGV[1]. */ | ||
| 59 | void | ||
| 60 | fsutil_rm (char **argv) | ||
| 61 | { | ||
| 62 | const char *file_name = argv[1]; | ||
| 63 | |||
| 64 | printf ("Deleting '%s'...\n", file_name); | ||
| 65 | if (!filesys_remove (file_name)) | ||
| 66 | PANIC ("%s: delete failed\n", file_name); | ||
| 67 | } | ||
| 68 | |||
| 69 | /* Extracts a ustar-format tar archive from the scratch block | ||
| 70 | device into the Pintos file system. */ | ||
| 71 | void | ||
| 72 | fsutil_extract (char **argv UNUSED) | ||
| 73 | { | ||
| 74 | static block_sector_t sector = 0; | ||
| 75 | |||
| 76 | struct block *src; | ||
| 77 | void *header, *data; | ||
| 78 | |||
| 79 | /* Allocate buffers. */ | ||
| 80 | header = malloc (BLOCK_SECTOR_SIZE); | ||
| 81 | data = malloc (BLOCK_SECTOR_SIZE); | ||
| 82 | if (header == NULL || data == NULL) | ||
| 83 | PANIC ("couldn't allocate buffers"); | ||
| 84 | |||
| 85 | /* Open source block device. */ | ||
| 86 | src = block_get_role (BLOCK_SCRATCH); | ||
| 87 | if (src == NULL) | ||
| 88 | PANIC ("couldn't open scratch device"); | ||
| 89 | |||
| 90 | printf ("Extracting ustar archive from scratch device " | ||
| 91 | "into file system...\n"); | ||
| 92 | |||
| 93 | for (;;) | ||
| 94 | { | ||
| 95 | const char *file_name; | ||
| 96 | const char *error; | ||
| 97 | enum ustar_type type; | ||
| 98 | int size; | ||
| 99 | |||
| 100 | /* Read and parse ustar header. */ | ||
| 101 | block_read (src, sector++, header); | ||
| 102 | error = ustar_parse_header (header, &file_name, &type, &size); | ||
| 103 | if (error != NULL) | ||
| 104 | PANIC ("bad ustar header in sector %"PRDSNu" (%s)", sector - 1, error); | ||
| 105 | |||
| 106 | if (type == USTAR_EOF) | ||
| 107 | { | ||
| 108 | /* End of archive. */ | ||
| 109 | break; | ||
| 110 | } | ||
| 111 | else if (type == USTAR_DIRECTORY) | ||
| 112 | printf ("ignoring directory %s\n", file_name); | ||
| 113 | else if (type == USTAR_REGULAR) | ||
| 114 | { | ||
| 115 | struct file *dst; | ||
| 116 | |||
| 117 | printf ("Putting '%s' into the file system...\n", file_name); | ||
| 118 | |||
| 119 | /* Create destination file. */ | ||
| 120 | if (!filesys_create (file_name, size)) | ||
| 121 | PANIC ("%s: create failed", file_name); | ||
| 122 | dst = filesys_open (file_name); | ||
| 123 | if (dst == NULL) | ||
| 124 | PANIC ("%s: open failed", file_name); | ||
| 125 | |||
| 126 | /* Do copy. */ | ||
| 127 | while (size > 0) | ||
| 128 | { | ||
| 129 | int chunk_size = (size > BLOCK_SECTOR_SIZE | ||
| 130 | ? BLOCK_SECTOR_SIZE | ||
| 131 | : size); | ||
| 132 | block_read (src, sector++, data); | ||
| 133 | if (file_write (dst, data, chunk_size) != chunk_size) | ||
| 134 | PANIC ("%s: write failed with %d bytes unwritten", | ||
| 135 | file_name, size); | ||
| 136 | size -= chunk_size; | ||
| 137 | } | ||
| 138 | |||
| 139 | /* Finish up. */ | ||
| 140 | file_close (dst); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | /* Erase the ustar header from the start of the block device, | ||
| 145 | so that the extraction operation is idempotent. We erase | ||
| 146 | two blocks because two blocks of zeros are the ustar | ||
| 147 | end-of-archive marker. */ | ||
| 148 | printf ("Erasing ustar archive...\n"); | ||
| 149 | memset (header, 0, BLOCK_SECTOR_SIZE); | ||
| 150 | block_write (src, 0, header); | ||
| 151 | block_write (src, 1, header); | ||
| 152 | |||
| 153 | free (data); | ||
| 154 | free (header); | ||
| 155 | } | ||
| 156 | |||
| 157 | /* Copies file FILE_NAME from the file system to the scratch | ||
| 158 | device, in ustar format. | ||
| 159 | |||
| 160 | The first call to this function will write starting at the | ||
| 161 | beginning of the scratch device. Later calls advance across | ||
| 162 | the device. This position is independent of that used for | ||
| 163 | fsutil_extract(), so `extract' should precede all | ||
| 164 | `append's. */ | ||
| 165 | void | ||
| 166 | fsutil_append (char **argv) | ||
| 167 | { | ||
| 168 | static block_sector_t sector = 0; | ||
| 169 | |||
| 170 | const char *file_name = argv[1]; | ||
| 171 | void *buffer; | ||
| 172 | struct file *src; | ||
| 173 | struct block *dst; | ||
| 174 | off_t size; | ||
| 175 | |||
| 176 | printf ("Appending '%s' to ustar archive on scratch device...\n", file_name); | ||
| 177 | |||
| 178 | /* Allocate buffer. */ | ||
| 179 | buffer = malloc (BLOCK_SECTOR_SIZE); | ||
| 180 | if (buffer == NULL) | ||
| 181 | PANIC ("couldn't allocate buffer"); | ||
| 182 | |||
| 183 | /* Open source file. */ | ||
| 184 | src = filesys_open (file_name); | ||
| 185 | if (src == NULL) | ||
| 186 | PANIC ("%s: open failed", file_name); | ||
| 187 | size = file_length (src); | ||
| 188 | |||
| 189 | /* Open target block device. */ | ||
| 190 | dst = block_get_role (BLOCK_SCRATCH); | ||
| 191 | if (dst == NULL) | ||
| 192 | PANIC ("couldn't open scratch device"); | ||
| 193 | |||
| 194 | /* Write ustar header to first sector. */ | ||
| 195 | if (!ustar_make_header (file_name, USTAR_REGULAR, size, buffer)) | ||
| 196 | PANIC ("%s: name too long for ustar format", file_name); | ||
| 197 | block_write (dst, sector++, buffer); | ||
| 198 | |||
| 199 | /* Do copy. */ | ||
| 200 | while (size > 0) | ||
| 201 | { | ||
| 202 | int chunk_size = size > BLOCK_SECTOR_SIZE ? BLOCK_SECTOR_SIZE : size; | ||
| 203 | if (sector >= block_size (dst)) | ||
| 204 | PANIC ("%s: out of space on scratch device", file_name); | ||
| 205 | if (file_read (src, buffer, chunk_size) != chunk_size) | ||
| 206 | PANIC ("%s: read failed with %"PROTd" bytes unread", file_name, size); | ||
| 207 | memset (buffer + chunk_size, 0, BLOCK_SECTOR_SIZE - chunk_size); | ||
| 208 | block_write (dst, sector++, buffer); | ||
| 209 | size -= chunk_size; | ||
| 210 | } | ||
| 211 | |||
| 212 | /* Write ustar end-of-archive marker, which is two consecutive | ||
| 213 | sectors full of zeros. Don't advance our position past | ||
| 214 | them, though, in case we have more files to append. */ | ||
| 215 | memset (buffer, 0, BLOCK_SECTOR_SIZE); | ||
| 216 | block_write (dst, sector, buffer); | ||
| 217 | block_write (dst, sector, buffer + 1); | ||
| 218 | |||
| 219 | /* Finish up. */ | ||
| 220 | file_close (src); | ||
| 221 | free (buffer); | ||
| 222 | } | ||
diff --git a/filesys/fsutil.h b/filesys/fsutil.h new file mode 100644 index 0000000..cc73705 --- /dev/null +++ b/filesys/fsutil.h | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | #ifndef FILESYS_FSUTIL_H | ||
| 2 | #define FILESYS_FSUTIL_H | ||
| 3 | |||
| 4 | void fsutil_ls (char **argv); | ||
| 5 | void fsutil_cat (char **argv); | ||
| 6 | void fsutil_rm (char **argv); | ||
| 7 | void fsutil_extract (char **argv); | ||
| 8 | void fsutil_append (char **argv); | ||
| 9 | |||
| 10 | #endif /* filesys/fsutil.h */ | ||
diff --git a/filesys/inode.c b/filesys/inode.c new file mode 100644 index 0000000..3463563 --- /dev/null +++ b/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 | } | ||
diff --git a/filesys/inode.h b/filesys/inode.h new file mode 100644 index 0000000..cb42310 --- /dev/null +++ b/filesys/inode.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | #ifndef FILESYS_INODE_H | ||
| 2 | #define FILESYS_INODE_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include "filesys/off_t.h" | ||
| 6 | #include "devices/block.h" | ||
| 7 | |||
| 8 | struct bitmap; | ||
| 9 | |||
| 10 | void inode_init (void); | ||
| 11 | bool inode_create (block_sector_t, off_t); | ||
| 12 | struct inode *inode_open (block_sector_t); | ||
| 13 | struct inode *inode_reopen (struct inode *); | ||
| 14 | block_sector_t inode_get_inumber (const struct inode *); | ||
| 15 | void inode_close (struct inode *); | ||
| 16 | void inode_remove (struct inode *); | ||
| 17 | off_t inode_read_at (struct inode *, void *, off_t size, off_t offset); | ||
| 18 | off_t inode_write_at (struct inode *, const void *, off_t size, off_t offset); | ||
| 19 | void inode_deny_write (struct inode *); | ||
| 20 | void inode_allow_write (struct inode *); | ||
| 21 | off_t inode_length (const struct inode *); | ||
| 22 | |||
| 23 | #endif /* filesys/inode.h */ | ||
diff --git a/filesys/off_t.h b/filesys/off_t.h new file mode 100644 index 0000000..9caff4d --- /dev/null +++ b/filesys/off_t.h | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #ifndef FILESYS_OFF_T_H | ||
| 2 | #define FILESYS_OFF_T_H | ||
| 3 | |||
| 4 | #include <stdint.h> | ||
| 5 | |||
| 6 | /* An offset within a file. | ||
| 7 | This is a separate header because multiple headers want this | ||
| 8 | definition but not any others. */ | ||
| 9 | typedef int32_t off_t; | ||
| 10 | |||
| 11 | /* Format specifier for printf(), e.g.: | ||
| 12 | printf ("offset=%"PROTd"\n", offset); */ | ||
| 13 | #define PROTd PRId32 | ||
| 14 | |||
| 15 | #endif /* filesys/off_t.h */ | ||
