From 8c8570e6d1941453a6c5c5637fb6259fad354fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Thu, 17 Feb 2022 11:16:30 +0200 Subject: [PATCH] start with Header --- README.md | 7 +++++- src/header.zig | 63 +++++++++++++++++++++++++++++++++++++++++++++++ src/shell.zig | 3 ++- src/test_main.zig | 1 + 4 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 src/header.zig diff --git a/README.md b/README.md index 6874ea9..d3b18da 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ OFFSET TYPE NAME DESCRIPTION 0 [4]u8 magic always 0xf09fa4b7 4 u8 version now `0` 5 u16 bom 0x1234 - 7 u6 num_shells max value: 63 + u8 num_shells max value: 63. Padding is strange on little endian. 8 u32 num_users number of passwd entries 12 u32 num_groups number of group entries 16 u32 offset_cmph_uid2user @@ -186,6 +186,11 @@ header field. For example, `cmph_gid2group` comes immediately after the header, and `idx_groupname2group` comes after `idx_gid2group`, whose offset is `offset_idx`, and size can be calculated. +`num_shells` would fit to u6; however, we would need 2 bits of padding (all +other fields are byte-aligned). If we instead do `u2` followed by `u6`, the +byte would look very unusual on a little-endian architecture. Therefore we will +just refuse loading the file if the number of shells exceeds 63. + Primitive types --------------- diff --git a/src/header.zig b/src/header.zig new file mode 100644 index 0000000..3963f89 --- /dev/null +++ b/src/header.zig @@ -0,0 +1,63 @@ +const std = @import("std"); + +const HeaderSize = @bitSizeOf(Header) / 8; +const Magic = [4]u8{ 0xf0, 0x9f, 0xa4, 0xb7 }; +const Version = 0; +const Bom = 0x1234; + +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, + + // TODO(motiejus) should we be returning *Header instead? That way all + // stacks could be smaller by 48 bytes, and we would be referring to + // it's fields in the mmap'ed region. + pub fn init(blob: [HeaderSize]u8) Header { + return @bitCast(Header, blob); + } + + 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 header = 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 = header.asArray(); + const header2 = Header.init(blob); + try testing.expectEqual(header, header2); +} diff --git a/src/shell.zig b/src/shell.zig index 0adf7ce..366735b 100644 --- a/src/shell.zig +++ b/src/shell.zig @@ -4,7 +4,6 @@ const PriorityDequeue = std.PriorityDequeue; const StringArrayHashMap = std.StringArrayHashMap; const StringHashMap = std.StringHashMap; const BoundedArray = std.BoundedArray; -const testing = std.testing; const StringContext = std.hash_map.StringContext; // MaxShells is the maximum number of "popular" shells. @@ -182,6 +181,8 @@ inline fn roundUp4Padding(n: u12) u12 { return ((n + 3) & ~@intCast(u12, 3)) - n; } +const testing = std.testing; + test "basic shellpopcon" { var popcon = ShellWriter.init(testing.allocator); defer popcon.deinit(); diff --git a/src/test_main.zig b/src/test_main.zig index 76b625e..a7ba2c4 100644 --- a/src/test_main.zig +++ b/src/test_main.zig @@ -1,4 +1,5 @@ test "turbonss test suite" { _ = @import("main.zig"); _ = @import("shell.zig"); + _ = @import("header.zig"); }