From 3eec9a32740002aa0cf69052c4c5e4279840adca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Mon, 28 Feb 2022 10:31:14 +0200 Subject: [PATCH] starting with sections --- src/group.zig | 16 ++-- src/sections.zig | 221 ++++++++++++++++++++++++++++++++++++++++++++++ src/so.zig | 20 +++++ src/test_main.zig | 4 +- 4 files changed, 255 insertions(+), 6 deletions(-) create mode 100644 src/sections.zig create mode 100644 src/so.zig diff --git a/src/group.zig b/src/group.zig index 2d88b7b..5f12b81 100644 --- a/src/group.zig +++ b/src/group.zig @@ -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), diff --git a/src/sections.zig b/src/sections.zig new file mode 100644 index 0000000..f5e5cee --- /dev/null +++ b/src/sections.zig @@ -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)); +} diff --git a/src/so.zig b/src/so.zig new file mode 100644 index 0000000..093564c --- /dev/null +++ b/src/so.zig @@ -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 +}; diff --git a/src/test_main.zig b/src/test_main.zig index b4d3101..0504d37 100644 --- a/src/test_main.zig +++ b/src/test_main.zig @@ -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");