add tests for upper limits
This commit is contained in:
parent
26fbfc5f08
commit
78dc63669d
34
README.md
34
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:
|
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
|
||||||
|
|
20
src/user.zig
20
src/user.zig
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue