generalize Shell2Index for hash and test stubs

This commit is contained in:
Motiejus Jakštys 2022-03-06 17:55:29 +02:00 committed by Motiejus Jakštys
parent a6349cd114
commit d3be68b51d
2 changed files with 53 additions and 51 deletions

View File

@ -225,6 +225,8 @@ pub const UserGids = struct {
}
};
const userGidsPaddingBits = 3;
const userGidsErr = Allocator.Error || error{Overflow};
pub fn userGids(allocator: Allocator, corpus: *const Corpus) userGidsErr!UserGids {
var blob = ArrayList(u8).init(allocator);
@ -234,6 +236,7 @@ pub fn userGids(allocator: Allocator, corpus: *const Corpus) userGidsErr!UserGid
// zero'th entry is empty, so groupless users can refer to it.
try compress.appendUvarint(&blob, 0);
try pad.arrayList(&blob, userGidsPaddingBits);
var scratch = try allocator.alloc(u32, 256);
defer allocator.free(scratch);
@ -250,6 +253,7 @@ pub fn userGids(allocator: Allocator, corpus: *const Corpus) userGidsErr!UserGid
try compress.appendUvarint(&blob, usergroups.len);
for (scratch) |gid|
try compress.appendUvarint(&blob, gid);
try pad.arrayList(&blob, userGidsPaddingBits);
} else {
try name2offset.putNoClobber(user.name, 0);
}
@ -266,18 +270,18 @@ pub fn usersSection(
corpus: *const Corpus,
gids: *const UserGids,
shells: *const ShellSections,
) error{ OutOfMemory, Overflow }![]const u8 {
) error{ OutOfMemory, Overflow, InvalidRecord }![]const u8 {
// as of writing each user takes 15 bytes + strings + padding, padded to
// 8 bytes. 24 is an optimistic lower bound for an average record size.
var buf = try ArrayList(u8).initCapacity(allocator, 24 * corpus.users.len);
for (corpus.users) |user| {
const offset = gids.name2offset.get(user.name).?;
std.debug.assert(offset & 7 == 0);
try userImport.PackedUserConst.packTo(
try userImport.PackedUserHash.packTo(
&buf,
user,
@truncate(u29, @shrExact(offset, 3)),
shells.indices.get,
shells.indices,
);
}
return buf.toOwnedSlice();
@ -480,7 +484,7 @@ test "userGids" {
const groups = corpus.username2groups.get(user.name);
const offset = user_gids.name2offset.get(user.name);
if (groups == null) {
try testing.expect(offset == null);
try testing.expect(offset.? == 0);
continue;
}
var vit = try compress.VarintSliceIterator(user_gids.blob[offset.?..]);

View File

@ -62,15 +62,28 @@ pub const User = struct {
}
};
pub const PackedUserMut = packedUser(false);
pub const PackedUserConst = packedUser(true);
pub fn Shell2Index(T: type) type {
return struct {
const Self = @This();
data: T,
fn packedUser(immutable: bool) type {
pub fn init(data: T) Self {
return Self{ .data = data };
}
pub fn get(self: *const Self, str: []const u8) ?u6 {
return self.data.get(str);
}
};
}
pub const PackedUserHash = packedUser(std.StringHashMap(u6));
fn packedUser(comptime ShellIndexType: type) type {
return struct {
const Self = @This();
const alignmentBits = 3;
const shell2idxProto = fn ([]const u8) ?u6;
const InnerSize = @divExact(@bitSizeOf(Inner), 8);
const Inner = packed struct {
@ -133,21 +146,18 @@ fn packedUser(immutable: bool) type {
}
};
const Bytes = if (immutable) []const u8 else []u8;
const InnerPointer = if (immutable) *const Inner else *Inner;
// PackedUser does not allocate; it re-interprets the "bytes" blob
// field. Both of those fields are pointers to "our representation" of
// that field.
inner: InnerPointer,
userdata: Bytes,
inner: *const Inner,
userdata: []const u8,
pub const Entry = struct {
user: Self,
next: ?Bytes,
next: ?[]const u8,
};
pub fn fromBytes(bytes: Bytes) Entry {
pub fn fromBytes(bytes: []const u8) Entry {
const inner = mem.bytesAsValue(
Inner,
// Should use InnerSize instead of sizeOf, see
@ -159,7 +169,7 @@ fn packedUser(immutable: bool) type {
const endBlob = startBlob + inner.blobLength();
const nextStart = pad.roundUp(usize, alignmentBits, endBlob);
var next: ?Bytes = null;
var next: ?[]const u8 = null;
if (nextStart < bytes.len)
next = bytes[nextStart..];
@ -173,7 +183,7 @@ fn packedUser(immutable: bool) type {
}
pub const Iterator = struct {
section: ?Bytes,
section: ?[]const u8,
shellIndex: Idx2ShellProto,
pub fn next(it: *Iterator) ?Self {
@ -186,7 +196,7 @@ fn packedUser(immutable: bool) type {
}
};
pub fn iterator(section: Bytes, idxFn: Idx2ShellProto) Iterator {
pub fn iterator(section: []const u8, idxFn: Idx2ShellProto) Iterator {
return Iterator{ .section = section, .shellIndex = idxFn };
}
@ -198,7 +208,7 @@ fn packedUser(immutable: bool) type {
arr: *ArrayList(u8),
user: User,
additional_gids_offset: u29,
idxFn: shell2idxProto,
idxFn: ShellIndexType,
) packErr!void {
// function arguments are consts. We need to mutate the underlying
// slice, so passing it via pointer instead.
@ -216,8 +226,8 @@ fn packedUser(immutable: bool) type {
.uid = user.uid,
.gid = user.gid,
.additional_gids_offset = additional_gids_offset,
.shell_here = idxFn(user.shell) == null,
.shell_len_or_idx = idxFn(user.shell) orelse shell_len,
.shell_here = idxFn.get(user.shell) == null,
.shell_len_or_idx = idxFn.get(user.shell) orelse shell_len,
.home_len = home_len,
.name_is_a_suffix = mem.endsWith(u8, user.home, user.name),
.name_len = name_len,
@ -306,12 +316,13 @@ test "PackedUser internal and external alignment" {
// more than 1, but it probably could be higher.
try testing.expectEqual(
8,
@sizeOf(PackedUserConst.Inner) * 8 -
@bitSizeOf(PackedUserConst.Inner),
@sizeOf(PackedUserHash.Inner) * 8 -
@bitSizeOf(PackedUserHash.Inner),
);
}
fn testShellIndex(shell: []const u8) ?u6 {
const TestShellIndex = struct {
pub fn get(_: *const TestShellIndex, shell: []const u8) ?u6 {
if (mem.eql(u8, shell, "/bin/bash")) {
return 0;
} else if (mem.eql(u8, shell, "/bin/zsh")) {
@ -319,6 +330,9 @@ fn testShellIndex(shell: []const u8) ?u6 {
}
return null;
}
};
const PackedUserTest = packedUser(TestShellIndex);
fn testShell(index: u6) []const u8 {
return switch (index) {
@ -362,10 +376,10 @@ test "construct PackedUser section" {
.shell = "/",
} };
for (users) |user|
try PackedUserConst.packTo(&buf, user, math.maxInt(u29), testShellIndex);
try PackedUserTest.packTo(&buf, user, math.maxInt(u29), TestShellIndex{});
var i: u29 = 0;
var it1 = PackedUserConst.iterator(buf.items, testShell);
var it1 = PackedUserTest.iterator(buf.items, testShell);
while (it1.next()) |user| : (i += 1) {
try testing.expectEqual(users[i].uid, user.uid());
try testing.expectEqual(users[i].gid, user.gid());
@ -379,22 +393,6 @@ test "construct PackedUser section" {
try testing.expectEqualStrings(users[i].shell, user.shell(testShell));
}
try testing.expectEqual(users.len, i);
var it2 = PackedUserMut.iterator(buf.items, testShell);
i = 0;
while (it2.next()) |user| : (i += 1) {
user.setAdditionalGidsOffset(i);
}
try testing.expectEqual(users.len, i);
var it3 = PackedUserConst.iterator(buf.items, testShell);
i = 0;
while (it3.next()) |user| : (i += 1) {
try testing.expectEqual(users[i].gid, user.gid());
try testing.expectEqual(i, user.additionalGidsOffset());
try testing.expectEqualStrings(users[i].name, user.name());
}
try testing.expectEqual(users.len, i);
}
test "PackedUser.maxSize()" {
@ -402,7 +400,7 @@ test "PackedUser.maxSize()" {
// As of writing, I am getting a stack smashing error.
var buf = try ArrayList(u8).initCapacity(
testing.allocator,
PackedUserConst.maxSize(),
PackedUserHash.maxSize(),
);
defer buf.deinit();
@ -414,8 +412,8 @@ test "PackedUser.maxSize()" {
.home = "Home" ** 16, // 64
.shell = "She.LllL" ** 8, // 64
};
try PackedUserConst.packTo(&buf, largeUser, math.maxInt(u29), testShellIndex);
try testing.expectEqual(PackedUserConst.maxSize(), buf.items.len);
try PackedUserTest.packTo(&buf, largeUser, math.maxInt(u29), TestShellIndex{});
try testing.expectEqual(PackedUserTest.maxSize(), buf.items.len);
}
test "User.clone" {