wip writing of users

This commit is contained in:
Motiejus Jakštys 2022-03-06 13:11:06 +02:00 committed by Motiejus Jakštys
parent e3fd1c833c
commit a6349cd114
2 changed files with 43 additions and 32 deletions

View File

@ -232,25 +232,27 @@ pub fn userGids(allocator: Allocator, corpus: *const Corpus) userGidsErr!UserGid
var name2offset = StringHashMap(u32).init(allocator); var name2offset = StringHashMap(u32).init(allocator);
errdefer name2offset.deinit(); 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); var scratch = try allocator.alloc(u32, 256);
defer allocator.free(scratch); defer allocator.free(scratch);
for (corpus.users) |user| { for (corpus.users) |user| {
const usergroups_maybe = corpus.username2groups.get(user.name); if (corpus.username2groups.get(user.name)) |usergroups| {
if (usergroups_maybe == null) try name2offset.putNoClobber(user.name, try math.cast(u32, blob.items.len));
continue; scratch = try allocator.realloc(scratch, usergroups.len);
const usergroups = usergroups_maybe.?; scratch.len = usergroups.len;
for (usergroups) |group, i|
try name2offset.putNoClobber(user.name, try math.cast(u32, blob.items.len)); scratch[i] = group.gid;
scratch = try allocator.realloc(scratch, usergroups.len); compress.deltaCompress(u32, scratch) catch |err| switch (err) {
scratch.len = usergroups.len; error.NotSorted => unreachable,
for (usergroups) |group, i| };
scratch[i] = group.gid; try compress.appendUvarint(&blob, usergroups.len);
compress.deltaCompress(u32, scratch) catch |err| switch (err) { for (scratch) |gid|
error.NotSorted => unreachable, try compress.appendUvarint(&blob, gid);
}; } else {
try compress.appendUvarint(&blob, usergroups.len); try name2offset.putNoClobber(user.name, 0);
for (scratch) |gid| }
try compress.appendUvarint(&blob, gid);
} }
return UserGids{ 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( pub fn usersSection(
allocator: Allocator, allocator: Allocator,
corpus: *const Corpus, corpus: *const Corpus,
gids: *const UserGids, gids: *const UserGids,
shells: *const ShellSections, shells: *const ShellSections,
) usersSectionErr![]const u8 { ) error{ OutOfMemory, Overflow }![]const u8 {
// as of writing each user takes 15 bytes + strings + padding, padded to // 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); var buf = try ArrayList(u8).initCapacity(allocator, 24 * corpus.users.len);
_ = gids; for (corpus.users) |user| {
_ = shells; 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(); return buf.toOwnedSlice();
} }

View File

@ -6,6 +6,7 @@ const InvalidRecord = validate.InvalidRecord;
const assert = std.debug.assert; const assert = std.debug.assert;
const mem = std.mem; const mem = std.mem;
const math = std.math;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const ArrayList = std.ArrayList; const ArrayList = std.ArrayList;
@ -196,6 +197,7 @@ fn packedUser(immutable: bool) type {
pub fn packTo( pub fn packTo(
arr: *ArrayList(u8), arr: *ArrayList(u8),
user: User, user: User,
additional_gids_offset: u29,
idxFn: shell2idxProto, idxFn: shell2idxProto,
) packErr!void { ) packErr!void {
// function arguments are consts. We need to mutate the underlying // function arguments are consts. We need to mutate the underlying
@ -213,7 +215,7 @@ fn packedUser(immutable: bool) type {
const inner = Inner{ const inner = Inner{
.uid = user.uid, .uid = user.uid,
.gid = user.gid, .gid = user.gid,
.additional_gids_offset = std.math.maxInt(u29), .additional_gids_offset = additional_gids_offset,
.shell_here = idxFn(user.shell) == null, .shell_here = idxFn(user.shell) == null,
.shell_len_or_idx = idxFn(user.shell) orelse shell_len, .shell_len_or_idx = idxFn(user.shell) orelse shell_len,
.home_len = home_len, .home_len = home_len,
@ -241,10 +243,10 @@ fn packedUser(immutable: bool) type {
pub fn maxSize() usize { pub fn maxSize() usize {
comptime { comptime {
const unpadded = InnerSize + const unpadded = InnerSize +
std.math.maxInt(u6) + 1 + // home math.maxInt(u6) + 1 + // home
std.math.maxInt(u5) + 1 + // name math.maxInt(u5) + 1 + // name
std.math.maxInt(u6) + 1 + // shell math.maxInt(u6) + 1 + // shell
std.math.maxInt(u8); // gecos math.maxInt(u8); // gecos
return pad.roundUp(u64, alignmentBits, unpadded); return pad.roundUp(u64, alignmentBits, unpadded);
} }
} }
@ -346,7 +348,7 @@ test "construct PackedUser section" {
.shell = "/usr/bin/nologin", .shell = "/usr/bin/nologin",
}, User{ }, User{
.uid = 0, .uid = 0,
.gid = std.math.maxInt(u32), .gid = math.maxInt(u32),
.name = "Name" ** 8, .name = "Name" ** 8,
.gecos = "Gecos" ** 51, .gecos = "Gecos" ** 51,
.home = "Home" ** 16, .home = "Home" ** 16,
@ -360,7 +362,7 @@ test "construct PackedUser section" {
.shell = "/", .shell = "/",
} }; } };
for (users) |user| for (users) |user|
try PackedUserConst.packTo(&buf, user, testShellIndex); try PackedUserConst.packTo(&buf, user, math.maxInt(u29), testShellIndex);
var i: u29 = 0; var i: u29 = 0;
var it1 = PackedUserConst.iterator(buf.items, testShell); 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].uid, user.uid());
try testing.expectEqual(users[i].gid, user.gid()); try testing.expectEqual(users[i].gid, user.gid());
try testing.expectEqual( try testing.expectEqual(
@as(u29, std.math.maxInt(u29)), @as(u29, math.maxInt(u29)),
user.additionalGidsOffset(), user.additionalGidsOffset(),
); );
try testing.expectEqualStrings(users[i].name, user.name()); try testing.expectEqualStrings(users[i].name, user.name());
@ -405,14 +407,14 @@ test "PackedUser.maxSize()" {
defer buf.deinit(); defer buf.deinit();
const largeUser = User{ const largeUser = User{
.uid = std.math.maxInt(u32), .uid = math.maxInt(u32),
.gid = std.math.maxInt(u32), .gid = math.maxInt(u32),
.name = "Name" ** 8, // 32 .name = "Name" ** 8, // 32
.gecos = "Gecos" ** 51, // 255 .gecos = "Gecos" ** 51, // 255
.home = "Home" ** 16, // 64 .home = "Home" ** 16, // 64
.shell = "She.LllL" ** 8, // 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); try testing.expectEqual(PackedUserConst.maxSize(), buf.items.len);
} }