summaryrefslogtreecommitdiffstats
path: root/filesys
diff options
context:
space:
mode:
authormanuel <manuel@mausz.at>2012-03-27 11:51:08 +0200
committermanuel <manuel@mausz.at>2012-03-27 11:51:08 +0200
commit4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b (patch)
tree868c52e06f207b5ec8a3cc141f4b8b2bdfcc165c /filesys
parenteae0bd57f0a26314a94785061888d193d186944a (diff)
downloadprogos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.tar.gz
progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.tar.bz2
progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.zip
reorganize file structure to match the upstream requirements
Diffstat (limited to 'filesys')
-rw-r--r--filesys/.gitignore3
-rw-r--r--filesys/Make.vars13
-rw-r--r--filesys/Makefile1
-rw-r--r--filesys/directory.c236
-rw-r--r--filesys/directory.h30
-rw-r--r--filesys/file.c168
-rw-r--r--filesys/file.h29
-rw-r--r--filesys/filesys.c103
-rw-r--r--filesys/filesys.h20
-rw-r--r--filesys/free-map.c85
-rw-r--r--filesys/free-map.h17
-rw-r--r--filesys/fsutil.c222
-rw-r--r--filesys/fsutil.h10
-rw-r--r--filesys/inode.c345
-rw-r--r--filesys/inode.h23
-rw-r--r--filesys/off_t.h15
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 @@
1build
2bochsrc.txt
3bochsout.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
3kernel.bin: DEFINES = -DUSERPROG -DFILESYS
4KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys
5TEST_SUBDIRS = tests/userprog tests/filesys/base tests/filesys/extended
6GRADING_FILE = $(SRCDIR)/tests/filesys/Grading.no-vm
7SIMULATOR = --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. */
10struct dir
11 {
12 struct inode *inode; /* Backing store. */
13 off_t pos; /* Current position. */
14 };
15
16/* A single directory entry. */
17struct 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. */
26bool
27dir_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. */
34struct dir *
35dir_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. */
54struct dir *
55dir_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. */
62struct dir *
63dir_reopen (struct dir *dir)
64{
65 return dir_open (inode_reopen (dir->inode));
66}
67
68/* Destroys DIR and frees associated resources. */
69void
70dir_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. */
80struct inode *
81dir_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. */
91static bool
92lookup (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. */
118bool
119dir_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. */
141bool
142dir_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. */
184bool
185dir_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. */
221bool
222dir_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
14struct inode;
15
16/* Opening and closing directories. */
17bool dir_create (block_sector_t sector, size_t entry_cnt);
18struct dir *dir_open (struct inode *);
19struct dir *dir_open_root (void);
20struct dir *dir_reopen (struct dir *);
21void dir_close (struct dir *);
22struct inode *dir_get_inode (struct dir *);
23
24/* Reading and writing. */
25bool dir_lookup (const struct dir *, const char *name, struct inode **);
26bool dir_add (struct dir *, const char *name, block_sector_t);
27bool dir_remove (struct dir *, const char *name);
28bool 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. */
7struct 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. */
17struct file *
18file_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. */
38struct file *
39file_reopen (struct file *file)
40{
41 return file_open (inode_reopen (file->inode));
42}
43
44/* Closes FILE. */
45void
46file_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. */
57struct inode *
58file_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. */
68off_t
69file_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. */
81off_t
82file_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. */
94off_t
95file_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. */
109off_t
110file_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. */
118void
119file_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.) */
132void
133file_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. */
144off_t
145file_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. */
153void
154file_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. */
163off_t
164file_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
6struct inode;
7
8/* Opening and closing files. */
9struct file *file_open (struct inode *);
10struct file *file_reopen (struct file *);
11void file_close (struct file *);
12struct inode *file_get_inode (struct file *);
13
14/* Reading and writing. */
15off_t file_read (struct file *, void *, off_t);
16off_t file_read_at (struct file *, void *, off_t size, off_t start);
17off_t file_write (struct file *, const void *, off_t);
18off_t file_write_at (struct file *, const void *, off_t size, off_t start);
19
20/* Preventing writes. */
21void file_deny_write (struct file *);
22void file_allow_write (struct file *);
23
24/* File position. */
25void file_seek (struct file *, off_t);
26off_t file_tell (struct file *);
27off_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. */
11struct block *fs_device;
12
13static void do_format (void);
14
15/* Initializes the file system module.
16 If FORMAT is true, reformats the file system. */
17void
18filesys_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. */
35void
36filesys_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. */
45bool
46filesys_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. */
66struct file *
67filesys_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. */
83bool
84filesys_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. */
94static void
95do_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. */
12struct block *fs_device;
13
14void filesys_init (bool format);
15void filesys_done (void);
16bool filesys_create (const char *name, off_t initial_size);
17struct file *filesys_open (const char *name);
18bool 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
8static struct file *free_map_file; /* Free map file. */
9static struct bitmap *free_map; /* Free map, one bit per sector. */
10
11/* Initializes the free map. */
12void
13free_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. */
27bool
28free_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. */
44void
45free_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. */
53void
54free_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. */
64void
65free_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. */
72void
73free_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
8void free_map_init (void);
9void free_map_read (void);
10void free_map_create (void);
11void free_map_open (void);
12void free_map_close (void);
13
14bool free_map_allocate (size_t, block_sector_t *);
15void 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. */
15void
16fsutil_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. */
32void
33fsutil_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]. */
59void
60fsutil_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. */
71void
72fsutil_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. */
165void
166fsutil_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
4void fsutil_ls (char **argv);
5void fsutil_cat (char **argv);
6void fsutil_rm (char **argv);
7void fsutil_extract (char **argv);
8void 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. */
15struct 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. */
25static inline size_t
26bytes_to_sectors (off_t size)
27{
28 return DIV_ROUND_UP (size, BLOCK_SECTOR_SIZE);
29}
30
31/* In-memory inode. */
32struct 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. */
46static block_sector_t
47byte_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'. */
58static struct list open_inodes;
59
60/* Initializes the inode module. */
61void
62inode_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. */
72bool
73inode_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. */
111struct inode *
112inode_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. */
145struct inode *
146inode_reopen (struct inode *inode)
147{
148 if (inode != NULL)
149 inode->open_cnt++;
150 return inode;
151}
152
153/* Returns INODE's inode number. */
154block_sector_t
155inode_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. */
163void
164inode_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. */
190void
191inode_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. */
200off_t
201inode_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.) */
257off_t
258inode_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. */
322void
323inode_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. */
332void
333inode_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. */
341off_t
342inode_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
8struct bitmap;
9
10void inode_init (void);
11bool inode_create (block_sector_t, off_t);
12struct inode *inode_open (block_sector_t);
13struct inode *inode_reopen (struct inode *);
14block_sector_t inode_get_inumber (const struct inode *);
15void inode_close (struct inode *);
16void inode_remove (struct inode *);
17off_t inode_read_at (struct inode *, void *, off_t size, off_t offset);
18off_t inode_write_at (struct inode *, const void *, off_t size, off_t offset);
19void inode_deny_write (struct inode *);
20void inode_allow_write (struct inode *);
21off_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. */
9typedef 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 */