diff --git a/src/padding.zig b/src/padding.zig index df8017c..ee9671d 100644 --- a/src/padding.zig +++ b/src/padding.zig @@ -1,19 +1,19 @@ const std = @import("std"); const assert = std.debug.assert; -// rounds up a u12 to the nearest factor of 4 and returns the difference -// (padding) -pub fn roundUpPadding(comptime T: type, comptime nbits: u8, n: T) T { - return roundUp(T, nbits, n) - n; -} - -// rounds up a u12 to the nearest factor of 4. +// rounds up an int to the nearest factor of nbits. pub fn roundUp(comptime T: type, comptime nbits: u8, n: T) T { comptime assert(nbits < @bitSizeOf(T)); const factor = comptime (1 << nbits) - 1; return ((n + factor) & ~@as(T, factor)); } +// rounds up an integer to the nearest factor of nbits and returns the +// difference (padding) +pub fn roundUpPadding(comptime T: type, comptime nbits: u8, n: T) T { + return roundUp(T, nbits, n) - n; +} + const testing = std.testing; test "padding" { diff --git a/src/shell.zig b/src/shell.zig index a1174fd..e515854 100644 --- a/src/shell.zig +++ b/src/shell.zig @@ -25,7 +25,7 @@ pub const ShellReader = struct { } // get returns a shell at the given index. - pub fn get(self: *const ShellReader, idx: u10) []const u8 { + pub fn get(self: *const ShellReader, idx: u6) []const u8 { const shellIndex = self.sectionIndex[idx]; const start = shellIndex.offset << 2; const end = start + shellIndex.len + 1; diff --git a/src/user.zig b/src/user.zig index 57745f7..6d88b7c 100644 --- a/src/user.zig +++ b/src/user.zig @@ -17,6 +17,21 @@ pub const PackedUser = packed struct { name_is_a_suffix: bool, name_len: u5, gecos_len: u8, + wip_padding: u8, // https://github.com/ziglang/zig/pull/10941 + + // blobSize returns the length of the blob storing string values. + pub fn blobLength(self: *const PackedUser) usize { + var result: usize = self.home_len; + if (!self.name_is_a_suffix) { + result += self.name_len; + } + result += self.gecos_len; + if (self.shell_here) { + result += self.shell_len_or_idx; + } + + return result; + } }; const PackedUserAlignmentBits = 3; @@ -71,6 +86,7 @@ pub const UserWriter = struct { .name_is_a_suffix = std.mem.endsWith(u8, user.home, user.name), .name_len = name_len, .gecos_len = gecos_len, + .wip_padding = 0, }; try self.appendTo.appendSlice(std.mem.asBytes(&puser)); @@ -92,37 +108,52 @@ pub const UserWriter = struct { }; pub const UserReader = struct { - const shellIndexProto = fn (u10) []const u8; + const shellIndexProto = fn (u6) []const u8; + blob: []u8, shellIndex: shellIndexProto, - blob: []const u8, - pub const PackedUserEntry = struct { + const PackedEntry = struct { packed_user: *PackedUser, blob: []const u8, }; - pub fn init(shellIndex: shellIndexProto, blob: []const u8) UserReader { + pub fn init(blob: []u8, shellIndex: shellIndexProto) UserReader { return UserReader{ - .shellIndex = shellIndex, .blob = blob, + .shellIndex = shellIndex, }; } - pub const PackedUserIterator = struct { - ur: *const UserReader, - index: u32 = 0, + pub const PackedIterator = struct { + ur: *UserReader, + index: usize = 0, - pub fn next(it: *PackedUserIterator) ?PackedUserEntry { + pub fn next(it: *PackedIterator) ?PackedEntry { if (it.index == it.ur.blob.len) return null; - assert(it.index < it.blob.len); + assert(it.index < it.ur.blob.len); - const start = it.index; - const end = it.index + it.index + @sizeOf(PackedUser); - var pu = std.mem.bytesAsValue(it.ur.blob[start..end]); - _ = pu; + const endUser = it.index + PackedUserSize; + var packedUser = std.mem.bytesAsValue( + PackedUser, + it.ur.blob[it.index..endUser][0..PackedUserSize], + ); + + const startBlob = endUser + 1; + const endBlob = startBlob + packedUser.blobLength(); + + it.index = pad.roundUp(usize, PackedUserAlignmentBits, endBlob + 1); + + return PackedEntry{ + .packed_user = packedUser, + .blob = it.ur.blob[startBlob..endBlob], + }; } }; + + pub fn packedIterator(self: *UserReader) PackedIterator { + return .{ .ur = self }; + } }; const testing = std.testing; @@ -140,27 +171,46 @@ fn testShellIndex(shell: []const u8) ?u6 { return null; } +fn testShell(index: u6) []const u8 { + return switch (index) { + 0 => "/bin/bash", + 1 => "/bin/zsh", + else => unreachable, + }; +} + test "construct PackedUser blob" { var buf = ArrayList(u8).init(testing.allocator); defer buf.deinit(); var writer = UserWriter.init(&buf, testShellIndex); - const user1 = User{ - .uid = 1000, - .gid = 1000, - .name = "vidmantas", - .gecos = "Vidmantas Kaminskas", - .home = "/home/vidmantas", - .shell = "/bin/bash", + const users = [_]User{ + User{ + .uid = 1000, + .gid = 1000, + .name = "vidmantas", + .gecos = "Vidmantas Kaminskas", + .home = "/home/vidmantas", + .shell = "/bin/bash", + }, + User{ + .uid = 1001, + .gid = 1001, + .name = "svc-foo", + .gecos = "Service Account", + .home = "/home/service1", + .shell = "/usr/bin/nologin", + }, }; - const user2 = User{ - .uid = 1001, - .gid = 1001, - .name = "svc-foo", - .gecos = "Service Account", - .home = "/home/service1", - .shell = "/usr/bin/nologin", - }; - try writer.appendUser(user1); - try writer.appendUser(user2); + for (users) |user| { + try writer.appendUser(user); + } + + var rd = UserReader.init(buf.items, testShell); + var it = rd.packedIterator(); + var i: u32 = 0; + while (it.next()) |entry| : (i += 1) { + try testing.expectEqual(users[i].uid, entry.packed_user.uid); + try testing.expectEqual(users[i].gid, entry.packed_user.gid); + } }