generalize Shell2Index for hash and test stubs
This commit is contained in:
parent
a6349cd114
commit
d3be68b51d
@ -225,6 +225,8 @@ pub const UserGids = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const userGidsPaddingBits = 3;
|
||||||
|
|
||||||
const userGidsErr = Allocator.Error || error{Overflow};
|
const userGidsErr = Allocator.Error || error{Overflow};
|
||||||
pub fn userGids(allocator: Allocator, corpus: *const Corpus) userGidsErr!UserGids {
|
pub fn userGids(allocator: Allocator, corpus: *const Corpus) userGidsErr!UserGids {
|
||||||
var blob = ArrayList(u8).init(allocator);
|
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.
|
// zero'th entry is empty, so groupless users can refer to it.
|
||||||
try compress.appendUvarint(&blob, 0);
|
try compress.appendUvarint(&blob, 0);
|
||||||
|
try pad.arrayList(&blob, userGidsPaddingBits);
|
||||||
|
|
||||||
var scratch = try allocator.alloc(u32, 256);
|
var scratch = try allocator.alloc(u32, 256);
|
||||||
defer allocator.free(scratch);
|
defer allocator.free(scratch);
|
||||||
@ -250,6 +253,7 @@ pub fn userGids(allocator: Allocator, corpus: *const Corpus) userGidsErr!UserGid
|
|||||||
try compress.appendUvarint(&blob, usergroups.len);
|
try compress.appendUvarint(&blob, usergroups.len);
|
||||||
for (scratch) |gid|
|
for (scratch) |gid|
|
||||||
try compress.appendUvarint(&blob, gid);
|
try compress.appendUvarint(&blob, gid);
|
||||||
|
try pad.arrayList(&blob, userGidsPaddingBits);
|
||||||
} else {
|
} else {
|
||||||
try name2offset.putNoClobber(user.name, 0);
|
try name2offset.putNoClobber(user.name, 0);
|
||||||
}
|
}
|
||||||
@ -266,18 +270,18 @@ pub fn usersSection(
|
|||||||
corpus: *const Corpus,
|
corpus: *const Corpus,
|
||||||
gids: *const UserGids,
|
gids: *const UserGids,
|
||||||
shells: *const ShellSections,
|
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
|
// 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.
|
// 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);
|
||||||
for (corpus.users) |user| {
|
for (corpus.users) |user| {
|
||||||
const offset = gids.name2offset.get(user.name).?;
|
const offset = gids.name2offset.get(user.name).?;
|
||||||
std.debug.assert(offset & 7 == 0);
|
std.debug.assert(offset & 7 == 0);
|
||||||
try userImport.PackedUserConst.packTo(
|
try userImport.PackedUserHash.packTo(
|
||||||
&buf,
|
&buf,
|
||||||
user,
|
user,
|
||||||
@truncate(u29, @shrExact(offset, 3)),
|
@truncate(u29, @shrExact(offset, 3)),
|
||||||
shells.indices.get,
|
shells.indices,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return buf.toOwnedSlice();
|
return buf.toOwnedSlice();
|
||||||
@ -480,7 +484,7 @@ test "userGids" {
|
|||||||
const groups = corpus.username2groups.get(user.name);
|
const groups = corpus.username2groups.get(user.name);
|
||||||
const offset = user_gids.name2offset.get(user.name);
|
const offset = user_gids.name2offset.get(user.name);
|
||||||
if (groups == null) {
|
if (groups == null) {
|
||||||
try testing.expect(offset == null);
|
try testing.expect(offset.? == 0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var vit = try compress.VarintSliceIterator(user_gids.blob[offset.?..]);
|
var vit = try compress.VarintSliceIterator(user_gids.blob[offset.?..]);
|
||||||
|
92
src/user.zig
92
src/user.zig
@ -62,15 +62,28 @@ pub const User = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const PackedUserMut = packedUser(false);
|
pub fn Shell2Index(T: type) type {
|
||||||
pub const PackedUserConst = packedUser(true);
|
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 {
|
return struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
const alignmentBits = 3;
|
const alignmentBits = 3;
|
||||||
const shell2idxProto = fn ([]const u8) ?u6;
|
|
||||||
|
|
||||||
const InnerSize = @divExact(@bitSizeOf(Inner), 8);
|
const InnerSize = @divExact(@bitSizeOf(Inner), 8);
|
||||||
const Inner = packed struct {
|
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
|
// PackedUser does not allocate; it re-interprets the "bytes" blob
|
||||||
// field. Both of those fields are pointers to "our representation" of
|
// field. Both of those fields are pointers to "our representation" of
|
||||||
// that field.
|
// that field.
|
||||||
inner: InnerPointer,
|
inner: *const Inner,
|
||||||
userdata: Bytes,
|
userdata: []const u8,
|
||||||
|
|
||||||
pub const Entry = struct {
|
pub const Entry = struct {
|
||||||
user: Self,
|
user: Self,
|
||||||
next: ?Bytes,
|
next: ?[]const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn fromBytes(bytes: Bytes) Entry {
|
pub fn fromBytes(bytes: []const u8) Entry {
|
||||||
const inner = mem.bytesAsValue(
|
const inner = mem.bytesAsValue(
|
||||||
Inner,
|
Inner,
|
||||||
// Should use InnerSize instead of sizeOf, see
|
// Should use InnerSize instead of sizeOf, see
|
||||||
@ -159,7 +169,7 @@ fn packedUser(immutable: bool) type {
|
|||||||
const endBlob = startBlob + inner.blobLength();
|
const endBlob = startBlob + inner.blobLength();
|
||||||
|
|
||||||
const nextStart = pad.roundUp(usize, alignmentBits, endBlob);
|
const nextStart = pad.roundUp(usize, alignmentBits, endBlob);
|
||||||
var next: ?Bytes = null;
|
var next: ?[]const u8 = null;
|
||||||
if (nextStart < bytes.len)
|
if (nextStart < bytes.len)
|
||||||
next = bytes[nextStart..];
|
next = bytes[nextStart..];
|
||||||
|
|
||||||
@ -173,7 +183,7 @@ fn packedUser(immutable: bool) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const Iterator = struct {
|
pub const Iterator = struct {
|
||||||
section: ?Bytes,
|
section: ?[]const u8,
|
||||||
shellIndex: Idx2ShellProto,
|
shellIndex: Idx2ShellProto,
|
||||||
|
|
||||||
pub fn next(it: *Iterator) ?Self {
|
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 };
|
return Iterator{ .section = section, .shellIndex = idxFn };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,7 +208,7 @@ fn packedUser(immutable: bool) type {
|
|||||||
arr: *ArrayList(u8),
|
arr: *ArrayList(u8),
|
||||||
user: User,
|
user: User,
|
||||||
additional_gids_offset: u29,
|
additional_gids_offset: u29,
|
||||||
idxFn: shell2idxProto,
|
idxFn: ShellIndexType,
|
||||||
) packErr!void {
|
) packErr!void {
|
||||||
// function arguments are consts. We need to mutate the underlying
|
// function arguments are consts. We need to mutate the underlying
|
||||||
// slice, so passing it via pointer instead.
|
// slice, so passing it via pointer instead.
|
||||||
@ -216,8 +226,8 @@ fn packedUser(immutable: bool) type {
|
|||||||
.uid = user.uid,
|
.uid = user.uid,
|
||||||
.gid = user.gid,
|
.gid = user.gid,
|
||||||
.additional_gids_offset = additional_gids_offset,
|
.additional_gids_offset = additional_gids_offset,
|
||||||
.shell_here = idxFn(user.shell) == null,
|
.shell_here = idxFn.get(user.shell) == null,
|
||||||
.shell_len_or_idx = idxFn(user.shell) orelse shell_len,
|
.shell_len_or_idx = idxFn.get(user.shell) orelse shell_len,
|
||||||
.home_len = home_len,
|
.home_len = home_len,
|
||||||
.name_is_a_suffix = mem.endsWith(u8, user.home, user.name),
|
.name_is_a_suffix = mem.endsWith(u8, user.home, user.name),
|
||||||
.name_len = name_len,
|
.name_len = name_len,
|
||||||
@ -306,19 +316,23 @@ test "PackedUser internal and external alignment" {
|
|||||||
// more than 1, but it probably could be higher.
|
// more than 1, but it probably could be higher.
|
||||||
try testing.expectEqual(
|
try testing.expectEqual(
|
||||||
8,
|
8,
|
||||||
@sizeOf(PackedUserConst.Inner) * 8 -
|
@sizeOf(PackedUserHash.Inner) * 8 -
|
||||||
@bitSizeOf(PackedUserConst.Inner),
|
@bitSizeOf(PackedUserHash.Inner),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testShellIndex(shell: []const u8) ?u6 {
|
const TestShellIndex = struct {
|
||||||
if (mem.eql(u8, shell, "/bin/bash")) {
|
pub fn get(_: *const TestShellIndex, shell: []const u8) ?u6 {
|
||||||
return 0;
|
if (mem.eql(u8, shell, "/bin/bash")) {
|
||||||
} else if (mem.eql(u8, shell, "/bin/zsh")) {
|
return 0;
|
||||||
return 1;
|
} else if (mem.eql(u8, shell, "/bin/zsh")) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return null;
|
};
|
||||||
}
|
|
||||||
|
const PackedUserTest = packedUser(TestShellIndex);
|
||||||
|
|
||||||
fn testShell(index: u6) []const u8 {
|
fn testShell(index: u6) []const u8 {
|
||||||
return switch (index) {
|
return switch (index) {
|
||||||
@ -362,10 +376,10 @@ test "construct PackedUser section" {
|
|||||||
.shell = "/",
|
.shell = "/",
|
||||||
} };
|
} };
|
||||||
for (users) |user|
|
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 i: u29 = 0;
|
||||||
var it1 = PackedUserConst.iterator(buf.items, testShell);
|
var it1 = PackedUserTest.iterator(buf.items, testShell);
|
||||||
while (it1.next()) |user| : (i += 1) {
|
while (it1.next()) |user| : (i += 1) {
|
||||||
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());
|
||||||
@ -379,22 +393,6 @@ test "construct PackedUser section" {
|
|||||||
try testing.expectEqualStrings(users[i].shell, user.shell(testShell));
|
try testing.expectEqualStrings(users[i].shell, user.shell(testShell));
|
||||||
}
|
}
|
||||||
try testing.expectEqual(users.len, i);
|
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()" {
|
test "PackedUser.maxSize()" {
|
||||||
@ -402,7 +400,7 @@ test "PackedUser.maxSize()" {
|
|||||||
// As of writing, I am getting a stack smashing error.
|
// As of writing, I am getting a stack smashing error.
|
||||||
var buf = try ArrayList(u8).initCapacity(
|
var buf = try ArrayList(u8).initCapacity(
|
||||||
testing.allocator,
|
testing.allocator,
|
||||||
PackedUserConst.maxSize(),
|
PackedUserHash.maxSize(),
|
||||||
);
|
);
|
||||||
defer buf.deinit();
|
defer buf.deinit();
|
||||||
|
|
||||||
@ -414,8 +412,8 @@ test "PackedUser.maxSize()" {
|
|||||||
.home = "Home" ** 16, // 64
|
.home = "Home" ** 16, // 64
|
||||||
.shell = "She.LllL" ** 8, // 64
|
.shell = "She.LllL" ** 8, // 64
|
||||||
};
|
};
|
||||||
try PackedUserConst.packTo(&buf, largeUser, math.maxInt(u29), testShellIndex);
|
try PackedUserTest.packTo(&buf, largeUser, math.maxInt(u29), TestShellIndex{});
|
||||||
try testing.expectEqual(PackedUserConst.maxSize(), buf.items.len);
|
try testing.expectEqual(PackedUserTest.maxSize(), buf.items.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "User.clone" {
|
test "User.clone" {
|
||||||
|
Loading…
Reference in New Issue
Block a user