Implement Groupmembers and Groups section without tests

This commit is contained in:
Motiejus Jakštys 2022-03-10 18:50:40 +02:00 committed by Motiejus Jakštys
parent d6ba7bed63
commit c78a2c0def
4 changed files with 71 additions and 49 deletions

View File

@ -376,7 +376,7 @@ Section creation order:
1. ✅ `shellIndex`, `shellBlob`. No dependencies.
1. ✅ userGids. No dependencies.
1. ✅ Users. Requires `userGids` and shell.
1. Groupmembers. Requires Users.
1. Groupmembers. Requires Users.
1. Groups. Requires Groupmembers.
1. `idx_*`. Requires offsets to Groups and Users.
1. Header.

View File

@ -31,14 +31,14 @@ pub const Group = struct {
}
};
const GroupStored = struct {
pub const GroupStored = struct {
gid: u32,
name: []const u8,
members_offset: u64,
};
const PackedGroup = struct {
const alignment_bits = 3;
pub const PackedGroup = struct {
pub const alignment_bits = 3;
const Inner = packed struct {
gid: u32,
@ -119,6 +119,7 @@ const PackedGroup = struct {
arr: *ArrayList(u8),
group: GroupStored,
) packErr!void {
std.debug.assert(arr.items.len & 7 == 0);
const groupname_len = try validate.downCast(u5, group.name.len - 1);
try validate.utf8(group.name);
const inner = Inner{
@ -129,7 +130,6 @@ const PackedGroup = struct {
try arr.*.appendSlice(mem.asBytes(&inner));
try arr.*.appendSlice(group.name);
try compress.appendUvarint(arr, group.members_offset);
try pad.arrayList(arr, alignment_bits);
}
};
@ -142,9 +142,8 @@ pub fn someMembers(
) Allocator.Error!BufSet {
var bufset = BufSet.init(allocator);
errdefer bufset.deinit();
for (members) |member| {
for (members) |member|
try bufset.insert(member);
}
return bufset;
}
@ -171,6 +170,7 @@ test "construct PackedGroups" {
for (groups) |group| {
try PackedGroup.packTo(&buf, group);
try pad.arrayList(&buf, PackedGroup.alignment_bits);
}
var i: u29 = 0;

View File

@ -243,7 +243,7 @@ pub fn usersSection(
) error{ OutOfMemory, Overflow, InvalidRecord }!UsersSection {
var idx2offset = try allocator.alloc(u32, corpus.users.len);
errdefer allocator.free(idx2offset);
// as of writing each user takes 15 bytes + strings + padding, padded to
// as of writing each user takes 12 bytes + blobs + padding, padded to
// 8 bytes. 24 is an optimistic lower bound for an average record size.
var blob = try ArrayList(u8).initCapacity(allocator, 24 * corpus.users.len);
errdefer blob.deinit();
@ -257,6 +257,7 @@ pub fn usersSection(
gids.idx2offset[i],
shells.indices,
);
try pad.arrayList(&blob, userImport.PackedUserHash.alignment_bits);
}
return UsersSection{
.idx2offset = idx2offset,
@ -317,6 +318,48 @@ pub fn groupMembers(
};
}
pub const GroupsSection = struct {
// group index -> offset in blob
idx2offset: []const u32,
blob: []const u8,
pub fn deinit(self: *GroupsSection, allocator: Allocator) void {
allocator.free(self.idx2offset);
allocator.free(self.blob);
self.* = undefined;
}
};
pub fn groupsSection(
allocator: Allocator,
corpus: *const Corpus,
members_offset: []const u64,
) error{ OutOfMemory, Overflow, InvalidRecord }!GroupsSection {
var idx2offset = try allocator.alloc(u32, corpus.groups.len);
errdefer allocator.free(idx2offset);
var blob = try ArrayList(u8).initCapacity(allocator, 8 * corpus.groups.len);
errdefer blob.deinit();
for (corpus.groups) |group, i| {
const group_offset = try math.cast(u32, blob.items.len);
std.debug.assert(group_offset & 7 == 0);
idx2offset[i] = group_offset;
const group_stored = groupImport.GroupStored{
.gid = group.gid,
.name = group.name,
.members_offset = members_offset[i],
};
try groupImport.PackedGroup.packTo(&blob, group_stored);
try pad.arrayList(&blob, groupImport.PackedGroup.alignment_bits);
}
return GroupsSection{
.idx2offset = idx2offset,
.blob = blob.toOwnedSlice(),
};
}
// cmpUser compares two users for sorting. By username's utf8 codepoints, ascending.
fn cmpUser(_: void, a: User, b: User) bool {
var utf8_a = (unicode.Utf8View.init(a.name) catch unreachable).iterator();
@ -352,6 +395,7 @@ pub const AllSections = struct {
shell_blob: []const u8,
user_gids: UserGids,
group_members: GroupMembers,
groups: GroupsSection,
pub fn init(
allocator: Allocator,
@ -376,6 +420,11 @@ pub const AllSections = struct {
corpus,
users.idx2offset,
);
const groups = try groupsSection(
allocator,
corpus,
group_members.idx2offset,
);
return AllSections{
.allocator = allocator,
.bdz_gid = bdz_gid,
@ -388,6 +437,7 @@ pub const AllSections = struct {
.user_gids = user_gids,
.users = users,
.group_members = group_members,
.groups = groups,
};
}
@ -400,6 +450,7 @@ pub const AllSections = struct {
self.user_gids.deinit(self.allocator);
self.users.deinit(self.allocator);
self.group_members.deinit(self.allocator);
self.groups.deinit(self.allocator);
self.* = undefined;
}
};
@ -510,7 +561,7 @@ test "test corpus" {
try testing.expectEqual(groupsOfVidmantas[2], g_all);
}
test "test group members" {
test "test groups and group members" {
const allocator = testing.allocator;
var corpus = try testCorpus(allocator);
defer corpus.deinit();
@ -529,7 +580,11 @@ test "test group members" {
const want_user_offset = sections.users.idx2offset[user_idx];
try testing.expectEqual(got_user_offset, want_user_offset);
}
try testing.expectEqual(it.next(), null);
}
//var it = userImport.PackedUserHash.iterator(sections.groups.blob);
//_ = it;
}
test "userGids" {
@ -555,6 +610,7 @@ test "userGids" {
while (try it.next()) |gid| : (i += 1) {
try testing.expectEqual(gid, corpus.groups[groups[i]].gid);
}
try testing.expectEqual(i, groups.len);
}
}

View File

@ -84,7 +84,7 @@ fn packedUser(comptime ShellIndexType: type) type {
return struct {
const Self = @This();
const alignmentBits = 3;
pub const alignment_bits = 3;
const Inner = packed struct {
uid: u32,
@ -165,7 +165,7 @@ fn packedUser(comptime ShellIndexType: type) type {
const gids_offset = try compress.uvarint(bytes[end_strings..]);
const end_blob = end_strings + gids_offset.bytes_read;
const nextStart = pad.roundUp(usize, alignmentBits, end_blob);
const nextStart = pad.roundUp(usize, alignment_bits, end_blob);
var next: ?[]const u8 = null;
if (nextStart < bytes.len)
next = bytes[nextStart..];
@ -207,6 +207,7 @@ fn packedUser(comptime ShellIndexType: type) type {
additional_gids_offset: u64,
idxFn: ShellIndexType,
) error{ InvalidRecord, OutOfMemory }!void {
std.debug.assert(arr.items.len & 7 == 0);
// function arguments are consts. We need to mutate the underlying
// slice, so passing it via pointer instead.
const home_len = try validate.downCast(u6, user.home.len - 1);
@ -231,8 +232,6 @@ fn packedUser(comptime ShellIndexType: type) type {
};
const innerBytes = mem.asBytes(&inner);
// innerBytes.len is longer than @sizeOf(Inner). We want to copy
// only the @sizeOf(Inner)-number of bytes.
try arr.*.appendSlice(innerBytes[0..@sizeOf(Inner)]);
try arr.*.appendSlice(user.home);
@ -242,20 +241,6 @@ fn packedUser(comptime ShellIndexType: type) type {
if (inner.shell_here)
try arr.*.appendSlice(user.shell);
try compress.appendUvarint(arr, additional_gids_offset);
try pad.arrayList(arr, alignmentBits);
}
// maxSize is the maximum number of records a PackedUser can take
// (struct + strings).
pub fn maxSize() usize {
comptime {
const unpadded = @sizeOf(Inner) +
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);
}
}
pub fn uid(self: Self) u32 {
@ -360,8 +345,10 @@ test "construct PackedUser section" {
.home = "/",
.shell = "/",
} };
for (users) |user|
for (users) |user| {
try PackedUserTest.packTo(&buf, user, math.maxInt(u64), TestShellIndex{});
try pad.arrayList(&buf, PackedUserHash.alignment_bits);
}
var i: u29 = 0;
var it1 = PackedUserTest.iterator(buf.items, testShell);
@ -380,27 +367,6 @@ test "construct PackedUser section" {
try testing.expectEqual(users.len, i);
}
test "PackedUser.maxSize()" {
// TODO(motiejus) try using a slice that points to an array in stack.
// As of writing, I am getting a stack smashing error.
var buf = try ArrayList(u8).initCapacity(
testing.allocator,
PackedUserHash.maxSize(),
);
defer buf.deinit();
const largeUser = User{
.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 PackedUserTest.packTo(&buf, largeUser, math.maxInt(u29), TestShellIndex{});
try testing.expectEqual(PackedUserTest.maxSize(), buf.items.len);
}
test "User.clone" {
var allocator = testing.allocator;
const user = User{