From c11cc7189b137c56138a0a6c7986a8151a127cd7 Mon Sep 17 00:00:00 2001 From: Clara Date: Tue, 3 Jun 2025 19:31:05 +0100 Subject: [PATCH 1/4] Implement GPT support --- README.md | 16 + src/BuildInterface.zig | 53 +++ src/components/part/GptPartitionTable.zig | 420 +++++++++++++++++++--- src/components/part/MbrPartitionTable.zig | 4 +- src/dim.zig | 3 +- 5 files changed, 450 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index cd908cf..8515929 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,23 @@ A complete list can be [found on Wikipedia](https://en.wikipedia.org/wiki/Partit ### GPT Partition Table (`gpt-part`) ```plain +gpt-part + [disk-id ] # optional, random guid used + [part <…>] # partition 1 + [part <…>] # partition 2 + […] # up to 128 partitions total +endgpt +``` +```plain +part + type + [part-id ] # optional, random guid used + [size ] + [offset ] + [name ] + contains +endpart ``` ### FAT File System (`vfat`) diff --git a/src/BuildInterface.zig b/src/BuildInterface.zig index 724b644..6843ce3 100644 --- a/src/BuildInterface.zig +++ b/src/BuildInterface.zig @@ -152,6 +152,40 @@ const ContentWriter = struct { } }, + .gpt_part_table => |data| { + try cw.code.writeAll("gpt-part\n"); + + for (data.partitions) |part| { + try cw.code.writeAll(" part\n"); + try cw.code.writeAll(" type "); + switch (part.type) { + .name => |name| { + try cw.code.writeAll(@tagName(name)); + }, + .guid => |guid_text| { + try cw.code.writeAll(&guid_text); + }, + } + try cw.code.writeByte('\n'); + + if (part.name) |name| { + try cw.code.print(" name {s}\n", .{name}); + } + if (part.offset) |offset| { + try cw.code.print(" offset {d}\n", .{offset}); + } + if (part.size) |size| { + try cw.code.print(" size {d}\n", .{size}); + } + try cw.code.writeAll(" contains"); + try cw.render(part.data); + try cw.code.writeAll("\n"); + try cw.code.writeAll(" endpart\n"); + } + + try cw.code.writeAll("endgpt"); + }, + .vfat => |data| { try cw.code.print("vfat {s}\n", .{ @tagName(data.format), @@ -291,6 +325,7 @@ pub const Content = union(enum) { fill: u8, paste_file: std.Build.LazyPath, mbr_part_table: MbrPartTable, + gpt_part_table: GptPartTable, vfat: FatFs, }; @@ -317,6 +352,24 @@ pub const MbrPartTable = struct { }; }; +pub const GptPartTable = struct { + partitions: []const Partition, + + pub const Partition = struct { + type: union(enum) { + name: enum { + unused, + @"efi-system", + }, + guid: [36]u8, + }, + name: ?[]const u8 = null, + size: ?u64 = null, + offset: ?u64 = null, + data: Content, + }; +}; + pub const FatFs = struct { format: enum { fat12, diff --git a/src/components/part/GptPartitionTable.zig b/src/components/part/GptPartitionTable.zig index 0252037..f83f8dd 100644 --- a/src/components/part/GptPartitionTable.zig +++ b/src/components/part/GptPartitionTable.zig @@ -1,68 +1,402 @@ const std = @import("std"); +const MbrPartTable = @import("MbrPartitionTable.zig"); const dim = @import("../../dim.zig"); -pub fn execute(ctx: dim.Context) !void { - _ = ctx; - @panic("gpt-part not implemented yet!"); -} +const PartTable = @This(); -pub const gpt = struct { - pub const Guid = [16]u8; +const block_size = 512; // TODO support other block sizes - pub const Table = struct { - disk_id: Guid, +disk_id: ?Guid, +partitions: []Partition, - partitions: []const Partition, +pub fn parse(ctx: dim.Context) !dim.Content { + const pt = try ctx.alloc_object(PartTable); + pt.* = PartTable{ + .disk_id = null, + .partitions = undefined, }; - pub const Partition = struct { - type: Guid, - part_id: Guid, + var partitions = std.ArrayList(Partition).init(ctx.get_arena()); + loop: while (true) { + const kw = try ctx.parse_enum(enum { + guid, + part, + endgpt, + }); + switch (kw) { + .guid => { + const guid_str = try ctx.parse_string(); + if (guid_str.len != 36) + return ctx.report_fatal_error("Invalid disk GUID: wrong length", .{}); - offset: ?u64 = null, - size: u64, + pt.disk_id = Guid.parse(guid_str[0..36].*) catch |err| + return ctx.report_fatal_error("Invalid disk GUID: {}", .{err}); + }, + .part => (try partitions.addOne()).* = try parsePartition(ctx), + .endgpt => break :loop, + } + } - name: [36]u16, + pt.partitions = partitions.items; - attributes: Attributes, + if (pt.partitions.len != 0) { + for (pt.partitions[0..pt.partitions.len -| 2], 0..) |part, i| { + if (part.size == null) { + try ctx.report_nonfatal_error("GPT partition {} does not have a size, but it is not last.", .{i}); + } + } - // data: Content, + var all_auto = true; + var all_manual = true; - pub const Attributes = packed struct(u32) { - system: bool, - efi_hidden: bool, - legacy: bool, - read_only: bool, - hidden: bool, - no_automount: bool, + for (pt.partitions) |part| { + if (part.offset != null) { + all_auto = false; + } else { + all_manual = false; + } + } - padding: u26 = 0, - }; + if (!all_auto and !all_manual) { + try ctx.report_nonfatal_error("not all partitions have an explicit offset!", .{}); + } + } + + if (pt.partitions.len > 128) { + return ctx.report_fatal_error("number of partitions ({}) exceeded maximum of 128", .{pt.partitions.len}); + } + + return .create_handle(pt, .create(PartTable, .{ + .render_fn = render, + })); +} + +fn parsePartition(ctx: dim.Context) !Partition { + var part = Partition{ + .type = undefined, + .part_id = null, + .size = null, + .name = @splat(0), + .offset = null, + .attributes = .{ + .required = false, + .no_block_io_protocol = false, + .legacy = false, + }, + .contains = .empty, }; - /// https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs - pub const PartitionType = struct { - pub const unused: Guid = .{}; + var updater: dim.FieldUpdater(Partition, &.{ + .part_id, + .size, + .name, + .offset, + .attributes, + }) = .init(ctx, &part); + + parse_loop: while (true) { + const kw = try ctx.parse_enum(enum { + type, + name, + guid, + size, + offset, + contains, + endpart, + }); + switch (kw) { + .type => { + const type_name = try ctx.parse_string(); + + const type_guid = known_types.get(type_name) orelse blk: { + if (type_name.len == 36) if (Guid.parse(type_name[0..36].*)) |guid| break :blk guid else |_| {}; + return ctx.report_fatal_error("unknown partition type: `{}`", .{std.zig.fmtEscapes(type_name)}); + }; + + try updater.set(.type, type_guid); + }, + .name => { + const string = try ctx.parse_string(); + const name = stringToName(string) catch return error.BadStringLiteral; + + try updater.set(.name, name); + }, + .guid => { + const string = try ctx.parse_string(); + if (string.len != 36) + return ctx.report_fatal_error("Invalid partition GUID: wrong length", .{}); + + try updater.set(.part_id, Guid.parse(string[0..36].*) catch |err| { + return ctx.report_fatal_error("Invalid partition GUID: {}", .{err}); + }); + }, + .size => try updater.set(.size, try ctx.parse_mem_size()), + .offset => try updater.set(.offset, try ctx.parse_mem_size()), + .contains => try updater.set(.contains, try ctx.parse_content()), + .endpart => break :parse_loop, + } + } + + try updater.validate(); + + return part; +} + +pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderError!void { + const random = std.crypto.random; + + const lba_len = stream.length / block_size; + const secondary_pth_lba = lba_len - 1; + const secondary_pe_array_lba = secondary_pth_lba - 32; + const max_partition_lba = secondary_pe_array_lba - 1; - pub const microsoft_basic_data: Guid = .{}; - pub const microsoft_reserved: Guid = .{}; + // create the partition entry array, lba 2 through 33 + var pe_blocks: [block_size * 32]u8 = @splat(0); + const partition_entries = std.mem.bytesAsSlice([0x80]u8, &pe_blocks); - pub const windows_recovery: Guid = .{}; + var next_lba: u64 = 34; + for (table.partitions[0..], partition_entries[0..table.partitions.len], 0..) |partition, *entry, i| { + const offset = partition.offset orelse next_lba * block_size; + const size = partition.size orelse if (i == table.partitions.len - 1) + ((max_partition_lba + 1) * block_size) - offset + else + return error.ConfigurationError; - pub const plan9: Guid = .{}; + if (offset % block_size != 0) { + std.log.err("partition offset is not divisible by {}!", .{block_size}); + return error.ConfigurationError; + } - pub const linux_swap: Guid = .{}; - pub const linux_fs: Guid = .{}; - pub const linux_reserved: Guid = .{}; - pub const linux_lvm: Guid = .{}; + if (size % block_size != 0) { + std.log.err("partition size is not divisible by {}!", .{block_size}); + return error.ConfigurationError; + } + + const start_lba = @divExact(offset, block_size); + const end_lba = @divExact(size + offset, block_size) - 1; + + if (start_lba <= 33) { + std.log.err("partition {} overlaps with gpt. the partition begins at lba {}, and the gpt ends at {}", .{ i + 1, start_lba, 33 }); + return error.ConfigurationError; + } + + if (end_lba >= secondary_pe_array_lba) { + std.log.err("partition {} overlaps with backup gpt. the partition ends at lba {}, and the backup gpt starts at {}", .{ i + 1, end_lba, secondary_pe_array_lba }); + return error.ConfigurationError; + } + + entry[0x00..0x10].* = @bitCast(partition.type); + entry[0x10..0x20].* = @bitCast(partition.part_id orelse Guid.rand(random)); + std.mem.writeInt(u64, entry[0x20..0x28], start_lba, .little); + std.mem.writeInt(u64, entry[0x28..0x30], end_lba, .little); + // TODO attributes + entry[0x38..].* = @bitCast(partition.name); + + var sub_view = try stream.slice(offset, size); + try partition.contains.render(&sub_view); + + next_lba = end_lba + 1; + } + + // create the protective mbr + var mbr: MbrPartTable = .{ + .bootloader = null, + .disk_id = null, + .partitions = .{ .{ + .offset = block_size, + .size = null, + .bootable = false, + .type = 0xee, + .contains = .empty, + }, null, null, null }, }; - pub fn nameLiteral(comptime name: []const u8) [36]u16 { - return comptime blk: { - var buf: [36]u16 = undefined; - const len = std.unicode.utf8ToUtf16Le(&buf, name) catch |err| @compileError(@tagName(err)); - @memset(buf[len..], 0); - break :blk &buf; + var gpt_header_block: [block_size]u8 = @splat(0); + const gpt_header = gpt_header_block[0x0..0x5c]; + + gpt_header[0x00..0x08].* = "EFI PART".*; + gpt_header[0x08..0x0c].* = .{ 0x0, 0x0, 0x1, 0x0 }; + std.mem.writeInt(u32, gpt_header[0x0c..0x10], 0x5c, .little); // Header size + std.mem.writeInt(u64, gpt_header[0x18..0x20], 1, .little); // LBA of this header + std.mem.writeInt(u64, gpt_header[0x20..0x28], secondary_pth_lba, .little); // LBA of other header + std.mem.writeInt(u64, gpt_header[0x28..0x30], 34, .little); // First usable LBA + std.mem.writeInt(u64, gpt_header[0x30..0x38], max_partition_lba, .little); // Last usable LBA + gpt_header[0x38..0x48].* = @bitCast(table.disk_id orelse Guid.rand(random)); + std.mem.writeInt(u64, gpt_header[0x48..0x50], 2, .little); // First LBA of the partition entry array + std.mem.writeInt(u32, gpt_header[0x50..0x54], 0x80, .little); // Number of partition entries + std.mem.writeInt(u32, gpt_header[0x54..0x58], 0x80, .little); // Size of a partition entry + + var backup_gpt_header_block: [block_size]u8 = gpt_header_block; + const backup_gpt_header = backup_gpt_header_block[0x0..0x5c]; + + std.mem.writeInt(u64, backup_gpt_header[0x18..0x20], secondary_pth_lba, .little); // LBA of this header + std.mem.writeInt(u64, backup_gpt_header[0x20..0x28], 1, .little); // LBA of other header + std.mem.writeInt(u64, backup_gpt_header[0x48..0x50], secondary_pe_array_lba, .little); // First LBA of the backup partition entry array + + const pe_array_crc32 = std.hash.Crc32.hash(&pe_blocks); + std.mem.writeInt(u32, gpt_header[0x58..0x5c], pe_array_crc32, .little); // CRC32 of partition entries array + std.mem.writeInt(u32, backup_gpt_header[0x58..0x5c], pe_array_crc32, .little); // CRC32 of partition entries array + + const gpt_header_crc32 = std.hash.Crc32.hash(gpt_header); + std.mem.writeInt(u32, gpt_header[0x10..0x14], gpt_header_crc32, .little); // CRC32 of header + + const backup_gpt_header_crc32 = std.hash.Crc32.hash(backup_gpt_header); + std.mem.writeInt(u32, backup_gpt_header[0x10..0x14], backup_gpt_header_crc32, .little); // CRC32 of backup header + + // write everything we generated to disk + try mbr.render(stream); + try stream.write(block_size, &gpt_header_block); + try stream.write(block_size * 2, &pe_blocks); + try stream.write(block_size * secondary_pe_array_lba, &pe_blocks); + try stream.write(block_size * secondary_pth_lba, &backup_gpt_header_block); +} + +fn crc32Header(header: [0x5c]u8) u32 { + var crc32 = std.hash.Crc32.init(); + crc32.update(header[0x00..0x14]); + crc32.update(header[0x18..]); + return crc32.final(); +} + +pub const Guid = extern struct { + time_low: u32, // LE + time_mid: u16, // LE + time_high_and_version: u16, // LE + clock_seq_high_and_reserved: u8, + clock_seq_low: u8, + node: [6]u8, // byte array + + pub const epoch = -12219292725; + + pub fn rand(random: std.Random) Guid { + var ret: Guid = undefined; + random.bytes(std.mem.asBytes(&ret)); + + ret.clock_seq_high_and_reserved &= 0b00111111; + ret.clock_seq_high_and_reserved |= 0b10000000; + + ret.time_high_and_version &= std.mem.nativeToLittle(u16, 0b00001111_11111111); + ret.time_high_and_version |= std.mem.nativeToLittle(u16, 0b01000000_00000000); + + return ret; + } + + pub fn parse(str: [36]u8) !Guid { + const tl_hex = str[0..8]; + if (str[8] != '-') return error.MissingSeparator; + const tm_hex = str[9..13]; + if (str[13] != '-') return error.MissingSeparator; + const th_hex = str[14..18]; + if (str[18] != '-') return error.MissingSeparator; + const cs_hex = str[19..23]; + if (str[23] != '-') return error.MissingSeparator; + const node_hex = str[24..36]; + + const tl_be: u32 = @bitCast(try hexToBytes(tl_hex.*)); + const tm_be: u16 = @bitCast(try hexToBytes(tm_hex.*)); + const th_be: u16 = @bitCast(try hexToBytes(th_hex.*)); + const cs_bytes = try hexToBytes(cs_hex.*); + const node_bytes = try hexToBytes(node_hex.*); + + const tl_le = @byteSwap(tl_be); + const tm_le = @byteSwap(tm_be); + const th_le = @byteSwap(th_be); + const csh = cs_bytes[0]; + const csl = cs_bytes[1]; + + return Guid{ + .time_low = tl_le, + .time_mid = tm_le, + .time_high_and_version = th_le, + .clock_seq_high_and_reserved = csh, + .clock_seq_low = csl, + .node = node_bytes, }; } + + fn HexToBytesType(comptime T: type) type { + const ti = @typeInfo(T); + const len = @divExact(ti.array.len, 2); + return @Type(.{ .array = .{ + .len = len, + .child = u8, + .sentinel_ptr = null, + } }); + } + + fn hexToBytes(hex: anytype) !HexToBytesType(@TypeOf(hex)) { + var ret: [@divExact(hex.len, 2)]u8 = undefined; + + for (0..ret.len) |i| { + const hi = try std.fmt.charToDigit(hex[i * 2], 16); + const lo = try std.fmt.charToDigit(hex[i * 2 + 1], 16); + ret[i] = (hi << 4) | lo; + } + + return ret; + } }; + +pub const Partition = struct { + type: Guid, + part_id: ?Guid, + + offset: ?u64 = null, + size: ?u64 = null, + + name: [36]u16, + + attributes: Attributes, + + contains: dim.Content, + + pub const Attributes = packed struct(u64) { + required: bool, // should be true for an esp + no_block_io_protocol: bool, + legacy: bool, + + reserved: u45 = 0, + + type_specific: u16 = 0, + }; +}; + +// TODO fill from https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs +pub const known_types = std.StaticStringMap(Guid).initComptime(.{ + .{ "unused", Guid.parse("00000000-0000-0000-0000-000000000000".*) catch unreachable }, + .{ "efi-system", Guid.parse("C12A7328-F81F-11D2-BA4B-00A0C93EC93B".*) catch unreachable }, +}); + +// struct { +// pub const unused: Guid = .{}; + +// pub const microsoft_basic_data: Guid = .{}; +// pub const microsoft_reserved: Guid = .{}; + +// pub const windows_recovery: Guid = .{}; + +// pub const plan9: Guid = .{}; + +// pub const linux_swap: Guid = .{}; +// pub const linux_fs: Guid = .{}; +// pub const linux_reserved: Guid = .{}; +// pub const linux_lvm: Guid = .{}; +// }; + +pub fn nameLiteral(comptime name: []const u8) [36]u16 { + return comptime blk: { + var buf: [36]u16 = undefined; + const len = std.unicode.utf8ToUtf16Le(&buf, name) catch |err| @compileError(@tagName(err)); + @memset(buf[len..], 0); + break :blk &buf; + }; +} + +pub fn stringToName(name: []const u8) ![36]u16 { + var buf: [36]u16 = @splat(0); + + if (try std.unicode.calcUtf16LeLen(name) > 36) return error.StringTooLong; + + _ = try std.unicode.utf8ToUtf16Le(&buf, name); + return buf; +} diff --git a/src/components/part/MbrPartitionTable.zig b/src/components/part/MbrPartitionTable.zig index 16589d4..66ceca8 100644 --- a/src/components/part/MbrPartitionTable.zig +++ b/src/components/part/MbrPartitionTable.zig @@ -134,7 +134,7 @@ fn parse_partition(ctx: dim.Context) !Partition { return part; } -fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderError!void { +pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderError!void { const last_part_id = blk: { var last: usize = 0; for (table.partitions, 0..) |p, i| { @@ -249,7 +249,7 @@ fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderError! } pub const Partition = struct { - offset: ?u64 = null, + offset: ?u64, size: ?u64, bootable: bool, diff --git a/src/dim.zig b/src/dim.zig index 83e1fc1..5e7e487 100644 --- a/src/dim.zig +++ b/src/dim.zig @@ -198,7 +198,7 @@ fn fatal(msg: []const u8) noreturn { const content_types: []const struct { []const u8, type } = &.{ .{ "mbr-part", @import("components/part/MbrPartitionTable.zig") }, - // .{ "gpt-part", @import("components/part/GptPartitionTable.zig") }, + .{ "gpt-part", @import("components/part/GptPartitionTable.zig") }, .{ "vfat", @import("components/fs/FatFileSystem.zig") }, .{ "paste-file", @import("components/PasteFile.zig") }, .{ "empty", @import("components/EmptyData.zig") }, @@ -747,6 +747,7 @@ pub const BinaryStream = struct { error.DeviceBusy, error.InvalidArgument, error.AccessDenied, + error.PermissionDenied, error.BrokenPipe, error.SystemResources, error.OperationAborted, From f96a6f666c9fc369d100c8eef1b6452058d7e04c Mon Sep 17 00:00:00 2001 From: Khitiara Date: Tue, 3 Jun 2025 16:52:17 -0400 Subject: [PATCH 2/4] Address Nits --- src/components/part/GptPartitionTable.zig | 136 ++++++++++------------ src/dim.zig | 1 - 2 files changed, 61 insertions(+), 76 deletions(-) diff --git a/src/components/part/GptPartitionTable.zig b/src/components/part/GptPartitionTable.zig index f83f8dd..7bf2b4a 100644 --- a/src/components/part/GptPartitionTable.zig +++ b/src/components/part/GptPartitionTable.zig @@ -151,11 +151,19 @@ pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderEr const max_partition_lba = secondary_pe_array_lba - 1; // create the partition entry array, lba 2 through 33 - var pe_blocks: [block_size * 32]u8 = @splat(0); - const partition_entries = std.mem.bytesAsSlice([0x80]u8, &pe_blocks); + var pe_block: [0x80]u8 = undefined; + var pe_crc: std.hash.Crc32 = .init(); + + var pe_ofs: usize = 0; + + var next_lba: u64 = 2 + (std.math.divCeil(u64, table.partitions.len * 0x80, block_size) catch |e| switch (e) { + error.DivisionByZero => unreachable, + inline else => |e2| return e2, + }); + const pe_end_plus_one_lba = next_lba; + for (table.partitions[0..], 0..) |partition, i| { + @memset(&pe_block, 0); - var next_lba: u64 = 34; - for (table.partitions[0..], partition_entries[0..table.partitions.len], 0..) |partition, *entry, i| { const offset = partition.offset orelse next_lba * block_size; const size = partition.size orelse if (i == table.partitions.len - 1) ((max_partition_lba + 1) * block_size) - offset @@ -175,8 +183,8 @@ pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderEr const start_lba = @divExact(offset, block_size); const end_lba = @divExact(size + offset, block_size) - 1; - if (start_lba <= 33) { - std.log.err("partition {} overlaps with gpt. the partition begins at lba {}, and the gpt ends at {}", .{ i + 1, start_lba, 33 }); + if (start_lba <= pe_end_plus_one_lba) { + std.log.err("partition {} overlaps with gpt. the partition begins at lba {}, and the gpt ends at {}", .{ i + 1, start_lba, pe_end_plus_one_lba }); return error.ConfigurationError; } @@ -185,19 +193,26 @@ pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderEr return error.ConfigurationError; } - entry[0x00..0x10].* = @bitCast(partition.type); - entry[0x10..0x20].* = @bitCast(partition.part_id orelse Guid.rand(random)); - std.mem.writeInt(u64, entry[0x20..0x28], start_lba, .little); - std.mem.writeInt(u64, entry[0x28..0x30], end_lba, .little); + pe_block[0x00..0x10].* = @bitCast(partition.type); + (partition.part_id orelse Guid.rand(random)).write(pe_block[0x10..0x20]); + std.mem.writeInt(u64, pe_block[0x20..0x28], start_lba, .little); + std.mem.writeInt(u64, pe_block[0x28..0x30], end_lba, .little); // TODO attributes - entry[0x38..].* = @bitCast(partition.name); + pe_block[0x38..].* = @bitCast(partition.name); + + pe_crc.update(&pe_block); + try stream.write(block_size * 2 + pe_ofs, &pe_block); + try stream.write(block_size * secondary_pe_array_lba + pe_ofs, &pe_block); var sub_view = try stream.slice(offset, size); try partition.contains.render(&sub_view); next_lba = end_lba + 1; + pe_ofs += 0x80; } + const pe_array_crc32 = pe_crc.final(); + // create the protective mbr var mbr: MbrPartTable = .{ .bootloader = null, @@ -221,7 +236,7 @@ pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderEr std.mem.writeInt(u64, gpt_header[0x20..0x28], secondary_pth_lba, .little); // LBA of other header std.mem.writeInt(u64, gpt_header[0x28..0x30], 34, .little); // First usable LBA std.mem.writeInt(u64, gpt_header[0x30..0x38], max_partition_lba, .little); // Last usable LBA - gpt_header[0x38..0x48].* = @bitCast(table.disk_id orelse Guid.rand(random)); + (table.disk_id orelse Guid.rand(random)).write(gpt_header[0x38..0x48]); std.mem.writeInt(u64, gpt_header[0x48..0x50], 2, .little); // First LBA of the partition entry array std.mem.writeInt(u32, gpt_header[0x50..0x54], 0x80, .little); // Number of partition entries std.mem.writeInt(u32, gpt_header[0x54..0x58], 0x80, .little); // Size of a partition entry @@ -233,7 +248,6 @@ pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderEr std.mem.writeInt(u64, backup_gpt_header[0x20..0x28], 1, .little); // LBA of other header std.mem.writeInt(u64, backup_gpt_header[0x48..0x50], secondary_pe_array_lba, .little); // First LBA of the backup partition entry array - const pe_array_crc32 = std.hash.Crc32.hash(&pe_blocks); std.mem.writeInt(u32, gpt_header[0x58..0x5c], pe_array_crc32, .little); // CRC32 of partition entries array std.mem.writeInt(u32, backup_gpt_header[0x58..0x5c], pe_array_crc32, .little); // CRC32 of partition entries array @@ -246,18 +260,9 @@ pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderEr // write everything we generated to disk try mbr.render(stream); try stream.write(block_size, &gpt_header_block); - try stream.write(block_size * 2, &pe_blocks); - try stream.write(block_size * secondary_pe_array_lba, &pe_blocks); try stream.write(block_size * secondary_pth_lba, &backup_gpt_header_block); } -fn crc32Header(header: [0x5c]u8) u32 { - var crc32 = std.hash.Crc32.init(); - crc32.update(header[0x00..0x14]); - crc32.update(header[0x18..]); - return crc32.final(); -} - pub const Guid = extern struct { time_low: u32, // LE time_mid: u16, // LE @@ -282,6 +287,8 @@ pub const Guid = extern struct { } pub fn parse(str: [36]u8) !Guid { + @setEvalBranchQuota(16384); + const tl_hex = str[0..8]; if (str[8] != '-') return error.MissingSeparator; const tm_hex = str[9..13]; @@ -292,48 +299,37 @@ pub const Guid = extern struct { if (str[23] != '-') return error.MissingSeparator; const node_hex = str[24..36]; - const tl_be: u32 = @bitCast(try hexToBytes(tl_hex.*)); - const tm_be: u16 = @bitCast(try hexToBytes(tm_hex.*)); - const th_be: u16 = @bitCast(try hexToBytes(th_hex.*)); - const cs_bytes = try hexToBytes(cs_hex.*); - const node_bytes = try hexToBytes(node_hex.*); + const tl: u32 = try std.fmt.parseInt(u32, tl_hex, 16); + const tm: u16 = try std.fmt.parseInt(u16, tm_hex, 16); + const th: u16 = try std.fmt.parseInt(u16, th_hex, 16); + const cs: u16 = try std.fmt.parseInt(u16, cs_hex, 16); + const node: u48 = try std.fmt.parseInt(u48, node_hex, 16); + + var cs_bytes: [2]u8 = undefined; + std.mem.writeInt(u16, &cs_bytes, cs, .big); + var node_bytes: [6]u8 = undefined; + std.mem.writeInt(u48, &node_bytes, node, .big); - const tl_le = @byteSwap(tl_be); - const tm_le = @byteSwap(tm_be); - const th_le = @byteSwap(th_be); const csh = cs_bytes[0]; const csl = cs_bytes[1]; return Guid{ - .time_low = tl_le, - .time_mid = tm_le, - .time_high_and_version = th_le, + .time_low = tl, + .time_mid = tm, + .time_high_and_version = th, .clock_seq_high_and_reserved = csh, .clock_seq_low = csl, .node = node_bytes, }; } - fn HexToBytesType(comptime T: type) type { - const ti = @typeInfo(T); - const len = @divExact(ti.array.len, 2); - return @Type(.{ .array = .{ - .len = len, - .child = u8, - .sentinel_ptr = null, - } }); - } - - fn hexToBytes(hex: anytype) !HexToBytesType(@TypeOf(hex)) { - var ret: [@divExact(hex.len, 2)]u8 = undefined; - - for (0..ret.len) |i| { - const hi = try std.fmt.charToDigit(hex[i * 2], 16); - const lo = try std.fmt.charToDigit(hex[i * 2 + 1], 16); - ret[i] = (hi << 4) | lo; - } - - return ret; + pub fn write(guid: Guid, buf: *[16]u8) void { + std.mem.writeInt(u32, buf[0..4], guid.time_low, .little); + std.mem.writeInt(u16, buf[4..6], guid.time_mid, .little); + std.mem.writeInt(u16, buf[6..8], guid.time_high_and_version, .little); + buf[8] = guid.clock_seq_high_and_reserved; + buf[9] = guid.clock_seq_low; + buf[10..16].* = guid.node; } }; @@ -364,33 +360,23 @@ pub const Partition = struct { // TODO fill from https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs pub const known_types = std.StaticStringMap(Guid).initComptime(.{ .{ "unused", Guid.parse("00000000-0000-0000-0000-000000000000".*) catch unreachable }, - .{ "efi-system", Guid.parse("C12A7328-F81F-11D2-BA4B-00A0C93EC93B".*) catch unreachable }, -}); -// struct { -// pub const unused: Guid = .{}; + .{ "esp", Guid.parse("C12A7328-F81F-11D2-BA4B-00A0C93EC93B".*) catch unreachable }, + .{ "legacy_mbr", Guid.parse("024DEE41-33E7-11D3-9D69-0008C781F39F".*) catch unreachable }, + .{ "bios_boot", Guid.parse("21686148-6449-6E6F-744E-656564454649".*) catch unreachable }, -// pub const microsoft_basic_data: Guid = .{}; -// pub const microsoft_reserved: Guid = .{}; + .{ "microsoft_basic_data", Guid.parse("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7".*) catch unreachable }, + .{ "microsoft_reserved", Guid.parse("E3C9E316-0B5C-4DB8-817D-F92DF00215AE".*) catch unreachable }, -// pub const windows_recovery: Guid = .{}; + .{ "windows_recovery", Guid.parse("DE94BBA4-06D1-4D40-A16A-BFD50179D6AC".*) catch unreachable }, -// pub const plan9: Guid = .{}; + .{ "plan9", Guid.parse("C91818F9-8025-47AF-89D2-F030D7000C2C".*) catch unreachable }, -// pub const linux_swap: Guid = .{}; -// pub const linux_fs: Guid = .{}; -// pub const linux_reserved: Guid = .{}; -// pub const linux_lvm: Guid = .{}; -// }; - -pub fn nameLiteral(comptime name: []const u8) [36]u16 { - return comptime blk: { - var buf: [36]u16 = undefined; - const len = std.unicode.utf8ToUtf16Le(&buf, name) catch |err| @compileError(@tagName(err)); - @memset(buf[len..], 0); - break :blk &buf; - }; -} + .{ "linux_swap", Guid.parse("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F".*) catch unreachable }, + .{ "linux_fs", Guid.parse("0FC63DAF-8483-4772-8E79-3D69D8477DE4".*) catch unreachable }, + .{ "linux_reserved", Guid.parse("8DA63339-0007-60C0-C436-083AC8230908".*) catch unreachable }, + .{ "linux_lvm", Guid.parse("E6D6D379-F507-44C2-A23C-238F2A3DF928".*) catch unreachable }, +}); pub fn stringToName(name: []const u8) ![36]u16 { var buf: [36]u16 = @splat(0); diff --git a/src/dim.zig b/src/dim.zig index 5e7e487..01438d8 100644 --- a/src/dim.zig +++ b/src/dim.zig @@ -747,7 +747,6 @@ pub const BinaryStream = struct { error.DeviceBusy, error.InvalidArgument, error.AccessDenied, - error.PermissionDenied, error.BrokenPipe, error.SystemResources, error.OperationAborted, From 56eab320039e7ecc8df4fc079708f3efe7c8aef2 Mon Sep 17 00:00:00 2001 From: Lilly Sieberer Date: Wed, 4 Jun 2025 11:49:49 -0400 Subject: [PATCH 3/4] Update partition type keywords --- src/components/part/GptPartitionTable.zig | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/part/GptPartitionTable.zig b/src/components/part/GptPartitionTable.zig index 7bf2b4a..e61a69a 100644 --- a/src/components/part/GptPartitionTable.zig +++ b/src/components/part/GptPartitionTable.zig @@ -361,21 +361,21 @@ pub const Partition = struct { pub const known_types = std.StaticStringMap(Guid).initComptime(.{ .{ "unused", Guid.parse("00000000-0000-0000-0000-000000000000".*) catch unreachable }, - .{ "esp", Guid.parse("C12A7328-F81F-11D2-BA4B-00A0C93EC93B".*) catch unreachable }, - .{ "legacy_mbr", Guid.parse("024DEE41-33E7-11D3-9D69-0008C781F39F".*) catch unreachable }, - .{ "bios_boot", Guid.parse("21686148-6449-6E6F-744E-656564454649".*) catch unreachable }, + .{ "efi-system", Guid.parse("C12A7328-F81F-11D2-BA4B-00A0C93EC93B".*) catch unreachable }, + .{ "legacy-mbr", Guid.parse("024DEE41-33E7-11D3-9D69-0008C781F39F".*) catch unreachable }, + .{ "bios-boot", Guid.parse("21686148-6449-6E6F-744E-656564454649".*) catch unreachable }, - .{ "microsoft_basic_data", Guid.parse("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7".*) catch unreachable }, - .{ "microsoft_reserved", Guid.parse("E3C9E316-0B5C-4DB8-817D-F92DF00215AE".*) catch unreachable }, + .{ "microsoft-basic-data", Guid.parse("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7".*) catch unreachable }, + .{ "microsoft-reserved", Guid.parse("E3C9E316-0B5C-4DB8-817D-F92DF00215AE".*) catch unreachable }, - .{ "windows_recovery", Guid.parse("DE94BBA4-06D1-4D40-A16A-BFD50179D6AC".*) catch unreachable }, + .{ "windows-recovery", Guid.parse("DE94BBA4-06D1-4D40-A16A-BFD50179D6AC".*) catch unreachable }, .{ "plan9", Guid.parse("C91818F9-8025-47AF-89D2-F030D7000C2C".*) catch unreachable }, - .{ "linux_swap", Guid.parse("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F".*) catch unreachable }, - .{ "linux_fs", Guid.parse("0FC63DAF-8483-4772-8E79-3D69D8477DE4".*) catch unreachable }, - .{ "linux_reserved", Guid.parse("8DA63339-0007-60C0-C436-083AC8230908".*) catch unreachable }, - .{ "linux_lvm", Guid.parse("E6D6D379-F507-44C2-A23C-238F2A3DF928".*) catch unreachable }, + .{ "linux-swap", Guid.parse("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F".*) catch unreachable }, + .{ "linux-fs", Guid.parse("0FC63DAF-8483-4772-8E79-3D69D8477DE4".*) catch unreachable }, + .{ "linux-reserved", Guid.parse("8DA63339-0007-60C0-C436-083AC8230908".*) catch unreachable }, + .{ "linux-lvm", Guid.parse("E6D6D379-F507-44C2-A23C-238F2A3DF928".*) catch unreachable }, }); pub fn stringToName(name: []const u8) ![36]u16 { From ea3193043a2089902fba47c5f50d1979ab10f063 Mon Sep 17 00:00:00 2001 From: Khitiara Date: Sat, 14 Jun 2025 13:21:36 -0400 Subject: [PATCH 4/4] one little mistake --- src/components/part/GptPartitionTable.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/part/GptPartitionTable.zig b/src/components/part/GptPartitionTable.zig index e61a69a..a3732c1 100644 --- a/src/components/part/GptPartitionTable.zig +++ b/src/components/part/GptPartitionTable.zig @@ -238,7 +238,7 @@ pub fn render(table: *PartTable, stream: *dim.BinaryStream) dim.Content.RenderEr std.mem.writeInt(u64, gpt_header[0x30..0x38], max_partition_lba, .little); // Last usable LBA (table.disk_id orelse Guid.rand(random)).write(gpt_header[0x38..0x48]); std.mem.writeInt(u64, gpt_header[0x48..0x50], 2, .little); // First LBA of the partition entry array - std.mem.writeInt(u32, gpt_header[0x50..0x54], 0x80, .little); // Number of partition entries + std.mem.writeInt(u32, gpt_header[0x50..0x54], @intCast(table.partitions.len), .little); // Number of partition entries std.mem.writeInt(u32, gpt_header[0x54..0x58], 0x80, .little); // Size of a partition entry var backup_gpt_header_block: [block_size]u8 = gpt_header_block;