diff --git a/Makefile b/Makefile index 2584e4a..c954e41 100644 --- a/Makefile +++ b/Makefile @@ -132,6 +132,7 @@ UPROGS=\ $U/_grind\ $U/_wc\ $U/_zombie\ + $U/_df\ fs.img: mkfs/mkfs README.md $(UPROGS) mkfs/mkfs fs.img README.md $(UPROGS) diff --git a/kernel/buf.h b/kernel/buf.h index 4616e9e..5a20510 100644 --- a/kernel/buf.h +++ b/kernel/buf.h @@ -1,3 +1,6 @@ +#ifndef BUF_H +#define BUF_H + struct buf { int valid; // has data been read from disk? int disk; // does disk "own" buf? @@ -10,3 +13,4 @@ struct buf { uchar data[BSIZE]; }; +#endif diff --git a/kernel/file.h b/kernel/file.h index b076d1d..6f2926f 100644 --- a/kernel/file.h +++ b/kernel/file.h @@ -1,3 +1,5 @@ +#include "sleeplock.h" + struct file { enum { FD_NONE, FD_PIPE, FD_INODE, FD_DEVICE } type; int ref; // reference count diff --git a/kernel/fs.c b/kernel/fs.c index c6bab15..119ecb8 100644 --- a/kernel/fs.c +++ b/kernel/fs.c @@ -695,3 +695,67 @@ nameiparent(char *path, char *name) { return namex(path, 1, name); } + +#include "fs.h" +#include "buf.h" + +/** + * bmap_isfree - Check if a block is free by inspecting the block bitmap. + * @param blockno: The block number to check + * @returns 1 if the block is free, 0 if the block is allocated + */ +int bmap_isfree(int blockno) { + struct buf *bp; + int bmap_block; + int block_offset; + int bit_index; + + // Calculate which block in the bitmap we need to check + bmap_block = blockno / (BSIZE * 8); // Each block in bitmap covers (BSIZE * 8) blocks + block_offset = blockno % (BSIZE * 8); // Offset within the block + bit_index = block_offset % 8; // Which bit in the byte to check + + // Read the block bitmap into a buffer + bp = bread(ROOTDEV, sb.bmapstart + bmap_block); + + // Check the relevant bit in the byte + int is_free = !(bp->data[block_offset / 8] & (1 << bit_index)); // 0 means free + + brelse(bp); // Release the buffer after use + return is_free; +} + + +/** + * bmap_free_blocks - Count the number of free blocks by inspecting the block bitmap. + * @returns the number of free blocks + */ +int bmap_free_blocks(void) { + int free_blocks = 0; + + for (int b = 0; b < sb.nblocks; b++) { + if (bmap_isfree(b)) { + free_blocks++; + } + } + + return free_blocks; +} + +/** + * free_inodes - Count the number of free inodes by inspecting the inode bitmap. + * @returns the number of free inodes + */ +int free_inodes(void) { + int free_inodes = 0; + + for (int i = 1; i < sb.ninodes; i++) { + struct inode *ip = iget(ROOTDEV, i); + if (ip->type == 0) { + free_inodes++; + } + iput(ip); + } + + return free_inodes; +} diff --git a/kernel/fs.h b/kernel/fs.h index 139dcc9..7fd35d8 100644 --- a/kernel/fs.h +++ b/kernel/fs.h @@ -1,6 +1,10 @@ +#ifndef FS_H +#define FS_H + // On-disk file system format. // Both the kernel and user programs use this header file. - +int bmap_free_blocks(void); +int free_inodes(void); #define ROOTINO 1 // root i-number #define BSIZE 1024 // block size @@ -58,3 +62,4 @@ struct dirent { char name[DIRSIZ]; }; +#endif diff --git a/kernel/sleeplock.h b/kernel/sleeplock.h index 110e6f3..e827ad1 100644 --- a/kernel/sleeplock.h +++ b/kernel/sleeplock.h @@ -1,3 +1,6 @@ +#ifndef SLEEPLOCK_H +#define SLEEPLOCK_H + // Long-term locks for processes struct sleeplock { uint locked; // Is the lock held? @@ -8,3 +11,4 @@ struct sleeplock { int pid; // Process holding lock }; +#endif diff --git a/kernel/syscall.c b/kernel/syscall.c index ed65409..d205bb6 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -101,6 +101,7 @@ extern uint64 sys_unlink(void); extern uint64 sys_link(void); extern uint64 sys_mkdir(void); extern uint64 sys_close(void); +extern uint64 sys_statvfs(void); // An array mapping syscall numbers from syscall.h // to the function that handles the system call. @@ -126,6 +127,7 @@ static uint64 (*syscalls[])(void) = { [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, +[SYS_statvfs] sys_statvfs, }; void diff --git a/kernel/syscall.h b/kernel/syscall.h index bc5f356..e483bf1 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -20,3 +20,4 @@ #define SYS_link 19 #define SYS_mkdir 20 #define SYS_close 21 +#define SYS_statvfs 26 diff --git a/kernel/sysproc.c b/kernel/sysproc.c index 1de184e..33e188b 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -5,6 +5,11 @@ #include "memlayout.h" #include "spinlock.h" #include "proc.h" +#include "fs.h" +#include "file.h" +#include "user/stat.h" + +extern struct superblock sb; uint64 sys_exit(void) @@ -89,3 +94,30 @@ sys_uptime(void) release(&tickslock); return xticks; } + +/** + * sys_statvfs - system call to gather filesystem statistics + * @returns 0 on success, -1 on failure + */ +uint64 sys_statvfs(void) { + struct statvfs stats; + + // Fill the stats structure with filesystem information + stats.total_blocks = sb.nblocks; + stats.free_blocks = bmap_free_blocks(); + stats.used_blocks = stats.total_blocks - stats.free_blocks; + stats.total_inodes = sb.ninodes; + stats.free_inodes = free_inodes(); + + uint64 addr; + + argaddr(0,&addr); + + // Copy the stats structure to the user space + if (copyout(myproc()->pagetable, addr, (char *)&stats, sizeof(stats)) < 0) { + return -1; + } + + // Return success + return 0; +} diff --git a/user/df.c b/user/df.c new file mode 100644 index 0000000..1f8c48f --- /dev/null +++ b/user/df.c @@ -0,0 +1,37 @@ +#include "user/user.h" +#include "user/stat.h" +#include "kernel/types.h" + + +int main(int argc, char *argv[]) { + struct statvfs stats; + int human_readable = 0; + int inode_usage = 0; + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-h") == 0) { + human_readable = 1; + } else if (strcmp(argv[i], "-i") == 0) { + inode_usage = 1; + } + } + + if (statvfs(&stats) < 0) { + printf("Error: Failed to retrieve filesystem stats\n"); + exit(1); + } + + if (inode_usage) { + printf("Inodes: Total: %d, Free: %d\n", stats.total_inodes, stats.free_inodes); + } else { + if (human_readable) { + printf("Disk: Total: %dKB, Used: %dKB, Free: %dKB\n", + stats.total_blocks / 2, stats.used_blocks / 2, stats.free_blocks / 2); + } else { + printf("Disk: Total: %d blocks, Used: %d blocks, Free: %d blocks\n", + stats.total_blocks, stats.used_blocks, stats.free_blocks); + } + } + + return 0; +} diff --git a/user/stat.h b/user/stat.h new file mode 100644 index 0000000..df88f37 --- /dev/null +++ b/user/stat.h @@ -0,0 +1,12 @@ +#ifndef STAT_H +#define STAT_H + +struct statvfs { + unsigned int total_blocks; + unsigned int free_blocks; + unsigned int used_blocks; + unsigned int total_inodes; + unsigned int free_inodes; +}; + +#endif diff --git a/user/user.h b/user/user.h index 2e6fc55..349c3ca 100644 --- a/user/user.h +++ b/user/user.h @@ -1,5 +1,7 @@ struct stat; +typedef unsigned int uint; + // system calls int fork(void); int exit(int) __attribute__((noreturn)); @@ -41,3 +43,4 @@ void free(void*); int atoi(const char*); int memcmp(const void *, const void *, uint); void *memcpy(void *, const void *, uint); +int statvfs(void*); diff --git a/user/usys.pl b/user/usys.pl index 01e426e..2a73774 100755 --- a/user/usys.pl +++ b/user/usys.pl @@ -36,3 +36,4 @@ sub entry { entry("sbrk"); entry("sleep"); entry("uptime"); +entry("statvfs");