CHECKP: shitty VFS

This commit is contained in:
boreddevnl 2026-04-11 21:41:11 +02:00
parent 6b6a22d518
commit 5933483009
15 changed files with 2768 additions and 575 deletions

View file

@ -17,6 +17,7 @@
#include "io.h" #include "io.h"
#include "fat32.h" #include "fat32.h"
#include "tar.h" #include "tar.h"
#include "vfs.h"
#include "memory_manager.h" #include "memory_manager.h"
#include "platform.h" #include "platform.h"
#include "wallpaper.h" #include "wallpaper.h"
@ -133,6 +134,7 @@ static void fat32_mkdir_recursive(const char *path) {
void kmain(void) { void kmain(void) {
init_serial(); init_serial();
vfs_init();
serial_write("\n[DEBUG] Entering kmain...\n"); serial_write("\n[DEBUG] Entering kmain...\n");
platform_init(); platform_init();

509
src/dev/ahci.c Normal file
View file

@ -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 <stddef.h>
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");
}
}

174
src/dev/ahci.h Normal file
View file

@ -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 <stdint.h>
#include <stdbool.h>
// ============================================================================
// 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

View file

@ -8,6 +8,7 @@
#include <stdbool.h> #include <stdbool.h>
#define SECTOR_SIZE 512 #define SECTOR_SIZE 512
#define MAX_DISKS 16
typedef enum { typedef enum {
DISK_TYPE_RAM, DISK_TYPE_RAM,
@ -17,26 +18,45 @@ typedef enum {
} DiskType; } DiskType;
typedef struct Disk { typedef struct Disk {
char letter; char devname[16]; // Device name: "sda", "sdb", "sda1", etc.
DiskType type; DiskType type;
bool is_fat32; bool is_fat32;
char name[32]; char label[32]; // Human-readable label
uint32_t partition_lba_offset; // LBA offset of FAT32 partition (0 for raw) 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 // Function pointers for driver operations
int (*read_sector)(struct Disk *disk, uint32_t sector, uint8_t *buffer); 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); int (*write_sector)(struct Disk *disk, uint32_t sector, const uint8_t *buffer);
// Private driver data // 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; } Disk;
// Initialization and scanning
void disk_manager_init(void); void disk_manager_init(void);
void disk_manager_scan(void); // Scans for new disks void disk_manager_scan(void);
Disk* disk_get_by_letter(char letter);
char disk_get_next_free_letter(void); // Device registration
void disk_register(Disk *disk); 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); int disk_get_count(void);
Disk* disk_get_by_index(int index); 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 #endif

View file

@ -5,15 +5,39 @@
#include "pci.h" #include "pci.h"
#include "memory_manager.h" #include "memory_manager.h"
#include "io.h" #include "io.h"
#include "wm.h" #include "wm.h"
#include "ahci.h"
#include "../fs/vfs.h"
#include "../fs/fat32.h"
#include <stddef.h> #include <stddef.h>
#define MAX_DISKS 26
static Disk *disks[MAX_DISKS]; static Disk *disks[MAX_DISKS];
static int disk_count = 0; 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_IO 0x1F0
#define ATA_PRIMARY_CTRL 0x3F6 #define ATA_PRIMARY_CTRL 0x3F6
@ -35,40 +59,21 @@ static int disk_count = 0;
#define ATA_CMD_WRITE_PIO 0x30 #define ATA_CMD_WRITE_PIO 0x30
#define ATA_CMD_IDENTIFY 0xEC #define ATA_CMD_IDENTIFY 0xEC
#define ATA_SR_BSY 0x80 // Busy #define ATA_SR_BSY 0x80
#define ATA_SR_DRDY 0x40 // Drive ready #define ATA_SR_DRDY 0x40
#define ATA_SR_DF 0x20 // Drive write fault #define ATA_SR_DF 0x20
#define ATA_SR_DSC 0x10 // Drive seek complete #define ATA_SR_DSC 0x10
#define ATA_SR_DRQ 0x08 // Data request ready #define ATA_SR_DRQ 0x08
#define ATA_SR_CORR 0x04 // Corrected data #define ATA_SR_CORR 0x04
#define ATA_SR_IDX 0x02 // Index #define ATA_SR_IDX 0x02
#define ATA_SR_ERR 0x01 // Error #define ATA_SR_ERR 0x01
typedef struct { typedef struct {
uint16_t port_base; uint16_t port_base;
bool slave; bool slave;
} ATADriverData; } ATADriverData;
// === Helpers === // === ATA PIO Driver ===
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;
}
static void ata_wait_bsy(uint16_t port_base) { static void ata_wait_bsy(uint16_t port_base) {
while (inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY); 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)); 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) { static int ata_identify(uint16_t port_base, bool slave) {
// Select Drive
outb(port_base + ATA_REG_HDDEVSEL, slave ? 0xB0 : 0xA0); 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_SEC_COUNT0, 0);
outb(port_base + ATA_REG_LBA0, 0); outb(port_base + ATA_REG_LBA0, 0);
outb(port_base + ATA_REG_LBA1, 0); outb(port_base + ATA_REG_LBA1, 0);
outb(port_base + ATA_REG_LBA2, 0); outb(port_base + ATA_REG_LBA2, 0);
// Send Identify command
outb(port_base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY); 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); uint8_t status = inb(port_base + ATA_REG_STATUS);
if (status == 0) return 0; if (status == 0) return 0;
// Wait until BSY clears
int timeout = 10000; int timeout = 10000;
while ((inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY) && --timeout > 0) { while ((inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY) && --timeout > 0) {
status = inb(port_base + ATA_REG_STATUS); 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 if (timeout <= 0) return 0;
// 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 (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) 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++) { for (int i = 0; i < 256; i++) {
uint16_t data = inw(port_base + ATA_REG_DATA); 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) { static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) {
ATADriverData *data = (ATADriverData*)disk->driver_data; ATADriverData *data = (ATADriverData*)disk->driver_data;
uint16_t port_base = data->port_base; uint16_t port_base = data->port_base;
bool slave = data->slave; 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); 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_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
outb(port_base + ATA_REG_FEATURES, 0x00); outb(port_base + ATA_REG_FEATURES, 0x00);
outb(port_base + ATA_REG_SEC_COUNT0, 1); 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_LBA1, (uint8_t)(lba >> 8));
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16)); outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
outb(port_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO); outb(port_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
ata_wait_bsy(port_base); ata_wait_bsy(port_base);
ata_wait_drq(port_base); ata_wait_drq(port_base);
uint16_t *ptr = (uint16_t*)buffer; uint16_t *ptr = (uint16_t*)buffer;
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++) {
ptr[i] = inw(port_base + ATA_REG_DATA); 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) { static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) {
ATADriverData *data = (ATADriverData*)disk->driver_data; ATADriverData *data = (ATADriverData*)disk->driver_data;
uint16_t port_base = data->port_base; uint16_t port_base = data->port_base;
bool slave = data->slave; 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); ata_wait_bsy(port_base);
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F)); outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
outb(port_base + ATA_REG_FEATURES, 0x00); outb(port_base + ATA_REG_FEATURES, 0x00);
outb(port_base + ATA_REG_SEC_COUNT0, 1); 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_LBA1, (uint8_t)(lba >> 8));
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16)); outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
outb(port_base + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO); outb(port_base + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
ata_wait_bsy(port_base); ata_wait_bsy(port_base);
ata_wait_drq(port_base); ata_wait_drq(port_base);
const uint16_t *ptr = (const uint16_t*)buffer; const uint16_t *ptr = (const uint16_t*)buffer;
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++) {
outw(port_base + ATA_REG_DATA, ptr[i]); outw(port_base + ATA_REG_DATA, ptr[i]);
} }
// Flush / Sync
outb(port_base + ATA_REG_COMMAND, 0xE7); // Cache Flush outb(port_base + ATA_REG_COMMAND, 0xE7); // Cache Flush
ata_wait_bsy(port_base); ata_wait_bsy(port_base);
return 0; return 0;
} }
// === Device Naming ===
char disk_get_next_free_letter(void) { const char* disk_get_next_dev_name(void) {
for (int i = 0; i < MAX_DISKS; i++) { static char name[8];
char letter = 'A' + i; name[0] = 's';
bool used = false; name[1] = 'd';
for (int j = 0; j < disk_count; j++) { name[2] = 'a' + next_sd_index;
if (disks[j]->letter == letter) { name[3] = 0;
used = true; next_sd_index++;
break; return name;
}
}
if (!used) return letter;
}
return 0; // No free letters
} }
// === Registration ===
void disk_register(Disk *disk) { void disk_register(Disk *disk) {
if (disk_count >= MAX_DISKS) return; if (disk_count >= MAX_DISKS) return;
// Ensure letter is unique // Auto-assign devname if empty
if (disk->letter == 0) { if (disk->devname[0] == 0) {
disk->letter = disk_get_next_free_letter(); const char *n = disk_get_next_dev_name();
dm_strcpy(disk->devname, n);
} }
disk->registered = true;
disks[disk_count++] = disk; 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) { void disk_register_partition(Disk *parent, uint32_t lba_offset, uint32_t sector_count,
for (int i = 0; i < MAX_DISKS; i++) { bool is_fat32, int part_num) {
disks[i] = NULL; 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) { // === Lookup ===
// Uppercase
if (letter >= 'a' && letter <= 'z') letter -= 32; Disk* disk_get_by_name(const char *devname) {
if (!devname) return NULL;
for (int i = 0; i < disk_count; i++) { for (int i = 0; i < disk_count; i++) {
if (disks[i]->letter == letter) { if (dm_strcmp(disks[i]->devname, devname) == 0) {
return disks[i]; return disks[i];
} }
} }
@ -247,130 +305,181 @@ Disk* disk_get_by_index(int index) {
return disks[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 { typedef struct {
uint8_t status; // 0x80 = bootable, 0x00 = inactive uint8_t status;
uint8_t chs_first[3]; // CHS of first sector uint8_t chs_first[3];
uint8_t type; // Partition type uint8_t type;
uint8_t chs_last[3]; // CHS of last sector uint8_t chs_last[3];
uint32_t lba_start; // LBA of first sector uint32_t lba_start;
uint32_t sector_count; // Number of sectors uint32_t sector_count;
} __attribute__((packed)) MBR_PartitionEntry; } __attribute__((packed)) MBR_PartitionEntry;
// FAT32 partition type codes
#define PART_TYPE_FAT32 0x0B #define PART_TYPE_FAT32 0x0B
#define PART_TYPE_FAT32_LBA 0x0C #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) { static bool is_fat32_bpb(const uint8_t *sector) {
// Must have 0xAA55 boot signature
if (sector[510] != 0x55 || sector[511] != 0xAA) return false; 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' && if (sector[82] == 'F' && sector[83] == 'A' && sector[84] == 'T' &&
sector[85] == '3' && sector[86] == '2') { sector[85] == '3' && sector[86] == '2') {
return true; 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*)&sector[11]; uint16_t bps = *(uint16_t*)&sector[11];
uint16_t spf16 = *(uint16_t*)&sector[22]; uint16_t spf16 = *(uint16_t*)&sector[22];
uint32_t spf32 = *(uint32_t*)&sector[36]; uint32_t spf32 = *(uint32_t*)&sector[36];
if (bps == 512 && spf16 == 0 && spf32 > 0) { if (bps == 512 && spf16 == 0 && spf32 > 0) {
return true; return true;
} }
return false; return false;
} }
// Parse MBR partition table and find a FAT32 partition. // Parse MBR and register each partition as a child block device
// Sets disk->partition_lba_offset and returns true if found. static void parse_mbr_partitions(Disk *disk) {
static bool detect_fat32_partition(Disk *disk) {
uint8_t *buffer = (uint8_t*)kmalloc(512); uint8_t *buffer = (uint8_t*)kmalloc(512);
if (!buffer) return false; if (!buffer) return;
// Read sector 0 (MBR or raw BPB)
if (disk->read_sector(disk, 0, buffer) != 0) { if (disk->read_sector(disk, 0, buffer) != 0) {
kfree(buffer); kfree(buffer);
return false; return;
} }
// Must have 0xAA55 boot signature // Check for valid MBR signature
if (buffer[510] != 0x55 || buffer[511] != 0xAA) { if (buffer[510] != 0x55 || buffer[511] != 0xAA) {
kfree(buffer); kfree(buffer);
return false; return;
} }
// Check MBR partition table entries (4 entries at offset 446)
MBR_PartitionEntry *partitions = (MBR_PartitionEntry*)&buffer[446]; MBR_PartitionEntry *partitions = (MBR_PartitionEntry*)&buffer[446];
int part_num = 1;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if (partitions[i].type == PART_TYPE_FAT32 || uint32_t start = partitions[i].lba_start;
partitions[i].type == PART_TYPE_FAT32_LBA) { uint32_t size = partitions[i].sector_count;
uint8_t type = partitions[i].type;
uint32_t part_lba = partitions[i].lba_start;
if (type == 0x00) continue; // Empty entry
// Read the partition's first sector to verify it's a valid FAT32 BPB 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); uint8_t *pbuf = (uint8_t*)kmalloc(512);
if (!pbuf) { kfree(buffer); return false; } if (pbuf) {
if (disk->read_sector(disk, start, pbuf) == 0) {
if (disk->read_sector(disk, part_lba, pbuf) == 0 && is_fat32_bpb(pbuf)) { fat32 = is_fat32_bpb(pbuf);
disk->partition_lba_offset = part_lba; }
kfree(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) // Fallback: if no partitions found, check if entire disk is a raw FAT32 volume
if (is_fat32_bpb(buffer)) { 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; disk->partition_lba_offset = 0;
kfree(buffer);
return true;
} }
kfree(buffer); kfree(buffer);
return false;
} }
// === ATA Drive Discovery ===
static void try_add_ata_drive(uint16_t port, bool slave, const char *name) { 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)); Disk *new_disk = (Disk*)kmalloc(sizeof(Disk));
if (!new_disk) return; if (!new_disk) return;
ATADriverData *data = (ATADriverData*)kmalloc(sizeof(ATADriverData)); ATADriverData *data = (ATADriverData*)kmalloc(sizeof(ATADriverData));
data->port_base = port; data->port_base = port;
data->slave = slave; data->slave = slave;
new_disk->letter = 0; // Auto-assign new_disk->devname[0] = 0; // Auto-assign
new_disk->type = DISK_TYPE_IDE; 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->read_sector = ata_read_sector;
new_disk->write_sector = ata_write_sector; new_disk->write_sector = ata_write_sector;
new_disk->driver_data = data; new_disk->driver_data = data;
new_disk->partition_lba_offset = 0; new_disk->partition_lba_offset = 0;
new_disk->total_sectors = sectors;
// Detect FAT32 (with MBR partition support) new_disk->parent = NULL;
if (detect_fat32_partition(new_disk)) { new_disk->is_partition = false;
new_disk->is_fat32 = true; new_disk->is_fat32 = false;
disk_register(new_disk);
} else { disk_register(new_disk);
kfree(data);
kfree(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) { void disk_manager_scan(void) {
// Probe Standard ATA Ports serial_write("[DISK] Initializing AHCI (SATA DMA)...\n");
try_add_ata_drive(ATA_PRIMARY_IO, false, "IDE1"); ahci_init();
try_add_ata_drive(ATA_PRIMARY_IO, true, "IDE2");
try_add_ata_drive(ATA_SECONDARY_IO, false, "IDE3"); if (ahci_get_port_count() == 0) {
try_add_ata_drive(ATA_SECONDARY_IO, true, "IDE4"); 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");
}
} }

View file

@ -97,3 +97,23 @@ int pci_find_device_by_class(uint8_t class_code, uint8_t subclass, pci_device_t*
} }
return 0; 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);
}

View file

@ -22,6 +22,9 @@ typedef struct {
#define PCI_CLASS_NETWORK_CONTROLLER 0x02 #define PCI_CLASS_NETWORK_CONTROLLER 0x02
#define PCI_CLASS_ETHERNET_CONTROLLER 0x00 #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); 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); 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(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); 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 #endif

File diff suppressed because it is too large Load diff

View file

@ -25,7 +25,7 @@ typedef struct {
uint16_t num_heads; // Number of heads uint16_t num_heads; // Number of heads
uint32_t hidden_sectors; // Hidden sectors uint32_t hidden_sectors; // Hidden sectors
uint32_t total_sectors_32; // Total sectors 32-bit uint32_t total_sectors_32; // Total sectors 32-bit
// FAT32 Specific // FAT32 Specific
uint32_t sectors_per_fat_32; // Sectors per FAT 32-bit uint32_t sectors_per_fat_32; // Sectors per FAT 32-bit
uint16_t flags; // Flags uint16_t flags; // Flags
@ -61,6 +61,18 @@ typedef struct {
uint32_t file_size; // File size uint32_t file_size; // File size
} __attribute__((packed)) FAT32_DirEntry; } __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 // File Attributes
#define ATTR_READ_ONLY 0x01 #define ATTR_READ_ONLY 0x01
#define ATTR_HIDDEN 0x02 #define ATTR_HIDDEN 0x02
@ -70,6 +82,7 @@ typedef struct {
#define ATTR_ARCHIVE 0x20 #define ATTR_ARCHIVE 0x20
#define ATTR_DEVICE 0x40 #define ATTR_DEVICE 0x40
#define ATTR_RESERVED 0x80 #define ATTR_RESERVED 0x80
#define ATTR_LFN 0x0F // LFN marker (all of the above ORed)
// FAT32 Constants // FAT32 Constants
#define FAT32_SECTOR_SIZE 512 #define FAT32_SECTOR_SIZE 512
@ -88,7 +101,7 @@ typedef struct {
bool valid; // Is this handle valid? bool valid; // Is this handle valid?
uint32_t dir_sector; // Sector containing the directory entry uint32_t dir_sector; // Sector containing the directory entry
uint32_t dir_offset; // Offset within that sector 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; } FAT32_FileHandle;
// Directory Entry Info (for listing) // Directory Entry Info (for listing)
@ -101,12 +114,23 @@ typedef struct {
uint16_t write_time; uint16_t write_time;
} FAT32_FileInfo; } 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 === // === Function Declarations ===
// Initialization // Initialization
void fat32_init(void); void fat32_init(void);
// File Operations // File Operations (backward-compat wrappers — dispatch through VFS)
FAT32_FileHandle* fat32_open(const char *path, const char *mode); FAT32_FileHandle* fat32_open(const char *path, const char *mode);
void fat32_close(FAT32_FileHandle *handle); void fat32_close(FAT32_FileHandle *handle);
int fat32_read(FAT32_FileHandle *handle, void *buffer, int size); int fat32_read(FAT32_FileHandle *handle, void *buffer, int size);
@ -124,7 +148,10 @@ bool fat32_is_directory(const char *path);
// Listing // Listing
int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entries); 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); bool fat32_chdir(const char *path);
void fat32_get_current_dir(char *buffer, int size); void fat32_get_current_dir(char *buffer, int size);
bool fat32_change_drive(char drive); bool fat32_change_drive(char drive);

760
src/fs/vfs.c Normal file
View file

@ -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 <stddef.h>
#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/<devname>
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.
}

115
src/fs/vfs.h Normal file
View file

@ -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 <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#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

View file

@ -8,6 +8,7 @@
#include "process.h" #include "process.h"
#include "wm.h" #include "wm.h"
#include "fat32.h" #include "fat32.h"
#include "vfs.h"
#include "paging.h" #include "paging.h"
#include "work_queue.h" #include "work_queue.h"
#include "smp.h" #include "smp.h"
@ -852,33 +853,33 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
const char *mode = (const char *)arg3; const char *mode = (const char *)arg3;
if (!path || !mode) return -1; if (!path || !mode) return -1;
FAT32_FileHandle *fh = fat32_open(path, mode); vfs_file_t *vf = vfs_open(path, mode);
if (!fh) return -1; if (!vf) return -1;
for (int i = 0; i < MAX_PROCESS_FDS; i++) { for (int i = 0; i < MAX_PROCESS_FDS; i++) {
if (proc->fds[i] == NULL) { if (proc->fds[i] == NULL) {
proc->fds[i] = fh; proc->fds[i] = vf;
return (uint64_t)i; return (uint64_t)i;
} }
} }
fat32_close(fh); vfs_close(vf);
return -1; return -1;
} else if (cmd == FS_CMD_READ) { } else if (cmd == FS_CMD_READ) {
int fd = (int)arg2; int fd = (int)arg2;
void *buf = (void *)arg3; void *buf = (void *)arg3;
uint32_t len = (uint32_t)arg4; uint32_t len = (uint32_t)arg4;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; 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) { } else if (cmd == FS_CMD_WRITE) {
int fd = (int)arg2; int fd = (int)arg2;
const void *buf = (const void *)arg3; const void *buf = (const void *)arg3;
uint32_t len = (uint32_t)arg4; uint32_t len = (uint32_t)arg4;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; 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) { } else if (cmd == FS_CMD_CLOSE) {
int fd = (int)arg2; int fd = (int)arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; 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; proc->fds[fd] = NULL;
return 0; return 0;
} else if (cmd == FS_CMD_SEEK) { } else if (cmd == FS_CMD_SEEK) {
@ -886,49 +887,81 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
int offset = (int)arg3; int offset = (int)arg3;
int whence = (int)arg4; // 0=SET, 1=CUR, 2=END int whence = (int)arg4; // 0=SET, 1=CUR, 2=END
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; 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) { } else if (cmd == FS_CMD_TELL) {
int fd = (int)arg2; int fd = (int)arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; 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) { } else if (cmd == FS_CMD_SIZE) {
int fd = (int)arg2; int fd = (int)arg2;
if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1; if (fd < 0 || fd >= MAX_PROCESS_FDS || !proc->fds[fd]) return -1;
return (uint64_t)((FAT32_FileHandle*)proc->fds[fd])->size; return (uint64_t)vfs_file_size((vfs_file_t*)proc->fds[fd]);
} } else if (cmd == FS_CMD_LIST) {
else if (cmd == FS_CMD_LIST) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
FAT32_FileInfo *entries = (FAT32_FileInfo *)arg3; FAT32_FileInfo *u_entries = (FAT32_FileInfo *)arg3;
int max_entries = (int)arg4; int max_entries = (int)arg4;
if (!path || !entries) return -1; if (!path || !u_entries) return -1;
return (uint64_t)fat32_list_directory(path, entries, max_entries);
// 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) { } else if (cmd == FS_CMD_DELETE) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
if (!path) return -1; if (!path) return -1;
return fat32_delete(path) ? 0 : -1; return vfs_delete(path) ? 0 : -1;
} else if (cmd == FS_CMD_GET_INFO) { } else if (cmd == FS_CMD_GET_INFO) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
FAT32_FileInfo *info = (FAT32_FileInfo *)arg3; FAT32_FileInfo *u_info = (FAT32_FileInfo *)arg3;
if (!path || !info) return -1; if (!path || !u_info) return -1;
extern int fat32_get_info(const char *path, FAT32_FileInfo *info);
return (uint64_t)fat32_get_info(path, info); 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) { } else if (cmd == FS_CMD_MKDIR) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
if (!path) return -1; if (!path) return -1;
return fat32_mkdir(path) ? 0 : -1; return vfs_mkdir(path) ? 0 : -1;
} else if (cmd == FS_CMD_EXISTS) { } else if (cmd == FS_CMD_EXISTS) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
if (!path) return 0; if (!path) return 0;
return fat32_exists(path) ? 1 : 0; return vfs_exists(path) ? 1 : 0;
} else if (cmd == FS_CMD_GETCWD) { } else if (cmd == FS_CMD_GETCWD) {
char *buf = (char *)arg2; char *buf = (char *)arg2;
int size = (int)arg3; int size = (int)arg3;
if (!buf) return -1; if (!buf) return -1;
extern void fat32_get_current_dir(char *buf, int size);
fat32_get_current_dir(buf, size); fat32_get_current_dir(buf, size);
return 0; return 0;
} else if (cmd == FS_CMD_CHDIR) { } else if (cmd == FS_CMD_CHDIR) {
const char *path = (const char *)arg2; const char *path = (const char *)arg2;
if (!path) return -1; if (!path) return -1;
extern bool fat32_chdir(const char *path);
return fat32_chdir(path) ? 0 : -1; return fat32_chdir(path) ? 0 : -1;
} }
return 0; return 0;

View file

@ -8,6 +8,7 @@
#include "rtc.h" #include "rtc.h"
#include "fat32.h" #include "fat32.h"
#include "vfs.h"
#include "disk.h" #include "disk.h"
#include "kutils.h" #include "kutils.h"
#include <stddef.h> #include <stddef.h>
@ -75,7 +76,6 @@ static ShellConfig shell_config;
// CMD Window State (per-window context) // CMD Window State (per-window context)
typedef struct { typedef struct {
char current_drive;
char current_dir[256]; char current_dir[256];
} CmdState; } CmdState;
@ -554,10 +554,7 @@ void cmd_print_prompt(void) {
cursor_col = 0; cursor_col = 0;
if (shell_config.show_drive) { if (shell_config.show_drive) {
current_color = shell_config.prompt_drive_color; // No more drive letter in VFS
cmd_putchar(cmd_state->current_drive);
current_color = shell_config.prompt_colon_color;
cmd_putchar(':');
} }
if (shell_config.show_dir) { if (shell_config.show_dir) {
@ -951,11 +948,7 @@ static void internal_cmd_cd(char *args) {
if (!args || !args[0]) { if (!args || !args[0]) {
// No argument - show current directory // No argument - show current directory
if (cmd_state) { 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(cmd_state->current_dir);
cmd_write("\n"); cmd_write("\n");
} else { } else {
@ -985,9 +978,7 @@ static void internal_cmd_cd(char *args) {
cmd_strcpy(full_path, path); cmd_strcpy(full_path, path);
} else if (path[0] == '/') { } else if (path[0] == '/') {
// Absolute path // Absolute path
full_path[0] = cmd_state->current_drive; int j = 0;
full_path[1] = ':';
int j = 2;
int k = 0; int k = 0;
while (path[k] && j < 509) { while (path[k] && j < 509) {
full_path[j++] = path[k++]; full_path[j++] = path[k++];
@ -995,9 +986,7 @@ static void internal_cmd_cd(char *args) {
full_path[j] = 0; full_path[j] = 0;
} else { } else {
// Relative path - resolve from current directory // Relative path - resolve from current directory
full_path[0] = cmd_state->current_drive; int j = 0;
full_path[1] = ':';
int j = 2;
// Copy current directory // Copy current directory
const char *dir = cmd_state->current_dir; const char *dir = cmd_state->current_dir;
@ -1019,18 +1008,14 @@ static void internal_cmd_cd(char *args) {
} }
// Validate directory exists // Validate directory exists
if (fat32_is_directory(full_path)) { if (vfs_is_directory(full_path)) {
// Normalize the path to resolve .. and . // Normalize the path to resolve .. and .
char normalized_path[512]; char normalized_path[512];
fat32_normalize_path(full_path, normalized_path); vfs_normalize_path(full_path, normalized_path);
cmd_update_dir(normalized_path); cmd_update_dir(normalized_path);
cmd_write("Changed to: "); 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(cmd_state->current_dir);
cmd_write("\n"); cmd_write("\n");
} else { } else {
@ -1105,15 +1090,13 @@ void cmd_exec_elf(char *args) {
char full_exec_path[512] = {0}; char full_exec_path[512] = {0};
int i = 0; int i = 0;
if (args[0] && args[1] != ':') { if (args[0] && args[0] != '/') {
if (cmd_state) { if (cmd_state) {
full_exec_path[i++] = cmd_state->current_drive;
full_exec_path[i++] = ':';
const char *dir = cmd_state->current_dir; const char *dir = cmd_state->current_dir;
while (*dir && i < 509) { while (*dir && i < 509) {
full_exec_path[i++] = *dir++; 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++] = '/'; 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) { static void cmd_update_dir(const char *path) {
if (!cmd_state || !path) return; if (!cmd_state || !path) return;
// Extract drive if provided
const char *p = path; 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) { if (*p) {
// Remove trailing slashes and copy // Remove trailing slashes and copy
int len = 0; int len = 0;
@ -1269,20 +1239,7 @@ static void cmd_exec_single(char *cmd) {
// Check for drive switch (e.g. "A:", "B:") // Check for drive switch (e.g. "A:", "B:")
if (cmd[0] && cmd[1] == ':' && cmd[2] == 0) { if (cmd[0] && cmd[1] == ':' && cmd[2] == 0) {
char letter = cmd[0]; cmd_write("Drive letters are no longer supported. Use VFS paths (e.g. /dev/sda1)\n");
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");
}
return; return;
} }
@ -1336,19 +1293,15 @@ static void cmd_exec_single(char *cmd) {
char full_exec_path[512]; char full_exec_path[512];
int i = 0; int i = 0;
// Add drive letter // Add current directory
if (cmd_state) { 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; const char *dir = cmd_state->current_dir;
while (*dir && i < 509) { while (*dir && i < 509) {
full_exec_path[i++] = *dir++; full_exec_path[i++] = *dir++;
} }
// Add separator if current dir doesn't end with / // 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++] = '/'; full_exec_path[i++] = '/';
} }
} }
@ -1436,15 +1389,11 @@ static void cmd_exec_single(char *cmd) {
i += 2; i += 2;
while (args[i] == ' ') { temp_args[j++] = ' '; i++; } while (args[i] == ' ') { temp_args[j++] = ' '; i++; }
// Prepend drive and directory to filename if relative // Prepend directory to filename if relative
if (args[i] && args[i+1] != ':') { if (args[i] && args[i] != '/') {
temp_args[j++] = cmd_state->current_drive; const char *d = cmd_state->current_dir;
temp_args[j++] = ':'; while (*d && j < 509) temp_args[j++] = *d++;
if (args[i] != '/') { if (j > 0 && temp_args[j-1] != '/') temp_args[j++] = '/';
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++] = '/';
}
} }
in_redirect = true; in_redirect = true;
} else if (args[i] == '>' && args[i+1] != '>') { } else if (args[i] == '>' && args[i+1] != '>') {
@ -1453,15 +1402,11 @@ static void cmd_exec_single(char *cmd) {
i++; i++;
while (args[i] == ' ') { temp_args[j++] = ' '; i++; } while (args[i] == ' ') { temp_args[j++] = ' '; i++; }
// Prepend drive and directory to filename if relative // Prepend directory to filename if relative
if (args[i] && args[i+1] != ':') { if (args[i] && args[i] != '/') {
temp_args[j++] = cmd_state->current_drive; const char *d = cmd_state->current_dir;
temp_args[j++] = ':'; while (*d && j < 509) temp_args[j++] = *d++;
if (args[i] != '/') { if (j > 0 && temp_args[j-1] != '/') temp_args[j++] = '/';
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++] = '/';
}
} }
in_redirect = true; in_redirect = true;
} else { } else {
@ -1488,10 +1433,8 @@ static void cmd_exec_single(char *cmd) {
// Already has drive letter // Already has drive letter
cmd_strcpy(full_path_arg, args); cmd_strcpy(full_path_arg, args);
} else if (args[0] == '/') { } else if (args[0] == '/') {
// Absolute path, just prepend drive // Absolute path
full_path_arg[0] = cmd_state->current_drive; int i = 0;
full_path_arg[1] = ':';
int i = 2;
int j = 0; int j = 0;
while (args[j] && i < 509) { while (args[j] && i < 509) {
full_path_arg[i++] = args[j++]; full_path_arg[i++] = args[j++];
@ -1500,8 +1443,6 @@ static void cmd_exec_single(char *cmd) {
} else { } else {
// Relative path - need to build from current directory // Relative path - need to build from current directory
int i = 0; int i = 0;
full_path_arg[i++] = cmd_state->current_drive;
full_path_arg[i++] = ':';
// Add current directory // Add current directory
const char *dir = cmd_state->current_dir; 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 / // 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++] = '/'; full_path_arg[i++] = '/';
} }
@ -1529,10 +1470,8 @@ static void cmd_exec_single(char *cmd) {
// Has drive letter, use as-is // Has drive letter, use as-is
cmd_strcpy(full_path_arg, args); cmd_strcpy(full_path_arg, args);
} else if (args[0] == '/') { } else if (args[0] == '/') {
// Absolute path, just prepend drive // Absolute path
full_path_arg[0] = cmd_state->current_drive; int i = 0;
full_path_arg[1] = ':';
int i = 2;
int j = 0; int j = 0;
while (args[j] && i < 509) { while (args[j] && i < 509) {
full_path_arg[i++] = args[j++]; full_path_arg[i++] = args[j++];
@ -1541,8 +1480,6 @@ static void cmd_exec_single(char *cmd) {
} else { } else {
// Relative path - need to build from current directory // Relative path - need to build from current directory
int i = 0; int i = 0;
full_path_arg[i++] = cmd_state->current_drive;
full_path_arg[i++] = ':';
// Add current directory // Add current directory
const char *dir = cmd_state->current_dir; 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 / // 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++] = '/'; full_path_arg[i++] = '/';
} }
@ -1568,10 +1505,10 @@ static void cmd_exec_single(char *cmd) {
cmd_strcpy(full_path_arg, args); cmd_strcpy(full_path_arg, args);
args = full_path_arg; args = full_path_arg;
} else { } else {
// Add drive letter // Try to use arg as is (it's either absolute or relative)
full_path_arg[0] = cmd_state->current_drive; // If it's relative, we should actually build it...
full_path_arg[1] = ':'; // Let's just use it as is for now since later path resolving will catch it
int i = 2; int i = 0;
int j = 0; int j = 0;
while (args[j] && i < 509) { while (args[j] && i < 509) {
full_path_arg[i++] = args[j++]; full_path_arg[i++] = args[j++];
@ -1580,10 +1517,8 @@ static void cmd_exec_single(char *cmd) {
args = full_path_arg; args = full_path_arg;
} }
} else if (is_ls_command || is_cd_command) { } else if (is_ls_command || is_cd_command) {
// For ls and cd with no args, pass current directory with drive // For ls and cd with no args, pass current directory
full_path_arg[0] = cmd_state->current_drive; int i = 0;
full_path_arg[1] = ':';
int i = 2;
const char *dir = cmd_state->current_dir; const char *dir = cmd_state->current_dir;
while (*dir && i < 509) { while (*dir && i < 509) {
full_path_arg[i++] = *dir++; full_path_arg[i++] = *dir++;
@ -1619,11 +1554,9 @@ static void cmd_exec_single(char *cmd) {
// 1. Try Current Directory + .elf // 1. Try Current Directory + .elf
if (cmd_state) { if (cmd_state) {
int idx = 0; int idx = 0;
search_path[idx++] = cmd_state->current_drive;
search_path[idx++] = ':';
const char *dir = cmd_state->current_dir; const char *dir = cmd_state->current_dir;
while (*dir && idx < 500) search_path[idx++] = *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; const char *c = cmd;
while (*c && idx < 500) search_path[idx++] = *c++; while (*c && idx < 500) search_path[idx++] = *c++;
if (!has_elf_ext) { 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; int idx = 0;
const char *bin_prefix = "A:/bin/"; const char *bin_prefix = "/bin/";
while (*bin_prefix) search_path[idx++] = *bin_prefix++; while (*bin_prefix) search_path[idx++] = *bin_prefix++;
const char *c = cmd; const char *c = cmd;
while (*c && idx < 500) search_path[idx++] = *c++; while (*c && idx < 500) search_path[idx++] = *c++;
@ -2389,7 +2322,7 @@ void cmd_init(void) {
// Initialize cmd state (per-window context) // Initialize cmd state (per-window context)
CmdState *state = (CmdState*)kmalloc(sizeof(CmdState)); CmdState *state = (CmdState*)kmalloc(sizeof(CmdState));
if (state) { if (state) {
state->current_drive = 'A';
state->current_dir[0] = '/'; state->current_dir[0] = '/';
state->current_dir[1] = 0; state->current_dir[1] = 0;
win_cmd.data = state; win_cmd.data = state;

View file

@ -5,6 +5,7 @@
#include "graphics.h" #include "graphics.h"
#include "font_manager.h" #include "font_manager.h"
#include "fat32.h" #include "fat32.h"
#include "vfs.h"
#include "disk.h" #include "disk.h"
#include "wm.h" #include "wm.h"
#include "memory_manager.h" #include "memory_manager.h"
@ -302,14 +303,14 @@ static void dialog_confirm_create_file(Window *win) {
} }
explorer_strcat(full_path, state->dialog_input); explorer_strcat(full_path, state->dialog_input);
if (fat32_exists(full_path)) { if (vfs_exists(full_path)) {
state->dialog_state = DIALOG_CREATE_REPLACE_CONFIRM; state->dialog_state = DIALOG_CREATE_REPLACE_CONFIRM;
return; return;
} }
FAT32_FileHandle *file = fat32_open(full_path, "w"); vfs_file_t *file = vfs_open(full_path, "w");
if (file) { if (file) {
fat32_close(file); vfs_close(file);
explorer_refresh_all(); explorer_refresh_all();
} }
@ -325,9 +326,9 @@ static void dialog_force_create_file(Window *win) {
} }
explorer_strcat(full_path, state->dialog_input); 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) { if (file) {
fat32_close(file); vfs_close(file);
explorer_refresh_all(); explorer_refresh_all();
} }
dialog_close(win); dialog_close(win);
@ -346,7 +347,7 @@ static void dialog_confirm_create_folder(Window *win) {
} }
explorer_strcat(full_path, state->dialog_input); explorer_strcat(full_path, state->dialog_input);
if (fat32_mkdir(full_path)) { if (vfs_mkdir(full_path)) {
explorer_refresh_all(); explorer_refresh_all();
} }
@ -354,18 +355,18 @@ static void dialog_confirm_create_folder(Window *win) {
} }
bool explorer_delete_permanently(const char *path) { bool explorer_delete_permanently(const char *path) {
if (fat32_is_directory(path)) { if (vfs_is_directory(path)) {
int capacity = 64; 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; if (!entries) return false;
int count = fat32_list_directory(path, entries, capacity); int count = vfs_list_directory(path, entries, capacity);
while (count == capacity) { while (count == capacity) {
capacity *= 2; 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; } if (!new_entries) { kfree(entries); return false; }
entries = new_entries; entries = new_entries;
count = fat32_list_directory(path, entries, capacity); count = vfs_list_directory(path, entries, capacity);
} }
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
@ -381,13 +382,13 @@ bool explorer_delete_permanently(const char *path) {
if (entries[i].is_directory) { if (entries[i].is_directory) {
explorer_delete_permanently(child_path); explorer_delete_permanently(child_path);
} else { } else {
fat32_delete(child_path); vfs_delete(child_path);
} }
} }
kfree(entries); kfree(entries);
return fat32_rmdir(path); return vfs_rmdir(path);
} else { } 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]; char origin_path[FAT32_MAX_PATH];
explorer_strcpy(origin_path, dest_path); explorer_strcpy(origin_path, dest_path);
explorer_strcat(origin_path, ".origin"); explorer_strcat(origin_path, ".origin");
FAT32_FileHandle *fh = fat32_open(origin_path, "w"); vfs_file_t *fh = vfs_open(origin_path, "w");
if (fh) { if (fh) {
fat32_write(fh, path, explorer_strlen(path)); vfs_write(fh, path, explorer_strlen(path));
fat32_close(fh); vfs_close(fh);
} }
explorer_copy_recursive(path, dest_path); 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) { static bool explorer_copy_recursive(const char *src_path, const char *dest_path) {
if (fat32_is_directory(src_path)) { if (vfs_is_directory(src_path)) {
if (!fat32_mkdir(dest_path)) return false; if (!vfs_mkdir(dest_path)) return false;
int capacity = 64; 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; 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) { while (count == capacity) {
capacity *= 2; 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; } if (!new_files) { kfree(files); return false; }
files = new_files; 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++) { for (int i = 0; i < count; i++) {
if (explorer_strcmp(files[i].name, ".") == 0 || explorer_strcmp(files[i].name, "..") == 0) continue; 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); kfree(files);
return true; return true;
} else { } else {
FAT32_FileHandle *src = fat32_open(src_path, "r"); vfs_file_t *src = vfs_open(src_path, "r");
FAT32_FileHandle *dst = fat32_open(dest_path, "w"); vfs_file_t *dst = vfs_open(dest_path, "w");
bool success = false; bool success = false;
if (src && dst) { if (src && dst) {
uint8_t *buf = (uint8_t*)kmalloc(4096); uint8_t *buf = (uint8_t*)kmalloc(4096);
if (buf) { if (buf) {
int bytes; int bytes;
success = true; success = true;
while ((bytes = fat32_read(src, buf, 4096)) > 0) { while ((bytes = vfs_read(src, buf, 4096)) > 0) {
if (fat32_write(dst, buf, bytes) != bytes) { success = false; break; } if (vfs_write(dst, buf, bytes) != bytes) { success = false; break; }
} }
kfree(buf); kfree(buf);
} }
} }
if (src) fat32_close(src); if (src) vfs_close(src);
if (dst) fat32_close(dst); if (dst) vfs_close(dst);
return success; 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); explorer_copy_file_internal(clipboard_path, dest_dir);
if (clipboard_action == 2) { if (clipboard_action == 2) {
if (fat32_is_directory(clipboard_path)) { if (vfs_is_directory(clipboard_path)) {
explorer_delete_permanently(clipboard_path); explorer_delete_permanently(clipboard_path);
} else { } else {
fat32_delete(clipboard_path); vfs_delete(clipboard_path);
} }
clipboard_action = 0; clipboard_action = 0;
} }
@ -580,7 +581,7 @@ void explorer_clipboard_paste(Window *win, const char *dest_dir) {
} }
explorer_strcat(dest_path, filename); explorer_strcat(dest_path, filename);
if (fat32_exists(dest_path)) { if (vfs_exists(dest_path)) {
state->dialog_state = DIALOG_REPLACE_CONFIRM; state->dialog_state = DIALOG_REPLACE_CONFIRM;
explorer_strcpy(state->dialog_dest_dir, dest_dir); explorer_strcpy(state->dialog_dest_dir, dest_dir);
return; return;
@ -605,10 +606,10 @@ void explorer_create_shortcut(Window *win, const char *target_path) {
explorer_strcat(shortcut_path, filename); explorer_strcat(shortcut_path, filename);
explorer_strcat(shortcut_path, ".shortcut"); explorer_strcat(shortcut_path, ".shortcut");
FAT32_FileHandle *fh = fat32_open(shortcut_path, "w"); vfs_file_t *fh = vfs_open(shortcut_path, "w");
if (fh) { if (fh) {
fat32_write(fh, target_path, explorer_strlen(target_path)); vfs_write(fh, target_path, explorer_strlen(target_path));
fat32_close(fh); vfs_close(fh);
explorer_refresh_all(); explorer_refresh_all();
} }
} }
@ -680,18 +681,18 @@ static void explorer_restore_file(Window *win, int item_idx) {
explorer_strcat(origin_file_path, ".origin"); explorer_strcat(origin_file_path, ".origin");
char original_path[FAT32_MAX_PATH] = {0}; 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) { 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; if (len > 0) original_path[len] = 0;
fat32_close(fh); vfs_close(fh);
} }
if (original_path[0] == 0) return; if (original_path[0] == 0) return;
explorer_copy_recursive(recycle_path, original_path); explorer_copy_recursive(recycle_path, original_path);
explorer_delete_permanently(recycle_path); explorer_delete_permanently(recycle_path);
fat32_delete(origin_file_path); vfs_delete(origin_file_path);
explorer_refresh_all(); explorer_refresh_all();
} }
@ -704,16 +705,16 @@ static void explorer_load_directory(Window *win, const char *path) {
state->item_count = 0; state->item_count = 0;
int capacity = EXPLORER_INITIAL_CAPACITY; 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; if (!entries) return;
int count = fat32_list_directory(path, entries, capacity); int count = vfs_list_directory(path, entries, capacity);
while (count == capacity) { while (count == capacity) {
capacity *= 2; 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; } if (!new_entries) { kfree(entries); return; }
entries = new_entries; entries = new_entries;
count = fat32_list_directory(path, entries, capacity); count = vfs_list_directory(path, entries, capacity);
} }
if (state->items_capacity < count) { if (state->items_capacity < count) {
@ -804,7 +805,7 @@ void explorer_open_directory(const char *path) {
} }
static void explorer_open_target(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); explorer_open_directory(path);
} else { } else {
if (explorer_str_ends_with(path, ".elf")) { if (explorer_str_ends_with(path, ".elf")) {
@ -864,11 +865,11 @@ static void explorer_open_item(Window *win, int index) {
return; return;
} }
FAT32_FileHandle *fh = fat32_open(full_path, "r"); vfs_file_t *fh = vfs_open(full_path, "r");
if (fh) { if (fh) {
char buf[FAT32_MAX_PATH]; char buf[FAT32_MAX_PATH];
int len = fat32_read(fh, buf, 255); int len = vfs_read(fh, buf, 255);
fat32_close(fh); vfs_close(fh);
if (len > 0) { if (len > 0) {
buf[len] = 0; buf[len] = 0;
explorer_open_target(buf); explorer_open_target(buf);
@ -1025,14 +1026,11 @@ static void explorer_paint(Window *win) {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
Disk *d = disk_get_by_index(i); Disk *d = disk_get_by_index(i);
if (d) { if (d) {
char buf[16]; char buf[32];
buf[0] = d->letter; explorer_strcpy(buf, d->devname);
buf[1] = ':';
buf[2] = ' ';
int n = 0; while(d->name[n] && n < 10) { buf[3+n] = d->name[n]; n++; }
buf[3+n] = 0;
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_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); draw_string(menu_x + 5, menu_y + i*25 + 6, buf, COLOR_WHITE);
} else { } 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, "/"); if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/");
explorer_strcat(new_path, state->dialog_input); 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); dialog_close(win);
return; return;
} }
@ -1347,11 +1345,10 @@ static void explorer_handle_click(Window *win, int x, int y) {
int idx = (y - menu_y) / 25; int idx = (y - menu_y) / 25;
Disk *d = disk_get_by_index(idx); Disk *d = disk_get_by_index(idx);
if (d) { if (d) {
char path[4]; char path[64];
path[0] = d->letter; explorer_strcpy(path, "/dev/");
path[1] = ':'; explorer_strcat(path, d->devname);
path[2] = '/'; explorer_strcat(path, "/"); // Mount point is /dev/devname
path[3] = 0;
explorer_load_directory(win, path); explorer_load_directory(win, path);
} }
state->drive_menu_visible = false; 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); explorer_strcpy(new_path, state->current_path);
if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/"); if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/");
explorer_strcat(new_path, state->dialog_input); 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); dialog_close(win);
} }
} else if (c == 19) { } 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]; char origin_path[FAT32_MAX_PATH];
explorer_strcpy(origin_path, dest_path); explorer_strcpy(origin_path, dest_path);
explorer_strcat(origin_path, ".origin"); explorer_strcat(origin_path, ".origin");
FAT32_FileHandle *fh = fat32_open(origin_path, "w"); vfs_file_t *fh = vfs_open(origin_path, "w");
if (fh) { if (fh) {
fat32_write(fh, source_path, explorer_strlen(source_path)); vfs_write(fh, source_path, explorer_strlen(source_path));
fat32_close(fh); 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]; char origin_path[FAT32_MAX_PATH];
explorer_strcpy(origin_path, source_path); explorer_strcpy(origin_path, source_path);
explorer_strcat(origin_path, ".origin"); explorer_strcat(origin_path, ".origin");
fat32_delete(origin_path); vfs_delete(origin_path);
} }
if (explorer_copy_recursive(source_path, dest_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, "/"); if (dest_path[explorer_strlen(dest_path) - 1] != '/') explorer_strcat(dest_path, "/");
explorer_strcat(dest_path, filename); 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_move_src, source_path);
explorer_strcpy(state->dialog_dest_dir, dest_dir); explorer_strcpy(state->dialog_dest_dir, dest_dir);
state->dialog_state = DIALOG_REPLACE_MOVE_CONFIRM; state->dialog_state = DIALOG_REPLACE_MOVE_CONFIRM;

View file

@ -25,7 +25,7 @@ void wm_lock_release(uint64_t flags);
#define COLOR_ORANGE 0xFFFF7F00 #define COLOR_ORANGE 0xFFFF7F00
#define COLOR_YELLOW 0xFFFFFF00 #define COLOR_YELLOW 0xFFFFFF00
#define COLOR_GREEN 0xFF00FF00 #define COLOR_GREEN 0xFF00FF00
#define COLOR_BLUE 0xFF0000FF #define COLOR_LIGHTBLUE 0xFF0000FF
#define COLOR_APPLE_INDIGO 0xFF4B0082 #define COLOR_APPLE_INDIGO 0xFF4B0082
#define COLOR_APPLE_VIOLET 0xFF9400D3 #define COLOR_APPLE_VIOLET 0xFF9400D3