starting with sections
This commit is contained in:
parent
40deb3f0be
commit
3eec9a3274
@ -8,7 +8,13 @@ const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
const Group = struct {
|
||||
pub const Group = struct {
|
||||
gid: u32,
|
||||
name: []const u8,
|
||||
members: std.BufSet,
|
||||
};
|
||||
|
||||
const GroupStored = struct {
|
||||
gid: u32,
|
||||
name: []const u8,
|
||||
members_offset: u32,
|
||||
@ -92,7 +98,7 @@ const PackedGroup = struct {
|
||||
|
||||
pub fn packTo(
|
||||
arr: *ArrayList(u8),
|
||||
group: Group,
|
||||
group: GroupStored,
|
||||
) packErr!void {
|
||||
const groupname_len = try validate.downCast(u5, group.name.len - 1);
|
||||
|
||||
@ -121,13 +127,13 @@ test "construct PackedGroups" {
|
||||
var buf = ArrayList(u8).init(testing.allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
const groups = [_]Group{
|
||||
Group{
|
||||
const groups = [_]GroupStored{
|
||||
.{
|
||||
.gid = 1000,
|
||||
.name = "sudo",
|
||||
.members_offset = 1,
|
||||
},
|
||||
Group{
|
||||
.{
|
||||
.gid = std.math.maxInt(u32),
|
||||
.name = "Name" ** 8, // 32
|
||||
.members_offset = std.math.maxInt(u32),
|
||||
|
221
src/sections.zig
Normal file
221
src/sections.zig
Normal file
@ -0,0 +1,221 @@
|
||||
const std = @import("std");
|
||||
const unicode = std.unicode;
|
||||
const sort = std.sort;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ArrayList = std.ArrayList;
|
||||
const StringHashMap = std.StringHashMap;
|
||||
const AutoHashMap = std.AutoHashMap;
|
||||
const BufSet = std.BufSet;
|
||||
|
||||
const pad = @import("padding.zig");
|
||||
const userImport = @import("user.zig");
|
||||
const groupImport = @import("group.zig");
|
||||
const User = userImport.User;
|
||||
const Group = groupImport.Group;
|
||||
|
||||
const Corpus = struct {
|
||||
allocator: Allocator,
|
||||
|
||||
// sorted by name, by unicode codepoint
|
||||
users: []User,
|
||||
// sorted by gid
|
||||
groups: []Group,
|
||||
|
||||
// pointing to `users` and `groups` slices above.
|
||||
name2user: StringHashMap(*const User),
|
||||
uid2user: AutoHashMap(u32, *const User),
|
||||
name2group: StringHashMap(*const Group),
|
||||
gid2group: AutoHashMap(u32, *const Group),
|
||||
groupname2users: StringHashMap(ArrayList(*const User)),
|
||||
username2groups: StringHashMap(ArrayList(*const Group)),
|
||||
|
||||
pub const initErr = Allocator.Error ||
|
||||
error{InvalidUtf8} ||
|
||||
error{Duplicate} ||
|
||||
error{NotFound};
|
||||
|
||||
pub fn init(
|
||||
allocator: Allocator,
|
||||
usersConst: []const User,
|
||||
groupsConst: []const Group,
|
||||
) initErr!Corpus {
|
||||
var users = try allocator.alloc(User, usersConst.len);
|
||||
var groups = try allocator.alloc(Group, groupsConst.len);
|
||||
std.mem.copy(User, users, usersConst);
|
||||
std.mem.copy(Group, groups, groupsConst);
|
||||
|
||||
sort.sort(User, users, {}, cmpUser);
|
||||
sort.sort(Group, groups, {}, cmpGroup);
|
||||
|
||||
var name2user = StringHashMap(*const User).init(allocator);
|
||||
var uid2user = AutoHashMap(u32, *const User).init(allocator);
|
||||
var name2group = StringHashMap(*const Group).init(allocator);
|
||||
var gid2group = AutoHashMap(u32, *const Group).init(allocator);
|
||||
for (users) |user| {
|
||||
var res1 = try name2user.getOrPut(user.name);
|
||||
if (res1.found_existing) {
|
||||
return error.Duplicate;
|
||||
}
|
||||
res1.value_ptr.* = &user;
|
||||
|
||||
var res2 = try uid2user.getOrPut(user.uid);
|
||||
if (res2.found_existing) {
|
||||
return error.Duplicate;
|
||||
}
|
||||
res2.value_ptr.* = &user;
|
||||
}
|
||||
|
||||
for (groups) |group| {
|
||||
var res1 = try name2group.getOrPut(group.name);
|
||||
if (res1.found_existing) {
|
||||
return error.Duplicate;
|
||||
}
|
||||
res1.value_ptr.* = &group;
|
||||
var res2 = try gid2group.getOrPut(group.gid);
|
||||
if (res2.found_existing) {
|
||||
return error.Duplicate;
|
||||
}
|
||||
res2.value_ptr.* = &group;
|
||||
}
|
||||
|
||||
var groupname2users = StringHashMap(ArrayList(*const User)).init(allocator);
|
||||
var username2groups = StringHashMap(ArrayList(*const Group)).init(allocator);
|
||||
for (groups) |group| {
|
||||
const cnt = group.members.count();
|
||||
var members = try ArrayList(*const User).initCapacity(allocator, cnt);
|
||||
|
||||
var it = group.members.iterator();
|
||||
while (it.next()) |memberName| {
|
||||
if (name2user.get(memberName.*)) |user| {
|
||||
members.appendAssumeCapacity(user);
|
||||
} else {
|
||||
return error.NotFound;
|
||||
}
|
||||
|
||||
var groupUsers: ArrayList(*const Group) = undefined;
|
||||
var result = try username2groups.getOrPut(memberName.*);
|
||||
if (result.found_existing) {
|
||||
groupUsers = ArrayList(*const Group).init(allocator);
|
||||
result.value_ptr.* = groupUsers;
|
||||
} else {
|
||||
groupUsers = result.value_ptr.*;
|
||||
}
|
||||
try groupUsers.append(&group);
|
||||
}
|
||||
|
||||
var result = try groupname2users.getOrPut(group.name);
|
||||
if (result.found_existing) {
|
||||
return error.Duplicate;
|
||||
}
|
||||
result.value_ptr.* = members;
|
||||
}
|
||||
|
||||
return Corpus{
|
||||
.allocator = allocator,
|
||||
.users = users,
|
||||
.groups = groups,
|
||||
.name2user = name2user,
|
||||
.uid2user = uid2user,
|
||||
.name2group = name2group,
|
||||
.gid2group = gid2group,
|
||||
.groupname2users = groupname2users,
|
||||
.username2groups = username2groups,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// 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();
|
||||
var utf8_b = (unicode.Utf8View.init(b.name) catch unreachable).iterator();
|
||||
while (utf8_a.nextCodepoint()) |codepoint_a| {
|
||||
if (utf8_b.nextCodepoint()) |codepoint_b| {
|
||||
if (codepoint_a != codepoint_b) {
|
||||
return codepoint_a < codepoint_b;
|
||||
}
|
||||
} else {
|
||||
// a is a prefix of b. It is thus shorter.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// b is a prefix of a
|
||||
return true;
|
||||
}
|
||||
|
||||
fn cmpGroup(_: void, a: Group, b: Group) bool {
|
||||
return a.gid < b.gid;
|
||||
}
|
||||
|
||||
const testing = std.testing;
|
||||
|
||||
fn someMembers(allocator: Allocator, members: []const []const u8) !BufSet {
|
||||
var bufset = BufSet.init(allocator);
|
||||
for (members) |member| {
|
||||
try bufset.insert(member);
|
||||
}
|
||||
return bufset;
|
||||
}
|
||||
|
||||
test "test corpus" {
|
||||
const allocator = testing.allocator;
|
||||
|
||||
var users = [_]User{ User{
|
||||
.uid = 1000,
|
||||
.gid = 1000,
|
||||
.name = "vidmantas",
|
||||
.gecos = "Vidmantas Kaminskas",
|
||||
.home = "/home/vidmantas",
|
||||
.shell = "/bin/bash",
|
||||
}, User{
|
||||
.uid = 0,
|
||||
.gid = std.math.maxInt(u32),
|
||||
.name = "Name" ** 8,
|
||||
.gecos = "Gecos" ** 51,
|
||||
.home = "Home" ** 16,
|
||||
.shell = "She.LllL" ** 8,
|
||||
}, User{
|
||||
.uid = 1002,
|
||||
.gid = 1002,
|
||||
.name = "svc-bar",
|
||||
.gecos = "",
|
||||
.home = "/",
|
||||
.shell = "/",
|
||||
} };
|
||||
|
||||
var groups = [_]Group{ Group{
|
||||
.gid = 1000,
|
||||
.name = "vidmantas",
|
||||
.members = try someMembers(allocator, &[_][]const u8{"vidmantas"}),
|
||||
}, Group{
|
||||
.gid = 1234,
|
||||
.name = "service-account",
|
||||
.members = try someMembers(allocator, &[_][]const u8{ "svc-bar", "Name" ** 8 }),
|
||||
}, Group{
|
||||
.gid = 9999,
|
||||
.name = "empty",
|
||||
.members = try someMembers(allocator, &[_][]const u8{}),
|
||||
} };
|
||||
|
||||
const corpus = try Corpus.init(allocator, users[0..], groups[0..]);
|
||||
try testing.expectEqualStrings(corpus.users[0].name, "svc-bar");
|
||||
try testing.expectEqualStrings(corpus.users[1].name, "vidmantas");
|
||||
try testing.expectEqualStrings(corpus.users[2].name, "Name" ** 8);
|
||||
}
|
||||
|
||||
fn testUser(name: []const u8) User {
|
||||
var result = std.mem.zeroes(User);
|
||||
result.name = name;
|
||||
return result;
|
||||
}
|
||||
|
||||
test "users compare function" {
|
||||
const a = testUser("a");
|
||||
const b = testUser("b");
|
||||
const bb = testUser("bb");
|
||||
try testing.expect(cmpUser({}, a, b));
|
||||
try testing.expect(!cmpUser({}, b, a));
|
||||
try testing.expect(cmpUser({}, a, bb));
|
||||
try testing.expect(!cmpUser({}, bb, a));
|
||||
try testing.expect(cmpUser({}, b, bb));
|
||||
try testing.expect(!cmpUser({}, bb, b));
|
||||
}
|
20
src/so.zig
Normal file
20
src/so.zig
Normal file
@ -0,0 +1,20 @@
|
||||
const Passwd = extern struct {
|
||||
// zig fmt: off
|
||||
pw_name: [*:0]u8, // username
|
||||
pw_passwd: [*:0]const u8, // user password, always '*'
|
||||
pw_uid: u32, // user ID
|
||||
pw_gid: u32, // group ID
|
||||
pw_gecos: [*:0]const u8, // user information
|
||||
pw_dir: [*:0]const u8, // home directory
|
||||
pw_shell: [*:0]const u8, // shell program
|
||||
// zig fmt: on
|
||||
};
|
||||
|
||||
const Group = extern struct {
|
||||
// zig fmt: off
|
||||
gr_name: [*:0]u8, // group name
|
||||
gr_passwd: [*:0]u8, // group password, always '*'
|
||||
gr_gid: u32, // group ID
|
||||
gr_mem: [*:0][*:0] const u8, // NULL-terminated array of pointers to group members
|
||||
// zig fmt: off
|
||||
};
|
@ -1,7 +1,9 @@
|
||||
test "turbonss test suite" {
|
||||
_ = @import("main.zig");
|
||||
_ = @import("shell.zig");
|
||||
_ = @import("header.zig");
|
||||
_ = @import("so.zig");
|
||||
_ = @import("sections.zig");
|
||||
_ = @import("shell.zig");
|
||||
_ = @import("user.zig");
|
||||
_ = @import("group.zig");
|
||||
_ = @import("validate.zig");
|
||||
|
Loading…
Reference in New Issue
Block a user