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. ✅ `shellIndex`, `shellBlob`. No dependencies.
1. ✅ userGids. No dependencies. 1. ✅ userGids. No dependencies.
1. ✅ Users. Requires `userGids` and shell. 1. ✅ Users. Requires `userGids` and shell.
1. Groupmembers. Requires Users. 1. Groupmembers. Requires Users.
1. Groups. Requires Groupmembers. 1. Groups. Requires Groupmembers.
1. `idx_*`. Requires offsets to Groups and Users. 1. `idx_*`. Requires offsets to Groups and Users.
1. Header. 1. Header.

View File

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

View File

@ -243,7 +243,7 @@ pub fn usersSection(
) error{ OutOfMemory, Overflow, InvalidRecord }!UsersSection { ) error{ OutOfMemory, Overflow, InvalidRecord }!UsersSection {
var idx2offset = try allocator.alloc(u32, corpus.users.len); var idx2offset = try allocator.alloc(u32, corpus.users.len);
errdefer allocator.free(idx2offset); 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. // 8 bytes. 24 is an optimistic lower bound for an average record size.
var blob = try ArrayList(u8).initCapacity(allocator, 24 * corpus.users.len); var blob = try ArrayList(u8).initCapacity(allocator, 24 * corpus.users.len);
errdefer blob.deinit(); errdefer blob.deinit();
@ -257,6 +257,7 @@ pub fn usersSection(
gids.idx2offset[i], gids.idx2offset[i],
shells.indices, shells.indices,
); );
try pad.arrayList(&blob, userImport.PackedUserHash.alignment_bits);
} }
return UsersSection{ return UsersSection{
.idx2offset = idx2offset, .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. // cmpUser compares two users for sorting. By username's utf8 codepoints, ascending.
fn cmpUser(_: void, a: User, b: User) bool { fn cmpUser(_: void, a: User, b: User) bool {
var utf8_a = (unicode.Utf8View.init(a.name) catch unreachable).iterator(); var utf8_a = (unicode.Utf8View.init(a.name) catch unreachable).iterator();
@ -352,6 +395,7 @@ pub const AllSections = struct {
shell_blob: []const u8, shell_blob: []const u8,
user_gids: UserGids, user_gids: UserGids,
group_members: GroupMembers, group_members: GroupMembers,
groups: GroupsSection,
pub fn init( pub fn init(
allocator: Allocator, allocator: Allocator,
@ -376,6 +420,11 @@ pub const AllSections = struct {
corpus, corpus,
users.idx2offset, users.idx2offset,
); );
const groups = try groupsSection(
allocator,
corpus,
group_members.idx2offset,
);
return AllSections{ return AllSections{
.allocator = allocator, .allocator = allocator,
.bdz_gid = bdz_gid, .bdz_gid = bdz_gid,
@ -388,6 +437,7 @@ pub const AllSections = struct {
.user_gids = user_gids, .user_gids = user_gids,
.users = users, .users = users,
.group_members = group_members, .group_members = group_members,
.groups = groups,
}; };
} }
@ -400,6 +450,7 @@ pub const AllSections = struct {
self.user_gids.deinit(self.allocator); self.user_gids.deinit(self.allocator);
self.users.deinit(self.allocator); self.users.deinit(self.allocator);
self.group_members.deinit(self.allocator); self.group_members.deinit(self.allocator);
self.groups.deinit(self.allocator);
self.* = undefined; self.* = undefined;
} }
}; };
@ -510,7 +561,7 @@ test "test corpus" {
try testing.expectEqual(groupsOfVidmantas[2], g_all); try testing.expectEqual(groupsOfVidmantas[2], g_all);
} }
test "test group members" { test "test groups and group members" {
const allocator = testing.allocator; const allocator = testing.allocator;
var corpus = try testCorpus(allocator); var corpus = try testCorpus(allocator);
defer corpus.deinit(); defer corpus.deinit();
@ -529,7 +580,11 @@ test "test group members" {
const want_user_offset = sections.users.idx2offset[user_idx]; const want_user_offset = sections.users.idx2offset[user_idx];
try testing.expectEqual(got_user_offset, want_user_offset); 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" { test "userGids" {
@ -555,6 +610,7 @@ test "userGids" {
while (try it.next()) |gid| : (i += 1) { while (try it.next()) |gid| : (i += 1) {
try testing.expectEqual(gid, corpus.groups[groups[i]].gid); 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 { return struct {
const Self = @This(); const Self = @This();
const alignmentBits = 3; pub const alignment_bits = 3;
const Inner = packed struct { const Inner = packed struct {
uid: u32, uid: u32,
@ -165,7 +165,7 @@ fn packedUser(comptime ShellIndexType: type) type {
const gids_offset = try compress.uvarint(bytes[end_strings..]); const gids_offset = try compress.uvarint(bytes[end_strings..]);
const end_blob = end_strings + gids_offset.bytes_read; 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; var next: ?[]const u8 = null;
if (nextStart < bytes.len) if (nextStart < bytes.len)
next = bytes[nextStart..]; next = bytes[nextStart..];
@ -207,6 +207,7 @@ fn packedUser(comptime ShellIndexType: type) type {
additional_gids_offset: u64, additional_gids_offset: u64,
idxFn: ShellIndexType, idxFn: ShellIndexType,
) error{ InvalidRecord, OutOfMemory }!void { ) error{ InvalidRecord, OutOfMemory }!void {
std.debug.assert(arr.items.len & 7 == 0);
// 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.
const home_len = try validate.downCast(u6, user.home.len - 1); 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); 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(innerBytes[0..@sizeOf(Inner)]);
try arr.*.appendSlice(user.home); try arr.*.appendSlice(user.home);
@ -242,20 +241,6 @@ fn packedUser(comptime ShellIndexType: type) type {
if (inner.shell_here) if (inner.shell_here)
try arr.*.appendSlice(user.shell); try arr.*.appendSlice(user.shell);
try compress.appendUvarint(arr, additional_gids_offset); 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 { pub fn uid(self: Self) u32 {
@ -360,8 +345,10 @@ test "construct PackedUser section" {
.home = "/", .home = "/",
.shell = "/", .shell = "/",
} }; } };
for (users) |user| for (users) |user| {
try PackedUserTest.packTo(&buf, user, math.maxInt(u64), TestShellIndex{}); try PackedUserTest.packTo(&buf, user, math.maxInt(u64), TestShellIndex{});
try pad.arrayList(&buf, PackedUserHash.alignment_bits);
}
var i: u29 = 0; var i: u29 = 0;
var it1 = PackedUserTest.iterator(buf.items, testShell); var it1 = PackedUserTest.iterator(buf.items, testShell);
@ -380,27 +367,6 @@ test "construct PackedUser section" {
try testing.expectEqual(users.len, i); 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" { test "User.clone" {
var allocator = testing.allocator; var allocator = testing.allocator;
const user = User{ const user = User{