diff --git a/.gitignore b/.gitignore index 8420e88..7db5ec6 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ limine 2/Makefile limine 2/limine limine 2/limine.dSYM/Contents/Resources/DWARF/limine limine 2/limine.exe +disk.img \ No newline at end of file diff --git a/Makefile b/Makefile index 9302bf7..1c1f214 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ ISO_DIR = iso_root KERNEL_ELF = $(BUILD_DIR)/brewos.elf ISO_IMAGE = brewos.iso +DISK_IMG = disk.img C_SOURCES = $(wildcard $(SRC_DIR)/*.c) CLI_APP_SOURCES = $(wildcard $(SRC_DIR)/cli_apps/*.c) @@ -37,7 +38,7 @@ LIMINE_URL_BASE = https://github.com/limine-bootloader/limine/raw/v$(LIMINE_VERS .PHONY: all clean run limine-setup -all: $(ISO_IMAGE) +all: $(ISO_IMAGE) $(DISK_IMG) # Ensure build directories exist $(BUILD_DIR): @@ -112,10 +113,19 @@ $(ISO_IMAGE): $(KERNEL_ELF) limine.cfg limine-setup # Install Limine to ISO (for BIOS boot) ./limine/limine bios-install $(ISO_IMAGE) -clean: - rm -rf $(BUILD_DIR) $(ISO_DIR) $(ISO_IMAGE) +# Create 512MB FAT32 Disk Image +$(DISK_IMG): + @if [ ! -f $(DISK_IMG) ]; then \ + echo "Creating 512MB FAT32 disk image..."; \ + hdiutil create -size 512m -fs "MS-DOS FAT32" -layout NONE -type UDTO -o disk_temp; \ + mv disk_temp.cdr $(DISK_IMG); \ + fi -run: $(ISO_IMAGE) +clean: + rm -rf $(BUILD_DIR) $(ISO_DIR) $(ISO_IMAGE) $(DISK_IMG) + +run: $(ISO_IMAGE) $(DISK_IMG) qemu-system-x86_64 -m 2G -serial stdio -cdrom $(ISO_IMAGE) -boot d \ -audiodev coreaudio,id=audio0 -machine pcspk-audiodev=audio0 \ - -netdev user,id=net0,hostfwd=udp::12345-:12345 -device e1000,netdev=net0 + -netdev user,id=net0,hostfwd=udp::12345-:12345 -device e1000,netdev=net0 \ + -drive file=$(DISK_IMG),format=raw,media=disk,index=1 diff --git a/README.md b/README.md index fbeb795..59dadbb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Brew OS 1.20 Alpha +# Brew OS 1.40 Alpha ## Brewkernel is now BrewOS! Brewkernel will from now on be deprecated as it's core became too messy. I have built a less bloated kernel and wrote a DE above it, which is why it is now an OS instead of a kernel (in my opinion). @@ -9,7 +9,8 @@ Brew Kernel is a simple x86_64 hobbyist operating system. It features a DE (and WM), a FAT32 filesystem, customizable UI and much much more! ## Features -- Basic Networking Stack +-Disk saving +- Basic Networking Stack (UDP, TCP) - Brew WM - Fat 32 FS - 64-bit long mode support diff --git a/brewos.iso b/brewos.iso index 79f39b4..8dfbf5f 100644 Binary files a/brewos.iso and b/brewos.iso differ diff --git a/build/about.o b/build/about.o index 2481993..d896358 100644 Binary files a/build/about.o and b/build/about.o differ diff --git a/build/ata.o b/build/ata.o new file mode 100644 index 0000000..df8464b Binary files /dev/null and b/build/ata.o differ diff --git a/build/brewos.elf b/build/brewos.elf index c33e912..fbc1d89 100755 Binary files a/build/brewos.elf and b/build/brewos.elf differ diff --git a/build/calculator.o b/build/calculator.o index 032a0a9..8bd59ae 100644 Binary files a/build/calculator.o and b/build/calculator.o differ diff --git a/build/cli_apps/about.o b/build/cli_apps/about.o index 19001a8..45fb4fc 100644 Binary files a/build/cli_apps/about.o and b/build/cli_apps/about.o differ diff --git a/build/cli_apps/beep.o b/build/cli_apps/beep.o index 27522fa..af3a888 100644 Binary files a/build/cli_apps/beep.o and b/build/cli_apps/beep.o differ diff --git a/build/cli_apps/blind.o b/build/cli_apps/blind.o index 0b3e866..ce7040f 100644 Binary files a/build/cli_apps/blind.o and b/build/cli_apps/blind.o differ diff --git a/build/cli_apps/cc.o b/build/cli_apps/cc.o index 3f0d347..0508aff 100644 Binary files a/build/cli_apps/cc.o and b/build/cli_apps/cc.o differ diff --git a/build/cli_apps/clear.o b/build/cli_apps/clear.o index ff6ff00..93b8647 100644 Binary files a/build/cli_apps/clear.o and b/build/cli_apps/clear.o differ diff --git a/build/cli_apps/cli_utils.o b/build/cli_apps/cli_utils.o index eb10a93..b0a0698 100644 Binary files a/build/cli_apps/cli_utils.o and b/build/cli_apps/cli_utils.o differ diff --git a/build/cli_apps/cowsay.o b/build/cli_apps/cowsay.o index 9f174a8..394f050 100644 Binary files a/build/cli_apps/cowsay.o and b/build/cli_apps/cowsay.o differ diff --git a/build/cli_apps/date.o b/build/cli_apps/date.o index 58b1106..3d81c8c 100644 Binary files a/build/cli_apps/date.o and b/build/cli_apps/date.o differ diff --git a/build/cli_apps/exit.o b/build/cli_apps/exit.o index 64d31fc..7bf49a1 100644 Binary files a/build/cli_apps/exit.o and b/build/cli_apps/exit.o differ diff --git a/build/cli_apps/fs_commands.o b/build/cli_apps/fs_commands.o index 9c32906..36f89d8 100644 Binary files a/build/cli_apps/fs_commands.o and b/build/cli_apps/fs_commands.o differ diff --git a/build/cli_apps/help.o b/build/cli_apps/help.o index 5a6069a..521e7fd 100644 Binary files a/build/cli_apps/help.o and b/build/cli_apps/help.o differ diff --git a/build/cli_apps/license.o b/build/cli_apps/license.o index 2c9dad6..f0c77af 100644 Binary files a/build/cli_apps/license.o and b/build/cli_apps/license.o differ diff --git a/build/cli_apps/man.o b/build/cli_apps/man.o index 3470d6e..0affd3d 100644 Binary files a/build/cli_apps/man.o and b/build/cli_apps/man.o differ diff --git a/build/cli_apps/math.o b/build/cli_apps/math.o index b0e6922..368dc97 100644 Binary files a/build/cli_apps/math.o and b/build/cli_apps/math.o differ diff --git a/build/cli_apps/memcmd.o b/build/cli_apps/memcmd.o index 254256b..5b519e3 100644 Binary files a/build/cli_apps/memcmd.o and b/build/cli_apps/memcmd.o differ diff --git a/build/cli_apps/meminfo.o b/build/cli_apps/meminfo.o index b639223..4e8abd6 100644 Binary files a/build/cli_apps/meminfo.o and b/build/cli_apps/meminfo.o differ diff --git a/build/cli_apps/net.o b/build/cli_apps/net.o index c49162d..8d73995 100644 Binary files a/build/cli_apps/net.o and b/build/cli_apps/net.o differ diff --git a/build/cli_apps/pci_list.o b/build/cli_apps/pci_list.o index df733a7..c763bf7 100644 Binary files a/build/cli_apps/pci_list.o and b/build/cli_apps/pci_list.o differ diff --git a/build/cli_apps/readtheman.o b/build/cli_apps/readtheman.o index 320bef1..4756727 100644 Binary files a/build/cli_apps/readtheman.o and b/build/cli_apps/readtheman.o differ diff --git a/build/cli_apps/reboot.o b/build/cli_apps/reboot.o index 2734c35..30f00d2 100644 Binary files a/build/cli_apps/reboot.o and b/build/cli_apps/reboot.o differ diff --git a/build/cli_apps/shutdown.o b/build/cli_apps/shutdown.o index 16fa3ce..816d506 100644 Binary files a/build/cli_apps/shutdown.o and b/build/cli_apps/shutdown.o differ diff --git a/build/cli_apps/txtedit.o b/build/cli_apps/txtedit.o index b3ad107..90dc5e0 100644 Binary files a/build/cli_apps/txtedit.o and b/build/cli_apps/txtedit.o differ diff --git a/build/cli_apps/uptime.o b/build/cli_apps/uptime.o index e155568..f31e93b 100644 Binary files a/build/cli_apps/uptime.o and b/build/cli_apps/uptime.o differ diff --git a/build/cmd.o b/build/cmd.o index ae4a6d9..10e8bff 100644 Binary files a/build/cmd.o and b/build/cmd.o differ diff --git a/build/control_panel.o b/build/control_panel.o index b33233f..c73de00 100644 Binary files a/build/control_panel.o and b/build/control_panel.o differ diff --git a/build/dns.o b/build/dns.o index fccdc31..367ccb7 100644 Binary files a/build/dns.o and b/build/dns.o differ diff --git a/build/e1000.o b/build/e1000.o index efe2821..d07f92f 100644 Binary files a/build/e1000.o and b/build/e1000.o differ diff --git a/build/editor.o b/build/editor.o index b2b2b1f..8c56a10 100644 Binary files a/build/editor.o and b/build/editor.o differ diff --git a/build/explorer.o b/build/explorer.o index 7b522d1..8546b00 100644 Binary files a/build/explorer.o and b/build/explorer.o differ diff --git a/build/fat32.o b/build/fat32.o index 9b608cc..0b853e1 100644 Binary files a/build/fat32.o and b/build/fat32.o differ diff --git a/build/graphics.o b/build/graphics.o index e063cc7..1da4256 100644 Binary files a/build/graphics.o and b/build/graphics.o differ diff --git a/build/http.o b/build/http.o index b6d5e81..c377d61 100644 Binary files a/build/http.o and b/build/http.o differ diff --git a/build/icmp.o b/build/icmp.o index 0f25c43..365e546 100644 Binary files a/build/icmp.o and b/build/icmp.o differ diff --git a/build/idt.o b/build/idt.o index 4f13af2..f4bec80 100644 Binary files a/build/idt.o and b/build/idt.o differ diff --git a/build/licensewr.o b/build/licensewr.o index 48e9b82..b731e0d 100644 Binary files a/build/licensewr.o and b/build/licensewr.o differ diff --git a/build/main.o b/build/main.o index 3c87b9b..115c4f8 100644 Binary files a/build/main.o and b/build/main.o differ diff --git a/build/markdown.o b/build/markdown.o index 21e749e..492a232 100644 Binary files a/build/markdown.o and b/build/markdown.o differ diff --git a/build/memory_manager.o b/build/memory_manager.o index efb2b26..717c032 100644 Binary files a/build/memory_manager.o and b/build/memory_manager.o differ diff --git a/build/minesweeper.o b/build/minesweeper.o index afcb07c..d07457d 100644 Binary files a/build/minesweeper.o and b/build/minesweeper.o differ diff --git a/build/network.o b/build/network.o index b42e845..8b348ba 100644 Binary files a/build/network.o and b/build/network.o differ diff --git a/build/notepad.o b/build/notepad.o index 30d99db..c5cb440 100644 Binary files a/build/notepad.o and b/build/notepad.o differ diff --git a/build/pci.o b/build/pci.o index 284b0aa..804b336 100644 Binary files a/build/pci.o and b/build/pci.o differ diff --git a/build/platform.o b/build/platform.o index e63f482..c4e7752 100644 Binary files a/build/platform.o and b/build/platform.o differ diff --git a/build/ps2.o b/build/ps2.o index f2832c9..c6ed361 100644 Binary files a/build/ps2.o and b/build/ps2.o differ diff --git a/build/rtc.o b/build/rtc.o index 88921b4..685e094 100644 Binary files a/build/rtc.o and b/build/rtc.o differ diff --git a/build/tcp.o b/build/tcp.o index 5509d88..e1f91d5 100644 Binary files a/build/tcp.o and b/build/tcp.o differ diff --git a/build/vm.o b/build/vm.o index 7a4f913..627cbba 100644 Binary files a/build/vm.o and b/build/vm.o differ diff --git a/build/wm.o b/build/wm.o index 84b9702..8a60e98 100644 Binary files a/build/wm.o and b/build/wm.o differ diff --git a/iso_root/brewos.elf b/iso_root/brewos.elf index c33e912..fbc1d89 100755 Binary files a/iso_root/brewos.elf and b/iso_root/brewos.elf differ diff --git a/src/kernel/about.c b/src/kernel/about.c index a3c3a3e..fecd481 100644 --- a/src/kernel/about.c +++ b/src/kernel/about.c @@ -35,8 +35,8 @@ static void about_paint(Window *win) { // Version info draw_string(offset_x, offset_y + 105, "BrewOS", COLOR_BLACK); - draw_string(offset_x, offset_y + 120, "BrewOS Version 1.30", COLOR_BLACK); - draw_string(offset_x, offset_y + 135, "Kernel Version 2.3.0", COLOR_BLACK); + draw_string(offset_x, offset_y + 120, "BrewOS Version 1.40", COLOR_BLACK); + draw_string(offset_x, offset_y + 135, "Kernel Version 2.4.0", COLOR_BLACK); // Copyright draw_string(offset_x, offset_y + 150, "(C) 2026 boreddevnl.", COLOR_BLACK); diff --git a/src/kernel/ata.c b/src/kernel/ata.c new file mode 100644 index 0000000..298f4e3 --- /dev/null +++ b/src/kernel/ata.c @@ -0,0 +1,124 @@ +#include "ata.h" + +#define ATA_DATA 0x1F0 +#define ATA_ERROR 0x1F1 +#define ATA_SEC_CNT 0x1F2 +#define ATA_LBA_LO 0x1F3 +#define ATA_LBA_MID 0x1F4 +#define ATA_LBA_HI 0x1F5 +#define ATA_DRIVE_HEAD 0x1F6 +#define ATA_STATUS 0x1F7 +#define ATA_CMD 0x1F7 + +#define CMD_READ_PIO 0x20 +#define CMD_WRITE_PIO 0x30 +#define CMD_IDENTIFY 0xEC + +// IO Port Helpers +static inline void outb(uint16_t port, uint8_t val) { + __asm__ volatile ("outb %0, %1" : : "a"(val), "Nd"(port)); +} + +static inline uint8_t inb(uint16_t port) { + uint8_t ret; + __asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port)); + return ret; +} + +static inline void insw(uint16_t port, void *addr, uint32_t cnt) { + __asm__ volatile ("rep insw" : "+D"(addr), "+c"(cnt) : "d"(port) : "memory"); +} + +static inline void outsw(uint16_t port, const void *addr, uint32_t cnt) { + __asm__ volatile ("rep outsw" : "+S"(addr), "+c"(cnt) : "d"(port) : "memory"); +} + +static void ata_wait_bsy(void) { + while (inb(ATA_STATUS) & 0x80); +} + +static void ata_wait_drq(void) { + while (!(inb(ATA_STATUS) & 0x08)); +} + +// Select drive (0 = Master, 1 = Slave) +static void ata_select_drive(int drive) { + outb(ATA_DRIVE_HEAD, 0xE0 | (drive << 4)); + // Small delay + inb(ATA_STATUS); + inb(ATA_STATUS); + inb(ATA_STATUS); + inb(ATA_STATUS); +} + +bool ata_init(void) { + // Simple probe: Try to identify Primary Slave (where we put disk.img in QEMU index=1) + // Note: QEMU index=0 is usually the CDROM if -boot d is used, or Primary Master. + // We will try to read from the drive to verify it exists. + ata_select_drive(1); // Select Slave + outb(ATA_SEC_CNT, 0); + outb(ATA_LBA_LO, 0); + outb(ATA_LBA_MID, 0); + outb(ATA_LBA_HI, 0); + outb(ATA_CMD, CMD_IDENTIFY); + + uint8_t status = inb(ATA_STATUS); + if (status == 0) return false; // No drive + + // Poll until BSY clears + while(inb(ATA_STATUS) & 0x80); + + // Check for ATAPI signature (we don't want the CDROM) + if (inb(ATA_LBA_MID) != 0 || inb(ATA_LBA_HI) != 0) { + return false; // Not ATA + } + + // Read 256 words of identify data to clear buffer + uint16_t buffer[256]; + ata_wait_drq(); + insw(ATA_DATA, buffer, 256); + + return true; +} + +int ata_read_sectors(uint32_t lba, uint8_t count, void* buffer) { + ata_wait_bsy(); + ata_select_drive(1); // Always using Slave for storage + + outb(ATA_SEC_CNT, count); + outb(ATA_LBA_LO, (uint8_t)lba); + outb(ATA_LBA_MID, (uint8_t)(lba >> 8)); + outb(ATA_LBA_HI, (uint8_t)(lba >> 16)); + outb(ATA_DRIVE_HEAD, 0xE0 | (1 << 4) | ((lba >> 24) & 0x0F)); + outb(ATA_CMD, CMD_READ_PIO); + + uint16_t *buf = (uint16_t*)buffer; + for (int i = 0; i < count; i++) { + ata_wait_bsy(); + ata_wait_drq(); + insw(ATA_DATA, buf, 256); + buf += 256; + } + return 0; +} + +int ata_write_sectors(uint32_t lba, uint8_t count, const void* buffer) { + ata_wait_bsy(); + ata_select_drive(1); + + outb(ATA_SEC_CNT, count); + outb(ATA_LBA_LO, (uint8_t)lba); + outb(ATA_LBA_MID, (uint8_t)(lba >> 8)); + outb(ATA_LBA_HI, (uint8_t)(lba >> 16)); + outb(ATA_DRIVE_HEAD, 0xE0 | (1 << 4) | ((lba >> 24) & 0x0F)); + outb(ATA_CMD, CMD_WRITE_PIO); + + const uint16_t *buf = (const uint16_t*)buffer; + for (int i = 0; i < count; i++) { + ata_wait_bsy(); + ata_wait_drq(); + outsw(ATA_DATA, buf, 256); + buf += 256; + } + return 0; +} \ No newline at end of file diff --git a/src/kernel/ata.h b/src/kernel/ata.h new file mode 100644 index 0000000..d8ff49b --- /dev/null +++ b/src/kernel/ata.h @@ -0,0 +1,11 @@ +#pragma once +#include +#include + +// Initialize ATA driver and find the FAT32 disk +bool ata_init(void); + +// Read/Write sectors (LBA28) +// Returns 0 on success, non-zero on error +int ata_read_sectors(uint32_t lba, uint8_t count, void* buffer); +int ata_write_sectors(uint32_t lba, uint8_t count, const void* buffer); \ No newline at end of file diff --git a/src/kernel/cli_apps/about.c b/src/kernel/cli_apps/about.c index 93df31c..60c73cb 100644 --- a/src/kernel/cli_apps/about.c +++ b/src/kernel/cli_apps/about.c @@ -2,6 +2,6 @@ void cli_cmd_brewver(char *args) { (void)args; - cli_write("BrewOS v1.30 Alpha\n"); - cli_write("BrewOS Kernel V2.3.0 Pre-Alpha\n"); + cli_write("BrewOS v1.40 Alpha\n"); + cli_write("BrewOS Kernel V2.4.0 Pre-Alpha\n"); } diff --git a/src/kernel/cli_apps/cc.c b/src/kernel/cli_apps/cc.c index 2cbcf31..e66e770 100644 --- a/src/kernel/cli_apps/cc.c +++ b/src/kernel/cli_apps/cc.c @@ -618,11 +618,21 @@ static void program() { } void cli_cmd_cc(char *args) { + // Skip leading whitespace + while (args && *args == ' ') args++; + if (!args || !*args) { cmd_write("Usage: cc \n"); return; } + // Trim trailing whitespace + int len = cli_strlen(args); + while (len > 0 && (args[len-1] == ' ' || args[len-1] == '\t' || args[len-1] == '\n')) { + args[len-1] = 0; + len--; + } + FAT32_FileHandle *fh = fat32_open(args, "r"); if (!fh) { cmd_write("Error: Cannot open source file.\n"); @@ -636,8 +646,8 @@ void cli_cmd_cc(char *args) { return; } - int len = fat32_read(fh, source, MAX_SOURCE - 1); - source[len] = 0; + int read_len = fat32_read(fh, source, MAX_SOURCE - 1); + source[read_len] = 0; fat32_close(fh); lexer(source); diff --git a/src/kernel/cli_apps/fs_commands.c b/src/kernel/cli_apps/fs_commands.c index f8bc464..0860b6a 100644 --- a/src/kernel/cli_apps/fs_commands.c +++ b/src/kernel/cli_apps/fs_commands.c @@ -210,6 +210,9 @@ void cli_cmd_echo(char *args) { } void cli_cmd_cat(char *args) { + // Skip leading whitespace + while (args && *args == ' ') args++; + if (!args || args[0] == 0) { cli_write("Usage: cat \n"); return; @@ -253,7 +256,7 @@ void cli_cmd_touch(char *args) { char filename[256]; int i = 0; - while (args[i] && args[i] != ' ' && args[i] != '\t') { + while (args[i] && args[i] != ' ' && args[i] != '\t' && args[i] != '\n') { filename[i] = args[i]; i++; } diff --git a/src/kernel/cmd.c b/src/kernel/cmd.c index 511fbf3..faf7cfb 100644 --- a/src/kernel/cmd.c +++ b/src/kernel/cmd.c @@ -328,6 +328,45 @@ void pager_set_mode(void) { current_mode = MODE_PAGER; } +// Internal LS command to avoid stack overflow in external module +static void internal_cmd_ls(char *args) { + char path[256]; + if (args && *args) { + int i=0; + while(args[i] && i < 255) { path[i] = args[i]; i++; } + path[i] = 0; + } else { + path[0] = '.'; path[1] = 0; + } + + int max_files = 64; + FAT32_FileInfo *files = (FAT32_FileInfo*)kmalloc(max_files * sizeof(FAT32_FileInfo)); + if (!files) { + cmd_write("Error: Out of memory\n"); + return; + } + + int count = fat32_list_directory(path, files, max_files); + + for (int i = 0; i < count; i++) { + if (files[i].is_directory) { + cmd_write("[DIR] "); + } else { + cmd_write("[FILE] "); + } + + cmd_write(files[i].name); + if (!files[i].is_directory) { + cmd_write(" "); + cmd_write_int(files[i].size); + cmd_write("b"); + } + cmd_write("\n"); + } + + kfree(files); +} + // --- Commands (now delegated to cli_apps/) --- // Command dispatch table @@ -374,8 +413,8 @@ static const CommandEntry commands[] = { {"cd", cli_cmd_cd}, {"PWD", cli_cmd_pwd}, {"pwd", cli_cmd_pwd}, - {"LS", cli_cmd_ls}, - {"ls", cli_cmd_ls}, + {"LS", internal_cmd_ls}, + {"ls", internal_cmd_ls}, {"MKDIR", cli_cmd_mkdir}, {"mkdir", cli_cmd_mkdir}, {"RM", cli_cmd_rm}, @@ -895,12 +934,14 @@ void cmd_reset(void) { } static void create_test_files(void) { - fat32_mkdir("Documents"); - fat32_mkdir("Projects"); - fat32_mkdir("Documents/Important"); - fat32_mkdir("Apps"); + if (!fat32_exists("Documents")) fat32_mkdir("Documents"); + if (!fat32_exists("Projects")) fat32_mkdir("Projects"); + if (!fat32_exists("Documents/Important")) fat32_mkdir("Documents/Important"); + if (!fat32_exists("Apps")) fat32_mkdir("Apps"); - FAT32_FileHandle *fh = fat32_open("README.md", "w"); + FAT32_FileHandle *fh; + // Always try to write README to ensure content exists (fixes empty file issue) + fh = fat32_open("README.md", "w"); if (fh) { const char *content = "# Brew OS 1.01 Alpha\n\n" @@ -992,8 +1033,8 @@ static void create_test_files(void) { "The above attribution requirements are informational and intended to\n" "ensure proper credit is given. They do not alter or supersede the\n" "terms of the GNU General Public License (GPL), which governs this work.\n"; - fat32_write(fh, (void *)content, cmd_strlen(content)); - fat32_close(fh); + fat32_write(fh, (void *)content, cmd_strlen(content)); + fat32_close(fh); } fh = fat32_open("Apps/README.md", "w"); @@ -1001,7 +1042,7 @@ static void create_test_files(void) { const char *content = "# All compiled C files in this directory are openable from any other directory by typing in the name of the compiled file by typing in the name of the compiled file.\n\n" "The c file 'wordofgod.c' contains a C program similar to one in TempleOS, which Terry A. Davis (RIP) saw as 'words from god' telling him what to do with his kernel.\n" - "I made this file as a tribute to him, as he also inspired me to create this project in '24. If you want to run it you simply do cc (or compc) wordofgod.c and then run ./wordofgod \n"; + "I made this file as a tribute to him, as he also inspired me to create this project in '24. If you want to run it you simply do cc (or compc) wordgod.c and then run ./wordgod \n"; fat32_write(fh, (void *)content, cmd_strlen(content)); fat32_close(fh); } @@ -1024,74 +1065,60 @@ static void create_test_files(void) { fh = fat32_open("Apps/wordofgod.c", "w"); if (fh) { - char *h = "int main(){int l;l=malloc(1200);"; - fat32_write(fh, h, cmd_strlen(h)); - char *w1 = "poke(l+0,\"In \");poke(l+4,\"the \");poke(l+8,\"beginning \");poke(l+12,\"God \");poke(l+16,\"created \");poke(l+20,\"heaven \");poke(l+24,\"and \");poke(l+28,\"earth \");poke(l+32,\"light \");poke(l+36,\"darkness \");"; - fat32_write(fh, w1, cmd_strlen(w1)); - char *w2 = "poke(l+40,\"day \");poke(l+44,\"night \");poke(l+48,\"waters \");poke(l+52,\"firmament \");poke(l+56,\"evening \");poke(l+60,\"morning \");poke(l+64,\"land \");poke(l+68,\"seas \");poke(l+72,\"grass \");poke(l+76,\"herb \");"; - fat32_write(fh, w2, cmd_strlen(w2)); - char *w3 = "poke(l+80,\"seed \");poke(l+84,\"fruit \");poke(l+88,\"tree \");poke(l+92,\"sun \");poke(l+96,\"moon \");poke(l+100,\"stars \");poke(l+104,\"signs \");poke(l+108,\"seasons \");poke(l+112,\"days \");poke(l+116,\"years \");"; - fat32_write(fh, w3, cmd_strlen(w3)); - char *w4 = "poke(l+120,\"creature \");poke(l+124,\"life \");poke(l+128,\"fowl \");poke(l+132,\"whales \");poke(l+136,\"cattle \");poke(l+140,\"creeping \");poke(l+144,\"beast \");poke(l+148,\"man \");poke(l+152,\"image \");poke(l+156,\"likeness \");"; - fat32_write(fh, w4, cmd_strlen(w4)); - char *w5 = "poke(l+160,\"dominion \");poke(l+164,\"fish \");poke(l+168,\"air \");poke(l+172,\"every \");poke(l+176,\"CIA \");poke(l+180,\"meat \");poke(l+184,\"holy \");poke(l+188,\"rest \");poke(l+192,\"dust \");poke(l+196,\"breath \");"; - fat32_write(fh, w5, cmd_strlen(w5)); - char *w6 = "poke(l+200,\"soul \");poke(l+204,\"garden \");poke(l+208,\"east \");poke(l+212,\"Eden \");poke(l+216,\"ground \");poke(l+220,\"sight \");poke(l+224,\"good \");poke(l+228,\"evil \");poke(l+232,\"river \");poke(l+236,\"gold \");"; - fat32_write(fh, w6, cmd_strlen(w6)); - char *w7 = "poke(l+240,\"stone \");poke(l+244,\"woman \");poke(l+248,\"wife \");poke(l+252,\"flesh \");poke(l+256,\"bone \");poke(l+260,\"naked \");poke(l+264,\"serpent \");poke(l+268,\"subtle \");poke(l+272,\"eat \");poke(l+276,\"eyes \");"; - fat32_write(fh, w7, cmd_strlen(w7)); - char *w8 = "poke(l+280,\"wise \");poke(l+284,\"cool \");poke(l+288,\"voice \");poke(l+292,\"fear \");poke(l+296,\"hid \");poke(l+300,\"cursed \");poke(l+304,\"belly \");poke(l+308,\"enmity \");poke(l+312,\"sorrow \");poke(l+316,\"conception \");"; - fat32_write(fh, w8, cmd_strlen(w8)); - char *w9 = "poke(l+320,\"children \");poke(l+324,\"desire \");poke(l+328,\"husband \");poke(l+332,\"thorns \");poke(l+336,\"thistles \");poke(l+340,\"sweat \");poke(l+344,\"bread \");poke(l+348,\"mother \");poke(l+352,\"skin \");poke(l+356,\"coats \");"; - fat32_write(fh, w9, cmd_strlen(w9)); - char *w10 = "poke(l+360,\"cherubims \");poke(l+364,\"sword \");poke(l+368,\"gate \");poke(l+372,\"offering \");poke(l+376,\"respect \");poke(l+380,\"sin \");poke(l+384,\"door \");poke(l+388,\"blood \");poke(l+392,\"brother \");poke(l+396,\"keeper \");"; - fat32_write(fh, w10, cmd_strlen(w10)); - char *w11 = "poke(l+400,\"voice \");poke(l+404,\"heard \");poke(l+408,\"walking \");poke(l+412,\"cool \");poke(l+416,\"day \");poke(l+420,\"where \");poke(l+424,\"art \");poke(l+428,\"thou \");poke(l+432,\"told \");poke(l+436,\"thee \");"; - fat32_write(fh, w11, cmd_strlen(w11)); - char *w12 = "poke(l+440,\"hast \");poke(l+444,\"eaten \");poke(l+448,\"tree \");poke(l+452,\"whereof \");poke(l+456,\"commanded \");poke(l+460,\"shouldest \");poke(l+464,\"not \");poke(l+468,\"eat \");poke(l+472,\"gave \");poke(l+476,\"me \");"; - fat32_write(fh, w12, cmd_strlen(w12)); - char *w13 = "poke(l+480,\"beguiled \");poke(l+484,\"belly \");poke(l+488,\"go \");poke(l+492,\"dust \");poke(l+496,\"shalt \");poke(l+500,\"eat \");poke(l+504,\"days \");poke(l+508,\"life \");poke(l+512,\"put \");poke(l+516,\"enmity \");"; - fat32_write(fh, w13, cmd_strlen(w13)); - char *w14 = "poke(l+520,\"between \");poke(l+524,\"seed \");poke(l+528,\"bruise \");poke(l+532,\"head \");poke(l+536,\"heel \");poke(l+540,\"multiply \");poke(l+544,\"sorrow \");poke(l+548,\"conception \");poke(l+552,\"forth \");poke(l+556,\"children \");"; - fat32_write(fh, w14, cmd_strlen(w14)); - char *w15 = "poke(l+560,\"desire \");poke(l+564,\"rule \");poke(l+568,\"over \");poke(l+572,\"sake \");poke(l+576,\"sweat \");poke(l+580,\"face \");poke(l+584,\"till \");poke(l+588,\"return \");poke(l+592,\"ground \");poke(l+596,\"taken \");"; - fat32_write(fh, w15, cmd_strlen(w15)); - char *w16 = "poke(l+600,\"mother \");poke(l+604,\"living \");poke(l+608,\"coats \");poke(l+612,\"skins \");poke(l+616,\"clothed \");poke(l+620,\"become \");poke(l+624,\"one \");poke(l+628,\"us \");poke(l+632,\"know \");poke(l+636,\"good \");"; - fat32_write(fh, w16, cmd_strlen(w16)); - char *w17 = "poke(l+640,\"evil \");poke(l+644,\"lest \");poke(l+648,\"put \");poke(l+652,\"hand \");poke(l+656,\"take \");poke(l+660,\"live \");poke(l+664,\"ever \");poke(l+668,\"sent \");poke(l+672,\"garden \");poke(l+676,\"eden \");"; - fat32_write(fh, w17, cmd_strlen(w17)); - char *w18 = "poke(l+680,\"flaming \");poke(l+684,\"sword \");poke(l+688,\"turned \");poke(l+692,\"way \");poke(l+696,\"knew \");poke(l+700,\"conceived \");poke(l+704,\"bare \");poke(l+708,\"cain \");poke(l+712,\"said \");poke(l+716,\"gotten \");"; - fat32_write(fh, w18, cmd_strlen(w18)); - char *w19 = "poke(l+720,\"lord \");poke(l+724,\"again \");poke(l+728,\"abel \");poke(l+732,\"sheep \");poke(l+736,\"tiller \");poke(l+740,\"process \");poke(l+744,\"time \");poke(l+748,\"pass \");poke(l+752,\"brought \");poke(l+756,\"fruit \");"; - fat32_write(fh, w19, cmd_strlen(w19)); - char *w20 = "poke(l+760,\"offering \");poke(l+764,\"firstlings \");poke(l+768,\"flock \");poke(l+772,\"fat \");poke(l+776,\"thereof \");poke(l+780,\"respect \");poke(l+784,\"wroth \");poke(l+788,\"countenance \");poke(l+792,\"fallen \");poke(l+796,\"well \");"; - fat32_write(fh, w20, cmd_strlen(w20)); - char *w21 = "poke(l+800,\"accepted \");poke(l+804,\"not \");poke(l+808,\"sin \");poke(l+812,\"lieth \");poke(l+816,\"door \");poke(l+820,\"unto \");poke(l+824,\"rule \");poke(l+828,\"talked \");poke(l+832,\"field \");poke(l+836,\"rose \");"; - fat32_write(fh, w21, cmd_strlen(w21)); - char *w22 = "poke(l+840,\"slew \");poke(l+844,\"done \");poke(l+848,\"crieth \");poke(l+852,\"mouth \");poke(l+856,\"receive \");poke(l+860,\"strength \");poke(l+864,\"fugitive \");poke(l+868,\"vagabond \");poke(l+872,\"punishment \");poke(l+876,\"greater \");"; - fat32_write(fh, w22, cmd_strlen(w22)); - char *w23 = "poke(l+880,\"bear \");poke(l+884,\"driven \");poke(l+888,\"hid \");poke(l+892,\"findeth \");poke(l+896,\"slay \");poke(l+900,\"vengeance \");poke(l+904,\"sevenfold \");poke(l+908,\"mark \");poke(l+912,\"finding \");poke(l+916,\"kill \");"; - fat32_write(fh, w23, cmd_strlen(w23)); - char *w24 = "poke(l+920,\"presence \");poke(l+924,\"dwelt \");poke(l+928,\"nod \");poke(l+932,\"enoch \");poke(l+936,\"city \");poke(l+940,\"irad \");poke(l+944,\"mehujael \");poke(l+948,\"methusael \");poke(l+952,\"lamech \");poke(l+956,\"adah \");"; - fat32_write(fh, w24, cmd_strlen(w24)); - char *w25 = "poke(l+960,\"zillah \");poke(l+964,\"jabal \");poke(l+968,\"tent \");poke(l+972,\"cattle \");poke(l+976,\"jubal \");poke(l+980,\"harp \");poke(l+984,\"organ \");poke(l+988,\"tubalcain \");poke(l+992,\"brass \");poke(l+996,\"iron \");"; - fat32_write(fh, w25, cmd_strlen(w25)); - char *w26 = "poke(l+1000,\"naamah \");poke(l+1004,\"wives \");poke(l+1008,\"hear \");poke(l+1012,\"speech \");poke(l+1016,\"hearken \");poke(l+1020,\"young \");poke(l+1024,\"hurt \");poke(l+1028,\"wounding \");poke(l+1032,\"avenged \");poke(l+1036,\"seventy \");"; - fat32_write(fh, w26, cmd_strlen(w26)); - char *w27 = "poke(l+1040,\"seth \");poke(l+1044,\"appointed \");poke(l+1048,\"enos \");poke(l+1052,\"began \");poke(l+1056,\"call \");poke(l+1060,\"name \");poke(l+1064,\"generations \");poke(l+1068,\"adam \");poke(l+1072,\"likeness \");poke(l+1076,\"blessed \");"; - fat32_write(fh, w27, cmd_strlen(w27)); - char *w28 = "poke(l+1080,\"begat \");poke(l+1084,\"sons \");poke(l+1088,\"daughters \");poke(l+1092,\"lived \");poke(l+1096,\"died \");poke(l+1100,\"cainan \");poke(l+1104,\"mahalaleel \");poke(l+1108,\"jared \");poke(l+1112,\"walked \");poke(l+1116,\"three \");"; - fat32_write(fh, w28, cmd_strlen(w28)); - char *w29 = "poke(l+1120,\"hundred \");poke(l+1124,\"sixty \");poke(l+1128,\"five \");poke(l+1132,\"methuselah \");poke(l+1136,\"lamech \");poke(l+1140,\"noah \");poke(l+1144,\"comfort \");poke(l+1148,\"work \");poke(l+1152,\"toil \");poke(l+1156,\"hands \");"; - fat32_write(fh, w29, cmd_strlen(w29)); - char *w30 = "poke(l+1160,\"shem \");poke(l+1164,\"ham \");poke(l+1168,\"japheth \");poke(l+1172,\"men \");poke(l+1176,\"daughters \");poke(l+1180,\"born \");poke(l+1184,\"fair \");poke(l+1188,\"chose \");poke(l+1192,\"spirit \");poke(l+1196,\"strive \");"; - fat32_write(fh, w30, cmd_strlen(w30)); - char *e = "int c;int r;r=abs(rand());r=r-(r/5)*5;c=14+r;int i;i=0;while(i @@ -278,7 +279,9 @@ static void explorer_set_folder_color(const char *folder_path, uint32_t color) { static void explorer_load_directory(const char *path) { explorer_strcpy(current_path, path); - FAT32_FileInfo entries[EXPLORER_MAX_FILES]; + FAT32_FileInfo *entries = (FAT32_FileInfo*)kmalloc(EXPLORER_MAX_FILES * sizeof(FAT32_FileInfo)); + if (!entries) return; + int count = fat32_list_directory(path, entries, EXPLORER_MAX_FILES); item_count = 0; @@ -306,6 +309,7 @@ static void explorer_load_directory(const char *path) { item_count++; } + kfree(entries); selected_item = -1; } diff --git a/src/kernel/fat32.c b/src/kernel/fat32.c index 2b5c6f9..6b752a2 100644 --- a/src/kernel/fat32.c +++ b/src/kernel/fat32.c @@ -1,37 +1,28 @@ #include "fat32.h" +#include "ata.h" +#include "memory_manager.h" #include #include +#include -// === Memory-based FAT32 Implementation === -// This is a simplified FAT32 for the OS kernel -// It allocates everything in RAM instead of using a real disk +// === Disk-based FAT32 Implementation === -#define MAX_FILES 256 -#define MAX_CLUSTERS 1024 #define MAX_OPEN_HANDLES 32 +#define SECTOR_SIZE 512 -// In-memory FAT table -static uint32_t fat_table[MAX_CLUSTERS]; -static uint8_t cluster_data[MAX_CLUSTERS][FAT32_CLUSTER_SIZE]; +// Globals +static uint32_t fat_start_lba; +static uint32_t data_start_lba; +static uint32_t sectors_per_cluster; +static uint32_t sectors_per_fat; +static uint32_t root_cluster; +static uint32_t bytes_per_cluster; +static uint32_t total_clusters; -// File/Directory tracking -typedef struct { - char full_path[FAT32_MAX_PATH]; - char filename[FAT32_MAX_FILENAME]; - uint32_t start_cluster; - uint32_t size; - uint32_t attributes; - bool used; - char parent_path[FAT32_MAX_PATH]; -} FileEntry; - -static FileEntry files[MAX_FILES]; -static uint32_t next_cluster = 3; // Start after reserved clusters 0, 1, 2 static FAT32_FileHandle open_handles[MAX_OPEN_HANDLES]; static char current_dir[FAT32_MAX_PATH] = "/"; // === Helper Functions === - static size_t fs_strlen(const char *str) { size_t len = 0; while (str[len]) len++; @@ -51,61 +42,44 @@ static int fs_strcmp(const char *s1, const char *s2) { return *(const unsigned char*)s1 - *(const unsigned char*)s2; } +static int fs_toupper(char c) { + if (c >= 'a' && c <= 'z') return c - 32; + return c; +} + +static int fs_strcasecmp(const char *s1, const char *s2) { + while (*s1 && fs_toupper(*s1) == fs_toupper(*s2)) { + s1++; + s2++; + } + return (unsigned char)fs_toupper(*s1) - (unsigned char)fs_toupper(*s2); +} + +static void fs_memset(void *dest, int val, size_t len) { + uint8_t *ptr = (uint8_t*)dest; + while (len--) *ptr++ = (uint8_t)val; +} + +static void fs_memcpy(void *dest, const void *src, size_t len) { + uint8_t *d = (uint8_t*)dest; + const uint8_t *s = (const uint8_t*)src; + while (len--) *d++ = *s++; +} + +static int fs_memcmp(const void *ptr1, const void *ptr2, size_t num) { + const unsigned char *p1 = (const unsigned char *)ptr1; + const unsigned char *p2 = (const unsigned char *)ptr2; + for (size_t i = 0; i < num; i++) { + if (p1[i] != p2[i]) return p1[i] - p2[i]; + } + return 0; +} + static void fs_strcat(char *dest, const char *src) { while (*dest) dest++; fs_strcpy(dest, src); } -static bool fs_ends_with(const char *str, const char *suffix) { - int str_len = fs_strlen(str); - int suffix_len = fs_strlen(suffix); - if (suffix_len > str_len) return false; - return fs_strcmp(str + str_len - suffix_len, suffix) == 0; -} - -// Extract filename from path -static void extract_filename(const char *path, char *filename) { - int len = fs_strlen(path); - int i = len - 1; - - // Skip trailing slashes - while (i > 0 && path[i] == '/') i--; - - // Find last slash - int start = i; - while (start >= 0 && path[start] != '/') start--; - start++; - - // Copy filename - int j = 0; - for (int k = start; k <= i; k++) { - filename[j++] = path[k]; - } - filename[j] = 0; -} - -// Extract parent path -static void extract_parent_path(const char *path, char *parent) { - int len = fs_strlen(path); - int i = len - 1; - - // Skip trailing slashes - while (i > 0 && path[i] == '/') i--; - - // Find last slash - while (i > 0 && path[i] != '/') i--; - - if (i == 0) { - parent[0] = '/'; - parent[1] = 0; - } else { - for (int j = 0; j < i; j++) { - parent[j] = path[j]; - } - parent[i] = 0; - } -} - // Normalize path (remove .., ., etc) void fat32_normalize_path(const char *path, char *normalized) { char temp[FAT32_MAX_PATH]; @@ -167,29 +141,6 @@ void fat32_normalize_path(const char *path, char *normalized) { fs_strcpy(normalized, temp); } -// Find file entry by path -static FileEntry* find_file(const char *path) { - char normalized[FAT32_MAX_PATH]; - fat32_normalize_path(path, normalized); - - for (int i = 0; i < MAX_FILES; i++) { - if (files[i].used && fs_strcmp(files[i].full_path, normalized) == 0) { - return &files[i]; - } - } - return NULL; -} - -// Find first unused file entry -static FileEntry* find_free_entry(void) { - for (int i = 0; i < MAX_FILES; i++) { - if (!files[i].used) { - return &files[i]; - } - } - return NULL; -} - // Find free handle static FAT32_FileHandle* find_free_handle(void) { for (int i = 0; i < MAX_OPEN_HANDLES; i++) { @@ -200,101 +151,615 @@ static FAT32_FileHandle* find_free_handle(void) { return NULL; } -// Allocate cluster -static uint32_t allocate_cluster(void) { - if (next_cluster >= MAX_CLUSTERS) return 0; - uint32_t cluster = next_cluster++; - fat_table[cluster] = 0xFFFFFFFF; // End of chain - return cluster; +// === Disk Access Helpers === + +static uint32_t cluster_to_lba(uint32_t cluster) { + return data_start_lba + (cluster - 2) * sectors_per_cluster; +} + +static uint32_t get_fat_entry(uint32_t cluster) { + uint32_t sector = fat_start_lba + (cluster * 4) / SECTOR_SIZE; + uint32_t offset = (cluster * 4) % SECTOR_SIZE; + + uint8_t buf[SECTOR_SIZE]; + ata_read_sectors(sector, 1, buf); + + uint32_t entry = *(uint32_t*)&buf[offset]; + return entry & 0x0FFFFFFF; +} + +static void set_fat_entry(uint32_t cluster, uint32_t value) { + uint32_t sector = fat_start_lba + (cluster * 4) / SECTOR_SIZE; + uint32_t offset = (cluster * 4) % SECTOR_SIZE; + + uint8_t buf[SECTOR_SIZE]; + ata_read_sectors(sector, 1, buf); + + *(uint32_t*)&buf[offset] = (value & 0x0FFFFFFF); + + // Write to FAT1 + ata_write_sectors(sector, 1, buf); + + // Write to FAT2 (Mirroring) + ata_write_sectors(sector + sectors_per_fat, 1, buf); +} + +static uint32_t find_free_cluster(void) { + // Start searching from cluster 3 (2 is root usually) + // Simple linear search + for (uint32_t i = 3; i < total_clusters; i++) { + if (get_fat_entry(i) == 0) { + return i; + } + } + return 0; // Disk full +} + +static void clear_cluster(uint32_t cluster) { + uint8_t zeros[SECTOR_SIZE] = {0}; + uint32_t lba = cluster_to_lba(cluster); + for (uint32_t i = 0; i < sectors_per_cluster; i++) { + ata_write_sectors(lba + i, 1, zeros); + } +} + +// Convert 8.3 name to string +static void fat_name_to_str(const char *name, const char *ext, uint8_t nt_res, char *dest) { + int i, j = 0; + bool name_lower = (nt_res & 0x08) != 0; + bool ext_lower = (nt_res & 0x10) != 0; + + for (i = 0; i < 8 && name[i] != ' '; i++) { + char c = name[i]; + if (name_lower && c >= 'A' && c <= 'Z') c += 32; + dest[j++] = c; + } + if (ext[0] != ' ') { + dest[j++] = '.'; + for (i = 0; i < 3 && ext[i] != ' '; i++) { + char c = ext[i]; + if (ext_lower && c >= 'A' && c <= 'Z') c += 32; + dest[j++] = c; + } + } + dest[j] = 0; +} + +// Convert string to 8.3 name +static void str_to_fat_name(const char *str, char *name, char *ext, uint8_t *nt_res) { + // Pad with spaces + for(int i=0; i<8; i++) name[i] = ' '; + for(int i=0; i<3; i++) ext[i] = ' '; + + *nt_res = 0; + bool all_lower_name = true; + bool all_lower_ext = true; + bool has_ext = false; + + int i = 0; + int j = 0; + + // Copy name + while(str[i] && str[i] != '.' && j < 8) { + char c = str[i++]; + if (c >= 'A' && c <= 'Z') all_lower_name = false; + if (c >= 'a' && c <= 'z') c -= 32; + name[j++] = c; + } + + // Skip to extension + while(str[i] && str[i] != '.') { + if (str[i] >= 'A' && str[i] <= 'Z') all_lower_name = false; + i++; + } + + if(str[i] == '.') { + has_ext = true; + i++; + j = 0; + while(str[i] && j < 3) { + char c = str[i++]; + if (c >= 'A' && c <= 'Z') all_lower_ext = false; + if (c >= 'a' && c <= 'z') c -= 32; + ext[j++] = c; + } + } + + if (all_lower_name) *nt_res |= 0x08; + if (all_lower_ext && has_ext) *nt_res |= 0x10; +} + +typedef struct { + uint8_t order; + uint16_t name1[5]; + uint8_t attr; + uint8_t type; + uint8_t checksum; + uint16_t name2[6]; + uint16_t zero; + uint16_t name3[2]; +} __attribute__((packed)) FAT32_LFNEntry; + +static void extract_lfn_part(FAT32_LFNEntry *lfn, char *buffer) { + int idx = 0; + for(int i=0; i<5; i++) { + uint16_t c = lfn->name1[i]; + if (c == 0) { buffer[idx] = 0; return; } + if (c < 128) buffer[idx++] = (char)c; + else buffer[idx++] = '?'; + } + for(int i=0; i<6; i++) { + uint16_t c = lfn->name2[i]; + if (c == 0) { buffer[idx] = 0; return; } + if (c < 128) buffer[idx++] = (char)c; + else buffer[idx++] = '?'; + } + for(int i=0; i<2; i++) { + uint16_t c = lfn->name3[i]; + if (c == 0) { buffer[idx] = 0; return; } + if (c < 128) buffer[idx++] = (char)c; + else buffer[idx++] = '?'; + } + buffer[idx] = 0; +} + +// Calculate checksum for LFN +static uint8_t lfn_checksum(const uint8_t *short_name) { + uint8_t sum = 0; + for (int i = 0; i < 11; i++) { + sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) + short_name[i]; + } + return sum; +} + +// Check if a character is valid for a strict 8.3 Short File Name +static bool is_valid_sfn_char(char c) { + if (c >= 'A' && c <= 'Z') return true; + if (c >= '0' && c <= '9') return true; + // Standard allowed special chars in SFN + if (c == ' ' || c == '$' || c == '%' || c == '-' || c == '_' || c == '@' || + c == '~' || c == '`' || c == '!' || c == '(' || c == ')' || c == '{' || + c == '}' || c == '^' || c == '#' || c == '&') return true; + return false; +} + +// Determine if a filename requires LFN entries +static bool needs_lfn(const char *name) { + int len = fs_strlen(name); + if (len > 12) return true; // Exceeds 8.3 length + + int dot_pos = -1; + for (int i = 0; i < len; i++) { + if (name[i] == '.') { + if (dot_pos != -1) return true; // More than one dot + dot_pos = i; + } else { + if (!is_valid_sfn_char(name[i])) return true; // Invalid char (including lowercase) + } + } + + if (dot_pos == -1) { + if (len > 8) return true; + } else { + if (dot_pos > 8) return true; // Name part > 8 + if (len - dot_pos - 1 > 3) return true; // Ext part > 3 + } + + return false; +} + +// Check if a specific SFN exists in the directory (to avoid collisions) +static bool sfn_exists(uint32_t dir_cluster, const char *sfn_name, const char *sfn_ext) { + uint32_t current_cluster = dir_cluster; + uint8_t *buf = (uint8_t*)kmalloc(bytes_per_cluster); + if (!buf) return false; + + while (current_cluster >= 2 && current_cluster < 0x0FFFFFF8) { + uint32_t lba = cluster_to_lba(current_cluster); + ata_read_sectors(lba, sectors_per_cluster, buf); + + int entries_per_cluster = bytes_per_cluster / sizeof(FAT32_DirEntry); + FAT32_DirEntry *entries = (FAT32_DirEntry*)buf; + + for (int i = 0; i < entries_per_cluster; i++) { + if (entries[i].filename[0] == 0) { kfree(buf); return false; } + if (entries[i].filename[0] == 0xE5) continue; + if (entries[i].attributes == 0x0F) continue; + + if (fs_memcmp(entries[i].filename, sfn_name, 8) == 0 && + fs_memcmp(entries[i].extension, sfn_ext, 3) == 0) { + kfree(buf); + return true; + } + } + current_cluster = get_fat_entry(current_cluster); + } + kfree(buf); + return false; +} + +// Generate a unique SFN (e.g., FILE~1, FILE~2) +static void generate_unique_sfn(uint32_t dir_cluster, const char *long_name, char *out_name, char *out_ext) { + // 1. Create Basis: Uppercase, skip dots/spaces, max 6 chars + char basis[7]; + int bi = 0; + for (int i = 0; long_name[i] && bi < 6; i++) { + char c = long_name[i]; + if (c == '.') break; // Stop at extension + if (c != ' ') { + if (c >= 'a' && c <= 'z') c -= 32; + basis[bi++] = c; + } + } + basis[bi] = 0; + + // 2. Extract Extension: Uppercase, max 3 chars + char ext[4] = " "; + const char *dot = long_name; + while (*dot && *dot != '.') dot++; + if (*dot == '.') { + dot++; + int ei = 0; + while (dot[ei] && ei < 3) { + char c = dot[ei]; + if (c >= 'a' && c <= 'z') c -= 32; + ext[ei] = c; + ei++; + } + } + + // 3. Try numeric tails ~1 to ~9 (and beyond if needed, simplified to ~9 for now) + for (int i = 1; i <= 9; i++) { + fs_memset(out_name, ' ', 8); + fs_memset(out_ext, ' ', 3); + + // Copy basis + for(int k=0; k= 2 && current_cluster < 0x0FFFFFF8) { + uint32_t lba = cluster_to_lba(current_cluster); + ata_read_sectors(lba, sectors_per_cluster, buf); + + int entries_per_cluster = bytes_per_cluster / sizeof(FAT32_DirEntry); + FAT32_DirEntry *entries = (FAT32_DirEntry*)buf; + + char lfn_name[256]; + fs_memset(lfn_name, 0, 256); + bool has_lfn = false; + + for (int i = 0; i < entries_per_cluster; i++) { + if (entries[i].filename[0] == 0) { kfree(buf); return false; } // End of dir + if (entries[i].filename[0] == 0xE5) { has_lfn = false; continue; } // Deleted + + if (entries[i].attributes == 0x0F) { + FAT32_LFNEntry *lfn = (FAT32_LFNEntry*)&entries[i]; + if (lfn->order & 0x40) { + fs_memset(lfn_name, 0, 256); + has_lfn = true; + } + if (has_lfn) { + int order = lfn->order & 0x3F; + char part[14]; + extract_lfn_part(lfn, part); + int pos = (order - 1) * 13; + if (pos < 255) { + int part_len = fs_strlen(part); + for(int k=0; kattributes = ATTR_DIRECTORY; + entry->start_cluster_low = root_cluster & 0xFFFF; + entry->start_cluster_high = root_cluster >> 16; + return true; + } + + char *p = normalized; + if (*p == '/') p++; + + while (*p) { + char component[256]; + int i = 0; + while (*p && *p != '/' && i < 255) component[i++] = *p++; + component[i] = 0; + if (*p == '/') p++; + + if (!find_in_directory(current_cluster, component, entry, NULL, NULL)) { + return false; + } + + if (*p) { + if (!(entry->attributes & ATTR_DIRECTORY)) return false; + current_cluster = (entry->start_cluster_high << 16) | entry->start_cluster_low; + } + } + return true; +} + +// Create a new entry in a directory +static bool create_entry(uint32_t dir_cluster, const char *name, uint8_t attributes, uint32_t *created_cluster) { + FAT32_DirEntry new_entry; + // Prepare entry + fs_memset(&new_entry, 0, sizeof(FAT32_DirEntry)); + new_entry.attributes = attributes; + + bool need_lfn = needs_lfn(name); + int name_len = fs_strlen(name); + + if (need_lfn) { + // Generate unique SFN (e.g. FILE~1) + char sfn_name[8], sfn_ext[3]; + generate_unique_sfn(dir_cluster, name, sfn_name, sfn_ext); + + fs_memcpy(new_entry.filename, sfn_name, 8); + fs_memcpy(new_entry.extension, sfn_ext, 3); + new_entry.reserved = 0; // No NT case flags for SFN when LFN exists + } else { + // Fits in 8.3, use standard conversion + str_to_fat_name(name, (char*)new_entry.filename, (char*)new_entry.extension, &new_entry.reserved); + } + + // Allocate a cluster for the file content (if not empty file) + // For now, we start with 0 size and 0 cluster + new_entry.start_cluster_high = 0; + new_entry.start_cluster_low = 0; + new_entry.file_size = 0; + + if (attributes & ATTR_DIRECTORY) { + uint32_t clus = find_free_cluster(); + if (clus == 0) return false; + set_fat_entry(clus, 0x0FFFFFFF); + clear_cluster(clus); + new_entry.start_cluster_high = (clus >> 16); + new_entry.start_cluster_low = (clus & 0xFFFF); + if (created_cluster) *created_cluster = clus; + } + + // Calculate LFN entries needed + int lfn_count = 0; + if (need_lfn) { + lfn_count = (name_len + 12) / 13; + } + int entries_needed = 1 + lfn_count; + + // Find free slot in directory + uint32_t current_cluster = dir_cluster; + uint8_t *buf = (uint8_t*)kmalloc(bytes_per_cluster); + if (!buf) return false; + + while (true) { + uint32_t lba = cluster_to_lba(current_cluster); + ata_read_sectors(lba, sectors_per_cluster, buf); + + FAT32_DirEntry *entries = (FAT32_DirEntry*)buf; + int entries_per_cluster = bytes_per_cluster / sizeof(FAT32_DirEntry); + + for (int i = 0; i < entries_per_cluster; i++) { + // Check if we have 'entries_needed' contiguous free slots + bool found = true; + if (i + entries_needed > entries_per_cluster) { + found = false; // Split across clusters not implemented for simplicity + } else { + for (int k = 0; k < entries_needed; k++) { + if (entries[i+k].filename[0] != 0 && entries[i+k].filename[0] != 0xE5) { + found = false; + break; + } + } + } + + if (found) { + // Write LFN entries first (in reverse order) + if (need_lfn) { + uint8_t checksum = lfn_checksum(new_entry.filename); + for (int k = 0; k < lfn_count; k++) { + FAT32_LFNEntry *lfn = (FAT32_LFNEntry*)&entries[i + lfn_count - 1 - k]; + fs_memset(lfn, 0, sizeof(FAT32_LFNEntry)); + + lfn->order = (k + 1) | (k == lfn_count - 1 ? 0x40 : 0); + lfn->attr = 0x0F; + lfn->type = 0; + lfn->checksum = checksum; + + int char_idx = k * 13; + // Name 1 (5 chars) + for(int m=0; m<5; m++) { + if (char_idx < name_len) lfn->name1[m] = (uint8_t)name[char_idx++]; + else if (char_idx == name_len) { lfn->name1[m] = 0; char_idx++; } + else lfn->name1[m] = 0xFFFF; + } + // Name 2 (6 chars) + for(int m=0; m<6; m++) { + if (char_idx < name_len) lfn->name2[m] = (uint8_t)name[char_idx++]; + else if (char_idx == name_len) { lfn->name2[m] = 0; char_idx++; } + else lfn->name2[m] = 0xFFFF; + } + // Name 3 (2 chars) + for(int m=0; m<2; m++) { + if (char_idx < name_len) lfn->name3[m] = (uint8_t)name[char_idx++]; + else if (char_idx == name_len) { lfn->name3[m] = 0; char_idx++; } + else lfn->name3[m] = 0xFFFF; + } + } + } + entries[i + lfn_count] = new_entry; + ata_write_sectors(lba, sectors_per_cluster, buf); + kfree(buf); + return true; + } + } + + uint32_t next = get_fat_entry(current_cluster); + if (next >= 0x0FFFFFF8) { + // Extend directory + uint32_t new_clus = find_free_cluster(); + if (new_clus == 0) { kfree(buf); return false; } + set_fat_entry(current_cluster, new_clus); + set_fat_entry(new_clus, 0x0FFFFFFF); + clear_cluster(new_clus); + current_cluster = new_clus; + } else { + current_cluster = next; + } + } } // === Public API === void fat32_init(void) { - // Initialize FAT table - for (int i = 0; i < MAX_CLUSTERS; i++) { - fat_table[i] = 0; - } - fat_table[0] = 0xFFFFFFF8; // Media descriptor - fat_table[1] = 0xFFFFFFFF; // Bad sector marker - - // Create root directory entry - FileEntry *root = find_free_entry(); - if (root) { - root->used = true; - root->filename[0] = 0; - fs_strcpy(root->full_path, "/"); - root->start_cluster = 2; // Root cluster - root->size = 0; - root->attributes = ATTR_DIRECTORY; - fat_table[2] = 0xFFFFFFFF; // Root is EOF + if (!ata_init()) { + // Fallback or panic if no disk found + return; } - next_cluster = 3; - current_dir[0] = '/'; - current_dir[1] = 0; + uint8_t buf[SECTOR_SIZE]; + ata_read_sectors(0, 1, buf); + + FAT32_BootSector *bpb = (FAT32_BootSector*)buf; + + // Basic BPB parsing + fat_start_lba = bpb->reserved_sectors; + sectors_per_cluster = bpb->sectors_per_cluster; + sectors_per_fat = bpb->sectors_per_fat_32; + root_cluster = bpb->root_cluster; + data_start_lba = fat_start_lba + (bpb->num_fats * bpb->sectors_per_fat_32); + bytes_per_cluster = sectors_per_cluster * SECTOR_SIZE; + total_clusters = bpb->total_sectors_32 / sectors_per_cluster; } FAT32_FileHandle* fat32_open(const char *path, const char *mode) { + FAT32_DirEntry entry; + uint32_t dir_sector = 0, dir_offset = 0; + + // We need to find the entry AND its location on disk char normalized[FAT32_MAX_PATH]; fat32_normalize_path(path, normalized); - FileEntry *entry = find_file(normalized); + // Split path into parent and filename + char parent_path[FAT32_MAX_PATH]; + char filename[256]; + int len = fs_strlen(normalized); + int split = len; + while(split > 0 && normalized[split] != '/') split--; - if (mode[0] == 'r') { - // Read mode - if (!entry || (entry->attributes & ATTR_DIRECTORY)) { - return NULL; // File not found or is directory - } - } else if (mode[0] == 'w' || (mode[0] == 'a')) { - // Write/append mode - create if not exists - if (!entry) { - entry = find_free_entry(); - if (!entry) return NULL; - - entry->used = true; - fs_strcpy(entry->full_path, normalized); - extract_filename(normalized, entry->filename); - extract_parent_path(normalized, entry->parent_path); - entry->start_cluster = allocate_cluster(); - if (!entry->start_cluster) return NULL; - entry->size = 0; - entry->attributes = 0; // Regular file - } - - if (mode[0] == 'w') { - entry->size = 0; // Truncate - } + if (split == 0) { + fs_strcpy(parent_path, "/"); + fs_strcpy(filename, normalized + (normalized[0] == '/' ? 1 : 0)); + } else { + for(int i=0; ivalid = true; - handle->cluster = entry->start_cluster; - handle->start_cluster = entry->start_cluster; + handle->cluster = start_cluster; + handle->start_cluster = start_cluster; handle->position = 0; - handle->size = entry->size; + handle->size = entry.file_size; + handle->dir_sector = dir_sector; + handle->dir_offset = dir_offset; if (mode[0] == 'r') { handle->mode = 0; } else if (mode[0] == 'w') { handle->mode = 1; + handle->size = 0; // Truncate logic would go here + // Note: We should update the size on disk to 0 here, but we do it on write/close } else { handle->mode = 2; // append - handle->position = entry->size; - - // Walk to the correct cluster for the current position - uint32_t current_cluster = handle->start_cluster; + handle->position = entry.file_size; + // Fast forward cluster uint32_t pos = 0; - while (pos + FAT32_CLUSTER_SIZE <= handle->position) { - uint32_t next = fat_table[current_cluster]; - if (next >= 0xFFFFFFF8) break; - current_cluster = next; - pos += FAT32_CLUSTER_SIZE; + while (pos + bytes_per_cluster <= handle->position) { + uint32_t next = get_fat_entry(handle->cluster); + if (next >= 0x0FFFFFF8) break; + handle->cluster = next; + pos += bytes_per_cluster; } - handle->cluster = current_cluster; } return handle; @@ -315,32 +780,33 @@ int fat32_read(FAT32_FileHandle *handle, void *buffer, int size) { uint8_t *buf = (uint8_t *)buffer; while (bytes_read < size && handle->position < handle->size) { - uint32_t offset_in_cluster = handle->position % FAT32_CLUSTER_SIZE; + uint32_t offset_in_cluster = handle->position % bytes_per_cluster; int to_read = size - bytes_read; int available = handle->size - handle->position; if (to_read > available) { to_read = available; } - if (to_read > FAT32_CLUSTER_SIZE - offset_in_cluster) { - to_read = FAT32_CLUSTER_SIZE - offset_in_cluster; + if ((uint32_t)to_read > bytes_per_cluster - offset_in_cluster) { + to_read = bytes_per_cluster - offset_in_cluster; } - if (handle->cluster >= MAX_CLUSTERS) { - break; - } + // Read whole cluster to temp buffer (inefficient but simple) + uint8_t *cluster_buf = (uint8_t*)kmalloc(bytes_per_cluster); + if (!cluster_buf) return -1; + ata_read_sectors(cluster_to_lba(handle->cluster), sectors_per_cluster, cluster_buf); - uint8_t *src = cluster_data[handle->cluster] + offset_in_cluster; for (int i = 0; i < to_read; i++) { - buf[bytes_read + i] = src[i]; + buf[bytes_read + i] = cluster_buf[offset_in_cluster + i]; } + kfree(cluster_buf); bytes_read += to_read; handle->position += to_read; // Move to next cluster if needed - if (handle->position % FAT32_CLUSTER_SIZE == 0 && handle->position < handle->size) { - handle->cluster = fat_table[handle->cluster]; + if (handle->position % bytes_per_cluster == 0 && handle->position < handle->size) { + handle->cluster = get_fat_entry(handle->cluster); } } @@ -348,65 +814,73 @@ int fat32_read(FAT32_FileHandle *handle, void *buffer, int size) { } int fat32_write(FAT32_FileHandle *handle, const void *buffer, int size) { - if (!handle || !handle->valid || (handle->mode != 1 && handle->mode != 2)) { - return -1; - } - + if (!handle || !handle->valid || handle->mode == 0) return -1; + + const uint8_t *buf = (const uint8_t*)buffer; int bytes_written = 0; - const uint8_t *buf = (const uint8_t *)buffer; - - // Check if we are at a cluster boundary from a previous write - if (handle->position > 0 && (handle->position % FAT32_CLUSTER_SIZE) == 0) { - uint32_t next = fat_table[handle->cluster]; - if (next >= 0xFFFFFFF8) { - next = allocate_cluster(); - if (!next) return 0; - fat_table[handle->cluster] = next; - } - handle->cluster = next; + + // Allocate first cluster if needed + if (handle->cluster == 0) { + uint32_t new_clus = find_free_cluster(); + if (new_clus == 0) return 0; + set_fat_entry(new_clus, 0x0FFFFFFF); + clear_cluster(new_clus); + handle->cluster = new_clus; + handle->start_cluster = new_clus; + + // Update directory entry start cluster + uint8_t sec_buf[SECTOR_SIZE]; + ata_read_sectors(handle->dir_sector, 1, sec_buf); + FAT32_DirEntry *ent = (FAT32_DirEntry*)(sec_buf + handle->dir_offset); + ent->start_cluster_high = (new_clus >> 16); + ent->start_cluster_low = (new_clus & 0xFFFF); + ata_write_sectors(handle->dir_sector, 1, sec_buf); } while (bytes_written < size) { - uint32_t offset_in_cluster = handle->position % FAT32_CLUSTER_SIZE; + uint32_t offset_in_cluster = handle->position % bytes_per_cluster; int to_write = size - bytes_written; - - if (to_write > FAT32_CLUSTER_SIZE - offset_in_cluster) { - to_write = FAT32_CLUSTER_SIZE - offset_in_cluster; + if ((uint32_t)to_write > bytes_per_cluster - offset_in_cluster) { + to_write = bytes_per_cluster - offset_in_cluster; } + + // Read-Modify-Write cluster + uint8_t *cluster_buf = (uint8_t*)kmalloc(bytes_per_cluster); + if (!cluster_buf) return -1; + uint32_t lba = cluster_to_lba(handle->cluster); + ata_read_sectors(lba, sectors_per_cluster, cluster_buf); - if (handle->cluster >= MAX_CLUSTERS) { - break; - } + for(int i=0; icluster] + offset_in_cluster; - for (int i = 0; i < to_write; i++) { - dest[i] = buf[bytes_written + i]; - } + ata_write_sectors(lba, sectors_per_cluster, cluster_buf); + kfree(cluster_buf); bytes_written += to_write; handle->position += to_write; - - if (handle->position > handle->size) { - handle->size = handle->position; - } - - // Move to next cluster if needed - if (offset_in_cluster + to_write >= FAT32_CLUSTER_SIZE && bytes_written < size) { - uint32_t next = allocate_cluster(); - if (!next) break; - fat_table[handle->cluster] = next; - handle->cluster = next; + if (handle->position > handle->size) handle->size = handle->position; + + if (handle->position % bytes_per_cluster == 0 && bytes_written < size) { + uint32_t next = get_fat_entry(handle->cluster); + if (next >= 0x0FFFFFF8) { + uint32_t new_clus = find_free_cluster(); + if (new_clus == 0) break; // Disk full + set_fat_entry(handle->cluster, new_clus); + set_fat_entry(new_clus, 0x0FFFFFFF); + clear_cluster(new_clus); + handle->cluster = new_clus; + } else { + handle->cluster = next; + } } } - - // Update file entry - for (int i = 0; i < MAX_FILES; i++) { - if (files[i].used && files[i].start_cluster == handle->start_cluster) { - files[i].size = handle->size; - break; - } - } - + + // Update file size in directory entry + uint8_t sec_buf[SECTOR_SIZE]; + ata_read_sectors(handle->dir_sector, 1, sec_buf); + FAT32_DirEntry *ent = (FAT32_DirEntry*)(sec_buf + handle->dir_offset); + ent->file_size = handle->size; + ata_write_sectors(handle->dir_sector, 1, sec_buf); + return bytes_written; } @@ -434,81 +908,117 @@ int fat32_seek(FAT32_FileHandle *handle, int offset, int whence) { } bool fat32_mkdir(const char *path) { + FAT32_DirEntry dummy; + if (resolve_path(path, &dummy)) return false; // Already exists + char normalized[FAT32_MAX_PATH]; fat32_normalize_path(path, normalized); - if (find_file(normalized)) { - return false; // Already exists + // Split path + char parent_path[FAT32_MAX_PATH]; + char dirname[256]; + int len = fs_strlen(normalized); + int split = len; + while(split > 0 && normalized[split] != '/') split--; + + if (split == 0) { + fs_strcpy(parent_path, "/"); + fs_strcpy(dirname, normalized + (normalized[0] == '/' ? 1 : 0)); + } else { + for(int i=0; iused = true; - fs_strcpy(entry->full_path, normalized); - extract_filename(normalized, entry->filename); - extract_parent_path(normalized, entry->parent_path); - entry->start_cluster = allocate_cluster(); - entry->size = 0; - entry->attributes = ATTR_DIRECTORY; - - return true; + return create_entry(parent_cluster, dirname, ATTR_DIRECTORY, NULL); } bool fat32_rmdir(const char *path) { - char normalized[FAT32_MAX_PATH]; - fat32_normalize_path(path, normalized); - - FileEntry *entry = find_file(normalized); - if (!entry || !(entry->attributes & ATTR_DIRECTORY)) { - return false; - } - - entry->used = false; - return true; + (void)path; + return false; // Not implemented } bool fat32_delete(const char *path) { - char normalized[FAT32_MAX_PATH]; - fat32_normalize_path(path, normalized); - - FileEntry *entry = find_file(normalized); - if (!entry || (entry->attributes & ATTR_DIRECTORY)) { - return false; - } - - entry->used = false; - return true; + (void)path; + return false; // Not implemented } bool fat32_exists(const char *path) { - return find_file(path) != NULL; + FAT32_DirEntry entry; + return resolve_path(path, &entry); } bool fat32_is_directory(const char *path) { - FileEntry *entry = find_file(path); - return entry && (entry->attributes & ATTR_DIRECTORY); + FAT32_DirEntry entry; + return resolve_path(path, &entry) && (entry.attributes & ATTR_DIRECTORY); } int fat32_list_directory(const char *path, FAT32_FileInfo *entries, int max_entries) { - char normalized[FAT32_MAX_PATH]; - fat32_normalize_path(path, normalized); - - FileEntry *dir = find_file(normalized); - if (!dir || !(dir->attributes & ATTR_DIRECTORY)) { - return 0; // Not a directory + FAT32_DirEntry dir_entry; + if (!resolve_path(path, &dir_entry) || !(dir_entry.attributes & ATTR_DIRECTORY)) { + return 0; } + uint32_t current_cluster = (dir_entry.start_cluster_high << 16) | dir_entry.start_cluster_low; + if (current_cluster == 0) current_cluster = root_cluster; + int count = 0; - for (int i = 0; i < MAX_FILES && count < max_entries; i++) { - if (files[i].used && fs_strcmp(files[i].parent_path, normalized) == 0) { - fs_strcpy(entries[count].name, files[i].filename); - entries[count].size = files[i].size; - entries[count].is_directory = (files[i].attributes & ATTR_DIRECTORY) != 0; - entries[count].start_cluster = files[i].start_cluster; + uint8_t *buf = (uint8_t*)kmalloc(bytes_per_cluster); + if (!buf) return 0; + + char lfn_name[256]; + fs_memset(lfn_name, 0, 256); + bool has_lfn = false; + + while (current_cluster >= 2 && current_cluster < 0x0FFFFFF8 && count < max_entries) { + ata_read_sectors(cluster_to_lba(current_cluster), sectors_per_cluster, buf); + FAT32_DirEntry *d = (FAT32_DirEntry*)buf; + int entries_per_cluster = bytes_per_cluster / sizeof(FAT32_DirEntry); + + for (int i = 0; i < entries_per_cluster && count < max_entries; i++) { + if (d[i].filename[0] == 0) { kfree(buf); return count; } + if (d[i].filename[0] == 0xE5) { has_lfn = false; continue; } + + if (d[i].attributes == 0x0F) { + FAT32_LFNEntry *lfn = (FAT32_LFNEntry*)&d[i]; + if (lfn->order & 0x40) { + fs_memset(lfn_name, 0, 256); + has_lfn = true; + } + if (has_lfn) { + int order = lfn->order & 0x3F; + char part[14]; + extract_lfn_part(lfn, part); + int pos = (order - 1) * 13; + if (pos < 255) { + int part_len = fs_strlen(part); + for(int k=0; kattributes & ATTR_DIRECTORY)) { + FAT32_DirEntry entry; + if (!resolve_path(normalized, &entry) || !(entry.attributes & ATTR_DIRECTORY)) { return false; } diff --git a/src/kernel/fat32.h b/src/kernel/fat32.h index f4eb119..47dc18e 100644 --- a/src/kernel/fat32.h +++ b/src/kernel/fat32.h @@ -83,6 +83,8 @@ typedef struct { uint32_t size; // File size uint32_t mode; // 0=read, 1=write, 2=append bool valid; // Is this handle valid? + uint32_t dir_sector; // Sector containing the directory entry + uint32_t dir_offset; // Offset within that sector } FAT32_FileHandle; // Directory Entry Info (for listing)