硬盘支持(FAT32)

This commit is contained in:
2026-04-22 21:43:09 +08:00
parent 6b5a19a19d
commit 0fe34d9a09
22 changed files with 2768 additions and 18 deletions

View File

@@ -93,6 +93,8 @@ set(ISO_ROOT "${BUILD_ROOT}/iso_root")
set(RAMDISK_ROOT "${BUILD_ROOT}/ramdisk_root") set(RAMDISK_ROOT "${BUILD_ROOT}/ramdisk_root")
set(KERNEL_ELF "${BUILD_ROOT}/clks_kernel.elf") set(KERNEL_ELF "${BUILD_ROOT}/clks_kernel.elf")
set(RAMDISK_IMAGE "${BUILD_ROOT}/cleonos_ramdisk.tar") 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(ISO_IMAGE "${CMAKE_SOURCE_DIR}/build/CLeonOS-x86_64.iso")
set(KERNEL_SYMBOLS_FILE "${BUILD_ROOT}/kernel.sym") set(KERNEL_SYMBOLS_FILE "${BUILD_ROOT}/kernel.sym")
@@ -457,6 +459,15 @@ add_custom_command(
-P "${CL_LOG_EMIT_SCRIPT}" -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( add_custom_command(
OUTPUT "${ISO_IMAGE}" OUTPUT "${ISO_IMAGE}"
COMMAND ${CMAKE_COMMAND} -E rm -rf "${ISO_ROOT}" COMMAND ${CMAKE_COMMAND} -E rm -rf "${ISO_ROOT}"
@@ -511,8 +522,8 @@ add_custom_target(run
"-DLOG_LEVEL=STEP" "-DLOG_LEVEL=STEP"
"-DLOG_TEXT=launching qemu run" "-DLOG_TEXT=launching qemu run"
-P "${CL_LOG_EMIT_SCRIPT}" -P "${CL_LOG_EMIT_SCRIPT}"
COMMAND ${QEMU_X86_64} -M q35 -m 1024M -cdrom "${ISO_IMAGE}" -serial stdio 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 DEPENDS iso disk-image
USES_TERMINAL USES_TERMINAL
) )
@@ -522,8 +533,8 @@ add_custom_target(debug
"-DLOG_LEVEL=STEP" "-DLOG_LEVEL=STEP"
"-DLOG_TEXT=launching qemu debug (-s -S)" "-DLOG_TEXT=launching qemu debug (-s -S)"
-P "${CL_LOG_EMIT_SCRIPT}" -P "${CL_LOG_EMIT_SCRIPT}"
COMMAND ${QEMU_X86_64} -M q35 -m 1024M -cdrom "${ISO_IMAGE}" -serial stdio -s -S 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 DEPENDS iso disk-image
USES_TERMINAL 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 iso"
COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target run" 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 debug"
COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target disk-image"
) )

View File

@@ -18,6 +18,7 @@ READELF_FOR_TARGET ?=
PYTHON ?= python3 PYTHON ?= python3
MENUCONFIG_ARGS ?= MENUCONFIG_ARGS ?=
MENUCONFIG_PRESET ?= MENUCONFIG_PRESET ?=
DISK_IMAGE_MB ?=
ifeq ($(strip $(CMAKE_GENERATOR)),) ifeq ($(strip $(CMAKE_GENERATOR)),)
GEN_ARG := GEN_ARG :=
@@ -52,8 +53,11 @@ endif
ifneq ($(strip $(READELF_FOR_TARGET)),) ifneq ($(strip $(READELF_FOR_TARGET)),)
CMAKE_PASSTHROUGH_ARGS += -DREADELF_FOR_TARGET=$(READELF_FOR_TARGET) CMAKE_PASSTHROUGH_ARGS += -DREADELF_FOR_TARGET=$(READELF_FOR_TARGET)
endif 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 all: iso
@@ -107,6 +111,9 @@ ramdisk-root: configure
ramdisk: configure ramdisk: configure
> @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target ramdisk > @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target ramdisk
disk-image: configure
> @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target disk-image
iso: configure iso: configure
> @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target iso > @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target iso
@@ -137,6 +144,7 @@ help:
> @echo " make menuconfig-gui" > @echo " make menuconfig-gui"
> @echo " make setup" > @echo " make setup"
> @echo " make userapps" > @echo " make userapps"
> @echo " make disk-image"
> @echo " make iso" > @echo " make iso"
> @echo " make run" > @echo " make run"
> @echo " make debug" > @echo " make debug"
@@ -147,6 +155,8 @@ help:
> @echo " make configure CMAKE_EXTRA_ARGS='-DLIMINE_SKIP_CONFIGURE=1 -DOBJCOPY_FOR_TARGET=objcopy'" > @echo " make configure CMAKE_EXTRA_ARGS='-DLIMINE_SKIP_CONFIGURE=1 -DOBJCOPY_FOR_TARGET=objcopy'"
> @echo "Direct passthrough is also supported:" > @echo "Direct passthrough is also supported:"
> @echo " make run LIMINE_SKIP_CONFIGURE=1" > @echo " make run LIMINE_SKIP_CONFIGURE=1"
> @echo "Disk image size example:"
> @echo " make run DISK_IMAGE_MB=128"
> @echo "Preset examples:" > @echo "Preset examples:"
> @echo " make menuconfig MENUCONFIG_PRESET=full" > @echo " make menuconfig MENUCONFIG_PRESET=full"
> @echo " make menuconfig MENUCONFIG_PRESET=minimal" > @echo " make menuconfig MENUCONFIG_PRESET=minimal"

View File

@@ -8,6 +8,7 @@ Experimental x86_64 operating system project with a C kernel, Rust-assisted runt
- x86_64 kernel booted by Limine - x86_64 kernel booted by Limine
- RAM-disk VFS layout (`/system`, `/shell`, `/temp`, `/driver`) - 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) - Virtual TTY subsystem (multi TTY, ANSI handling, cursor, PSF font support)
- Keyboard and mouse input stack, plus desktop mode on TTY2 - Keyboard and mouse input stack, plus desktop mode on TTY2
- User-space ELF app model with syscall ABI (`int 0x80`) - User-space ELF app model with syscall ABI (`int 0x80`)
@@ -60,6 +61,12 @@ git submodule update --init --recursive
make run 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: If you already have Limine artifacts and want to skip configure:
```bash ```bash
@@ -72,6 +79,7 @@ make run LIMINE_SKIP_CONFIGURE=1
- `make kernel` - build kernel ELF - `make kernel` - build kernel ELF
- `make userapps` - build user-space ELF apps - `make userapps` - build user-space ELF apps
- `make ramdisk` - package runtime ramdisk - `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 iso` - build bootable ISO
- `make run` - launch QEMU - `make run` - launch QEMU
- `make debug` - launch QEMU with `-s -S` for GDB - `make debug` - launch QEMU with `-s -S` for GDB
@@ -102,6 +110,10 @@ cat /shell/init.cmd
grep -n exec /shell/init.cmd grep -n exec /shell/init.cmd
cat /shell/init.cmd | grep -n exec cat /shell/init.cmd | grep -n exec
ls /shell > /temp/shell_list.txt ls /shell > /temp/shell_list.txt
diskinfo
mkfsfat32 CLEONOS
mount /temp/disk
write /temp/disk/hello.txt hello-disk
``` ```
## Documentation ## Documentation

View File

@@ -8,6 +8,7 @@
- 基于 Limine 启动的 x86_64 内核 - 基于 Limine 启动的 x86_64 内核
- RAM-disk VFS 目录布局(`/system``/shell``/temp``/driver` - RAM-disk VFS 目录布局(`/system``/shell``/temp``/driver`
- 虚拟磁盘后端,支持 FAT32 格式化与挂载(默认挂载点:`/temp/disk`
- 虚拟 TTY 子系统(多 TTY、ANSI、光标、PSF 字体) - 虚拟 TTY 子系统(多 TTY、ANSI、光标、PSF 字体)
- 键盘/鼠标输入栈TTY2 提供桌面模式 - 键盘/鼠标输入栈TTY2 提供桌面模式
- 用户态 ELF 应用模型syscall ABI 使用 `int 0x80` - 用户态 ELF 应用模型syscall ABI 使用 `int 0x80`
@@ -60,6 +61,12 @@ git submodule update --init --recursive
make run 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 如果你已经准备好 Limine 产物,可跳过 configure
```bash ```bash
@@ -72,6 +79,7 @@ make run LIMINE_SKIP_CONFIGURE=1
- `make kernel` - 构建内核 ELF - `make kernel` - 构建内核 ELF
- `make userapps` - 构建用户态 ELF 应用 - `make userapps` - 构建用户态 ELF 应用
- `make ramdisk` - 打包运行时 ramdisk - `make ramdisk` - 打包运行时 ramdisk
- `make disk-image` - 创建/调整运行时磁盘镜像(`build/x86_64/cleonos_disk.img`
- `make iso` - 生成可启动 ISO - `make iso` - 生成可启动 ISO
- `make run` - 启动 QEMU - `make run` - 启动 QEMU
- `make debug` - 以 `-s -S` 启动 QEMU 供 GDB 附加 - `make debug` - 以 `-s -S` 启动 QEMU 供 GDB 附加
@@ -102,6 +110,10 @@ cat /shell/init.cmd
grep -n exec /shell/init.cmd grep -n exec /shell/init.cmd
cat /shell/init.cmd | grep -n exec cat /shell/init.cmd | grep -n exec
ls /shell > /temp/shell_list.txt ls /shell > /temp/shell_list.txt
diskinfo
mkfsfat32 CLEONOS
mount /temp/disk
write /temp/disk/hello.txt hello-disk
``` ```
## 文档 ## 文档

View File

@@ -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 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 bg fg jobs kill ps top
procstat sysstat procstat sysstat
diskinfo mkfsfat32 mount
shutdown restart exit clear ansi ansitest wavplay fastfetch memstat fsstat taskstat userstat 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 shstat stats tty dmesg kbdstat mkdir touch write append cp mv rm kdbg bmpview qrcode
) )

View File

@@ -0,0 +1,87 @@
#include "cmd_runtime.h"
#include <stdio.h>
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;
}

View File

@@ -29,6 +29,9 @@ static int ush_cmd_help(void) {
ush_writeln(" cp <src> <dst> (dst /temp only)"); ush_writeln(" cp <src> <dst> (dst /temp only)");
ush_writeln(" mv <src> <dst> (/temp only)"); ush_writeln(" mv <src> <dst> (/temp only)");
ush_writeln(" rm <path> (/temp only)"); ush_writeln(" rm <path> (/temp only)");
ush_writeln(" diskinfo");
ush_writeln(" mkfsfat32 [label]");
ush_writeln(" mount [path] (default suggested: /temp/disk)");
ush_writeln(" pid"); ush_writeln(" pid");
ush_writeln(" spawn <path|name> [args...] / bg <path|name> [args...]"); ush_writeln(" spawn <path|name> [args...] / bg <path|name> [args...]");
ush_writeln(" wait <pid> / fg [pid]"); ush_writeln(" wait <pid> / fg [pid]");

View File

@@ -0,0 +1,106 @@
#include "cmd_runtime.h"
#include <stdio.h>
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;
}

134
cleonos/c/apps/mount_main.c Normal file
View File

@@ -0,0 +1,134 @@
#include "cmd_runtime.h"
#include <stdio.h>
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;
}

View File

@@ -145,6 +145,14 @@ typedef struct cleonos_fb_blit_req {
#define CLEONOS_SYSCALL_FB_BLIT 82ULL #define CLEONOS_SYSCALL_FB_BLIT 82ULL
#define CLEONOS_SYSCALL_FB_CLEAR 83ULL #define CLEONOS_SYSCALL_FB_CLEAR 83ULL
#define CLEONOS_SYSCALL_KERNEL_VERSION 84ULL #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_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
u64 cleonos_sys_log_write(const char *message, u64 length); 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_blit(const cleonos_fb_blit_req *req);
u64 cleonos_sys_fb_clear(u64 rgb); u64 cleonos_sys_fb_clear(u64 rgb);
u64 cleonos_sys_kernel_version(char *out_version, u64 out_size); 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 #endif

View File

@@ -371,3 +371,35 @@ u64 cleonos_sys_fb_clear(u64 rgb) {
u64 cleonos_sys_kernel_version(char *out_version, u64 out_size) { u64 cleonos_sys_kernel_version(char *out_version, u64 out_size) {
return cleonos_syscall(CLEONOS_SYSCALL_KERNEL_VERSION, (u64)out_version, out_size, 0ULL); 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);
}

38
clks/include/clks/disk.h Normal file
View File

@@ -0,0 +1,38 @@
#ifndef CLKS_DISK_H
#define CLKS_DISK_H
#include <clks/types.h>
#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

View File

@@ -88,6 +88,14 @@
#define CLKS_SYSCALL_FB_BLIT 82ULL #define CLKS_SYSCALL_FB_BLIT 82ULL
#define CLKS_SYSCALL_FB_CLEAR 83ULL #define CLKS_SYSCALL_FB_CLEAR 83ULL
#define CLKS_SYSCALL_KERNEL_VERSION 84ULL #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); void clks_syscall_init(void);
u64 clks_syscall_dispatch(void *frame_ptr); u64 clks_syscall_dispatch(void *frame_ptr);

View File

@@ -1,5 +1,6 @@
#include <clks/cpu.h> #include <clks/cpu.h>
#include <clks/audio.h> #include <clks/audio.h>
#include <clks/disk.h>
#include <clks/exec.h> #include <clks/exec.h>
#include <clks/framebuffer.h> #include <clks/framebuffer.h>
#include <clks/fs.h> #include <clks/fs.h>
@@ -36,7 +37,7 @@
#define CLKS_SYSCALL_KDBG_STACK_WINDOW_BYTES (128ULL * 1024ULL) #define CLKS_SYSCALL_KDBG_STACK_WINDOW_BYTES (128ULL * 1024ULL)
#define CLKS_SYSCALL_KERNEL_SYMBOL_FILE "/system/kernel.sym" #define CLKS_SYSCALL_KERNEL_SYMBOL_FILE "/system/kernel.sym"
#define CLKS_SYSCALL_KERNEL_ADDR_BASE 0xFFFF800000000000ULL #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_STATS_RING_SIZE 256U
#define CLKS_SYSCALL_USC_MAX_ALLOWED_APPS 64U #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); 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) { static u64 clks_syscall_fd_open(u64 arg0, u64 arg1, u64 arg2) {
char path[CLKS_SYSCALL_PATH_MAX]; char path[CLKS_SYSCALL_PATH_MAX];
@@ -1994,6 +2045,22 @@ static const char *clks_syscall_usc_syscall_name(u64 id) {
return "SHUTDOWN"; return "SHUTDOWN";
case CLKS_SYSCALL_RESTART: case CLKS_SYSCALL_RESTART:
return "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: default:
return "UNKNOWN"; 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; return (CLKS_CFG_USC_SC_SHUTDOWN != 0) ? CLKS_TRUE : CLKS_FALSE;
case CLKS_SYSCALL_RESTART: case CLKS_SYSCALL_RESTART:
return (CLKS_CFG_USC_SC_RESTART != 0) ? CLKS_TRUE : CLKS_FALSE; return (CLKS_CFG_USC_SC_RESTART != 0) ? CLKS_TRUE : CLKS_FALSE;
case CLKS_SYSCALL_DISK_FORMAT_FAT32:
return CLKS_TRUE;
default: default:
return CLKS_FALSE; return CLKS_FALSE;
} }
@@ -2528,6 +2597,22 @@ u64 clks_syscall_dispatch(void *frame_ptr) {
return clks_syscall_fb_clear(frame->rbx); return clks_syscall_fb_clear(frame->rbx);
case CLKS_SYSCALL_KERNEL_VERSION: case CLKS_SYSCALL_KERNEL_VERSION:
return clks_syscall_kernel_version(frame->rbx, frame->rcx); 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: default:
return (u64)-1; return (u64)-1;
} }

1853
clks/kernel/storage/disk.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
#include <clks/boot.h> #include <clks/boot.h>
#include <clks/disk.h>
#include <clks/fs.h> #include <clks/fs.h>
#include <clks/heap.h> #include <clks/heap.h>
#include <clks/log.h> #include <clks/log.h>
@@ -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", "NODE_COUNT", (u64)clks_fs_nodes_used);
clks_log_hex(CLKS_LOG_INFO, "FS", "FILE_COUNT", stats.file_count); 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) { if (clks_fs_require_directory("/system") == CLKS_FALSE) {
return; 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) { clks_bool clks_fs_stat(const char *path, struct clks_fs_node_info *out_info) {
i32 node_index; i32 node_index;
u64 disk_type = 0ULL;
u64 disk_size = 0ULL;
if (clks_fs_ready == CLKS_FALSE || out_info == CLKS_NULL) { if (clks_fs_ready == CLKS_FALSE || out_info == CLKS_NULL) {
return CLKS_FALSE; 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); node_index = clks_fs_find_node_by_external(path);
if (node_index < 0) { 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) { const void *clks_fs_read_all(const char *path, u64 *out_size) {
i32 node_index; i32 node_index;
const void *disk_data;
if (clks_fs_ready == CLKS_FALSE) { if (clks_fs_ready == CLKS_FALSE) {
return CLKS_NULL; 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); node_index = clks_fs_find_node_by_external(path);
if (node_index < 0) { if (node_index < 0) {
@@ -598,6 +625,10 @@ u64 clks_fs_count_children(const char *dir_path) {
return 0ULL; 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); dir_index = clks_fs_find_node_by_external(dir_path);
if (dir_index < 0) { 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; 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); 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) { 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; 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) { if (clks_fs_normalize_external_path(path, internal, sizeof(internal)) == CLKS_FALSE) {
return 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; 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) { if (clks_fs_normalize_external_path(path, internal, sizeof(internal)) == CLKS_FALSE) {
return 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; 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) { if (size > 0ULL && data == CLKS_NULL) {
return CLKS_FALSE; return CLKS_FALSE;
} }
@@ -851,6 +898,10 @@ clks_bool clks_fs_remove(const char *path) {
return CLKS_FALSE; 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) { if (clks_fs_normalize_external_path(path, internal, sizeof(internal)) == CLKS_FALSE) {
return 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; return used;
} }

View File

@@ -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)")

View File

@@ -4,4 +4,4 @@ verbose: yes
/CLeonOS /CLeonOS
protocol: limine protocol: limine
kernel_path: boot():/boot/clks_kernel.elf kernel_path: boot():/boot/clks_kernel.elf
module_path: boot():/boot/cleonos_ramdisk.tar module_path: boot():/boot/cleonos_ramdisk.tar

View File

@@ -73,7 +73,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
文件系统写入类 syscall 的权限限制: 文件系统写入类 syscall 的权限限制:
- `FS_MKDIR` / `FS_WRITE` / `FS_APPEND` / `FS_REMOVE` 仅允许 `/temp` 树下路径。 - `FS_MKDIR` / `FS_WRITE` / `FS_APPEND` / `FS_REMOVE` 仅允许 `/temp` 树下路径,或已挂载磁盘路径树下(默认挂载点通常为 `/temp/disk`
`/proc` 虚拟目录(由 syscall 层动态导出): `/proc` 虚拟目录(由 syscall 层动态导出):
@@ -83,7 +83,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
- `/proc/<pid>`:指定 PID 快照文本 - `/proc/<pid>`:指定 PID 快照文本
- `/proc` 为只读;写入类 syscall 不支持。 - `/proc` 为只读;写入类 syscall 不支持。
## 4. Syscall 列表0~84 ## 4. Syscall 列表0~92
### 0 `CLEONOS_SYSCALL_LOG_WRITE` ### 0 `CLEONOS_SYSCALL_LOG_WRITE`
@@ -667,6 +667,54 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
- 返回:实际写入字节数(不含终止符),失败返回 `0` - 返回:实际写入字节数(不含终止符),失败返回 `0`
- 说明:返回 CLKS 内核版本字符串(当前默认 `1.0.0-alpha`),内核会保证输出以 `\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. 用户态封装函数 ## 5. 用户态封装函数
用户态封装位于: 用户态封装位于:
@@ -696,17 +744,19 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
- `cleonos_sys_exec_pathv_io()` - `cleonos_sys_exec_pathv_io()`
- `cleonos_sys_fb_info()` / `cleonos_sys_fb_blit()` / `cleonos_sys_fb_clear()` - `cleonos_sys_fb_info()` / `cleonos_sys_fb_blit()` / `cleonos_sys_fb_clear()`
- `cleonos_sys_kernel_version()` - `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. 开发注意事项 ## 6. 开发注意事项
- 传入的字符串/缓冲指针目前按“同地址空间可直接访问”模型处理,后续若引入严格用户态地址隔离,需要补充用户内存校验。 - 传入的字符串/缓冲指针目前按“同地址空间可直接访问”模型处理,后续若引入严格用户态地址隔离,需要补充用户内存校验。
- `FS_READ` 不保证文本终止符;读取文本请预留 1 字节并手动 `buf[n] = '\0'` - `FS_READ` 不保证文本终止符;读取文本请预留 1 字节并手动 `buf[n] = '\0'`
- `FS_WRITE`/`FS_APPEND` 仅允许 `/temp`;大数据写入由内核自动分块处理。 - `FS_WRITE`/`FS_APPEND` 仅允许 `/temp` 或已挂载磁盘路径;大数据写入由内核自动分块处理。
- `/proc` 由 syscall 层虚拟导出,不占用 RAMDISK 节点,也不能通过写入类 syscall 修改。 - `/proc` 由 syscall 层虚拟导出,不占用 RAMDISK 节点,也不能通过写入类 syscall 修改。
## 7. Wine 兼容说明 ## 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_*``77..79`)在 Wine 中为“可运行兼容”实现:
- `DL_OPEN`:加载 guest ELF 到当前 Unicorn 地址空间,返回稳定 `handle`,并做引用计数。 - `DL_OPEN`:加载 guest ELF 到当前 Unicorn 地址空间,返回稳定 `handle`,并做引用计数。
- `DL_SYM`:解析 ELF `SYMTAB/DYNSYM` 并返回 guest 可调用地址。 - `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 后端);未启用时保持内存缓冲模式。 - 配合 Wine 参数 `--fb-window` 可将 framebuffer 实时显示到主机窗口pygame 后端);未启用时保持内存缓冲模式。
- `FB_CLEAR` 支持清屏颜色写入。 - `FB_CLEAR` 支持清屏颜色写入。
- `KERNEL_VERSION``84`)在 Wine 中返回内核版本字符串(当前默认 `1.0.0-alpha`)。 - `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 在运行时崩溃场景下会生成与内核一致格式的“信号编码退出状态”,可通过 `WAITPID` 读取。
- Wine 当前音频 syscall 为占位实现:`AUDIO_AVAILABLE=0``AUDIO_PLAY_TONE=0``AUDIO_STOP=1` - 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 扩展继续增长)。

View File

@@ -38,10 +38,10 @@ python wine/cleonos_wine.py build/x86_64/ramdisk_root/shell/shell.elf --rootfs b
## 支持 ## 支持
- ELF64 (x86_64) PT_LOAD 段装载 - 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 输出与键盘输入队列 - TTY 输出与键盘输入队列
- rootfs 文件/目录访问(`FS_*` - rootfs 文件/目录访问(`FS_*`
- `/temp` 写入限制(`FS_MKDIR/WRITE/APPEND/REMOVE` - `/temp` 与已挂载磁盘路径写入限制(`FS_MKDIR/WRITE/APPEND/REMOVE`
- `EXEC_PATH/EXEC_PATHV` 执行 ELF带深度限制 - `EXEC_PATH/EXEC_PATHV` 执行 ELF带深度限制
- `EXEC_PATHV_IO`(支持 stdio fd 继承/重定向) - `EXEC_PATHV_IO`(支持 stdio fd 继承/重定向)
- `SPAWN_PATH/SPAWN_PATHV/WAITPID/EXIT/SLEEP_TICKS/YIELD` - `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 符号解析) - 动态库兼容加载(`DL_OPEN/DL_CLOSE/DL_SYM`,基于 ELF 符号解析)
- framebuffer 兼容(`FB_INFO/FB_BLIT/FB_CLEAR`,支持内存缓冲与窗口显示) - framebuffer 兼容(`FB_INFO/FB_BLIT/FB_CLEAR`,支持内存缓冲与窗口显示)
- 内核版本查询(`KERNEL_VERSION` - 内核版本查询(`KERNEL_VERSION`
- 磁盘接口兼容(`DISK_PRESENT/SIZE_BYTES/SECTOR_COUNT/FORMATTED/FORMAT_FAT32/MOUNT/MOUNTED/MOUNT_PATH`
- Wine 虚拟磁盘目录默认位于 `<rootfs>/__clks_disk0__`(格式化标记文件 `.fat32`
- 异常退出状态编码与故障元信息(`PROC_LAST_SIGNAL/PROC_FAULT_*` - 异常退出状态编码与故障元信息(`PROC_LAST_SIGNAL/PROC_FAULT_*`
## 版本策略 ## 版本策略
- CLeonOS-Wine 版本号固定为:`85.0.0-wine` - CLeonOS-Wine 版本号固定为:`85.0.0-wine`
- 该值来源于“当前实现 syscall 数量 = 850..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 透传(推荐) - `--` 之后内容:作为 guest argv 透传(推荐)
- `--max-exec-depth N`:设置 exec 嵌套深度上限 - `--max-exec-depth N`:设置 exec 嵌套深度上限
- `--verbose`:打印更多日志 - `--verbose`:打印更多日志
- 环境变量 `CLEONOS_WINE_DISK_SIZE_MB`:设置 Wine 虚拟磁盘容量MB默认 `64`
## `execv/spawnv` 参数格式 ## `execv/spawnv` 参数格式

View File

@@ -99,6 +99,14 @@ SYS_FB_INFO = 81
SYS_FB_BLIT = 82 SYS_FB_BLIT = 82
SYS_FB_CLEAR = 83 SYS_FB_CLEAR = 83
SYS_KERNEL_VERSION = 84 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 states (from cleonos/c/include/cleonos_syscall.h)
PROC_STATE_UNUSED = 0 PROC_STATE_UNUSED = 0

View File

@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import os import os
import shutil
import struct import struct
import sys import sys
import time import time
@@ -62,6 +63,14 @@ from .constants import (
SYS_FS_STAT_SIZE, SYS_FS_STAT_SIZE,
SYS_FS_STAT_TYPE, SYS_FS_STAT_TYPE,
SYS_FS_WRITE, 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_GETPID,
SYS_KERNEL_VERSION, SYS_KERNEL_VERSION,
SYS_KBD_BUFFERED, SYS_KBD_BUFFERED,
@@ -272,6 +281,23 @@ class CLeonOSWineNative:
self._fb_dirty = False self._fb_dirty = False
self._fb_presented_once = 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}") 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.argv_items, self.env_items = self._prepare_exec_items(default_path, self.argv_items, self.env_items)
self._init_default_fds() self._init_default_fds()
@@ -717,6 +743,25 @@ class CLeonOSWineNative:
return self._fb_clear(arg0) return self._fb_clear(arg0)
if sid == SYS_KERNEL_VERSION: if sid == SYS_KERNEL_VERSION:
return self._kernel_version(uc, arg0, arg1) 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() return u64_neg1()
@@ -1008,9 +1053,90 @@ class CLeonOSWineNative:
def _guest_path_is_under_temp(path: str) -> bool: def _guest_path_is_under_temp(path: str) -> bool:
return path == "/temp" or path.startswith("/temp/") 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: def _fs_mkdir(self, uc: Uc, path_ptr: int) -> int:
path = self._normalize_guest_path(self._read_guest_cstring(uc, path_ptr)) 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 return 0
host_path = self._guest_to_host(path, must_exist=False) 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: 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)) 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 return 0
if size < 0 or size > self.state.fs_write_max: 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: def _fs_remove(self, uc: Uc, path_ptr: int) -> int:
path = self._normalize_guest_path(self._read_guest_cstring(uc, path_ptr)) 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 return 0
host_path = self._guest_to_host(path, must_exist=True) 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]: def _guest_to_host(self, guest_path: str, *, must_exist: bool) -> Optional[Path]:
norm = self._normalize_guest_path(guest_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 == "/": if norm == "/":
return self.rootfs if (not must_exist or self.rootfs.exists()) else None return self.rootfs if (not must_exist or self.rootfs.exists()) else None