const std = @import("std"); const mem = std.mem; const math = std.math; const native_endian = @import("builtin").target.cpu.arch.endian(); const ptr_size = @import("Group.zig").ptr_size; const max_shells = @import("shell.zig").max_shells; const magic = [4]u8{ 0xf0, 0x9f, 0xa4, 0xb7 }; const version = 0; const Endian = enum(u4) { big, little, fn native() Endian { return switch (native_endian) { .Little => Endian.little, .Big => Endian.big, }; } }; pub const Host = packed struct { endian: Endian = Endian.native(), ptr_size: u4 = ptr_size, pub fn new() Host { return Host{}; } }; pub const section_length_bits = 6; pub const section_length = 1 << section_length_bits; pub const Invalid = error{ InvalidMagic, InvalidVersion, InvalidEndianess, InvalidPointerSize, }; pub const Header = extern struct { magic: [8]u8 = magic ++ [1]u8{0} ** 4, nblocks_groups: u64, nblocks_users: u64, nblocks_groupmembers: u64, nblocks_additional_gids: u64, getgr_bufsize: u64, getpw_bufsize: u64, num_groups: u32, num_users: u32, nblocks_bdz_gid: u32, nblocks_bdz_groupname: u32, nblocks_bdz_uid: u32, nblocks_bdz_username: u32, nblocks_shell_blob: u8, num_shells: u8, version: u8 = version, host: Host = Host.new(), padding: [40]u8 = [1]u8{0} ** 40, pub fn fromBytes(blob: *align(8) const [@sizeOf(Header)]u8) Invalid!*const Header { const self = mem.bytesAsValue(Header, blob); if (!mem.eql(u8, magic[0..4], blob[0..4])) return error.InvalidMagic; if (self.version != 0) return error.InvalidVersion; if (self.host.endian != Endian.native()) return error.InvalidEndianess; // when ptr size is larger than on the host that constructed it the DB, // getgr_bufsize/getpw_bufsize may return insufficient values, causing // OutOfMemory for getgr* and getpw* calls. if (self.host.ptr_size < ptr_size) return error.InvalidPointerSize; return self; } }; const testing = std.testing; test "header Section length is a power of two" { try testing.expect(std.math.isPowerOfTwo(section_length)); } test "header fits into two sections" { try testing.expectEqual(2 * section_length, @sizeOf(Header)); } test "header bit header size is equal to @sizeOf(Header)" { try testing.expectEqual(@sizeOf(Header) * 8, @bitSizeOf(Header)); } test "header pack and unpack" { const header1 = Header{ .nblocks_shell_blob = 0, .num_shells = 0, .num_groups = 0, .num_users = 0, .nblocks_bdz_gid = 0, .nblocks_bdz_groupname = 0, .nblocks_bdz_uid = 0, .nblocks_bdz_username = 0, .nblocks_groups = 0, .nblocks_users = 0, .nblocks_groupmembers = 0, .nblocks_additional_gids = 1, .getgr_bufsize = 16, .getpw_bufsize = 32, }; const bytes = mem.asBytes(&header1); const header = try Header.fromBytes(bytes); try testing.expectEqual(header1, header.*); }