* same with xv6
[mascara-docs.git] / i386 / MIT / course / src / src.lab / fs / fs.c
blob42fa3b156dfb7e0f33cc52c0475247872a63df77
1 #include <inc/string.h>
3 #include "fs.h"
5 // --------------------------------------------------------------
6 // Super block
7 // --------------------------------------------------------------
9 // Validate the file system super-block.
10 void
11 check_super(void)
13 if (super->s_magic != FS_MAGIC)
14 panic("bad file system magic number");
16 if (super->s_nblocks > DISKSIZE/BLKSIZE)
17 panic("file system is too large");
19 cprintf("superblock is good\n");
22 // --------------------------------------------------------------
23 // Free block bitmap
24 // --------------------------------------------------------------
26 // Check to see if the block bitmap indicates that block 'blockno' is free.
27 // Return 1 if the block is free, 0 if not.
28 bool
29 block_is_free(uint32_t blockno)
31 if (super == 0 || blockno >= super->s_nblocks)
32 return 0;
33 if (bitmap[blockno / 32] & (1 << (blockno % 32)))
34 return 1;
35 return 0;
38 // Mark a block free in the bitmap
39 void
40 free_block(uint32_t blockno)
42 // Blockno zero is the null pointer of block numbers.
43 if (blockno == 0)
44 panic("attempt to free zero block");
45 bitmap[blockno/32] |= 1<<(blockno%32);
48 // Search the bitmap for a free block and allocate it. When you
49 // allocate a block, immediately flush the changed bitmap block
50 // to disk.
52 // Return block number allocated on success,
53 // -E_NO_DISK if we are out of blocks.
55 // Hint: use free_block as an example for manipulating the bitmap.
56 int
57 alloc_block(void)
59 // The bitmap consists of one or more blocks. A single bitmap block
60 // contains the in-use bits for BLKBITSIZE blocks. There are
61 // super->s_nblocks blocks in the disk altogether.
63 // LAB 5: Your code here.
64 panic("alloc_block not implemented");
65 return -E_NO_DISK;
68 // Validate the file system bitmap.
70 // Check that all reserved blocks -- 0, 1, and the bitmap blocks themselves --
71 // are all marked as in-use.
72 void
73 check_bitmap(void)
75 uint32_t i;
77 // Make sure all bitmap blocks are marked in-use
78 for (i = 0; i * BLKBITSIZE < super->s_nblocks; i++)
79 assert(!block_is_free(2+i));
81 // Make sure the reserved and root blocks are marked in-use.
82 assert(!block_is_free(0));
83 assert(!block_is_free(1));
85 cprintf("bitmap is good\n");
88 // --------------------------------------------------------------
89 // File system structures
90 // --------------------------------------------------------------
92 // Initialize the file system
93 void
94 fs_init(void)
96 static_assert(sizeof(struct File) == 256);
98 // Find a JOS disk. Use the second IDE disk (number 1) if available.
99 if (ide_probe_disk1())
100 ide_set_disk(1);
101 else
102 ide_set_disk(0);
104 bc_init();
106 // Set "super" to point to the super block.
107 super = diskaddr(1);
108 // Set "bitmap" to the beginning of the first bitmap block.
109 bitmap = diskaddr(2);
111 check_super();
112 check_bitmap();
115 // Find the disk block number slot for the 'filebno'th block in file 'f'.
116 // Set '*ppdiskbno' to point to that slot.
117 // The slot will be one of the f->f_direct[] entries,
118 // or an entry in the indirect block.
119 // When 'alloc' is set, this function will allocate an indirect block
120 // if necessary.
122 // Returns:
123 // 0 on success (but note that *ppdiskbno might equal 0).
124 // -E_NOT_FOUND if the function needed to allocate an indirect block, but
125 // alloc was 0.
126 // -E_NO_DISK if there's no space on the disk for an indirect block.
127 // -E_INVAL if filebno is out of range (it's >= NDIRECT + NINDIRECT).
129 // Analogy: This is like pgdir_walk for files.
130 // Hint: Don't forget to clear any block you allocate.
131 static int
132 file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool alloc)
134 // LAB 5: Your code here.
135 panic("file_block_walk not implemented");
138 // Set *blk to the address in memory where the filebno'th
139 // block of file 'f' would be mapped.
140 // Allocate the block if it doesn't yet exist.
142 // Returns 0 on success, < 0 on error. Errors are:
143 // -E_NO_DISK if a block needed to be allocated but the disk is full.
144 // -E_INVAL if filebno is out of range.
146 // Hint: Use file_block_walk and alloc_block.
148 file_get_block(struct File *f, uint32_t filebno, char **blk)
150 // LAB 5: Your code here.
151 panic("file_get_block not implemented");
154 // Try to find a file named "name" in dir. If so, set *file to it.
156 // Returns 0 and sets *file on success, < 0 on error. Errors are:
157 // -E_NOT_FOUND if the file is not found
158 static int
159 dir_lookup(struct File *dir, const char *name, struct File **file)
161 int r;
162 uint32_t i, j, nblock;
163 char *blk;
164 struct File *f;
166 // Search dir for name.
167 // We maintain the invariant that the size of a directory-file
168 // is always a multiple of the file system's block size.
169 assert((dir->f_size % BLKSIZE) == 0);
170 nblock = dir->f_size / BLKSIZE;
171 for (i = 0; i < nblock; i++) {
172 if ((r = file_get_block(dir, i, &blk)) < 0)
173 return r;
174 f = (struct File*) blk;
175 for (j = 0; j < BLKFILES; j++)
176 if (strcmp(f[j].f_name, name) == 0) {
177 *file = &f[j];
178 return 0;
181 return -E_NOT_FOUND;
184 // Set *file to point at a free File structure in dir. The caller is
185 // responsible for filling in the File fields.
186 static int
187 dir_alloc_file(struct File *dir, struct File **file)
189 int r;
190 uint32_t nblock, i, j;
191 char *blk;
192 struct File *f;
194 assert((dir->f_size % BLKSIZE) == 0);
195 nblock = dir->f_size / BLKSIZE;
196 for (i = 0; i < nblock; i++) {
197 if ((r = file_get_block(dir, i, &blk)) < 0)
198 return r;
199 f = (struct File*) blk;
200 for (j = 0; j < BLKFILES; j++)
201 if (f[j].f_name[0] == '\0') {
202 *file = &f[j];
203 return 0;
206 dir->f_size += BLKSIZE;
207 if ((r = file_get_block(dir, i, &blk)) < 0)
208 return r;
209 f = (struct File*) blk;
210 *file = &f[0];
211 return 0;
214 // Skip over slashes.
215 static const char*
216 skip_slash(const char *p)
218 while (*p == '/')
219 p++;
220 return p;
223 // Evaluate a path name, starting at the root.
224 // On success, set *pf to the file we found
225 // and set *pdir to the directory the file is in.
226 // If we cannot find the file but find the directory
227 // it should be in, set *pdir and copy the final path
228 // element into lastelem.
229 static int
230 walk_path(const char *path, struct File **pdir, struct File **pf, char *lastelem)
232 const char *p;
233 char name[MAXNAMELEN];
234 struct File *dir, *f;
235 int r;
237 // if (*path != '/')
238 // return -E_BAD_PATH;
239 path = skip_slash(path);
240 f = &super->s_root;
241 dir = 0;
242 name[0] = 0;
244 if (pdir)
245 *pdir = 0;
246 *pf = 0;
247 while (*path != '\0') {
248 dir = f;
249 p = path;
250 while (*path != '/' && *path != '\0')
251 path++;
252 if (path - p >= MAXNAMELEN)
253 return -E_BAD_PATH;
254 memmove(name, p, path - p);
255 name[path - p] = '\0';
256 path = skip_slash(path);
258 if (dir->f_type != FTYPE_DIR)
259 return -E_NOT_FOUND;
261 if ((r = dir_lookup(dir, name, &f)) < 0) {
262 if (r == -E_NOT_FOUND && *path == '\0') {
263 if (pdir)
264 *pdir = dir;
265 if (lastelem)
266 strcpy(lastelem, name);
267 *pf = 0;
269 return r;
273 if (pdir)
274 *pdir = dir;
275 *pf = f;
276 return 0;
279 // --------------------------------------------------------------
280 // File operations
281 // --------------------------------------------------------------
283 // Create "path". On success set *pf to point at the file and return 0.
284 // On error return < 0.
286 file_create(const char *path, struct File **pf)
288 char name[MAXNAMELEN];
289 int r;
290 struct File *dir, *f;
292 if ((r = walk_path(path, &dir, &f, name)) == 0)
293 return -E_FILE_EXISTS;
294 if (r != -E_NOT_FOUND || dir == 0)
295 return r;
296 if ((r = dir_alloc_file(dir, &f)) < 0)
297 return r;
298 strcpy(f->f_name, name);
299 *pf = f;
300 file_flush(dir);
301 return 0;
304 // Open "path". On success set *pf to point at the file and return 0.
305 // On error return < 0.
307 file_open(const char *path, struct File **pf)
309 return walk_path(path, 0, pf, 0);
312 // Read count bytes from f into buf, starting from seek position
313 // offset. This meant to mimic the standard pread function.
314 // Returns the number of bytes read, < 0 on error.
315 ssize_t
316 file_read(struct File *f, void *buf, size_t count, off_t offset)
318 int r, bn;
319 off_t pos;
320 char *blk;
322 if (offset >= f->f_size)
323 return 0;
325 count = MIN(count, f->f_size - offset);
327 for (pos = offset; pos < offset + count; ) {
328 if ((r = file_get_block(f, pos / BLKSIZE, &blk)) < 0)
329 return r;
330 bn = MIN(BLKSIZE - pos % BLKSIZE, offset + count - pos);
331 memmove(buf, blk + pos % BLKSIZE, bn);
332 pos += bn;
333 buf += bn;
336 return count;
339 // Write count bytes from buf into f, starting at seek position
340 // offset. This is meant to mimic the standard pwrite function.
341 // Extends the file if necessary.
342 // Returns the number of bytes written, < 0 on error.
344 file_write(struct File *f, const void *buf, size_t count, off_t offset)
346 int r, bn;
347 off_t pos;
348 char *blk;
350 // Extend file if necessary
351 if (offset + count > f->f_size)
352 if ((r = file_set_size(f, offset + count)) < 0)
353 return r;
355 for (pos = offset; pos < offset + count; ) {
356 if ((r = file_get_block(f, pos / BLKSIZE, &blk)) < 0)
357 return r;
358 bn = MIN(BLKSIZE - pos % BLKSIZE, offset + count - pos);
359 memmove(blk + pos % BLKSIZE, buf, bn);
360 pos += bn;
361 buf += bn;
364 return count;
367 // Remove a block from file f. If it's not there, just silently succeed.
368 // Returns 0 on success, < 0 on error.
369 static int
370 file_free_block(struct File *f, uint32_t filebno)
372 int r;
373 uint32_t *ptr;
375 if ((r = file_block_walk(f, filebno, &ptr, 0)) < 0)
376 return r;
377 if (*ptr) {
378 free_block(*ptr);
379 *ptr = 0;
381 return 0;
384 // Remove any blocks currently used by file 'f',
385 // but not necessary for a file of size 'newsize'.
386 // For both the old and new sizes, figure out the number of blocks required,
387 // and then clear the blocks from new_nblocks to old_nblocks.
388 // If the new_nblocks is no more than NDIRECT, and the indirect block has
389 // been allocated (f->f_indirect != 0), then free the indirect block too.
390 // (Remember to clear the f->f_indirect pointer so you'll know
391 // whether it's valid!)
392 // Do not change f->f_size.
393 static void
394 file_truncate_blocks(struct File *f, off_t newsize)
396 int r;
397 uint32_t bno, old_nblocks, new_nblocks;
399 old_nblocks = (f->f_size + BLKSIZE - 1) / BLKSIZE;
400 new_nblocks = (newsize + BLKSIZE - 1) / BLKSIZE;
401 for (bno = new_nblocks; bno < old_nblocks; bno++)
402 if ((r = file_free_block(f, bno)) < 0)
403 cprintf("warning: file_free_block: %e", r);
405 if (new_nblocks <= NDIRECT && f->f_indirect) {
406 free_block(f->f_indirect);
407 f->f_indirect = 0;
411 // Set the size of file f, truncating or extending as necessary.
413 file_set_size(struct File *f, off_t newsize)
415 if (f->f_size > newsize)
416 file_truncate_blocks(f, newsize);
417 f->f_size = newsize;
418 flush_block(f);
419 return 0;
422 // Flush the contents and metadata of file f out to disk.
423 // Loop over all the blocks in file.
424 // Translate the file block number into a disk block number
425 // and then check whether that disk block is dirty. If so, write it out.
426 void
427 file_flush(struct File *f)
429 int i;
430 uint32_t *pdiskbno;
432 for (i = 0; i < (f->f_size + BLKSIZE - 1) / BLKSIZE; i++) {
433 if (file_block_walk(f, i, &pdiskbno, 0) < 0 ||
434 pdiskbno == NULL || *pdiskbno == 0)
435 continue;
436 flush_block(diskaddr(*pdiskbno));
438 flush_block(f);
439 if (f->f_indirect)
440 flush_block(diskaddr(f->f_indirect));
443 // Remove a file by truncating it and then zeroing the name.
445 file_remove(const char *path)
447 int r;
448 struct File *f;
450 if ((r = walk_path(path, 0, &f, 0)) < 0)
451 return r;
453 file_truncate_blocks(f, 0);
454 f->f_name[0] = '\0';
455 f->f_size = 0;
456 flush_block(f);
458 return 0;
461 // Sync the entire file system. A big hammer.
462 void
463 fs_sync(void)
465 int i;
466 for (i = 1; i < super->s_nblocks; i++)
467 flush_block(diskaddr(i));