spawn implemented.
[mit-jos.git] / fs / fs.c
blobfbcf85d030ed7b1d22ed7a259e6809626fa93ff8
1 #include <inc/string.h>
3 #include "fs.h"
5 struct Super *super; // superblock
6 uint32_t *bitmap; // bitmap blocks mapped in memory
8 void file_flush(struct File *f);
9 bool block_is_free(uint32_t blockno);
11 // Return the virtual address of this disk block.
12 char*
13 diskaddr(uint32_t blockno)
15 if (super && blockno >= super->s_nblocks)
16 panic("bad block number %08x in diskaddr", blockno);
17 return (char*) (DISKMAP + blockno * BLKSIZE);
20 // Is this virtual address mapped?
21 bool
22 va_is_mapped(void *va)
24 return (vpd[PDX(va)] & PTE_P) && (vpt[VPN(va)] & PTE_P);
27 // Is this disk block mapped?
28 bool
29 block_is_mapped(uint32_t blockno)
31 char *va = diskaddr(blockno);
32 return va_is_mapped(va) && va != 0;
35 // Is this virtual address dirty?
36 bool
37 va_is_dirty(void *va)
39 return (vpt[VPN(va)] & PTE_D) != 0;
42 // Is this block dirty?
43 bool
44 block_is_dirty(uint32_t blockno)
46 char *va = diskaddr(blockno);
47 return va_is_mapped(va) && va_is_dirty(va);
50 // Allocate a page to hold the disk block
51 int
52 map_block(uint32_t blockno)
54 if (block_is_mapped(blockno))
55 return 0;
56 return sys_page_alloc(0, diskaddr(blockno), PTE_U|PTE_P|PTE_W);
59 // Make sure a particular disk block is loaded into memory.
60 // Returns 0 on success, or a negative error code on error.
61 //
62 // If blk != 0, set *blk to the address of the block in memory.
64 // Hint: Use diskaddr, map_block, and ide_read.
65 static int
66 read_block(uint32_t blockno, char **blk)
68 int r;
69 char *addr;
71 if (super && blockno >= super->s_nblocks)
72 panic("reading non-existent block %08x\n", blockno);
74 if (bitmap && block_is_free(blockno))
75 panic("reading free block %08x\n", blockno);
77 // LAB 5: Your code here.
78 if ((r = map_block(blockno)) < 0)
79 return r;
81 if ((r = ide_read(blockno * BLKSECTS, diskaddr(blockno), BLKSECTS)) < 0)
82 return r;
84 if (blk)
85 *blk = diskaddr(blockno);
87 return 0;
90 // Copy the current contents of the block out to disk.
91 // Then clear the PTE_D bit using sys_page_map.
92 // Hint: Use ide_write.
93 // Hint: Use the PTE_USER constant when calling sys_page_map.
94 void
95 write_block(uint32_t blockno)
97 char *addr;
98 int r;
100 if (!block_is_mapped(blockno))
101 panic("write unmapped block %08x", blockno);
103 // Write the disk block and clear PTE_D.
104 // LAB 5: Your code here.
105 addr = diskaddr(blockno);
106 if ((r = ide_write(blockno * BLKSECTS, addr, BLKSECTS)) < 0)
107 panic("ide write error: %e", r);
108 if ((r = sys_page_map(0, addr, 0, addr, PTE_USER)) < 0)
109 panic("sys page map error: %e", r);
112 // Make sure this block is unmapped.
113 void
114 unmap_block(uint32_t blockno)
116 int r;
118 if (!block_is_mapped(blockno))
119 return;
121 assert(block_is_free(blockno) || !block_is_dirty(blockno));
123 if ((r = sys_page_unmap(0, diskaddr(blockno))) < 0)
124 panic("unmap_block: sys_mem_unmap: %e", r);
125 assert(!block_is_mapped(blockno));
128 // Check to see if the block bitmap indicates that block 'blockno' is free.
129 // Return 1 if the block is free, 0 if not.
130 bool
131 block_is_free(uint32_t blockno)
133 if (super == 0 || blockno >= super->s_nblocks)
134 return 0;
135 if (bitmap[blockno / 32] & (1 << (blockno % 32)))
136 return 1;
137 return 0;
140 // Mark a block free in the bitmap
141 void
142 free_block(uint32_t blockno)
144 // Blockno zero is the null pointer of block numbers.
145 if (blockno == 0)
146 panic("attempt to free zero block");
147 bitmap[blockno/32] |= 1<<(blockno%32);
150 // Search the bitmap for a free block and allocate it.
152 // Return block number allocated on success,
153 // -E_NO_DISK if we are out of blocks.
155 alloc_block_num(void)
157 // LAB 5: Your code here.
158 int i;
160 for (i = 0; i < super->s_nblocks; i++) {
161 if (block_is_free(i)) {
162 bitmap[i / 32] &= ~(1 << (i % 32));
163 write_block(i / BLKBITSIZE + 2);
164 return i;
167 return -E_NO_DISK;
170 // Allocate a block -- first find a free block in the bitmap,
171 // then map it into memory.
173 alloc_block(void)
175 int r, bno;
177 if ((r = alloc_block_num()) < 0)
178 return r;
179 bno = r;
181 if ((r = map_block(bno)) < 0) {
182 free_block(bno);
183 return r;
185 return bno;
188 // Read and validate the file system super-block.
189 void
190 read_super(void)
192 int r;
193 char *blk;
195 if ((r = read_block(1, &blk)) < 0)
196 panic("cannot read superblock: %e", r);
198 super = (struct Super*) blk;
199 if (super->s_magic != FS_MAGIC)
200 panic("bad file system magic number");
202 if (super->s_nblocks > DISKSIZE/BLKSIZE)
203 panic("file system is too large");
205 cprintf("superblock is good\n");
208 // Read and validate the file system bitmap.
210 // Read all the bitmap blocks into memory.
211 // Set the "bitmap" pointer to point at the beginning of the first
212 // bitmap block.
214 // Check that all reserved blocks -- 0, 1, and the bitmap blocks themselves --
215 // are all marked as in-use
216 // (for each block i, assert(!block_is_free(i))).
218 // Hint: Assume that the superblock has already been loaded into
219 // memory (in variable 'super'). Check out super->s_nblocks.
220 void
221 read_bitmap(void)
223 int r;
224 uint32_t i, n;
225 char *blk;
227 // Read the bitmap into memory.
228 // The bitmap consists of one or more blocks. A single bitmap block
229 // contains the in-use bits for BLKBITSIZE blocks. There are
230 // super->s_nblocks blocks in the disk altogether.
231 // Set 'bitmap' to point to the first address in the bitmap.
232 // Hint: Use read_block.
234 // LAB 5: Your code here.
235 n = super->s_nblocks / BLKBITSIZE;
236 cprintf("read the nblocks: %d.\n", n);
237 read_block(2, &blk);
238 bitmap = (uint32_t *)blk;
239 for (i = 1; i < n; i++)
240 read_block(i + 2, NULL);
242 // Make sure the reserved and root blocks are marked in-use.
243 assert(!block_is_free(0));
244 assert(!block_is_free(1));
245 assert(bitmap);
247 // Make sure that the bitmap blocks are marked in-use.
248 // LAB 5: Your code here.
249 for (i = 0; i < n; i++)
250 assert(!block_is_free(i + 2));
252 cprintf("read_bitmap is good\n");
255 // Test that write_block works, by smashing the superblock and reading it back.
256 void
257 check_write_block(void)
259 super = 0;
261 // back up super block
262 read_block(0, 0);
263 memmove(diskaddr(0), diskaddr(1), PGSIZE);
265 // smash it
266 strcpy(diskaddr(1), "OOPS!\n");
267 write_block(1);
268 assert(block_is_mapped(1));
269 assert(!va_is_dirty(diskaddr(1)));
271 // clear it out
272 sys_page_unmap(0, diskaddr(1));
273 assert(!block_is_mapped(1));
275 // read it back in
276 read_block(1, 0);
277 assert(strcmp(diskaddr(1), "OOPS!\n") == 0);
279 // fix it
280 memmove(diskaddr(1), diskaddr(0), PGSIZE);
281 write_block(1);
282 super = (struct Super*)diskaddr(1);
284 cprintf("write_block is good\n");
287 // Initialize the file system
288 void
289 fs_init(void)
291 static_assert(sizeof(struct File) == 256);
293 // Find a JOS disk. Use the second IDE disk (number 1) if available.
294 if (ide_probe_disk1())
295 ide_set_disk(1);
296 else
297 ide_set_disk(0);
299 read_super();
300 check_write_block();
301 read_bitmap();
304 // Find the disk block number slot for the 'filebno'th block in file 'f'.
305 // Set '*ppdiskbno' to point to that slot.
306 // The slot will be one of the f->f_direct[] entries,
307 // or an entry in the indirect block.
308 // When 'alloc' is set, this function will allocate an indirect block
309 // if necessary.
311 // Returns:
312 // 0 on success (but note that *ppdiskbno might equal 0).
313 // -E_NOT_FOUND if the function needed to allocate an indirect block, but
314 // alloc was 0.
315 // -E_NO_DISK if there's no space on the disk for an indirect block.
316 // -E_NO_MEM if there's no space in memory for an indirect block.
317 // -E_INVAL if filebno is out of range (it's >= NINDIRECT).
319 // Analogy: This is like pgdir_walk for files.
321 file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool alloc)
323 int r;
324 uint32_t *ptr;
325 char *blk;
327 if (filebno < NDIRECT)
328 ptr = &f->f_direct[filebno];
329 else if (filebno < NINDIRECT) {
330 if (f->f_indirect == 0) {
331 if (alloc == 0)
332 return -E_NOT_FOUND;
333 if ((r = alloc_block()) < 0)
334 return r;
335 f->f_indirect = r;
336 } else
337 alloc = 0; // we did not allocate a block
338 if ((r = read_block(f->f_indirect, &blk)) < 0)
339 return r;
340 assert(blk != 0);
341 if (alloc) // must clear any block we allocated
342 memset(blk, 0, BLKSIZE);
343 ptr = (uint32_t*)blk + filebno;
344 } else
345 return -E_INVAL;
347 *ppdiskbno = ptr;
348 return 0;
351 // Set '*diskbno' to the disk block number for the 'filebno'th block
352 // in file 'f'.
353 // If 'alloc' is set and the block does not exist, allocate it.
355 // Returns 0 on success, < 0 on error. Errors are:
356 // -E_NOT_FOUND if alloc was 0 but the block did not exist.
357 // -E_NO_DISK if a block needed to be allocated but the disk is full.
358 // -E_NO_MEM if we're out of memory.
359 // -E_INVAL if filebno is out of range.
361 file_map_block(struct File *f, uint32_t filebno, uint32_t *diskbno, bool alloc)
363 int r;
364 uint32_t *ptr;
366 if ((r = file_block_walk(f, filebno, &ptr, alloc)) < 0)
367 return r;
368 if (*ptr == 0) {
369 if (alloc == 0)
370 return -E_NOT_FOUND;
371 if ((r = alloc_block()) < 0)
372 return r;
373 *ptr = r;
375 *diskbno = *ptr;
376 return 0;
379 // Remove a block from file f. If it's not there, just silently succeed.
380 // Returns 0 on success, < 0 on error.
382 file_clear_block(struct File *f, uint32_t filebno)
384 int r;
385 uint32_t *ptr;
387 if ((r = file_block_walk(f, filebno, &ptr, 0)) < 0)
388 return r;
389 if (*ptr) {
390 free_block(*ptr);
391 *ptr = 0;
393 return 0;
396 // Set *blk to point at the filebno'th block in file 'f'.
397 // Allocate the block if it doesn't yet exist.
398 // Returns 0 on success, < 0 on error.
400 file_get_block(struct File *f, uint32_t filebno, char **blk)
402 int r;
403 uint32_t diskbno;
405 // Read in the block, leaving the pointer in *blk.
406 // Hint: Use file_map_block and read_block.
407 // LAB 5: Your code here.
408 if ((r = file_map_block(f, filebno, &diskbno, 1)) < 0)
409 return r;
410 if ((r = read_block(diskbno, blk)) < 0)
411 return r;
412 return 0;
415 // Mark the offset/BLKSIZE'th block dirty in file f
416 // by writing its first word to itself.
418 file_dirty(struct File *f, off_t offset)
420 int r;
421 char *blk;
423 if ((r = file_get_block(f, offset/BLKSIZE, &blk)) < 0)
424 return r;
425 *(volatile char*)blk = *(volatile char*)blk;
426 return 0;
429 // Try to find a file named "name" in dir. If so, set *file to it.
431 dir_lookup(struct File *dir, const char *name, struct File **file)
433 int r;
434 uint32_t i, j, nblock;
435 char *blk;
436 struct File *f;
438 // Search dir for name.
439 // We maintain the invariant that the size of a directory-file
440 // is always a multiple of the file system's block size.
441 assert((dir->f_size % BLKSIZE) == 0);
442 nblock = dir->f_size / BLKSIZE;
443 for (i = 0; i < nblock; i++) {
444 if ((r = file_get_block(dir, i, &blk)) < 0)
445 return r;
446 f = (struct File*) blk;
447 for (j = 0; j < BLKFILES; j++)
448 if (strcmp(f[j].f_name, name) == 0) {
449 *file = &f[j];
450 f[j].f_dir = dir;
451 return 0;
454 return -E_NOT_FOUND;
457 // Set *file to point at a free File structure in dir.
459 dir_alloc_file(struct File *dir, struct File **file)
461 int r;
462 uint32_t nblock, i, j;
463 char *blk;
464 struct File *f;
466 assert((dir->f_size % BLKSIZE) == 0);
467 nblock = dir->f_size / BLKSIZE;
468 for (i = 0; i < nblock; i++) {
469 if ((r = file_get_block(dir, i, &blk)) < 0)
470 return r;
471 f = (struct File*) blk;
472 for (j = 0; j < BLKFILES; j++)
473 if (f[j].f_name[0] == '\0') {
474 *file = &f[j];
475 f[j].f_dir = dir;
476 return 0;
479 dir->f_size += BLKSIZE;
480 if ((r = file_get_block(dir, i, &blk)) < 0)
481 return r;
482 f = (struct File*) blk;
483 *file = &f[0];
484 f[0].f_dir = dir;
485 return 0;
488 // Skip over slashes.
489 static inline const char*
490 skip_slash(const char *p)
492 while (*p == '/')
493 p++;
494 return p;
497 // Evaluate a path name, starting at the root.
498 // On success, set *pf to the file we found
499 // and set *pdir to the directory the file is in.
500 // If we cannot find the file but find the directory
501 // it should be in, set *pdir and copy the final path
502 // element into lastelem.
503 static int
504 walk_path(const char *path, struct File **pdir, struct File **pf, char *lastelem)
506 const char *p;
507 char name[MAXNAMELEN];
508 struct File *dir, *f;
509 int r;
511 // if (*path != '/')
512 // return -E_BAD_PATH;
513 path = skip_slash(path);
514 f = &super->s_root;
515 dir = 0;
516 name[0] = 0;
518 if (pdir)
519 *pdir = 0;
520 *pf = 0;
521 while (*path != '\0') {
522 dir = f;
523 p = path;
524 while (*path != '/' && *path != '\0')
525 path++;
526 if (path - p >= MAXNAMELEN)
527 return -E_BAD_PATH;
528 memmove(name, p, path - p);
529 name[path - p] = '\0';
530 path = skip_slash(path);
532 if (dir->f_type != FTYPE_DIR)
533 return -E_NOT_FOUND;
535 if ((r = dir_lookup(dir, name, &f)) < 0) {
536 if (r == -E_NOT_FOUND && *path == '\0') {
537 if (pdir)
538 *pdir = dir;
539 if (lastelem)
540 strcpy(lastelem, name);
541 *pf = 0;
543 return r;
547 if (pdir)
548 *pdir = dir;
549 *pf = f;
550 return 0;
553 // Create "path". On success set *pf to point at the file and return 0.
554 // On error return < 0.
556 file_create(const char *path, struct File **pf)
558 char name[MAXNAMELEN];
559 int r;
560 struct File *dir, *f;
562 if ((r = walk_path(path, &dir, &f, name)) == 0)
563 return -E_FILE_EXISTS;
564 if (r != -E_NOT_FOUND || dir == 0)
565 return r;
566 if (dir_alloc_file(dir, &f) < 0)
567 return r;
568 strcpy(f->f_name, name);
569 *pf = f;
570 return 0;
573 // Open "path". On success set *pf to point at the file and return 0.
574 // On error return < 0.
576 file_open(const char *path, struct File **pf)
578 // Hint: Use walk_path.
579 // LAB 5: Your code here.
580 struct File *dir, *f;
581 int r;
583 if ((r = walk_path(path, &dir, &f, 0)) < 0)
584 return r;
585 *pf = f;
586 return 0;
589 // Remove any blocks currently used by file 'f',
590 // but not necessary for a file of size 'newsize'.
591 // For both the old and new sizes, figure out the number of blocks required,
592 // and then clear the blocks from new_nblocks to old_nblocks.
593 // If the new_nblocks is no more than NDIRECT, and the indirect block has
594 // been allocated (f->f_indirect != 0), then free the indirect block too.
595 // (Remember to clear the f->f_indirect pointer so you'll know
596 // whether it's valid!)
597 // Do not change f->f_size.
598 static void
599 file_truncate_blocks(struct File *f, off_t newsize)
601 int r;
602 uint32_t bno, old_nblocks, new_nblocks;
604 // Hint: Use file_clear_block and/or free_block.
605 for (old_nblocks = 0; old_nblocks < f->f_size; old_nblocks += BLKSIZE)
607 old_nblocks /= BLKSIZE;
608 for (new_nblocks = 0; new_nblocks < newsize; new_nblocks += BLKSIZE)
610 new_nblocks /= BLKSIZE;
611 cprintf("truncate from %d[%d] -> %d[%d].\n", f->f_size, new_nblocks, f->f_size, old_nblocks);
612 for (bno = new_nblocks; bno <= old_nblocks; bno++)
613 if ((r = file_clear_block(f, bno)) < 0)
614 panic("file clear block error: %e\n", r);
616 // Yeah, we need to clear the extra block as well
617 if (new_nblocks < NDIRECT) {
618 if (f->f_indirect != 0) {
619 free_block(f->f_indirect);
620 f->f_indirect = 0;
622 while (new_nblocks < NDIRECT)
623 f->f_direct[new_nblocks++] = 0;
628 file_set_size(struct File *f, off_t newsize)
630 if (f->f_size > newsize)
631 file_truncate_blocks(f, newsize);
632 f->f_size = newsize;
633 if (f->f_dir)
634 file_flush(f->f_dir);
635 return 0;
638 // Flush the contents of file f out to disk.
639 // Loop over all the blocks in file.
640 // Translate the file block number into a disk block number
641 // and then check whether that disk block is dirty. If so, write it out.
643 // Hint: use file_map_block, block_is_dirty, and write_block.
644 void
645 file_flush(struct File *f)
647 // LAB 5: Your code here.
648 int i, n, r;
649 uint32_t diskbno;
651 for (i = 0; i < f->f_size; i += BLKSIZE) {
652 if ((r = file_map_block(f, i/BLKSIZE, &diskbno, 0)) < 0)
653 panic("file map block error: %e", r);
654 if (block_is_dirty(diskbno))
655 write_block(diskbno);
656 //if (f->f_indirect && block_is_dirty(f->f_indirect))
657 //write_block(f->f_indirect);
661 // Sync the entire file system. A big hammer.
662 void
663 fs_sync(void)
665 int i;
666 for (i = 0; i < super->s_nblocks; i++)
667 if (block_is_dirty(i))
668 write_block(i);
671 // Close a file.
672 void
673 file_close(struct File *f)
675 file_flush(f);
676 if (f->f_dir)
677 file_flush(f->f_dir);
680 // Remove a file by truncating it and then zeroing the name.
682 file_remove(const char *path)
684 int r;
685 struct File *f;
687 if ((r = walk_path(path, 0, &f, 0)) < 0)
688 return r;
690 file_truncate_blocks(f, 0);
691 f->f_name[0] = '\0';
692 f->f_size = 0;
693 if (f->f_dir)
694 file_flush(f->f_dir);
696 return 0;