Skip to content

Commit 46d4e59

Browse files
author
Felix "xq" Queißner
committed
Implements basic FAT formatting, but does not write files yet.
1 parent a6ad692 commit 46d4e59

File tree

8 files changed

+272
-168
lines changed

8 files changed

+272
-168
lines changed

build.zig

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,18 @@ pub fn build(b: *std.Build) void {
77

88
const test_step = b.step("test", "Runs the test suite.");
99

10-
// // Dependency Setup:
11-
// const zfat_dep = b.dependency("zfat", .{
12-
// // .max_long_name_len = 121,
13-
// .code_page = .us,
14-
// .@"volume-count" = @as(u32, 1),
15-
// .@"sector-size" = @as(u32, 512),
16-
// // .rtc = .dynamic,
17-
// .mkfs = true,
18-
// .exfat = true,
19-
// });
20-
21-
// const zfat_mod = zfat_dep.module("zfat");
10+
// Dependency Setup:
11+
const zfat_dep = b.dependency("zfat", .{
12+
// .max_long_name_len = 121,
13+
.code_page = .us,
14+
.@"volume-count" = @as(u32, 1),
15+
.@"sector-size" = @as(u32, 512),
16+
// .rtc = .dynamic,
17+
.mkfs = true,
18+
.exfat = true,
19+
});
2220

23-
// const mkfs_fat = b.addExecutable(.{
24-
// .name = "mkfs.fat",
25-
// .target = b.graph.host,
26-
// .optimize = .ReleaseSafe,
27-
// .root_source_file = b.path("src/mkfs.fat.zig"),
28-
// });
29-
// mkfs_fat.root_module.addImport("fat", zfat_mod);
30-
// mkfs_fat.linkLibC();
31-
// b.installArtifact(mkfs_fat);
21+
const zfat_mod = zfat_dep.module("zfat");
3222

3323
const args_dep = b.dependency("args", .{});
3424
const args_mod = args_dep.module("args");
@@ -37,8 +27,10 @@ pub fn build(b: *std.Build) void {
3727
.root_source_file = b.path("src/dim.zig"),
3828
.target = target,
3929
.optimize = optimize,
30+
.link_libc = true,
4031
});
4132
dim_mod.addImport("args", args_mod);
33+
dim_mod.addImport("zfat", zfat_mod);
4234

4335
const dim_exe = b.addExecutable(.{
4436
.name = "dim",

justfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ behaviour-tests: \
2222
(behaviour-test "tests/part/mbr/minimal.dis") \
2323
(behaviour-test "tests/part/mbr/no-part-bootloader.dis") \
2424
(behaviour-test "tests/part/mbr/basic-single-part-sized.dis") \
25+
(behaviour-test "tests/fs/fat12.dis") \
26+
(behaviour-test "tests/fs/fat16.dis") \
2527
(behaviour-test "tests/fs/fat32.dis")
2628

2729
behaviour-test script: install
2830
@mkdir -p {{ join(out, parent_directory(script)) }}
29-
./zig-out/bin/dim --output {{ join(out, without_extension(script) + ".img") }} --script "{{script}}" --size 30M
31+
./zig-out/bin/dim --output {{ join(out, without_extension(script) + ".img") }} --script "{{script}}" --size 33M
3032

3133
# TODO(fqu): sfdisk --json .dim-out/tests/part/mbr/basic-single-part-unsized.img
3234

src/components/fs/FatFileSystem.zig

Lines changed: 186 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ const std = @import("std");
22
const dim = @import("../../dim.zig");
33
const common = @import("common.zig");
44

5+
const fatfs = @import("zfat");
6+
7+
const block_size = 512;
8+
const max_path_len = 8192; // this should be enough
9+
510
const FAT = @This();
611

712
format_as: FatType,
@@ -50,13 +55,191 @@ const Appender = struct {
5055
};
5156

5257
fn render(self: *FAT, stream: *dim.BinaryStream) dim.Content.RenderError!void {
53-
_ = self;
54-
_ = stream;
58+
var bsd: BinaryStreamDisk = .{ .stream = stream };
59+
60+
const min_size, const max_size = self.format_as.get_size_limits();
61+
62+
if (stream.length < min_size) {
63+
// TODO(fqu): Report fatal erro!
64+
std.log.err("cannot format {} bytes with {s}: min required size is {}", .{
65+
@as(dim.DiskSize, @enumFromInt(stream.length)),
66+
@tagName(self.format_as),
67+
@as(dim.DiskSize, @enumFromInt(min_size)),
68+
});
69+
return;
70+
}
71+
72+
if (stream.length > max_size) {
73+
// TODO(fqu): Report warning
74+
std.log.warn("will not use all available space: available space is {}, but maximum size for {s} is {}", .{
75+
@as(dim.DiskSize, @enumFromInt(stream.length)),
76+
@tagName(self.format_as),
77+
@as(dim.DiskSize, @enumFromInt(min_size)),
78+
});
79+
}
80+
81+
var filesystem: fatfs.FileSystem = undefined;
82+
83+
fatfs.disks[0] = &bsd.disk;
84+
defer fatfs.disks[0] = null;
85+
86+
var workspace: [8192]u8 = undefined;
87+
fatfs.mkfs("0:", .{
88+
.filesystem = self.format_as.get_zfat_type(),
89+
.fats = .two,
90+
.sector_align = 0, // default/auto
91+
.rootdir_size = 512, // randomly chosen, might need adjustment
92+
.use_partitions = false,
93+
}, &workspace) catch |err| switch (err) {
94+
error.OutOfMemory => return error.OutOfMemory,
95+
error.WriteProtected => @panic("bug in zfat"),
96+
error.InvalidParameter => @panic("bug in zfat disk wrapper"),
97+
error.DiskErr => return error.IoError,
98+
error.NotReady => @panic("bug in zfat disk wrapper"),
99+
error.InvalidDrive => @panic("bug in AtomicOps"),
100+
error.MkfsAborted => return error.IoError,
101+
};
102+
103+
const ops = self.ops.items;
104+
if (ops.len > 0) {
105+
filesystem.mount("0:", true) catch |err| switch (err) {
106+
error.NotEnabled => @panic("bug in zfat"),
107+
error.DiskErr => return error.IoError,
108+
error.NotReady => @panic("bug in zfat disk wrapper"),
109+
error.InvalidDrive => @panic("bug in AtomicOps"),
110+
error.NoFilesystem => @panic("bug in zfat"),
111+
};
112+
113+
const wrapper = AtomicOps{};
114+
115+
for (ops) |op| {
116+
try op.execute(wrapper);
117+
}
118+
}
55119
}
56120

57121
const FatType = enum {
58122
fat12,
59123
fat16,
60124
fat32,
61-
exfat,
125+
// exfat,
126+
127+
fn get_zfat_type(fat: FatType) fatfs.DiskFormat {
128+
return switch (fat) {
129+
.fat12 => .fat,
130+
.fat16 => .fat,
131+
.fat32 => .fat32,
132+
// .exfat => .exfat,
133+
};
134+
}
135+
136+
fn get_size_limits(fat: FatType) struct { u64, u64 } {
137+
// see https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#Size_limits
138+
return switch (fat) {
139+
.fat12 => .{ 512, 133_824_512 }, // 512 B ... 127 MB
140+
.fat16 => .{ 2_091_520, 2_147_090_432 }, // 2042.5 kB ... 2047 MB
141+
.fat32 => .{ 33_548_800, 1_099_511_578_624 }, // 32762.5 kB ... 1024 GB
142+
};
143+
}
144+
};
145+
146+
const AtomicOps = struct {
147+
pub fn mkdir(ops: AtomicOps, path: []const u8) !void {
148+
_ = ops;
149+
150+
var path_buffer: [max_path_len:0]u8 = undefined;
151+
var fba: std.heap.FixedBufferAllocator = .init(&path_buffer);
152+
153+
const joined = try std.mem.concatWithSentinel(fba.allocator(), u8, &.{ "0:/", path }, 0);
154+
fatfs.mkdir(joined) catch |err| switch (err) {
155+
error.Exist => {}, // this is good
156+
else => |e| return e,
157+
};
158+
}
159+
160+
pub fn mkfile(ops: AtomicOps, path: []const u8, host_file: std.fs.File) !void {
161+
_ = ops;
162+
163+
var path_buffer: [max_path_len:0]u8 = undefined;
164+
if (path.len > path_buffer.len)
165+
return error.InvalidPath;
166+
@memcpy(path_buffer[0..path.len], path);
167+
path_buffer[path.len] = 0;
168+
169+
const path_z = path_buffer[0..path.len :0];
170+
171+
const stat = try host_file.stat();
172+
173+
const size = std.math.cast(u32, stat.size) orelse return error.FileTooBig;
174+
175+
_ = size;
176+
177+
var fs_file = try fatfs.File.create(path_z);
178+
defer fs_file.close();
179+
180+
var fifo: std.fifo.LinearFifo(u8, .{ .Static = 8192 }) = .init();
181+
try fifo.pump(
182+
host_file.reader(),
183+
fs_file.writer(),
184+
);
185+
}
186+
};
187+
188+
const BinaryStreamDisk = struct {
189+
disk: fatfs.Disk = .{
190+
.getStatusFn = disk_getStatus,
191+
.initializeFn = disk_initialize,
192+
.readFn = disk_read,
193+
.writeFn = disk_write,
194+
.ioctlFn = disk_ioctl,
195+
},
196+
stream: *dim.BinaryStream,
197+
198+
fn disk_getStatus(intf: *fatfs.Disk) fatfs.Disk.Status {
199+
_ = intf;
200+
return .{
201+
.initialized = true,
202+
.disk_present = true,
203+
.write_protected = false,
204+
};
205+
}
206+
207+
fn disk_initialize(intf: *fatfs.Disk) fatfs.Disk.Error!fatfs.Disk.Status {
208+
return disk_getStatus(intf);
209+
}
210+
211+
fn disk_read(intf: *fatfs.Disk, buff: [*]u8, sector: fatfs.LBA, count: c_uint) fatfs.Disk.Error!void {
212+
const bsd: *BinaryStreamDisk = @fieldParentPtr("disk", intf);
213+
214+
bsd.stream.read(block_size * sector, buff[0 .. count * block_size]) catch return error.IoError;
215+
}
216+
217+
fn disk_write(intf: *fatfs.Disk, buff: [*]const u8, sector: fatfs.LBA, count: c_uint) fatfs.Disk.Error!void {
218+
const bsd: *BinaryStreamDisk = @fieldParentPtr("disk", intf);
219+
220+
bsd.stream.write(block_size * sector, buff[0 .. count * block_size]) catch return error.IoError;
221+
}
222+
223+
fn disk_ioctl(intf: *fatfs.Disk, cmd: fatfs.IoCtl, buff: [*]u8) fatfs.Disk.Error!void {
224+
const bsd: *BinaryStreamDisk = @fieldParentPtr("disk", intf);
225+
226+
switch (cmd) {
227+
.sync => {},
228+
229+
.get_sector_count => {
230+
const size: *fatfs.LBA = @ptrCast(@alignCast(buff));
231+
size.* = @intCast(bsd.stream.length / block_size);
232+
},
233+
.get_sector_size => {
234+
const size: *fatfs.WORD = @ptrCast(@alignCast(buff));
235+
size.* = block_size;
236+
},
237+
.get_block_size => {
238+
const size: *fatfs.DWORD = @ptrCast(@alignCast(buff));
239+
size.* = 1;
240+
},
241+
242+
else => return error.InvalidParameter,
243+
}
244+
}
62245
};

src/components/fs/common.zig

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,24 @@ pub const FsOperation = union(enum) {
2525
size: u64,
2626
contents: dim.Content,
2727
},
28+
29+
pub fn execute(op: FsOperation, executor: anytype) !void {
30+
_ = executor;
31+
switch (op) {
32+
.copy_file => |data| {
33+
_ = data;
34+
},
35+
.copy_dir => |data| {
36+
_ = data;
37+
},
38+
.make_dir => |data| {
39+
_ = data;
40+
},
41+
.create_file => |data| {
42+
_ = data;
43+
},
44+
}
45+
}
2846
};
2947

3048
fn parse_path(ctx: dim.Context) ![]const u8 {

src/dim.zig

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ const Tokenizer = @import("Tokenizer.zig");
77
const Parser = @import("Parser.zig");
88
const args = @import("args");
99

10+
comptime {
11+
// Ensure zfat is linked to prevent compiler errors!
12+
_ = @import("zfat");
13+
}
14+
1015
const max_script_size = 10 * DiskSize.MiB;
1116

1217
const Options = struct {
@@ -128,16 +133,14 @@ pub fn main() !u8 {
128133
}
129134

130135
{
131-
var output_file = try current_dir.atomicFile(output_path, .{});
132-
defer output_file.deinit();
136+
var output_file = try current_dir.createFile(output_path, .{ .read = true });
137+
defer output_file.close();
133138

134-
try output_file.file.setEndPos(size_limit);
139+
try output_file.setEndPos(size_limit);
135140

136-
var stream: BinaryStream = .init_file(output_file.file, size_limit);
141+
var stream: BinaryStream = .init_file(output_file, size_limit);
137142

138143
try root_content.render(&stream);
139-
140-
try output_file.finish();
141144
}
142145

143146
return 0;
@@ -345,6 +348,7 @@ pub const Content = struct {
345348
pub const RenderError = FileName.OpenError || FileHandle.ReadError || BinaryStream.WriteError || error{
346349
ConfigurationError,
347350
OutOfBounds,
351+
OutOfMemory,
348352
};
349353
pub const GuessError = FileName.GetSizeError;
350354

@@ -517,6 +521,7 @@ pub const FileHandle = struct {
517521

518522
pub const BinaryStream = struct {
519523
pub const WriteError = error{ Overflow, IoError };
524+
pub const ReadError = error{ Overflow, IoError };
520525
pub const Writer = std.io.Writer(*BinaryStream, WriteError, write_some);
521526

522527
backing: Backing,
@@ -569,6 +574,38 @@ pub const BinaryStream = struct {
569574
};
570575
}
571576

577+
pub fn read(bs: *BinaryStream, offset: u64, data: []u8) ReadError!void {
578+
const end_pos = offset + data.len;
579+
if (end_pos > bs.length)
580+
return error.Overflow;
581+
582+
switch (bs.backing) {
583+
.buffer => |ptr| @memcpy(data, ptr[@intCast(offset)..][0..data.len]),
584+
.file => |state| {
585+
state.file.seekTo(state.base + offset) catch return error.IoError;
586+
state.file.reader().readNoEof(data) catch |err| switch (err) {
587+
error.InputOutput,
588+
error.AccessDenied,
589+
error.BrokenPipe,
590+
error.SystemResources,
591+
error.OperationAborted,
592+
error.LockViolation,
593+
error.WouldBlock,
594+
error.ConnectionResetByPeer,
595+
error.ProcessNotFound,
596+
error.Unexpected,
597+
error.IsDir,
598+
error.ConnectionTimedOut,
599+
error.NotOpenForReading,
600+
error.SocketNotConnected,
601+
error.Canceled,
602+
error.EndOfStream,
603+
=> return error.IoError,
604+
};
605+
},
606+
}
607+
}
608+
572609
pub fn write(bs: *BinaryStream, offset: u64, data: []const u8) WriteError!void {
573610
const end_pos = offset + data.len;
574611
if (end_pos > bs.length)
@@ -636,7 +673,7 @@ test {
636673
_ = Parser;
637674
}
638675

639-
const DiskSize = enum(u64) {
676+
pub const DiskSize = enum(u64) {
640677
const KiB = 1024;
641678
const MiB = 1024 * 1024;
642679
const GiB = 1024 * 1024 * 1024;

0 commit comments

Comments
 (0)