From a6349cd1145ade08531bb30ca759a8aa6a93402c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Sun, 6 Mar 2022 13:11:06 +0200 Subject: [PATCH] wip writing of users --- src/sections.zig | 51 ++++++++++++++++++++++++++++-------------------- src/user.zig | 24 ++++++++++++----------- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/sections.zig b/src/sections.zig index 183a22e..d9bcaf1 100644 --- a/src/sections.zig +++ b/src/sections.zig @@ -232,25 +232,27 @@ pub fn userGids(allocator: Allocator, corpus: *const Corpus) userGidsErr!UserGid var name2offset = StringHashMap(u32).init(allocator); errdefer name2offset.deinit(); + // zero'th entry is empty, so groupless users can refer to it. + try compress.appendUvarint(&blob, 0); + var scratch = try allocator.alloc(u32, 256); defer allocator.free(scratch); for (corpus.users) |user| { - const usergroups_maybe = corpus.username2groups.get(user.name); - if (usergroups_maybe == null) - continue; - const usergroups = usergroups_maybe.?; - - try name2offset.putNoClobber(user.name, try math.cast(u32, blob.items.len)); - scratch = try allocator.realloc(scratch, usergroups.len); - scratch.len = usergroups.len; - for (usergroups) |group, i| - scratch[i] = group.gid; - compress.deltaCompress(u32, scratch) catch |err| switch (err) { - error.NotSorted => unreachable, - }; - try compress.appendUvarint(&blob, usergroups.len); - for (scratch) |gid| - try compress.appendUvarint(&blob, gid); + if (corpus.username2groups.get(user.name)) |usergroups| { + try name2offset.putNoClobber(user.name, try math.cast(u32, blob.items.len)); + scratch = try allocator.realloc(scratch, usergroups.len); + scratch.len = usergroups.len; + for (usergroups) |group, i| + scratch[i] = group.gid; + compress.deltaCompress(u32, scratch) catch |err| switch (err) { + error.NotSorted => unreachable, + }; + try compress.appendUvarint(&blob, usergroups.len); + for (scratch) |gid| + try compress.appendUvarint(&blob, gid); + } else { + try name2offset.putNoClobber(user.name, 0); + } } return UserGids{ @@ -259,18 +261,25 @@ pub fn userGids(allocator: Allocator, corpus: *const Corpus) userGidsErr!UserGid }; } -pub const usersSectionErr = Allocator.Error || error{Overflow}; pub fn usersSection( allocator: Allocator, corpus: *const Corpus, gids: *const UserGids, shells: *const ShellSections, -) usersSectionErr![]const u8 { +) error{ OutOfMemory, Overflow }![]const u8 { // as of writing each user takes 15 bytes + strings + padding, padded to - // 8 bytes. 24 is a very optimistic lower bound. + // 8 bytes. 24 is an optimistic lower bound for an average record size. var buf = try ArrayList(u8).initCapacity(allocator, 24 * corpus.users.len); - _ = gids; - _ = shells; + for (corpus.users) |user| { + const offset = gids.name2offset.get(user.name).?; + std.debug.assert(offset & 7 == 0); + try userImport.PackedUserConst.packTo( + &buf, + user, + @truncate(u29, @shrExact(offset, 3)), + shells.indices.get, + ); + } return buf.toOwnedSlice(); } diff --git a/src/user.zig b/src/user.zig index e8d6ee0..6b316ad 100644 --- a/src/user.zig +++ b/src/user.zig @@ -6,6 +6,7 @@ const InvalidRecord = validate.InvalidRecord; const assert = std.debug.assert; const mem = std.mem; +const math = std.math; const Allocator = mem.Allocator; const ArrayList = std.ArrayList; @@ -196,6 +197,7 @@ fn packedUser(immutable: bool) type { pub fn packTo( arr: *ArrayList(u8), user: User, + additional_gids_offset: u29, idxFn: shell2idxProto, ) packErr!void { // function arguments are consts. We need to mutate the underlying @@ -213,7 +215,7 @@ fn packedUser(immutable: bool) type { const inner = Inner{ .uid = user.uid, .gid = user.gid, - .additional_gids_offset = std.math.maxInt(u29), + .additional_gids_offset = additional_gids_offset, .shell_here = idxFn(user.shell) == null, .shell_len_or_idx = idxFn(user.shell) orelse shell_len, .home_len = home_len, @@ -241,10 +243,10 @@ fn packedUser(immutable: bool) type { pub fn maxSize() usize { comptime { const unpadded = InnerSize + - std.math.maxInt(u6) + 1 + // home - std.math.maxInt(u5) + 1 + // name - std.math.maxInt(u6) + 1 + // shell - std.math.maxInt(u8); // gecos + math.maxInt(u6) + 1 + // home + math.maxInt(u5) + 1 + // name + math.maxInt(u6) + 1 + // shell + math.maxInt(u8); // gecos return pad.roundUp(u64, alignmentBits, unpadded); } } @@ -346,7 +348,7 @@ test "construct PackedUser section" { .shell = "/usr/bin/nologin", }, User{ .uid = 0, - .gid = std.math.maxInt(u32), + .gid = math.maxInt(u32), .name = "Name" ** 8, .gecos = "Gecos" ** 51, .home = "Home" ** 16, @@ -360,7 +362,7 @@ test "construct PackedUser section" { .shell = "/", } }; for (users) |user| - try PackedUserConst.packTo(&buf, user, testShellIndex); + try PackedUserConst.packTo(&buf, user, math.maxInt(u29), testShellIndex); var i: u29 = 0; var it1 = PackedUserConst.iterator(buf.items, testShell); @@ -368,7 +370,7 @@ test "construct PackedUser section" { try testing.expectEqual(users[i].uid, user.uid()); try testing.expectEqual(users[i].gid, user.gid()); try testing.expectEqual( - @as(u29, std.math.maxInt(u29)), + @as(u29, math.maxInt(u29)), user.additionalGidsOffset(), ); try testing.expectEqualStrings(users[i].name, user.name()); @@ -405,14 +407,14 @@ test "PackedUser.maxSize()" { defer buf.deinit(); const largeUser = User{ - .uid = std.math.maxInt(u32), - .gid = std.math.maxInt(u32), + .uid = math.maxInt(u32), + .gid = math.maxInt(u32), .name = "Name" ** 8, // 32 .gecos = "Gecos" ** 51, // 255 .home = "Home" ** 16, // 64 .shell = "She.LllL" ** 8, // 64 }; - try PackedUserConst.packTo(&buf, largeUser, testShellIndex); + try PackedUserConst.packTo(&buf, largeUser, math.maxInt(u29), testShellIndex); try testing.expectEqual(PackedUserConst.maxSize(), buf.items.len); }