mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
CHECKP: shitty VFS
This commit is contained in:
parent
6b6a22d518
commit
5933483009
15 changed files with 2768 additions and 575 deletions
|
|
@ -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
509
src/dev/ahci.c
Normal 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
174
src/dev/ahci.h
Normal 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
|
||||||
|
|
@ -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,11 +18,12 @@ 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);
|
||||||
|
|
@ -29,14 +31,32 @@ typedef struct Disk {
|
||||||
|
|
||||||
// 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
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,38 @@
|
||||||
#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,48 +83,39 @@ 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;
|
||||||
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)));
|
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
|
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) {
|
||||||
|
|
@ -127,9 +123,17 @@ static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) {
|
||||||
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);
|
||||||
|
|
@ -146,7 +150,7 @@ static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) {
|
||||||
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) {
|
||||||
|
|
@ -154,6 +158,14 @@ static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) {
|
||||||
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));
|
||||||
|
|
@ -172,66 +184,112 @@ static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) {
|
||||||
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,36 +305,57 @@ 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*)§or[11];
|
uint16_t bps = *(uint16_t*)§or[11];
|
||||||
uint16_t spf16 = *(uint16_t*)§or[22];
|
uint16_t spf16 = *(uint16_t*)§or[22];
|
||||||
uint32_t spf32 = *(uint32_t*)§or[36];
|
uint32_t spf32 = *(uint32_t*)§or[36];
|
||||||
|
|
@ -287,60 +366,68 @@ static bool is_fat32_bpb(const uint8_t *sector) {
|
||||||
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
|
||||||
|
if (size == 0) continue;
|
||||||
|
if (start >= disk->total_sectors) continue; // Invalid start
|
||||||
|
|
||||||
// Read the partition's first sector to verify it's a valid FAT32 BPB
|
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(buffer);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
kfree(pbuf);
|
kfree(pbuf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: check if sector 0 itself is a raw FAT32 BPB (no partition table)
|
disk_register_partition(disk, partitions[i].lba_start,
|
||||||
if (is_fat32_bpb(buffer)) {
|
partitions[i].sector_count, fat32, part_num);
|
||||||
|
part_num++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: if no partitions found, check if entire disk is a raw FAT32 volume
|
||||||
|
if (part_num == 1 && is_fat32_bpb(buffer)) {
|
||||||
|
serial_write("[DISK] No MBR partitions — raw FAT32 volume on /dev/");
|
||||||
|
serial_write(disk->devname);
|
||||||
|
serial_write("\n");
|
||||||
|
disk->is_fat32 = true;
|
||||||
disk->partition_lba_offset = 0;
|
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;
|
||||||
|
|
||||||
|
|
@ -348,29 +435,51 @@ static void try_add_ata_drive(uint16_t port, bool slave, const char *name) {
|
||||||
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;
|
||||||
|
new_disk->parent = NULL;
|
||||||
|
new_disk->is_partition = false;
|
||||||
|
new_disk->is_fat32 = false;
|
||||||
|
|
||||||
// Detect FAT32 (with MBR partition support)
|
|
||||||
if (detect_fat32_partition(new_disk)) {
|
|
||||||
new_disk->is_fat32 = true;
|
|
||||||
disk_register(new_disk);
|
disk_register(new_disk);
|
||||||
} else {
|
|
||||||
kfree(data);
|
// Parse MBR to find partitions
|
||||||
kfree(new_disk);
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
768
src/fs/fat32.c
768
src/fs/fat32.c
File diff suppressed because it is too large
Load diff
|
|
@ -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
760
src/fs/vfs.c
Normal 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
115
src/fs/vfs.h
Normal 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
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
137
src/wm/cmd.c
137
src/wm/cmd.c
|
|
@ -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
|
|
||||||
if (cmd_state) {
|
|
||||||
full_exec_path[i++] = cmd_state->current_drive;
|
|
||||||
full_exec_path[i++] = ':';
|
|
||||||
|
|
||||||
// Add current directory
|
// Add current directory
|
||||||
|
if (cmd_state) {
|
||||||
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;
|
|
||||||
temp_args[j++] = ':';
|
|
||||||
if (args[i] != '/') {
|
|
||||||
const char *d = cmd_state->current_dir;
|
const char *d = cmd_state->current_dir;
|
||||||
while (*d && j < 509) temp_args[j++] = *d++;
|
while (*d && j < 509) temp_args[j++] = *d++;
|
||||||
if (j > 2 && temp_args[j-1] != '/') temp_args[j++] = '/';
|
if (j > 0 && 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;
|
|
||||||
temp_args[j++] = ':';
|
|
||||||
if (args[i] != '/') {
|
|
||||||
const char *d = cmd_state->current_dir;
|
const char *d = cmd_state->current_dir;
|
||||||
while (*d && j < 509) temp_args[j++] = *d++;
|
while (*d && j < 509) temp_args[j++] = *d++;
|
||||||
if (j > 2 && temp_args[j-1] != '/') temp_args[j++] = '/';
|
if (j > 0 && 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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue