1
Fork 0

add tests for upper limits

This commit is contained in:
Motiejus Jakštys 2022-02-22 15:04:59 +02:00 committed by Motiejus Jakštys
parent 26fbfc5f08
commit 78dc63669d
2 changed files with 34 additions and 20 deletions

View File

@ -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: 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. 1. lookup gid -> group info (this is on hot path in id) without members.
2. lookup uid -> user. 2. lookup username -> user's group memberships.
3. lookup groupname -> group. 3. lookup uid -> user.
4. lookup username -> user. 4. lookup groupname -> group.
5. lookup username -> user.
These indices can use perfect hashing like [bdz from cmph][cmph]: a perfect 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 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`. [protocol buffer varints][varint], except the largest possible value is `u64`.
They compress integers well. 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 `groupmembers` and `additional_gids` store group and user memberships
respectively: for each group, a list of pointers (offsets) to User records, and respectively: for each group, a list of pointers (offsets) to User records, and
@ -307,16 +325,12 @@ pseudo-code:
``` ```
const PackedList = struct { const PackedList = struct {
length: varint, length: varint,
members: []varint members: [length]varint
} }
const Groupmembers = PackedList; const Groupmembers = PackedList;
const AdditionalGids = 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. A packed list is a list of varints.
Complete file structure Complete file structure

View File

@ -32,15 +32,15 @@ pub const PackedUser = packed struct {
} }
pub fn realHomeLen(self: *const PackedUser) usize { 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 { 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 { 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 { pub fn realGecosLen(self: *const PackedUser) usize {
@ -180,7 +180,7 @@ pub const UserReader = struct {
if (u.name_is_a_suffix) { if (u.name_is_a_suffix) {
const name_start = u.realHomeLen() - u.realNameLen(); const name_start = u.realHomeLen() - u.realNameLen();
name = entry.blob[name_start..u.realHomeLen()]; name = entry.blob[name_start..u.realHomeLen()];
pos = u.home_len + 1; pos = u.realHomeLen();
} else { } else {
const name_start = u.realHomeLen(); const name_start = u.realHomeLen();
name = entry.blob[name_start .. name_start + u.realNameLen()]; name = entry.blob[name_start .. name_start + u.realNameLen()];
@ -269,12 +269,12 @@ test "construct PackedUser blob" {
.home = "/home/service1", .home = "/home/service1",
.shell = "/usr/bin/nologin", .shell = "/usr/bin/nologin",
}, User{ }, User{
.uid = 1002, .uid = 0,
.gid = 1002, .gid = 4294967295,
.name = "user-account", .name = "n" ** 32,
.gecos = "", .gecos = "g" ** 255,
.home = "/a", .home = "h" ** 64,
.shell = "/bin/zsh", .shell = "s" ** 64,
} }; } };
for (users) |user| { for (users) |user| {
try writer.appendUser(user); try writer.appendUser(user);