From 78dc63669de188cb87d219d30709aa9c366ac366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Tue, 22 Feb 2022 15:04:59 +0200 Subject: [PATCH] add tests for upper limits --- README.md | 34 ++++++++++++++++++++++++---------- src/user.zig | 20 ++++++++++---------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index febe278..f29d11a 100644 --- a/README.md +++ b/README.md @@ -130,9 +130,10 @@ Now that we've sketched the implementation of `id(3)`, it's clearer to understand which operations need to be fast; in order of importance: 1. lookup gid -> group info (this is on hot path in id) without members. -2. lookup uid -> user. -3. lookup groupname -> group. -4. lookup username -> user. +2. lookup username -> user's group memberships. +3. lookup uid -> user. +4. lookup groupname -> group. +5. lookup username -> user. These indices can use perfect hashing like [bdz from cmph][cmph]: a perfect hash hashes a list of bytes to a sequential list of integers. Perfect hashing @@ -292,8 +293,25 @@ Varint is an efficiently encoded integer (packed for small values). Same as [protocol buffer varints][varint], except the largest possible value is `u64`. They compress integers well. -`groupmembers`, `additional_gids` ---------------------------------- +Group memberships +----------------- + +There are two group memberships at play: + +1. given a username, resolve user's group gids (for `initgroups(3)`). +2. given a group (gid/name), resolve the members' names (e.g. `getgrgid`). + +When user's groups are resolved in (1), the additional userdata is not +requested (there is no way to return it). Therefore, it is reasonable to store +the user's memberships completely out-of-bound, keyed by the hash of the +username. + +When group's memberships are resolved in (2), the same call also requires other +group information: gid and group name. Therefore it makes sense to store a +pointer to the group members in the group information itself. However, the +memberships are not *always* necessary (see remarks about `id(1)` in this +document), therefore the memberships will be stored separately, outside of the +groups section. `groupmembers` and `additional_gids` store group and user memberships respectively: for each group, a list of pointers (offsets) to User records, and @@ -307,16 +325,12 @@ pseudo-code: ``` const PackedList = struct { length: varint, - members: []varint + members: [length]varint } const Groupmembers = PackedList; const AdditionalGids = PackedList; ``` -An entry in `members` field points to the offset into a respective `User` or -`Group` entry (number of bytes relative to the first entry of the type). -`members` in `PackedList` are sorted the same way as in the input. - A packed list is a list of varints. Complete file structure diff --git a/src/user.zig b/src/user.zig index 079adc4..ec455d4 100644 --- a/src/user.zig +++ b/src/user.zig @@ -32,15 +32,15 @@ pub const PackedUser = packed struct { } pub fn realHomeLen(self: *const PackedUser) usize { - return self.home_len + 1; + return @as(u32, self.home_len) + 1; } pub fn realNameLen(self: *const PackedUser) usize { - return self.name_len + 1; + return @as(u32, self.name_len) + 1; } pub fn realShellLen(self: *const PackedUser) usize { - return self.shell_len_or_idx + 1; + return @as(u32, self.shell_len_or_idx) + 1; } pub fn realGecosLen(self: *const PackedUser) usize { @@ -180,7 +180,7 @@ pub const UserReader = struct { if (u.name_is_a_suffix) { const name_start = u.realHomeLen() - u.realNameLen(); name = entry.blob[name_start..u.realHomeLen()]; - pos = u.home_len + 1; + pos = u.realHomeLen(); } else { const name_start = u.realHomeLen(); name = entry.blob[name_start .. name_start + u.realNameLen()]; @@ -269,12 +269,12 @@ test "construct PackedUser blob" { .home = "/home/service1", .shell = "/usr/bin/nologin", }, User{ - .uid = 1002, - .gid = 1002, - .name = "user-account", - .gecos = "", - .home = "/a", - .shell = "/bin/zsh", + .uid = 0, + .gid = 4294967295, + .name = "n" ** 32, + .gecos = "g" ** 255, + .home = "h" ** 64, + .shell = "s" ** 64, } }; for (users) |user| { try writer.appendUser(user);