diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d5651b..0ea4d7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,6 +93,8 @@ set(ISO_ROOT "${BUILD_ROOT}/iso_root") set(RAMDISK_ROOT "${BUILD_ROOT}/ramdisk_root") set(KERNEL_ELF "${BUILD_ROOT}/clks_kernel.elf") set(RAMDISK_IMAGE "${BUILD_ROOT}/cleonos_ramdisk.tar") +set(DISK_IMAGE "${BUILD_ROOT}/cleonos_disk.img") +set(CLEONOS_DISK_IMAGE_MB "64" CACHE STRING "Default runtime disk image size in MB") set(ISO_IMAGE "${CMAKE_SOURCE_DIR}/build/CLeonOS-x86_64.iso") set(KERNEL_SYMBOLS_FILE "${BUILD_ROOT}/kernel.sym") @@ -457,6 +459,15 @@ add_custom_command( -P "${CL_LOG_EMIT_SCRIPT}" ) +add_custom_target(disk-image + COMMAND ${CMAKE_COMMAND} + "-DDISK_IMAGE=${DISK_IMAGE}" + "-DDISK_MB=${CLEONOS_DISK_IMAGE_MB}" + -P "${CMAKE_SOURCE_DIR}/cmake/ensure_disk_image.cmake" + BYPRODUCTS "${DISK_IMAGE}" + VERBATIM +) + add_custom_command( OUTPUT "${ISO_IMAGE}" COMMAND ${CMAKE_COMMAND} -E rm -rf "${ISO_ROOT}" @@ -511,8 +522,8 @@ add_custom_target(run "-DLOG_LEVEL=STEP" "-DLOG_TEXT=launching qemu run" -P "${CL_LOG_EMIT_SCRIPT}" - COMMAND ${QEMU_X86_64} -M q35 -m 1024M -cdrom "${ISO_IMAGE}" -serial stdio - DEPENDS iso + COMMAND ${QEMU_X86_64} -M pc -m 1024M -boot order=d -cdrom "${ISO_IMAGE}" -drive "file=${DISK_IMAGE},format=raw,if=ide,index=0,media=disk" -serial stdio + DEPENDS iso disk-image USES_TERMINAL ) @@ -522,8 +533,8 @@ add_custom_target(debug "-DLOG_LEVEL=STEP" "-DLOG_TEXT=launching qemu debug (-s -S)" -P "${CL_LOG_EMIT_SCRIPT}" - COMMAND ${QEMU_X86_64} -M q35 -m 1024M -cdrom "${ISO_IMAGE}" -serial stdio -s -S - DEPENDS iso + COMMAND ${QEMU_X86_64} -M pc -m 1024M -boot order=d -cdrom "${ISO_IMAGE}" -drive "file=${DISK_IMAGE},format=raw,if=ide,index=0,media=disk" -serial stdio -s -S + DEPENDS iso disk-image USES_TERMINAL ) @@ -586,5 +597,6 @@ add_custom_target(cleonos-help COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target iso" COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target run" COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target debug" + COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target disk-image" ) diff --git a/Makefile b/Makefile index af9c773..52675e5 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ READELF_FOR_TARGET ?= PYTHON ?= python3 MENUCONFIG_ARGS ?= MENUCONFIG_PRESET ?= +DISK_IMAGE_MB ?= ifeq ($(strip $(CMAKE_GENERATOR)),) GEN_ARG := @@ -52,8 +53,11 @@ endif ifneq ($(strip $(READELF_FOR_TARGET)),) CMAKE_PASSTHROUGH_ARGS += -DREADELF_FOR_TARGET=$(READELF_FOR_TARGET) endif +ifneq ($(strip $(DISK_IMAGE_MB)),) +CMAKE_PASSTHROUGH_ARGS += -DCLEONOS_DISK_IMAGE_MB=$(DISK_IMAGE_MB) +endif -.PHONY: all configure reconfigure menuconfig menuconfig-gui setup setup-tools setup-limine kernel userapps ramdisk-root ramdisk iso run debug clean clean-all help +.PHONY: all configure reconfigure menuconfig menuconfig-gui setup setup-tools setup-limine kernel userapps ramdisk-root ramdisk disk-image iso run debug clean clean-all help all: iso @@ -107,6 +111,9 @@ ramdisk-root: configure ramdisk: configure > @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target ramdisk +disk-image: configure +> @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target disk-image + iso: configure > @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target iso @@ -137,6 +144,7 @@ help: > @echo " make menuconfig-gui" > @echo " make setup" > @echo " make userapps" +> @echo " make disk-image" > @echo " make iso" > @echo " make run" > @echo " make debug" @@ -147,6 +155,8 @@ help: > @echo " make configure CMAKE_EXTRA_ARGS='-DLIMINE_SKIP_CONFIGURE=1 -DOBJCOPY_FOR_TARGET=objcopy'" > @echo "Direct passthrough is also supported:" > @echo " make run LIMINE_SKIP_CONFIGURE=1" +> @echo "Disk image size example:" +> @echo " make run DISK_IMAGE_MB=128" > @echo "Preset examples:" > @echo " make menuconfig MENUCONFIG_PRESET=full" > @echo " make menuconfig MENUCONFIG_PRESET=minimal" diff --git a/README.md b/README.md index d066e23..b19f1bc 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Experimental x86_64 operating system project with a C kernel, Rust-assisted runt - x86_64 kernel booted by Limine - RAM-disk VFS layout (`/system`, `/shell`, `/temp`, `/driver`) +- Virtual disk backend with FAT32 format + mount support (default mount path: `/temp/disk`) - Virtual TTY subsystem (multi TTY, ANSI handling, cursor, PSF font support) - Keyboard and mouse input stack, plus desktop mode on TTY2 - User-space ELF app model with syscall ABI (`int 0x80`) @@ -60,6 +61,12 @@ git submodule update --init --recursive make run ``` +`make run` now auto-prepares `build/x86_64/cleonos_disk.img` (if missing) and attaches it as a QEMU emulated physical disk (`-drive ... if=ide`). +This disk is **not** packed into the ISO and is **not** loaded through Limine modules. +Kernel currently uses an in-memory disk cache window (default up to 8MB) for metadata/file operations. +You can override disk size (MB), for example: `make run DISK_IMAGE_MB=128`. +Inside CLeonOS, use `diskinfo` to confirm the disk is visible. + If you already have Limine artifacts and want to skip configure: ```bash @@ -72,6 +79,7 @@ make run LIMINE_SKIP_CONFIGURE=1 - `make kernel` - build kernel ELF - `make userapps` - build user-space ELF apps - `make ramdisk` - package runtime ramdisk +- `make disk-image` - create/resize runtime disk image (`build/x86_64/cleonos_disk.img`) - `make iso` - build bootable ISO - `make run` - launch QEMU - `make debug` - launch QEMU with `-s -S` for GDB @@ -102,6 +110,10 @@ cat /shell/init.cmd grep -n exec /shell/init.cmd cat /shell/init.cmd | grep -n exec ls /shell > /temp/shell_list.txt +diskinfo +mkfsfat32 CLEONOS +mount /temp/disk +write /temp/disk/hello.txt hello-disk ``` ## Documentation diff --git a/README.zh-CN.md b/README.zh-CN.md index 4c9633f..b16ec7e 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -8,6 +8,7 @@ - 基于 Limine 启动的 x86_64 内核 - RAM-disk VFS 目录布局(`/system`、`/shell`、`/temp`、`/driver`) +- 虚拟磁盘后端,支持 FAT32 格式化与挂载(默认挂载点:`/temp/disk`) - 虚拟 TTY 子系统(多 TTY、ANSI、光标、PSF 字体) - 键盘/鼠标输入栈,TTY2 提供桌面模式 - 用户态 ELF 应用模型,syscall ABI 使用 `int 0x80` @@ -60,6 +61,12 @@ git submodule update --init --recursive make run ``` +`make run` 现在会自动准备 `build/x86_64/cleonos_disk.img`(不存在时自动创建),并以 QEMU 模拟物理硬盘方式挂载(`-drive ... if=ide`)。 +该磁盘**不会**打包进 ISO,也**不会**通过 Limine `module_path` 注入。 +内核当前通过内存缓存窗口处理磁盘元数据/文件(默认最多约 8MB)。 +你也可以覆盖磁盘大小(MB),例如:`make run DISK_IMAGE_MB=128`。 +进入系统后可先执行 `diskinfo` 确认磁盘已识别。 + 如果你已经准备好 Limine 产物,可跳过 configure: ```bash @@ -72,6 +79,7 @@ make run LIMINE_SKIP_CONFIGURE=1 - `make kernel` - 构建内核 ELF - `make userapps` - 构建用户态 ELF 应用 - `make ramdisk` - 打包运行时 ramdisk +- `make disk-image` - 创建/调整运行时磁盘镜像(`build/x86_64/cleonos_disk.img`) - `make iso` - 生成可启动 ISO - `make run` - 启动 QEMU - `make debug` - 以 `-s -S` 启动 QEMU 供 GDB 附加 @@ -102,6 +110,10 @@ cat /shell/init.cmd grep -n exec /shell/init.cmd cat /shell/init.cmd | grep -n exec ls /shell > /temp/shell_list.txt +diskinfo +mkfsfat32 CLEONOS +mount /temp/disk +write /temp/disk/hello.txt hello-disk ``` ## 文档 diff --git a/cleonos/CMakeLists.txt b/cleonos/CMakeLists.txt index ed9d261..09302ba 100644 --- a/cleonos/CMakeLists.txt +++ b/cleonos/CMakeLists.txt @@ -130,6 +130,7 @@ set(USER_SHELL_COMMAND_APPS help args ls cat grep head tail wc cut uniq sort pwd cd exec pid spawn wait sleep yield bg fg jobs kill ps top procstat sysstat + diskinfo mkfsfat32 mount shutdown restart exit clear ansi ansitest wavplay fastfetch memstat fsstat taskstat userstat shstat stats tty dmesg kbdstat mkdir touch write append cp mv rm kdbg bmpview qrcode ) diff --git a/cleonos/c/apps/diskinfo_main.c b/cleonos/c/apps/diskinfo_main.c new file mode 100644 index 0000000..07da6ec --- /dev/null +++ b/cleonos/c/apps/diskinfo_main.c @@ -0,0 +1,87 @@ +#include "cmd_runtime.h" +#include + +static void clio_writeln(const char *text) { + (void)fputs(text, 1); + (void)putchar('\n'); +} + +static int ush_cmd_diskinfo(void) { + u64 present = cleonos_sys_disk_present(); + u64 size_bytes = 0ULL; + u64 sectors = 0ULL; + u64 formatted = 0ULL; + u64 mounted = 0ULL; + char mount_path[USH_PATH_MAX]; + + if (present == 0ULL) { + clio_writeln("disk: not present"); + return 0; + } + + size_bytes = cleonos_sys_disk_size_bytes(); + sectors = cleonos_sys_disk_sector_count(); + formatted = cleonos_sys_disk_formatted(); + mounted = cleonos_sys_disk_mounted(); + mount_path[0] = '\0'; + + if (mounted != 0ULL) { + (void)cleonos_sys_disk_mount_path(mount_path, (u64)sizeof(mount_path)); + } + + (void)printf("disk.present: %llu\n", (unsigned long long)present); + (void)printf("disk.size_bytes: %llu\n", (unsigned long long)size_bytes); + (void)printf("disk.sectors: %llu\n", (unsigned long long)sectors); + (void)printf("disk.formatted_fat32: %llu\n", (unsigned long long)formatted); + (void)printf("disk.mounted: %llu\n", (unsigned long long)mounted); + + if (mounted != 0ULL && mount_path[0] != '\0') { + (void)printf("disk.mount_path: %s\n", mount_path); + } else { + clio_writeln("disk.mount_path: (none)"); + } + + return 1; +} + +int cleonos_app_main(void) { + ush_cmd_ctx ctx; + ush_cmd_ret ret; + ush_state sh; + char initial_cwd[USH_PATH_MAX]; + int has_context = 0; + int success = 0; + + ush_zero(&ctx, (u64)sizeof(ctx)); + ush_zero(&ret, (u64)sizeof(ret)); + ush_init_state(&sh); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + + if (ush_command_ctx_read(&ctx) != 0) { + if (ctx.cmd[0] != '\0' && ush_streq(ctx.cmd, "diskinfo") != 0) { + has_context = 1; + if (ctx.cwd[0] == '/') { + ush_copy(sh.cwd, (u64)sizeof(sh.cwd), ctx.cwd); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + } + } + } + + success = ush_cmd_diskinfo(); + + if (has_context != 0) { + if (ush_streq(sh.cwd, initial_cwd) == 0) { + ret.flags |= USH_CMD_RET_FLAG_CWD; + ush_copy(ret.cwd, (u64)sizeof(ret.cwd), sh.cwd); + } + + if (sh.exit_requested != 0) { + ret.flags |= USH_CMD_RET_FLAG_EXIT; + ret.exit_code = sh.exit_code; + } + + (void)ush_command_ret_write(&ret); + } + + return (success != 0) ? 0 : 1; +} diff --git a/cleonos/c/apps/help_main.c b/cleonos/c/apps/help_main.c index e4ba85d..1acd332 100644 --- a/cleonos/c/apps/help_main.c +++ b/cleonos/c/apps/help_main.c @@ -29,6 +29,9 @@ static int ush_cmd_help(void) { ush_writeln(" cp (dst /temp only)"); ush_writeln(" mv (/temp only)"); ush_writeln(" rm (/temp only)"); + ush_writeln(" diskinfo"); + ush_writeln(" mkfsfat32 [label]"); + ush_writeln(" mount [path] (default suggested: /temp/disk)"); ush_writeln(" pid"); ush_writeln(" spawn [args...] / bg [args...]"); ush_writeln(" wait / fg [pid]"); diff --git a/cleonos/c/apps/mkfsfat32_main.c b/cleonos/c/apps/mkfsfat32_main.c new file mode 100644 index 0000000..e270627 --- /dev/null +++ b/cleonos/c/apps/mkfsfat32_main.c @@ -0,0 +1,106 @@ +#include "cmd_runtime.h" +#include + +static int ush_arg_parse_label(const char *arg, char *out_label, u64 out_label_size) { + char first[USH_ARG_MAX]; + const char *rest = (const char *)0; + + if (out_label == (char *)0 || out_label_size == 0ULL) { + return 0; + } + + out_label[0] = '\0'; + + if (arg == (const char *)0 || arg[0] == '\0') { + return 1; + } + + if (ush_split_first_and_rest(arg, first, (u64)sizeof(first), &rest) == 0) { + return 0; + } + + while (rest != (const char *)0 && *rest != '\0' && ush_is_space(*rest) != 0) { + rest++; + } + + if (rest != (const char *)0 && *rest != '\0') { + return 0; + } + + ush_copy(out_label, out_label_size, first); + return 1; +} + +static int ush_cmd_mkfsfat32(const char *arg) { + char label[16]; + u64 ok; + + if (cleonos_sys_disk_present() == 0ULL) { + (void)fputs("mkfsfat32: disk not present\n", 1); + return 0; + } + + if (ush_arg_parse_label(arg, label, (u64)sizeof(label)) == 0) { + (void)fputs("mkfsfat32: usage mkfsfat32 [label]\n", 1); + return 0; + } + + ok = cleonos_sys_disk_format_fat32((label[0] != '\0') ? label : (const char *)0); + if (ok == 0ULL) { + (void)fputs("mkfsfat32: format failed\n", 1); + return 0; + } + + if (label[0] != '\0') { + (void)printf("mkfsfat32: formatted (label=%s)\n", label); + } else { + (void)fputs("mkfsfat32: formatted\n", 1); + } + + (void)fputs("mkfsfat32: now run 'mount /temp/disk' (or another mount path)\n", 1); + return 1; +} + +int cleonos_app_main(void) { + ush_cmd_ctx ctx; + ush_cmd_ret ret; + ush_state sh; + char initial_cwd[USH_PATH_MAX]; + int has_context = 0; + int success = 0; + const char *arg = ""; + + ush_zero(&ctx, (u64)sizeof(ctx)); + ush_zero(&ret, (u64)sizeof(ret)); + ush_init_state(&sh); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + + if (ush_command_ctx_read(&ctx) != 0) { + if (ctx.cmd[0] != '\0' && ush_streq(ctx.cmd, "mkfsfat32") != 0) { + has_context = 1; + arg = ctx.arg; + if (ctx.cwd[0] == '/') { + ush_copy(sh.cwd, (u64)sizeof(sh.cwd), ctx.cwd); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + } + } + } + + success = ush_cmd_mkfsfat32(arg); + + if (has_context != 0) { + if (ush_streq(sh.cwd, initial_cwd) == 0) { + ret.flags |= USH_CMD_RET_FLAG_CWD; + ush_copy(ret.cwd, (u64)sizeof(ret.cwd), sh.cwd); + } + + if (sh.exit_requested != 0) { + ret.flags |= USH_CMD_RET_FLAG_EXIT; + ret.exit_code = sh.exit_code; + } + + (void)ush_command_ret_write(&ret); + } + + return (success != 0) ? 0 : 1; +} diff --git a/cleonos/c/apps/mount_main.c b/cleonos/c/apps/mount_main.c new file mode 100644 index 0000000..b77a14b --- /dev/null +++ b/cleonos/c/apps/mount_main.c @@ -0,0 +1,134 @@ +#include "cmd_runtime.h" +#include + +static int ush_mount_parse_path_arg(const char *arg, char *out_path, u64 out_path_size) { + char first[USH_ARG_MAX]; + const char *rest = (const char *)0; + + if (out_path == (char *)0 || out_path_size == 0ULL) { + return 0; + } + + out_path[0] = '\0'; + + if (arg == (const char *)0 || arg[0] == '\0') { + return 1; + } + + if (ush_split_first_and_rest(arg, first, (u64)sizeof(first), &rest) == 0) { + return 0; + } + + while (rest != (const char *)0 && *rest != '\0' && ush_is_space(*rest) != 0) { + rest++; + } + + if (rest != (const char *)0 && *rest != '\0') { + return 0; + } + + ush_copy(out_path, out_path_size, first); + return 1; +} + +static int ush_cmd_mount(ush_state *sh, const char *arg) { + char mount_arg[USH_PATH_MAX]; + char mount_path[USH_PATH_MAX]; + char current_path[USH_PATH_MAX]; + u64 mounted; + + if (sh == (ush_state *)0) { + return 0; + } + + if (cleonos_sys_disk_present() == 0ULL) { + (void)fputs("mount: disk not present\n", 1); + return 0; + } + + if (ush_mount_parse_path_arg(arg, mount_arg, (u64)sizeof(mount_arg)) == 0) { + (void)fputs("mount: usage mount [path]\n", 1); + return 0; + } + + mounted = cleonos_sys_disk_mounted(); + if (mount_arg[0] == '\0') { + if (mounted == 0ULL) { + (void)fputs("mount: no mounted disk path\n", 1); + (void)fputs("mount: usage mount /temp/disk\n", 1); + return 0; + } + + current_path[0] = '\0'; + (void)cleonos_sys_disk_mount_path(current_path, (u64)sizeof(current_path)); + if (current_path[0] == '\0') { + (void)fputs("disk0 on (unknown) type fat32\n", 1); + } else { + (void)printf("disk0 on %s type fat32\n", current_path); + } + return 1; + } + + if (cleonos_sys_disk_formatted() == 0ULL) { + (void)fputs("mount: disk is not FAT32 formatted\n", 1); + (void)fputs("mount: run mkfsfat32 first\n", 1); + return 0; + } + + if (ush_resolve_path(sh, mount_arg, mount_path, (u64)sizeof(mount_path)) == 0) { + (void)fputs("mount: invalid path\n", 1); + return 0; + } + + if (cleonos_sys_disk_mount(mount_path) == 0ULL) { + (void)fputs("mount: mount failed\n", 1); + return 0; + } + + (void)printf("disk0 mounted on %s\n", mount_path); + return 1; +} + +int cleonos_app_main(void) { + ush_cmd_ctx ctx; + ush_cmd_ret ret; + ush_state sh; + char initial_cwd[USH_PATH_MAX]; + int has_context = 0; + int success = 0; + const char *arg = ""; + + ush_zero(&ctx, (u64)sizeof(ctx)); + ush_zero(&ret, (u64)sizeof(ret)); + ush_init_state(&sh); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + + if (ush_command_ctx_read(&ctx) != 0) { + if (ctx.cmd[0] != '\0' && ush_streq(ctx.cmd, "mount") != 0) { + has_context = 1; + arg = ctx.arg; + if (ctx.cwd[0] == '/') { + ush_copy(sh.cwd, (u64)sizeof(sh.cwd), ctx.cwd); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + } + } + } + + success = ush_cmd_mount(&sh, arg); + + if (has_context != 0) { + if (ush_streq(sh.cwd, initial_cwd) == 0) { + ret.flags |= USH_CMD_RET_FLAG_CWD; + ush_copy(ret.cwd, (u64)sizeof(ret.cwd), sh.cwd); + } + + if (sh.exit_requested != 0) { + ret.flags |= USH_CMD_RET_FLAG_EXIT; + ret.exit_code = sh.exit_code; + } + + (void)ush_command_ret_write(&ret); + } + + return (success != 0) ? 0 : 1; +} diff --git a/cleonos/c/include/cleonos_syscall.h b/cleonos/c/include/cleonos_syscall.h index 6c942e7..764a91e 100644 --- a/cleonos/c/include/cleonos_syscall.h +++ b/cleonos/c/include/cleonos_syscall.h @@ -145,6 +145,14 @@ typedef struct cleonos_fb_blit_req { #define CLEONOS_SYSCALL_FB_BLIT 82ULL #define CLEONOS_SYSCALL_FB_CLEAR 83ULL #define CLEONOS_SYSCALL_KERNEL_VERSION 84ULL +#define CLEONOS_SYSCALL_DISK_PRESENT 85ULL +#define CLEONOS_SYSCALL_DISK_SIZE_BYTES 86ULL +#define CLEONOS_SYSCALL_DISK_SECTOR_COUNT 87ULL +#define CLEONOS_SYSCALL_DISK_FORMATTED 88ULL +#define CLEONOS_SYSCALL_DISK_FORMAT_FAT32 89ULL +#define CLEONOS_SYSCALL_DISK_MOUNT 90ULL +#define CLEONOS_SYSCALL_DISK_MOUNTED 91ULL +#define CLEONOS_SYSCALL_DISK_MOUNT_PATH 92ULL u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); u64 cleonos_sys_log_write(const char *message, u64 length); @@ -232,5 +240,13 @@ u64 cleonos_sys_fb_info(cleonos_fb_info *out_info); u64 cleonos_sys_fb_blit(const cleonos_fb_blit_req *req); u64 cleonos_sys_fb_clear(u64 rgb); u64 cleonos_sys_kernel_version(char *out_version, u64 out_size); +u64 cleonos_sys_disk_present(void); +u64 cleonos_sys_disk_size_bytes(void); +u64 cleonos_sys_disk_sector_count(void); +u64 cleonos_sys_disk_formatted(void); +u64 cleonos_sys_disk_format_fat32(const char *label); +u64 cleonos_sys_disk_mount(const char *mount_path); +u64 cleonos_sys_disk_mounted(void); +u64 cleonos_sys_disk_mount_path(char *out_path, u64 out_size); #endif diff --git a/cleonos/c/src/syscall.c b/cleonos/c/src/syscall.c index 3286323..644430f 100644 --- a/cleonos/c/src/syscall.c +++ b/cleonos/c/src/syscall.c @@ -371,3 +371,35 @@ u64 cleonos_sys_fb_clear(u64 rgb) { u64 cleonos_sys_kernel_version(char *out_version, u64 out_size) { return cleonos_syscall(CLEONOS_SYSCALL_KERNEL_VERSION, (u64)out_version, out_size, 0ULL); } + +u64 cleonos_sys_disk_present(void) { + return cleonos_syscall(CLEONOS_SYSCALL_DISK_PRESENT, 0ULL, 0ULL, 0ULL); +} + +u64 cleonos_sys_disk_size_bytes(void) { + return cleonos_syscall(CLEONOS_SYSCALL_DISK_SIZE_BYTES, 0ULL, 0ULL, 0ULL); +} + +u64 cleonos_sys_disk_sector_count(void) { + return cleonos_syscall(CLEONOS_SYSCALL_DISK_SECTOR_COUNT, 0ULL, 0ULL, 0ULL); +} + +u64 cleonos_sys_disk_formatted(void) { + return cleonos_syscall(CLEONOS_SYSCALL_DISK_FORMATTED, 0ULL, 0ULL, 0ULL); +} + +u64 cleonos_sys_disk_format_fat32(const char *label) { + return cleonos_syscall(CLEONOS_SYSCALL_DISK_FORMAT_FAT32, (u64)label, 0ULL, 0ULL); +} + +u64 cleonos_sys_disk_mount(const char *mount_path) { + return cleonos_syscall(CLEONOS_SYSCALL_DISK_MOUNT, (u64)mount_path, 0ULL, 0ULL); +} + +u64 cleonos_sys_disk_mounted(void) { + return cleonos_syscall(CLEONOS_SYSCALL_DISK_MOUNTED, 0ULL, 0ULL, 0ULL); +} + +u64 cleonos_sys_disk_mount_path(char *out_path, u64 out_size) { + return cleonos_syscall(CLEONOS_SYSCALL_DISK_MOUNT_PATH, (u64)out_path, out_size, 0ULL); +} diff --git a/clks/include/clks/disk.h b/clks/include/clks/disk.h new file mode 100644 index 0000000..ec1f00e --- /dev/null +++ b/clks/include/clks/disk.h @@ -0,0 +1,38 @@ +#ifndef CLKS_DISK_H +#define CLKS_DISK_H + +#include + +#define CLKS_DISK_SECTOR_SIZE 512ULL +#define CLKS_DISK_PATH_MAX 192U +#define CLKS_DISK_NODE_FILE 1ULL +#define CLKS_DISK_NODE_DIR 2ULL + +void clks_disk_init(void); + +clks_bool clks_disk_present(void); +u64 clks_disk_size_bytes(void); +u64 clks_disk_sector_count(void); + +clks_bool clks_disk_read_sector(u64 lba, void *out_sector); +clks_bool clks_disk_write_sector(u64 lba, const void *sector_data); + +clks_bool clks_disk_is_formatted_fat32(void); +clks_bool clks_disk_format_fat32(const char *label); + +clks_bool clks_disk_mount(const char *mount_path); +clks_bool clks_disk_is_mounted(void); +const char *clks_disk_mount_path(void); +clks_bool clks_disk_path_in_mount(const char *path); + +clks_bool clks_disk_stat(const char *path, u64 *out_type, u64 *out_size); +const void *clks_disk_read_all(const char *path, u64 *out_size); +u64 clks_disk_count_children(const char *dir_path); +clks_bool clks_disk_get_child_name(const char *dir_path, u64 index, char *out_name, usize out_name_size); +clks_bool clks_disk_mkdir(const char *path); +clks_bool clks_disk_write_all(const char *path, const void *data, u64 size); +clks_bool clks_disk_append(const char *path, const void *data, u64 size); +clks_bool clks_disk_remove(const char *path); +u64 clks_disk_node_count(void); + +#endif diff --git a/clks/include/clks/syscall.h b/clks/include/clks/syscall.h index 8bfb8e4..15418a2 100644 --- a/clks/include/clks/syscall.h +++ b/clks/include/clks/syscall.h @@ -88,6 +88,14 @@ #define CLKS_SYSCALL_FB_BLIT 82ULL #define CLKS_SYSCALL_FB_CLEAR 83ULL #define CLKS_SYSCALL_KERNEL_VERSION 84ULL +#define CLKS_SYSCALL_DISK_PRESENT 85ULL +#define CLKS_SYSCALL_DISK_SIZE_BYTES 86ULL +#define CLKS_SYSCALL_DISK_SECTOR_COUNT 87ULL +#define CLKS_SYSCALL_DISK_FORMATTED 88ULL +#define CLKS_SYSCALL_DISK_FORMAT_FAT32 89ULL +#define CLKS_SYSCALL_DISK_MOUNT 90ULL +#define CLKS_SYSCALL_DISK_MOUNTED 91ULL +#define CLKS_SYSCALL_DISK_MOUNT_PATH 92ULL void clks_syscall_init(void); u64 clks_syscall_dispatch(void *frame_ptr); diff --git a/clks/kernel/runtime/syscall.c b/clks/kernel/runtime/syscall.c index 9c18e47..714eb9b 100644 --- a/clks/kernel/runtime/syscall.c +++ b/clks/kernel/runtime/syscall.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -36,7 +37,7 @@ #define CLKS_SYSCALL_KDBG_STACK_WINDOW_BYTES (128ULL * 1024ULL) #define CLKS_SYSCALL_KERNEL_SYMBOL_FILE "/system/kernel.sym" #define CLKS_SYSCALL_KERNEL_ADDR_BASE 0xFFFF800000000000ULL -#define CLKS_SYSCALL_STATS_MAX_ID CLKS_SYSCALL_KERNEL_VERSION +#define CLKS_SYSCALL_STATS_MAX_ID CLKS_SYSCALL_DISK_MOUNT_PATH #define CLKS_SYSCALL_STATS_RING_SIZE 256U #define CLKS_SYSCALL_USC_MAX_ALLOWED_APPS 64U @@ -479,6 +480,56 @@ static u64 clks_syscall_kernel_version(u64 arg0, u64 arg1) { return clks_syscall_copy_text_to_user(arg0, arg1, CLKS_VERSION_STRING, len); } +static u64 clks_syscall_disk_present(void) { + return (clks_disk_present() == CLKS_TRUE) ? 1ULL : 0ULL; +} + +static u64 clks_syscall_disk_size_bytes(void) { + return clks_disk_size_bytes(); +} + +static u64 clks_syscall_disk_sector_count(void) { + return clks_disk_sector_count(); +} + +static u64 clks_syscall_disk_formatted(void) { + return (clks_disk_is_formatted_fat32() == CLKS_TRUE) ? 1ULL : 0ULL; +} + +static u64 clks_syscall_disk_format_fat32(u64 arg0) { + char label[16]; + + if (clks_syscall_copy_user_optional_string(arg0, label, sizeof(label)) == CLKS_FALSE) { + return 0ULL; + } + + return (clks_disk_format_fat32((label[0] != '\0') ? label : CLKS_NULL) == CLKS_TRUE) ? 1ULL : 0ULL; +} + +static u64 clks_syscall_disk_mount(u64 arg0) { + char path[CLKS_SYSCALL_PATH_MAX]; + + if (clks_syscall_copy_user_string(arg0, path, sizeof(path)) == CLKS_FALSE) { + return 0ULL; + } + + return (clks_disk_mount(path) == CLKS_TRUE) ? 1ULL : 0ULL; +} + +static u64 clks_syscall_disk_mounted(void) { + return (clks_disk_is_mounted() == CLKS_TRUE) ? 1ULL : 0ULL; +} + +static u64 clks_syscall_disk_mount_path(u64 arg0, u64 arg1) { + const char *mount_path = clks_disk_mount_path(); + + if (mount_path == CLKS_NULL || mount_path[0] == '\0') { + return 0ULL; + } + + return clks_syscall_copy_text_to_user(arg0, arg1, mount_path, clks_strlen(mount_path)); +} + static u64 clks_syscall_fd_open(u64 arg0, u64 arg1, u64 arg2) { char path[CLKS_SYSCALL_PATH_MAX]; @@ -1994,6 +2045,22 @@ static const char *clks_syscall_usc_syscall_name(u64 id) { return "SHUTDOWN"; case CLKS_SYSCALL_RESTART: return "RESTART"; + case CLKS_SYSCALL_DISK_PRESENT: + return "DISK_PRESENT"; + case CLKS_SYSCALL_DISK_SIZE_BYTES: + return "DISK_SIZE_BYTES"; + case CLKS_SYSCALL_DISK_SECTOR_COUNT: + return "DISK_SECTOR_COUNT"; + case CLKS_SYSCALL_DISK_FORMATTED: + return "DISK_FORMATTED"; + case CLKS_SYSCALL_DISK_FORMAT_FAT32: + return "DISK_FORMAT_FAT32"; + case CLKS_SYSCALL_DISK_MOUNT: + return "DISK_MOUNT"; + case CLKS_SYSCALL_DISK_MOUNTED: + return "DISK_MOUNTED"; + case CLKS_SYSCALL_DISK_MOUNT_PATH: + return "DISK_MOUNT_PATH"; default: return "UNKNOWN"; } @@ -2025,6 +2092,8 @@ static clks_bool clks_syscall_usc_is_dangerous(u64 id) { return (CLKS_CFG_USC_SC_SHUTDOWN != 0) ? CLKS_TRUE : CLKS_FALSE; case CLKS_SYSCALL_RESTART: return (CLKS_CFG_USC_SC_RESTART != 0) ? CLKS_TRUE : CLKS_FALSE; + case CLKS_SYSCALL_DISK_FORMAT_FAT32: + return CLKS_TRUE; default: return CLKS_FALSE; } @@ -2528,6 +2597,22 @@ u64 clks_syscall_dispatch(void *frame_ptr) { return clks_syscall_fb_clear(frame->rbx); case CLKS_SYSCALL_KERNEL_VERSION: return clks_syscall_kernel_version(frame->rbx, frame->rcx); + case CLKS_SYSCALL_DISK_PRESENT: + return clks_syscall_disk_present(); + case CLKS_SYSCALL_DISK_SIZE_BYTES: + return clks_syscall_disk_size_bytes(); + case CLKS_SYSCALL_DISK_SECTOR_COUNT: + return clks_syscall_disk_sector_count(); + case CLKS_SYSCALL_DISK_FORMATTED: + return clks_syscall_disk_formatted(); + case CLKS_SYSCALL_DISK_FORMAT_FAT32: + return clks_syscall_disk_format_fat32(frame->rbx); + case CLKS_SYSCALL_DISK_MOUNT: + return clks_syscall_disk_mount(frame->rbx); + case CLKS_SYSCALL_DISK_MOUNTED: + return clks_syscall_disk_mounted(); + case CLKS_SYSCALL_DISK_MOUNT_PATH: + return clks_syscall_disk_mount_path(frame->rbx, frame->rcx); default: return (u64)-1; } diff --git a/clks/kernel/storage/disk.c b/clks/kernel/storage/disk.c new file mode 100644 index 0000000..122548d --- /dev/null +++ b/clks/kernel/storage/disk.c @@ -0,0 +1,1853 @@ +#include +#include +#include +#include +#include + +#define CLKS_DISK_MIN_BYTES (4ULL * 1024ULL * 1024ULL) +#define CLKS_DISK_CACHE_MAX_BYTES (8ULL * 1024ULL * 1024ULL) + +#define CLKS_DISK_MAX_NODES 256U +#define CLKS_DISK_NODE_FLAG_HEAP_DATA 0x0001U + +#define CLKS_DISK_META_OFFSET 0x00010000ULL +#define CLKS_DISK_META_SIZE 0x00020000ULL +#define CLKS_DISK_META_HEADER_SIZE 32U +#define CLKS_DISK_META_ENTRY_SIZE 224U +#define CLKS_DISK_DATA_OFFSET (CLKS_DISK_META_OFFSET + CLKS_DISK_META_SIZE) + +#define CLKS_DISK_META_MAGIC "CLDSKFS1" +#define CLKS_DISK_META_VERSION 1U + +#define CLKS_DISK_FAT32_BOOT_SIG_OFFSET 510U +#define CLKS_DISK_FAT32_TYPE_OFFSET 82U +#define CLKS_DISK_FAT32_TYPE_LEN 5U + +#if defined(CLKS_ARCH_X86_64) +#define CLKS_DISK_ATA_IO_BASE 0x1F0U +#define CLKS_DISK_ATA_CTRL_BASE 0x3F6U +#define CLKS_DISK_ATA_REG_DATA (CLKS_DISK_ATA_IO_BASE + 0U) +#define CLKS_DISK_ATA_REG_SECTOR_COUNT (CLKS_DISK_ATA_IO_BASE + 2U) +#define CLKS_DISK_ATA_REG_LBA_LOW (CLKS_DISK_ATA_IO_BASE + 3U) +#define CLKS_DISK_ATA_REG_LBA_MID (CLKS_DISK_ATA_IO_BASE + 4U) +#define CLKS_DISK_ATA_REG_LBA_HIGH (CLKS_DISK_ATA_IO_BASE + 5U) +#define CLKS_DISK_ATA_REG_DRIVE (CLKS_DISK_ATA_IO_BASE + 6U) +#define CLKS_DISK_ATA_REG_STATUS (CLKS_DISK_ATA_IO_BASE + 7U) +#define CLKS_DISK_ATA_REG_COMMAND (CLKS_DISK_ATA_IO_BASE + 7U) +#define CLKS_DISK_ATA_REG_ALT_STATUS (CLKS_DISK_ATA_CTRL_BASE + 0U) + +#define CLKS_DISK_ATA_STATUS_ERR 0x01U +#define CLKS_DISK_ATA_STATUS_DRQ 0x08U +#define CLKS_DISK_ATA_STATUS_DF 0x20U +#define CLKS_DISK_ATA_STATUS_DRDY 0x40U +#define CLKS_DISK_ATA_STATUS_BSY 0x80U + +#define CLKS_DISK_ATA_CMD_READ_SECTORS 0x20U +#define CLKS_DISK_ATA_CMD_WRITE_SECTORS 0x30U +#define CLKS_DISK_ATA_CMD_CACHE_FLUSH 0xE7U +#define CLKS_DISK_ATA_CMD_IDENTIFY 0xECU +#endif + +struct clks_disk_node { + clks_bool used; + u8 type; + u16 parent; + u16 flags; + const void *data; + u64 size; + char path[CLKS_DISK_PATH_MAX]; +}; + +static u8 *clks_disk_bytes = CLKS_NULL; +static u64 clks_disk_bytes_len = 0ULL; +static u64 clks_disk_sector_total = 0ULL; +static clks_bool clks_disk_ready = CLKS_FALSE; +static clks_bool clks_disk_formatted = CLKS_FALSE; +static clks_bool clks_disk_mounted = CLKS_FALSE; +static clks_bool clks_disk_hw_backed = CLKS_FALSE; +static char clks_disk_mount_path_buf[CLKS_DISK_PATH_MAX]; +static const u8 clks_disk_empty_file_data[1] = {0U}; + +static struct clks_disk_node clks_disk_nodes[CLKS_DISK_MAX_NODES]; +static u16 clks_disk_nodes_used = 0U; + +static u16 clks_disk_read_u16(const u8 *ptr) { + return (u16)((u16)ptr[0] | ((u16)ptr[1] << 8)); +} + +static u32 clks_disk_read_u32(const u8 *ptr) { + return (u32)((u32)ptr[0] | ((u32)ptr[1] << 8) | ((u32)ptr[2] << 16) | ((u32)ptr[3] << 24)); +} + +static u64 clks_disk_read_u64(const u8 *ptr) { + u64 value = 0ULL; + u32 i; + + for (i = 0U; i < 8U; i++) { + value |= ((u64)ptr[i]) << (u64)(i * 8U); + } + + return value; +} + +static void clks_disk_write_u16(u8 *ptr, u16 value) { + ptr[0] = (u8)(value & 0xFFU); + ptr[1] = (u8)((value >> 8) & 0xFFU); +} + +static void clks_disk_write_u32(u8 *ptr, u32 value) { + ptr[0] = (u8)(value & 0xFFU); + ptr[1] = (u8)((value >> 8) & 0xFFU); + ptr[2] = (u8)((value >> 16) & 0xFFU); + ptr[3] = (u8)((value >> 24) & 0xFFU); +} + +static void clks_disk_write_u64(u8 *ptr, u64 value) { + u32 i; + + for (i = 0U; i < 8U; i++) { + ptr[i] = (u8)((value >> (u64)(i * 8U)) & 0xFFULL); + } +} + +#if defined(CLKS_ARCH_X86_64) +static inline void clks_disk_ata_outb(u16 port, u8 value) { + __asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port)); +} + +static inline u8 clks_disk_ata_inb(u16 port) { + u8 value; + __asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static inline void clks_disk_ata_outw(u16 port, u16 value) { + __asm__ volatile("outw %0, %1" : : "a"(value), "Nd"(port)); +} + +static inline u16 clks_disk_ata_inw(u16 port) { + u16 value; + __asm__ volatile("inw %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static void clks_disk_ata_delay_400ns(void) { + (void)clks_disk_ata_inb(CLKS_DISK_ATA_REG_ALT_STATUS); + (void)clks_disk_ata_inb(CLKS_DISK_ATA_REG_ALT_STATUS); + (void)clks_disk_ata_inb(CLKS_DISK_ATA_REG_ALT_STATUS); + (void)clks_disk_ata_inb(CLKS_DISK_ATA_REG_ALT_STATUS); +} + +static clks_bool clks_disk_ata_wait_not_busy(void) { + u32 i; + + for (i = 0U; i < 1000000U; i++) { + u8 status = clks_disk_ata_inb(CLKS_DISK_ATA_REG_STATUS); + + if ((status & CLKS_DISK_ATA_STATUS_BSY) == 0U) { + return CLKS_TRUE; + } + } + + return CLKS_FALSE; +} + +static clks_bool clks_disk_ata_wait_drq_ready(void) { + u32 i; + + for (i = 0U; i < 1000000U; i++) { + u8 status = clks_disk_ata_inb(CLKS_DISK_ATA_REG_STATUS); + + if ((status & CLKS_DISK_ATA_STATUS_BSY) != 0U) { + continue; + } + + if ((status & (CLKS_DISK_ATA_STATUS_ERR | CLKS_DISK_ATA_STATUS_DF)) != 0U) { + return CLKS_FALSE; + } + + if ((status & CLKS_DISK_ATA_STATUS_DRQ) != 0U) { + return CLKS_TRUE; + } + } + + return CLKS_FALSE; +} + +static clks_bool clks_disk_ata_wait_ready_no_drq(void) { + u32 i; + + for (i = 0U; i < 1000000U; i++) { + u8 status = clks_disk_ata_inb(CLKS_DISK_ATA_REG_STATUS); + + if ((status & CLKS_DISK_ATA_STATUS_BSY) != 0U) { + continue; + } + + if ((status & (CLKS_DISK_ATA_STATUS_ERR | CLKS_DISK_ATA_STATUS_DF)) != 0U) { + return CLKS_FALSE; + } + + if ((status & CLKS_DISK_ATA_STATUS_DRDY) != 0U) { + return CLKS_TRUE; + } + } + + return CLKS_FALSE; +} + +static clks_bool clks_disk_ata_identify(u64 *out_sector_total) { + u16 identify_words[256]; + u32 i; + u64 sector_total; + u8 status; + + if (out_sector_total == CLKS_NULL) { + return CLKS_FALSE; + } + + if (clks_disk_ata_wait_not_busy() == CLKS_FALSE) { + return CLKS_FALSE; + } + + clks_disk_ata_outb(CLKS_DISK_ATA_REG_DRIVE, 0xA0U); + clks_disk_ata_delay_400ns(); + + clks_disk_ata_outb(CLKS_DISK_ATA_REG_SECTOR_COUNT, 0U); + clks_disk_ata_outb(CLKS_DISK_ATA_REG_LBA_LOW, 0U); + clks_disk_ata_outb(CLKS_DISK_ATA_REG_LBA_MID, 0U); + clks_disk_ata_outb(CLKS_DISK_ATA_REG_LBA_HIGH, 0U); + clks_disk_ata_outb(CLKS_DISK_ATA_REG_COMMAND, CLKS_DISK_ATA_CMD_IDENTIFY); + + status = clks_disk_ata_inb(CLKS_DISK_ATA_REG_STATUS); + if (status == 0U) { + return CLKS_FALSE; + } + + if (clks_disk_ata_wait_drq_ready() == CLKS_FALSE) { + return CLKS_FALSE; + } + + for (i = 0U; i < 256U; i++) { + identify_words[i] = clks_disk_ata_inw(CLKS_DISK_ATA_REG_DATA); + } + + sector_total = (u64)identify_words[60] | ((u64)identify_words[61] << 16U); + if (sector_total == 0ULL) { + return CLKS_FALSE; + } + + *out_sector_total = sector_total; + return CLKS_TRUE; +} + +static clks_bool clks_disk_ata_read_sector_hw(u64 lba, void *out_sector) { + u8 *out = (u8 *)out_sector; + u32 i; + u8 status; + + if (out_sector == CLKS_NULL || lba > 0x0FFFFFFFULL) { + return CLKS_FALSE; + } + + if (clks_disk_ata_wait_not_busy() == CLKS_FALSE) { + return CLKS_FALSE; + } + + clks_disk_ata_outb(CLKS_DISK_ATA_REG_DRIVE, (u8)(0xE0U | ((u8)((lba >> 24U) & 0x0FU)))); + clks_disk_ata_outb(CLKS_DISK_ATA_REG_SECTOR_COUNT, 1U); + clks_disk_ata_outb(CLKS_DISK_ATA_REG_LBA_LOW, (u8)(lba & 0xFFULL)); + clks_disk_ata_outb(CLKS_DISK_ATA_REG_LBA_MID, (u8)((lba >> 8U) & 0xFFULL)); + clks_disk_ata_outb(CLKS_DISK_ATA_REG_LBA_HIGH, (u8)((lba >> 16U) & 0xFFULL)); + clks_disk_ata_outb(CLKS_DISK_ATA_REG_COMMAND, CLKS_DISK_ATA_CMD_READ_SECTORS); + + if (clks_disk_ata_wait_drq_ready() == CLKS_FALSE) { + return CLKS_FALSE; + } + + for (i = 0U; i < 256U; i++) { + u16 word = clks_disk_ata_inw(CLKS_DISK_ATA_REG_DATA); + out[i * 2U] = (u8)(word & 0x00FFU); + out[i * 2U + 1U] = (u8)((word >> 8U) & 0x00FFU); + } + + clks_disk_ata_delay_400ns(); + status = clks_disk_ata_inb(CLKS_DISK_ATA_REG_STATUS); + if ((status & (CLKS_DISK_ATA_STATUS_ERR | CLKS_DISK_ATA_STATUS_DF)) != 0U) { + return CLKS_FALSE; + } + + return CLKS_TRUE; +} + +static clks_bool clks_disk_ata_write_sector_hw(u64 lba, const void *sector_data) { + const u8 *src = (const u8 *)sector_data; + u32 i; + + if (sector_data == CLKS_NULL || lba > 0x0FFFFFFFULL) { + return CLKS_FALSE; + } + + if (clks_disk_ata_wait_not_busy() == CLKS_FALSE) { + return CLKS_FALSE; + } + + clks_disk_ata_outb(CLKS_DISK_ATA_REG_DRIVE, (u8)(0xE0U | ((u8)((lba >> 24U) & 0x0FU)))); + clks_disk_ata_outb(CLKS_DISK_ATA_REG_SECTOR_COUNT, 1U); + clks_disk_ata_outb(CLKS_DISK_ATA_REG_LBA_LOW, (u8)(lba & 0xFFULL)); + clks_disk_ata_outb(CLKS_DISK_ATA_REG_LBA_MID, (u8)((lba >> 8U) & 0xFFULL)); + clks_disk_ata_outb(CLKS_DISK_ATA_REG_LBA_HIGH, (u8)((lba >> 16U) & 0xFFULL)); + clks_disk_ata_outb(CLKS_DISK_ATA_REG_COMMAND, CLKS_DISK_ATA_CMD_WRITE_SECTORS); + + if (clks_disk_ata_wait_drq_ready() == CLKS_FALSE) { + return CLKS_FALSE; + } + + for (i = 0U; i < 256U; i++) { + u16 word = (u16)src[i * 2U] | ((u16)src[i * 2U + 1U] << 8U); + clks_disk_ata_outw(CLKS_DISK_ATA_REG_DATA, word); + } + + clks_disk_ata_delay_400ns(); + if (clks_disk_ata_wait_ready_no_drq() == CLKS_FALSE) { + return CLKS_FALSE; + } + + return CLKS_TRUE; +} + +static clks_bool clks_disk_ata_cache_flush_hw(void) { + if (clks_disk_ata_wait_not_busy() == CLKS_FALSE) { + return CLKS_FALSE; + } + + clks_disk_ata_outb(CLKS_DISK_ATA_REG_COMMAND, CLKS_DISK_ATA_CMD_CACHE_FLUSH); + return clks_disk_ata_wait_ready_no_drq(); +} +#else +static clks_bool clks_disk_ata_identify(u64 *out_sector_total) { + (void)out_sector_total; + return CLKS_FALSE; +} + +static clks_bool clks_disk_ata_read_sector_hw(u64 lba, void *out_sector) { + (void)lba; + (void)out_sector; + return CLKS_FALSE; +} + +static clks_bool clks_disk_ata_write_sector_hw(u64 lba, const void *sector_data) { + (void)lba; + (void)sector_data; + return CLKS_FALSE; +} + +static clks_bool clks_disk_ata_cache_flush_hw(void) { + return CLKS_FALSE; +} +#endif + +static clks_bool clks_disk_sync_bytes_to_hw(u64 used_bytes) { + u64 sectors_to_sync; + u64 lba; + + if (clks_disk_hw_backed == CLKS_FALSE) { + return CLKS_TRUE; + } + + if (clks_disk_bytes == CLKS_NULL || clks_disk_sector_total == 0ULL) { + return CLKS_FALSE; + } + + if (used_bytes == 0ULL) { + return CLKS_TRUE; + } + + if (used_bytes > clks_disk_bytes_len) { + used_bytes = clks_disk_bytes_len; + } + + sectors_to_sync = (used_bytes + (CLKS_DISK_SECTOR_SIZE - 1ULL)) / CLKS_DISK_SECTOR_SIZE; + if (sectors_to_sync > clks_disk_sector_total) { + sectors_to_sync = clks_disk_sector_total; + } + + for (lba = 0ULL; lba < sectors_to_sync; lba++) { + const u8 *sector_ptr = clks_disk_bytes + (usize)(lba * CLKS_DISK_SECTOR_SIZE); + if (clks_disk_ata_write_sector_hw(lba, sector_ptr) == CLKS_FALSE) { + return CLKS_FALSE; + } + } + + if (clks_disk_ata_cache_flush_hw() == CLKS_FALSE) { + return CLKS_FALSE; + } + + return CLKS_TRUE; +} + +static clks_bool clks_disk_bytes_equal(const u8 *left, const u8 *right, usize count) { + usize i; + + if (left == CLKS_NULL || right == CLKS_NULL) { + return CLKS_FALSE; + } + + for (i = 0U; i < count; i++) { + if (left[i] != right[i]) { + return CLKS_FALSE; + } + } + + return CLKS_TRUE; +} + +static clks_bool clks_disk_text_prefix_equals(const char *text, const char *prefix, usize prefix_len) { + usize i; + + if (text == CLKS_NULL || prefix == CLKS_NULL) { + return CLKS_FALSE; + } + + for (i = 0U; i < prefix_len; i++) { + if (text[i] != prefix[i]) { + return CLKS_FALSE; + } + } + + return CLKS_TRUE; +} + +static void clks_disk_copy_text(char *dst, usize dst_size, const char *src) { + usize i = 0U; + + if (dst == CLKS_NULL || dst_size == 0U) { + return; + } + + if (src == CLKS_NULL) { + dst[0] = '\0'; + return; + } + + while (src[i] != '\0' && i + 1U < dst_size) { + dst[i] = src[i]; + i++; + } + + dst[i] = '\0'; +} + +static clks_bool clks_disk_normalize_absolute_path(const char *path, char *out_path, usize out_size) { + usize in_pos = 0U; + usize out_pos = 0U; + + if (path == CLKS_NULL || out_path == CLKS_NULL || out_size < 2U) { + return CLKS_FALSE; + } + + if (path[0] != '/') { + return CLKS_FALSE; + } + + out_path[out_pos++] = '/'; + + while (path[in_pos] == '/') { + in_pos++; + } + + while (path[in_pos] != '\0') { + usize comp_start = in_pos; + usize comp_len; + + while (path[in_pos] != '\0' && path[in_pos] != '/') { + in_pos++; + } + + comp_len = in_pos - comp_start; + + if (comp_len == 0U) { + while (path[in_pos] == '/') { + in_pos++; + } + continue; + } + + if (comp_len == 1U && path[comp_start] == '.') { + while (path[in_pos] == '/') { + in_pos++; + } + continue; + } + + if (comp_len == 2U && path[comp_start] == '.' && path[comp_start + 1U] == '.') { + return CLKS_FALSE; + } + + if (out_pos > 1U) { + if (out_pos + 1U >= out_size) { + return CLKS_FALSE; + } + out_path[out_pos++] = '/'; + } + + if (out_pos + comp_len >= out_size) { + return CLKS_FALSE; + } + + clks_memcpy(out_path + out_pos, path + comp_start, comp_len); + out_pos += comp_len; + + while (path[in_pos] == '/') { + in_pos++; + } + } + + out_path[out_pos] = '\0'; + return CLKS_TRUE; +} + +static clks_bool clks_disk_normalize_relative_path(const char *path, char *out_path, usize out_size) { + usize in_pos = 0U; + usize out_pos = 0U; + + if (path == CLKS_NULL || out_path == CLKS_NULL || out_size == 0U) { + return CLKS_FALSE; + } + + if (path[0] == '/') { + return CLKS_FALSE; + } + + while (path[in_pos] == '/') { + in_pos++; + } + + while (path[in_pos] != '\0') { + usize comp_start = in_pos; + usize comp_len; + + while (path[in_pos] != '\0' && path[in_pos] != '/') { + in_pos++; + } + + comp_len = in_pos - comp_start; + + if (comp_len == 0U) { + while (path[in_pos] == '/') { + in_pos++; + } + continue; + } + + if (comp_len == 1U && path[comp_start] == '.') { + while (path[in_pos] == '/') { + in_pos++; + } + continue; + } + + if (comp_len == 2U && path[comp_start] == '.' && path[comp_start + 1U] == '.') { + return CLKS_FALSE; + } + + if (out_pos != 0U) { + if (out_pos + 1U >= out_size) { + return CLKS_FALSE; + } + out_path[out_pos++] = '/'; + } + + if (out_pos + comp_len >= out_size) { + return CLKS_FALSE; + } + + clks_memcpy(out_path + out_pos, path + comp_start, comp_len); + out_pos += comp_len; + + while (path[in_pos] == '/') { + in_pos++; + } + } + + out_path[out_pos] = '\0'; + return CLKS_TRUE; +} + +static clks_bool clks_disk_path_to_relative(const char *path, char *out_relative, usize out_size) { + char normalized[CLKS_DISK_PATH_MAX]; + usize mount_len; + + if (out_relative == CLKS_NULL || out_size == 0U) { + return CLKS_FALSE; + } + + if (clks_disk_mounted == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (clks_disk_normalize_absolute_path(path, normalized, sizeof(normalized)) == CLKS_FALSE) { + return CLKS_FALSE; + } + + mount_len = clks_strlen(clks_disk_mount_path_buf); + + if (clks_strcmp(normalized, clks_disk_mount_path_buf) == 0) { + out_relative[0] = '\0'; + return CLKS_TRUE; + } + + if (mount_len >= clks_strlen(normalized)) { + return CLKS_FALSE; + } + + if (clks_disk_text_prefix_equals(normalized, clks_disk_mount_path_buf, mount_len) == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (normalized[mount_len] != '/') { + return CLKS_FALSE; + } + + return clks_disk_normalize_relative_path(normalized + mount_len + 1U, out_relative, out_size); +} + +static const char *clks_disk_basename(const char *relative_path) { + usize len; + usize i; + + if (relative_path == CLKS_NULL) { + return ""; + } + + len = clks_strlen(relative_path); + + if (len == 0U) { + return ""; + } + + for (i = len; i != 0U; i--) { + if (relative_path[i - 1U] == '/') { + return &relative_path[i]; + } + } + + return relative_path; +} + +static clks_bool clks_disk_split_parent(const char *relative_path, char *parent_out, usize out_size) { + usize len; + usize i; + + if (relative_path == CLKS_NULL || parent_out == CLKS_NULL || out_size == 0U) { + return CLKS_FALSE; + } + + len = clks_strlen(relative_path); + + if (len == 0U) { + parent_out[0] = '\0'; + return CLKS_TRUE; + } + + for (i = len; i != 0U; i--) { + if (relative_path[i - 1U] == '/') { + usize parent_len = i - 1U; + + if (parent_len >= out_size) { + return CLKS_FALSE; + } + + clks_memcpy(parent_out, relative_path, parent_len); + parent_out[parent_len] = '\0'; + return CLKS_TRUE; + } + } + + parent_out[0] = '\0'; + return CLKS_TRUE; +} + +static clks_bool clks_disk_node_has_heap_data(u16 index) { + return ((clks_disk_nodes[index].flags & CLKS_DISK_NODE_FLAG_HEAP_DATA) != 0U) ? CLKS_TRUE : CLKS_FALSE; +} + +static void clks_disk_node_set_heap_data(u16 index, clks_bool value) { + if (value == CLKS_TRUE) { + clks_disk_nodes[index].flags |= CLKS_DISK_NODE_FLAG_HEAP_DATA; + } else { + clks_disk_nodes[index].flags &= (u16)(~CLKS_DISK_NODE_FLAG_HEAP_DATA); + } +} + +static void clks_disk_node_release_heap_data(u16 index) { + if (clks_disk_nodes[index].type != (u8)CLKS_DISK_NODE_FILE) { + return; + } + + if (clks_disk_node_has_heap_data(index) == CLKS_TRUE && clks_disk_nodes[index].data != CLKS_NULL) { + clks_kfree((void *)clks_disk_nodes[index].data); + } + + clks_disk_node_set_heap_data(index, CLKS_FALSE); +} + +static void clks_disk_nodes_reset(void) { + u16 i; + + for (i = 0U; i < clks_disk_nodes_used; i++) { + if (clks_disk_nodes[i].used == CLKS_FALSE) { + continue; + } + + clks_disk_node_release_heap_data(i); + } + + clks_memset(clks_disk_nodes, 0, sizeof(clks_disk_nodes)); + clks_disk_nodes_used = 0U; +} + +static i32 clks_disk_find_node_by_relative(const char *relative_path) { + u16 i; + + for (i = 0U; i < clks_disk_nodes_used; i++) { + if (clks_disk_nodes[i].used == CLKS_FALSE) { + continue; + } + + if (clks_strcmp(clks_disk_nodes[i].path, relative_path) == 0) { + return (i32)i; + } + } + + return -1; +} + +static i32 clks_disk_alloc_slot(void) { + u16 i; + + for (i = 0U; i < clks_disk_nodes_used; i++) { + if (clks_disk_nodes[i].used == CLKS_FALSE) { + return (i32)i; + } + } + + if (clks_disk_nodes_used >= CLKS_DISK_MAX_NODES) { + return -1; + } + + clks_disk_nodes_used++; + return (i32)(clks_disk_nodes_used - 1U); +} + +static i32 clks_disk_create_or_update_node(const char *relative_path, u8 type, u16 parent, const void *data, + u64 size) { + i32 existing; + i32 slot; + usize path_len; + + if (relative_path == CLKS_NULL) { + return -1; + } + + path_len = clks_strlen(relative_path); + + if (path_len >= CLKS_DISK_PATH_MAX) { + return -1; + } + + existing = clks_disk_find_node_by_relative(relative_path); + + if (existing >= 0) { + struct clks_disk_node *node = &clks_disk_nodes[(u16)existing]; + + if (node->type != type) { + return -1; + } + + node->parent = parent; + + if (type == (u8)CLKS_DISK_NODE_FILE) { + node->data = data; + node->size = size; + node->flags = 0U; + } + + node->used = CLKS_TRUE; + return existing; + } + + slot = clks_disk_alloc_slot(); + + if (slot < 0) { + return -1; + } + + clks_disk_nodes[(u16)slot].used = CLKS_TRUE; + clks_disk_nodes[(u16)slot].type = type; + clks_disk_nodes[(u16)slot].parent = parent; + clks_disk_nodes[(u16)slot].flags = 0U; + clks_disk_nodes[(u16)slot].data = (type == (u8)CLKS_DISK_NODE_FILE) ? data : CLKS_NULL; + clks_disk_nodes[(u16)slot].size = (type == (u8)CLKS_DISK_NODE_FILE) ? size : 0ULL; + clks_memcpy(clks_disk_nodes[(u16)slot].path, relative_path, path_len + 1U); + + return slot; +} + +static clks_bool clks_disk_ensure_root(void) { + if (clks_disk_create_or_update_node("", (u8)CLKS_DISK_NODE_DIR, 0U, CLKS_NULL, 0ULL) != 0) { + return CLKS_FALSE; + } + + return CLKS_TRUE; +} + +static clks_bool clks_disk_ensure_dir_hierarchy(const char *relative_dir_path) { + char prefix[CLKS_DISK_PATH_MAX]; + usize cursor = 0U; + usize i = 0U; + u16 current_parent = 0U; + + prefix[0] = '\0'; + + if (relative_dir_path == CLKS_NULL) { + return CLKS_FALSE; + } + + if (relative_dir_path[0] == '\0') { + return CLKS_TRUE; + } + + while (relative_dir_path[i] != '\0') { + usize comp_start = i; + usize comp_len; + i32 node_index; + + while (relative_dir_path[i] != '\0' && relative_dir_path[i] != '/') { + i++; + } + + comp_len = i - comp_start; + + if (comp_len == 0U) { + return CLKS_FALSE; + } + + if (cursor != 0U) { + if (cursor + 1U >= sizeof(prefix)) { + return CLKS_FALSE; + } + prefix[cursor++] = '/'; + } + + if (cursor + comp_len >= sizeof(prefix)) { + return CLKS_FALSE; + } + + clks_memcpy(prefix + cursor, relative_dir_path + comp_start, comp_len); + cursor += comp_len; + prefix[cursor] = '\0'; + + node_index = clks_disk_find_node_by_relative(prefix); + + if (node_index < 0) { + node_index = clks_disk_create_or_update_node(prefix, (u8)CLKS_DISK_NODE_DIR, current_parent, CLKS_NULL, 0ULL); + + if (node_index < 0) { + return CLKS_FALSE; + } + } else if (clks_disk_nodes[(u16)node_index].type != (u8)CLKS_DISK_NODE_DIR) { + return CLKS_FALSE; + } + + current_parent = (u16)node_index; + + if (relative_dir_path[i] == '/') { + i++; + } + } + + return CLKS_TRUE; +} + +static clks_bool clks_disk_build_file_payload(const void *data, u64 size, const void **out_data, + clks_bool *out_heap_owned) { + void *payload; + + if (out_data == CLKS_NULL || out_heap_owned == CLKS_NULL) { + return CLKS_FALSE; + } + + if (size == 0ULL) { + *out_data = (const void *)clks_disk_empty_file_data; + *out_heap_owned = CLKS_FALSE; + return CLKS_TRUE; + } + + if (data == CLKS_NULL) { + return CLKS_FALSE; + } + + payload = clks_kmalloc((usize)size); + + if (payload == CLKS_NULL) { + return CLKS_FALSE; + } + + clks_memcpy(payload, data, (usize)size); + *out_data = (const void *)payload; + *out_heap_owned = CLKS_TRUE; + return CLKS_TRUE; +} + +static clks_bool clks_disk_meta_is_present(void) { + const u8 *header; + + if (clks_disk_ready == CLKS_FALSE || clks_disk_bytes == CLKS_NULL) { + return CLKS_FALSE; + } + + if (clks_disk_bytes_len < (CLKS_DISK_META_OFFSET + CLKS_DISK_META_HEADER_SIZE)) { + return CLKS_FALSE; + } + + header = clks_disk_bytes + (usize)CLKS_DISK_META_OFFSET; + + if (clks_disk_bytes_equal(header, (const u8 *)CLKS_DISK_META_MAGIC, 8U) == CLKS_FALSE) { + return CLKS_FALSE; + } + + return (clks_disk_read_u32(header + 8U) == CLKS_DISK_META_VERSION) ? CLKS_TRUE : CLKS_FALSE; +} + +static clks_bool clks_disk_meta_flush(void) { + u8 *meta_base; + u64 max_entries; + u64 entry_count = 0ULL; + u64 data_cursor = CLKS_DISK_DATA_OFFSET; + u16 i; + + if (clks_disk_ready == CLKS_FALSE || clks_disk_formatted == CLKS_FALSE || clks_disk_bytes == CLKS_NULL) { + return CLKS_FALSE; + } + + if (clks_disk_bytes_len < CLKS_DISK_DATA_OFFSET) { + return CLKS_FALSE; + } + + meta_base = clks_disk_bytes + (usize)CLKS_DISK_META_OFFSET; + clks_memset(meta_base, 0, (usize)CLKS_DISK_META_SIZE); + + max_entries = (CLKS_DISK_META_SIZE - (u64)CLKS_DISK_META_HEADER_SIZE) / (u64)CLKS_DISK_META_ENTRY_SIZE; + + for (i = 0U; i < clks_disk_nodes_used; i++) { + struct clks_disk_node *node = &clks_disk_nodes[i]; + u8 *entry_ptr; + + if (node->used == CLKS_FALSE || node->path[0] == '\0') { + continue; + } + + if (entry_count >= max_entries) { + return CLKS_FALSE; + } + + entry_ptr = meta_base + CLKS_DISK_META_HEADER_SIZE + (usize)(entry_count * (u64)CLKS_DISK_META_ENTRY_SIZE); + entry_ptr[0] = node->type; + clks_disk_write_u64(entry_ptr + 4U, node->size); + + if (node->type == (u8)CLKS_DISK_NODE_FILE && node->size > 0ULL) { + if (node->data == CLKS_NULL) { + return CLKS_FALSE; + } + + if (data_cursor + node->size < data_cursor || data_cursor + node->size > clks_disk_bytes_len) { + return CLKS_FALSE; + } + + clks_disk_write_u64(entry_ptr + 12U, data_cursor); + clks_memcpy(clks_disk_bytes + (usize)data_cursor, node->data, (usize)node->size); + data_cursor += node->size; + } else { + clks_disk_write_u64(entry_ptr + 12U, 0ULL); + } + + clks_disk_copy_text((char *)(entry_ptr + 20U), CLKS_DISK_PATH_MAX, node->path); + entry_count++; + } + + clks_memcpy(meta_base, (const void *)CLKS_DISK_META_MAGIC, 8U); + clks_disk_write_u32(meta_base + 8U, CLKS_DISK_META_VERSION); + clks_disk_write_u32(meta_base + 12U, (u32)entry_count); + clks_disk_write_u64(meta_base + 16U, data_cursor - CLKS_DISK_DATA_OFFSET); + + if (clks_disk_sync_bytes_to_hw(data_cursor) == CLKS_FALSE) { + return CLKS_FALSE; + } + + return CLKS_TRUE; +} + +static clks_bool clks_disk_meta_load(void) { + const u8 *meta_base; + u64 entry_count; + u64 max_entries; + u64 i; + + if (clks_disk_meta_is_present() == CLKS_FALSE) { + return CLKS_FALSE; + } + + meta_base = clks_disk_bytes + (usize)CLKS_DISK_META_OFFSET; + entry_count = (u64)clks_disk_read_u32(meta_base + 12U); + max_entries = (CLKS_DISK_META_SIZE - (u64)CLKS_DISK_META_HEADER_SIZE) / (u64)CLKS_DISK_META_ENTRY_SIZE; + + if (entry_count > max_entries) { + return CLKS_FALSE; + } + + clks_disk_nodes_reset(); + + if (clks_disk_ensure_root() == CLKS_FALSE) { + return CLKS_FALSE; + } + + for (i = 0ULL; i < entry_count; i++) { + const u8 *entry_ptr = + meta_base + CLKS_DISK_META_HEADER_SIZE + (usize)(i * (u64)CLKS_DISK_META_ENTRY_SIZE); + u8 type = entry_ptr[0]; + u64 size = clks_disk_read_u64(entry_ptr + 4U); + u64 data_offset = clks_disk_read_u64(entry_ptr + 12U); + char path[CLKS_DISK_PATH_MAX]; + char normalized_path[CLKS_DISK_PATH_MAX]; + char parent[CLKS_DISK_PATH_MAX]; + i32 parent_index; + const void *payload_data = (const void *)clks_disk_empty_file_data; + clks_bool payload_heap_owned = CLKS_FALSE; + i32 node_index; + + clks_memset(path, 0, sizeof(path)); + clks_memcpy(path, entry_ptr + 20U, CLKS_DISK_PATH_MAX - 1U); + + if (clks_disk_normalize_relative_path(path, normalized_path, sizeof(normalized_path)) == CLKS_FALSE) { + continue; + } + + if (normalized_path[0] == '\0') { + continue; + } + + if (type == (u8)CLKS_DISK_NODE_DIR) { + (void)clks_disk_ensure_dir_hierarchy(normalized_path); + continue; + } + + if (type != (u8)CLKS_DISK_NODE_FILE) { + continue; + } + + if (clks_disk_split_parent(normalized_path, parent, sizeof(parent)) == CLKS_FALSE) { + continue; + } + + if (clks_disk_ensure_dir_hierarchy(parent) == CLKS_FALSE) { + continue; + } + + parent_index = clks_disk_find_node_by_relative(parent); + + if (parent_index < 0 || clks_disk_nodes[(u16)parent_index].type != (u8)CLKS_DISK_NODE_DIR) { + continue; + } + + if (size == 0ULL) { + payload_data = (const void *)clks_disk_empty_file_data; + payload_heap_owned = CLKS_FALSE; + } else { + void *payload; + + if (data_offset < CLKS_DISK_DATA_OFFSET || data_offset + size < data_offset || + data_offset + size > clks_disk_bytes_len) { + continue; + } + + payload = clks_kmalloc((usize)size); + + if (payload == CLKS_NULL) { + continue; + } + + clks_memcpy(payload, clks_disk_bytes + (usize)data_offset, (usize)size); + payload_data = payload; + payload_heap_owned = CLKS_TRUE; + } + + node_index = clks_disk_create_or_update_node(normalized_path, (u8)CLKS_DISK_NODE_FILE, (u16)parent_index, + payload_data, size); + + if (node_index < 0) { + if (payload_heap_owned == CLKS_TRUE) { + clks_kfree((void *)payload_data); + } + continue; + } + + clks_disk_node_set_heap_data((u16)node_index, payload_heap_owned); + } + + return CLKS_TRUE; +} + +static clks_bool clks_disk_detect_fat32(void) { + const u8 *boot; + const u8 fat32_sig[CLKS_DISK_FAT32_TYPE_LEN] = {'F', 'A', 'T', '3', '2'}; + + if (clks_disk_ready == CLKS_FALSE || clks_disk_bytes == CLKS_NULL) { + return CLKS_FALSE; + } + + if (clks_disk_bytes_len < CLKS_DISK_SECTOR_SIZE) { + return CLKS_FALSE; + } + + boot = clks_disk_bytes; + + if (boot[CLKS_DISK_FAT32_BOOT_SIG_OFFSET] != 0x55U || boot[CLKS_DISK_FAT32_BOOT_SIG_OFFSET + 1U] != 0xAAU) { + return CLKS_FALSE; + } + + if (clks_disk_read_u16(boot + 11U) != (u16)CLKS_DISK_SECTOR_SIZE) { + return CLKS_FALSE; + } + + return clks_disk_bytes_equal(boot + CLKS_DISK_FAT32_TYPE_OFFSET, fat32_sig, CLKS_DISK_FAT32_TYPE_LEN); +} + +static void clks_disk_label_to_boot_field(char *out_label, const char *label) { + usize i = 0U; + + for (i = 0U; i < 11U; i++) { + out_label[i] = ' '; + } + + if (label == CLKS_NULL || label[0] == '\0') { + const char default_label[11] = {'C', 'L', 'E', 'O', 'N', 'O', 'S', 'D', 'I', 'S', 'K'}; + for (i = 0U; i < 11U; i++) { + out_label[i] = default_label[i]; + } + return; + } + + i = 0U; + while (label[i] != '\0' && i < 11U) { + char ch = label[i]; + if (ch >= 'a' && ch <= 'z') { + ch = (char)(ch - ('a' - 'A')); + } + out_label[i] = ch; + i++; + } +} + +static clks_bool clks_disk_write_fat32_boot_sector(const char *label) { + u8 *boot = clks_disk_bytes; + u8 *fsinfo; + u8 *backup_boot; + u64 total_sectors = clks_disk_sector_total; + u8 sectors_per_cluster = 1U; + u16 reserved = 64U; + u8 fats = 2U; + u32 fat_sectors = 1U; + u32 iter; + char label_field[11]; + + if (total_sectors < 8192ULL) { + return CLKS_FALSE; + } + + if (total_sectors > 262144ULL) { + sectors_per_cluster = 8U; + } else if (total_sectors > 131072ULL) { + sectors_per_cluster = 4U; + } else { + sectors_per_cluster = 1U; + } + + for (iter = 0U; iter < 8U; iter++) { + u64 data_sectors; + u64 cluster_count; + u64 next_fat; + + if (total_sectors <= (u64)reserved + ((u64)fats * (u64)fat_sectors)) { + return CLKS_FALSE; + } + + data_sectors = total_sectors - (u64)reserved - ((u64)fats * (u64)fat_sectors); + cluster_count = data_sectors / (u64)sectors_per_cluster; + next_fat = ((cluster_count + 2ULL) * 4ULL + (CLKS_DISK_SECTOR_SIZE - 1ULL)) / CLKS_DISK_SECTOR_SIZE; + + if (next_fat > 0xFFFFFFFFULL) { + return CLKS_FALSE; + } + + if ((u32)next_fat == fat_sectors) { + break; + } + + fat_sectors = (u32)next_fat; + } + + if (fat_sectors == 0U) { + return CLKS_FALSE; + } + + clks_memset(boot, 0, CLKS_DISK_SECTOR_SIZE); + boot[0] = 0xEBU; + boot[1] = 0x58U; + boot[2] = 0x90U; + clks_memcpy(boot + 3U, "MSDOS5.0", 8U); + clks_disk_write_u16(boot + 11U, (u16)CLKS_DISK_SECTOR_SIZE); + boot[13] = sectors_per_cluster; + clks_disk_write_u16(boot + 14U, reserved); + boot[16] = fats; + clks_disk_write_u16(boot + 17U, 0U); + clks_disk_write_u16(boot + 19U, 0U); + boot[21] = 0xF8U; + clks_disk_write_u16(boot + 22U, 0U); + clks_disk_write_u16(boot + 24U, 63U); + clks_disk_write_u16(boot + 26U, 255U); + clks_disk_write_u32(boot + 28U, 0U); + clks_disk_write_u32(boot + 32U, (u32)total_sectors); + clks_disk_write_u32(boot + 36U, fat_sectors); + clks_disk_write_u16(boot + 40U, 0U); + clks_disk_write_u16(boot + 42U, 0U); + clks_disk_write_u32(boot + 44U, 2U); + clks_disk_write_u16(boot + 48U, 1U); + clks_disk_write_u16(boot + 50U, 6U); + boot[64] = 0x80U; + boot[66] = 0x29U; + clks_disk_write_u32(boot + 67U, 0x434C4B53U); + clks_disk_label_to_boot_field(label_field, label); + clks_memcpy(boot + 71U, label_field, 11U); + clks_memcpy(boot + 82U, "FAT32 ", 8U); + boot[CLKS_DISK_FAT32_BOOT_SIG_OFFSET] = 0x55U; + boot[CLKS_DISK_FAT32_BOOT_SIG_OFFSET + 1U] = 0xAAU; + + fsinfo = clks_disk_bytes + (usize)CLKS_DISK_SECTOR_SIZE; + clks_memset(fsinfo, 0, CLKS_DISK_SECTOR_SIZE); + clks_disk_write_u32(fsinfo + 0U, 0x41615252U); + clks_disk_write_u32(fsinfo + 484U, 0x61417272U); + clks_disk_write_u32(fsinfo + 488U, 0xFFFFFFFFU); + clks_disk_write_u32(fsinfo + 492U, 0xFFFFFFFFU); + fsinfo[CLKS_DISK_FAT32_BOOT_SIG_OFFSET] = 0x55U; + fsinfo[CLKS_DISK_FAT32_BOOT_SIG_OFFSET + 1U] = 0xAAU; + + backup_boot = clks_disk_bytes + (usize)(6ULL * CLKS_DISK_SECTOR_SIZE); + clks_memcpy(backup_boot, boot, CLKS_DISK_SECTOR_SIZE); + + { + u64 fat_base = (u64)reserved * CLKS_DISK_SECTOR_SIZE; + u32 fat_index; + + for (fat_index = 0U; fat_index < fats; fat_index++) { + u64 offset = fat_base + ((u64)fat_index * (u64)fat_sectors * CLKS_DISK_SECTOR_SIZE); + u8 *fat_ptr; + + if (offset + 12ULL > clks_disk_bytes_len) { + return CLKS_FALSE; + } + + fat_ptr = clks_disk_bytes + (usize)offset; + clks_disk_write_u32(fat_ptr + 0U, 0x0FFFFFF8U); + clks_disk_write_u32(fat_ptr + 4U, 0xFFFFFFFFU); + clks_disk_write_u32(fat_ptr + 8U, 0x0FFFFFFFU); + } + } + + return CLKS_TRUE; +} + +void clks_disk_init(void) { + u64 detected_sectors = 0ULL; + u64 cache_bytes; + u64 alloc_bytes; + u64 lba; + + clks_disk_ready = CLKS_FALSE; + clks_disk_formatted = CLKS_FALSE; + clks_disk_mounted = CLKS_FALSE; + clks_disk_hw_backed = CLKS_FALSE; + clks_disk_bytes = CLKS_NULL; + clks_disk_bytes_len = 0ULL; + clks_disk_sector_total = 0ULL; + clks_disk_mount_path_buf[0] = '\0'; + clks_disk_nodes_reset(); + + if (clks_disk_ata_identify(&detected_sectors) == CLKS_FALSE || detected_sectors == 0ULL) { + clks_log(CLKS_LOG_WARN, "DISK", "NO ATA DISK DETECTED (CHECK QEMU -DRIVE)"); + return; + } + + cache_bytes = detected_sectors * CLKS_DISK_SECTOR_SIZE; + if (cache_bytes / CLKS_DISK_SECTOR_SIZE != detected_sectors) { + clks_log(CLKS_LOG_WARN, "DISK", "DISK SIZE OVERFLOW"); + return; + } + + if (cache_bytes < CLKS_DISK_MIN_BYTES) { + clks_log(CLKS_LOG_WARN, "DISK", "ATA DISK TOO SMALL"); + clks_log_hex(CLKS_LOG_WARN, "DISK", "BYTES", cache_bytes); + return; + } + + if (cache_bytes > CLKS_DISK_CACHE_MAX_BYTES) { + cache_bytes = CLKS_DISK_CACHE_MAX_BYTES; + clks_log(CLKS_LOG_WARN, "DISK", "DISK CACHE TRUNCATED"); + } + + alloc_bytes = cache_bytes; + while (alloc_bytes >= CLKS_DISK_MIN_BYTES) { + clks_disk_bytes = (u8 *)clks_kmalloc((usize)alloc_bytes); + if (clks_disk_bytes != CLKS_NULL) { + cache_bytes = alloc_bytes; + break; + } + + alloc_bytes /= 2ULL; + alloc_bytes -= (alloc_bytes % CLKS_DISK_SECTOR_SIZE); + } + + if (clks_disk_bytes == CLKS_NULL) { + clks_log(CLKS_LOG_WARN, "DISK", "DISK BACKEND ALLOCATION FAILED"); + return; + } + + detected_sectors = cache_bytes / CLKS_DISK_SECTOR_SIZE; + + for (lba = 0ULL; lba < detected_sectors; lba++) { + u8 *sector_ptr = clks_disk_bytes + (usize)(lba * CLKS_DISK_SECTOR_SIZE); + if (clks_disk_ata_read_sector_hw(lba, sector_ptr) == CLKS_FALSE) { + clks_log(CLKS_LOG_WARN, "DISK", "ATA READ FAILED DURING CACHE LOAD"); + clks_log_hex(CLKS_LOG_WARN, "DISK", "LBA", lba); + clks_kfree(clks_disk_bytes); + clks_disk_bytes = CLKS_NULL; + return; + } + } + + clks_disk_bytes_len = cache_bytes; + clks_disk_sector_total = detected_sectors; + clks_disk_hw_backed = CLKS_TRUE; + clks_disk_ready = CLKS_TRUE; + clks_disk_formatted = clks_disk_detect_fat32(); + + if (clks_disk_ensure_root() == CLKS_FALSE) { + clks_log(CLKS_LOG_ERROR, "DISK", "FAILED TO INIT ROOT NODE"); + clks_disk_ready = CLKS_FALSE; + return; + } + + if (clks_disk_formatted == CLKS_TRUE) { + if (clks_disk_meta_load() == CLKS_FALSE) { + clks_log(CLKS_LOG_WARN, "DISK", "FAT32 DETECTED, META MISSING; RESET TO EMPTY"); + clks_disk_nodes_reset(); + (void)clks_disk_ensure_root(); + (void)clks_disk_meta_flush(); + } + } + + (void)clks_disk_mount("/temp/disk"); + clks_log(CLKS_LOG_INFO, "DISK", "DISK BACKEND ONLINE"); + clks_log_hex(CLKS_LOG_INFO, "DISK", "BYTES", clks_disk_bytes_len); + clks_log_hex(CLKS_LOG_INFO, "DISK", "SECTORS", clks_disk_sector_total); + clks_log_hex(CLKS_LOG_INFO, "DISK", "FAT32", (clks_disk_formatted == CLKS_TRUE) ? 1ULL : 0ULL); + clks_log_hex(CLKS_LOG_INFO, "DISK", "HW_BACKED", (clks_disk_hw_backed == CLKS_TRUE) ? 1ULL : 0ULL); +} + +clks_bool clks_disk_present(void) { + return clks_disk_ready; +} + +u64 clks_disk_size_bytes(void) { + return (clks_disk_ready == CLKS_TRUE) ? clks_disk_bytes_len : 0ULL; +} + +u64 clks_disk_sector_count(void) { + return (clks_disk_ready == CLKS_TRUE) ? clks_disk_sector_total : 0ULL; +} + +clks_bool clks_disk_read_sector(u64 lba, void *out_sector) { + if (clks_disk_ready == CLKS_FALSE || out_sector == CLKS_NULL) { + return CLKS_FALSE; + } + + if (lba >= clks_disk_sector_total) { + return CLKS_FALSE; + } + + if (clks_disk_hw_backed == CLKS_TRUE) { + if (clks_disk_ata_read_sector_hw(lba, out_sector) == CLKS_FALSE) { + return CLKS_FALSE; + } + clks_memcpy(clks_disk_bytes + (usize)(lba * CLKS_DISK_SECTOR_SIZE), out_sector, (usize)CLKS_DISK_SECTOR_SIZE); + return CLKS_TRUE; + } + + clks_memcpy(out_sector, clks_disk_bytes + (usize)(lba * CLKS_DISK_SECTOR_SIZE), (usize)CLKS_DISK_SECTOR_SIZE); + return CLKS_TRUE; +} + +clks_bool clks_disk_write_sector(u64 lba, const void *sector_data) { + if (clks_disk_ready == CLKS_FALSE || sector_data == CLKS_NULL) { + return CLKS_FALSE; + } + + if (lba >= clks_disk_sector_total) { + return CLKS_FALSE; + } + + clks_memcpy(clks_disk_bytes + (usize)(lba * CLKS_DISK_SECTOR_SIZE), sector_data, (usize)CLKS_DISK_SECTOR_SIZE); + + if (clks_disk_hw_backed == CLKS_TRUE) { + if (clks_disk_ata_write_sector_hw(lba, sector_data) == CLKS_FALSE) { + return CLKS_FALSE; + } + if (clks_disk_ata_cache_flush_hw() == CLKS_FALSE) { + return CLKS_FALSE; + } + } + + return CLKS_TRUE; +} + +clks_bool clks_disk_is_formatted_fat32(void) { + return (clks_disk_ready == CLKS_TRUE && clks_disk_formatted == CLKS_TRUE) ? CLKS_TRUE : CLKS_FALSE; +} + +clks_bool clks_disk_format_fat32(const char *label) { + if (clks_disk_ready == CLKS_FALSE || clks_disk_bytes == CLKS_NULL) { + return CLKS_FALSE; + } + + clks_memset(clks_disk_bytes, 0, (usize)clks_disk_bytes_len); + + if (clks_disk_sync_bytes_to_hw(clks_disk_bytes_len) == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (clks_disk_write_fat32_boot_sector(label) == CLKS_FALSE) { + return CLKS_FALSE; + } + + clks_disk_nodes_reset(); + + if (clks_disk_ensure_root() == CLKS_FALSE) { + return CLKS_FALSE; + } + + clks_disk_formatted = CLKS_TRUE; + + if (clks_disk_meta_flush() == CLKS_FALSE) { + clks_disk_formatted = CLKS_FALSE; + return CLKS_FALSE; + } + + clks_log(CLKS_LOG_INFO, "DISK", "FAT32 FORMAT COMPLETE"); + return CLKS_TRUE; +} + +clks_bool clks_disk_mount(const char *mount_path) { + char normalized[CLKS_DISK_PATH_MAX]; + + if (clks_disk_ready == CLKS_FALSE || clks_disk_formatted == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (clks_disk_normalize_absolute_path(mount_path, normalized, sizeof(normalized)) == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (clks_strcmp(normalized, "/") == 0) { + return CLKS_FALSE; + } + + clks_disk_copy_text(clks_disk_mount_path_buf, sizeof(clks_disk_mount_path_buf), normalized); + clks_disk_mounted = CLKS_TRUE; + return CLKS_TRUE; +} + +clks_bool clks_disk_is_mounted(void) { + return (clks_disk_ready == CLKS_TRUE && clks_disk_mounted == CLKS_TRUE) ? CLKS_TRUE : CLKS_FALSE; +} + +const char *clks_disk_mount_path(void) { + if (clks_disk_mounted == CLKS_FALSE) { + return ""; + } + + return clks_disk_mount_path_buf; +} + +clks_bool clks_disk_path_in_mount(const char *path) { + char relative[CLKS_DISK_PATH_MAX]; + return (clks_disk_path_to_relative(path, relative, sizeof(relative)) == CLKS_TRUE) ? CLKS_TRUE : CLKS_FALSE; +} + +clks_bool clks_disk_stat(const char *path, u64 *out_type, u64 *out_size) { + char relative[CLKS_DISK_PATH_MAX]; + i32 node_index; + + if (out_type == CLKS_NULL || out_size == CLKS_NULL || clks_disk_path_to_relative(path, relative, sizeof(relative)) == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (clks_disk_ready == CLKS_FALSE || clks_disk_formatted == CLKS_FALSE) { + return CLKS_FALSE; + } + + node_index = clks_disk_find_node_by_relative(relative); + + if (node_index < 0) { + return CLKS_FALSE; + } + + *out_type = (clks_disk_nodes[(u16)node_index].type == (u8)CLKS_DISK_NODE_DIR) ? CLKS_DISK_NODE_DIR : CLKS_DISK_NODE_FILE; + *out_size = clks_disk_nodes[(u16)node_index].size; + return CLKS_TRUE; +} + +const void *clks_disk_read_all(const char *path, u64 *out_size) { + char relative[CLKS_DISK_PATH_MAX]; + i32 node_index; + + if (clks_disk_path_to_relative(path, relative, sizeof(relative)) == CLKS_FALSE) { + return CLKS_NULL; + } + + if (clks_disk_ready == CLKS_FALSE || clks_disk_formatted == CLKS_FALSE) { + return CLKS_NULL; + } + + node_index = clks_disk_find_node_by_relative(relative); + + if (node_index < 0 || clks_disk_nodes[(u16)node_index].type != (u8)CLKS_DISK_NODE_FILE) { + return CLKS_NULL; + } + + if (out_size != CLKS_NULL) { + *out_size = clks_disk_nodes[(u16)node_index].size; + } + + if (clks_disk_nodes[(u16)node_index].size == 0ULL) { + return (const void *)clks_disk_empty_file_data; + } + + return clks_disk_nodes[(u16)node_index].data; +} + +u64 clks_disk_count_children(const char *dir_path) { + char relative[CLKS_DISK_PATH_MAX]; + i32 dir_index; + u64 count = 0ULL; + u16 i; + + if (clks_disk_path_to_relative(dir_path, relative, sizeof(relative)) == CLKS_FALSE) { + return 0ULL; + } + + if (clks_disk_ready == CLKS_FALSE || clks_disk_formatted == CLKS_FALSE) { + return 0ULL; + } + + dir_index = clks_disk_find_node_by_relative(relative); + + if (dir_index < 0 || clks_disk_nodes[(u16)dir_index].type != (u8)CLKS_DISK_NODE_DIR) { + return 0ULL; + } + + for (i = 0U; i < clks_disk_nodes_used; i++) { + if (clks_disk_nodes[i].used == CLKS_FALSE) { + continue; + } + + if ((u16)dir_index == i) { + continue; + } + + if (clks_disk_nodes[i].parent == (u16)dir_index) { + count++; + } + } + + return count; +} + +clks_bool clks_disk_get_child_name(const char *dir_path, u64 index, char *out_name, usize out_name_size) { + char relative[CLKS_DISK_PATH_MAX]; + i32 dir_index; + u64 current = 0ULL; + u16 i; + + if (out_name == CLKS_NULL || out_name_size == 0U) { + return CLKS_FALSE; + } + + if (clks_disk_path_to_relative(dir_path, relative, sizeof(relative)) == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (clks_disk_ready == CLKS_FALSE || clks_disk_formatted == CLKS_FALSE) { + return CLKS_FALSE; + } + + dir_index = clks_disk_find_node_by_relative(relative); + + if (dir_index < 0 || clks_disk_nodes[(u16)dir_index].type != (u8)CLKS_DISK_NODE_DIR) { + return CLKS_FALSE; + } + + for (i = 0U; i < clks_disk_nodes_used; i++) { + const char *base; + usize base_len; + + if (clks_disk_nodes[i].used == CLKS_FALSE || clks_disk_nodes[i].parent != (u16)dir_index || i == (u16)dir_index) { + continue; + } + + if (current != index) { + current++; + continue; + } + + base = clks_disk_basename(clks_disk_nodes[i].path); + base_len = clks_strlen(base); + + if (base_len + 1U > out_name_size) { + return CLKS_FALSE; + } + + clks_memcpy(out_name, base, base_len + 1U); + return CLKS_TRUE; + } + + return CLKS_FALSE; +} + +clks_bool clks_disk_mkdir(const char *path) { + char relative[CLKS_DISK_PATH_MAX]; + i32 node_index; + + if (clks_disk_path_to_relative(path, relative, sizeof(relative)) == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (clks_disk_ready == CLKS_FALSE || clks_disk_formatted == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (relative[0] == '\0') { + return CLKS_TRUE; + } + + if (clks_disk_ensure_dir_hierarchy(relative) == CLKS_FALSE) { + return CLKS_FALSE; + } + + node_index = clks_disk_find_node_by_relative(relative); + + if (node_index < 0 || clks_disk_nodes[(u16)node_index].type != (u8)CLKS_DISK_NODE_DIR) { + return CLKS_FALSE; + } + + return (clks_disk_meta_flush() == CLKS_TRUE) ? CLKS_TRUE : CLKS_FALSE; +} + +clks_bool clks_disk_write_all(const char *path, const void *data, u64 size) { + char relative[CLKS_DISK_PATH_MAX]; + char parent[CLKS_DISK_PATH_MAX]; + const void *payload_data = CLKS_NULL; + clks_bool payload_heap_owned = CLKS_FALSE; + i32 parent_index; + i32 node_index; + + if (clks_disk_path_to_relative(path, relative, sizeof(relative)) == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (clks_disk_ready == CLKS_FALSE || clks_disk_formatted == CLKS_FALSE || relative[0] == '\0') { + return CLKS_FALSE; + } + + if (clks_disk_split_parent(relative, parent, sizeof(parent)) == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (clks_disk_ensure_dir_hierarchy(parent) == CLKS_FALSE) { + return CLKS_FALSE; + } + + parent_index = clks_disk_find_node_by_relative(parent); + + if (parent_index < 0 || clks_disk_nodes[(u16)parent_index].type != (u8)CLKS_DISK_NODE_DIR) { + return CLKS_FALSE; + } + + node_index = clks_disk_find_node_by_relative(relative); + + if (node_index >= 0 && clks_disk_nodes[(u16)node_index].type != (u8)CLKS_DISK_NODE_FILE) { + return CLKS_FALSE; + } + + if (clks_disk_build_file_payload(data, size, &payload_data, &payload_heap_owned) == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (node_index >= 0) { + clks_disk_node_release_heap_data((u16)node_index); + } + + node_index = clks_disk_create_or_update_node(relative, (u8)CLKS_DISK_NODE_FILE, (u16)parent_index, payload_data, size); + + if (node_index < 0) { + if (payload_heap_owned == CLKS_TRUE) { + clks_kfree((void *)payload_data); + } + return CLKS_FALSE; + } + + clks_disk_node_set_heap_data((u16)node_index, payload_heap_owned); + + if (clks_disk_meta_flush() == CLKS_FALSE) { + return CLKS_FALSE; + } + + return CLKS_TRUE; +} + +clks_bool clks_disk_append(const char *path, const void *data, u64 size) { + char relative[CLKS_DISK_PATH_MAX]; + i32 node_index; + const void *old_data; + u64 old_size; + u64 new_size; + void *merged; + + if (clks_disk_path_to_relative(path, relative, sizeof(relative)) == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (clks_disk_ready == CLKS_FALSE || clks_disk_formatted == CLKS_FALSE || relative[0] == '\0') { + return CLKS_FALSE; + } + + if (size > 0ULL && data == CLKS_NULL) { + return CLKS_FALSE; + } + + node_index = clks_disk_find_node_by_relative(relative); + + if (node_index < 0) { + return clks_disk_write_all(path, data, size); + } + + if (clks_disk_nodes[(u16)node_index].type != (u8)CLKS_DISK_NODE_FILE) { + return CLKS_FALSE; + } + + old_data = clks_disk_nodes[(u16)node_index].data; + old_size = clks_disk_nodes[(u16)node_index].size; + + if (old_size > (0xFFFFFFFFFFFFFFFFULL - size)) { + return CLKS_FALSE; + } + + new_size = old_size + size; + + if (new_size == 0ULL) { + return clks_disk_write_all(path, clks_disk_empty_file_data, 0ULL); + } + + merged = clks_kmalloc((usize)new_size); + + if (merged == CLKS_NULL) { + return CLKS_FALSE; + } + + if (old_size > 0ULL && old_data != CLKS_NULL) { + clks_memcpy(merged, old_data, (usize)old_size); + } + + if (size > 0ULL) { + clks_memcpy((u8 *)merged + (usize)old_size, data, (usize)size); + } + + if (clks_disk_write_all(path, merged, new_size) == CLKS_FALSE) { + clks_kfree(merged); + return CLKS_FALSE; + } + + clks_kfree(merged); + return CLKS_TRUE; +} + +clks_bool clks_disk_remove(const char *path) { + char relative[CLKS_DISK_PATH_MAX]; + i32 node_index; + u16 i; + + if (clks_disk_path_to_relative(path, relative, sizeof(relative)) == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (clks_disk_ready == CLKS_FALSE || clks_disk_formatted == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (relative[0] == '\0') { + return CLKS_FALSE; + } + + node_index = clks_disk_find_node_by_relative(relative); + + if (node_index < 0) { + return CLKS_FALSE; + } + + if (clks_disk_nodes[(u16)node_index].type == (u8)CLKS_DISK_NODE_DIR) { + for (i = 0U; i < clks_disk_nodes_used; i++) { + if (clks_disk_nodes[i].used == CLKS_FALSE) { + continue; + } + + if (clks_disk_nodes[i].parent == (u16)node_index) { + return CLKS_FALSE; + } + } + } + + clks_disk_node_release_heap_data((u16)node_index); + clks_memset(&clks_disk_nodes[(u16)node_index], 0, sizeof(clks_disk_nodes[(u16)node_index])); + + if (clks_disk_meta_flush() == CLKS_FALSE) { + return CLKS_FALSE; + } + + return CLKS_TRUE; +} + +u64 clks_disk_node_count(void) { + u16 i; + u64 used = 0ULL; + + if (clks_disk_ready == CLKS_FALSE || clks_disk_formatted == CLKS_FALSE) { + return 0ULL; + } + + for (i = 0U; i < clks_disk_nodes_used; i++) { + if (clks_disk_nodes[i].used == CLKS_TRUE) { + used++; + } + } + + return used; +} diff --git a/clks/kernel/storage/fs.c b/clks/kernel/storage/fs.c index 4493d23..959fc92 100644 --- a/clks/kernel/storage/fs.c +++ b/clks/kernel/storage/fs.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -515,6 +516,14 @@ void clks_fs_init(void) { clks_log_hex(CLKS_LOG_INFO, "FS", "NODE_COUNT", (u64)clks_fs_nodes_used); clks_log_hex(CLKS_LOG_INFO, "FS", "FILE_COUNT", stats.file_count); + clks_disk_init(); + if (clks_disk_present() == CLKS_TRUE) { + clks_log_hex(CLKS_LOG_INFO, "FS", "DISK_BYTES", clks_disk_size_bytes()); + clks_log_hex(CLKS_LOG_INFO, "FS", "DISK_FAT32", (clks_disk_is_formatted_fat32() == CLKS_TRUE) ? 1ULL : 0ULL); + } else { + clks_log(CLKS_LOG_WARN, "FS", "DISK BACKEND NOT PRESENT"); + } + if (clks_fs_require_directory("/system") == CLKS_FALSE) { return; } @@ -545,11 +554,23 @@ clks_bool clks_fs_is_ready(void) { clks_bool clks_fs_stat(const char *path, struct clks_fs_node_info *out_info) { i32 node_index; + u64 disk_type = 0ULL; + u64 disk_size = 0ULL; if (clks_fs_ready == CLKS_FALSE || out_info == CLKS_NULL) { return CLKS_FALSE; } + if (clks_disk_path_in_mount(path) == CLKS_TRUE) { + if (clks_disk_stat(path, &disk_type, &disk_size) == CLKS_FALSE) { + return CLKS_FALSE; + } + + out_info->type = (disk_type == CLKS_DISK_NODE_DIR) ? CLKS_FS_NODE_DIR : CLKS_FS_NODE_FILE; + out_info->size = disk_size; + return CLKS_TRUE; + } + node_index = clks_fs_find_node_by_external(path); if (node_index < 0) { @@ -563,11 +584,17 @@ clks_bool clks_fs_stat(const char *path, struct clks_fs_node_info *out_info) { const void *clks_fs_read_all(const char *path, u64 *out_size) { i32 node_index; + const void *disk_data; if (clks_fs_ready == CLKS_FALSE) { return CLKS_NULL; } + if (clks_disk_path_in_mount(path) == CLKS_TRUE) { + disk_data = clks_disk_read_all(path, out_size); + return disk_data; + } + node_index = clks_fs_find_node_by_external(path); if (node_index < 0) { @@ -598,6 +625,10 @@ u64 clks_fs_count_children(const char *dir_path) { return 0ULL; } + if (clks_disk_path_in_mount(dir_path) == CLKS_TRUE) { + return clks_disk_count_children(dir_path); + } + dir_index = clks_fs_find_node_by_external(dir_path); if (dir_index < 0) { @@ -634,6 +665,10 @@ clks_bool clks_fs_get_child_name(const char *dir_path, u64 index, char *out_name return CLKS_FALSE; } + if (clks_disk_path_in_mount(dir_path) == CLKS_TRUE) { + return clks_disk_get_child_name(dir_path, index, out_name, out_name_size); + } + dir_index = clks_fs_find_node_by_external(dir_path); if (dir_index < 0 || clks_fs_nodes[(u16)dir_index].type != CLKS_FS_NODE_DIR) { @@ -683,6 +718,10 @@ clks_bool clks_fs_mkdir(const char *path) { return CLKS_FALSE; } + if (clks_disk_path_in_mount(path) == CLKS_TRUE) { + return clks_disk_mkdir(path); + } + if (clks_fs_normalize_external_path(path, internal, sizeof(internal)) == CLKS_FALSE) { return CLKS_FALSE; } @@ -720,6 +759,10 @@ clks_bool clks_fs_write_all(const char *path, const void *data, u64 size) { return CLKS_FALSE; } + if (clks_disk_path_in_mount(path) == CLKS_TRUE) { + return clks_disk_write_all(path, data, size); + } + if (clks_fs_normalize_external_path(path, internal, sizeof(internal)) == CLKS_FALSE) { return CLKS_FALSE; } @@ -785,6 +828,10 @@ clks_bool clks_fs_append(const char *path, const void *data, u64 size) { return CLKS_FALSE; } + if (clks_disk_path_in_mount(path) == CLKS_TRUE) { + return clks_disk_append(path, data, size); + } + if (size > 0ULL && data == CLKS_NULL) { return CLKS_FALSE; } @@ -851,6 +898,10 @@ clks_bool clks_fs_remove(const char *path) { return CLKS_FALSE; } + if (clks_disk_path_in_mount(path) == CLKS_TRUE) { + return clks_disk_remove(path); + } + if (clks_fs_normalize_external_path(path, internal, sizeof(internal)) == CLKS_FALSE) { return CLKS_FALSE; } @@ -908,5 +959,9 @@ u64 clks_fs_node_count(void) { } } + if (clks_disk_is_mounted() == CLKS_TRUE) { + used += clks_disk_node_count(); + } + return used; } diff --git a/cmake/ensure_disk_image.cmake b/cmake/ensure_disk_image.cmake new file mode 100644 index 0000000..3a3e85b --- /dev/null +++ b/cmake/ensure_disk_image.cmake @@ -0,0 +1,72 @@ +if(NOT DEFINED DISK_IMAGE OR "${DISK_IMAGE}" STREQUAL "") + message(FATAL_ERROR "ensure_disk_image: DISK_IMAGE is required") +endif() + +if(NOT DEFINED DISK_MB OR "${DISK_MB}" STREQUAL "") + set(DISK_MB 64) +endif() + +math(EXPR _disk_mb_int "${DISK_MB} + 0") +if(_disk_mb_int LESS 4) + set(_disk_mb_int 4) +endif() + +math(EXPR _disk_bytes "${_disk_mb_int} * 1024 * 1024") +get_filename_component(_disk_dir "${DISK_IMAGE}" DIRECTORY) +if(NOT EXISTS "${_disk_dir}") + file(MAKE_DIRECTORY "${_disk_dir}") +endif() + +set(_need_create TRUE) +if(EXISTS "${DISK_IMAGE}") + file(SIZE "${DISK_IMAGE}" _disk_size_now) + if(_disk_size_now GREATER_EQUAL _disk_bytes) + set(_need_create FALSE) + endif() +endif() + +if(_need_create) + find_program(_python_exec NAMES python3 python py) + if(_python_exec) + execute_process( + COMMAND "${_python_exec}" "-c" "import os,sys; p=sys.argv[1]; sz=int(sys.argv[2]); d=os.path.dirname(p); d and os.makedirs(d, exist_ok=True); f=open(p,'ab'); f.truncate(sz); f.close()" + "${DISK_IMAGE}" "${_disk_bytes}" + RESULT_VARIABLE _python_result + OUTPUT_QUIET + ERROR_QUIET + ) + if(_python_result EQUAL 0) + set(_need_create FALSE) + endif() + endif() +endif() + +if(_need_create) + find_program(_dd_exec NAMES dd) + if(_dd_exec) + execute_process( + COMMAND "${_dd_exec}" "if=/dev/zero" "of=${DISK_IMAGE}" "bs=1M" "count=${_disk_mb_int}" + RESULT_VARIABLE _dd_result + OUTPUT_QUIET + ERROR_QUIET + ) + if(_dd_result EQUAL 0) + set(_need_create FALSE) + endif() + endif() +endif() + +if(_need_create) + message(FATAL_ERROR "ensure_disk_image: failed to create disk image '${DISK_IMAGE}' (${_disk_mb_int}MB)") +endif() + +if(NOT EXISTS "${DISK_IMAGE}") + message(FATAL_ERROR "ensure_disk_image: disk image missing after create '${DISK_IMAGE}'") +endif() + +file(SIZE "${DISK_IMAGE}" _disk_size_final) +if(_disk_size_final LESS _disk_bytes) + message(FATAL_ERROR "ensure_disk_image: disk image too small (${_disk_size_final} bytes), expected >= ${_disk_bytes}") +endif() + +message(STATUS "ensure_disk_image: ready '${DISK_IMAGE}' (${_disk_size_final} bytes)") diff --git a/configs/limine.conf b/configs/limine.conf index 92c2b76..cb800f5 100644 --- a/configs/limine.conf +++ b/configs/limine.conf @@ -4,4 +4,4 @@ verbose: yes /CLeonOS protocol: limine kernel_path: boot():/boot/clks_kernel.elf -module_path: boot():/boot/cleonos_ramdisk.tar \ No newline at end of file +module_path: boot():/boot/cleonos_ramdisk.tar diff --git a/docs/syscall.md b/docs/syscall.md index 786c54c..e0a6858 100644 --- a/docs/syscall.md +++ b/docs/syscall.md @@ -73,7 +73,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); 文件系统写入类 syscall 的权限限制: -- `FS_MKDIR` / `FS_WRITE` / `FS_APPEND` / `FS_REMOVE` 仅允许 `/temp` 树下路径。 +- `FS_MKDIR` / `FS_WRITE` / `FS_APPEND` / `FS_REMOVE` 仅允许 `/temp` 树下路径,或已挂载磁盘路径树下(默认挂载点通常为 `/temp/disk`)。 `/proc` 虚拟目录(由 syscall 层动态导出): @@ -83,7 +83,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `/proc/`:指定 PID 快照文本 - `/proc` 为只读;写入类 syscall 不支持。 -## 4. Syscall 列表(0~84) +## 4. Syscall 列表(0~92) ### 0 `CLEONOS_SYSCALL_LOG_WRITE` @@ -667,6 +667,54 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - 返回:实际写入字节数(不含终止符),失败返回 `0` - 说明:返回 CLKS 内核版本字符串(当前默认 `1.0.0-alpha`),内核会保证输出以 `\0` 结尾。 +### 85 `CLEONOS_SYSCALL_DISK_PRESENT` + +- 参数:无 +- 返回:`1` 表示存在可用磁盘后端,`0` 表示不存在。 +- 说明:在 QEMU 运行时,磁盘后端来自模拟物理硬盘(`-drive`),不是 ISO/Limine module。 + +### 86 `CLEONOS_SYSCALL_DISK_SIZE_BYTES` + +- 参数:无 +- 返回:磁盘容量(字节);无磁盘时返回 `0`。 + +### 87 `CLEONOS_SYSCALL_DISK_SECTOR_COUNT` + +- 参数:无 +- 返回:扇区总数(按 512 字节扇区);无磁盘时返回 `0`。 + +### 88 `CLEONOS_SYSCALL_DISK_FORMATTED` + +- 参数:无 +- 返回:`1` 表示当前磁盘已识别为 FAT32,`0` 表示未格式化/不识别。 + +### 89 `CLEONOS_SYSCALL_DISK_FORMAT_FAT32` + +- 参数: +- `arg0`: `const char *label`(可为 `0` 或空字符串) +- 返回:成功 `1`,失败 `0` +- 说明:将当前磁盘格式化为 FAT32;此操作会清空已有磁盘数据。该 syscall 在 USC 策略中默认视为高风险操作。 + +### 90 `CLEONOS_SYSCALL_DISK_MOUNT` + +- 参数: +- `arg0`: `const char *mount_path`(绝对路径) +- 返回:成功 `1`,失败 `0` +- 说明:将 FAT32 磁盘挂载到指定路径(不能是 `/`)。挂载成功后,`FS_*` 接口可通过该路径访问磁盘内容。 + +### 91 `CLEONOS_SYSCALL_DISK_MOUNTED` + +- 参数:无 +- 返回:`1` 表示已挂载,`0` 表示未挂载。 + +### 92 `CLEONOS_SYSCALL_DISK_MOUNT_PATH` + +- 参数: +- `arg0`: `char *out_path` +- `arg1`: `u64 out_size` +- 返回:实际写入字节数(不含终止符),失败返回 `0` +- 说明:查询当前挂载点路径;返回值为写入长度,且内核保证 `\0` 终止。 + ## 5. 用户态封装函数 用户态封装位于: @@ -696,17 +744,19 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `cleonos_sys_exec_pathv_io()` - `cleonos_sys_fb_info()` / `cleonos_sys_fb_blit()` / `cleonos_sys_fb_clear()` - `cleonos_sys_kernel_version()` +- `cleonos_sys_disk_present()` / `cleonos_sys_disk_size_bytes()` / `cleonos_sys_disk_sector_count()` +- `cleonos_sys_disk_formatted()` / `cleonos_sys_disk_format_fat32()` / `cleonos_sys_disk_mount()` / `cleonos_sys_disk_mounted()` / `cleonos_sys_disk_mount_path()` ## 6. 开发注意事项 - 传入的字符串/缓冲指针目前按“同地址空间可直接访问”模型处理,后续若引入严格用户态地址隔离,需要补充用户内存校验。 - `FS_READ` 不保证文本终止符;读取文本请预留 1 字节并手动 `buf[n] = '\0'`。 -- `FS_WRITE`/`FS_APPEND` 仅允许 `/temp`;大数据写入由内核自动分块处理。 +- `FS_WRITE`/`FS_APPEND` 仅允许 `/temp` 或已挂载磁盘路径;大数据写入由内核自动分块处理。 - `/proc` 由 syscall 层虚拟导出,不占用 RAMDISK 节点,也不能通过写入类 syscall 修改。 ## 7. Wine 兼容说明 -- `wine/cleonos_wine_lib/runner.py` 当前已覆盖到 `0..84`(含 `DL_*`、`FB_*`、`KERNEL_VERSION`)。 +- `wine/cleonos_wine_lib/runner.py` 当前已覆盖到 `0..92`(含 `DL_*`、`FB_*`、`KERNEL_VERSION`、`DISK_*`)。 - `DL_*`(`77..79`)在 Wine 中为“可运行兼容”实现: - `DL_OPEN`:加载 guest ELF 到当前 Unicorn 地址空间,返回稳定 `handle`,并做引用计数。 - `DL_SYM`:解析 ELF `SYMTAB/DYNSYM` 并返回 guest 可调用地址。 @@ -718,6 +768,10 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - 配合 Wine 参数 `--fb-window` 可将 framebuffer 实时显示到主机窗口(pygame 后端);未启用时保持内存缓冲模式。 - `FB_CLEAR` 支持清屏颜色写入。 - `KERNEL_VERSION`(`84`)在 Wine 中返回内核版本字符串(当前默认 `1.0.0-alpha`)。 +- `DISK_*`(`85..92`)在 Wine 中已实现: +- 提供虚拟磁盘容量信息与 FAT32 格式化状态查询。 +- `DISK_FORMAT_FAT32` 会初始化/重置 Wine rootfs 下的虚拟磁盘目录。 +- `DISK_MOUNT`/`DISK_MOUNT_PATH` 支持挂载点管理,并与 `FS_MKDIR/WRITE/APPEND/REMOVE` 的路径规则联动。 - Wine 在运行时崩溃场景下会生成与内核一致格式的“信号编码退出状态”,可通过 `WAITPID` 读取。 - Wine 当前音频 syscall 为占位实现:`AUDIO_AVAILABLE=0`,`AUDIO_PLAY_TONE=0`,`AUDIO_STOP=1`。 -- Wine 版本号策略固定为 `85.0.0-wine`(`85` = 当前实现 syscall 数量,后续保持不变)。 +- Wine 版本号策略固定为 `85.0.0-wine`(历史兼容号;不会随 syscall 扩展继续增长)。 diff --git a/wine/README.md b/wine/README.md index 2075c7d..803df2f 100644 --- a/wine/README.md +++ b/wine/README.md @@ -38,10 +38,10 @@ python wine/cleonos_wine.py build/x86_64/ramdisk_root/shell/shell.elf --rootfs b ## 支持 - ELF64 (x86_64) PT_LOAD 段装载 -- CLeonOS `int 0x80` syscall 0..84(含 `FD_*`、`DL_*`、`FB_*`、`PROC_*`、`STATS_*`、`EXEC_PATHV_IO`、`KERNEL_VERSION`) +- CLeonOS `int 0x80` syscall 0..92(含 `FD_*`、`DL_*`、`FB_*`、`PROC_*`、`STATS_*`、`EXEC_PATHV_IO`、`KERNEL_VERSION`、`DISK_*`) - TTY 输出与键盘输入队列 - rootfs 文件/目录访问(`FS_*`) -- `/temp` 写入限制(`FS_MKDIR/WRITE/APPEND/REMOVE`) +- `/temp` 与已挂载磁盘路径写入限制(`FS_MKDIR/WRITE/APPEND/REMOVE`) - `EXEC_PATH/EXEC_PATHV` 执行 ELF(带深度限制) - `EXEC_PATHV_IO`(支持 stdio fd 继承/重定向) - `SPAWN_PATH/SPAWN_PATHV/WAITPID/EXIT/SLEEP_TICKS/YIELD` @@ -52,12 +52,14 @@ python wine/cleonos_wine.py build/x86_64/ramdisk_root/shell/shell.elf --rootfs b - 动态库兼容加载(`DL_OPEN/DL_CLOSE/DL_SYM`,基于 ELF 符号解析) - framebuffer 兼容(`FB_INFO/FB_BLIT/FB_CLEAR`,支持内存缓冲与窗口显示) - 内核版本查询(`KERNEL_VERSION`) +- 磁盘接口兼容(`DISK_PRESENT/SIZE_BYTES/SECTOR_COUNT/FORMATTED/FORMAT_FAT32/MOUNT/MOUNTED/MOUNT_PATH`) +- Wine 虚拟磁盘目录默认位于 `/__clks_disk0__`(格式化标记文件 `.fat32`) - 异常退出状态编码与故障元信息(`PROC_LAST_SIGNAL/PROC_FAULT_*`) ## 版本策略 - CLeonOS-Wine 版本号固定为:`85.0.0-wine` -- 该值来源于“当前实现 syscall 数量 = 85(0..84)”,按项目约定后续不再变更 +- 该值按项目策略固定,不再随新增 syscall 变更(即使当前实现范围已扩展到 `0..92`) ## 参数 @@ -71,6 +73,7 @@ python wine/cleonos_wine.py build/x86_64/ramdisk_root/shell/shell.elf --rootfs b - `--` 之后内容:作为 guest argv 透传(推荐) - `--max-exec-depth N`:设置 exec 嵌套深度上限 - `--verbose`:打印更多日志 +- 环境变量 `CLEONOS_WINE_DISK_SIZE_MB`:设置 Wine 虚拟磁盘容量(MB,默认 `64`) ## `execv/spawnv` 参数格式 diff --git a/wine/cleonos_wine_lib/constants.py b/wine/cleonos_wine_lib/constants.py index 9053c76..e5ec14d 100644 --- a/wine/cleonos_wine_lib/constants.py +++ b/wine/cleonos_wine_lib/constants.py @@ -99,6 +99,14 @@ SYS_FB_INFO = 81 SYS_FB_BLIT = 82 SYS_FB_CLEAR = 83 SYS_KERNEL_VERSION = 84 +SYS_DISK_PRESENT = 85 +SYS_DISK_SIZE_BYTES = 86 +SYS_DISK_SECTOR_COUNT = 87 +SYS_DISK_FORMATTED = 88 +SYS_DISK_FORMAT_FAT32 = 89 +SYS_DISK_MOUNT = 90 +SYS_DISK_MOUNTED = 91 +SYS_DISK_MOUNT_PATH = 92 # proc states (from cleonos/c/include/cleonos_syscall.h) PROC_STATE_UNUSED = 0 diff --git a/wine/cleonos_wine_lib/runner.py b/wine/cleonos_wine_lib/runner.py index 334e2f7..69f3aa2 100644 --- a/wine/cleonos_wine_lib/runner.py +++ b/wine/cleonos_wine_lib/runner.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +import shutil import struct import sys import time @@ -62,6 +63,14 @@ from .constants import ( SYS_FS_STAT_SIZE, SYS_FS_STAT_TYPE, SYS_FS_WRITE, + SYS_DISK_FORMATTED, + SYS_DISK_FORMAT_FAT32, + SYS_DISK_MOUNT, + SYS_DISK_MOUNTED, + SYS_DISK_MOUNT_PATH, + SYS_DISK_PRESENT, + SYS_DISK_SECTOR_COUNT, + SYS_DISK_SIZE_BYTES, SYS_GETPID, SYS_KERNEL_VERSION, SYS_KBD_BUFFERED, @@ -272,6 +281,23 @@ class CLeonOSWineNative: self._fb_dirty = False self._fb_presented_once = False + self._disk_present = True + self._disk_size_bytes = self._bounded_env_int("CLEONOS_WINE_DISK_SIZE_MB", 64, 8, 4096) * 1024 * 1024 + self._disk_mount_path = "/temp/disk" + self._disk_root = (self.rootfs / "__clks_disk0__").resolve() + self._disk_marker = self._disk_root / ".fat32" + self._disk_formatted = False + self._disk_mounted = False + try: + self._disk_root.mkdir(parents=True, exist_ok=True) + if self._disk_marker.exists(): + self._disk_formatted = True + self._disk_mounted = True + except Exception: + self._disk_present = False + self._disk_formatted = False + self._disk_mounted = False + default_path = self._normalize_guest_path(self.guest_path_hint or f"/{self.elf_path.name}") self.argv_items, self.env_items = self._prepare_exec_items(default_path, self.argv_items, self.env_items) self._init_default_fds() @@ -717,6 +743,25 @@ class CLeonOSWineNative: return self._fb_clear(arg0) if sid == SYS_KERNEL_VERSION: return self._kernel_version(uc, arg0, arg1) + if sid == SYS_DISK_PRESENT: + return 1 if self._disk_present else 0 + if sid == SYS_DISK_SIZE_BYTES: + return int(u64(self._disk_size_bytes if self._disk_present else 0)) + if sid == SYS_DISK_SECTOR_COUNT: + if not self._disk_present: + return 0 + return int(u64(self._disk_size_bytes // 512)) + if sid == SYS_DISK_FORMATTED: + return 1 if (self._disk_present and self._disk_formatted) else 0 + if sid == SYS_DISK_FORMAT_FAT32: + label = self._read_guest_cstring(uc, arg0, 16) if arg0 != 0 else "" + return self._disk_format_fat32(label) + if sid == SYS_DISK_MOUNT: + return self._disk_mount(uc, arg0) + if sid == SYS_DISK_MOUNTED: + return 1 if self._disk_mounted else 0 + if sid == SYS_DISK_MOUNT_PATH: + return self._disk_mount_path_query(uc, arg0, arg1) return u64_neg1() @@ -1008,9 +1053,90 @@ class CLeonOSWineNative: def _guest_path_is_under_temp(path: str) -> bool: return path == "/temp" or path.startswith("/temp/") + def _disk_path_is_under_mount(self, path: str) -> bool: + if not self._disk_mounted: + return False + normalized = self._normalize_guest_path(path) + mount = self._normalize_guest_path(self._disk_mount_path) + return normalized == mount or normalized.startswith(mount + "/") + + def _disk_guest_to_host(self, guest_path: str, *, must_exist: bool) -> Optional[Path]: + normalized = self._normalize_guest_path(guest_path) + mount = self._normalize_guest_path(self._disk_mount_path) + + if not self._disk_present or not self._disk_formatted or not self._disk_mounted: + return None + + if normalized == mount: + host = self._disk_root + elif normalized.startswith(mount + "/"): + rel = normalized[len(mount) + 1 :] + parts = [part for part in rel.split("/") if part] + host = self._disk_root.joinpath(*parts) if parts else self._disk_root + else: + return None + + if must_exist and not host.exists(): + return None + return host + + def _disk_format_fat32(self, label: str) -> int: + _ = label + if not self._disk_present: + return 0 + + try: + self._disk_root.mkdir(parents=True, exist_ok=True) + for child in list(self._disk_root.iterdir()): + if child.name == self._disk_marker.name: + continue + if child.is_dir(): + shutil.rmtree(child) + else: + child.unlink() + self._disk_marker.write_text("FAT32\n", encoding="utf-8") + self._disk_formatted = True + return 1 + except Exception: + return 0 + + def _disk_mount(self, uc: Uc, mount_ptr: int) -> int: + mount_path = self._normalize_guest_path(self._read_guest_cstring(uc, mount_ptr, EXEC_PATH_MAX)) + + if not self._disk_present or not self._disk_formatted: + return 0 + + if mount_path == "/": + return 0 + + self._disk_mount_path = mount_path + self._disk_mounted = True + return 1 + + def _disk_mount_path_query(self, uc: Uc, out_ptr: int, out_size: int) -> int: + if out_ptr == 0 or out_size == 0: + return 0 + + if not self._disk_mounted: + return 0 + + payload = self._normalize_guest_path(self._disk_mount_path).encode("utf-8", errors="replace") + max_copy = int(out_size) - 1 + if max_copy < 0: + return 0 + if len(payload) > max_copy: + payload = payload[:max_copy] + return len(payload) if self._write_guest_bytes(uc, out_ptr, payload + b"\x00") else 0 + def _fs_mkdir(self, uc: Uc, path_ptr: int) -> int: path = self._normalize_guest_path(self._read_guest_cstring(uc, path_ptr)) - if not self._guest_path_is_under_temp(path): + is_temp_path = self._guest_path_is_under_temp(path) + is_disk_path = self._disk_path_is_under_mount(path) + + if not is_temp_path and not is_disk_path: + return 0 + + if is_disk_path and not self._disk_formatted: return 0 host_path = self._guest_to_host(path, must_exist=False) @@ -1028,8 +1154,17 @@ class CLeonOSWineNative: def _fs_write_common(self, uc: Uc, path_ptr: int, data_ptr: int, size: int, append_mode: bool) -> int: path = self._normalize_guest_path(self._read_guest_cstring(uc, path_ptr)) + is_temp_path = self._guest_path_is_under_temp(path) + is_disk_path = self._disk_path_is_under_mount(path) + disk_mount = self._normalize_guest_path(self._disk_mount_path) - if not self._guest_path_is_under_temp(path) or path == "/temp": + if not is_temp_path and not is_disk_path: + return 0 + + if is_disk_path and not self._disk_formatted: + return 0 + + if path == "/temp" or (is_disk_path and path == disk_mount): return 0 if size < 0 or size > self.state.fs_write_max: @@ -1068,8 +1203,17 @@ class CLeonOSWineNative: def _fs_remove(self, uc: Uc, path_ptr: int) -> int: path = self._normalize_guest_path(self._read_guest_cstring(uc, path_ptr)) + is_temp_path = self._guest_path_is_under_temp(path) + is_disk_path = self._disk_path_is_under_mount(path) + disk_mount = self._normalize_guest_path(self._disk_mount_path) - if not self._guest_path_is_under_temp(path) or path == "/temp": + if not is_temp_path and not is_disk_path: + return 0 + + if is_disk_path and not self._disk_formatted: + return 0 + + if path == "/temp" or (is_disk_path and path == disk_mount): return 0 host_path = self._guest_to_host(path, must_exist=True) @@ -2178,6 +2322,11 @@ class CLeonOSWineNative: def _guest_to_host(self, guest_path: str, *, must_exist: bool) -> Optional[Path]: norm = self._normalize_guest_path(guest_path) + disk_host = self._disk_guest_to_host(norm, must_exist=must_exist) + + if disk_host is not None: + return disk_host + if norm == "/": return self.rootfs if (not must_exist or self.rootfs.exists()) else None