From 044574909a161bd17b49eba3d70f2d22597bcce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=22xq=22=20Quei=C3=9Fner?= Date: Fri, 11 Oct 2024 12:45:29 +0200 Subject: [PATCH] Starts to implement exerciser application --- flake.nix | 5 +- src/fatfs.zig | 4 +- test/build.zig | 39 ++++++++++++ test/build.zig.zon | 14 +++++ test/op_tester.zig | 154 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 213 insertions(+), 3 deletions(-) create mode 100644 test/build.zig create mode 100644 test/build.zig.zon create mode 100644 test/op_tester.zig diff --git a/flake.nix b/flake.nix index aef7026..49ae555 100644 --- a/flake.nix +++ b/flake.nix @@ -32,7 +32,10 @@ packages.default = pkgs.stdenv.mkDerivation { name = "turtlefont"; src = ./.; - nativeBuildInputs = [zig]; + nativeBuildInputs = [ + zig + pkgs.gdb + ]; configurePhase = ""; diff --git a/src/fatfs.zig b/src/fatfs.zig index 98a216e..38f012b 100644 --- a/src/fatfs.zig +++ b/src/fatfs.zig @@ -40,12 +40,12 @@ pub fn stat(path: Path) StatError.Error!FileInfo { pub const ChmodError = ErrorSet(&.{ FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_NO_FILE, FR_NO_PATH, FR_INVALID_NAME, FR_WRITE_PROTECTED, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT, FR_NOT_ENOUGH_CORE }); pub fn chmod(path: Path, attributes: u8, mask: u8) ChmodError.Error!void { - try ChmodError.throw(api.unlink(path.ptr, attributes, mask)); + try ChmodError.throw(api.chmod(path.ptr, attributes, mask)); } pub const UTimeError = ErrorSet(&.{ FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_NO_FILE, FR_NO_PATH, FR_INVALID_NAME, FR_WRITE_PROTECTED, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT, FR_NOT_ENOUGH_CORE }); pub fn utime(path: Path, file_info: c.FILINFO) UTimeError.Error!void { - try UTimeError.throw(api.unlink(path.ptr, &file_info)); + try UTimeError.throw(api.utime(path.ptr, &file_info)); } pub const ChDirError = ErrorSet(&.{ FR_DISK_ERR, FR_INT_ERR, FR_NOT_READY, FR_NO_PATH, FR_INVALID_NAME, FR_INVALID_DRIVE, FR_NOT_ENABLED, FR_NO_FILESYSTEM, FR_TIMEOUT, FR_NOT_ENOUGH_CORE }); diff --git a/test/build.zig b/test/build.zig new file mode 100644 index 0000000..eadf972 --- /dev/null +++ b/test/build.zig @@ -0,0 +1,39 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const zfat_dep = b.dependency("zfat", .{ + .code_page = .us, + .@"sector-size" = @as(u32, 512), + .@"volume-count" = @as(u32, 5), + // .@"volume-names" = @as([]const u8, "a,b,c,h,z"), // TODO(fqu): Requires VolToPart to be defined + + // Enable features: + .find = true, + .mkfs = true, + .fastseek = true, + .expand = true, + .chmod = true, + .label = true, + .forward = true, + .relative_path_api = .enabled_with_getcwd, + // .multi_partition = true, // TODO(fqu): Requires VolToPart to be defined + .lba64 = true, + .use_trim = true, + .exfat = true, + }); + const zfat_mod = zfat_dep.module("zfat"); + + const op_tester = b.addExecutable(.{ + .name = "zfat-op-tester", + .target = target, + .optimize = optimize, + .root_source_file = b.path("op_tester.zig"), + }); + op_tester.root_module.addImport("zfat", zfat_mod); + op_tester.linkLibC(); + + b.installArtifact(op_tester); +} diff --git a/test/build.zig.zon b/test/build.zig.zon new file mode 100644 index 0000000..cbd9381 --- /dev/null +++ b/test/build.zig.zon @@ -0,0 +1,14 @@ +.{ + .name = "zfat-tests", + .version = "0.1.0", + .paths = .{ + "op_tester.zig", + "build.zig", + "build.zig.zon", + }, + .dependencies = .{ + .zfat = .{ + .path = "..", + }, + }, +} diff --git a/test/op_tester.zig b/test/op_tester.zig new file mode 100644 index 0000000..61238d6 --- /dev/null +++ b/test/op_tester.zig @@ -0,0 +1,154 @@ +//! +//! This file implements several tests that try to exercise +//! and perform all possible operations of the zfat library. +//! +const std = @import("std"); +const zfat = @import("zfat"); + +// requires pointer stability +var global_fs: zfat.FileSystem = undefined; + +// requires pointer stability +var ramdisks: [5]RamDisk = .{.{}} ** 5; + +pub const std_options = std.Options{ + .log_level = .info, +}; + +pub fn main() !void { + try ramdisks[0].init(100_000); + + zfat.disks[0] = &ramdisks[0].interface; + + // try mkfs with all formats: + { + const formats = [_]zfat.DiskFormat{ .any, .fat, .exfat, .fat32 }; + var workspace: [4096]u8 = undefined; + + for (formats) |target_fmt| { + std.log.info("format disk with {s}...", .{@tagName(target_fmt)}); + try zfat.mkfs("0:", .{ + .filesystem = target_fmt, + .sector_align = 1, + .use_partitions = true, + }, &workspace); + } + } + + std.log.info("mount disk...", .{}); + try global_fs.mount("0:", true); + defer zfat.FileSystem.unmount("0:") catch |e| std.log.err("failed to unmount filesystem: {s}", .{@errorName(e)}); + + // mkdir + // unlink + // rename + // stat + // chmod + // utime + // chdir + // chdrive + // getcwd + + // Dir.open + // Dir.close + // Dir.next + // Dir.rewind + + // File.open + // File.create + // File.openRead + // File.openWrite + // File.close + // File.sync + // File.truncate + // File.seekTo + // File.expand + // File.endOfFile + // File.hasError + // File.tell + // File.size + // File.rewind + // File.write + // File.read + + // api.fdisk + // api.getfree + // api.getlabel + // api.setlabel + // api.setcp +} + +pub const RamDisk = struct { + const sector_size = 512; + + interface: zfat.Disk = .{ + .getStatusFn = getStatus, + .initializeFn = initialize, + .readFn = read, + .writeFn = write, + .ioctlFn = ioctl, + }, + sectors: [][sector_size]u8 = &.{}, + + pub fn init(rd: *RamDisk, sector_count: usize) !void { + rd.* = .{}; + rd.sectors = try std.heap.page_allocator.alloc([sector_size]u8, sector_count); + } + + pub fn deinit(rd: *RamDisk) void { + if (rd.sectors.len > 0) { + std.heap.page_allocator.free(rd.sectors); + } + rd.sectors = &.{}; + } + + pub fn getStatus(interface: *zfat.Disk) zfat.Disk.Status { + const self: *RamDisk = @fieldParentPtr("interface", interface); + return zfat.Disk.Status{ + .initialized = (self.sectors.len > 0), + .disk_present = true, + .write_protected = false, + }; + } + + pub fn initialize(interface: *zfat.Disk) zfat.Disk.Error!zfat.Disk.Status { + const self: *RamDisk = @fieldParentPtr("interface", interface); + return getStatus(&self.interface); + } + + pub fn read(interface: *zfat.Disk, buff: [*]u8, sector: zfat.LBA, count: c_uint) zfat.Disk.Error!void { + const self: *RamDisk = @fieldParentPtr("interface", interface); + + std.log.debug("read({*}, {}, {})", .{ buff, sector, count }); + + var sectors = std.io.fixedBufferStream(std.mem.sliceAsBytes(self.sectors)); + sectors.seekTo(sector * sector_size) catch return error.IoError; + sectors.reader().readNoEof(buff[0 .. sector_size * count]) catch return error.IoError; + } + + pub fn write(interface: *zfat.Disk, buff: [*]const u8, sector: zfat.LBA, count: c_uint) zfat.Disk.Error!void { + const self: *RamDisk = @fieldParentPtr("interface", interface); + + std.log.debug("write({*}, {}, {})", .{ buff, sector, count }); + + var sectors = std.io.fixedBufferStream(std.mem.sliceAsBytes(self.sectors)); + sectors.seekTo(sector * sector_size) catch return error.IoError; + sectors.writer().writeAll(buff[0 .. sector_size * count]) catch return error.IoError; + } + + pub fn ioctl(interface: *zfat.Disk, cmd: zfat.IoCtl, buff: [*]u8) zfat.Disk.Error!void { + const self: *RamDisk = @fieldParentPtr("interface", interface); + + switch (cmd) { + .sync => {}, + .get_sector_count => { + @as(*align(1) zfat.LBA, @ptrCast(buff)).* = @intCast(self.sectors.len); + }, + .trim => {}, + else => { + std.log.err("invalid ioctl: {}", .{cmd}); + return error.InvalidParameter; + }, + } + } +};