const std = @import("std"); const shells = @import("shell.zig"); const HeaderSize = @sizeOf(Header); const Magic = [4]u8{ 0xf0, 0x9f, 0xa4, 0xb7 }; const Version = 0; const Bom = 0x1234; const HeaderError = error{ InvalidMagic, InvalidVersion, InvalidBom, TooManyShells, InvalidOffset, }; const Header = packed struct { magic: [4]u8, version: u8, bom: u16, num_shells: u8, num_users: u32, num_groups: u32, offset_cmph_uid2user: u32, offset_cmph_groupname2group: u32, offset_cmph_username2user: u32, offset_idx: u32, offset_groups: u32, offset_users: u32, offset_groupmembers: u32, offset_additional_gids: u32, pub fn init(blob: [HeaderSize]u8) HeaderError!Header { const self = @bitCast(Header, blob); for (Magic) |item, index| { if (self.magic[index] != item) return error.InvalidMagic; } if (self.version != 0) { return error.InvalidVersion; } if (self.bom != Bom) { return error.InvalidBom; } if (self.num_shells > shells.MaxShells) { return error.TooManyShells; } const offsets = [_]u32{ self.offset_cmph_uid2user, self.offset_cmph_groupname2group, self.offset_cmph_username2user, self.offset_idx, self.offset_groups, self.offset_users, self.offset_groupmembers, self.offset_additional_gids, }; for (offsets) |offset| { if (offset & @intCast(u32, 63) != 0) { return error.InvalidOffset; } } return self; } pub fn asArray(self: *const Header) [HeaderSize]u8 { return @bitCast([HeaderSize]u8, self.*); } }; const testing = std.testing; test "header size is byte-aligned" { try testing.expectEqual(HeaderSize * 8, @bitSizeOf(Header)); } test "header pack and unpack" { const goodHeader = Header{ .magic = Magic, .version = Version, .bom = Bom, .num_shells = 0, .num_users = 0, .num_groups = 0, .offset_cmph_uid2user = 0, .offset_cmph_groupname2group = 0, .offset_cmph_username2user = 0, .offset_idx = 0, .offset_groups = 0, .offset_users = 0, .offset_groupmembers = 0, .offset_additional_gids = 0, }; const blob = goodHeader.asArray(); const gotHeader = try Header.init(blob); try testing.expectEqual(goodHeader, gotHeader); { var header = goodHeader; header.magic[0] = 0; try testing.expectError(error.InvalidMagic, Header.init(header.asArray())); } { var header = goodHeader; header.bom = 0x3412; try testing.expectError(error.InvalidBom, Header.init(header.asArray())); } { var header = goodHeader; header.num_shells = shells.MaxShells + 1; try testing.expectError(error.TooManyShells, Header.init(header.asArray())); } { var header = goodHeader; header.offset_cmph_uid2user = 128; try testing.expectError(error.InvalidOffset, Header.init(header.asArray())); } }