Skip to content

Commit

Permalink
Starts to implement exerciser application
Browse files Browse the repository at this point in the history
  • Loading branch information
Felix "xq" Queißner committed Oct 11, 2024
1 parent 6c9fcec commit 0445749
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 3 deletions.
5 changes: 4 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@
packages.default = pkgs.stdenv.mkDerivation {
name = "turtlefont";
src = ./.;
nativeBuildInputs = [zig];
nativeBuildInputs = [
zig
pkgs.gdb
];

configurePhase = "";

Expand Down
4 changes: 2 additions & 2 deletions src/fatfs.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
Expand Down
39 changes: 39 additions & 0 deletions test/build.zig
Original file line number Diff line number Diff line change
@@ -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);
}
14 changes: 14 additions & 0 deletions test/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.{
.name = "zfat-tests",
.version = "0.1.0",
.paths = .{
"op_tester.zig",
"build.zig",
"build.zig.zon",
},
.dependencies = .{
.zfat = .{
.path = "..",
},
},
}
154 changes: 154 additions & 0 deletions test/op_tester.zig
Original file line number Diff line number Diff line change
@@ -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;
},
}
}
};

0 comments on commit 0445749

Please sign in to comment.