luajitos

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

diskfs.c (47686B)


      1 /* DiskFS - Simple Filesystem for ATA drives in LuajitOS */
      2 
      3 #include "diskfs.h"
      4 #include "ata.h"
      5 #include "fde.h"
      6 #include <string.h>
      7 #include <stdlib.h>
      8 #include <lua.h>
      9 #include <lauxlib.h>
     10 
     11 /* External FDE contexts from fde.c */
     12 extern fde_context_t fde_contexts[4];
     13 
     14 /* Check if FDE is active for a drive */
     15 static int fde_is_active(uint8_t bus, uint8_t drive) {
     16     int idx = bus * 2 + drive;
     17     if (idx < 0 || idx >= 4) return 0;
     18     return fde_contexts[idx].is_open;
     19 }
     20 
     21 /* Get FDE context for a drive */
     22 static fde_context_t* get_fde_context(uint8_t bus, uint8_t drive) {
     23     int idx = bus * 2 + drive;
     24     if (idx < 0 || idx >= 4) return NULL;
     25     return &fde_contexts[idx];
     26 }
     27 
     28 /* External terminal function */
     29 extern void terminal_writestring(const char* str);
     30 
     31 /* strdup implementation for bare metal */
     32 static char* diskfs_strdup(const char* s) {
     33     if (!s) return NULL;
     34     size_t len = strlen(s) + 1;
     35     char* dup = malloc(len);
     36     if (dup) {
     37         memcpy(dup, s, len);
     38     }
     39     return dup;
     40 }
     41 
     42 /* Mount table */
     43 static diskfs_mount_t mounts[DISKFS_MAX_MOUNTS];
     44 
     45 /* Sector buffer for I/O */
     46 static uint8_t sector_buffer[DISKFS_SECTOR_SIZE];
     47 
     48 /* Get mount index from bus/drive */
     49 static int get_mount_index(uint8_t bus, uint8_t drive) {
     50     return (bus * 2) + drive;
     51 }
     52 
     53 /* Read a sector - routes through FDE if encryption is active */
     54 static int read_sector(uint8_t bus, uint8_t drive, uint32_t sector, void* buffer) {
     55     if (fde_is_active(bus, drive)) {
     56         fde_context_t* ctx = get_fde_context(bus, drive);
     57         int result = fde_read_sector(ctx, sector, buffer);
     58         if (sector < 5) {
     59             char dbg[80];
     60             snprintf(dbg, sizeof(dbg), "[DISKFS] read_sector FDE: sector=%d result=%d\n", (int)sector, result);
     61             terminal_writestring(dbg);
     62         }
     63         return (result == FDE_OK) ? ATA_OK : ATA_ERR_IO;
     64     }
     65     return ata_read_sectors(bus, drive, sector, 1, buffer);
     66 }
     67 
     68 /* Write a sector - routes through FDE if encryption is active */
     69 static int write_sector(uint8_t bus, uint8_t drive, uint32_t sector, const void* buffer) {
     70     if (fde_is_active(bus, drive)) {
     71         fde_context_t* ctx = get_fde_context(bus, drive);
     72         if (sector < 5) {
     73             char dbg[80];
     74             snprintf(dbg, sizeof(dbg), "[DISKFS] write_sector FDE: sector=%d\n", (int)sector);
     75             terminal_writestring(dbg);
     76         }
     77         int result = fde_write_sector(ctx, sector, buffer);
     78         if (result != FDE_OK) {
     79             char dbg[80];
     80             snprintf(dbg, sizeof(dbg), "[DISKFS] write_sector FDE FAILED: sector=%d result=%d\n", (int)sector, result);
     81             terminal_writestring(dbg);
     82         }
     83         return (result == FDE_OK) ? ATA_OK : ATA_ERR_IO;
     84     }
     85     return ata_write_sectors(bus, drive, sector, 1, buffer);
     86 }
     87 
     88 /* Allocate a free sector */
     89 static int alloc_sector(uint8_t bus, uint8_t drive, uint32_t* sector_out) {
     90     diskfs_superblock_t sb;
     91 
     92     if (read_sector(bus, drive, DISKFS_SUPERBLOCK_SECTOR, &sb) != ATA_OK) {
     93         return DISKFS_ERR_IO;
     94     }
     95 
     96     if (sb.free_list_head == 0 || sb.free_sectors == 0) {
     97         return DISKFS_ERR_FULL;
     98     }
     99 
    100     *sector_out = sb.free_list_head;
    101 
    102     /* Read the free sector to get next in free list */
    103     uint8_t temp[DISKFS_SECTOR_SIZE];
    104     if (read_sector(bus, drive, sb.free_list_head, temp) != ATA_OK) {
    105         return DISKFS_ERR_IO;
    106     }
    107 
    108     /* Next free sector is stored at end of sector */
    109     uint32_t next_free = *(uint32_t*)(temp + DISKFS_SECTOR_SIZE - 4);
    110 
    111     /* Update superblock */
    112     sb.free_list_head = next_free;
    113     sb.free_sectors--;
    114 
    115     if (write_sector(bus, drive, DISKFS_SUPERBLOCK_SECTOR, &sb) != ATA_OK) {
    116         return DISKFS_ERR_IO;
    117     }
    118 
    119     /* Clear the allocated sector */
    120     memset(temp, 0, DISKFS_SECTOR_SIZE);
    121     if (write_sector(bus, drive, *sector_out, temp) != ATA_OK) {
    122         return DISKFS_ERR_IO;
    123     }
    124 
    125     return DISKFS_OK;
    126 }
    127 
    128 /* Free a sector back to free list */
    129 static int free_sector(uint8_t bus, uint8_t drive, uint32_t sector) {
    130     diskfs_superblock_t sb;
    131 
    132     if (read_sector(bus, drive, DISKFS_SUPERBLOCK_SECTOR, &sb) != ATA_OK) {
    133         return DISKFS_ERR_IO;
    134     }
    135 
    136     /* Read sector and set its next pointer to current free list head */
    137     uint8_t temp[DISKFS_SECTOR_SIZE];
    138     memset(temp, 0, DISKFS_SECTOR_SIZE);
    139     *(uint32_t*)(temp + DISKFS_SECTOR_SIZE - 4) = sb.free_list_head;
    140 
    141     if (write_sector(bus, drive, sector, temp) != ATA_OK) {
    142         return DISKFS_ERR_IO;
    143     }
    144 
    145     /* Update superblock */
    146     sb.free_list_head = sector;
    147     sb.free_sectors++;
    148 
    149     if (write_sector(bus, drive, DISKFS_SUPERBLOCK_SECTOR, &sb) != ATA_OK) {
    150         return DISKFS_ERR_IO;
    151     }
    152 
    153     return DISKFS_OK;
    154 }
    155 
    156 /* Find entry in directory by name */
    157 static int find_entry_in_dir(uint8_t bus, uint8_t drive, uint32_t dir_sector,
    158                              const char* name, uint32_t* entry_sector, uint32_t* entry_index) {
    159     uint32_t current = dir_sector;
    160 
    161     while (current != 0) {
    162         if (read_sector(bus, drive, current, sector_buffer) != ATA_OK) {
    163             return DISKFS_ERR_IO;
    164         }
    165 
    166         diskfs_entry_t* entries = (diskfs_entry_t*)sector_buffer;
    167         for (int i = 0; i < DISKFS_ENTRIES_PER_SECTOR; i++) {
    168             if (entries[i].type != DISKFS_TYPE_FREE) {
    169                 if (strcmp(entries[i].name, name) == 0) {
    170                     *entry_sector = current;
    171                     *entry_index = i;
    172                     return DISKFS_OK;
    173                 }
    174             }
    175         }
    176 
    177         /* Check for continuation sector (stored at end) */
    178         current = *(uint32_t*)(sector_buffer + DISKFS_SECTOR_SIZE - 4);
    179     }
    180 
    181     return DISKFS_ERR_NOT_FOUND;
    182 }
    183 
    184 /* Find a free entry slot in directory */
    185 static int find_free_entry(uint8_t bus, uint8_t drive, uint32_t dir_sector,
    186                            uint32_t* entry_sector, uint32_t* entry_index) {
    187     uint32_t current = dir_sector;
    188     uint32_t last = dir_sector;
    189 
    190     while (current != 0) {
    191         if (read_sector(bus, drive, current, sector_buffer) != ATA_OK) {
    192             return DISKFS_ERR_IO;
    193         }
    194 
    195         diskfs_entry_t* entries = (diskfs_entry_t*)sector_buffer;
    196         for (int i = 0; i < DISKFS_ENTRIES_PER_SECTOR; i++) {
    197             if (entries[i].type == DISKFS_TYPE_FREE) {
    198                 *entry_sector = current;
    199                 *entry_index = i;
    200                 return DISKFS_OK;
    201             }
    202         }
    203 
    204         last = current;
    205         current = *(uint32_t*)(sector_buffer + DISKFS_SECTOR_SIZE - 4);
    206     }
    207 
    208     /* No free entry found, allocate new directory sector */
    209     uint32_t new_sector;
    210     int result = alloc_sector(bus, drive, &new_sector);
    211     if (result != DISKFS_OK) {
    212         return result;
    213     }
    214 
    215     /* Link new sector to last sector */
    216     if (read_sector(bus, drive, last, sector_buffer) != ATA_OK) {
    217         return DISKFS_ERR_IO;
    218     }
    219     *(uint32_t*)(sector_buffer + DISKFS_SECTOR_SIZE - 4) = new_sector;
    220     if (write_sector(bus, drive, last, sector_buffer) != ATA_OK) {
    221         return DISKFS_ERR_IO;
    222     }
    223 
    224     *entry_sector = new_sector;
    225     *entry_index = 0;
    226     return DISKFS_OK;
    227 }
    228 
    229 /* Resolve path to directory entry */
    230 static int resolve_path(uint8_t bus, uint8_t drive, const char* path,
    231                         uint32_t* entry_sector, uint32_t* entry_index,
    232                         uint32_t* parent_sector) {
    233     if (!path || path[0] == '\0' || (path[0] == '/' && path[1] == '\0')) {
    234         /* Root directory - return special values */
    235         *entry_sector = DISKFS_ROOT_DIR_SECTOR;
    236         *entry_index = 0xFFFFFFFF;  /* Special marker for root */
    237         if (parent_sector) *parent_sector = 0;
    238         return DISKFS_OK;
    239     }
    240 
    241     /* Skip leading slash */
    242     if (path[0] == '/') path++;
    243 
    244     uint32_t current_dir = DISKFS_ROOT_DIR_SECTOR;
    245     char component[DISKFS_NAME_MAX + 1];
    246 
    247     while (*path) {
    248         /* Extract next path component */
    249         const char* slash = strchr(path, '/');
    250         size_t len;
    251         if (slash) {
    252             len = slash - path;
    253         } else {
    254             len = strlen(path);
    255         }
    256 
    257         if (len > DISKFS_NAME_MAX) {
    258             return DISKFS_ERR_NAME_TOO_LONG;
    259         }
    260 
    261         memcpy(component, path, len);
    262         component[len] = '\0';
    263 
    264         /* Find entry in current directory */
    265         uint32_t found_sector, found_index;
    266         int result = find_entry_in_dir(bus, drive, current_dir, component, &found_sector, &found_index);
    267         if (result != DISKFS_OK) {
    268             return result;
    269         }
    270 
    271         /* Read the entry */
    272         if (read_sector(bus, drive, found_sector, sector_buffer) != ATA_OK) {
    273             return DISKFS_ERR_IO;
    274         }
    275         diskfs_entry_t* entry = &((diskfs_entry_t*)sector_buffer)[found_index];
    276 
    277         /* Move to next component or return */
    278         path += len;
    279         if (*path == '/') path++;
    280 
    281         if (*path == '\0') {
    282             /* This is the final component */
    283             *entry_sector = found_sector;
    284             *entry_index = found_index;
    285             if (parent_sector) *parent_sector = current_dir;
    286             return DISKFS_OK;
    287         }
    288 
    289         /* Must be a directory to continue */
    290         if (entry->type != DISKFS_TYPE_DIR) {
    291             return DISKFS_ERR_NOT_DIR;
    292         }
    293 
    294         current_dir = entry->first_sector;
    295     }
    296 
    297     return DISKFS_ERR_NOT_FOUND;
    298 }
    299 
    300 /* Get parent directory path */
    301 static void get_parent_path(const char* path, char* parent, const char** basename) {
    302     const char* last_slash = strrchr(path, '/');
    303     if (!last_slash || last_slash == path) {
    304         strcpy(parent, "/");
    305         *basename = (last_slash == path) ? path + 1 : path;
    306     } else {
    307         size_t len = last_slash - path;
    308         memcpy(parent, path, len);
    309         parent[len] = '\0';
    310         *basename = last_slash + 1;
    311     }
    312 }
    313 
    314 /* ============================================================================
    315  * Public API
    316  * ========================================================================= */
    317 
    318 void diskfs_init(void) {
    319     memset(mounts, 0, sizeof(mounts));
    320 
    321     /* Try to mount any formatted drives */
    322     for (int bus = 0; bus < 2; bus++) {
    323         for (int drive = 0; drive < 2; drive++) {
    324             ata_drive_info_t* info = ata_get_drive_info(bus, drive);
    325             if (info && info->present && info->is_ata) {
    326                 diskfs_mount(bus, drive);
    327             }
    328         }
    329     }
    330 }
    331 
    332 int diskfs_format(uint8_t bus, uint8_t drive, const char* volume_name) {
    333     uint32_t total_sectors;
    334 
    335     /* Check if FDE is active - use FDE's virtual sector count */
    336     if (fde_is_active(bus, drive)) {
    337         fde_context_t* ctx = get_fde_context(bus, drive);
    338         total_sectors = ctx->total_sectors;
    339         terminal_writestring("[DISKFS] Format: FDE active, sectors=");
    340         char buf[20];
    341         snprintf(buf, sizeof(buf), "%d\n", (int)total_sectors);
    342         terminal_writestring(buf);
    343     } else {
    344         ata_drive_info_t* info = ata_get_drive_info(bus, drive);
    345         if (!info || !info->present || !info->is_ata) {
    346             return DISKFS_ERR_INVALID;
    347         }
    348         total_sectors = info->sectors;
    349         terminal_writestring("[DISKFS] Format: No FDE, using ATA directly\n");
    350     }
    351 
    352     if (total_sectors < 16) {
    353         terminal_writestring("[DISKFS] Format: Too few sectors\n");
    354         return DISKFS_ERR_INVALID;  /* Too small */
    355     }
    356 
    357     /* Create superblock */
    358     diskfs_superblock_t sb;
    359     memset(&sb, 0, sizeof(sb));
    360     sb.magic = DISKFS_MAGIC;
    361     sb.version = DISKFS_VERSION;
    362     sb.total_sectors = total_sectors;
    363     sb.free_sectors = total_sectors - 2;  /* Minus superblock and root dir */
    364     sb.root_dir_sector = DISKFS_ROOT_DIR_SECTOR;
    365     sb.free_list_head = 2;  /* First free sector after root dir */
    366 
    367     if (volume_name) {
    368         strncpy((char*)sb.volume_name, volume_name, 31);
    369         sb.volume_name[31] = '\0';
    370     }
    371 
    372     /* Write superblock */
    373     terminal_writestring("[DISKFS] Writing superblock...\n");
    374     if (write_sector(bus, drive, DISKFS_SUPERBLOCK_SECTOR, &sb) != ATA_OK) {
    375         terminal_writestring("[DISKFS] Superblock write failed!\n");
    376         return DISKFS_ERR_IO;
    377     }
    378 
    379     /* Initialize root directory (empty) */
    380     terminal_writestring("[DISKFS] Writing root directory...\n");
    381     memset(sector_buffer, 0, DISKFS_SECTOR_SIZE);
    382     if (write_sector(bus, drive, DISKFS_ROOT_DIR_SECTOR, sector_buffer) != ATA_OK) {
    383         terminal_writestring("[DISKFS] Root directory write failed!\n");
    384         return DISKFS_ERR_IO;
    385     }
    386 
    387     /* Quick format: initialize enough free list sectors for basic usage
    388      * Full format would take too long with encryption (246K+ writes).
    389      * Initialize first 100 sectors of free list for immediate use.
    390      */
    391     terminal_writestring("[DISKFS] Quick format - initializing first 100 free sectors\n");
    392     uint32_t init_sectors = (total_sectors > 102) ? 100 : (total_sectors - 2);
    393     for (uint32_t i = 2; i < 2 + init_sectors; i++) {
    394         memset(sector_buffer, 0, DISKFS_SECTOR_SIZE);
    395         /* Next pointer at end of sector */
    396         uint32_t next = (i + 1 < total_sectors) ? (i + 1) : 0;
    397         /* For sectors beyond our init range, point to 0 (end of list) */
    398         if (i + 1 >= 2 + init_sectors) {
    399             next = 0;
    400         }
    401         *(uint32_t*)(sector_buffer + DISKFS_SECTOR_SIZE - 4) = next;
    402         if (write_sector(bus, drive, i, sector_buffer) != ATA_OK) {
    403             char dbg[60];
    404             snprintf(dbg, sizeof(dbg), "[DISKFS] Free list init failed at sector %d\n", (int)i);
    405             terminal_writestring(dbg);
    406             return DISKFS_ERR_IO;
    407         }
    408     }
    409     terminal_writestring("[DISKFS] Free list initialized\n");
    410 
    411     /* Update superblock with correct free count */
    412     sb.free_sectors = init_sectors;
    413     sb.free_list_head = 2;
    414     if (write_sector(bus, drive, DISKFS_SUPERBLOCK_SECTOR, &sb) != ATA_OK) {
    415         terminal_writestring("[DISKFS] Failed to update superblock\n");
    416         return DISKFS_ERR_IO;
    417     }
    418 
    419     /* Flush cache */
    420     ata_flush(bus, drive);
    421 
    422     /* Mount the newly formatted drive */
    423     return diskfs_mount(bus, drive);
    424 }
    425 
    426 int diskfs_mount(uint8_t bus, uint8_t drive) {
    427     int idx = get_mount_index(bus, drive);
    428     if (idx >= DISKFS_MAX_MOUNTS) {
    429         return DISKFS_ERR_INVALID;
    430     }
    431 
    432     /* Check drive exists - either via FDE or ATA directly */
    433     if (!fde_is_active(bus, drive)) {
    434         ata_drive_info_t* info = ata_get_drive_info(bus, drive);
    435         if (!info || !info->present || !info->is_ata) {
    436             return DISKFS_ERR_INVALID;
    437         }
    438     }
    439 
    440     /* Read superblock (routes through FDE if active) */
    441     diskfs_superblock_t sb;
    442     if (read_sector(bus, drive, DISKFS_SUPERBLOCK_SECTOR, &sb) != ATA_OK) {
    443         return DISKFS_ERR_IO;
    444     }
    445 
    446     /* Check magic */
    447     if (sb.magic != DISKFS_MAGIC) {
    448         mounts[idx].mounted = 0;
    449         mounts[idx].formatted = 0;
    450         return DISKFS_ERR_NOT_FORMATTED;
    451     }
    452 
    453     /* Populate mount info */
    454     mounts[idx].bus = bus;
    455     mounts[idx].drive = drive;
    456     mounts[idx].mounted = 1;
    457     mounts[idx].formatted = 1;
    458     mounts[idx].total_sectors = sb.total_sectors;
    459     mounts[idx].free_sectors = sb.free_sectors;
    460     strncpy(mounts[idx].volume_name, (char*)sb.volume_name, 31);
    461     mounts[idx].volume_name[31] = '\0';
    462 
    463     return DISKFS_OK;
    464 }
    465 
    466 int diskfs_is_diskfs_path(const char* path, uint8_t* bus, uint8_t* drive, const char** subpath) {
    467     if (!path || strncmp(path, "/mnt/hd", 7) != 0) {
    468         return 0;
    469     }
    470 
    471     /* Parse /mnt/hdX or /mnt/hdXY */
    472     const char* p = path + 7;
    473     if (*p < '0' || *p > '3') {
    474         return 0;
    475     }
    476 
    477     int drive_num = *p - '0';
    478     *bus = drive_num / 2;
    479     *drive = drive_num % 2;
    480 
    481     p++;
    482     if (*p == '\0') {
    483         *subpath = "/";
    484     } else if (*p == '/') {
    485         *subpath = p;
    486     } else {
    487         return 0;
    488     }
    489 
    490     return 1;
    491 }
    492 
    493 int diskfs_exists(uint8_t bus, uint8_t drive, const char* path) {
    494     uint32_t entry_sector, entry_index;
    495     return resolve_path(bus, drive, path, &entry_sector, &entry_index, NULL) == DISKFS_OK;
    496 }
    497 
    498 int diskfs_is_dir(uint8_t bus, uint8_t drive, const char* path) {
    499     uint32_t entry_sector, entry_index;
    500     if (resolve_path(bus, drive, path, &entry_sector, &entry_index, NULL) != DISKFS_OK) {
    501         return 0;
    502     }
    503 
    504     /* Root is always a directory */
    505     if (entry_index == 0xFFFFFFFF) {
    506         return 1;
    507     }
    508 
    509     if (read_sector(bus, drive, entry_sector, sector_buffer) != ATA_OK) {
    510         return 0;
    511     }
    512 
    513     diskfs_entry_t* entry = &((diskfs_entry_t*)sector_buffer)[entry_index];
    514     return entry->type == DISKFS_TYPE_DIR;
    515 }
    516 
    517 int diskfs_is_file(uint8_t bus, uint8_t drive, const char* path) {
    518     uint32_t entry_sector, entry_index;
    519     if (resolve_path(bus, drive, path, &entry_sector, &entry_index, NULL) != DISKFS_OK) {
    520         return 0;
    521     }
    522 
    523     /* Root is not a file */
    524     if (entry_index == 0xFFFFFFFF) {
    525         return 0;
    526     }
    527 
    528     if (read_sector(bus, drive, entry_sector, sector_buffer) != ATA_OK) {
    529         return 0;
    530     }
    531 
    532     diskfs_entry_t* entry = &((diskfs_entry_t*)sector_buffer)[entry_index];
    533     return entry->type == DISKFS_TYPE_FILE;
    534 }
    535 
    536 int diskfs_mkdir(uint8_t bus, uint8_t drive, const char* path) {
    537     /* Check if already exists */
    538     if (diskfs_exists(bus, drive, path)) {
    539         return DISKFS_ERR_EXISTS;
    540     }
    541 
    542     /* Get parent directory */
    543     char parent_path[256];
    544     const char* name;
    545     get_parent_path(path, parent_path, &name);
    546 
    547     if (strlen(name) > DISKFS_NAME_MAX) {
    548         return DISKFS_ERR_NAME_TOO_LONG;
    549     }
    550 
    551     /* Find parent directory */
    552     uint32_t parent_entry_sector, parent_entry_index;
    553     int result = resolve_path(bus, drive, parent_path, &parent_entry_sector, &parent_entry_index, NULL);
    554     if (result != DISKFS_OK) {
    555         return result;
    556     }
    557 
    558     /* Get parent's content sector */
    559     uint32_t parent_content;
    560     if (parent_entry_index == 0xFFFFFFFF) {
    561         parent_content = DISKFS_ROOT_DIR_SECTOR;
    562     } else {
    563         if (read_sector(bus, drive, parent_entry_sector, sector_buffer) != ATA_OK) {
    564             return DISKFS_ERR_IO;
    565         }
    566         diskfs_entry_t* parent = &((diskfs_entry_t*)sector_buffer)[parent_entry_index];
    567         if (parent->type != DISKFS_TYPE_DIR) {
    568             return DISKFS_ERR_NOT_DIR;
    569         }
    570         parent_content = parent->first_sector;
    571     }
    572 
    573     /* Allocate sector for new directory content */
    574     uint32_t dir_sector;
    575     result = alloc_sector(bus, drive, &dir_sector);
    576     if (result != DISKFS_OK) {
    577         return result;
    578     }
    579 
    580     /* Find free entry in parent */
    581     uint32_t entry_sector, entry_index;
    582     result = find_free_entry(bus, drive, parent_content, &entry_sector, &entry_index);
    583     if (result != DISKFS_OK) {
    584         free_sector(bus, drive, dir_sector);
    585         return result;
    586     }
    587 
    588     /* Create the directory entry */
    589     if (read_sector(bus, drive, entry_sector, sector_buffer) != ATA_OK) {
    590         free_sector(bus, drive, dir_sector);
    591         return DISKFS_ERR_IO;
    592     }
    593 
    594     diskfs_entry_t* entry = &((diskfs_entry_t*)sector_buffer)[entry_index];
    595     memset(entry, 0, sizeof(*entry));
    596     strncpy(entry->name, name, DISKFS_NAME_MAX);
    597     entry->type = DISKFS_TYPE_DIR;
    598     entry->size = 0;
    599     entry->first_sector = dir_sector;
    600     entry->parent_sector = parent_content;
    601 
    602     if (write_sector(bus, drive, entry_sector, sector_buffer) != ATA_OK) {
    603         free_sector(bus, drive, dir_sector);
    604         return DISKFS_ERR_IO;
    605     }
    606 
    607     return DISKFS_OK;
    608 }
    609 
    610 diskfs_handle_t* diskfs_open(uint8_t bus, uint8_t drive, const char* path, const char* mode) {
    611     {
    612         char dbg[120];
    613         snprintf(dbg, sizeof(dbg), "[DISKFS] open: path=%s mode=%s\n", path ? path : "(null)", mode ? mode : "(null)");
    614         terminal_writestring(dbg);
    615     }
    616 
    617     if (!mode || (mode[0] != 'r' && mode[0] != 'w' && mode[0] != 'a')) {
    618         terminal_writestring("[DISKFS] open: invalid mode\n");
    619         return NULL;
    620     }
    621 
    622     diskfs_handle_t* handle = malloc(sizeof(diskfs_handle_t));
    623     if (!handle) {
    624         terminal_writestring("[DISKFS] open: malloc failed\n");
    625         return NULL;
    626     }
    627 
    628     memset(handle, 0, sizeof(*handle));
    629     handle->bus = bus;
    630     handle->drive = drive;
    631     handle->mode = mode[0];
    632 
    633     uint32_t entry_sector, entry_index, parent_sector;
    634     int result = resolve_path(bus, drive, path, &entry_sector, &entry_index, &parent_sector);
    635 
    636     if (mode[0] == 'r') {
    637         /* Read mode - file must exist */
    638         if (result != DISKFS_OK || entry_index == 0xFFFFFFFF) {
    639             free(handle);
    640             return NULL;
    641         }
    642 
    643         if (read_sector(bus, drive, entry_sector, sector_buffer) != ATA_OK) {
    644             free(handle);
    645             return NULL;
    646         }
    647 
    648         diskfs_entry_t* entry = &((diskfs_entry_t*)sector_buffer)[entry_index];
    649         if (entry->type != DISKFS_TYPE_FILE) {
    650             free(handle);
    651             return NULL;
    652         }
    653 
    654         handle->entry_sector = entry_sector;
    655         handle->entry_index = entry_index;
    656         handle->size = entry->size;
    657         handle->first_sector = entry->first_sector;
    658         handle->current_sector = entry->first_sector;
    659         handle->position = 0;
    660         handle->sector_offset = 0;
    661         handle->is_open = 1;
    662 
    663     } else {
    664         /* Write or append mode */
    665         if (result == DISKFS_OK && entry_index != 0xFFFFFFFF) {
    666             /* File exists */
    667             if (read_sector(bus, drive, entry_sector, sector_buffer) != ATA_OK) {
    668                 free(handle);
    669                 return NULL;
    670             }
    671 
    672             diskfs_entry_t* entry = &((diskfs_entry_t*)sector_buffer)[entry_index];
    673             if (entry->type != DISKFS_TYPE_FILE) {
    674                 free(handle);
    675                 return NULL;
    676             }
    677 
    678             handle->entry_sector = entry_sector;
    679             handle->entry_index = entry_index;
    680             handle->size = entry->size;
    681             handle->first_sector = entry->first_sector;
    682 
    683             if (mode[0] == 'w') {
    684                 /* Truncate - free all data sectors */
    685                 uint32_t sector = entry->first_sector;
    686                 while (sector != 0) {
    687                     if (read_sector(bus, drive, sector, sector_buffer) != ATA_OK) break;
    688                     uint32_t next = *(uint32_t*)(sector_buffer + DISKFS_SECTOR_SIZE - 4);
    689                     free_sector(bus, drive, sector);
    690                     sector = next;
    691                 }
    692 
    693                 /* Update entry */
    694                 if (read_sector(bus, drive, entry_sector, sector_buffer) != ATA_OK) {
    695                     free(handle);
    696                     return NULL;
    697                 }
    698                 entry = &((diskfs_entry_t*)sector_buffer)[entry_index];
    699                 entry->size = 0;
    700                 entry->first_sector = 0;
    701                 if (write_sector(bus, drive, entry_sector, sector_buffer) != ATA_OK) {
    702                     free(handle);
    703                     return NULL;
    704                 }
    705 
    706                 handle->size = 0;
    707                 handle->first_sector = 0;
    708                 handle->current_sector = 0;
    709                 handle->position = 0;
    710             } else {
    711                 /* Append - seek to end */
    712                 handle->position = handle->size;
    713                 /* Find last sector */
    714                 uint32_t sector = handle->first_sector;
    715                 uint32_t prev = 0;
    716                 while (sector != 0) {
    717                     if (read_sector(bus, drive, sector, sector_buffer) != ATA_OK) break;
    718                     prev = sector;
    719                     sector = *(uint32_t*)(sector_buffer + DISKFS_SECTOR_SIZE - 4);
    720                 }
    721                 handle->current_sector = prev;
    722                 handle->sector_offset = handle->size % DISKFS_DATA_PER_SECTOR;
    723             }
    724             handle->is_open = 1;
    725 
    726         } else {
    727             /* Create new file */
    728             char parent_path[256];
    729             const char* name;
    730             get_parent_path(path, parent_path, &name);
    731 
    732             if (strlen(name) > DISKFS_NAME_MAX) {
    733                 free(handle);
    734                 return NULL;
    735             }
    736 
    737             /* Find parent */
    738             result = resolve_path(bus, drive, parent_path, &entry_sector, &entry_index, NULL);
    739             if (result != DISKFS_OK) {
    740                 free(handle);
    741                 return NULL;
    742             }
    743 
    744             uint32_t parent_content;
    745             if (entry_index == 0xFFFFFFFF) {
    746                 parent_content = DISKFS_ROOT_DIR_SECTOR;
    747             } else {
    748                 if (read_sector(bus, drive, entry_sector, sector_buffer) != ATA_OK) {
    749                     free(handle);
    750                     return NULL;
    751                 }
    752                 diskfs_entry_t* parent = &((diskfs_entry_t*)sector_buffer)[entry_index];
    753                 if (parent->type != DISKFS_TYPE_DIR) {
    754                     free(handle);
    755                     return NULL;
    756                 }
    757                 parent_content = parent->first_sector;
    758             }
    759 
    760             /* Find free entry */
    761             uint32_t new_entry_sector, new_entry_index;
    762             result = find_free_entry(bus, drive, parent_content, &new_entry_sector, &new_entry_index);
    763             if (result != DISKFS_OK) {
    764                 free(handle);
    765                 return NULL;
    766             }
    767 
    768             /* Create entry */
    769             if (read_sector(bus, drive, new_entry_sector, sector_buffer) != ATA_OK) {
    770                 free(handle);
    771                 return NULL;
    772             }
    773 
    774             diskfs_entry_t* new_entry = &((diskfs_entry_t*)sector_buffer)[new_entry_index];
    775             memset(new_entry, 0, sizeof(*new_entry));
    776             strncpy(new_entry->name, name, DISKFS_NAME_MAX);
    777             new_entry->type = DISKFS_TYPE_FILE;
    778             new_entry->size = 0;
    779             new_entry->first_sector = 0;
    780             new_entry->parent_sector = parent_content;
    781 
    782             if (write_sector(bus, drive, new_entry_sector, sector_buffer) != ATA_OK) {
    783                 free(handle);
    784                 return NULL;
    785             }
    786 
    787             handle->entry_sector = new_entry_sector;
    788             handle->entry_index = new_entry_index;
    789             handle->size = 0;
    790             handle->first_sector = 0;
    791             handle->current_sector = 0;
    792             handle->position = 0;
    793             handle->sector_offset = 0;
    794             handle->is_open = 1;
    795         }
    796     }
    797 
    798     return handle;
    799 }
    800 
    801 int diskfs_read(diskfs_handle_t* handle, void* buffer, uint32_t size, uint32_t* bytes_read) {
    802     if (!handle || !handle->is_open || handle->mode != 'r') {
    803         return DISKFS_ERR_INVALID;
    804     }
    805 
    806     *bytes_read = 0;
    807     uint8_t* out = (uint8_t*)buffer;
    808 
    809     while (size > 0 && handle->position < handle->size) {
    810         if (handle->current_sector == 0) {
    811             break;
    812         }
    813 
    814         /* Read current sector */
    815         if (read_sector(handle->bus, handle->drive, handle->current_sector, sector_buffer) != ATA_OK) {
    816             return DISKFS_ERR_IO;
    817         }
    818 
    819         /* How much can we read from this sector? */
    820         uint32_t available = DISKFS_DATA_PER_SECTOR - handle->sector_offset;
    821         uint32_t remaining_in_file = handle->size - handle->position;
    822         uint32_t to_read = size;
    823         if (to_read > available) to_read = available;
    824         if (to_read > remaining_in_file) to_read = remaining_in_file;
    825 
    826         memcpy(out, sector_buffer + handle->sector_offset, to_read);
    827 
    828         out += to_read;
    829         *bytes_read += to_read;
    830         handle->position += to_read;
    831         handle->sector_offset += to_read;
    832         size -= to_read;
    833 
    834         /* Move to next sector if needed */
    835         if (handle->sector_offset >= DISKFS_DATA_PER_SECTOR) {
    836             handle->current_sector = *(uint32_t*)(sector_buffer + DISKFS_SECTOR_SIZE - 4);
    837             handle->sector_offset = 0;
    838         }
    839     }
    840 
    841     return DISKFS_OK;
    842 }
    843 
    844 int diskfs_write(diskfs_handle_t* handle, const void* data, uint32_t size) {
    845     {
    846         char dbg[80];
    847         snprintf(dbg, sizeof(dbg), "[DISKFS] write: size=%d\n", (int)size);
    848         terminal_writestring(dbg);
    849     }
    850 
    851     if (!handle || !handle->is_open || handle->mode == 'r') {
    852         terminal_writestring("[DISKFS] write: invalid handle/mode\n");
    853         return DISKFS_ERR_INVALID;
    854     }
    855 
    856     const uint8_t* in = (const uint8_t*)data;
    857 
    858     while (size > 0) {
    859         /* Allocate sector if needed */
    860         if (handle->current_sector == 0) {
    861             uint32_t new_sector;
    862             int result = alloc_sector(handle->bus, handle->drive, &new_sector);
    863             if (result != DISKFS_OK) {
    864                 return result;
    865             }
    866 
    867             if (handle->first_sector == 0) {
    868                 handle->first_sector = new_sector;
    869 
    870                 /* Update entry */
    871                 if (read_sector(handle->bus, handle->drive, handle->entry_sector, sector_buffer) != ATA_OK) {
    872                     return DISKFS_ERR_IO;
    873                 }
    874                 diskfs_entry_t* entry = &((diskfs_entry_t*)sector_buffer)[handle->entry_index];
    875                 entry->first_sector = new_sector;
    876                 if (write_sector(handle->bus, handle->drive, handle->entry_sector, sector_buffer) != ATA_OK) {
    877                     return DISKFS_ERR_IO;
    878                 }
    879             }
    880 
    881             handle->current_sector = new_sector;
    882             handle->sector_offset = 0;
    883         }
    884 
    885         /* Read current sector */
    886         if (read_sector(handle->bus, handle->drive, handle->current_sector, sector_buffer) != ATA_OK) {
    887             return DISKFS_ERR_IO;
    888         }
    889 
    890         /* How much can we write to this sector? */
    891         uint32_t available = DISKFS_DATA_PER_SECTOR - handle->sector_offset;
    892         uint32_t to_write = (size < available) ? size : available;
    893 
    894         memcpy(sector_buffer + handle->sector_offset, in, to_write);
    895 
    896         /* Check if we need to link to a new sector */
    897         if (handle->sector_offset + to_write >= DISKFS_DATA_PER_SECTOR) {
    898             uint32_t next = *(uint32_t*)(sector_buffer + DISKFS_SECTOR_SIZE - 4);
    899             if (next == 0 && size > to_write) {
    900                 /* Need to allocate next sector */
    901                 int result = alloc_sector(handle->bus, handle->drive, &next);
    902                 if (result != DISKFS_OK) {
    903                     return result;
    904                 }
    905                 *(uint32_t*)(sector_buffer + DISKFS_SECTOR_SIZE - 4) = next;
    906             }
    907         }
    908 
    909         if (write_sector(handle->bus, handle->drive, handle->current_sector, sector_buffer) != ATA_OK) {
    910             return DISKFS_ERR_IO;
    911         }
    912 
    913         in += to_write;
    914         handle->position += to_write;
    915         handle->sector_offset += to_write;
    916         size -= to_write;
    917 
    918         if (handle->position > handle->size) {
    919             handle->size = handle->position;
    920         }
    921 
    922         /* Move to next sector if needed */
    923         if (handle->sector_offset >= DISKFS_DATA_PER_SECTOR) {
    924             handle->current_sector = *(uint32_t*)(sector_buffer + DISKFS_SECTOR_SIZE - 4);
    925             handle->sector_offset = 0;
    926         }
    927     }
    928 
    929     /* Update file size in directory entry */
    930     if (read_sector(handle->bus, handle->drive, handle->entry_sector, sector_buffer) != ATA_OK) {
    931         return DISKFS_ERR_IO;
    932     }
    933     diskfs_entry_t* entry = &((diskfs_entry_t*)sector_buffer)[handle->entry_index];
    934     entry->size = handle->size;
    935     if (write_sector(handle->bus, handle->drive, handle->entry_sector, sector_buffer) != ATA_OK) {
    936         return DISKFS_ERR_IO;
    937     }
    938 
    939     return DISKFS_OK;
    940 }
    941 
    942 void diskfs_close(diskfs_handle_t* handle) {
    943     if (handle) {
    944         if (handle->is_open && handle->mode != 'r') {
    945             ata_flush(handle->bus, handle->drive);
    946         }
    947         free(handle);
    948     }
    949 }
    950 
    951 int diskfs_remove(uint8_t bus, uint8_t drive, const char* path) {
    952     uint32_t entry_sector, entry_index, parent_sector;
    953     int result = resolve_path(bus, drive, path, &entry_sector, &entry_index, &parent_sector);
    954     if (result != DISKFS_OK) {
    955         return result;
    956     }
    957 
    958     if (entry_index == 0xFFFFFFFF) {
    959         return DISKFS_ERR_INVALID;  /* Can't remove root */
    960     }
    961 
    962     if (read_sector(bus, drive, entry_sector, sector_buffer) != ATA_OK) {
    963         return DISKFS_ERR_IO;
    964     }
    965 
    966     diskfs_entry_t* entry = &((diskfs_entry_t*)sector_buffer)[entry_index];
    967 
    968     if (entry->type == DISKFS_TYPE_DIR) {
    969         /* Check if directory is empty */
    970         uint32_t dir_sector = entry->first_sector;
    971         if (dir_sector != 0) {
    972             uint8_t temp[DISKFS_SECTOR_SIZE];
    973             if (read_sector(bus, drive, dir_sector, temp) != ATA_OK) {
    974                 return DISKFS_ERR_IO;
    975             }
    976             diskfs_entry_t* entries = (diskfs_entry_t*)temp;
    977             for (int i = 0; i < DISKFS_ENTRIES_PER_SECTOR; i++) {
    978                 if (entries[i].type != DISKFS_TYPE_FREE) {
    979                     return DISKFS_ERR_NOT_EMPTY;
    980                 }
    981             }
    982             /* Free directory sector */
    983             free_sector(bus, drive, dir_sector);
    984         }
    985     } else if (entry->type == DISKFS_TYPE_FILE) {
    986         /* Free all data sectors */
    987         uint32_t sector = entry->first_sector;
    988         while (sector != 0) {
    989             uint8_t temp[DISKFS_SECTOR_SIZE];
    990             if (read_sector(bus, drive, sector, temp) != ATA_OK) break;
    991             uint32_t next = *(uint32_t*)(temp + DISKFS_SECTOR_SIZE - 4);
    992             free_sector(bus, drive, sector);
    993             sector = next;
    994         }
    995     }
    996 
    997     /* Clear the entry */
    998     if (read_sector(bus, drive, entry_sector, sector_buffer) != ATA_OK) {
    999         return DISKFS_ERR_IO;
   1000     }
   1001     entry = &((diskfs_entry_t*)sector_buffer)[entry_index];
   1002     memset(entry, 0, sizeof(*entry));
   1003     if (write_sector(bus, drive, entry_sector, sector_buffer) != ATA_OK) {
   1004         return DISKFS_ERR_IO;
   1005     }
   1006 
   1007     return DISKFS_OK;
   1008 }
   1009 
   1010 int diskfs_list(uint8_t bus, uint8_t drive, const char* path,
   1011                 char*** names, uint32_t** types, uint32_t* count) {
   1012     {
   1013         char dbg[100];
   1014         snprintf(dbg, sizeof(dbg), "[DISKFS] list: bus=%d drive=%d path=%s\n", bus, drive, path ? path : "(null)");
   1015         terminal_writestring(dbg);
   1016     }
   1017 
   1018     uint32_t entry_sector, entry_index;
   1019     int result = resolve_path(bus, drive, path, &entry_sector, &entry_index, NULL);
   1020     if (result != DISKFS_OK) {
   1021         char dbg[60];
   1022         snprintf(dbg, sizeof(dbg), "[DISKFS] list: resolve_path failed=%d\n", result);
   1023         terminal_writestring(dbg);
   1024         return result;
   1025     }
   1026 
   1027     uint32_t dir_sector;
   1028     if (entry_index == 0xFFFFFFFF) {
   1029         dir_sector = DISKFS_ROOT_DIR_SECTOR;
   1030         terminal_writestring("[DISKFS] list: using root dir sector\n");
   1031     } else {
   1032         if (read_sector(bus, drive, entry_sector, sector_buffer) != ATA_OK) {
   1033             return DISKFS_ERR_IO;
   1034         }
   1035         diskfs_entry_t* entry = &((diskfs_entry_t*)sector_buffer)[entry_index];
   1036         if (entry->type != DISKFS_TYPE_DIR) {
   1037             return DISKFS_ERR_NOT_DIR;
   1038         }
   1039         dir_sector = entry->first_sector;
   1040     }
   1041 
   1042     {
   1043         char dbg[60];
   1044         snprintf(dbg, sizeof(dbg), "[DISKFS] list: dir_sector=%d\n", (int)dir_sector);
   1045         terminal_writestring(dbg);
   1046     }
   1047 
   1048     /* Count entries first */
   1049     uint32_t total = 0;
   1050     uint32_t current = dir_sector;
   1051     while (current != 0) {
   1052         if (read_sector(bus, drive, current, sector_buffer) != ATA_OK) {
   1053             terminal_writestring("[DISKFS] list: read_sector failed during count\n");
   1054             return DISKFS_ERR_IO;
   1055         }
   1056         diskfs_entry_t* entries = (diskfs_entry_t*)sector_buffer;
   1057         for (int i = 0; i < DISKFS_ENTRIES_PER_SECTOR; i++) {
   1058             if (entries[i].type != DISKFS_TYPE_FREE) {
   1059                 total++;
   1060             }
   1061         }
   1062         current = *(uint32_t*)(sector_buffer + DISKFS_SECTOR_SIZE - 4);
   1063     }
   1064 
   1065     {
   1066         char dbg[60];
   1067         snprintf(dbg, sizeof(dbg), "[DISKFS] list: total entries=%d\n", (int)total);
   1068         terminal_writestring(dbg);
   1069     }
   1070 
   1071     *count = total;
   1072     if (total == 0) {
   1073         *names = NULL;
   1074         *types = NULL;
   1075         return DISKFS_OK;
   1076     }
   1077 
   1078     *names = malloc(total * sizeof(char*));
   1079     *types = malloc(total * sizeof(uint32_t));
   1080     if (!*names || !*types) {
   1081         free(*names);
   1082         free(*types);
   1083         return DISKFS_ERR_IO;
   1084     }
   1085 
   1086     /* Collect entries */
   1087     uint32_t idx = 0;
   1088     current = dir_sector;
   1089     while (current != 0 && idx < total) {
   1090         if (read_sector(bus, drive, current, sector_buffer) != ATA_OK) {
   1091             break;
   1092         }
   1093         diskfs_entry_t* entries = (diskfs_entry_t*)sector_buffer;
   1094         for (int i = 0; i < DISKFS_ENTRIES_PER_SECTOR && idx < total; i++) {
   1095             if (entries[i].type != DISKFS_TYPE_FREE) {
   1096                 (*names)[idx] = diskfs_strdup(entries[i].name);
   1097                 (*types)[idx] = entries[i].type;
   1098                 idx++;
   1099             }
   1100         }
   1101         current = *(uint32_t*)(sector_buffer + DISKFS_SECTOR_SIZE - 4);
   1102     }
   1103 
   1104     return DISKFS_OK;
   1105 }
   1106 
   1107 void diskfs_free_list(char** names, uint32_t* types, uint32_t count) {
   1108     if (names) {
   1109         for (uint32_t i = 0; i < count; i++) {
   1110             free(names[i]);
   1111         }
   1112         free(names);
   1113     }
   1114     free(types);
   1115 }
   1116 
   1117 int diskfs_get_size(uint8_t bus, uint8_t drive, const char* path, uint32_t* size) {
   1118     uint32_t entry_sector, entry_index;
   1119     int result = resolve_path(bus, drive, path, &entry_sector, &entry_index, NULL);
   1120     if (result != DISKFS_OK) {
   1121         return result;
   1122     }
   1123 
   1124     if (entry_index == 0xFFFFFFFF) {
   1125         *size = 0;  /* Root directory */
   1126         return DISKFS_OK;
   1127     }
   1128 
   1129     if (read_sector(bus, drive, entry_sector, sector_buffer) != ATA_OK) {
   1130         return DISKFS_ERR_IO;
   1131     }
   1132 
   1133     diskfs_entry_t* entry = &((diskfs_entry_t*)sector_buffer)[entry_index];
   1134     *size = entry->size;
   1135     return DISKFS_OK;
   1136 }
   1137 
   1138 diskfs_mount_t* diskfs_get_mount(uint8_t bus, uint8_t drive) {
   1139     int idx = get_mount_index(bus, drive);
   1140     if (idx >= DISKFS_MAX_MOUNTS) return NULL;
   1141     return &mounts[idx];
   1142 }
   1143 
   1144 const char* diskfs_error_string(int error) {
   1145     switch (error) {
   1146         case DISKFS_OK: return "Success";
   1147         case DISKFS_ERR_NOT_FORMATTED: return "Disk not formatted";
   1148         case DISKFS_ERR_IO: return "I/O error";
   1149         case DISKFS_ERR_NOT_FOUND: return "Not found";
   1150         case DISKFS_ERR_EXISTS: return "Already exists";
   1151         case DISKFS_ERR_FULL: return "Disk full";
   1152         case DISKFS_ERR_NOT_DIR: return "Not a directory";
   1153         case DISKFS_ERR_NOT_FILE: return "Not a file";
   1154         case DISKFS_ERR_INVALID: return "Invalid parameter";
   1155         case DISKFS_ERR_NOT_EMPTY: return "Directory not empty";
   1156         case DISKFS_ERR_NAME_TOO_LONG: return "Name too long";
   1157         default: return "Unknown error";
   1158     }
   1159 }
   1160 
   1161 /* ============================================================================
   1162  * Lua Bindings
   1163  * ========================================================================= */
   1164 
   1165 /* diskfs.format(bus, drive, volume_name) */
   1166 static int lua_diskfs_format(lua_State* L) {
   1167     int bus = luaL_checkinteger(L, 1);
   1168     int drive = luaL_checkinteger(L, 2);
   1169     const char* name = luaL_optstring(L, 3, "DISK");
   1170 
   1171     int result = diskfs_format(bus, drive, name);
   1172     if (result != DISKFS_OK) {
   1173         lua_pushnil(L);
   1174         lua_pushstring(L, diskfs_error_string(result));
   1175         return 2;
   1176     }
   1177 
   1178     lua_pushboolean(L, 1);
   1179     return 1;
   1180 }
   1181 
   1182 /* diskfs.exists(path) - path like "/mnt/hd0/dir/file.txt" */
   1183 static int lua_diskfs_exists(lua_State* L) {
   1184     const char* path = luaL_checkstring(L, 1);
   1185 
   1186     uint8_t bus, drive;
   1187     const char* subpath;
   1188     if (!diskfs_is_diskfs_path(path, &bus, &drive, &subpath)) {
   1189         lua_pushboolean(L, 0);
   1190         return 1;
   1191     }
   1192 
   1193     lua_pushboolean(L, diskfs_exists(bus, drive, subpath));
   1194     return 1;
   1195 }
   1196 
   1197 /* diskfs.isdir(path) */
   1198 static int lua_diskfs_isdir(lua_State* L) {
   1199     const char* path = luaL_checkstring(L, 1);
   1200 
   1201     uint8_t bus, drive;
   1202     const char* subpath;
   1203     if (!diskfs_is_diskfs_path(path, &bus, &drive, &subpath)) {
   1204         lua_pushboolean(L, 0);
   1205         return 1;
   1206     }
   1207 
   1208     lua_pushboolean(L, diskfs_is_dir(bus, drive, subpath));
   1209     return 1;
   1210 }
   1211 
   1212 /* diskfs.isfile(path) */
   1213 static int lua_diskfs_isfile(lua_State* L) {
   1214     const char* path = luaL_checkstring(L, 1);
   1215 
   1216     uint8_t bus, drive;
   1217     const char* subpath;
   1218     if (!diskfs_is_diskfs_path(path, &bus, &drive, &subpath)) {
   1219         lua_pushboolean(L, 0);
   1220         return 1;
   1221     }
   1222 
   1223     lua_pushboolean(L, diskfs_is_file(bus, drive, subpath));
   1224     return 1;
   1225 }
   1226 
   1227 /* diskfs.mkdir(path) */
   1228 static int lua_diskfs_mkdir(lua_State* L) {
   1229     const char* path = luaL_checkstring(L, 1);
   1230 
   1231     uint8_t bus, drive;
   1232     const char* subpath;
   1233     if (!diskfs_is_diskfs_path(path, &bus, &drive, &subpath)) {
   1234         lua_pushnil(L);
   1235         lua_pushstring(L, "Invalid path");
   1236         return 2;
   1237     }
   1238 
   1239     int result = diskfs_mkdir(bus, drive, subpath);
   1240     if (result != DISKFS_OK) {
   1241         lua_pushnil(L);
   1242         lua_pushstring(L, diskfs_error_string(result));
   1243         return 2;
   1244     }
   1245 
   1246     lua_pushboolean(L, 1);
   1247     return 1;
   1248 }
   1249 
   1250 /* diskfs.open(path, mode) -> handle */
   1251 static int lua_diskfs_open(lua_State* L) {
   1252     const char* path = luaL_checkstring(L, 1);
   1253     const char* mode = luaL_optstring(L, 2, "r");
   1254 
   1255     uint8_t bus, drive;
   1256     const char* subpath;
   1257     if (!diskfs_is_diskfs_path(path, &bus, &drive, &subpath)) {
   1258         lua_pushnil(L);
   1259         lua_pushstring(L, "Invalid path");
   1260         return 2;
   1261     }
   1262 
   1263     diskfs_handle_t* handle = diskfs_open(bus, drive, subpath, mode);
   1264     if (!handle) {
   1265         lua_pushnil(L);
   1266         lua_pushstring(L, "Failed to open file");
   1267         return 2;
   1268     }
   1269 
   1270     lua_pushlightuserdata(L, handle);
   1271     return 1;
   1272 }
   1273 
   1274 /* diskfs.read(handle) -> data */
   1275 static int lua_diskfs_read(lua_State* L) {
   1276     diskfs_handle_t* handle = lua_touserdata(L, 1);
   1277     if (!handle || !handle->is_open) {
   1278         lua_pushnil(L);
   1279         return 1;
   1280     }
   1281 
   1282     /* Read entire file */
   1283     uint32_t size = handle->size;
   1284     if (size == 0) {
   1285         lua_pushstring(L, "");
   1286         return 1;
   1287     }
   1288 
   1289     char* buffer = malloc(size);
   1290     if (!buffer) {
   1291         lua_pushnil(L);
   1292         return 1;
   1293     }
   1294 
   1295     uint32_t bytes_read;
   1296     int result = diskfs_read(handle, buffer, size, &bytes_read);
   1297     if (result != DISKFS_OK) {
   1298         free(buffer);
   1299         lua_pushnil(L);
   1300         return 1;
   1301     }
   1302 
   1303     lua_pushlstring(L, buffer, bytes_read);
   1304     free(buffer);
   1305     return 1;
   1306 }
   1307 
   1308 /* diskfs.write(handle, data) */
   1309 static int lua_diskfs_write(lua_State* L) {
   1310     diskfs_handle_t* handle = lua_touserdata(L, 1);
   1311     size_t len;
   1312     const char* data = luaL_checklstring(L, 2, &len);
   1313 
   1314     if (!handle || !handle->is_open) {
   1315         lua_pushnil(L);
   1316         lua_pushstring(L, "Invalid handle");
   1317         return 2;
   1318     }
   1319 
   1320     int result = diskfs_write(handle, data, len);
   1321     if (result != DISKFS_OK) {
   1322         lua_pushnil(L);
   1323         lua_pushstring(L, diskfs_error_string(result));
   1324         return 2;
   1325     }
   1326 
   1327     lua_pushboolean(L, 1);
   1328     return 1;
   1329 }
   1330 
   1331 /* diskfs.close(handle) */
   1332 static int lua_diskfs_close(lua_State* L) {
   1333     diskfs_handle_t* handle = lua_touserdata(L, 1);
   1334     diskfs_close(handle);
   1335     return 0;
   1336 }
   1337 
   1338 /* diskfs.remove(path) */
   1339 static int lua_diskfs_remove(lua_State* L) {
   1340     const char* path = luaL_checkstring(L, 1);
   1341 
   1342     uint8_t bus, drive;
   1343     const char* subpath;
   1344     if (!diskfs_is_diskfs_path(path, &bus, &drive, &subpath)) {
   1345         lua_pushnil(L);
   1346         lua_pushstring(L, "Invalid path");
   1347         return 2;
   1348     }
   1349 
   1350     int result = diskfs_remove(bus, drive, subpath);
   1351     if (result != DISKFS_OK) {
   1352         lua_pushnil(L);
   1353         lua_pushstring(L, diskfs_error_string(result));
   1354         return 2;
   1355     }
   1356 
   1357     lua_pushboolean(L, 1);
   1358     return 1;
   1359 }
   1360 
   1361 /* diskfs.list(path) -> {items} */
   1362 static int lua_diskfs_list(lua_State* L) {
   1363     const char* path = luaL_checkstring(L, 1);
   1364 
   1365     uint8_t bus, drive;
   1366     const char* subpath;
   1367     if (!diskfs_is_diskfs_path(path, &bus, &drive, &subpath)) {
   1368         lua_pushnil(L);
   1369         lua_pushstring(L, "Invalid path");
   1370         return 2;
   1371     }
   1372 
   1373     char** names;
   1374     uint32_t* types;
   1375     uint32_t count;
   1376 
   1377     int result = diskfs_list(bus, drive, subpath, &names, &types, &count);
   1378     if (result != DISKFS_OK) {
   1379         lua_pushnil(L);
   1380         lua_pushstring(L, diskfs_error_string(result));
   1381         return 2;
   1382     }
   1383 
   1384     lua_newtable(L);
   1385     for (uint32_t i = 0; i < count; i++) {
   1386         lua_newtable(L);
   1387         lua_pushstring(L, names[i]);
   1388         lua_setfield(L, -2, "name");
   1389         lua_pushstring(L, types[i] == DISKFS_TYPE_DIR ? "dir" : "file");
   1390         lua_setfield(L, -2, "type");
   1391         lua_rawseti(L, -2, i + 1);
   1392     }
   1393 
   1394     diskfs_free_list(names, types, count);
   1395     return 1;
   1396 }
   1397 
   1398 /* diskfs.getinfo(path) -> {size, type} */
   1399 static int lua_diskfs_getinfo(lua_State* L) {
   1400     const char* path = luaL_checkstring(L, 1);
   1401 
   1402     uint8_t bus, drive;
   1403     const char* subpath;
   1404     if (!diskfs_is_diskfs_path(path, &bus, &drive, &subpath)) {
   1405         lua_pushnil(L);
   1406         return 1;
   1407     }
   1408 
   1409     if (!diskfs_exists(bus, drive, subpath)) {
   1410         lua_pushnil(L);
   1411         return 1;
   1412     }
   1413 
   1414     lua_newtable(L);
   1415 
   1416     if (diskfs_is_dir(bus, drive, subpath)) {
   1417         lua_pushstring(L, "dir");
   1418     } else {
   1419         lua_pushstring(L, "file");
   1420     }
   1421     lua_setfield(L, -2, "type");
   1422 
   1423     uint32_t size;
   1424     if (diskfs_get_size(bus, drive, subpath, &size) == DISKFS_OK) {
   1425         lua_pushinteger(L, size);
   1426         lua_setfield(L, -2, "size");
   1427     }
   1428 
   1429     return 1;
   1430 }
   1431 
   1432 /* diskfs.getMounts() -> {{bus, drive, mounted, formatted, volume_name}, ...} */
   1433 static int lua_diskfs_getmounts(lua_State* L) {
   1434     lua_newtable(L);
   1435     int idx = 1;
   1436 
   1437     for (int bus = 0; bus < 2; bus++) {
   1438         for (int drive = 0; drive < 2; drive++) {
   1439             ata_drive_info_t* info = ata_get_drive_info(bus, drive);
   1440             if (info && info->present && info->is_ata) {
   1441                 diskfs_mount_t* mount = diskfs_get_mount(bus, drive);
   1442 
   1443                 lua_newtable(L);
   1444                 lua_pushinteger(L, bus);
   1445                 lua_setfield(L, -2, "bus");
   1446                 lua_pushinteger(L, drive);
   1447                 lua_setfield(L, -2, "drive");
   1448                 lua_pushstring(L, info->model);
   1449                 lua_setfield(L, -2, "model");
   1450                 lua_pushinteger(L, info->sectors);
   1451                 lua_setfield(L, -2, "sectors");
   1452                 lua_pushboolean(L, mount && mount->formatted);
   1453                 lua_setfield(L, -2, "formatted");
   1454                 if (mount && mount->formatted) {
   1455                     lua_pushstring(L, mount->volume_name);
   1456                     lua_setfield(L, -2, "volume_name");
   1457                     lua_pushinteger(L, mount->free_sectors);
   1458                     lua_setfield(L, -2, "free_sectors");
   1459                 }
   1460 
   1461                 /* Add FDE encryption info */
   1462                 lua_pushboolean(L, fde_is_active(bus, drive));
   1463                 lua_setfield(L, -2, "encrypted");
   1464                 if (fde_is_active(bus, drive)) {
   1465                     fde_context_t* ctx = get_fde_context(bus, drive);
   1466                     lua_pushstring(L, fde_cipher_name(ctx->cipher_mode));
   1467                     lua_setfield(L, -2, "cipher");
   1468                     lua_pushinteger(L, ctx->total_sectors);
   1469                     lua_setfield(L, -2, "encrypted_sectors");
   1470                 }
   1471 
   1472                 lua_rawseti(L, -2, idx++);
   1473             }
   1474         }
   1475     }
   1476 
   1477     return 1;
   1478 }
   1479 
   1480 int luaopen_diskfs(lua_State* L) {
   1481     static const luaL_Reg funcs[] = {
   1482         {"format", lua_diskfs_format},
   1483         {"exists", lua_diskfs_exists},
   1484         {"isdir", lua_diskfs_isdir},
   1485         {"isfile", lua_diskfs_isfile},
   1486         {"mkdir", lua_diskfs_mkdir},
   1487         {"open", lua_diskfs_open},
   1488         {"read", lua_diskfs_read},
   1489         {"write", lua_diskfs_write},
   1490         {"close", lua_diskfs_close},
   1491         {"remove", lua_diskfs_remove},
   1492         {"list", lua_diskfs_list},
   1493         {"getinfo", lua_diskfs_getinfo},
   1494         {"getMounts", lua_diskfs_getmounts},
   1495         {NULL, NULL}
   1496     };
   1497 
   1498     luaL_newlib(L, funcs);
   1499     return 1;
   1500 }