const std = @import("std"); const shell = @import("shell.zig"); const HeaderSize = @divExact(@bitSizeOf(Header), 8); const Magic = [4]u8{ 0xf0, 0x9f, 0xa4, 0xb7 }; const Version = 0; const Bom = 0x1234; pub const SectionLength = 64; const InvalidHeader = 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_bdz_uid2user: u32, offset_bdz_groupname2group: u32, offset_bdz_username2user: u32, offset_idx: u32, offset_groups: u32, offset_users: u32, offset_groupmembers: u32, offset_additional_gids: u32, pub fn init(blob: [HeaderSize]u8) InvalidHeader!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 > shell.max_shells) { return error.TooManyShells; } const offsets = [_]u32{ self.offset_bdz_uid2user, self.offset_bdz_groupname2group, self.offset_bdz_username2user, self.offset_idx, self.offset_groups, self.offset_users, self.offset_groupmembers, self.offset_additional_gids, }; for (offsets) |offset| { if (offset & (SectionLength - 1) != 0) { return error.InvalidOffset; } } return self; } pub fn asArray(self: *const Header) [HeaderSize]u8 { return @bitCast([HeaderSize]u8, self.*); } }; const testing = std.testing; test "Section length is a power of two" { try testing.expect(std.math.isPowerOfTwo(SectionLength)); } test "header pack, unpack and validation" { const goodHeader = Header{ .magic = Magic, .version = Version, .bom = Bom, .num_shells = 0, .num_users = 0, .num_groups = 0, .offset_bdz_uid2user = 0, .offset_bdz_groupname2group = 0, .offset_bdz_username2user = 0, .offset_idx = 0, .offset_groups = 0, .offset_users = 0, .offset_groupmembers = 0, .offset_additional_gids = 0, }; const gotHeader = try Header.init(goodHeader.asArray()); 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.offset_bdz_uid2user = 65; try testing.expectError(error.InvalidOffset, Header.init(header.asArray())); } }