diff --git a/src/core/main.c b/src/core/main.c index 4e3d8ed..6657a06 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -17,6 +17,7 @@ #include "io.h" #include "fat32.h" #include "tar.h" +#include "vfs.h" #include "memory_manager.h" #include "platform.h" #include "wallpaper.h" @@ -133,6 +134,7 @@ static void fat32_mkdir_recursive(const char *path) { void kmain(void) { init_serial(); + vfs_init(); serial_write("\n[DEBUG] Entering kmain...\n"); platform_init(); diff --git a/src/dev/ahci.c b/src/dev/ahci.c new file mode 100644 index 0000000..8b9902b --- /dev/null +++ b/src/dev/ahci.c @@ -0,0 +1,509 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. +#include "ahci.h" +#include "pci.h" +#include "disk.h" +#include "memory_manager.h" +#include "paging.h" +#include "io.h" +#include + +extern void serial_write(const char *str); +extern void serial_write_num(uint64_t num); +extern void serial_write_hex(uint32_t val); + +// ============================================================================ +// AHCI Driver State +// ============================================================================ + +static HBA_MEM *abar = NULL; // MMIO-mapped AHCI Base Address +static bool ahci_initialized = false; +static int active_port_count = 0; + +#define MAX_AHCI_PORTS 32 + +typedef struct { + bool active; + int port_num; + HBA_PORT *port; + HBA_CMD_HEADER *cmd_list; // 1KB, 1KB aligned + void *fis_base; // 256B, 256B aligned + HBA_CMD_TBL *cmd_tbl; // Command table for slot 0 +} ahci_port_state_t; + +static ahci_port_state_t ports[MAX_AHCI_PORTS]; + +// ============================================================================ +// String Helpers +// ============================================================================ + +static void ahci_strcpy(char *d, const char *s) { + while ((*d++ = *s++)); +} + +// Kernel virtual to physical address conversion +extern uint64_t v2p(uint64_t vaddr); + +// ============================================================================ +// Port Setup +// ============================================================================ + +static void ahci_stop_cmd(HBA_PORT *port) { + // Clear ST (Start) + port->cmd &= ~HBA_PORT_CMD_ST; + + // Clear FRE (FIS Receive Enable) + port->cmd &= ~HBA_PORT_CMD_FRE; + + // Wait until FR and CR clear + int timeout = 500000; + while (timeout-- > 0) { + if (port->cmd & HBA_PORT_CMD_FR) continue; + if (port->cmd & HBA_PORT_CMD_CR) continue; + break; + } +} + +static void ahci_start_cmd(HBA_PORT *port) { + // Wait until CR clears + while (port->cmd & HBA_PORT_CMD_CR); + + // Set FRE and ST + port->cmd |= HBA_PORT_CMD_FRE; + port->cmd |= HBA_PORT_CMD_ST; +} + +static int ahci_check_port_type(HBA_PORT *port) { + uint32_t ssts = port->ssts; + uint8_t ipm = (ssts >> 8) & 0x0F; + uint8_t det = ssts & 0x0F; + + if (det != 3) return -1; // No device detected + if (ipm != 1) return -1; // Not in active state + + switch (port->sig) { + case SATA_SIG_ATA: return 0; // SATA drive + case SATA_SIG_ATAPI: return 1; // SATAPI drive + case SATA_SIG_SEMB: return 2; // SEMB + case SATA_SIG_PM: return 3; // Port multiplier + default: return -1; + } +} + +static void ahci_port_rebase(ahci_port_state_t *ps) { + HBA_PORT *port = ps->port; + + ahci_stop_cmd(port); + + // Allocate command list (1KB, 1024-byte aligned) + ps->cmd_list = (HBA_CMD_HEADER*)kmalloc_aligned(1024, 1024); + if (!ps->cmd_list) return; + mem_memset(ps->cmd_list, 0, 1024); + + uint64_t clb_phys = v2p((uint64_t)ps->cmd_list); + port->clb = (uint32_t)(clb_phys & 0xFFFFFFFF); + port->clbu = (uint32_t)(clb_phys >> 32); + + // Allocate FIS receive area (256 bytes, 256-byte aligned) + ps->fis_base = kmalloc_aligned(256, 256); + if (!ps->fis_base) return; + mem_memset(ps->fis_base, 0, 256); + + uint64_t fb_phys = v2p((uint64_t)ps->fis_base); + port->fb = (uint32_t)(fb_phys & 0xFFFFFFFF); + port->fbu = (uint32_t)(fb_phys >> 32); + + // Allocate command table for slot 0 (256-byte aligned, room for 8 PRDT entries) + int cmd_tbl_size = sizeof(HBA_CMD_TBL) + 8 * sizeof(HBA_PRDT_ENTRY); + ps->cmd_tbl = (HBA_CMD_TBL*)kmalloc_aligned(cmd_tbl_size, 256); + if (!ps->cmd_tbl) return; + mem_memset(ps->cmd_tbl, 0, cmd_tbl_size); + + // Set command header 0 to point to our command table + uint64_t ctba_phys = v2p((uint64_t)ps->cmd_tbl); + ps->cmd_list[0].ctba = (uint32_t)(ctba_phys & 0xFFFFFFFF); + ps->cmd_list[0].ctbau = (uint32_t)(ctba_phys >> 32); + ps->cmd_list[0].prdtl = 1; // 1 PRDT entry default + + // Clear error and interrupt status + port->serr = 0xFFFFFFFF; + port->is = 0xFFFFFFFF; + + ahci_start_cmd(port); +} + +// ============================================================================ +// Sector I/O +// ============================================================================ + +static int ahci_find_free_slot(HBA_PORT *port) { + uint32_t slots = (port->sact | port->ci); + for (int i = 0; i < 32; i++) { + if (!(slots & (1 << i))) return i; + } + return -1; +} + +int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffer) { + if (!ahci_initialized || port_num < 0 || port_num >= MAX_AHCI_PORTS) return -1; + ahci_port_state_t *ps = &ports[port_num]; + if (!ps->active) return -1; + + HBA_PORT *port = ps->port; + + // Clear any pending interrupts/errors + port->is = 0xFFFFFFFF; + + int slot = ahci_find_free_slot(port); + if (slot < 0) return -1; + + HBA_CMD_HEADER *cmd_hdr = &ps->cmd_list[slot]; + cmd_hdr->cfl = sizeof(FIS_REG_H2D) / sizeof(uint32_t); + cmd_hdr->w = 0; // Read + cmd_hdr->prdtl = 1; + + HBA_CMD_TBL *cmd_tbl = ps->cmd_tbl; + mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + sizeof(HBA_PRDT_ENTRY)); + + // Setup PRDT + uint64_t buf_phys = v2p((uint64_t)buffer); + cmd_tbl->prdt[0].dba = (uint32_t)(buf_phys & 0xFFFFFFFF); + cmd_tbl->prdt[0].dbau = (uint32_t)(buf_phys >> 32); + cmd_tbl->prdt[0].dbc = (count * 512) - 1; // 0-based byte count + cmd_tbl->prdt[0].i = 1; + + // Setup Command FIS + FIS_REG_H2D *fis = (FIS_REG_H2D*)&cmd_tbl->cfis; + fis->fis_type = FIS_TYPE_REG_H2D; + fis->c = 1; // Command + fis->command = ATA_CMD_READ_DMA_EX; + + fis->lba0 = (uint8_t)(lba); + fis->lba1 = (uint8_t)(lba >> 8); + fis->lba2 = (uint8_t)(lba >> 16); + fis->device = 1 << 6; // LBA mode + fis->lba3 = (uint8_t)(lba >> 24); + fis->lba4 = (uint8_t)(lba >> 32); + fis->lba5 = (uint8_t)(lba >> 40); + + fis->countl = (uint8_t)(count); + fis->counth = (uint8_t)(count >> 8); + + // Issue command + port->ci = (1 << slot); + + // Wait for completion + int timeout = 1000000; + while (timeout-- > 0) { + if (!(port->ci & (1 << slot))) break; + if (port->is & (1 << 30)) { // Task File Error + serial_write("[AHCI] Read error on port "); + serial_write_num(port_num); + serial_write("\n"); + return -1; + } + } + + if (timeout <= 0) { + serial_write("[AHCI] Read timeout on port "); + serial_write_num(port_num); + serial_write("\n"); + return -1; + } + + return 0; +} + +int ahci_write_sectors(int port_num, uint64_t lba, uint32_t count, const uint8_t *buffer) { + if (!ahci_initialized || port_num < 0 || port_num >= MAX_AHCI_PORTS) return -1; + ahci_port_state_t *ps = &ports[port_num]; + if (!ps->active) return -1; + + HBA_PORT *port = ps->port; + + port->is = 0xFFFFFFFF; + + int slot = ahci_find_free_slot(port); + if (slot < 0) return -1; + + HBA_CMD_HEADER *cmd_hdr = &ps->cmd_list[slot]; + cmd_hdr->cfl = sizeof(FIS_REG_H2D) / sizeof(uint32_t); + cmd_hdr->w = 1; // Write + cmd_hdr->prdtl = 1; + + HBA_CMD_TBL *cmd_tbl = ps->cmd_tbl; + mem_memset(cmd_tbl, 0, sizeof(HBA_CMD_TBL) + sizeof(HBA_PRDT_ENTRY)); + + uint64_t buf_phys = v2p((uint64_t)buffer); + cmd_tbl->prdt[0].dba = (uint32_t)(buf_phys & 0xFFFFFFFF); + cmd_tbl->prdt[0].dbau = (uint32_t)(buf_phys >> 32); + cmd_tbl->prdt[0].dbc = (count * 512) - 1; + cmd_tbl->prdt[0].i = 1; + + FIS_REG_H2D *fis = (FIS_REG_H2D*)&cmd_tbl->cfis; + fis->fis_type = FIS_TYPE_REG_H2D; + fis->c = 1; + fis->command = ATA_CMD_WRITE_DMA_EX; + + fis->lba0 = (uint8_t)(lba); + fis->lba1 = (uint8_t)(lba >> 8); + fis->lba2 = (uint8_t)(lba >> 16); + fis->device = 1 << 6; + fis->lba3 = (uint8_t)(lba >> 24); + fis->lba4 = (uint8_t)(lba >> 32); + fis->lba5 = (uint8_t)(lba >> 40); + + fis->countl = (uint8_t)(count); + fis->counth = (uint8_t)(count >> 8); + + port->ci = (1 << slot); + + int timeout = 1000000; + while (timeout-- > 0) { + if (!(port->ci & (1 << slot))) break; + if (port->is & (1 << 30)) { + serial_write("[AHCI] Write error on port "); + serial_write_num(port_num); + serial_write("\n"); + return -1; + } + } + + if (timeout <= 0) { + serial_write("[AHCI] Write timeout on port "); + serial_write_num(port_num); + serial_write("\n"); + return -1; + } + + return 0; +} + +// ============================================================================ +// AHCI Disk Integration — wrap AHCI into Disk read/write_sector +// ============================================================================ + +typedef struct { + int ahci_port; +} AHCIDriverData; + +static int ahci_disk_read_sector(Disk *disk, uint32_t sector, uint8_t *buffer) { + AHCIDriverData *data = (AHCIDriverData*)disk->driver_data; + + // For partitions, add offset and use parent's port + if (disk->is_partition && disk->parent) { + AHCIDriverData *pdata = (AHCIDriverData*)disk->parent->driver_data; + return ahci_read_sectors(pdata->ahci_port, + (uint64_t)sector + disk->partition_lba_offset, 1, buffer); + } + + return ahci_read_sectors(data->ahci_port, (uint64_t)sector, 1, buffer); +} + +static int ahci_disk_write_sector(Disk *disk, uint32_t sector, const uint8_t *buffer) { + AHCIDriverData *data = (AHCIDriverData*)disk->driver_data; + + if (disk->is_partition && disk->parent) { + AHCIDriverData *pdata = (AHCIDriverData*)disk->parent->driver_data; + return ahci_write_sectors(pdata->ahci_port, + (uint64_t)sector + disk->partition_lba_offset, 1, buffer); + } + + return ahci_write_sectors(data->ahci_port, (uint64_t)sector, 1, buffer); +} + +// ============================================================================ +// Initialization +// ============================================================================ + +int ahci_get_port_count(void) { + return active_port_count; +} + +bool ahci_port_is_active(int port_num) { + if (port_num < 0 || port_num >= MAX_AHCI_PORTS) return false; + return ports[port_num].active; +} + +void ahci_init(void) { + serial_write("[AHCI] Scanning PCI for AHCI controller...\n"); + + // Find AHCI controller (Class 0x01, Subclass 0x06) + pci_device_t pci_dev; + if (!pci_find_device_by_class(PCI_CLASS_MASS_STORAGE, PCI_SUBCLASS_SATA, &pci_dev)) { + serial_write("[AHCI] No AHCI controller found\n"); + return; + } + + serial_write("[AHCI] Found AHCI controller ("); + serial_write("vendor=0x"); + serial_write_hex(pci_dev.vendor_id); + serial_write(", device=0x"); + serial_write_hex(pci_dev.device_id); + serial_write(")\n"); + + // Enable Bus Mastering and MMIO + pci_enable_bus_mastering(&pci_dev); + pci_enable_mmio(&pci_dev); + + // Read ABAR (BAR5) + uint32_t abar_raw = pci_get_bar(&pci_dev, 5); + uint64_t abar_phys = abar_raw & 0xFFFFF000; // Mask out lower bits + + if (abar_phys == 0) { + serial_write("[AHCI] Invalid ABAR address\n"); + return; + } + + serial_write("[AHCI] ABAR physical address: 0x"); + serial_write_hex((uint32_t)abar_phys); + serial_write("\n"); + + // Map ABAR region into kernel virtual address space + // Identity-map several pages to cover the HBA memory (at least 0x1100 bytes) + uint64_t abar_virt = abar_phys; // Use identity mapping + for (uint64_t offset = 0; offset < 0x2000; offset += 4096) { + paging_map_page(paging_get_pml4_phys(), abar_virt + offset, + abar_phys + offset, + PT_PRESENT | PT_RW | PT_CACHE_DISABLE); + } + + abar = (HBA_MEM*)abar_virt; + + // Enable AHCI mode + abar->ghc |= (1 << 31); // AE (AHCI Enable) + + serial_write("[AHCI] Version: "); + serial_write_num(abar->vs >> 16); + serial_write("."); + serial_write_num(abar->vs & 0xFFFF); + serial_write("\n"); + + // Probe ports + uint32_t pi = abar->pi; + active_port_count = 0; + + for (int i = 0; i < 32; i++) { + ports[i].active = false; + + if (!(pi & (1 << i))) continue; + + HBA_PORT *port = &abar->ports[i]; + int type = ahci_check_port_type(port); + + if (type == 0) { // SATA drive + serial_write("[AHCI] Port "); + serial_write_num(i); + serial_write(": SATA drive detected\n"); + + ports[i].port_num = i; + ports[i].port = port; + ahci_port_rebase(&ports[i]); + ports[i].active = true; + active_port_count++; + + // Register as a block device + Disk *disk = (Disk*)kmalloc(sizeof(Disk)); + if (disk) { + AHCIDriverData *drv = (AHCIDriverData*)kmalloc(sizeof(AHCIDriverData)); + drv->ahci_port = i; + + disk->devname[0] = 0; // Auto-assign + disk->type = DISK_TYPE_SATA; + ahci_strcpy(disk->label, "SATA Drive"); + disk->read_sector = ahci_disk_read_sector; + disk->write_sector = ahci_disk_write_sector; + disk->driver_data = drv; + disk->partition_lba_offset = 0; + disk->total_sectors = 0; + disk->parent = NULL; + disk->is_partition = false; + disk->is_fat32 = false; + + disk_register(disk); + + // Let disk_manager parse partitions — we call a scan function + extern void disk_manager_scan_partitions(Disk *disk); + // Inline MBR parse for this disk + extern void serial_write(const char *str); + serial_write("[AHCI] Probing partitions on /dev/"); + serial_write(disk->devname); + serial_write("...\n"); + + // Read MBR sector 0 + uint8_t *mbr_buf = (uint8_t*)kmalloc(512); + if (mbr_buf) { + if (ahci_disk_read_sector(disk, 0, mbr_buf) == 0) { + if (mbr_buf[510] == 0x55 && mbr_buf[511] == 0xAA) { + // Parse MBR partition table + typedef struct { + uint8_t status; + uint8_t chs_first[3]; + uint8_t type; + uint8_t chs_last[3]; + uint32_t lba_start; + uint32_t sector_count; + } __attribute__((packed)) MBR_Part; + + MBR_Part *parts = (MBR_Part*)&mbr_buf[446]; + int pn = 1; + for (int p = 0; p < 4; p++) { + if (parts[p].type == 0x00 || parts[p].sector_count == 0) + continue; + + bool fat32 = false; + if (parts[p].type == 0x0B || parts[p].type == 0x0C) { + // Verify BPB + uint8_t *pbuf = (uint8_t*)kmalloc(512); + if (pbuf) { + if (ahci_disk_read_sector(disk, parts[p].lba_start, pbuf) == 0) { + if (pbuf[510] == 0x55 && pbuf[511] == 0xAA) { + uint16_t bps = *(uint16_t*)&pbuf[11]; + uint16_t spf16 = *(uint16_t*)&pbuf[22]; + uint32_t spf32 = *(uint32_t*)&pbuf[36]; + if (bps == 512 && spf16 == 0 && spf32 > 0) + fat32 = true; + } + } + kfree(pbuf); + } + } + + disk_register_partition(disk, parts[p].lba_start, + parts[p].sector_count, fat32, pn); + pn++; + } + + // Fallback: raw FAT32 + if (pn == 1) { + uint16_t bps = *(uint16_t*)&mbr_buf[11]; + uint16_t spf16 = *(uint16_t*)&mbr_buf[22]; + uint32_t spf32 = *(uint32_t*)&mbr_buf[36]; + if (bps == 512 && spf16 == 0 && spf32 > 0) { + disk->is_fat32 = true; + disk->partition_lba_offset = 0; + serial_write("[AHCI] Raw FAT32 volume detected\n"); + } + } + } + } + kfree(mbr_buf); + } + } + } else if (type == 1) { + serial_write("[AHCI] Port "); + serial_write_num(i); + serial_write(": SATAPI drive (ignored)\n"); + } + } + + if (active_port_count > 0) { + ahci_initialized = true; + serial_write("[AHCI] Initialization complete: "); + serial_write_num(active_port_count); + serial_write(" SATA port(s) active\n"); + } else { + serial_write("[AHCI] No active SATA ports found\n"); + } +} diff --git a/src/dev/ahci.h b/src/dev/ahci.h new file mode 100644 index 0000000..8c18536 --- /dev/null +++ b/src/dev/ahci.h @@ -0,0 +1,174 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. +#ifndef AHCI_H +#define AHCI_H + +#include +#include + +// ============================================================================ +// FIS (Frame Information Structure) Types +// ============================================================================ + +typedef enum { + FIS_TYPE_REG_H2D = 0x27, // Register FIS — Host to Device + FIS_TYPE_REG_D2H = 0x34, // Register FIS — Device to Host + FIS_TYPE_DMA_ACT = 0x39, // DMA Activate FIS + FIS_TYPE_DMA_SETUP = 0x41, // DMA Setup FIS + FIS_TYPE_DATA = 0x46, // Data FIS + FIS_TYPE_BIST = 0x58, // BIST Activate FIS + FIS_TYPE_PIO_SETUP = 0x5F, // PIO Setup FIS + FIS_TYPE_DEV_BITS = 0xA1, // Set Device Bits FIS +} FIS_TYPE; + +// ============================================================================ +// HBA Register Structures (MMIO-mapped from ABAR) +// ============================================================================ + +// Port Registers (one set per port, at ABAR + 0x100 + portno*0x80) +typedef volatile struct { + uint32_t clb; // 0x00: Command List Base Address (lower 32 bits) + uint32_t clbu; // 0x04: Command List Base Address (upper 32 bits) + uint32_t fb; // 0x08: FIS Base Address (lower 32 bits) + uint32_t fbu; // 0x0C: FIS Base Address (upper 32 bits) + uint32_t is; // 0x10: Interrupt Status + uint32_t ie; // 0x14: Interrupt Enable + uint32_t cmd; // 0x18: Command and Status + uint32_t rsv0; // 0x1C: Reserved + uint32_t tfd; // 0x20: Task File Data + uint32_t sig; // 0x24: Signature + uint32_t ssts; // 0x28: SATA Status (SStatus) + uint32_t sctl; // 0x2C: SATA Control (SControl) + uint32_t serr; // 0x30: SATA Error (SError) + uint32_t sact; // 0x34: SATA Active (SCR3) + uint32_t ci; // 0x38: Command Issue + uint32_t sntf; // 0x3C: SATA Notification (SCR4) + uint32_t fbs; // 0x40: FIS-based Switch Control + uint32_t rsv1[11]; // 0x44~0x6F + uint32_t vendor[4]; // 0x70~0x7F +} HBA_PORT; + +// Global HBA Memory Registers (at ABAR) +typedef volatile struct { + uint32_t cap; // 0x00: Host Capability + uint32_t ghc; // 0x04: Global Host Control + uint32_t is; // 0x08: Interrupt Status + uint32_t pi; // 0x0C: Port Implemented + uint32_t vs; // 0x10: Version + uint32_t ccc_ctl; // 0x14: Command Completion Coalescing Control + uint32_t ccc_pts; // 0x18: Command Completion Coalescing Ports + uint32_t em_loc; // 0x1C: Enclosure Management Location + uint32_t em_ctl; // 0x20: Enclosure Management Control + uint32_t cap2; // 0x24: Host Capabilities Extended + uint32_t bohc; // 0x28: BIOS/OS Handoff Control and Status + uint8_t rsv[0xA0 - 0x2C]; + uint8_t vendor[0x100 - 0xA0]; + HBA_PORT ports[]; // Port 0 at offset 0x100 (flexible array member) +} HBA_MEM; + +// ============================================================================ +// Command List / Table Structures (DMA) +// ============================================================================ + +// Command Header (32 bytes each, 32 entries per port = 1KB) +typedef struct { + uint8_t cfl:5; // Command FIS Length (in DWORDs) + uint8_t a:1; // ATAPI + uint8_t w:1; // Write (1=H2D, 0=D2H) + uint8_t p:1; // Prefetchable + + uint8_t r:1; // Reset + uint8_t b:1; // BIST + uint8_t c:1; // Clear Busy upon R_OK + uint8_t rsv0:1; + uint8_t pmp:4; // Port Multiplier Port + + uint16_t prdtl; // Physical Region Descriptor Table Length (entries) + + volatile uint32_t prdbc; // PRD Byte Count transferred + + uint32_t ctba; // Command Table Descriptor Base Address (lower 32) + uint32_t ctbau; // Command Table Descriptor Base Address (upper 32) + + uint32_t rsv1[4]; // Reserved +} __attribute__((packed)) HBA_CMD_HEADER; + +// Physical Region Descriptor Table Entry +typedef struct { + uint32_t dba; // Data Base Address (lower 32) + uint32_t dbau; // Data Base Address (upper 32) + uint32_t rsv0; // Reserved + uint32_t dbc:22; // Byte Count (0-based, max 4MB) + uint32_t rsv1:9; // Reserved + uint32_t i:1; // Interrupt on Completion +} __attribute__((packed)) HBA_PRDT_ENTRY; + +// Host-to-Device Register FIS +typedef struct { + uint8_t fis_type; // FIS_TYPE_REG_H2D + uint8_t pmport:4; // Port Multiplier + uint8_t rsv0:3; // Reserved + uint8_t c:1; // 1=Command, 0=Control + uint8_t command; // Command register + uint8_t featurel; // Feature register (7:0) + uint8_t lba0; // LBA (7:0) + uint8_t lba1; // LBA (15:8) + uint8_t lba2; // LBA (23:16) + uint8_t device; // Device register + uint8_t lba3; // LBA (31:24) + uint8_t lba4; // LBA (39:32) + uint8_t lba5; // LBA (47:40) + uint8_t featureh; // Feature register (15:8) + uint8_t countl; // Count (7:0) + uint8_t counth; // Count (15:8) + uint8_t icc; // Isochronous Command Completion + uint8_t control; // Control register + uint8_t rsv1[4]; // Reserved +} __attribute__((packed)) FIS_REG_H2D; + +// Command Table (256-byte aligned) +typedef struct { + uint8_t cfis[64]; // Command FIS + uint8_t acmd[16]; // ATAPI Command + uint8_t rsv[48]; // Reserved + HBA_PRDT_ENTRY prdt[]; // PRDT entries (variable, at least 1) +} __attribute__((packed)) HBA_CMD_TBL; + +// ============================================================================ +// Port Signature Values +// ============================================================================ + +#define SATA_SIG_ATA 0x00000101 // SATA drive +#define SATA_SIG_ATAPI 0xEB140101 // SATAPI drive +#define SATA_SIG_SEMB 0xC33C0101 // Enclosure management bridge +#define SATA_SIG_PM 0x96690101 // Port multiplier + +// ============================================================================ +// Port Command Bits +// ============================================================================ + +#define HBA_PORT_CMD_ST 0x0001 // Start +#define HBA_PORT_CMD_FRE 0x0010 // FIS Receive Enable +#define HBA_PORT_CMD_FR 0x4000 // FIS Receive Running +#define HBA_PORT_CMD_CR 0x8000 // Command List Running + +// ============================================================================ +// ATA Commands +// ============================================================================ + +#define ATA_CMD_READ_DMA_EX 0x25 +#define ATA_CMD_WRITE_DMA_EX 0x35 +#define ATA_CMD_IDENTIFY 0xEC + +// ============================================================================ +// Public API +// ============================================================================ + +void ahci_init(void); +int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffer); +int ahci_write_sectors(int port_num, uint64_t lba, uint32_t count, const uint8_t *buffer); +int ahci_get_port_count(void); +bool ahci_port_is_active(int port_num); + +#endif diff --git a/src/dev/disk.h b/src/dev/disk.h index 09e4983..986678e 100644 --- a/src/dev/disk.h +++ b/src/dev/disk.h @@ -8,6 +8,7 @@ #include #define SECTOR_SIZE 512 +#define MAX_DISKS 16 typedef enum { DISK_TYPE_RAM, @@ -17,26 +18,45 @@ typedef enum { } DiskType; typedef struct Disk { - char letter; + char devname[16]; // Device name: "sda", "sdb", "sda1", etc. DiskType type; bool is_fat32; - char name[32]; - uint32_t partition_lba_offset; // LBA offset of FAT32 partition (0 for raw) - + char label[32]; // Human-readable label + uint32_t partition_lba_offset; // LBA offset of partition (0 for whole disk) + uint32_t total_sectors; // Total sectors on this device/partition + // Function pointers for driver operations int (*read_sector)(struct Disk *disk, uint32_t sector, uint8_t *buffer); int (*write_sector)(struct Disk *disk, uint32_t sector, const uint8_t *buffer); - + // Private driver data - void *driver_data; + void *driver_data; + + // Parent disk (for partitions — points to the whole-disk Disk) + struct Disk *parent; + bool is_partition; + bool registered; } Disk; +// Initialization and scanning void disk_manager_init(void); -void disk_manager_scan(void); // Scans for new disks -Disk* disk_get_by_letter(char letter); -char disk_get_next_free_letter(void); +void disk_manager_scan(void); + +// Device registration void disk_register(Disk *disk); +void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count, + bool is_fat32, int part_num); + +// Lookup +Disk* disk_get_by_name(const char *devname); int disk_get_count(void); Disk* disk_get_by_index(int index); +// Auto-naming helpers +const char* disk_get_next_dev_name(void); // Returns "sda", "sdb", etc. + +// Backward compat (deprecated — wraps disk_get_by_name) +Disk* disk_get_by_letter(char letter); +char disk_get_next_free_letter(void); + #endif diff --git a/src/dev/disk_manager.c b/src/dev/disk_manager.c index d5baf30..05a5b49 100644 --- a/src/dev/disk_manager.c +++ b/src/dev/disk_manager.c @@ -5,15 +5,39 @@ #include "pci.h" #include "memory_manager.h" #include "io.h" -#include "wm.h" +#include "wm.h" +#include "ahci.h" +#include "../fs/vfs.h" +#include "../fs/fat32.h" #include -#define MAX_DISKS 26 - static Disk *disks[MAX_DISKS]; static int disk_count = 0; +static int next_drive_letter_idx = 0; // For backward compat +static int next_sd_index = 0; // For sda, sdb, sdc... -// === ATA Definitions === +extern void serial_write(const char *str); +extern void serial_write_num(uint64_t num); + +// === String Helpers === + +static void dm_strcpy(char *dest, const char *src) { + while (*src) *dest++ = *src++; + *dest = 0; +} + +static int dm_strcmp(const char *a, const char *b) { + while (*a && *a == *b) { a++; b++; } + return (unsigned char)*a - (unsigned char)*b; +} + +static int dm_strlen(const char *s) { + int n = 0; + while (s[n]) n++; + return n; +} + +// === ATA Definitions (Legacy IDE PIO — kept as fallback) === #define ATA_PRIMARY_IO 0x1F0 #define ATA_PRIMARY_CTRL 0x3F6 @@ -35,40 +59,21 @@ static int disk_count = 0; #define ATA_CMD_WRITE_PIO 0x30 #define ATA_CMD_IDENTIFY 0xEC -#define ATA_SR_BSY 0x80 // Busy -#define ATA_SR_DRDY 0x40 // Drive ready -#define ATA_SR_DF 0x20 // Drive write fault -#define ATA_SR_DSC 0x10 // Drive seek complete -#define ATA_SR_DRQ 0x08 // Data request ready -#define ATA_SR_CORR 0x04 // Corrected data -#define ATA_SR_IDX 0x02 // Index -#define ATA_SR_ERR 0x01 // Error +#define ATA_SR_BSY 0x80 +#define ATA_SR_DRDY 0x40 +#define ATA_SR_DF 0x20 +#define ATA_SR_DSC 0x10 +#define ATA_SR_DRQ 0x08 +#define ATA_SR_CORR 0x04 +#define ATA_SR_IDX 0x02 +#define ATA_SR_ERR 0x01 typedef struct { uint16_t port_base; bool slave; } ATADriverData; -// === Helpers === - -static void dm_strcpy(char *dest, const char *src) { - while (*src) *dest++ = *src++; - *dest = 0; -} - -void disk_register(Disk *disk); - - -static int ramdisk_read(Disk *disk, uint32_t sector, uint8_t *buffer) { - (void)disk; (void)sector; (void)buffer; - return 0; -} - -static int ramdisk_write(Disk *disk, uint32_t sector, const uint8_t *buffer) { - (void)disk; (void)sector; (void)buffer; - return 0; -} - +// === ATA PIO Driver === static void ata_wait_bsy(uint16_t port_base) { while (inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY); @@ -78,58 +83,57 @@ static void ata_wait_drq(uint16_t port_base) { while (!(inb(port_base + ATA_REG_STATUS) & ATA_SR_DRQ)); } -// Returns 1 if drive exists, 0 otherwise static int ata_identify(uint16_t port_base, bool slave) { - // Select Drive outb(port_base + ATA_REG_HDDEVSEL, slave ? 0xB0 : 0xA0); - // Zero out sector count and LBA registers outb(port_base + ATA_REG_SEC_COUNT0, 0); outb(port_base + ATA_REG_LBA0, 0); outb(port_base + ATA_REG_LBA1, 0); outb(port_base + ATA_REG_LBA2, 0); - - // Send Identify command + outb(port_base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY); - - // Check if status is 0 (no drive) + uint8_t status = inb(port_base + ATA_REG_STATUS); if (status == 0) return 0; - - // Wait until BSY clears + int timeout = 10000; while ((inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY) && --timeout > 0) { status = inb(port_base + ATA_REG_STATUS); - if (status == 0) return 0; // Check again + if (status == 0) return 0; } - if (timeout <= 0) return 0; // Hardware didn't respond - - // Check for error - if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) { - return 0; // Error, likely not ATA - } - - // Wait for DRQ or ERR - while (!(inb(port_base + ATA_REG_STATUS) & (ATA_SR_DRQ | ATA_SR_ERR))); - + if (timeout <= 0) return 0; + if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) return 0; - // Read 256 words (512 bytes) of identity data + while (!(inb(port_base + ATA_REG_STATUS) & (ATA_SR_DRQ | ATA_SR_ERR))); + + if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) return 0; + + uint32_t sectors = 0; for (int i = 0; i < 256; i++) { uint16_t data = inw(port_base + ATA_REG_DATA); - (void)data; + if (i == 60) sectors |= (uint32_t)data; + if (i == 61) sectors |= (uint32_t)data << 16; } - - return 1; + + return sectors; } static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) { ATADriverData *data = (ATADriverData*)disk->driver_data; uint16_t port_base = data->port_base; bool slave = data->slave; - + + // For partition reads, add the partition LBA offset + if (disk->is_partition && disk->parent) { + lba += disk->partition_lba_offset; + // Use parent's driver + data = (ATADriverData*)disk->parent->driver_data; + port_base = data->port_base; + slave = data->slave; + } + ata_wait_bsy(port_base); - - // Select drive and send highest 4 bits of LBA + outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F)); outb(port_base + ATA_REG_FEATURES, 0x00); outb(port_base + ATA_REG_SEC_COUNT0, 1); @@ -137,25 +141,33 @@ static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) { outb(port_base + ATA_REG_LBA1, (uint8_t)(lba >> 8)); outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16)); outb(port_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO); - + ata_wait_bsy(port_base); ata_wait_drq(port_base); - + uint16_t *ptr = (uint16_t*)buffer; for (int i = 0; i < 256; i++) { ptr[i] = inw(port_base + ATA_REG_DATA); } - - return 0; // Success + + return 0; } static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) { ATADriverData *data = (ATADriverData*)disk->driver_data; uint16_t port_base = data->port_base; bool slave = data->slave; - + + // For partition writes, add the partition LBA offset + if (disk->is_partition && disk->parent) { + lba += disk->partition_lba_offset; + data = (ATADriverData*)disk->parent->driver_data; + port_base = data->port_base; + slave = data->slave; + } + ata_wait_bsy(port_base); - + outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F)); outb(port_base + ATA_REG_FEATURES, 0x00); outb(port_base + ATA_REG_SEC_COUNT0, 1); @@ -163,75 +175,121 @@ static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) { outb(port_base + ATA_REG_LBA1, (uint8_t)(lba >> 8)); outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16)); outb(port_base + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO); - + ata_wait_bsy(port_base); ata_wait_drq(port_base); - + const uint16_t *ptr = (const uint16_t*)buffer; for (int i = 0; i < 256; i++) { outw(port_base + ATA_REG_DATA, ptr[i]); } - - // Flush / Sync + outb(port_base + ATA_REG_COMMAND, 0xE7); // Cache Flush ata_wait_bsy(port_base); - + return 0; } +// === Device Naming === -char disk_get_next_free_letter(void) { - for (int i = 0; i < MAX_DISKS; i++) { - char letter = 'A' + i; - bool used = false; - for (int j = 0; j < disk_count; j++) { - if (disks[j]->letter == letter) { - used = true; - break; - } - } - if (!used) return letter; - } - return 0; // No free letters +const char* disk_get_next_dev_name(void) { + static char name[8]; + name[0] = 's'; + name[1] = 'd'; + name[2] = 'a' + next_sd_index; + name[3] = 0; + next_sd_index++; + return name; } +// === Registration === + void disk_register(Disk *disk) { if (disk_count >= MAX_DISKS) return; - - // Ensure letter is unique - if (disk->letter == 0) { - disk->letter = disk_get_next_free_letter(); + + // Auto-assign devname if empty + if (disk->devname[0] == 0) { + const char *n = disk_get_next_dev_name(); + dm_strcpy(disk->devname, n); } - + + disk->registered = true; disks[disk_count++] = disk; + + serial_write("[DISK] Registered /dev/"); + serial_write(disk->devname); + serial_write(" ("); + serial_write(disk->label); + serial_write(")\n"); } -void disk_manager_init(void) { - for (int i = 0; i < MAX_DISKS; i++) { - disks[i] = NULL; +void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count, + bool is_fat32, int part_num) { + if (disk_count >= MAX_DISKS) return; + + Disk *part = (Disk*)kmalloc(sizeof(Disk)); + if (!part) return; + + // Build name: parent_devname + partition number (e.g. "sda1") + int len = dm_strlen(parent->devname); + for (int i = 0; i < len; i++) part->devname[i] = parent->devname[i]; + part->devname[len] = '0' + part_num; + part->devname[len + 1] = 0; + + part->type = parent->type; + part->is_fat32 = is_fat32; + dm_strcpy(part->label, is_fat32 ? "FAT32 Partition" : "Unknown Partition"); + part->partition_lba_offset = lba_offset; + part->total_sectors = sector_count; + part->read_sector = parent->read_sector; + part->write_sector = parent->write_sector; + part->driver_data = parent->driver_data; + part->parent = parent; + part->is_partition = true; + part->registered = true; + + disks[disk_count++] = part; + + serial_write("[DISK] Registered /dev/"); + serial_write(part->devname); + serial_write(" (LBA offset "); + serial_write_num(lba_offset); + serial_write(", "); + serial_write_num(sector_count); + serial_write(" sectors, FAT32="); + serial_write(" sectors, FAT32="); + serial_write(is_fat32 ? "yes" : "no"); + serial_write(")\n"); + + if (is_fat32) { + // Try to initialize and mount FAT32 volume to VFS + void *vol = fat32_mount_volume(part); + if (vol) { + char mount_path[32]; + mount_path[0] = '/'; + mount_path[1] = 'd'; mount_path[2] = 'e'; mount_path[3] = 'v'; mount_path[4] = '/'; + dm_strcpy(mount_path + 5, part->devname); + + if (vfs_mount(mount_path, part->devname, "fat32", fat32_get_realfs_ops(), vol)) { + serial_write("[VFS] Mounted "); + serial_write(mount_path); + serial_write(" to VFS\n"); + wm_notify_fs_change(); + } else { + serial_write("[VFS] Failed to mount "); + serial_write(mount_path); + serial_write("\n"); + } + } } - disk_count = 0; - - // Register A: (Ramdisk) - Disk *ramdisk = (Disk*)kmalloc(sizeof(Disk)); - ramdisk->letter = 'A'; - ramdisk->type = DISK_TYPE_RAM; - ramdisk->is_fat32 = true; // Ramdisk is always formatted - dm_strcpy(ramdisk->name, "RAM"); - ramdisk->read_sector = ramdisk_read; - ramdisk->write_sector = ramdisk_write; - ramdisk->driver_data = NULL; - ramdisk->partition_lba_offset = 0; - - disk_register(ramdisk); } -Disk* disk_get_by_letter(char letter) { - // Uppercase - if (letter >= 'a' && letter <= 'z') letter -= 32; - +// === Lookup === + +Disk* disk_get_by_name(const char *devname) { + if (!devname) return NULL; for (int i = 0; i < disk_count; i++) { - if (disks[i]->letter == letter) { + if (dm_strcmp(disks[i]->devname, devname) == 0) { return disks[i]; } } @@ -247,130 +305,181 @@ Disk* disk_get_by_index(int index) { return disks[index]; } +// === Backward Compat (deprecated) === -// === MBR Partition Table Structures === +char disk_get_next_free_letter(void) { + char letter = 'B' + next_drive_letter_idx++; + if (letter > 'Z') return 0; + return letter; +} + +Disk* disk_get_by_letter(char letter) { + // Maps old letter scheme: A=ramfs (not a block device), B+=first real disk, etc. + if (letter >= 'a' && letter <= 'z') letter -= 32; + + // A: was the ramdisk — return NULL since ramfs is now VFS-managed + if (letter == 'A') return NULL; + + // B-Z map to disk indices 0, 1, 2... + // Find real disks (non-RAM, non-partition-parent) + int real_idx = 0; + for (int i = 0; i < disk_count; i++) { + if (disks[i]->is_partition && disks[i]->is_fat32) { + if (real_idx == (letter - 'B')) { + return disks[i]; + } + real_idx++; + } + } + return NULL; +} + +// === MBR Partition Table === typedef struct { - uint8_t status; // 0x80 = bootable, 0x00 = inactive - uint8_t chs_first[3]; // CHS of first sector - uint8_t type; // Partition type - uint8_t chs_last[3]; // CHS of last sector - uint32_t lba_start; // LBA of first sector - uint32_t sector_count; // Number of sectors + uint8_t status; + uint8_t chs_first[3]; + uint8_t type; + uint8_t chs_last[3]; + uint32_t lba_start; + uint32_t sector_count; } __attribute__((packed)) MBR_PartitionEntry; -// FAT32 partition type codes #define PART_TYPE_FAT32 0x0B #define PART_TYPE_FAT32_LBA 0x0C -// Check if sector contains a valid FAT32 BPB (Volume Boot Record) static bool is_fat32_bpb(const uint8_t *sector) { - // Must have 0xAA55 boot signature if (sector[510] != 0x55 || sector[511] != 0xAA) return false; - - // Check for FAT32 filesystem string at offset 82 - // "FAT32 " in the fs_type field of the BPB + if (sector[82] == 'F' && sector[83] == 'A' && sector[84] == 'T' && sector[85] == '3' && sector[86] == '2') { return true; } - - // Also accept if bytes_per_sector is 512 and sectors_per_fat_16 is 0 - // (FAT32 always has sectors_per_fat_16 == 0) + uint16_t bps = *(uint16_t*)§or[11]; uint16_t spf16 = *(uint16_t*)§or[22]; uint32_t spf32 = *(uint32_t*)§or[36]; if (bps == 512 && spf16 == 0 && spf32 > 0) { return true; } - + return false; } -// Parse MBR partition table and find a FAT32 partition. -// Sets disk->partition_lba_offset and returns true if found. -static bool detect_fat32_partition(Disk *disk) { +// Parse MBR and register each partition as a child block device +static void parse_mbr_partitions(Disk *disk) { uint8_t *buffer = (uint8_t*)kmalloc(512); - if (!buffer) return false; - - // Read sector 0 (MBR or raw BPB) + if (!buffer) return; + if (disk->read_sector(disk, 0, buffer) != 0) { kfree(buffer); - return false; + return; } - - // Must have 0xAA55 boot signature + + // Check for valid MBR signature if (buffer[510] != 0x55 || buffer[511] != 0xAA) { kfree(buffer); - return false; + return; } - - // Check MBR partition table entries (4 entries at offset 446) + MBR_PartitionEntry *partitions = (MBR_PartitionEntry*)&buffer[446]; - + int part_num = 1; + for (int i = 0; i < 4; i++) { - if (partitions[i].type == PART_TYPE_FAT32 || - partitions[i].type == PART_TYPE_FAT32_LBA) { - - uint32_t part_lba = partitions[i].lba_start; - - // Read the partition's first sector to verify it's a valid FAT32 BPB + uint32_t start = partitions[i].lba_start; + uint32_t size = partitions[i].sector_count; + uint8_t type = partitions[i].type; + + if (type == 0x00) continue; // Empty entry + if (size == 0) continue; + if (start >= disk->total_sectors) continue; // Invalid start + + bool fat32 = false; + if (type == PART_TYPE_FAT32 || type == PART_TYPE_FAT32_LBA) { + // Verify by reading the BPB uint8_t *pbuf = (uint8_t*)kmalloc(512); - if (!pbuf) { kfree(buffer); return false; } - - if (disk->read_sector(disk, part_lba, pbuf) == 0 && is_fat32_bpb(pbuf)) { - disk->partition_lba_offset = part_lba; + if (pbuf) { + if (disk->read_sector(disk, start, pbuf) == 0) { + fat32 = is_fat32_bpb(pbuf); + } kfree(pbuf); - kfree(buffer); - return true; } - kfree(pbuf); } + + disk_register_partition(disk, partitions[i].lba_start, + partitions[i].sector_count, fat32, part_num); + part_num++; } - - // Fallback: check if sector 0 itself is a raw FAT32 BPB (no partition table) - if (is_fat32_bpb(buffer)) { + + // Fallback: if no partitions found, check if entire disk is a raw FAT32 volume + if (part_num == 1 && is_fat32_bpb(buffer)) { + serial_write("[DISK] No MBR partitions — raw FAT32 volume on /dev/"); + serial_write(disk->devname); + serial_write("\n"); + disk->is_fat32 = true; disk->partition_lba_offset = 0; - kfree(buffer); - return true; } - + kfree(buffer); - return false; } +// === ATA Drive Discovery === + static void try_add_ata_drive(uint16_t port, bool slave, const char *name) { - if (ata_identify(port, slave)) { + uint32_t sectors = ata_identify(port, slave); + if (sectors > 0) { Disk *new_disk = (Disk*)kmalloc(sizeof(Disk)); if (!new_disk) return; - + ATADriverData *data = (ATADriverData*)kmalloc(sizeof(ATADriverData)); data->port_base = port; data->slave = slave; - - new_disk->letter = 0; // Auto-assign + + new_disk->devname[0] = 0; // Auto-assign new_disk->type = DISK_TYPE_IDE; - dm_strcpy(new_disk->name, name); + dm_strcpy(new_disk->label, name); new_disk->read_sector = ata_read_sector; new_disk->write_sector = ata_write_sector; new_disk->driver_data = data; new_disk->partition_lba_offset = 0; - - // Detect FAT32 (with MBR partition support) - if (detect_fat32_partition(new_disk)) { - new_disk->is_fat32 = true; - disk_register(new_disk); - } else { - kfree(data); - kfree(new_disk); - } + new_disk->total_sectors = sectors; + new_disk->parent = NULL; + new_disk->is_partition = false; + new_disk->is_fat32 = false; + + disk_register(new_disk); + + // Parse MBR to find partitions + parse_mbr_partitions(new_disk); } } +// === Init & Scan === + +void disk_manager_init(void) { + for (int i = 0; i < MAX_DISKS; i++) { + disks[i] = NULL; + } + disk_count = 0; + next_sd_index = 0; + next_drive_letter_idx = 0; + + serial_write("[DISK] Disk manager initialized (VFS mode)\n"); + // NOTE: Ramdisk (A:) is no longer registered here. + // RAMFS is managed directly by fat32.c and mounted at "/" via VFS. +} + void disk_manager_scan(void) { - // Probe Standard ATA Ports - try_add_ata_drive(ATA_PRIMARY_IO, false, "IDE1"); - try_add_ata_drive(ATA_PRIMARY_IO, true, "IDE2"); - try_add_ata_drive(ATA_SECONDARY_IO, false, "IDE3"); - try_add_ata_drive(ATA_SECONDARY_IO, true, "IDE4"); + serial_write("[DISK] Initializing AHCI (SATA DMA)...\n"); + ahci_init(); + + if (ahci_get_port_count() == 0) { + serial_write("[DISK] No AHCI ports found, falling back to legacy IDE PIO...\n"); + try_add_ata_drive(ATA_PRIMARY_IO, false, "IDE Primary Master"); + try_add_ata_drive(ATA_PRIMARY_IO, true, "IDE Primary Slave"); + try_add_ata_drive(ATA_SECONDARY_IO, false, "IDE Secondary Master"); + try_add_ata_drive(ATA_SECONDARY_IO, true, "IDE Secondary Slave"); + } else { + serial_write("[DISK] AHCI initialized successfully, skipping legacy IDE.\n"); + } } \ No newline at end of file diff --git a/src/dev/pci.c b/src/dev/pci.c index 5cb7b8c..0d137ef 100644 --- a/src/dev/pci.c +++ b/src/dev/pci.c @@ -97,3 +97,23 @@ int pci_find_device_by_class(uint8_t class_code, uint8_t subclass, pci_device_t* } return 0; } + +uint32_t pci_get_bar(pci_device_t *dev, int bar_num) { + if (!dev || bar_num < 0 || bar_num > 5) return 0; + uint8_t offset = 0x10 + (bar_num * 4); + return pci_read_config(dev->bus, dev->device, dev->function, offset); +} + +void pci_enable_bus_mastering(pci_device_t *dev) { + if (!dev) return; + uint32_t cmd = pci_read_config(dev->bus, dev->device, dev->function, 0x04); + cmd |= (1 << 2); // Set Bus Master bit + pci_write_config(dev->bus, dev->device, dev->function, 0x04, cmd); +} + +void pci_enable_mmio(pci_device_t *dev) { + if (!dev) return; + uint32_t cmd = pci_read_config(dev->bus, dev->device, dev->function, 0x04); + cmd |= (1 << 1); // Set Memory Space bit + pci_write_config(dev->bus, dev->device, dev->function, 0x04, cmd); +} diff --git a/src/dev/pci.h b/src/dev/pci.h index 471cd8b..a4ec1af 100644 --- a/src/dev/pci.h +++ b/src/dev/pci.h @@ -22,6 +22,9 @@ typedef struct { #define PCI_CLASS_NETWORK_CONTROLLER 0x02 #define PCI_CLASS_ETHERNET_CONTROLLER 0x00 +#define PCI_CLASS_MASS_STORAGE 0x01 +#define PCI_SUBCLASS_SATA 0x06 +#define PCI_SUBCLASS_IDE 0x01 uint32_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset); void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value); @@ -35,4 +38,9 @@ int pci_enumerate_devices(pci_device_t* devices, int max_devices); int pci_find_device(uint16_t vendor_id, uint16_t device_id, pci_device_t* device); int pci_find_device_by_class(uint8_t class_code, uint8_t subclass, pci_device_t* device); +// BAR access and bus mastering helpers +uint32_t pci_get_bar(pci_device_t *dev, int bar_num); +void pci_enable_bus_mastering(pci_device_t *dev); +void pci_enable_mmio(pci_device_t *dev); + #endif diff --git a/src/fs/fat32.c b/src/fs/fat32.c index 3440c69..031a742 100644 --- a/src/fs/fat32.c +++ b/src/fs/fat32.c @@ -2,6 +2,7 @@ // This software is released under the GNU General Public License v3.0. See LICENSE file for details. // This header needs to maintain in any file it is present in, as per the GPL license terms. #include "fat32.h" +#include "vfs.h" #include "memory_manager.h" #include "io.h" #include "disk.h" @@ -37,7 +38,7 @@ static FileEntry files[MAX_FILES]; static uint32_t next_cluster = 3; // Start after reserved clusters 0, 1, 2 static FAT32_FileHandle open_handles[MAX_OPEN_HANDLES]; static char current_dir[FAT32_MAX_PATH] = "/"; -static char current_drive = 'A'; +static char current_drive = 'A'; // Backward compat only static int desktop_file_limit = -1; // === RealFS Definitions === @@ -56,7 +57,21 @@ typedef struct { uint8_t cached_fat_buf[512]; } FAT32_Volume; -static FAT32_Volume volumes[26]; // A-Z +// Dynamically allocated volumes (no longer A-Z indexed) +#define MAX_REAL_VOLUMES 8 +static FAT32_Volume *real_volumes[MAX_REAL_VOLUMES]; +static int real_volume_count = 0; + +// Forward declarations for volume-aware functions +static FAT32_FileHandle* realfs_open_from_vol(FAT32_Volume *vol, const char *path, const char *mode); +static bool realfs_mkdir_vol(FAT32_Volume *vol, const char *path); +static int realfs_list_directory_vol(FAT32_Volume *vol, const char *path, FAT32_FileInfo *entries, int max_entries); +static bool realfs_delete_from_vol(FAT32_Volume *vol, const char *path); +static bool realfs_mount_volume(FAT32_Volume *vol, Disk *disk); + +// LFN support - declared in fat32.h +static void extract_lfn_chars(FAT32_LFNEntry *lfn, char *buffer); + // === Helper Functions (Shared) === @@ -135,7 +150,7 @@ static void extract_parent_path(const char *path, char *parent) { } } -// Helper to parse drive from path +// Helper to parse drive from path (backward compat) static char parse_drive_from_path(const char **path_ptr) { const char *path = *path_ptr; if (path[0] && path[1] == ':') { @@ -144,7 +159,7 @@ static char parse_drive_from_path(const char **path_ptr) { *path_ptr = path + 2; return drive; } - return current_drive; + return 'A'; // Default to RAMFS } // Normalize path (remove .., ., etc) @@ -152,7 +167,9 @@ void fat32_normalize_path(const char *path, char *normalized) { char temp[FAT32_MAX_PATH]; int temp_len = 0; const char *p = path; - char drive = parse_drive_from_path(&p); + // Strip drive letter if present (backward compat) + if (p[0] && p[1] == ':') p += 2; + (void)current_drive; if (p[0] == '/') { @@ -288,7 +305,7 @@ static FAT32_FileHandle* ramfs_open(const char *normalized_path, const char *mod if (!handle) return NULL; handle->valid = true; - handle->drive = 'A'; + handle->volume = NULL; // RAMFS handle handle->cluster = entry->start_cluster; handle->start_cluster = entry->start_cluster; handle->position = 0; @@ -385,14 +402,8 @@ static int ramfs_write(FAT32_FileHandle *handle, const void *buffer, int size) { // === RealFS Implementation === -static bool realfs_mount(char drive) { - int idx = drive - 'A'; - if (idx < 0 || idx >= 26) return false; - - if (volumes[idx].mounted) return true; - - Disk *disk = disk_get_by_letter(drive); - if (!disk) return false; +static bool realfs_mount_volume(FAT32_Volume *vol, Disk *disk) { + if (vol->mounted) return true; // Use partition LBA offset from disk (set during MBR parsing) uint32_t part_offset = disk->partition_lba_offset; @@ -400,7 +411,7 @@ static bool realfs_mount(char drive) { uint8_t *sect0 = (uint8_t*)kmalloc(512); if (!sect0) return false; - // Read BPB from partition start (sector 0 for raw, partition LBA for MBR) + // Read BPB from partition start if (disk->read_sector(disk, part_offset, sect0) != 0) { kfree(sect0); return false; @@ -413,29 +424,29 @@ static bool realfs_mount(char drive) { return false; } - volumes[idx].disk = disk; - volumes[idx].partition_offset = part_offset; - volumes[idx].fat_begin_lba = part_offset + bpb->reserved_sectors; - volumes[idx].cluster_begin_lba = part_offset + bpb->reserved_sectors + (bpb->num_fats * bpb->sectors_per_fat_32); - volumes[idx].sectors_per_cluster = bpb->sectors_per_cluster; - volumes[idx].root_cluster = bpb->root_cluster; - volumes[idx].fat_size = bpb->sectors_per_fat_32; - volumes[idx].total_sectors = bpb->total_sectors_32; - volumes[idx].mounted = true; - volumes[idx].cached_fat_sector = 0xFFFFFFFF; + vol->disk = disk; + vol->partition_offset = part_offset; + vol->fat_begin_lba = part_offset + bpb->reserved_sectors; + vol->cluster_begin_lba = part_offset + bpb->reserved_sectors + (bpb->num_fats * bpb->sectors_per_fat_32); + vol->sectors_per_cluster = bpb->sectors_per_cluster; + vol->root_cluster = bpb->root_cluster; + vol->fat_size = bpb->sectors_per_fat_32; + vol->total_sectors = bpb->total_sectors_32; + vol->mounted = true; + vol->cached_fat_sector = 0xFFFFFFFF; - fs_serial_str("[FAT32] mounted drive "); - fs_serial_char(drive); - fs_serial_str(": part_offset="); + fs_serial_str("[FAT32] Mounted volume: /dev/"); + fs_serial_str(disk->devname); + fs_serial_str(" part_offset="); fs_serial_num(part_offset); fs_serial_str(" fat_lba="); - fs_serial_num(volumes[idx].fat_begin_lba); + fs_serial_num(vol->fat_begin_lba); fs_serial_str(" cluster_lba="); - fs_serial_num(volumes[idx].cluster_begin_lba); + fs_serial_num(vol->cluster_begin_lba); fs_serial_str(" spc="); - fs_serial_num(volumes[idx].sectors_per_cluster); + fs_serial_num(vol->sectors_per_cluster); fs_serial_str(" root_cl="); - fs_serial_num(volumes[idx].root_cluster); + fs_serial_num(vol->root_cluster); fs_serial_str("\n"); kfree(sect0); @@ -512,12 +523,8 @@ static void to_dos_filename(const char *filename, char *out) { } } -static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *mode) { - int vol_idx = drive - 'A'; - if (!volumes[vol_idx].mounted) { - if (!realfs_mount(drive)) return NULL; - } - FAT32_Volume *vol = &volumes[vol_idx]; +static FAT32_FileHandle* realfs_open_from_vol(FAT32_Volume *vol, const char *path, const char *mode) { + if (!vol || !vol->mounted) return NULL; // Parse path to find start cluster uint32_t current_cluster = vol->root_cluster; @@ -534,7 +541,7 @@ static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *m FAT32_FileHandle *fh = ramfs_find_free_handle(); // Reuse handle pool if (fh) { fh->valid = true; - fh->drive = drive; + fh->volume = vol; fh->start_cluster = vol->root_cluster; fh->cluster = vol->root_cluster; fh->position = 0; @@ -567,6 +574,10 @@ static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *m uint8_t *cluster_buf = (uint8_t*)kmalloc(vol->sectors_per_cluster * 512); if (!cluster_buf) return NULL; + char lfn_buffer[256]; + bool has_lfn = false; + for(int i=0; i<256; i++) lfn_buffer[i] = 0; + while (search_cluster < 0x0FFFFFF8) { if (realfs_read_cluster(vol, search_cluster, cluster_buf) != 0) break; @@ -575,28 +586,55 @@ static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *m for (int e = 0; e < entries_per_cluster; e++) { if (entry[e].filename[0] == 0) break; // End of dir - if (entry[e].filename[0] == 0xE5) continue; // Deleted - if (entry[e].attributes == 0x0F) continue; // LFN entry - if (entry[e].attributes & ATTR_VOLUME_ID) continue; // Volume label - // Compare name (simplistic 8.3 matching) - char name[13]; - int n = 0; - for (int k = 0; k < 8 && entry[e].filename[k] != ' '; k++) name[n++] = entry[e].filename[k]; - if (entry[e].extension[0] != ' ') { - name[n++] = '.'; - for (int k = 0; k < 3 && entry[e].extension[k] != ' '; k++) name[n++] = entry[e].extension[k]; + if (entry[e].filename[0] == 0xE5) { + has_lfn = false; + continue; // Deleted + } + + if (entry[e].attributes == ATTR_LFN) { + FAT32_LFNEntry *lfn = (FAT32_LFNEntry*)&entry[e]; + if (lfn->order & 0x40) { + for(int i=0; i<256; i++) lfn_buffer[i] = 0; + } + extract_lfn_chars(lfn, lfn_buffer); + has_lfn = true; + continue; + } + + if (entry[e].attributes & ATTR_VOLUME_ID) { + has_lfn = false; + continue; // Volume label + } + + // Compare name + char name[256]; + if (has_lfn && lfn_buffer[0] != 0) { + fs_strcpy(name, lfn_buffer); + has_lfn = false; + } else { + int n = 0; + for (int k = 0; k < 8 && entry[e].filename[k] != ' '; k++) name[n++] = entry[e].filename[k]; + if (entry[e].extension[0] != ' ') { + name[n++] = '.'; + for (int k = 0; k < 3 && entry[e].extension[k] != ' '; k++) name[n++] = entry[e].extension[k]; + } + name[n] = 0; } - name[n] = 0; // Case insensitive compare bool match = true; - for (int c = 0; c < n+1; c++) { - char c1 = name[c]; - char c2 = component[c]; - if (c1 >= 'a' && c1 <= 'z') c1 -= 32; - if (c2 >= 'a' && c2 <= 'z') c2 -= 32; - if (c1 != c2) { match = false; break; } + int clen = fs_strlen(component); + int nlen = fs_strlen(name); + if (clen != nlen) match = false; + else { + for (int c = 0; c < clen; c++) { + char c1 = name[c]; + char c2 = component[c]; + if (c1 >= 'a' && c1 <= 'z') c1 -= 32; + if (c2 >= 'a' && c2 <= 'z') c2 -= 32; + if (c1 != c2) { match = false; break; } + } } if (match) { @@ -680,7 +718,7 @@ static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *m FAT32_FileHandle *fh = ramfs_find_free_handle(); if (fh) { fh->valid = true; - fh->drive = drive; + fh->volume = vol; fh->start_cluster = 0; fh->cluster = 0; fh->position = 0; @@ -706,7 +744,7 @@ static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *m FAT32_FileHandle *fh = ramfs_find_free_handle(); if (fh) { fh->valid = true; - fh->drive = drive; + fh->volume = vol; fh->start_cluster = current_cluster; fh->cluster = current_cluster; fh->position = 0; @@ -733,9 +771,9 @@ static FAT32_FileHandle* realfs_open(char drive, const char *path, const char *m return NULL; } -static int realfs_read(FAT32_FileHandle *handle, void *buffer, int size) { - int vol_idx = handle->drive - 'A'; - FAT32_Volume *vol = &volumes[vol_idx]; +static int realfs_read_file(FAT32_FileHandle *handle, void *buffer, int size) { + FAT32_Volume *vol = (FAT32_Volume*)handle->volume; + if (!vol) return 0; uint8_t *cluster_buf = (uint8_t*)kmalloc(vol->sectors_per_cluster * 512); if (!cluster_buf) return 0; @@ -812,9 +850,9 @@ static uint32_t realfs_allocate_cluster(FAT32_Volume *vol) { return 0; // Full } -static int realfs_write(FAT32_FileHandle *handle, const void *buffer, int size) { - int vol_idx = handle->drive - 'A'; - FAT32_Volume *vol = &volumes[vol_idx]; +static int realfs_write_file(FAT32_FileHandle *handle, const void *buffer, int size) { + FAT32_Volume *vol = (FAT32_Volume*)handle->volume; + if (!vol) return 0; if (handle->start_cluster == 0) { uint32_t new_cluster = realfs_allocate_cluster(vol); @@ -944,12 +982,8 @@ static int realfs_write(FAT32_FileHandle *handle, const void *buffer, int size) return bytes_written; } -static bool realfs_delete(char drive, const char *path) { - int vol_idx = drive - 'A'; - if (!volumes[vol_idx].mounted) { - if (!realfs_mount(drive)) return false; - } - FAT32_Volume *vol = &volumes[vol_idx]; +static bool realfs_delete_from_vol(FAT32_Volume *vol, const char *path) { + if (!vol || !vol->mounted) return false; // Parse path to find start cluster and directory entry location uint32_t current_cluster = vol->root_cluster; @@ -983,6 +1017,10 @@ static bool realfs_delete(char drive, const char *path) { bool found = false; uint32_t search_cluster = current_cluster; + char lfn_buffer[256]; + bool has_lfn = false; + for(int i=0; i<256; i++) lfn_buffer[i] = 0; + while (search_cluster < 0x0FFFFFF8) { if (realfs_read_cluster(vol, search_cluster, cluster_buf) != 0) break; @@ -991,28 +1029,54 @@ static bool realfs_delete(char drive, const char *path) { for (int e = 0; e < entries_per_cluster; e++) { if (entry[e].filename[0] == 0) break; - if (entry[e].filename[0] == 0xE5) continue; - if (entry[e].attributes == 0x0F) continue; // Skip LFN entries - if (entry[e].attributes & ATTR_VOLUME_ID) continue; // Skip volume label - // Format name and compare - char name[13]; - int n = 0; - for (int k = 0; k < 8 && entry[e].filename[k] != ' '; k++) name[n++] = entry[e].filename[k]; - if (entry[e].extension[0] != ' ') { - name[n++] = '.'; - for (int k = 0; k < 3 && entry[e].extension[k] != ' '; k++) name[n++] = entry[e].extension[k]; + if (entry[e].filename[0] == 0xE5) { + has_lfn = false; + continue; + } + + if (entry[e].attributes == ATTR_LFN) { + FAT32_LFNEntry *lfn = (FAT32_LFNEntry*)&entry[e]; + if (lfn->order & 0x40) { + for(int i=0; i<256; i++) lfn_buffer[i] = 0; + } + extract_lfn_chars(lfn, lfn_buffer); + has_lfn = true; + continue; + } + + if (entry[e].attributes & ATTR_VOLUME_ID) { + has_lfn = false; + continue; + } + + char name[256]; + if (has_lfn && lfn_buffer[0] != 0) { + fs_strcpy(name, lfn_buffer); + has_lfn = false; + } else { + int n = 0; + for (int k = 0; k < 8 && entry[e].filename[k] != ' '; k++) name[n++] = entry[e].filename[k]; + if (entry[e].extension[0] != ' ') { + name[n++] = '.'; + for (int k = 0; k < 3 && entry[e].extension[k] != ' '; k++) name[n++] = entry[e].extension[k]; + } + name[n] = 0; } - name[n] = 0; // Case insensitive compare bool match = true; - for (int c = 0; c < n+1; c++) { - char c1 = name[c]; - char c2 = component[c]; - if (c1 >= 'a' && c1 <= 'z') c1 -= 32; - if (c2 >= 'a' && c2 <= 'z') c2 -= 32; - if (c1 != c2) { match = false; break; } + int clen = fs_strlen(component); + int nlen = fs_strlen(name); + if (clen != nlen) match = false; + else { + for (int c = 0; c < clen; c++) { + char c1 = name[c]; + char c2 = component[c]; + if (c1 >= 'a' && c1 <= 'z') c1 -= 32; + if (c2 >= 'a' && c2 <= 'z') c2 -= 32; + if (c1 != c2) { match = false; break; } + } } if (match) { @@ -1102,15 +1166,32 @@ static bool realfs_delete(char drive, const char *path) { return true; } -static int realfs_list_directory(char drive, const char *path, FAT32_FileInfo *entries, int max_entries) { - int vol_idx = drive - 'A'; - if (!volumes[vol_idx].mounted) { - if (!realfs_mount(drive)) return 0; - } - FAT32_Volume *vol = &volumes[vol_idx]; +static void extract_lfn_chars(FAT32_LFNEntry *lfn, char *buffer) { + int order = lfn->order & 0x1F; + if (order < 1 || order > 20) return; + int offset = (order - 1) * 13; + for (int i = 0; i < 5; i++) { + uint16_t c = lfn->name1[i]; + if (c == 0x0000 || c == 0xFFFF) { buffer[offset] = 0; return; } + buffer[offset++] = (char)(c & 0xFF); + } + for (int i = 0; i < 6; i++) { + uint16_t c = lfn->name2[i]; + if (c == 0x0000 || c == 0xFFFF) { buffer[offset] = 0; return; } + buffer[offset++] = (char)(c & 0xFF); + } + for (int i = 0; i < 2; i++) { + uint16_t c = lfn->name3[i]; + if (c == 0x0000 || c == 0xFFFF) { buffer[offset] = 0; return; } + buffer[offset++] = (char)(c & 0xFF); + } +} - FAT32_FileHandle *dir_handle = realfs_open(drive, path, "r"); +static int realfs_list_directory_vol(FAT32_Volume *vol, const char *path, FAT32_FileInfo *entries, int max_entries) { + if (!vol || !vol->mounted) return 0; + + FAT32_FileHandle *dir_handle = realfs_open_from_vol(vol, path, "r"); if (!dir_handle) return 0; uint32_t current_cluster = dir_handle->start_cluster; @@ -1120,6 +1201,10 @@ static int realfs_list_directory(char drive, const char *path, FAT32_FileInfo *e uint8_t *cluster_buf = (uint8_t*)kmalloc(vol->sectors_per_cluster * 512); if (!cluster_buf) return 0; + char lfn_buffer[256]; + bool has_lfn = false; + for(int i=0; i<256; i++) lfn_buffer[i] = 0; + while (current_cluster < 0x0FFFFFF8 && count < max_entries) { if (realfs_read_cluster(vol, current_cluster, cluster_buf) != 0) break; @@ -1128,19 +1213,41 @@ static int realfs_list_directory(char drive, const char *path, FAT32_FileInfo *e for (int e = 0; e < entries_per_cluster && count < max_entries; e++) { if (entry[e].filename[0] == 0) break; - if (entry[e].filename[0] == 0xE5) continue; - if (entry[e].attributes == 0x0F) continue; // Skip LFN entries - if (entry[e].attributes & ATTR_VOLUME_ID) continue; // Skip volume label - // Format name - char name[13]; - int n = 0; - for (int k = 0; k < 8 && entry[e].filename[k] != ' '; k++) name[n++] = entry[e].filename[k]; - if (entry[e].extension[0] != ' ') { - name[n++] = '.'; - for (int k = 0; k < 3 && entry[e].extension[k] != ' '; k++) name[n++] = entry[e].extension[k]; + if (entry[e].filename[0] == 0xE5) { + has_lfn = false; + continue; + } + + if (entry[e].attributes == ATTR_LFN) { + FAT32_LFNEntry *lfn = (FAT32_LFNEntry*)&entry[e]; + if (lfn->order & 0x40) { + for(int i=0; i<256; i++) lfn_buffer[i] = 0; + } + extract_lfn_chars(lfn, lfn_buffer); + has_lfn = true; + continue; + } + + if (entry[e].attributes & ATTR_VOLUME_ID) { + has_lfn = false; + continue; + } + + char name[256]; + if (has_lfn && lfn_buffer[0] != 0) { + fs_strcpy(name, lfn_buffer); + has_lfn = false; + } else { + // Short name parsing + int n = 0; + for (int k = 0; k < 8 && entry[e].filename[k] != ' '; k++) name[n++] = entry[e].filename[k]; + if (entry[e].extension[0] != ' ') { + name[n++] = '.'; + for (int k = 0; k < 3 && entry[e].extension[k] != ' '; k++) name[n++] = entry[e].extension[k]; + } + name[n] = 0; } - name[n] = 0; // Skip . and .. if (fs_strcmp(name, ".") == 0 || fs_strcmp(name, "..") == 0) continue; @@ -1160,6 +1267,328 @@ static int realfs_list_directory(char drive, const char *path, FAT32_FileInfo *e } +// ============================================================================ +// VFS Adapters +// ============================================================================ + +static void* vfs_ramfs_open(void *fs_private, const char *rel_path, const char *mode) { + (void)fs_private; + return ramfs_open(rel_path, mode); +} + +static void vfs_ramfs_close(void *fs_private, void *file_handle) { + (void)fs_private; + fat32_close((FAT32_FileHandle*)file_handle); +} + +static int vfs_ramfs_read(void *fs_private, void *file_handle, void *buf, int size) { + (void)fs_private; + return ramfs_read((FAT32_FileHandle*)file_handle, buf, size); +} + +static int vfs_ramfs_write(void *fs_private, void *file_handle, const void *buf, int size) { + (void)fs_private; + return ramfs_write((FAT32_FileHandle*)file_handle, buf, size); +} + +static int vfs_ramfs_seek(void *fs_private, void *file_handle, int offset, int whence) { + (void)fs_private; + return fat32_seek((FAT32_FileHandle*)file_handle, offset, whence); +} + +static int vfs_ramfs_readdir(void *fs_private, const char *rel_path, vfs_dirent_t *entries, int max) { + (void)fs_private; + int count = 0; + char normalized[FAT32_MAX_PATH]; + fat32_normalize_path(rel_path, normalized); + if (normalized[0] == '\0') { + normalized[0] = '/'; + normalized[1] = '\0'; + } + + uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); + for (int i = 0; i < MAX_FILES && count < max; i++) { + bool match = false; + if (files[i].used && files[i].filename[0] != '\0') { + if (fs_strcmp(files[i].parent_path, normalized) == 0) match = true; + + // Root unification: Treat "", "/", and "A:/" as root parent + if (!match && normalized[0] == '/' && normalized[1] == '\0') { + if (files[i].parent_path[0] == '\0' || + fs_strcmp(files[i].parent_path, "/") == 0 || + fs_strcmp(files[i].parent_path, "A:/") == 0) { + match = true; + } + } + } + + if (match) { + fs_strcpy(entries[count].name, files[i].filename); + entries[count].size = files[i].size; + entries[count].is_directory = (files[i].attributes & ATTR_DIRECTORY) ? 1 : 0; + entries[count].start_cluster = files[i].start_cluster; + entries[count].write_date = 0; // Not tracked in RAMFS for now + entries[count].write_time = 0; + count++; + } + } + spinlock_release_irqrestore(&fat32_lock, rflags); + return count; +} + +static bool vfs_ramfs_mkdir(void *fs_private, const char *rel_path) { + (void)fs_private; + return fat32_mkdir(rel_path); +} + +static bool vfs_ramfs_rmdir(void *fs_private, const char *rel_path) { + (void)fs_private; + return fat32_rmdir(rel_path); +} + +static bool vfs_ramfs_unlink(void *fs_private, const char *rel_path) { + (void)fs_private; + return fat32_delete(rel_path); +} + +static bool vfs_ramfs_rename(void *fs_private, const char *old_path, const char *new_path) { + (void)fs_private; + return fat32_rename(old_path, new_path); +} + +static bool vfs_ramfs_exists(void *fs_private, const char *rel_path) { + (void)fs_private; + char normalized[FAT32_MAX_PATH]; + fat32_normalize_path(rel_path, normalized); + if (normalized[0] == '/' && normalized[1] == '\0') return true; + uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); + bool exists = (ramfs_find_file(normalized) != NULL); + spinlock_release_irqrestore(&fat32_lock, rflags); + return exists; +} + +static bool vfs_ramfs_is_dir(void *fs_private, const char *rel_path) { + (void)fs_private; + char normalized[FAT32_MAX_PATH]; + fat32_normalize_path(rel_path, normalized); + if (normalized[0] == '/' && normalized[1] == '\0') return true; + uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); + FileEntry *entry = ramfs_find_file(normalized); + bool is_dir = (entry && (entry->attributes & ATTR_DIRECTORY)); + spinlock_release_irqrestore(&fat32_lock, rflags); + return is_dir; +} + +static int vfs_ramfs_get_info(void *fs_private, const char *rel_path, vfs_dirent_t *info) { + (void)fs_private; + char normalized[FAT32_MAX_PATH]; + fat32_normalize_path(rel_path, normalized); + + uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); + FileEntry *entry = ramfs_find_file(normalized); + if (!entry) { + spinlock_release_irqrestore(&fat32_lock, rflags); + return -1; + } + + fs_strcpy(info->name, entry->filename); + info->size = entry->size; + info->is_directory = (entry->attributes & ATTR_DIRECTORY) ? 1 : 0; + info->start_cluster = entry->start_cluster; + info->write_date = 0; + info->write_time = 0; + + spinlock_release_irqrestore(&fat32_lock, rflags); + return 0; +} + +static uint32_t vfs_fat_get_position(void *file_handle) { + return ((FAT32_FileHandle*)file_handle)->position; +} + +static uint32_t vfs_fat_get_size(void *file_handle) { + return ((FAT32_FileHandle*)file_handle)->size; +} + +static struct vfs_fs_ops ramfs_ops = { + .open = vfs_ramfs_open, + .close = vfs_ramfs_close, + .read = vfs_ramfs_read, + .write = vfs_ramfs_write, + .seek = vfs_ramfs_seek, + .readdir = vfs_ramfs_readdir, + .mkdir = vfs_ramfs_mkdir, + .rmdir = vfs_ramfs_rmdir, + .unlink = vfs_ramfs_unlink, + .rename = vfs_ramfs_rename, + .exists = vfs_ramfs_exists, + .is_dir = vfs_ramfs_is_dir, + .get_info = vfs_ramfs_get_info, + .get_position = vfs_fat_get_position, + .get_size = vfs_fat_get_size +}; + +struct vfs_fs_ops* fat32_get_ramfs_ops(void) { + return &ramfs_ops; +} + +// --- RealFS VFS Wrappers --- + +static void* vfs_realfs_open(void *fs_private, const char *rel_path, const char *mode) { + uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); + FAT32_FileHandle* fh = realfs_open_from_vol((FAT32_Volume*)fs_private, rel_path, mode); + spinlock_release_irqrestore(&fat32_lock, rflags); + return fh; +} + +static void vfs_realfs_close(void *fs_private, void *file_handle) { + (void)fs_private; + fat32_close((FAT32_FileHandle*)file_handle); +} + +static int vfs_realfs_read(void *fs_private, void *file_handle, void *buf, int size) { + (void)fs_private; + uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); + int ret = realfs_read_file((FAT32_FileHandle*)file_handle, buf, size); + spinlock_release_irqrestore(&fat32_lock, rflags); + return ret; +} + +static int vfs_realfs_write(void *fs_private, void *file_handle, const void *buf, int size) { + (void)fs_private; + uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); + int ret = realfs_write_file((FAT32_FileHandle*)file_handle, buf, size); + spinlock_release_irqrestore(&fat32_lock, rflags); + return ret; +} + +static int vfs_realfs_seek(void *fs_private, void *file_handle, int offset, int whence) { + (void)fs_private; + return fat32_seek((FAT32_FileHandle*)file_handle, offset, whence); +} + +static int vfs_realfs_readdir(void *fs_private, const char *rel_path, vfs_dirent_t *entries, int max) { + uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); + FAT32_FileInfo *fat_entries = (FAT32_FileInfo*)kmalloc(max * sizeof(FAT32_FileInfo)); + if (!fat_entries) { spinlock_release_irqrestore(&fat32_lock, rflags); return 0; } + + int count = realfs_list_directory_vol((FAT32_Volume*)fs_private, rel_path, fat_entries, max); + for (int i = 0; i < count; i++) { + fs_strcpy(entries[i].name, fat_entries[i].name); + entries[i].size = fat_entries[i].size; + entries[i].is_directory = fat_entries[i].is_directory; + entries[i].write_date = fat_entries[i].write_date; + entries[i].write_time = fat_entries[i].write_time; + } + + kfree(fat_entries); + spinlock_release_irqrestore(&fat32_lock, rflags); + return count; +} + +static bool vfs_realfs_mkdir(void *fs_private, const char *rel_path) { + uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); + bool ret = realfs_mkdir_vol((FAT32_Volume*)fs_private, rel_path); + spinlock_release_irqrestore(&fat32_lock, rflags); + return ret; +} + +static bool vfs_realfs_rmdir(void *fs_private, const char *rel_path) { + (void)fs_private; (void)rel_path; + return false; // Requires full tree deletion support +} + +static bool vfs_realfs_unlink(void *fs_private, const char *rel_path) { + uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); + bool ret = realfs_delete_from_vol((FAT32_Volume*)fs_private, rel_path); + spinlock_release_irqrestore(&fat32_lock, rflags); + return ret; +} + +static bool vfs_realfs_rename(void *fs_private, const char *old_path, const char *new_path) { + (void)fs_private; (void)old_path; (void)new_path; + return false; // Not implemented yet for FAT32 +} + +static bool vfs_realfs_exists(void *fs_private, const char *rel_path) { + uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); + FAT32_FileHandle *fh = realfs_open_from_vol((FAT32_Volume*)fs_private, rel_path, "r"); + if (fh) { + fat32_close(fh); + spinlock_release_irqrestore(&fat32_lock, rflags); + return true; + } + spinlock_release_irqrestore(&fat32_lock, rflags); + return false; +} + +static bool vfs_realfs_is_dir(void *fs_private, const char *rel_path) { + if (fs_strcmp(rel_path, "/") == 0 || fs_strcmp(rel_path, "") == 0) return true; + // Real implementation requires verifying DIR attribute + uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); + FAT32_FileHandle *fh = realfs_open_from_vol((FAT32_Volume*)fs_private, rel_path, "r"); + if (fh) { + // Limited metadata logic for now, best effort + fat32_close(fh); + } + spinlock_release_irqrestore(&fat32_lock, rflags); + return true; +} + +static int vfs_realfs_get_info(void *fs_private, const char *rel_path, vfs_dirent_t *info) { + uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); + FAT32_FileHandle *fh = realfs_open_from_vol((FAT32_Volume*)fs_private, rel_path, "r"); + if (fh) { + extract_filename(rel_path, info->name); + info->size = fh->size; + info->is_directory = (fs_strcmp(rel_path, "/") == 0 || fs_strcmp(rel_path, "") == 0) ? 1 : 0; + fat32_close(fh); + spinlock_release_irqrestore(&fat32_lock, rflags); + return 0; + } + spinlock_release_irqrestore(&fat32_lock, rflags); + return -1; +} + +static struct vfs_fs_ops realfs_ops = { + .open = vfs_realfs_open, + .close = vfs_realfs_close, + .read = vfs_realfs_read, + .write = vfs_realfs_write, + .seek = vfs_realfs_seek, + .readdir = vfs_realfs_readdir, + .mkdir = vfs_realfs_mkdir, + .rmdir = vfs_realfs_rmdir, + .unlink = vfs_realfs_unlink, + .rename = vfs_realfs_rename, + .exists = vfs_realfs_exists, + .is_dir = vfs_realfs_is_dir, + .get_info = vfs_realfs_get_info, + .get_position = vfs_fat_get_position, + .get_size = vfs_fat_get_size +}; + +struct vfs_fs_ops* fat32_get_realfs_ops(void) { + return &realfs_ops; +} + +void* fat32_mount_volume(void *disk_ptr) { + if (real_volume_count >= MAX_REAL_VOLUMES) return NULL; + + FAT32_Volume *vol = (FAT32_Volume*)kmalloc(sizeof(FAT32_Volume)); + if (!vol) return NULL; + + vol->mounted = false; + if (realfs_mount_volume(vol, (Disk*)disk_ptr)) { + real_volumes[real_volume_count++] = vol; + return vol; + } + + kfree(vol); + return NULL; +} + + // === Public API (Dispatch) === void fat32_init(void) { @@ -1188,25 +1617,20 @@ void fat32_init(void) { } } - // Create root directory entry for RAMFS - FileEntry *root = ramfs_find_free_entry(); - if (root) { - root->used = true; - root->filename[0] = 0; - fs_strcpy(root->full_path, "/"); - root->start_cluster = 2; - root->size = 0; - root->attributes = ATTR_DIRECTORY; - fat_table[2] = 0xFFFFFFFF; - } - + // Reserve cluster 2 for root directory + fat_table[2] = 0xFFFFFFFF; next_cluster = 3; + + // Register with VFS as root mount + vfs_mount("/", "ramfs", "ramfs", fat32_get_ramfs_ops(), NULL); + current_dir[0] = '/'; current_dir[1] = 0; current_drive = 'A'; // Reset Volumes - for(int i=0; i<26; i++) volumes[i].mounted = false; + for(int i=0; i= 'a' && drive <= 'z') drive -= 32; - Disk *d = disk_get_by_letter(drive); - if (d) { - current_drive = drive; - current_dir[0] = '/'; - current_dir[1] = 0; - return true; - } - return false; + (void)drive; + return false; // Obsolete in VFS } char fat32_get_current_drive(void) { @@ -1241,9 +1658,27 @@ FAT32_FileHandle* fat32_open(const char *path, const char *mode) { char normalized[FAT32_MAX_PATH]; fat32_normalize_path(p, normalized); handle = ramfs_open(normalized, mode); - } else { - // Real Drive - handle = realfs_open(drive, p, mode); + } else if (drive != 0) { + // Find volume by drive letter (B=0, C=1...) + Disk *d = disk_get_by_letter(drive); + if (d) { + for (int i = 0; i < real_volume_count; i++) { + if (real_volumes[i]->disk == d) { + handle = realfs_open_from_vol(real_volumes[i], p, mode); + break; + } + } + } + } else if (path[0] == '/') { + // Absolute VFS path - bypass legacy drive letters + spinlock_release_irqrestore(&fat32_lock, rflags); + vfs_file_t *vf = vfs_open(path, mode); + if (vf && vf->fs_handle) { + // WARNING: This assumes the fs_handle is a FAT32_FileHandle + // This is true for all our current physical/ramfs mounts + return (FAT32_FileHandle*)vf->fs_handle; + } + return NULL; } spinlock_release_irqrestore(&fat32_lock, rflags); @@ -1251,25 +1686,22 @@ FAT32_FileHandle* fat32_open(const char *path, const char *mode) { } void fat32_close(FAT32_FileHandle *handle) { - // SMP: Use FAT32 spinlock uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); if (handle && handle->valid) { - if (handle->drive != 'A' && handle->mode != 0) { // Both read and write modes for real drives - Disk *d = disk_get_by_letter(handle->drive); + if (handle->volume != NULL && handle->mode != 0) { // Both read and write modes for real drives + FAT32_Volume *vol = (FAT32_Volume*)handle->volume; + Disk *d = vol->disk; if (d && handle->dir_sector != 0) { uint8_t *buf = (uint8_t*)kmalloc(512); if (buf) { if (d->read_sector(d, handle->dir_sector, buf) == 0) { FAT32_DirEntry *entry = (FAT32_DirEntry*)(buf + handle->dir_offset); - // Always update file size entry->file_size = handle->size; - // Update start cluster if it exists if (handle->start_cluster != 0) { entry->start_cluster_high = (handle->start_cluster >> 16); entry->start_cluster_low = (handle->start_cluster & 0xFFFF); } - if (d->write_sector(d, handle->dir_sector, buf) != 0) { - } + d->write_sector(d, handle->dir_sector, buf); } kfree(buf); } @@ -1289,10 +1721,10 @@ int fat32_read(FAT32_FileHandle *handle, void *buffer, int size) { } int ret = 0; - if (handle->drive == 'A') { + if (handle->volume == NULL) { ret = ramfs_read(handle, buffer, size); } else { - ret = realfs_read(handle, buffer, size); + ret = realfs_read_file(handle, buffer, size); } spinlock_release_irqrestore(&fat32_lock, rflags); @@ -1308,10 +1740,10 @@ int fat32_write(FAT32_FileHandle *handle, const void *buffer, int size) { } int ret = 0; - if (handle->drive == 'A') { + if (handle->volume == NULL) { ret = ramfs_write(handle, buffer, size); } else { - ret = realfs_write(handle, buffer, size); + ret = realfs_write_file(handle, buffer, size); } spinlock_release_irqrestore(&fat32_lock, rflags); @@ -1336,7 +1768,7 @@ int fat32_seek(FAT32_FileHandle *handle, int offset, int whence) { handle->position = new_position; // Both RealFS and RAMFS must accurately re-walk their cluster chains - if (handle->drive == 'A') { + if (handle->volume == NULL) { handle->cluster = handle->start_cluster; uint32_t pos = 0; while (pos + FAT32_CLUSTER_SIZE <= handle->position) { @@ -1347,8 +1779,7 @@ int fat32_seek(FAT32_FileHandle *handle, int offset, int whence) { } } else { // Re-walk to find current cluster - int vol_idx = handle->drive - 'A'; - FAT32_Volume *vol = &volumes[vol_idx]; + FAT32_Volume *vol = (FAT32_Volume*)handle->volume; uint32_t cluster_size = vol->sectors_per_cluster * 512; handle->cluster = handle->start_cluster; @@ -1365,12 +1796,8 @@ int fat32_seek(FAT32_FileHandle *handle, int offset, int whence) { return new_position; } -static bool realfs_mkdir(char drive, const char *path) { - int vol_idx = drive - 'A'; - if (!volumes[vol_idx].mounted) { - if (!realfs_mount(drive)) return false; - } - FAT32_Volume *vol = &volumes[vol_idx]; +static bool realfs_mkdir_vol(FAT32_Volume *vol, const char *path) { + if (!vol || !vol->mounted) return false; // Find parent directory and name of new directory char parent_path[FAT32_MAX_PATH]; @@ -1379,13 +1806,13 @@ static bool realfs_mkdir(char drive, const char *path) { extract_filename(path, dirname); // Open parent directory - FAT32_FileHandle *parent_fh = realfs_open(drive, parent_path, "r"); + FAT32_FileHandle *parent_fh = realfs_open_from_vol(vol, parent_path, "r"); if (!parent_fh) return false; uint32_t parent_cluster = parent_fh->start_cluster; fat32_close(parent_fh); // Check if it already exists - FAT32_FileHandle *check_fh = realfs_open(drive, path, "r"); + FAT32_FileHandle *check_fh = realfs_open_from_vol(vol, path, "r"); if (check_fh) { fat32_close(check_fh); return false; @@ -1503,10 +1930,19 @@ bool fat32_mkdir(const char *path) { uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); if (drive != 'A') { - bool res = realfs_mkdir(drive, p); - wm_notify_fs_change(); + Disk *d = disk_get_by_letter(drive); + if (d) { + for (int i = 0; i < real_volume_count; i++) { + if (real_volumes[i]->disk == d) { + bool res = realfs_mkdir_vol(real_volumes[i], p); + wm_notify_fs_change(); + spinlock_release_irqrestore(&fat32_lock, rflags); + return res; + } + } + } spinlock_release_irqrestore(&fat32_lock, rflags); - return res; + return false; } char normalized[FAT32_MAX_PATH]; @@ -1585,8 +2021,7 @@ bool fat32_delete(const char *path) { wm_notify_fs_change(); result = true; } else { - // Real FAT32 deletion - result = realfs_delete(drive, p); + // Obsolete RealFS realfs_delete } spinlock_release_irqrestore(&fat32_lock, rflags); @@ -1594,7 +2029,23 @@ bool fat32_delete(const char *path) { } int fat32_get_info(const char *path, FAT32_FileInfo *info) { - // SMP: Use FAT32 spinlock + if (path[0] == '/') { + // Absolute path - route via VFS + vfs_dirent_t v_info; + int res = vfs_get_info(path, &v_info); + if (res == 0) { + fs_strcpy(info->name, v_info.name); + info->size = v_info.size; + info->is_directory = v_info.is_directory; + info->start_cluster = v_info.start_cluster; + info->write_date = v_info.write_date; + info->write_time = v_info.write_time; + return 0; + } + return -1; + } + + // Legacy drive-based path uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); const char *p = path; @@ -1613,21 +2064,29 @@ int fat32_get_info(const char *path, FAT32_FileInfo *info) { result = 0; } } else { - FAT32_FileHandle *fh = realfs_open(drive, p, "r"); - if (fh) { - - extract_filename(p, info->name); - info->size = fh->size; - info->start_cluster = fh->start_cluster; - - if (fs_strcmp(p, "/") == 0 || fs_strcmp(p, "") == 0) { - info->is_directory = 1; - } else { - - info->is_directory = fat32_is_directory(path); + Disk *d = disk_get_by_letter(drive); + if (d) { + for (int i = 0; i < real_volume_count; i++) { + if (real_volumes[i]->disk == d) { + FAT32_FileHandle *fh = realfs_open_from_vol(real_volumes[i], p, "r"); + if (fh) { + extract_filename(p, info->name); + info->size = fh->size; + info->start_cluster = fh->start_cluster; + + if (fs_strcmp(p, "/") == 0 || fs_strcmp(p, "") == 0) { + info->is_directory = 1; + } else { + // Temporary: Assume if it opens as "r" and it's not root, + // we'd need better dir check. For now just 0. + info->is_directory = 0; + } + fat32_close(fh); + result = 0; + break; + } + } } - fat32_close(fh); - result = 0; } } @@ -1649,10 +2108,18 @@ bool fat32_exists(const char *path) { exists = (ramfs_find_file(normalized) != NULL); } else { // RealFS check - FAT32_FileHandle *fh = realfs_open(drive, p, "r"); - if (fh) { - exists = true; - fat32_close(fh); + Disk *d = disk_get_by_letter(drive); + if (d) { + for (int i = 0; i < real_volume_count; i++) { + if (real_volumes[i]->disk == d) { + FAT32_FileHandle *fh = realfs_open_from_vol(real_volumes[i], p, "r"); + if (fh) { + exists = true; + fat32_close(fh); + break; + } + } + } } } @@ -1719,14 +2186,19 @@ bool fat32_is_directory(const char *path) { FileEntry *entry = ramfs_find_file(normalized); is_dir = (entry && (entry->attributes & ATTR_DIRECTORY)); } else { - FAT32_FileHandle *fh = realfs_open(drive, p, "r"); - if (fh) { - - if (fs_strcmp(p, "/") == 0 || fs_strcmp(p, "") == 0) is_dir = true; - else { - + Disk *d = disk_get_by_letter(drive); + if (d) { + for (int i = 0; i < real_volume_count; i++) { + if (real_volumes[i]->disk == d) { + FAT32_FileHandle *fh = realfs_open_from_vol(real_volumes[i], p, "r"); + if (fh) { + if (fs_strcmp(p, "/") == 0 || fs_strcmp(p, "") == 0) is_dir = true; + else is_dir = false; + fat32_close(fh); + break; + } + } } - fat32_close(fh); } } @@ -1735,7 +2207,25 @@ bool fat32_is_directory(const char *path) { } int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entries) { - // SMP: Use FAT32 spinlock + if (path[0] == '/') { + // Absolute path - Route through unified VFS + vfs_dirent_t *v_entries = (vfs_dirent_t*)kmalloc(sizeof(vfs_dirent_t) * max_entries); + if (!v_entries) return 0; + + int count = vfs_list_directory(path, v_entries, max_entries); + for (int i = 0; i < count; i++) { + fs_strcpy(entries[i].name, v_entries[i].name); + entries[i].size = v_entries[i].size; + entries[i].is_directory = v_entries[i].is_directory; + entries[i].start_cluster = v_entries[i].start_cluster; + entries[i].write_date = v_entries[i].write_date; + entries[i].write_time = v_entries[i].write_time; + } + kfree(v_entries); + return count; + } + + // Legacy drive-based path (A:, B: etc) uint64_t rflags = spinlock_acquire_irqsave(&fat32_lock); const char *p = path; @@ -1746,8 +2236,6 @@ int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entr char normalized[FAT32_MAX_PATH]; fat32_normalize_path(p, normalized); - - for (int i = 0; i < MAX_FILES && count < max_entries; i++) { if (files[i].used && fs_strcmp(files[i].parent_path, normalized) == 0) { fs_strcpy(entries[count].name, files[i].filename); @@ -1757,8 +2245,6 @@ int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entr count++; } } - } else { - count = realfs_list_directory(drive, p, entries, max_entries); } spinlock_release_irqrestore(&fat32_lock, rflags); diff --git a/src/fs/fat32.h b/src/fs/fat32.h index 28f4f40..ec085af 100644 --- a/src/fs/fat32.h +++ b/src/fs/fat32.h @@ -25,7 +25,7 @@ typedef struct { uint16_t num_heads; // Number of heads uint32_t hidden_sectors; // Hidden sectors uint32_t total_sectors_32; // Total sectors 32-bit - + // FAT32 Specific uint32_t sectors_per_fat_32; // Sectors per FAT 32-bit uint16_t flags; // Flags @@ -61,6 +61,18 @@ typedef struct { uint32_t file_size; // File size } __attribute__((packed)) FAT32_DirEntry; +// Long File Name Directory Entry (32 bytes) +typedef struct { + uint8_t order; // Sequence number (0x40 = last, | index) + uint16_t name1[5]; // Characters 1-5 (UCS-2) + uint8_t attr; // Always 0x0F + uint8_t type; // Always 0x00 + uint8_t checksum; // Checksum of short name + uint16_t name2[6]; // Characters 6-11 (UCS-2) + uint16_t first_cluster; // Always 0x0000 + uint16_t name3[2]; // Characters 12-13 (UCS-2) +} __attribute__((packed)) FAT32_LFNEntry; + // File Attributes #define ATTR_READ_ONLY 0x01 #define ATTR_HIDDEN 0x02 @@ -70,6 +82,7 @@ typedef struct { #define ATTR_ARCHIVE 0x20 #define ATTR_DEVICE 0x40 #define ATTR_RESERVED 0x80 +#define ATTR_LFN 0x0F // LFN marker (all of the above ORed) // FAT32 Constants #define FAT32_SECTOR_SIZE 512 @@ -88,7 +101,7 @@ typedef struct { bool valid; // Is this handle valid? uint32_t dir_sector; // Sector containing the directory entry uint32_t dir_offset; // Offset within that sector - char drive; // Drive letter (A, B, ...) + void *volume; // Pointer to owning FAT32_Volume (or NULL for ramfs) } FAT32_FileHandle; // Directory Entry Info (for listing) @@ -101,12 +114,23 @@ typedef struct { uint16_t write_time; } FAT32_FileInfo; +// === VFS Integration === +// Forward-declared VFS ops type (defined in vfs.h) +struct vfs_fs_ops; + +// Get VFS ops structs for registration +struct vfs_fs_ops* fat32_get_ramfs_ops(void); +struct vfs_fs_ops* fat32_get_realfs_ops(void); + +// Mount a real FAT32 volume from a block device — returns fs_private for VFS +void* fat32_mount_volume(void *disk_ptr); + // === Function Declarations === // Initialization void fat32_init(void); -// File Operations +// File Operations (backward-compat wrappers — dispatch through VFS) FAT32_FileHandle* fat32_open(const char *path, const char *mode); void fat32_close(FAT32_FileHandle *handle); int fat32_read(FAT32_FileHandle *handle, void *buffer, int size); @@ -124,7 +148,10 @@ bool fat32_is_directory(const char *path); // Listing int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entries); -// Working Directory +// Info +int fat32_get_info(const char *path, FAT32_FileInfo *info); + +// Working Directory (backward compat — wraps VFS path tracking) bool fat32_chdir(const char *path); void fat32_get_current_dir(char *buffer, int size); bool fat32_change_drive(char drive); diff --git a/src/fs/vfs.c b/src/fs/vfs.c new file mode 100644 index 0000000..eaa5955 --- /dev/null +++ b/src/fs/vfs.c @@ -0,0 +1,760 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. +#include "vfs.h" +#include "memory_manager.h" +#include "spinlock.h" +#include +#include "disk.h" + +// ============================================================================ +// VFS Mount Table and File Handle Pool +// ============================================================================ + +static vfs_mount_t mounts[VFS_MAX_MOUNTS]; +static int mount_count = 0; +static vfs_file_t open_files[VFS_MAX_OPEN_FILES]; +static spinlock_t vfs_lock = SPINLOCK_INIT; + +extern void serial_write(const char *str); +extern void serial_write_num(uint64_t num); + +// ============================================================================ +// String helpers (freestanding — no libc) +// ============================================================================ + +static int vfs_strlen(const char *s) { + int n = 0; + while (s[n]) n++; + return n; +} + +static void vfs_strcpy(char *d, const char *s) { + while ((*d++ = *s++)); +} + +static int vfs_strcmp(const char *a, const char *b) { + while (*a && *a == *b) { a++; b++; } + return (unsigned char)*a - (unsigned char)*b; +} + +static int vfs_strncmp(const char *a, const char *b, int n) { + for (int i = 0; i < n; i++) { + if (a[i] != b[i]) return (unsigned char)a[i] - (unsigned char)b[i]; + if (!a[i]) return 0; + } + return 0; +} + +static bool vfs_starts_with(const char *str, const char *prefix) { + while (*prefix) { + if (*str++ != *prefix++) return false; + } + return true; +} + +// ============================================================================ +// Path Normalization +// ============================================================================ + +void vfs_normalize_path(const char *path, char *normalized) { + // Resolve . and .. components, remove duplicate slashes + char parts[32][128]; + int depth = 0; + int i = 0; + + // Skip leading slash + if (path[0] == '/') i = 1; + + while (path[i]) { + // Skip duplicate slashes + if (path[i] == '/') { i++; continue; } + + // Extract component + int j = 0; + while (path[i] && path[i] != '/' && j < 127) { + parts[depth][j++] = path[i++]; + } + parts[depth][j] = 0; + + if (parts[depth][0] == '.' && parts[depth][1] == 0) { + // "." — skip + } else if (parts[depth][0] == '.' && parts[depth][1] == '.' && parts[depth][2] == 0) { + // ".." — go up + if (depth > 0) depth--; + } else { + depth++; + if (depth >= 32) break; + } + + if (path[i] == '/') i++; + } + + // Reconstruct + normalized[0] = '/'; + int pos = 1; + for (int k = 0; k < depth; k++) { + int l = 0; + while (parts[k][l] && pos < VFS_MAX_PATH - 2) { + normalized[pos++] = parts[k][l++]; + } + if (k < depth - 1) normalized[pos++] = '/'; + } + normalized[pos] = 0; + + // Ensure root is just "/" + if (pos == 1 && normalized[0] == '/') { + normalized[1] = 0; + } +} + +// ============================================================================ +// Mount Resolution — find the longest-prefix mount for a path +// ============================================================================ + +static vfs_mount_t* vfs_resolve_mount(const char *path, const char **rel_path_out) { + vfs_mount_t *best = NULL; + int best_len = -1; + + for (int i = 0; i < mount_count; i++) { + if (!mounts[i].active) continue; + + int mlen = mounts[i].path_len; + + // Root mount "/" matches everything + if (mlen == 1 && mounts[i].path[0] == '/') { + if (best_len < 1) { + best = &mounts[i]; + best_len = 1; + } + continue; + } + + // Check if path starts with this mount point + if (vfs_strncmp(path, mounts[i].path, mlen) == 0) { + // Must be followed by '/' or end of string + if (path[mlen] == '/' || path[mlen] == '\0') { + if (mlen > best_len) { + best = &mounts[i]; + best_len = mlen; + } + } + } + } + + if (best && rel_path_out) { + const char *rel = path + best_len; + // Skip leading slash in relative path + while (*rel == '/') rel++; + *rel_path_out = rel; + } + + return best; +} + +// ============================================================================ +// File Handle Pool +// ============================================================================ + +static vfs_file_t* vfs_alloc_file(void) { + for (int i = 0; i < VFS_MAX_OPEN_FILES; i++) { + if (!open_files[i].valid) { + open_files[i].valid = true; + return &open_files[i]; + } + } + return NULL; +} + +static void vfs_free_file(vfs_file_t *f) { + if (f) { + f->valid = false; + f->fs_handle = NULL; + f->mount = NULL; + } +} + +// ============================================================================ +// Initialization +// ============================================================================ + +void vfs_init(void) { + for (int i = 0; i < VFS_MAX_MOUNTS; i++) { + mounts[i].active = false; + } + for (int i = 0; i < VFS_MAX_OPEN_FILES; i++) { + open_files[i].valid = false; + } + mount_count = 0; + + serial_write("[VFS] Virtual File System initialized\n"); +} + +// ============================================================================ +// Mount / Unmount +// ============================================================================ + +bool vfs_mount(const char *mount_path, const char *device, const char *fs_type, + vfs_fs_ops_t *ops, void *fs_private) { + uint64_t flags = spinlock_acquire_irqsave(&vfs_lock); + + if (mount_count >= VFS_MAX_MOUNTS) { + spinlock_release_irqrestore(&vfs_lock, flags); + serial_write("[VFS] ERROR: Mount table full\n"); + return false; + } + + // Check for duplicate mount + for (int i = 0; i < mount_count; i++) { + if (mounts[i].active && vfs_strcmp(mounts[i].path, mount_path) == 0) { + spinlock_release_irqrestore(&vfs_lock, flags); + serial_write("[VFS] ERROR: Mount point already in use: "); + serial_write(mount_path); + serial_write("\n"); + return false; + } + } + + vfs_mount_t *m = &mounts[mount_count]; + vfs_strcpy(m->path, mount_path); + m->path_len = vfs_strlen(mount_path); + m->ops = ops; + m->fs_private = fs_private; + vfs_strcpy(m->device, device ? device : "none"); + vfs_strcpy(m->fs_type, fs_type ? fs_type : "unknown"); + m->active = true; + mount_count++; + + spinlock_release_irqrestore(&vfs_lock, flags); + + serial_write("[VFS] Mounted "); + serial_write(fs_type); + serial_write(" ("); + serial_write(device ? device : "none"); + serial_write(") at "); + serial_write(mount_path); + serial_write("\n"); + + return true; +} + +bool vfs_umount(const char *mount_path) { + uint64_t flags = spinlock_acquire_irqsave(&vfs_lock); + + for (int i = 0; i < mount_count; i++) { + if (mounts[i].active && vfs_strcmp(mounts[i].path, mount_path) == 0) { + // Close any open files on this mount + for (int j = 0; j < VFS_MAX_OPEN_FILES; j++) { + if (open_files[j].valid && open_files[j].mount == &mounts[i]) { + if (mounts[i].ops->close) { + mounts[i].ops->close(mounts[i].fs_private, open_files[j].fs_handle); + } + vfs_free_file(&open_files[j]); + } + } + + serial_write("[VFS] Unmounted "); + serial_write(mounts[i].path); + serial_write("\n"); + + mounts[i].active = false; + + // Compact array + for (int k = i; k < mount_count - 1; k++) { + mounts[k] = mounts[k + 1]; + } + mount_count--; + + spinlock_release_irqrestore(&vfs_lock, flags); + return true; + } + } + + spinlock_release_irqrestore(&vfs_lock, flags); + return false; +} + +// ============================================================================ +// File Operations +// ============================================================================ + +vfs_file_t* vfs_open(const char *path, const char *mode) { + if (!path || !mode) return NULL; + + // Normalize path + char normalized[VFS_MAX_PATH]; + vfs_normalize_path(path, normalized); + + uint64_t flags = spinlock_acquire_irqsave(&vfs_lock); + + const char *rel_path = NULL; + vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path); + if (!mount || !mount->ops->open) { + spinlock_release_irqrestore(&vfs_lock, flags); + return NULL; + } + + // If rel_path is empty, use root + if (!rel_path || rel_path[0] == '\0') { + rel_path = "/"; + } + + vfs_file_t *vf = vfs_alloc_file(); + if (!vf) { + spinlock_release_irqrestore(&vfs_lock, flags); + serial_write("[VFS] ERROR: No free file handles\n"); + return NULL; + } + + vf->mount = mount; + + // Release lock before calling FS ops (FS has its own locking) + spinlock_release_irqrestore(&vfs_lock, flags); + + void *fs_handle = mount->ops->open(mount->fs_private, rel_path, mode); + if (!fs_handle) { + flags = spinlock_acquire_irqsave(&vfs_lock); + vfs_free_file(vf); + spinlock_release_irqrestore(&vfs_lock, flags); + return NULL; + } + + vf->fs_handle = fs_handle; + return vf; +} + +void vfs_close(vfs_file_t *file) { + if (!file || !file->valid) return; + + vfs_mount_t *mount = file->mount; + if (mount && mount->ops->close) { + mount->ops->close(mount->fs_private, file->fs_handle); + } + + uint64_t flags = spinlock_acquire_irqsave(&vfs_lock); + vfs_free_file(file); + spinlock_release_irqrestore(&vfs_lock, flags); +} + +int vfs_read(vfs_file_t *file, void *buf, int size) { + if (!file || !file->valid || !file->mount) return -1; + if (!file->mount->ops->read) return -1; + return file->mount->ops->read(file->mount->fs_private, file->fs_handle, buf, size); +} + +int vfs_write(vfs_file_t *file, const void *buf, int size) { + if (!file || !file->valid || !file->mount) return -1; + if (!file->mount->ops->write) return -1; + return file->mount->ops->write(file->mount->fs_private, file->fs_handle, buf, size); +} + +int vfs_seek(vfs_file_t *file, int offset, int whence) { + if (!file || !file->valid || !file->mount) return -1; + if (!file->mount->ops->seek) return -1; + return file->mount->ops->seek(file->mount->fs_private, file->fs_handle, offset, whence); +} + +uint32_t vfs_file_position(vfs_file_t *file) { + if (!file || !file->valid || !file->mount) return 0; + if (!file->mount->ops->get_position) return 0; + return file->mount->ops->get_position(file->fs_handle); +} + +uint32_t vfs_file_size(vfs_file_t *file) { + if (!file || !file->valid || !file->mount) return 0; + if (!file->mount->ops->get_size) return 0; + return file->mount->ops->get_size(file->fs_handle); +} + +// ============================================================================ +// Directory Operations +// ============================================================================ + +int vfs_list_directory(const char *path, vfs_dirent_t *entries, int max) { + if (!path || !entries) return -1; + + char normalized[VFS_MAX_PATH]; + vfs_normalize_path(path, normalized); + + const char *rel_path = NULL; + vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path); + + int count = 0; + if (mount && mount->ops->readdir) { + if (!rel_path || rel_path[0] == '\0') rel_path = "/"; + count = mount->ops->readdir(mount->fs_private, rel_path, entries, max); + if (count < 0) count = 0; // Treat as virtual if readdir fails + } + + // Merge in other mount points that are direct children of this path + uint64_t v_flags = spinlock_acquire_irqsave(&vfs_lock); + for (int i = 0; i < mount_count; i++) { + if (!mounts[i].active) continue; + if (vfs_strcmp(mounts[i].path, normalized) == 0) continue; // Skip ourselves + + // Check if mount path starts with current path + if (vfs_starts_with(mounts[i].path, normalized)) { + const char *sub = mounts[i].path + vfs_strlen(normalized); + if (*sub == '/') sub++; // skip slash + + if (*sub != '\0') { + // Extract first component (direct child name) + char comp[VFS_MAX_NAME]; + int j = 0; + while (sub[j] && sub[j] != '/' && j < VFS_MAX_NAME - 1) { + comp[j] = sub[j]; + j++; + } + comp[j] = 0; + + // Check if already in results + bool found = false; + for (int k = 0; k < count; k++) { + if (vfs_strcmp(entries[k].name, comp) == 0) { + found = true; + break; + } + } + + if (!found && count < max) { + vfs_strcpy(entries[count].name, comp); + entries[count].is_directory = 1; + entries[count].size = 0; + entries[count].start_cluster = 0; + count++; + } + } + } + } + spinlock_release_irqrestore(&vfs_lock, v_flags); + + // Special case: Ensure "dev" is visible in "/" + if (vfs_strcmp(normalized, "/") == 0) { + bool found_dev = false; + for (int i = 0; i < count; i++) { + if (vfs_strcmp(entries[i].name, "dev") == 0) { + found_dev = true; + break; + } + } + if (!found_dev && count < max) { + vfs_strcpy(entries[count].name, "dev"); + entries[count].is_directory = 1; + entries[count].size = 0; + entries[count].start_cluster = 0; + count++; + } + } + + // Special case: /dev listing for block devices + if (vfs_strcmp(normalized, "/dev") == 0) { + int dcount = disk_get_count(); + for (int i = 0; i < dcount && count < max; i++) { + Disk *d = disk_get_by_index(i); + if (d) { + bool found = false; + for (int k = 0; k < count; k++) { + if (vfs_strcmp(entries[k].name, d->devname) == 0) { + found = true; + break; + } + } + if (!found) { + vfs_strcpy(entries[count].name, d->devname); + entries[count].size = d->total_sectors * 512; + entries[count].is_directory = d->is_partition ? 1 : 0; + entries[count].start_cluster = 0; + entries[count].write_date = 0; + entries[count].write_time = 0; + count++; + } + } + } + } + + return count; +} + +bool vfs_mkdir(const char *path) { + if (!path) return false; + + char normalized[VFS_MAX_PATH]; + vfs_normalize_path(path, normalized); + + const char *rel_path = NULL; + vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path); + if (!mount || !mount->ops->mkdir) return false; + + if (!rel_path || rel_path[0] == '\0') return false; + + return mount->ops->mkdir(mount->fs_private, rel_path); +} + +bool vfs_rmdir(const char *path) { + if (!path) return false; + + char normalized[VFS_MAX_PATH]; + vfs_normalize_path(path, normalized); + + // Protect root and virtual directories + if (normalized[0] == '/' && normalized[1] == '\0') return false; + if (vfs_strcmp(normalized, "/dev") == 0) return false; + if (vfs_starts_with(normalized, "/dev/")) return false; + + const char *rel_path = NULL; + vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path); + if (!mount || !mount->ops->rmdir) return false; + + if (!rel_path || rel_path[0] == '\0') return false; + + return mount->ops->rmdir(mount->fs_private, rel_path); +} + +bool vfs_delete(const char *path) { + if (!path) return false; + + char normalized[VFS_MAX_PATH]; + vfs_normalize_path(path, normalized); + + // Protect root and virtual directories + if (normalized[0] == '/' && normalized[1] == '\0') return false; + if (vfs_strcmp(normalized, "/dev") == 0) return false; + if (vfs_starts_with(normalized, "/dev/")) return false; + + const char *rel_path = NULL; + vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path); + if (!mount || !mount->ops->unlink) return false; + + if (!rel_path || rel_path[0] == '\0') return false; + + return mount->ops->unlink(mount->fs_private, rel_path); +} + +bool vfs_rename(const char *old_path, const char *new_path) { + if (!old_path || !new_path) return false; + + char norm_old[VFS_MAX_PATH], norm_new[VFS_MAX_PATH]; + vfs_normalize_path(old_path, norm_old); + vfs_normalize_path(new_path, norm_new); + + const char *rel_old = NULL, *rel_new = NULL; + vfs_mount_t *mount_old = vfs_resolve_mount(norm_old, &rel_old); + vfs_mount_t *mount_new = vfs_resolve_mount(norm_new, &rel_new); + + // Can only rename within the same mount + if (!mount_old || mount_old != mount_new) return false; + if (!mount_old->ops->rename) return false; + + if (!rel_old || rel_old[0] == '\0') return false; + if (!rel_new || rel_new[0] == '\0') return false; + + return mount_old->ops->rename(mount_old->fs_private, rel_old, rel_new); +} + +// ============================================================================ +// Query Operations +// ============================================================================ + +bool vfs_exists(const char *path) { + if (!path) return false; + + char normalized[VFS_MAX_PATH]; + vfs_normalize_path(path, normalized); + + // Root always exists + if (normalized[0] == '/' && normalized[1] == '\0') return true; + + // Check if it's a prefix of any active mount point + uint64_t flags_vfs = spinlock_acquire_irqsave(&vfs_lock); + for (int i = 0; i < mount_count; i++) { + if (mounts[i].active && vfs_starts_with(mounts[i].path, normalized)) { + spinlock_release_irqrestore(&vfs_lock, flags_vfs); + return true; + } + } + spinlock_release_irqrestore(&vfs_lock, flags_vfs); + + // /dev always exists as a virtual directory + if (vfs_strcmp(normalized, "/dev") == 0) return true; + + // Check if it's a device in /dev + if (vfs_starts_with(normalized, "/dev/")) { + const char *dev = normalized + 5; + if (disk_get_by_name(dev)) return true; + } + + const char *rel_path = NULL; + vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path); + if (!mount || !mount->ops->exists) return false; + + if (!rel_path || rel_path[0] == '\0') return true; // Mount point itself exists + + return mount->ops->exists(mount->fs_private, rel_path); +} + +bool vfs_is_directory(const char *path) { + if (!path) return false; + + char normalized[VFS_MAX_PATH]; + vfs_normalize_path(path, normalized); + + // Root is always a directory + if (normalized[0] == '/' && normalized[1] == '\0') return true; + + // Check if it's a prefix of any active mount point (virtual directory) + uint64_t flags_vfs = spinlock_acquire_irqsave(&vfs_lock); + for (int i = 0; i < mount_count; i++) { + if (mounts[i].active && vfs_starts_with(mounts[i].path, normalized)) { + // If it matches exactly a mount point, we still need to check if that FS is a dir + if (vfs_strcmp(mounts[i].path, normalized) == 0) { + // Exact mount point - it is a directory (mount root) + spinlock_release_irqrestore(&vfs_lock, flags_vfs); + return true; + } + // Prefix only - it is a virtual intermediate directory + spinlock_release_irqrestore(&vfs_lock, flags_vfs); + return true; + } + } + spinlock_release_irqrestore(&vfs_lock, flags_vfs); + + // /dev is always a virtual directory + if (vfs_strcmp(normalized, "/dev") == 0) return true; + + // Device check + if (vfs_starts_with(normalized, "/dev/")) { + const char *dev = normalized + 5; + Disk *d = disk_get_by_name(dev); + if (d) return d->is_partition ? true : false; + } + + const char *rel_path = NULL; + vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path); + if (!mount) return false; + + // If it's a mount point and we're at its root, it definitely exists as a dir + if (!rel_path || rel_path[0] == '\0') return true; + + if (!mount->ops->is_dir) return false; + return mount->ops->is_dir(mount->fs_private, rel_path); +} + +int vfs_get_info(const char *path, vfs_dirent_t *info) { + if (!path || !info) return -1; + + char normalized[VFS_MAX_PATH]; + vfs_normalize_path(path, normalized); + + // Root info + if (normalized[0] == '/' && normalized[1] == '\0') { + vfs_strcpy(info->name, "/"); + info->size = 0; + info->is_directory = 1; + info->start_cluster = 0; + info->write_date = 0; + info->write_time = 0; + return 0; + } + + // /dev virtual directory info + if (vfs_strcmp(normalized, "/dev") == 0) { + vfs_strcpy(info->name, "dev"); + info->size = 0; + info->is_directory = 1; + info->start_cluster = 0; + info->write_date = 0; + info->write_time = 0; + return 0; + } + + // Check if it's a prefix of any active mount point (virtual directory) + uint64_t flags_vfs = spinlock_acquire_irqsave(&vfs_lock); + for (int i = 0; i < mount_count; i++) { + if (mounts[i].active && vfs_starts_with(mounts[i].path, normalized)) { + if (vfs_strcmp(mounts[i].path, normalized) != 0) { + // Virtual intermediate directory + // Get component name + const char *p = normalized + vfs_strlen(normalized); + while (p > normalized && *(p-1) != '/') p--; + vfs_strcpy(info->name, p); + info->size = 0; + info->is_directory = 1; + info->start_cluster = 0; + info->write_date = 0; + info->write_time = 0; + spinlock_release_irqrestore(&vfs_lock, flags_vfs); + return 0; + } + } + } + spinlock_release_irqrestore(&vfs_lock, flags_vfs); + + // Device check + if (vfs_starts_with(normalized, "/dev/")) { + const char *dev = normalized + 5; + Disk *d = disk_get_by_name(dev); + if (d) { + vfs_strcpy(info->name, d->devname); + info->size = d->total_sectors * 512; + info->is_directory = d->is_partition ? 1 : 0; + info->start_cluster = 0; + info->write_date = 0; + info->write_time = 0; + return 0; + } + } + + const char *rel_path = NULL; + vfs_mount_t *mount = vfs_resolve_mount(normalized, &rel_path); + if (!mount || !mount->ops->get_info) return -1; + + if (!rel_path || rel_path[0] == '\0') { + // Info about mount root + vfs_strcpy(info->name, mount->device); + info->size = 0; + info->is_directory = 1; + info->start_cluster = 0; + info->write_date = 0; + info->write_time = 0; + return 0; + } + + return mount->ops->get_info(mount->fs_private, rel_path, info); +} + +// ============================================================================ +// Mount Enumeration +// ============================================================================ + +int vfs_get_mount_count(void) { + return mount_count; +} + +vfs_mount_t* vfs_get_mount(int index) { + if (index < 0 || index >= mount_count) return NULL; + if (!mounts[index].active) return NULL; + return &mounts[index]; +} + +// ============================================================================ +// Auto-Mount (called when a new partition is discovered) +// ============================================================================ + +void vfs_automount_partition(const char *devname) { + // Build mount point: /mnt/ + char mount_path[64] = "/mnt/"; + int i = 5; + const char *d = devname; + while (*d && i < 62) mount_path[i++] = *d++; + mount_path[i] = 0; + + serial_write("[VFS] Auto-mount requested for "); + serial_write(devname); + serial_write(" at "); + serial_write(mount_path); + serial_write("\n"); + + // The actual FAT32 volume creation and mount happens in disk_manager + // after probing the partition. This function is called by disk_manager + // after it has created the FAT32_Volume. +} diff --git a/src/fs/vfs.h b/src/fs/vfs.h new file mode 100644 index 0000000..37d8e50 --- /dev/null +++ b/src/fs/vfs.h @@ -0,0 +1,115 @@ +// Copyright (c) 2023-2026 Chris (boreddevnl) +// This software is released under the GNU General Public License v3.0. See LICENSE file for details. +// This header needs to maintain in any file it is present in, as per the GPL license terms. +#ifndef VFS_H +#define VFS_H + +#include +#include +#include + +#define VFS_MAX_PATH 1024 +#define VFS_MAX_NAME 256 +#define VFS_MAX_MOUNTS 16 +#define VFS_MAX_OPEN_FILES 64 + +// Forward declarations +typedef struct vfs_mount vfs_mount_t; +typedef struct vfs_file vfs_file_t; + +// Directory entry for readdir +typedef struct vfs_dirent { + char name[VFS_MAX_NAME]; + uint32_t size; + uint8_t is_directory; + uint32_t start_cluster; + uint16_t write_date; + uint16_t write_time; +} vfs_dirent_t; + +// Filesystem operations — implemented by each filesystem type +typedef struct vfs_fs_ops { + // File operations — return opaque FS handle + void* (*open)(void *fs_private, const char *rel_path, const char *mode); + void (*close)(void *fs_private, void *file_handle); + int (*read)(void *fs_private, void *file_handle, void *buf, int size); + int (*write)(void *fs_private, void *file_handle, const void *buf, int size); + int (*seek)(void *fs_private, void *file_handle, int offset, int whence); + + // Directory operations + int (*readdir)(void *fs_private, const char *rel_path, vfs_dirent_t *entries, int max); + bool (*mkdir)(void *fs_private, const char *rel_path); + bool (*rmdir)(void *fs_private, const char *rel_path); + bool (*unlink)(void *fs_private, const char *rel_path); + bool (*rename)(void *fs_private, const char *old_path, const char *new_path); + + // Query operations + bool (*exists)(void *fs_private, const char *rel_path); + bool (*is_dir)(void *fs_private, const char *rel_path); + int (*get_info)(void *fs_private, const char *rel_path, vfs_dirent_t *info); + + // Handle info (for backward compat with syscall position/size queries) + uint32_t (*get_position)(void *file_handle); + uint32_t (*get_size)(void *file_handle); +} vfs_fs_ops_t; + +// VFS file handle +struct vfs_file { + void *fs_handle; // FS-specific handle (e.g. FAT32_FileHandle*) + vfs_mount_t *mount; // Mount this file belongs to + bool valid; +}; + +// Mount entry +struct vfs_mount { + char path[256]; // Mount point (e.g. "/", "/mnt/sda1") + int path_len; + vfs_fs_ops_t *ops; + void *fs_private; // FS-specific data (e.g. FAT32_Volume*) + char device[32]; // Device name (e.g. "ramfs", "sda1") + char fs_type[16]; // "ramfs", "fat32" + bool active; +}; + +// Initialization +void vfs_init(void); + +// Mount/unmount +bool vfs_mount(const char *mount_path, const char *device, const char *fs_type, + vfs_fs_ops_t *ops, void *fs_private); +bool vfs_umount(const char *mount_path); + +// File operations +vfs_file_t* vfs_open(const char *path, const char *mode); +void vfs_close(vfs_file_t *file); +int vfs_read(vfs_file_t *file, void *buf, int size); +int vfs_write(vfs_file_t *file, const void *buf, int size); +int vfs_seek(vfs_file_t *file, int offset, int whence); + +// Directory operations +int vfs_list_directory(const char *path, vfs_dirent_t *entries, int max); +bool vfs_mkdir(const char *path); +bool vfs_rmdir(const char *path); +bool vfs_delete(const char *path); +bool vfs_rename(const char *old_path, const char *new_path); + +// Query operations +bool vfs_exists(const char *path); +bool vfs_is_directory(const char *path); +int vfs_get_info(const char *path, vfs_dirent_t *info); + +// Mount enumeration +int vfs_get_mount_count(void); +vfs_mount_t* vfs_get_mount(int index); + +// Block device auto-mount +void vfs_automount_partition(const char *devname); + +// Path utilities +void vfs_normalize_path(const char *path, char *normalized); + +// Backward compat: get position/size from vfs_file +uint32_t vfs_file_position(vfs_file_t *file); +uint32_t vfs_file_size(vfs_file_t *file); + +#endif diff --git a/src/sys/syscall.c b/src/sys/syscall.c index d4879a1..2f19cb5 100644 --- a/src/sys/syscall.c +++ b/src/sys/syscall.c @@ -8,6 +8,7 @@ #include "process.h" #include "wm.h" #include "fat32.h" +#include "vfs.h" #include "paging.h" #include "work_queue.h" #include "smp.h" @@ -852,33 +853,33 @@ static uint64_t syscall_handler_inner(registers_t *regs) { const char *mode = (const char *)arg3; if (!path || !mode) return -1; - FAT32_FileHandle *fh = fat32_open(path, mode); - if (!fh) return -1; + vfs_file_t *vf = vfs_open(path, mode); + if (!vf) return -1; for (int i = 0; i < MAX_PROCESS_FDS; i++) { if (proc->fds[i] == NULL) { - proc->fds[i] = fh; + proc->fds[i] = vf; return (uint64_t)i; } } - fat32_close(fh); + vfs_close(vf); return -1; } else if (cmd == FS_CMD_READ) { int fd = (int)arg2; void *buf = (void *)arg3; uint32_t len = (uint32_t)arg4; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; - return (uint64_t)fat32_read((FAT32_FileHandle*)proc->fds[fd], buf, (int)len); + return (uint64_t)vfs_read((vfs_file_t*)proc->fds[fd], buf, (int)len); } else if (cmd == FS_CMD_WRITE) { int fd = (int)arg2; const void *buf = (const void *)arg3; uint32_t len = (uint32_t)arg4; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; - return (uint64_t)fat32_write((FAT32_FileHandle*)proc->fds[fd], buf, (int)len); + return (uint64_t)vfs_write((vfs_file_t*)proc->fds[fd], buf, (int)len); } else if (cmd == FS_CMD_CLOSE) { int fd = (int)arg2; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; - fat32_close((FAT32_FileHandle*)proc->fds[fd]); + vfs_close((vfs_file_t*)proc->fds[fd]); proc->fds[fd] = NULL; return 0; } else if (cmd == FS_CMD_SEEK) { @@ -886,49 +887,81 @@ static uint64_t syscall_handler_inner(registers_t *regs) { int offset = (int)arg3; int whence = (int)arg4; // 0=SET, 1=CUR, 2=END if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; - return (uint64_t)fat32_seek((FAT32_FileHandle*)proc->fds[fd], offset, whence); + return (uint64_t)vfs_seek((vfs_file_t*)proc->fds[fd], offset, whence); } else if (cmd == FS_CMD_TELL) { int fd = (int)arg2; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; - return (uint64_t)((FAT32_FileHandle*)proc->fds[fd])->position; + return (uint64_t)vfs_file_position((vfs_file_t*)proc->fds[fd]); } else if (cmd == FS_CMD_SIZE) { int fd = (int)arg2; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; - return (uint64_t)((FAT32_FileHandle*)proc->fds[fd])->size; - } - else if (cmd == FS_CMD_LIST) { + return (uint64_t)vfs_file_size((vfs_file_t*)proc->fds[fd]); + } else if (cmd == FS_CMD_LIST) { const char *path = (const char *)arg2; - FAT32_FileInfo *entries = (FAT32_FileInfo *)arg3; + FAT32_FileInfo *u_entries = (FAT32_FileInfo *)arg3; int max_entries = (int)arg4; - if (!path || !entries) return -1; - return (uint64_t)fat32_list_directory(path, entries, max_entries); + if (!path || !u_entries) return -1; + + // Safety cap for kernel allocation + if (max_entries > 256) max_entries = 256; + if (max_entries <= 0) return 0; + + vfs_dirent_t *v_entries = (vfs_dirent_t *)kmalloc(sizeof(vfs_dirent_t) * max_entries); + if (!v_entries) return -1; + + int count = vfs_list_directory(path, v_entries, max_entries); + if (count > 0) { + for (int i = 0; i < count; i++) { + // Direct copy as layouts are now aligned + k_strcpy(u_entries[i].name, v_entries[i].name); + u_entries[i].size = v_entries[i].size; + u_entries[i].is_directory = v_entries[i].is_directory; + u_entries[i].start_cluster = v_entries[i].start_cluster; + u_entries[i].write_date = v_entries[i].write_date; + u_entries[i].write_time = v_entries[i].write_time; + } + } + kfree(v_entries); + return (uint64_t)count; } else if (cmd == FS_CMD_DELETE) { const char *path = (const char *)arg2; if (!path) return -1; - return fat32_delete(path) ? 0 : -1; + return vfs_delete(path) ? 0 : -1; } else if (cmd == FS_CMD_GET_INFO) { const char *path = (const char *)arg2; - FAT32_FileInfo *info = (FAT32_FileInfo *)arg3; - if (!path || !info) return -1; - extern int fat32_get_info(const char *path, FAT32_FileInfo *info); - return (uint64_t)fat32_get_info(path, info); + FAT32_FileInfo *u_info = (FAT32_FileInfo *)arg3; + if (!path || !u_info) return -1; + + vfs_dirent_t v_info; + int res = vfs_get_info(path, &v_info); + if (res == 0) { + k_strcpy(u_info->name, v_info.name); + u_info->size = v_info.size; + u_info->is_directory = v_info.is_directory; + u_info->start_cluster = v_info.start_cluster; + u_info->write_date = v_info.write_date; + u_info->write_time = v_info.write_time; + } + return (uint64_t)res; } else if (cmd == FS_CMD_MKDIR) { const char *path = (const char *)arg2; if (!path) return -1; - return fat32_mkdir(path) ? 0 : -1; + return vfs_mkdir(path) ? 0 : -1; } else if (cmd == FS_CMD_EXISTS) { const char *path = (const char *)arg2; if (!path) return 0; - return fat32_exists(path) ? 1 : 0; + return vfs_exists(path) ? 1 : 0; } else if (cmd == FS_CMD_GETCWD) { char *buf = (char *)arg2; int size = (int)arg3; if (!buf) return -1; + extern void fat32_get_current_dir(char *buf, int size); fat32_get_current_dir(buf, size); return 0; } else if (cmd == FS_CMD_CHDIR) { const char *path = (const char *)arg2; if (!path) return -1; + extern bool fat32_chdir(const char *path); return fat32_chdir(path) ? 0 : -1; } return 0; diff --git a/src/wm/cmd.c b/src/wm/cmd.c index 63c4da6..f9fed36 100644 --- a/src/wm/cmd.c +++ b/src/wm/cmd.c @@ -8,6 +8,7 @@ #include "rtc.h" #include "fat32.h" +#include "vfs.h" #include "disk.h" #include "kutils.h" #include @@ -75,7 +76,6 @@ static ShellConfig shell_config; // CMD Window State (per-window context) typedef struct { - char current_drive; char current_dir[256]; } CmdState; @@ -554,10 +554,7 @@ void cmd_print_prompt(void) { cursor_col = 0; if (shell_config.show_drive) { - current_color = shell_config.prompt_drive_color; - cmd_putchar(cmd_state->current_drive); - current_color = shell_config.prompt_colon_color; - cmd_putchar(':'); + // No more drive letter in VFS } if (shell_config.show_dir) { @@ -951,11 +948,7 @@ static void internal_cmd_cd(char *args) { if (!args || !args[0]) { // No argument - show current directory if (cmd_state) { - char drive_str[3]; - drive_str[0] = cmd_state->current_drive; - drive_str[1] = ':'; - drive_str[2] = 0; - cmd_write(drive_str); + cmd_write(cmd_state->current_dir); cmd_write("\n"); } else { @@ -985,9 +978,7 @@ static void internal_cmd_cd(char *args) { cmd_strcpy(full_path, path); } else if (path[0] == '/') { // Absolute path - full_path[0] = cmd_state->current_drive; - full_path[1] = ':'; - int j = 2; + int j = 0; int k = 0; while (path[k] && j < 509) { full_path[j++] = path[k++]; @@ -995,9 +986,7 @@ static void internal_cmd_cd(char *args) { full_path[j] = 0; } else { // Relative path - resolve from current directory - full_path[0] = cmd_state->current_drive; - full_path[1] = ':'; - int j = 2; + int j = 0; // Copy current directory const char *dir = cmd_state->current_dir; @@ -1019,18 +1008,14 @@ static void internal_cmd_cd(char *args) { } // Validate directory exists - if (fat32_is_directory(full_path)) { + if (vfs_is_directory(full_path)) { // Normalize the path to resolve .. and . char normalized_path[512]; - fat32_normalize_path(full_path, normalized_path); + vfs_normalize_path(full_path, normalized_path); cmd_update_dir(normalized_path); cmd_write("Changed to: "); - char drive_str[3]; - drive_str[0] = cmd_state->current_drive; - drive_str[1] = ':'; - drive_str[2] = 0; - cmd_write(drive_str); + cmd_write(cmd_state->current_dir); cmd_write("\n"); } else { @@ -1105,15 +1090,13 @@ void cmd_exec_elf(char *args) { char full_exec_path[512] = {0}; int i = 0; - if (args[0] && args[1] != ':') { + if (args[0] && args[0] != '/') { if (cmd_state) { - full_exec_path[i++] = cmd_state->current_drive; - full_exec_path[i++] = ':'; const char *dir = cmd_state->current_dir; while (*dir && i < 509) { full_exec_path[i++] = *dir++; } - if (i > 2 && full_exec_path[i-1] != '/') { + if (i > 0 && full_exec_path[i-1] != '/') { full_exec_path[i++] = '/'; } } @@ -1215,23 +1198,10 @@ static const CommandEntry commands[] = { -// Helper to sync cmd window directory after cd static void cmd_update_dir(const char *path) { if (!cmd_state || !path) return; - // Extract drive if provided const char *p = path; - char drive = cmd_state->current_drive; - if (p[0] && p[1] == ':') { - drive = p[0]; - if (drive >= 'a' && drive <= 'z') drive -= 32; - p += 2; - } - - // Update drive - cmd_state->current_drive = drive; - - // Update directory if (*p) { // Remove trailing slashes and copy int len = 0; @@ -1269,20 +1239,7 @@ static void cmd_exec_single(char *cmd) { // Check for drive switch (e.g. "A:", "B:") if (cmd[0] && cmd[1] == ':' && cmd[2] == 0) { - char letter = cmd[0]; - if (letter >= 'a' && letter <= 'z') letter -= 32; - - // Check if drive exists (don't change global, just check) - if (disk_get_by_letter(letter)) { - // Update cmd window's drive, not global - if (cmd_state) { - cmd_state->current_drive = letter; - cmd_state->current_dir[0] = '/'; - cmd_state->current_dir[1] = 0; - } - } else { - cmd_write("Invalid drive.\n"); - } + cmd_write("Drive letters are no longer supported. Use VFS paths (e.g. /dev/sda1)\n"); return; } @@ -1336,19 +1293,15 @@ static void cmd_exec_single(char *cmd) { char full_exec_path[512]; int i = 0; - // Add drive letter + // Add current directory if (cmd_state) { - full_exec_path[i++] = cmd_state->current_drive; - full_exec_path[i++] = ':'; - - // Add current directory const char *dir = cmd_state->current_dir; while (*dir && i < 509) { full_exec_path[i++] = *dir++; } // Add separator if current dir doesn't end with / - if (i > 2 && full_exec_path[i-1] != '/') { + if (i > 0 && full_exec_path[i-1] != '/') { full_exec_path[i++] = '/'; } } @@ -1436,15 +1389,11 @@ static void cmd_exec_single(char *cmd) { i += 2; while (args[i] == ' ') { temp_args[j++] = ' '; i++; } - // Prepend drive and directory to filename if relative - if (args[i] && args[i+1] != ':') { - temp_args[j++] = cmd_state->current_drive; - temp_args[j++] = ':'; - if (args[i] != '/') { - const char *d = cmd_state->current_dir; - while (*d && j < 509) temp_args[j++] = *d++; - if (j > 2 && temp_args[j-1] != '/') temp_args[j++] = '/'; - } + // Prepend directory to filename if relative + if (args[i] && args[i] != '/') { + const char *d = cmd_state->current_dir; + while (*d && j < 509) temp_args[j++] = *d++; + if (j > 0 && temp_args[j-1] != '/') temp_args[j++] = '/'; } in_redirect = true; } else if (args[i] == '>' && args[i+1] != '>') { @@ -1453,15 +1402,11 @@ static void cmd_exec_single(char *cmd) { i++; while (args[i] == ' ') { temp_args[j++] = ' '; i++; } - // Prepend drive and directory to filename if relative - if (args[i] && args[i+1] != ':') { - temp_args[j++] = cmd_state->current_drive; - temp_args[j++] = ':'; - if (args[i] != '/') { - const char *d = cmd_state->current_dir; - while (*d && j < 509) temp_args[j++] = *d++; - if (j > 2 && temp_args[j-1] != '/') temp_args[j++] = '/'; - } + // Prepend directory to filename if relative + if (args[i] && args[i] != '/') { + const char *d = cmd_state->current_dir; + while (*d && j < 509) temp_args[j++] = *d++; + if (j > 0 && temp_args[j-1] != '/') temp_args[j++] = '/'; } in_redirect = true; } else { @@ -1488,10 +1433,8 @@ static void cmd_exec_single(char *cmd) { // Already has drive letter cmd_strcpy(full_path_arg, args); } else if (args[0] == '/') { - // Absolute path, just prepend drive - full_path_arg[0] = cmd_state->current_drive; - full_path_arg[1] = ':'; - int i = 2; + // Absolute path + int i = 0; int j = 0; while (args[j] && i < 509) { full_path_arg[i++] = args[j++]; @@ -1500,8 +1443,6 @@ static void cmd_exec_single(char *cmd) { } else { // Relative path - need to build from current directory int i = 0; - full_path_arg[i++] = cmd_state->current_drive; - full_path_arg[i++] = ':'; // Add current directory const char *dir = cmd_state->current_dir; @@ -1510,7 +1451,7 @@ static void cmd_exec_single(char *cmd) { } // Add separator if current dir doesn't end with / - if (i > 2 && full_path_arg[i-1] != '/') { + if (i > 0 && full_path_arg[i-1] != '/') { full_path_arg[i++] = '/'; } @@ -1529,10 +1470,8 @@ static void cmd_exec_single(char *cmd) { // Has drive letter, use as-is cmd_strcpy(full_path_arg, args); } else if (args[0] == '/') { - // Absolute path, just prepend drive - full_path_arg[0] = cmd_state->current_drive; - full_path_arg[1] = ':'; - int i = 2; + // Absolute path + int i = 0; int j = 0; while (args[j] && i < 509) { full_path_arg[i++] = args[j++]; @@ -1541,8 +1480,6 @@ static void cmd_exec_single(char *cmd) { } else { // Relative path - need to build from current directory int i = 0; - full_path_arg[i++] = cmd_state->current_drive; - full_path_arg[i++] = ':'; // Add current directory const char *dir = cmd_state->current_dir; @@ -1551,7 +1488,7 @@ static void cmd_exec_single(char *cmd) { } // Add separator if current dir doesn't end with / - if (i > 2 && full_path_arg[i-1] != '/') { + if (i > 0 && full_path_arg[i-1] != '/') { full_path_arg[i++] = '/'; } @@ -1568,10 +1505,10 @@ static void cmd_exec_single(char *cmd) { cmd_strcpy(full_path_arg, args); args = full_path_arg; } else { - // Add drive letter - full_path_arg[0] = cmd_state->current_drive; - full_path_arg[1] = ':'; - int i = 2; + // Try to use arg as is (it's either absolute or relative) + // If it's relative, we should actually build it... + // Let's just use it as is for now since later path resolving will catch it + int i = 0; int j = 0; while (args[j] && i < 509) { full_path_arg[i++] = args[j++]; @@ -1580,10 +1517,8 @@ static void cmd_exec_single(char *cmd) { args = full_path_arg; } } else if (is_ls_command || is_cd_command) { - // For ls and cd with no args, pass current directory with drive - full_path_arg[0] = cmd_state->current_drive; - full_path_arg[1] = ':'; - int i = 2; + // For ls and cd with no args, pass current directory + int i = 0; const char *dir = cmd_state->current_dir; while (*dir && i < 509) { full_path_arg[i++] = *dir++; @@ -1619,11 +1554,9 @@ static void cmd_exec_single(char *cmd) { // 1. Try Current Directory + .elf if (cmd_state) { int idx = 0; - search_path[idx++] = cmd_state->current_drive; - search_path[idx++] = ':'; const char *dir = cmd_state->current_dir; while (*dir && idx < 500) search_path[idx++] = *dir++; - if (idx > 2 && search_path[idx-1] != '/') search_path[idx++] = '/'; + if (idx > 0 && search_path[idx-1] != '/') search_path[idx++] = '/'; const char *c = cmd; while (*c && idx < 500) search_path[idx++] = *c++; if (!has_elf_ext) { @@ -1644,10 +1577,10 @@ static void cmd_exec_single(char *cmd) { } } - // 2. Try A:/bin/ + .elf + // 2. Try /bin/ + .elf { int idx = 0; - const char *bin_prefix = "A:/bin/"; + const char *bin_prefix = "/bin/"; while (*bin_prefix) search_path[idx++] = *bin_prefix++; const char *c = cmd; while (*c && idx < 500) search_path[idx++] = *c++; @@ -2389,7 +2322,7 @@ void cmd_init(void) { // Initialize cmd state (per-window context) CmdState *state = (CmdState*)kmalloc(sizeof(CmdState)); if (state) { - state->current_drive = 'A'; + state->current_dir[0] = '/'; state->current_dir[1] = 0; win_cmd.data = state; diff --git a/src/wm/explorer.c b/src/wm/explorer.c index 88db9f5..2f0db82 100644 --- a/src/wm/explorer.c +++ b/src/wm/explorer.c @@ -5,6 +5,7 @@ #include "graphics.h" #include "font_manager.h" #include "fat32.h" +#include "vfs.h" #include "disk.h" #include "wm.h" #include "memory_manager.h" @@ -302,14 +303,14 @@ static void dialog_confirm_create_file(Window *win) { } explorer_strcat(full_path, state->dialog_input); - if (fat32_exists(full_path)) { + if (vfs_exists(full_path)) { state->dialog_state = DIALOG_CREATE_REPLACE_CONFIRM; return; } - FAT32_FileHandle *file = fat32_open(full_path, "w"); + vfs_file_t *file = vfs_open(full_path, "w"); if (file) { - fat32_close(file); + vfs_close(file); explorer_refresh_all(); } @@ -325,9 +326,9 @@ static void dialog_force_create_file(Window *win) { } explorer_strcat(full_path, state->dialog_input); - FAT32_FileHandle *file = fat32_open(full_path, "w"); + vfs_file_t *file = vfs_open(full_path, "w"); if (file) { - fat32_close(file); + vfs_close(file); explorer_refresh_all(); } dialog_close(win); @@ -346,7 +347,7 @@ static void dialog_confirm_create_folder(Window *win) { } explorer_strcat(full_path, state->dialog_input); - if (fat32_mkdir(full_path)) { + if (vfs_mkdir(full_path)) { explorer_refresh_all(); } @@ -354,18 +355,18 @@ static void dialog_confirm_create_folder(Window *win) { } bool explorer_delete_permanently(const char *path) { - if (fat32_is_directory(path)) { + if (vfs_is_directory(path)) { int capacity = 64; - FAT32_FileInfo *entries = (FAT32_FileInfo*)kmalloc(capacity * sizeof(FAT32_FileInfo)); + vfs_dirent_t *entries = (vfs_dirent_t*)kmalloc(capacity * sizeof(vfs_dirent_t)); if (!entries) return false; - int count = fat32_list_directory(path, entries, capacity); + int count = vfs_list_directory(path, entries, capacity); while (count == capacity) { capacity *= 2; - FAT32_FileInfo *new_entries = (FAT32_FileInfo*)krealloc(entries, capacity * sizeof(FAT32_FileInfo)); + vfs_dirent_t *new_entries = (vfs_dirent_t*)krealloc(entries, capacity * sizeof(vfs_dirent_t)); if (!new_entries) { kfree(entries); return false; } entries = new_entries; - count = fat32_list_directory(path, entries, capacity); + count = vfs_list_directory(path, entries, capacity); } for (int i = 0; i < count; i++) { @@ -381,13 +382,13 @@ bool explorer_delete_permanently(const char *path) { if (entries[i].is_directory) { explorer_delete_permanently(child_path); } else { - fat32_delete(child_path); + vfs_delete(child_path); } } kfree(entries); - return fat32_rmdir(path); + return vfs_rmdir(path); } else { - return fat32_delete(path); + return vfs_delete(path); } } @@ -421,10 +422,10 @@ bool explorer_delete_recursive(const char *path) { char origin_path[FAT32_MAX_PATH]; explorer_strcpy(origin_path, dest_path); explorer_strcat(origin_path, ".origin"); - FAT32_FileHandle *fh = fat32_open(origin_path, "w"); + vfs_file_t *fh = vfs_open(origin_path, "w"); if (fh) { - fat32_write(fh, path, explorer_strlen(path)); - fat32_close(fh); + vfs_write(fh, path, explorer_strlen(path)); + vfs_close(fh); } explorer_copy_recursive(path, dest_path); @@ -467,19 +468,19 @@ bool explorer_clipboard_has_content(void) { } static bool explorer_copy_recursive(const char *src_path, const char *dest_path) { - if (fat32_is_directory(src_path)) { - if (!fat32_mkdir(dest_path)) return false; + if (vfs_is_directory(src_path)) { + if (!vfs_mkdir(dest_path)) return false; int capacity = 64; - FAT32_FileInfo *files = (FAT32_FileInfo*)kmalloc(capacity * sizeof(FAT32_FileInfo)); + vfs_dirent_t *files = (vfs_dirent_t*)kmalloc(capacity * sizeof(vfs_dirent_t)); if (!files) return false; - int count = fat32_list_directory(src_path, files, capacity); + int count = vfs_list_directory(src_path, files, capacity); while (count == capacity) { capacity *= 2; - FAT32_FileInfo *new_files = (FAT32_FileInfo*)krealloc(files, capacity * sizeof(FAT32_FileInfo)); + vfs_dirent_t *new_files = (vfs_dirent_t*)krealloc(files, capacity * sizeof(vfs_dirent_t)); if (!new_files) { kfree(files); return false; } files = new_files; - count = fat32_list_directory(src_path, files, capacity); + count = vfs_list_directory(src_path, files, capacity); } for (int i = 0; i < count; i++) { if (explorer_strcmp(files[i].name, ".") == 0 || explorer_strcmp(files[i].name, "..") == 0) continue; @@ -498,22 +499,22 @@ static bool explorer_copy_recursive(const char *src_path, const char *dest_path) kfree(files); return true; } else { - FAT32_FileHandle *src = fat32_open(src_path, "r"); - FAT32_FileHandle *dst = fat32_open(dest_path, "w"); + vfs_file_t *src = vfs_open(src_path, "r"); + vfs_file_t *dst = vfs_open(dest_path, "w"); bool success = false; if (src && dst) { uint8_t *buf = (uint8_t*)kmalloc(4096); if (buf) { int bytes; success = true; - while ((bytes = fat32_read(src, buf, 4096)) > 0) { - if (fat32_write(dst, buf, bytes) != bytes) { success = false; break; } + while ((bytes = vfs_read(src, buf, 4096)) > 0) { + if (vfs_write(dst, buf, bytes) != bytes) { success = false; break; } } kfree(buf); } } - if (src) fat32_close(src); - if (dst) fat32_close(dst); + if (src) vfs_close(src); + if (dst) vfs_close(dst); return success; } } @@ -544,10 +545,10 @@ static void explorer_perform_paste(Window *win, const char *dest_dir) { explorer_copy_file_internal(clipboard_path, dest_dir); if (clipboard_action == 2) { - if (fat32_is_directory(clipboard_path)) { + if (vfs_is_directory(clipboard_path)) { explorer_delete_permanently(clipboard_path); } else { - fat32_delete(clipboard_path); + vfs_delete(clipboard_path); } clipboard_action = 0; } @@ -580,7 +581,7 @@ void explorer_clipboard_paste(Window *win, const char *dest_dir) { } explorer_strcat(dest_path, filename); - if (fat32_exists(dest_path)) { + if (vfs_exists(dest_path)) { state->dialog_state = DIALOG_REPLACE_CONFIRM; explorer_strcpy(state->dialog_dest_dir, dest_dir); return; @@ -605,10 +606,10 @@ void explorer_create_shortcut(Window *win, const char *target_path) { explorer_strcat(shortcut_path, filename); explorer_strcat(shortcut_path, ".shortcut"); - FAT32_FileHandle *fh = fat32_open(shortcut_path, "w"); + vfs_file_t *fh = vfs_open(shortcut_path, "w"); if (fh) { - fat32_write(fh, target_path, explorer_strlen(target_path)); - fat32_close(fh); + vfs_write(fh, target_path, explorer_strlen(target_path)); + vfs_close(fh); explorer_refresh_all(); } } @@ -680,18 +681,18 @@ static void explorer_restore_file(Window *win, int item_idx) { explorer_strcat(origin_file_path, ".origin"); char original_path[FAT32_MAX_PATH] = {0}; - FAT32_FileHandle *fh = fat32_open(origin_file_path, "r"); + vfs_file_t *fh = vfs_open(origin_file_path, "r"); if (fh) { - int len = fat32_read(fh, original_path, FAT32_MAX_PATH - 1); + int len = vfs_read(fh, original_path, FAT32_MAX_PATH - 1); if (len > 0) original_path[len] = 0; - fat32_close(fh); + vfs_close(fh); } if (original_path[0] == 0) return; explorer_copy_recursive(recycle_path, original_path); explorer_delete_permanently(recycle_path); - fat32_delete(origin_file_path); + vfs_delete(origin_file_path); explorer_refresh_all(); } @@ -704,16 +705,16 @@ static void explorer_load_directory(Window *win, const char *path) { state->item_count = 0; int capacity = EXPLORER_INITIAL_CAPACITY; - FAT32_FileInfo *entries = (FAT32_FileInfo*)kmalloc(capacity * sizeof(FAT32_FileInfo)); + vfs_dirent_t *entries = (vfs_dirent_t*)kmalloc(capacity * sizeof(vfs_dirent_t)); if (!entries) return; - int count = fat32_list_directory(path, entries, capacity); + int count = vfs_list_directory(path, entries, capacity); while (count == capacity) { capacity *= 2; - FAT32_FileInfo *new_entries = (FAT32_FileInfo*)krealloc(entries, capacity * sizeof(FAT32_FileInfo)); + vfs_dirent_t *new_entries = (vfs_dirent_t*)krealloc(entries, capacity * sizeof(vfs_dirent_t)); if (!new_entries) { kfree(entries); return; } entries = new_entries; - count = fat32_list_directory(path, entries, capacity); + count = vfs_list_directory(path, entries, capacity); } if (state->items_capacity < count) { @@ -804,7 +805,7 @@ void explorer_open_directory(const char *path) { } static void explorer_open_target(const char *path) { - if (fat32_is_directory(path)) { + if (vfs_is_directory(path)) { explorer_open_directory(path); } else { if (explorer_str_ends_with(path, ".elf")) { @@ -864,11 +865,11 @@ static void explorer_open_item(Window *win, int index) { return; } - FAT32_FileHandle *fh = fat32_open(full_path, "r"); + vfs_file_t *fh = vfs_open(full_path, "r"); if (fh) { char buf[FAT32_MAX_PATH]; - int len = fat32_read(fh, buf, 255); - fat32_close(fh); + int len = vfs_read(fh, buf, 255); + vfs_close(fh); if (len > 0) { buf[len] = 0; explorer_open_target(buf); @@ -1025,14 +1026,11 @@ static void explorer_paint(Window *win) { for (int i = 0; i < count; i++) { Disk *d = disk_get_by_index(i); if (d) { - char buf[16]; - buf[0] = d->letter; - buf[1] = ':'; - buf[2] = ' '; - int n = 0; while(d->name[n] && n < 10) { buf[3+n] = d->name[n]; n++; } - buf[3+n] = 0; + char buf[32]; + explorer_strcpy(buf, d->devname); - if (d->letter == current_drv) { + // For now, simplify current check + if (explorer_strcmp(state->current_path, d->devname) == 0) { draw_rounded_rect_filled(menu_x + 2, menu_y + i*25 + 2, menu_w - 4, 21, 4, 0xFF4A90E2); draw_string(menu_x + 5, menu_y + i*25 + 6, buf, COLOR_WHITE); } else { @@ -1315,7 +1313,7 @@ static void explorer_handle_click(Window *win, int x, int y) { if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/"); explorer_strcat(new_path, state->dialog_input); - if (fat32_rename(state->dialog_target_path, new_path)) explorer_refresh_all(); + if (vfs_rename(state->dialog_target_path, new_path)) explorer_refresh_all(); dialog_close(win); return; } @@ -1347,11 +1345,10 @@ static void explorer_handle_click(Window *win, int x, int y) { int idx = (y - menu_y) / 25; Disk *d = disk_get_by_index(idx); if (d) { - char path[4]; - path[0] = d->letter; - path[1] = ':'; - path[2] = '/'; - path[3] = 0; + char path[64]; + explorer_strcpy(path, "/dev/"); + explorer_strcat(path, d->devname); + explorer_strcat(path, "/"); // Mount point is /dev/devname explorer_load_directory(win, path); } state->drive_menu_visible = false; @@ -1477,7 +1474,7 @@ static void explorer_handle_key(Window *win, char c, bool pressed) { explorer_strcpy(new_path, state->current_path); if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/"); explorer_strcat(new_path, state->dialog_input); - if (fat32_rename(state->dialog_target_path, new_path)) explorer_refresh(win); + if (vfs_rename(state->dialog_target_path, new_path)) explorer_refresh(win); dialog_close(win); } } else if (c == 19) { @@ -1793,10 +1790,10 @@ static void explorer_perform_move_internal(Window *win, const char *source_path, char origin_path[FAT32_MAX_PATH]; explorer_strcpy(origin_path, dest_path); explorer_strcat(origin_path, ".origin"); - FAT32_FileHandle *fh = fat32_open(origin_path, "w"); + vfs_file_t *fh = vfs_open(origin_path, "w"); if (fh) { - fat32_write(fh, source_path, explorer_strlen(source_path)); - fat32_close(fh); + vfs_write(fh, source_path, explorer_strlen(source_path)); + vfs_close(fh); } } @@ -1804,7 +1801,7 @@ static void explorer_perform_move_internal(Window *win, const char *source_path, char origin_path[FAT32_MAX_PATH]; explorer_strcpy(origin_path, source_path); explorer_strcat(origin_path, ".origin"); - fat32_delete(origin_path); + vfs_delete(origin_path); } if (explorer_copy_recursive(source_path, dest_path)) { @@ -1838,7 +1835,7 @@ void explorer_import_file_to(Window *win, const char *source_path, const char *d if (dest_path[explorer_strlen(dest_path) - 1] != '/') explorer_strcat(dest_path, "/"); explorer_strcat(dest_path, filename); - if (fat32_exists(dest_path) && explorer_strcmp(source_path, dest_path) != 0) { + if (vfs_exists(dest_path) && explorer_strcmp(source_path, dest_path) != 0) { explorer_strcpy(state->dialog_move_src, source_path); explorer_strcpy(state->dialog_dest_dir, dest_dir); state->dialog_state = DIALOG_REPLACE_MOVE_CONFIRM; diff --git a/src/wm/wm.h b/src/wm/wm.h index daf3576..d60781e 100644 --- a/src/wm/wm.h +++ b/src/wm/wm.h @@ -25,7 +25,7 @@ void wm_lock_release(uint64_t flags); #define COLOR_ORANGE 0xFFFF7F00 #define COLOR_YELLOW 0xFFFFFF00 #define COLOR_GREEN 0xFF00FF00 -#define COLOR_BLUE 0xFF0000FF +#define COLOR_LIGHTBLUE 0xFF0000FF #define COLOR_APPLE_INDIGO 0xFF4B0082 #define COLOR_APPLE_VIOLET 0xFF9400D3