]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
diskfs: add minimal on-disk filesystem mounted at /disk
authorTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 00:11:50 +0000 (21:11 -0300)
committerTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 00:11:50 +0000 (21:11 -0300)
include/diskfs.h [new file with mode: 0644]
src/kernel/diskfs.c [new file with mode: 0644]
src/kernel/init.c
src/kernel/syscall.c
user/init.c

diff --git a/include/diskfs.h b/include/diskfs.h
new file mode 100644 (file)
index 0000000..2d05498
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef DISKFS_H
+#define DISKFS_H
+
+#include "fs.h"
+#include <stdint.h>
+
+fs_node_t* diskfs_create_root(void);
+
+// Open (and optionally create) a diskfs file at the root (flat namespace).
+// rel_path must not contain '/'.
+// flags: supports O_CREAT (0x40) and O_TRUNC (0x200) semantics (minimal).
+int diskfs_open_file(const char* rel_path, uint32_t flags, fs_node_t** out_node);
+
+#endif
diff --git a/src/kernel/diskfs.c b/src/kernel/diskfs.c
new file mode 100644 (file)
index 0000000..af333e3
--- /dev/null
@@ -0,0 +1,358 @@
+#include "diskfs.h"
+
+#include "ata_pio.h"
+#include "errno.h"
+#include "heap.h"
+#include "utils.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+// Very small on-disk FS (flat namespace) stored starting at LBA2.
+// - LBA0 reserved
+// - LBA1 used by persistfs counter (legacy)
+// - LBA2 superblock + directory entries
+// - data blocks allocated linearly from LBA3 upward
+//
+// Not a full POSIX FS; goal is persistent files with read/write + create/trunc.
+
+#define DISKFS_LBA_SUPER 2U
+#define DISKFS_LBA_SUPER2 3U
+#define DISKFS_LBA_DATA_START 4U
+
+#define DISKFS_MAGIC 0x44465331U /* 'DFS1' */
+#define DISKFS_VERSION 2U
+
+#define DISKFS_MAX_FILES 12
+#define DISKFS_NAME_MAX 32
+
+#define DISKFS_SECTOR 512U
+
+#define DISKFS_DEFAULT_CAP_SECTORS 8U /* 4KB */
+
+struct diskfs_dirent {
+    char name[DISKFS_NAME_MAX];
+    uint32_t start_lba;
+    uint32_t size_bytes;
+    uint32_t cap_sectors;
+};
+
+struct diskfs_super {
+    uint32_t magic;
+    uint32_t version;
+    uint32_t file_count;
+    uint32_t next_free_lba;
+    struct diskfs_dirent files[DISKFS_MAX_FILES];
+};
+
+struct diskfs_node {
+    fs_node_t vfs;
+    uint32_t idx;
+};
+
+static fs_node_t g_root;
+static uint32_t g_ready = 0;
+
+static void diskfs_close_impl(fs_node_t* node) {
+    if (!node) return;
+    struct diskfs_node* dn = (struct diskfs_node*)node;
+    kfree(dn);
+}
+
+static int diskfs_super_load(struct diskfs_super* sb) {
+    if (!sb) return -EINVAL;
+    uint8_t sec0[DISKFS_SECTOR];
+    uint8_t sec1[DISKFS_SECTOR];
+    if (ata_pio_read28(DISKFS_LBA_SUPER, sec0) < 0) return -EIO;
+    if (ata_pio_read28(DISKFS_LBA_SUPER2, sec1) < 0) return -EIO;
+
+    if (sizeof(*sb) > (size_t)(DISKFS_SECTOR * 2U)) return -EIO;
+    memcpy(sb, sec0, DISKFS_SECTOR);
+    if (sizeof(*sb) > DISKFS_SECTOR) {
+        memcpy(((uint8_t*)sb) + DISKFS_SECTOR, sec1, sizeof(*sb) - DISKFS_SECTOR);
+    }
+
+    if (sb->magic != DISKFS_MAGIC || sb->version != DISKFS_VERSION) {
+        memset(sb, 0, sizeof(*sb));
+        sb->magic = DISKFS_MAGIC;
+        sb->version = DISKFS_VERSION;
+        sb->file_count = 0;
+        sb->next_free_lba = DISKFS_LBA_DATA_START;
+
+        memset(sec0, 0, sizeof(sec0));
+        memset(sec1, 0, sizeof(sec1));
+        memcpy(sec0, sb, DISKFS_SECTOR);
+        if (sizeof(*sb) > DISKFS_SECTOR) {
+            memcpy(sec1, ((const uint8_t*)sb) + DISKFS_SECTOR, sizeof(*sb) - DISKFS_SECTOR);
+        }
+        if (ata_pio_write28(DISKFS_LBA_SUPER, sec0) < 0) return -EIO;
+        if (ata_pio_write28(DISKFS_LBA_SUPER2, sec1) < 0) return -EIO;
+    }
+
+    if (sb->file_count > DISKFS_MAX_FILES) sb->file_count = DISKFS_MAX_FILES;
+    if (sb->next_free_lba < DISKFS_LBA_DATA_START) sb->next_free_lba = DISKFS_LBA_DATA_START;
+    return 0;
+}
+
+static int diskfs_super_store(const struct diskfs_super* sb) {
+    if (!sb) return -EINVAL;
+    uint8_t sec0[DISKFS_SECTOR];
+    uint8_t sec1[DISKFS_SECTOR];
+    if (sizeof(*sb) > (size_t)(DISKFS_SECTOR * 2U)) return -EIO;
+
+    memset(sec0, 0, sizeof(sec0));
+    memset(sec1, 0, sizeof(sec1));
+    memcpy(sec0, sb, DISKFS_SECTOR);
+    if (sizeof(*sb) > DISKFS_SECTOR) {
+        memcpy(sec1, ((const uint8_t*)sb) + DISKFS_SECTOR, sizeof(*sb) - DISKFS_SECTOR);
+    }
+
+    if (ata_pio_write28(DISKFS_LBA_SUPER, sec0) < 0) return -EIO;
+    if (ata_pio_write28(DISKFS_LBA_SUPER2, sec1) < 0) return -EIO;
+    return 0;
+}
+
+static int diskfs_name_valid(const char* name) {
+    if (!name || name[0] == 0) return 0;
+    for (uint32_t i = 0; name[i] != 0; i++) {
+        if (name[i] == '/') return 0;
+        if (i + 1 >= DISKFS_NAME_MAX) return 0;
+    }
+    return 1;
+}
+
+static int diskfs_find(const struct diskfs_super* sb, const char* name) {
+    if (!sb || !name) return -1;
+    for (uint32_t i = 0; i < sb->file_count && i < DISKFS_MAX_FILES; i++) {
+        if (sb->files[i].name[0] != 0 && strcmp(sb->files[i].name, name) == 0) {
+            return (int)i;
+        }
+    }
+    return -1;
+}
+
+static int diskfs_alloc_file(struct diskfs_super* sb, const char* name, uint32_t cap_sectors, uint32_t* out_idx) {
+    if (!sb || !name || !out_idx) return -EINVAL;
+    if (sb->file_count >= DISKFS_MAX_FILES) return -ENOSPC;
+
+    uint32_t idx = sb->file_count;
+    sb->file_count++;
+
+    memset(&sb->files[idx], 0, sizeof(sb->files[idx]));
+    strcpy(sb->files[idx].name, name);
+    sb->files[idx].start_lba = sb->next_free_lba;
+    sb->files[idx].size_bytes = 0;
+    sb->files[idx].cap_sectors = cap_sectors ? cap_sectors : DISKFS_DEFAULT_CAP_SECTORS;
+
+    sb->next_free_lba += sb->files[idx].cap_sectors;
+
+    // Zero-init allocated sectors.
+    uint8_t zero[DISKFS_SECTOR];
+    memset(zero, 0, sizeof(zero));
+    for (uint32_t s = 0; s < sb->files[idx].cap_sectors; s++) {
+        (void)ata_pio_write28(sb->files[idx].start_lba + s, zero);
+    }
+
+    *out_idx = idx;
+    return 0;
+}
+
+static uint32_t diskfs_read_impl(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
+    if (!node || !buffer) return 0;
+    if (node->flags != FS_FILE) return 0;
+    if (!g_ready) return 0;
+
+    struct diskfs_node* dn = (struct diskfs_node*)node;
+    struct diskfs_super sb;
+    if (diskfs_super_load(&sb) < 0) return 0;
+    if (dn->idx >= sb.file_count || dn->idx >= DISKFS_MAX_FILES) return 0;
+
+    struct diskfs_dirent* de = &sb.files[dn->idx];
+    if (offset >= de->size_bytes) return 0;
+    if (offset + size > de->size_bytes) size = de->size_bytes - offset;
+    if (size == 0) return 0;
+
+    uint32_t total = 0;
+    while (total < size) {
+        uint32_t pos = offset + total;
+        uint32_t lba_off = pos / DISKFS_SECTOR;
+        uint32_t sec_off = pos % DISKFS_SECTOR;
+        uint32_t chunk = size - total;
+        if (chunk > (DISKFS_SECTOR - sec_off)) chunk = DISKFS_SECTOR - sec_off;
+        if (lba_off >= de->cap_sectors) break;
+
+        uint8_t sec[DISKFS_SECTOR];
+        if (ata_pio_read28(de->start_lba + lba_off, sec) < 0) break;
+        memcpy(buffer + total, sec + sec_off, chunk);
+        total += chunk;
+    }
+
+    return total;
+}
+
+static uint32_t diskfs_write_impl(fs_node_t* node, uint32_t offset, uint32_t size, const uint8_t* buffer) {
+    if (!node || !buffer) return 0;
+    if (node->flags != FS_FILE) return 0;
+    if (!g_ready) return 0;
+
+    struct diskfs_node* dn = (struct diskfs_node*)node;
+    struct diskfs_super sb;
+    if (diskfs_super_load(&sb) < 0) return 0;
+    if (dn->idx >= sb.file_count || dn->idx >= DISKFS_MAX_FILES) return 0;
+
+    struct diskfs_dirent* de = &sb.files[dn->idx];
+
+    uint64_t end = (uint64_t)offset + (uint64_t)size;
+    if (end > 0xFFFFFFFFULL) return 0;
+
+    uint32_t need_bytes = (uint32_t)end;
+    uint32_t need_sectors = (need_bytes + DISKFS_SECTOR - 1U) / DISKFS_SECTOR;
+
+    if (need_sectors > de->cap_sectors) {
+        // Grow by allocating a new extent at end, copy old contents.
+        uint32_t new_cap = de->cap_sectors;
+        while (new_cap < need_sectors) {
+            new_cap *= 2U;
+            if (new_cap == 0) return 0;
+        }
+
+        uint32_t new_start = sb.next_free_lba;
+        sb.next_free_lba += new_cap;
+
+        uint8_t sec[DISKFS_SECTOR];
+        for (uint32_t s = 0; s < new_cap; s++) {
+            memset(sec, 0, sizeof(sec));
+            if (s < de->cap_sectors) {
+                if (ata_pio_read28(de->start_lba + s, sec) < 0) {
+                    return 0;
+                }
+            }
+            (void)ata_pio_write28(new_start + s, sec);
+        }
+
+        de->start_lba = new_start;
+        de->cap_sectors = new_cap;
+    }
+
+    uint32_t total = 0;
+    while (total < size) {
+        uint32_t pos = offset + total;
+        uint32_t lba_off = pos / DISKFS_SECTOR;
+        uint32_t sec_off = pos % DISKFS_SECTOR;
+        uint32_t chunk = size - total;
+        if (chunk > (DISKFS_SECTOR - sec_off)) chunk = DISKFS_SECTOR - sec_off;
+        if (lba_off >= de->cap_sectors) break;
+
+        uint8_t sec[DISKFS_SECTOR];
+        if (sec_off != 0 || chunk != DISKFS_SECTOR) {
+            if (ata_pio_read28(de->start_lba + lba_off, sec) < 0) break;
+        } else {
+            memset(sec, 0, sizeof(sec));
+        }
+
+        memcpy(sec + sec_off, buffer + total, chunk);
+        if (ata_pio_write28(de->start_lba + lba_off, sec) < 0) break;
+
+        total += chunk;
+    }
+
+    if (offset + total > de->size_bytes) {
+        de->size_bytes = offset + total;
+    }
+
+    if (diskfs_super_store(&sb) < 0) return total;
+    node->length = de->size_bytes;
+    return total;
+}
+
+static struct fs_node* diskfs_root_finddir(struct fs_node* node, const char* name) {
+    (void)node;
+    if (!g_ready) return 0;
+    if (!name || name[0] == 0) return 0;
+    if (!diskfs_name_valid(name)) return 0;
+
+    struct diskfs_super sb;
+    if (diskfs_super_load(&sb) < 0) return 0;
+
+    int idx = diskfs_find(&sb, name);
+    if (idx < 0) return 0;
+
+    struct diskfs_node* dn = (struct diskfs_node*)kmalloc(sizeof(*dn));
+    if (!dn) return 0;
+    memset(dn, 0, sizeof(*dn));
+
+    strcpy(dn->vfs.name, name);
+    dn->vfs.flags = FS_FILE;
+    dn->vfs.inode = 100 + (uint32_t)idx;
+    dn->vfs.length = sb.files[idx].size_bytes;
+    dn->vfs.read = &diskfs_read_impl;
+    dn->vfs.write = &diskfs_write_impl;
+    dn->vfs.open = 0;
+    dn->vfs.close = &diskfs_close_impl;
+    dn->vfs.finddir = 0;
+    dn->idx = (uint32_t)idx;
+
+    return &dn->vfs;
+}
+
+int diskfs_open_file(const char* rel_path, uint32_t flags, fs_node_t** out_node) {
+    if (!out_node) return -EINVAL;
+    *out_node = 0;
+    if (!g_ready) return -ENODEV;
+    if (!diskfs_name_valid(rel_path)) return -EINVAL;
+
+    struct diskfs_super sb;
+    if (diskfs_super_load(&sb) < 0) return -EIO;
+
+    int idx = diskfs_find(&sb, rel_path);
+    if (idx < 0) {
+        if ((flags & 0x40U) == 0U) { // O_CREAT
+            return -ENOENT;
+        }
+        uint32_t new_idx = 0;
+        int rc = diskfs_alloc_file(&sb, rel_path, DISKFS_DEFAULT_CAP_SECTORS, &new_idx);
+        if (rc < 0) return rc;
+        if (diskfs_super_store(&sb) < 0) return -EIO;
+        idx = (int)new_idx;
+    }
+
+    if (idx < 0) return -ENOENT;
+
+    if ((flags & 0x200U) != 0U) { // O_TRUNC
+        sb.files[idx].size_bytes = 0;
+        if (diskfs_super_store(&sb) < 0) return -EIO;
+    }
+
+    fs_node_t* n = diskfs_root_finddir(&g_root, rel_path);
+    if (!n) return -EIO;
+    *out_node = n;
+    return 0;
+}
+
+fs_node_t* diskfs_create_root(void) {
+    if (!g_ready) {
+        if (ata_pio_init_primary_master() == 0) {
+            g_ready = 1;
+        } else {
+            g_ready = 0;
+        }
+
+        memset(&g_root, 0, sizeof(g_root));
+        strcpy(g_root.name, "disk");
+        g_root.flags = FS_DIRECTORY;
+        g_root.inode = 1;
+        g_root.length = 0;
+        g_root.read = 0;
+        g_root.write = 0;
+        g_root.open = 0;
+        g_root.close = 0;
+        g_root.finddir = &diskfs_root_finddir;
+
+        if (g_ready) {
+            struct diskfs_super sb;
+            (void)diskfs_super_load(&sb);
+        }
+    }
+
+    return g_ready ? &g_root : 0;
+}
index 601d859cfe2527cdc70fbae34ec5886ef7b9d1fc..e6fcf94d72fb44774626e92f005b0a7215b1e2aa 100644 (file)
@@ -10,6 +10,7 @@
 #include "tty.h"
 #include "pty.h"
 #include "persistfs.h"
+#include "diskfs.h"
 #include "uart_console.h"
 
 #include "hal/mm.h"
@@ -77,6 +78,11 @@ int init_start(const struct boot_info* bi) {
         (void)vfs_mount("/persist", persist);
     }
 
+    fs_node_t* disk = diskfs_create_root();
+    if (disk) {
+        (void)vfs_mount("/disk", disk);
+    }
+
     int user_ret = arch_platform_start_userspace(bi);
 
     if (bi && cmdline_has_token(bi->cmdline, "ring3")) {
index 8af6c2eb1363abd4031318b4deec0b26094d9f08..c25cbb32eee578011c66dd727d589006f2e5b563 100644 (file)
@@ -10,6 +10,7 @@
 #include "heap.h"
 #include "tty.h"
 #include "pty.h"
+#include "diskfs.h"
 
 #include "errno.h"
 #include "elf.h"
@@ -747,7 +748,6 @@ static int syscall_lseek_impl(int fd, int32_t offset, int whence) {
 }
 
 static int syscall_open_impl(const char* user_path, uint32_t flags) {
-    (void)flags;
     if (!user_path) return -EFAULT;
 
     char path[128];
@@ -762,8 +762,16 @@ static int syscall_open_impl(const char* user_path, uint32_t flags) {
         }
     }
 
-    fs_node_t* node = vfs_lookup(path);
-    if (!node) return -ENOENT;
+    fs_node_t* node = NULL;
+    if (path[0] == '/' && path[1] == 'd' && path[2] == 'i' && path[3] == 's' && path[4] == 'k' && path[5] == '/') {
+        const char* rel = path + 6;
+        if (rel[0] == 0) return -ENOENT;
+        int rc = diskfs_open_file(rel, flags, &node);
+        if (rc < 0) return rc;
+    } else {
+        node = vfs_lookup(path);
+        if (!node) return -ENOENT;
+    }
 
     struct file* f = (struct file*)kmalloc(sizeof(*f));
     if (!f) return -ENOMEM;
index 070c3ae4ea78e7d5e0fe098764be9f11f448ebf3..b2bed6b233c2e693a06693cc4995d8e5e8d92fbd 100644 (file)
@@ -109,6 +109,11 @@ enum {
     SEEK_END = 2,
 };
 
+enum {
+    O_CREAT = 0x40,
+    O_TRUNC = 0x200,
+};
+
 #define S_IFMT  0170000
 #define S_IFREG 0100000
 
@@ -1426,6 +1431,81 @@ void _start(void) {
         (void)sys_close(fd);
     }
 
+    // B2: on-disk general filesystem smoke (/disk)
+    {
+        int fd = sys_open("/disk/test", O_CREAT);
+        if (fd < 0) {
+            sys_write(1, "[init] /disk/test open failed\n",
+                      (uint32_t)(sizeof("[init] /disk/test open failed\n") - 1));
+            sys_exit(1);
+        }
+
+        char buf[16];
+        int rd = sys_read(fd, buf, sizeof(buf));
+        int prev = 0;
+        if (rd > 0) {
+            for (int i = 0; i < rd; i++) {
+                if (buf[i] < '0' || buf[i] > '9') break;
+                prev = prev * 10 + (buf[i] - '0');
+            }
+        }
+
+        (void)sys_close(fd);
+
+        fd = sys_open("/disk/test", O_CREAT | O_TRUNC);
+        if (fd < 0) {
+            sys_write(1, "[init] /disk/test open2 failed\n",
+                      (uint32_t)(sizeof("[init] /disk/test open2 failed\n") - 1));
+            sys_exit(1);
+        }
+
+        int next = prev + 1;
+        char out[16];
+        int n = 0;
+        int v = next;
+        if (v == 0) {
+            out[n++] = '0';
+        } else {
+            char tmp[16];
+            int t = 0;
+            while (v > 0 && t < (int)sizeof(tmp)) {
+                tmp[t++] = (char)('0' + (v % 10));
+                v /= 10;
+            }
+            while (t > 0) {
+                out[n++] = tmp[--t];
+            }
+        }
+
+        if (sys_write(fd, out, (uint32_t)n) != n) {
+            sys_write(1, "[init] /disk/test write failed\n",
+                      (uint32_t)(sizeof("[init] /disk/test write failed\n") - 1));
+            sys_exit(1);
+        }
+        (void)sys_close(fd);
+
+        fd = sys_open("/disk/test", 0);
+        if (fd < 0) {
+            sys_write(1, "[init] /disk/test open3 failed\n",
+                      (uint32_t)(sizeof("[init] /disk/test open3 failed\n") - 1));
+            sys_exit(1);
+        }
+        for (uint32_t i = 0; i < (uint32_t)sizeof(buf); i++) buf[i] = 0;
+        rd = sys_read(fd, buf, sizeof(buf));
+        (void)sys_close(fd);
+        if (rd != n || !memeq(buf, out, (uint32_t)n)) {
+            sys_write(1, "[init] /disk/test verify failed\n",
+                      (uint32_t)(sizeof("[init] /disk/test verify failed\n") - 1));
+            sys_exit(1);
+        }
+
+        sys_write(1, "[init] /disk/test prev=", (uint32_t)(sizeof("[init] /disk/test prev=") - 1));
+        write_int_dec(prev);
+        sys_write(1, " next=", (uint32_t)(sizeof(" next=") - 1));
+        write_int_dec(next);
+        sys_write(1, " OK\n", (uint32_t)(sizeof(" OK\n") - 1));
+    }
+
     enum { NCHILD = 100 };
     int children[NCHILD];
     for (int i = 0; i < NCHILD; i++) {