1 #include <inc/string.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.
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?
22 va_is_mapped(void *va
)
24 return (vpd
[PDX(va
)] & PTE_P
) && (vpt
[VPN(va
)] & PTE_P
);
27 // Is this disk block mapped?
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?
39 return (vpt
[VPN(va
)] & PTE_D
) != 0;
42 // Is this block dirty?
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
52 map_block(uint32_t blockno
)
54 if (block_is_mapped(blockno
))
56 return sys_page_alloc(0, diskaddr(blockno
), PTE_U
|PTE_P
|PTE_W
|PTE_SHARE
);
59 // Make sure a particular disk block is loaded into memory.
60 // Returns 0 on success, or a negative error code on error.
62 // If blk != 0, set *blk to the address of the block in memory.
64 // Hint: Use diskaddr, map_block, and ide_read.
66 read_block(uint32_t blockno
, char **blk
)
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)
81 if ((r
= ide_read(blockno
* BLKSECTS
, diskaddr(blockno
), BLKSECTS
)) < 0)
85 *blk
= diskaddr(blockno
);
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.
95 write_block(uint32_t blockno
)
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.
114 unmap_block(uint32_t blockno
)
118 if (!block_is_mapped(blockno
))
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.
131 block_is_free(uint32_t blockno
)
133 if (super
== 0 || blockno
>= super
->s_nblocks
)
135 if (bitmap
[blockno
/ 32] & (1 << (blockno
% 32)))
140 // Mark a block free in the bitmap
142 free_block(uint32_t blockno
)
144 // Blockno zero is the null pointer of block numbers.
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.
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);
170 // Allocate a block -- first find a free block in the bitmap,
171 // then map it into memory.
177 if ((r
= alloc_block_num()) < 0)
181 if ((r
= map_block(bno
)) < 0) {
188 // Read and validate the file system super-block.
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
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.
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
);
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));
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.
257 check_write_block(void)
261 // back up super block
263 memmove(diskaddr(0), diskaddr(1), PGSIZE
);
266 strcpy(diskaddr(1), "OOPS!\n");
268 assert(block_is_mapped(1));
269 assert(!va_is_dirty(diskaddr(1)));
272 sys_page_unmap(0, diskaddr(1));
273 assert(!block_is_mapped(1));
277 assert(strcmp(diskaddr(1), "OOPS!\n") == 0);
280 memmove(diskaddr(1), diskaddr(0), PGSIZE
);
282 super
= (struct Super
*)diskaddr(1);
284 cprintf("write_block is good\n");
287 // Initialize the file system
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())
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
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
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
)
327 if (filebno
< NDIRECT
)
328 ptr
= &f
->f_direct
[filebno
];
329 else if (filebno
< NINDIRECT
) {
330 if (f
->f_indirect
== 0) {
333 if ((r
= alloc_block()) < 0)
337 alloc
= 0; // we did not allocate a block
338 if ((r
= read_block(f
->f_indirect
, &blk
)) < 0)
341 if (alloc
) // must clear any block we allocated
342 memset(blk
, 0, BLKSIZE
);
343 ptr
= (uint32_t*)blk
+ filebno
;
351 // Set '*diskbno' to the disk block number for the 'filebno'th block
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
)
366 if ((r
= file_block_walk(f
, filebno
, &ptr
, alloc
)) < 0)
371 if ((r
= alloc_block()) < 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
)
387 if ((r
= file_block_walk(f
, filebno
, &ptr
, 0)) < 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
)
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)
410 if ((r
= read_block(diskbno
, blk
)) < 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
)
423 if ((r
= file_get_block(f
, offset
/BLKSIZE
, &blk
)) < 0)
425 *(volatile char*)blk
= *(volatile char*)blk
;
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
)
434 uint32_t i
, j
, nblock
;
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)
446 f
= (struct File
*) blk
;
447 for (j
= 0; j
< BLKFILES
; j
++)
448 if (strcmp(f
[j
].f_name
, name
) == 0) {
457 // Set *file to point at a free File structure in dir.
459 dir_alloc_file(struct File
*dir
, struct File
**file
)
462 uint32_t nblock
, i
, j
;
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)
471 f
= (struct File
*) blk
;
472 for (j
= 0; j
< BLKFILES
; j
++)
473 if (f
[j
].f_name
[0] == '\0') {
479 dir
->f_size
+= BLKSIZE
;
480 if ((r
= file_get_block(dir
, i
, &blk
)) < 0)
482 f
= (struct File
*) blk
;
488 // Skip over slashes.
489 static inline const char*
490 skip_slash(const char *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.
504 walk_path(const char *path
, struct File
**pdir
, struct File
**pf
, char *lastelem
)
507 char name
[MAXNAMELEN
];
508 struct File
*dir
, *f
;
512 // return -E_BAD_PATH;
513 path
= skip_slash(path
);
521 while (*path
!= '\0') {
524 while (*path
!= '/' && *path
!= '\0')
526 if (path
- p
>= MAXNAMELEN
)
528 memmove(name
, p
, path
- p
);
529 name
[path
- p
] = '\0';
530 path
= skip_slash(path
);
532 if (dir
->f_type
!= FTYPE_DIR
)
535 if ((r
= dir_lookup(dir
, name
, &f
)) < 0) {
536 if (r
== -E_NOT_FOUND
&& *path
== '\0') {
540 strcpy(lastelem
, name
);
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
];
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)
566 if (dir_alloc_file(dir
, &f
) < 0)
568 strcpy(f
->f_name
, name
);
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
;
583 if ((r
= walk_path(path
, &dir
, &f
, 0)) < 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.
599 file_truncate_blocks(struct File
*f
, off_t newsize
)
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
);
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
);
634 file_flush(f
->f_dir
);
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.
645 file_flush(struct File
*f
)
647 // LAB 5: Your code here.
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.
666 for (i
= 0; i
< super
->s_nblocks
; i
++)
667 if (block_is_dirty(i
))
673 file_close(struct File
*f
)
677 file_flush(f
->f_dir
);
680 // Remove a file by truncating it and then zeroing the name.
682 file_remove(const char *path
)
687 if ((r
= walk_path(path
, 0, &f
, 0)) < 0)
690 file_truncate_blocks(f
, 0);
694 file_flush(f
->f_dir
);