summaryrefslogtreecommitdiff
path: root/src/fs
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-01-03 02:19:57 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-01-03 02:19:57 +0800
commit210025560c80b48af4875af199f183ad709e3bf0 (patch)
treeeb11aeb7b641187f207e3b572c5184cf3526b463 /src/fs
parentbde87490d61135dcaa94d9b1489ff6c39876d169 (diff)
parent10614e04929a6d109a5e90524e446b637aaf0c13 (diff)
downloadlume-210025560c80b48af4875af199f183ad709e3bf0.tar.gz
lume-210025560c80b48af4875af199f183ad709e3bf0.tar.xz
Merge pull request #4 from Richard-Qin-001/dev/shell
Rebuilt the system's underlying structure; it is now stable enough to merge. The system now supports a read-only file system and has a simple shell implemented.
Diffstat (limited to 'src/fs')
-rw-r--r--src/fs/fat32.cc325
-rw-r--r--src/fs/file.cc180
-rw-r--r--src/fs/fs.cc192
3 files changed, 697 insertions, 0 deletions
diff --git a/src/fs/fat32.cc b/src/fs/fat32.cc
new file mode 100644
index 0000000..7ef829a
--- /dev/null
+++ b/src/fs/fat32.cc
@@ -0,0 +1,325 @@
+#include "fs/fat32.h"
+#include "lib/string.h"
+#include "drivers/uart.h"
+#include "kernel/mm.h"
+#include "kernel/proc.h"
+
+FAT32FileSystem fat32_fs;
+
+// FAT32 FileSystem Implementation
+
+void FAT32FileSystem::init()
+{
+ struct Buf *b = BufferCache::bread(0, 0);
+ uint8 *data = (uint8 *)b->data;
+ struct FAT32_BPB *bpb = (struct FAT32_BPB *)data;
+
+ uint32 partition_lba = 0;
+
+ bool is_valid_bpb = (bpb->bytesPerSector == 512) && (bpb->rootClus != 0);
+
+ if (!is_valid_bpb)
+ {
+ Drivers::uart_puts("[FAT32] Sector 0 is not a valid BPB. Checking MBR...\n");
+
+ if (data[510] != 0x55 || data[511] != 0xAA)
+ {
+ Drivers::uart_puts("[FAT32] Panic: Invalid Disk Signature!\n");
+ while (1)
+ ;
+ }
+
+ uint32 lba = *(uint32 *)(data + 446 + 8);
+
+ if (lba > 0)
+ {
+ Drivers::uart_puts("[FAT32] MBR Partition 1 found @ Sector ");
+ Drivers::uart_put_int(lba);
+ Drivers::uart_puts("\n");
+ partition_lba = lba;
+ }
+ else
+ {
+ Drivers::uart_puts("[FAT32] Panic: No partition found.\n");
+ while (1)
+ ;
+ }
+
+ BufferCache::brelse(b);
+
+ b = BufferCache::bread(0, partition_lba / 2);
+ bpb = (struct FAT32_BPB *)b->data;
+ }
+
+ if (bpb->bytesPerSector != 512)
+ {
+ Drivers::uart_puts("[FAT32] Panic: Invalid FAT32 BPB in partition.\n");
+ while (1)
+ ;
+ }
+
+ uint32 fat_sz = bpb->FATSz32;
+ uint32 reserved_sec = bpb->reservedSecCnt;
+ uint8 num_fats = bpb->numFATs;
+
+ sectors_per_cluster = bpb->secPerClus;
+ bytes_per_cluster = sectors_per_cluster * 512;
+ root_cluster = bpb->rootClus;
+
+ fat_start_sector = partition_lba + reserved_sec;
+ data_start_sector = partition_lba + reserved_sec + (fat_sz * num_fats);
+
+ Drivers::uart_puts("[FAT32] Init Success: RootClus=");
+ Drivers::uart_put_int(root_cluster);
+ Drivers::uart_puts(", DataStart=");
+ Drivers::uart_put_int(data_start_sector);
+ Drivers::uart_puts("\n");
+
+ BufferCache::brelse(b);
+}
+
+Inode *FAT32FileSystem::root()
+{
+ FAT32Inode *ip = new FAT32Inode();
+
+ ip->fs = this;
+ ip->dev = 0;
+ ip->inum = root_cluster;
+ ip->ref_cnt = 1;
+ ip->valid = 1;
+ ip->type = T_DIR;
+ ip->size = 0;
+ ip->nlink = 1;
+
+ ip->first_clus = root_cluster;
+ ip->parent_clus = 0;
+
+ return ip;
+}
+
+uint32 FAT32FileSystem::cluster_to_sector(uint32 cluster)
+{
+ return data_start_sector + (cluster - 2) * sectors_per_cluster;
+}
+
+uint32 FAT32FileSystem::next_cluster(uint32 cluster)
+{
+ uint32 fat_offset = cluster * 4;
+ uint32 fat_sector = fat_start_sector + (fat_offset / 512);
+ uint32 ent_offset = fat_offset % 512;
+
+ // block = sector / 2
+ // offset in block = (sector % 2) * 512 + ent_offset
+
+ struct Buf *b = BufferCache::bread(0, fat_sector / 2);
+
+ uint32 block_offset = (fat_sector % 2) * 512 + ent_offset;
+ uint32 next = *(uint32 *)(b->data + block_offset);
+
+ BufferCache::brelse(b);
+
+ return next & 0x0FFFFFFF;
+}
+
+// FAT32 Inode Implementation
+
+static bool match_name(const char *disk_name, const char *target)
+{
+ char temp[13];
+ int i, j;
+
+ for (i = 0; i < 8 && disk_name[i] != ' '; i++)
+ temp[i] = disk_name[i];
+ if (disk_name[8] != ' ')
+ {
+ temp[i++] = '.';
+ for (j = 0; j < 3 && disk_name[8 + j] != ' '; j++)
+ temp[i++] = disk_name[8 + j];
+ }
+ temp[i] = 0;
+ return strcasecmp(temp, target) == 0;
+}
+
+int FAT32Inode::read(char *dst, uint32 off, uint32 n, int isUser)
+{
+ if (first_clus == 0)
+ return 0;
+
+ uint32 tot = 0;
+ uint32 clus = first_clus;
+ uint32 clus_sz = fs->bytes_per_cluster;
+
+ uint32 skip = off / clus_sz;
+ uint32 current_off = off % clus_sz;
+
+ while (skip > 0)
+ {
+ clus = fs->next_cluster(clus);
+ if (clus >= 0x0FFFFFF8)
+ return 0;
+ skip--;
+ }
+
+ while (tot < n)
+ {
+ uint32 sec_in_clus = current_off / 512;
+
+ if (sec_in_clus >= fs->sectors_per_cluster)
+ {
+ clus = fs->next_cluster(clus);
+ if (clus >= 0x0FFFFFF8)
+ break;
+ current_off = 0;
+ continue;
+ }
+
+ uint32 sec = fs->cluster_to_sector(clus) + sec_in_clus;
+ uint32 off_in_sec = current_off % 512;
+
+ uint32 m = 512 - off_in_sec;
+ if (n - tot < m)
+ m = n - tot;
+
+ struct Buf *b = BufferCache::bread(dev, sec / 2);
+ uint32 block_offset = (sec % 2) * 512 + off_in_sec;
+
+ if (isUser)
+ {
+ if (VM::copyout(myproc()->pagetable, (uint64)dst, (char *)(b->data + block_offset), m) < 0)
+ {
+ BufferCache::brelse(b);
+ return -1;
+ }
+ }
+ else
+ {
+ memmove(dst, b->data + block_offset, m);
+ }
+ BufferCache::brelse(b);
+
+ tot += m;
+ dst += m;
+ current_off += m;
+ }
+ return tot;
+}
+
+Inode *FAT32Inode::lookup(const char *name)
+{
+ if (type != T_DIR)
+ return nullptr;
+
+ uint32 clus = first_clus;
+ struct FAT32_DirEnt de;
+
+ while (clus < 0x0FFFFFF8)
+ {
+ uint32 sec = fs->cluster_to_sector(clus);
+
+ for (int i = 0; i < fs->sectors_per_cluster; i++)
+ {
+ uint32 current_sec = sec + i;
+ struct Buf *b = BufferCache::bread(dev, current_sec / 2);
+ uint32 block_base = (current_sec % 2) * 512;
+
+ for (int off = 0; off < 512; off += 32)
+ {
+ memmove(&de, b->data + block_base + off, 32);
+
+ if (de.name[0] == 0x00)
+ {
+ BufferCache::brelse(b);
+ return nullptr;
+ }
+ if (de.name[0] == 0xE5)
+ continue;
+ if (de.attr & ATTR_LONG_NAME)
+ continue;
+
+ if (match_name(de.name, name))
+ {
+ FAT32Inode *ip = new FAT32Inode();
+ ip->fs = fs;
+ ip->dev = dev;
+ ip->type = (de.attr & ATTR_DIRECTORY) ? T_DIR : T_FILE;
+ ip->size = de.fileSize;
+ ip->first_clus = (de.fstClusHI << 16) | de.fstClusLO;
+ ip->inum = ip->first_clus;
+ ip->nlink = 1;
+ ip->ref_cnt = 1;
+ ip->valid = 1;
+
+ BufferCache::brelse(b);
+ return ip;
+ }
+ }
+ BufferCache::brelse(b);
+ }
+ clus = fs->next_cluster(clus);
+ }
+ return nullptr;
+}
+
+// Placeholder
+void FAT32Inode::update() {}
+int FAT32Inode::write(const char *src, uint32 off, uint32 n, int isUser) { return -1; }
+Inode *FAT32Inode::create(const char *name, short type, short major, short minor) { return nullptr; }
+void FAT32Inode::truncate() {}
+uint32 FAT32FileSystem::alloc_cluster() { return 0; }
+
+// Test Function
+void fat32_test()
+{
+ static bool inited = false;
+ if (!inited)
+ {
+ fat32_fs.init();
+ inited = true;
+ }
+
+ Drivers::uart_puts("\n[FAT32] Starting Test via Syscall...\n");
+
+ Inode *root = fat32_fs.root();
+ if (root)
+ {
+ Drivers::uart_puts("[FAT32] Root Inode retrieved. Inum: ");
+ Drivers::uart_put_int(root->inum);
+ Drivers::uart_puts("\n");
+
+ Drivers::uart_puts("[FAT32] Looking for TEST.TXT...\n");
+ Inode *node = root->lookup("test.txt");
+
+ if (node)
+ {
+ Drivers::uart_puts("[FAT32] Found TEST.TXT! Size: ");
+ Drivers::uart_put_int(node->size);
+ Drivers::uart_puts("\n");
+
+ char buf[128];
+ int n = node->read(buf, 0, 127, 0);
+ if (n > 0)
+ {
+ buf[n] = 0;
+ Drivers::uart_puts("[FAT32] Content:\n>> ");
+ Drivers::uart_puts(buf);
+ Drivers::uart_puts("\n");
+ }
+ else
+ {
+ Drivers::uart_puts("[FAT32] Read returned 0.\n");
+ }
+ VFS::iput(node);
+ }
+ else
+ {
+ Drivers::uart_puts("[FAT32] TEST.TXT not found.\n");
+ }
+ VFS::iput(root);
+ }
+ else
+ {
+ Drivers::uart_puts("[FAT32] Failed to get root inode.\n");
+ }
+
+ Drivers::uart_puts("[FAT32] Test Finished.\n");
+} \ No newline at end of file
diff --git a/src/fs/file.cc b/src/fs/file.cc
new file mode 100644
index 0000000..99bb65d
--- /dev/null
+++ b/src/fs/file.cc
@@ -0,0 +1,180 @@
+#include "fs/file.h"
+#include "kernel/spinlock.h"
+#include "kernel/mm.h"
+#include "drivers/uart.h"
+#include "kernel/proc.h"
+
+#define NFILE 100
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+
+namespace FileTable
+{
+ struct
+ {
+ Spinlock lock;
+ struct file files[NFILE];
+ } ftable;
+
+ void init()
+ {
+ ftable.lock.init("file_table");
+ }
+
+ struct file *alloc()
+ {
+ struct file *f;
+ ftable.lock.acquire();
+ for (f = ftable.files; f < ftable.files + NFILE; f++)
+ {
+ if (f->ref == 0)
+ {
+ f->ref = 1;
+ ftable.lock.release();
+ return f;
+ }
+ }
+ ftable.lock.release();
+ return nullptr;
+ }
+
+ struct file *dup(struct file *f)
+ {
+ ftable.lock.acquire();
+ if (f->ref < 1)
+ {
+ Drivers::uart_puts("panic: file dup\n");
+ while (1)
+ ;
+ }
+ f->ref++;
+ ftable.lock.release();
+ return f;
+ }
+
+ void close(struct file *f)
+ {
+ struct file ff;
+
+ ftable.lock.acquire();
+ if(f->ref < 1)
+ {
+ Drivers::uart_puts("panic: file close\n");
+ while (1)
+ ;
+ }
+ f->ref--;
+ if (f->ref > 0)
+ {
+ ftable.lock.release();
+ return;
+ }
+
+ ff = *f;
+ f->ref = 0;
+ f->type = FD_NONE;
+ ftable.lock.release();
+ if (ff.type == FD_INODE)
+ {
+ // Release the corresponding Inode
+ VFS::iput(ff.ip);
+ }
+ else if (ff.type == FD_PIPE)
+ {
+ // pipeclose(ff.pipe, ff.writable);
+ }
+ }
+
+ int read(struct file *f, uint64 addr, int n)
+ {
+ if (f->readable == 0)
+ return -1;
+ if (f->type == FD_INODE)
+ {
+ VFS::ilock(f->ip);
+ int r = f->ip->read((char*)addr, f->off, n, 1);
+ if(r > 0)
+ {
+ f->off += r;
+ }
+ VFS::iunlock(f->ip);
+ return r;
+ }
+ return -1;
+ }
+
+ int write(struct file *f, uint64 addr, int n)
+ {
+ if (f->writable == 0)
+ return -1;
+ if (f->type == FD_INODE)
+ {
+ VFS::ilock(f->ip);
+ int r = f->ip->write((char *)addr, f->off, n, 1);
+ if (r > 0)
+ {
+ f->off += r;
+ }
+ VFS::iunlock(f->ip);
+ return r;
+ }
+ // else if (f->type == FD_PIPE) ...
+
+ return -1;
+ }
+
+ int stat(struct file *f, uint64 addr)
+ {
+ if (f->type == FD_INODE)
+ {
+ struct kstat st;
+ VFS::ilock(f->ip);
+ f->ip->getattr(&st);
+ VFS::iunlock(f->ip);
+
+ struct Proc *p = myproc();
+ if (VM::copyout(p->pagetable, addr, (char *)&st, sizeof(st)) < 0)
+ return -1;
+ return 0;
+ }
+ return -1;
+ }
+
+ int lseek(struct file *f, int offset, int whence)
+ {
+ if (f->type != FD_INODE)
+ return -1;
+
+ VFS::ilock(f->ip);
+
+ int new_off = f->off;
+ int size = f->ip->size;
+
+ switch (whence)
+ {
+ case SEEK_SET:
+ new_off = offset;
+ break;
+ case SEEK_CUR:
+ new_off += offset;
+ break;
+ case SEEK_END:
+ new_off = size + offset;
+ break;
+ default:
+ VFS::iunlock(f->ip);
+ return -1;
+ }
+
+ if (new_off < 0)
+ {
+ VFS::iunlock(f->ip);
+ return -1;
+ }
+
+ f->off = new_off;
+ VFS::iunlock(f->ip);
+ return new_off;
+ }
+} // namespace FileTable
diff --git a/src/fs/fs.cc b/src/fs/fs.cc
new file mode 100644
index 0000000..c5a34ee
--- /dev/null
+++ b/src/fs/fs.cc
@@ -0,0 +1,192 @@
+#include "fs/fs.h"
+#include "kernel/riscv.h"
+#include "drivers/uart.h"
+#include "kernel/spinlock.h"
+#include "kernel/proc.h"
+#include "fs/fat32.h"
+#include "lib/string.h"
+
+static Inode *global_root_inode = nullptr;
+
+static const char *skipelem(const char *path, char *name)
+{
+ const char *s;
+ int len;
+
+ while (*path == '/')
+ path++;
+ if (*path == 0)
+ return nullptr;
+
+ s = path;
+ while (*path != '/' && *path != 0)
+ path++;
+
+ len = path - s;
+ if (len >= 128)
+ len = 127;
+
+ memmove(name, s, len);
+ name[len] = 0;
+
+ while (*path == '/')
+ path++;
+ return path;
+}
+
+void Inode::getattr(struct kstat *st)
+{
+ st->dev = dev;
+ st->ino = inum;
+ st->type = type;
+ st->nlink = nlink;
+ st->size = size;
+
+ st->uid = 0;
+ st->gid = 0;
+ st->mode = 0;
+}
+
+namespace VFS
+{
+ void init()
+ {
+ Drivers::uart_puts("[VFS] Inode subsystem initialized.\n");
+ }
+
+ void iput(Inode *ip)
+ {
+ if (ip == nullptr)
+ return;
+
+ ip->lock.acquire();
+ if(ip->ref_cnt < 1)
+ {
+ Drivers::uart_puts("panic: iput ref\n");
+ while (1)
+ ;
+ }
+ ip->ref_cnt--;
+ int ref = ip->ref_cnt;
+ ip->lock.release();
+
+ if (ref == 0)
+ {
+ delete ip;
+ }
+ }
+
+ Inode* idup(Inode *ip)
+ {
+ if (ip == nullptr)
+ return nullptr;
+
+ ip->lock.acquire();
+ ip->ref_cnt++;
+ ip->lock.release();
+ return ip;
+ }
+
+ void ilock(Inode *ip)
+ {
+ if (ip == 0 || ip->ref_cnt < 1)
+ {
+ Drivers::uart_puts("panic: ilock\n");
+ while (1)
+ ;
+ }
+
+ ip->lock.acquire();
+
+ }
+
+ void iunlock(Inode *ip)
+ {
+ if (ip == 0 || !ip->lock.holding() || ip->ref_cnt < 1)
+ {
+ Drivers::uart_puts("panic: iunlock\n");
+ while (1)
+ ;
+ }
+ ip->lock.release();
+ }
+
+ void iunlockput(Inode *ip)
+ {
+ if (!ip)
+ return;
+ iunlock(ip);
+ iput(ip);
+ }
+
+ void mount_root(Inode *root)
+ {
+ if (global_root_inode)
+ {
+ Drivers::uart_puts("VFS: Root already mounted!\n");
+ return;
+ }
+ global_root_inode = root;
+ Drivers::uart_puts("[VFS] Root filesystem mounted.\n");
+ }
+
+ Inode *get_root()
+ {
+ if (global_root_inode == nullptr)
+ {
+ Drivers::uart_puts("[VFS] Lazy init: Mounting FAT32...\n");
+ fat32_fs.init();
+ mount_root(fat32_fs.root());
+ }
+ if (!global_root_inode)
+ return nullptr;
+ return idup(global_root_inode);
+ }
+
+ Inode *namei(const char *path)
+ {
+ char name[128];
+ Inode *ip, *next;
+ struct Proc *p = myproc();
+
+ if (*path == '/')
+ {
+ ip = get_root();
+ }
+ else
+ {
+ if (p->cwd == nullptr)
+ {
+ ip = get_root();
+ }
+ else
+ {
+ ip = idup(p->cwd);
+ }
+ }
+
+ if (ip == nullptr)
+ return nullptr;
+
+ while ((path = skipelem(path, name)) != nullptr)
+ {
+ ilock(ip);
+
+ if (ip->type != T_DIR)
+ {
+ iunlockput(ip);
+ return nullptr;
+ }
+
+ next = ip->lookup(name);
+ iunlockput(ip);
+
+ ip = next;
+ if (ip == nullptr)
+ return nullptr;
+ }
+
+ return ip;
+ }
+
+} // namespace VFS