From abf7edf14cdbbfc1a2e8c2f5bb9dd00144f81fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Thu, 14 Jul 2022 11:09:01 +0300 Subject: [PATCH] finish(?) turbonss-getent --- build.zig | 11 ++++++ src/PackedGroup.zig | 35 +++++++++-------- src/PackedUser.zig | 3 +- src/libnss.zig | 1 + src/turbo-getent.zig | 94 +++++++++++++++++++++++++++++++------------- 5 files changed, 97 insertions(+), 47 deletions(-) diff --git a/build.zig b/build.zig index bf3d0ca..3143b47 100644 --- a/build.zig +++ b/build.zig @@ -80,6 +80,17 @@ pub fn build(b: *zbs.Builder) void { exe.install(); } + { + const exe = b.addExecutable("turbo-getent", "src/turbo-getent.zig"); + exe.strip = strip; + exe.linkLibC(); + exe.linkLibrary(bdz); + exe.addIncludeDir("deps/cmph/src"); + exe.setTarget(target); + exe.setBuildMode(mode); + exe.install(); + } + { const so = b.addSharedLibrary("nss_turbo", "src/libnss.zig", .{ .versioned = builtin.Version{ diff --git a/src/PackedGroup.zig b/src/PackedGroup.zig index 0029257..9f1f363 100644 --- a/src/PackedGroup.zig +++ b/src/PackedGroup.zig @@ -36,7 +36,7 @@ members_offset: u64, pub const Entry = struct { group: PackedGroup, - next: ?[]const u8, + end: usize, }; pub fn fromBytes(bytes: []const u8) Entry { @@ -47,11 +47,6 @@ pub fn fromBytes(bytes: []const u8) Entry { error.Overflow => unreachable, }; const end_blob = end_strings + members_offset.bytes_read; - const next_start = pad.roundUp(usize, alignment_bits, end_blob); - - var next: ?[]const u8 = null; - if (next_start < bytes.len) - next = bytes[next_start..]; return Entry{ .group = PackedGroup{ @@ -59,7 +54,7 @@ pub fn fromBytes(bytes: []const u8) Entry { .groupdata = bytes[start_blob..end_strings], .members_offset = members_offset.value, }, - .next = next, + .end = pad.roundUp(usize, alignment_bits, end_blob), }; } @@ -69,20 +64,26 @@ fn validateUtf8(s: []const u8) InvalidRecord!void { } pub const Iterator = struct { - section: ?[]const u8, + section: []const u8, + nextStart: usize = 0, + idx: u32 = 0, + total: u32, pub fn next(it: *Iterator) ?PackedGroup { - if (it.section) |section| { - const entry = fromBytes(section); - it.section = entry.next; - return entry.group; - } - return null; + if (it.idx == it.total) return null; + + const entry = fromBytes(it.section[it.nextStart..]); + it.nextStart += entry.end; + it.idx += 1; + return entry.group; } }; -pub fn iterator(section: []const u8) Iterator { - return Iterator{ .section = section }; +pub fn iterator(section: []const u8, total: u32) Iterator { + return Iterator{ + .section = section, + .total = total, + }; } pub fn gid(self: *const PackedGroup) u32 { @@ -139,7 +140,7 @@ test "PackedGroup construct" { } var i: u29 = 0; - var it = PackedGroup.iterator(buf.items); + var it = PackedGroup.iterator(buf.items, groups.len); while (it.next()) |group| : (i += 1) { try testing.expectEqual(groups[i].gid, group.gid()); try testing.expectEqualStrings(groups[i].name, group.name()); diff --git a/src/PackedUser.zig b/src/PackedUser.zig index a3f7c4a..72c1ebb 100644 --- a/src/PackedUser.zig +++ b/src/PackedUser.zig @@ -111,8 +111,7 @@ pub const Iterator = struct { total: u32, pub fn next(it: *Iterator) ?PackedUser { - if (it.idx == it.total) - return null; + if (it.idx == it.total) return null; const entry = PackedUser.fromBytes(it.section[it.nextStart..]); it.nextStart += entry.end; diff --git a/src/libnss.zig b/src/libnss.zig index 72da6c6..53b3dae 100644 --- a/src/libnss.zig +++ b/src/libnss.zig @@ -296,6 +296,7 @@ fn setgrent(state: *State) void { defer state.getgrent_iterator_mu.unlock(); state.getgrent_iterator = PackedGroup.iterator( state.file.db.groups, + state.file.db.header.num_groups, ); } diff --git a/src/turbo-getent.zig b/src/turbo-getent.zig index 318eeee..b127485 100644 --- a/src/turbo-getent.zig +++ b/src/turbo-getent.zig @@ -10,6 +10,7 @@ const flags = @import("flags.zig"); const DB = @import("DB.zig"); const File = @import("File.zig"); const PackedUser = @import("PackedUser.zig"); +const PackedGroup = @import("PackedGroup.zig"); const User = @import("User.zig"); const Mode = enum { group, passwd }; @@ -100,7 +101,7 @@ fn execute( fn passwd(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) u8 { if (keys.len == 0) - return passwd_all(stdout, db); + return passwdAll(stdout, db); var some_notfound = false; const shell_reader = db.shellReader(); for (keys) |key| { @@ -122,7 +123,7 @@ fn passwd(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) u8 { return if (some_notfound) 2 else 0; } -fn passwd_all(stdout: anytype, db: *const DB) u8 { +fn passwdAll(stdout: anytype, db: *const DB) u8 { const shell_reader = db.shellReader(); var it = PackedUser.iterator(db.users, db.header.num_users, shell_reader); while (it.next()) |packed_user| { @@ -134,7 +135,7 @@ fn passwd_all(stdout: anytype, db: *const DB) u8 { fn group(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) u8 { if (keys.len == 0) - return group_all(stdout, db); + return groupAll(stdout, db); var some_notfound = false; for (keys) |key| { @@ -149,38 +150,48 @@ fn group(stdout: anytype, db: *const DB, keys: []const [*:0]const u8) u8 { continue; }; - // not converting to Group to save a few memory allocations. - stdout.print("{s}:x:{d}:", .{ g.name(), g.gid() }) catch return 3; - - // TODO: move member iteration from here and DB.packCGroup - // to a common place. - const members_slice = db.groupmembers[g.members_offset..]; - var vit = compress.varintSliceIteratorMust(members_slice); - var it = compress.deltaDecompressionIterator(&vit); - - // lines will be buffered, but flushed on every EOL. - var line_writer = io.bufferedWriter(stdout); - var i: usize = 0; - while (it.nextMust()) |member_offset| : (i += 1) { - const puser = PackedUser.fromBytes(db.users[member_offset << 3 ..]); - const name = puser.user.name(); - if (i != 0) - _ = line_writer.write(",") catch return 3; - _ = line_writer.write(name) catch return 3; - } - _ = line_writer.write("\n") catch return 3; - line_writer.flush() catch return 3; + if (printGroup(stdout, db, &g)) |exit_code| + return exit_code; } return if (some_notfound) 2 else 0; } -fn group_all(stdout: anytype, db: *const DB) u8 { - _ = stdout; - _ = db; +fn groupAll(stdout: anytype, db: *const DB) u8 { + var it = PackedGroup.iterator(db.groups, db.header.num_groups); + while (it.next()) |g| + if (printGroup(stdout, db, &g)) |exit_code| + return exit_code; + return 0; } +fn printGroup(stdout: anytype, db: *const DB, g: *const PackedGroup) ?u8 { + // not converting to Group to save a few memory allocations. + stdout.print("{s}:x:{d}:", .{ g.name(), g.gid() }) catch return 3; + + // TODO: move member iteration from here and DB.packCGroup + // to a common place. + const members_slice = db.groupmembers[g.members_offset..]; + var vit = compress.varintSliceIteratorMust(members_slice); + var it = compress.deltaDecompressionIterator(&vit); + + // lines will be buffered, but flushed on every EOL. + var line_writer = io.bufferedWriter(stdout); + var i: usize = 0; + while (it.nextMust()) |member_offset| : (i += 1) { + const puser = PackedUser.fromBytes(db.users[member_offset << 3 ..]); + const name = puser.user.name(); + if (i != 0) + _ = line_writer.write(",") catch return 3; + _ = line_writer.write(name) catch return 3; + } + _ = line_writer.write("\n") catch return 3; + line_writer.flush() catch return 3; + + return null; +} + const testing = std.testing; test "turbo-getent passwd" { @@ -213,7 +224,7 @@ test "turbo-getent passwd" { } } -test "turbo-getent passwd_all" { +test "turbo-getent passwdAll" { var tf = try File.TestDB.init(testing.allocator); defer tf.deinit(); var stdout = ArrayList(u8).init(testing.allocator); @@ -278,3 +289,30 @@ test "turbo-getent group" { try testing.expectEqualStrings(want, stdout.items); } } + +test "turbo-getent groupAll" { + var tf = try File.TestDB.init(testing.allocator); + defer tf.deinit(); + var stdout = ArrayList(u8).init(testing.allocator); + defer stdout.deinit(); + var stderr = ArrayList(u8).init(testing.allocator); + defer stderr.deinit(); + + { + const args = &[_][*:0]const u8{ + "--db", + tf.path, + "group", + }; + const got = execute(stdout.writer(), stderr.writer(), args); + try testing.expectEqual(got, 0); + const want = + \\root:x:0: + \\vidmantas:x:128: + \\all:x:9999:NameNameNameNameNameNameNameName,root,svc-bar,vidmantas + \\service-group:x:100000:root,vidmantas + \\ + ; + try testing.expectEqualStrings(want, stdout.items); + } +}