diff --git a/lib/CGroup.zig b/lib/CGroup.zig deleted file mode 100644 index d0c9e70..0000000 --- a/lib/CGroup.zig +++ /dev/null @@ -1,7 +0,0 @@ -const CGroup = @This(); - -gid: u32, -name: [:0]const u8, -// Should be a sentinel-terminated array. -// https://github.com/ziglang/zig/issues/7517 -members: []align(1) const ?[*:0]const u8, diff --git a/lib/Corpus.zig b/lib/Corpus.zig index ec81dbf..3c84dc2 100644 --- a/lib/Corpus.zig +++ b/lib/Corpus.zig @@ -46,12 +46,12 @@ pub fn init( var getgr_bufsize: usize = 0; for (groupsConst) |*group, i| { groups_arr[i] = try group.clone(allocator); - getgr_bufsize = math.max(getgr_bufsize, groups_arr[i].strlenZ()); + getgr_bufsize = math.max(getgr_bufsize, group.strlenZ()); } var getpw_bufsize: usize = 0; for (usersConst) |*user, i| { users_arr[i] = try user.clone(allocator); - getpw_bufsize = math.max(getpw_bufsize, users_arr[i].strlenZ()); + getpw_bufsize = math.max(getpw_bufsize, user.strlenZ()); } sort.sort(User, users_arr, {}, cmpUser); diff --git a/lib/DB.zig b/lib/DB.zig index 895df9d..36f09e3 100644 --- a/lib/DB.zig +++ b/lib/DB.zig @@ -16,7 +16,7 @@ const pad = @import("padding.zig"); const compress = @import("compress.zig"); const PackedUser = @import("PackedUser.zig"); const User = @import("User.zig"); -const CGroup = @import("CGroup.zig"); +const CGroup = @import("cgroup.zig").CGroup; const Group = @import("Group.zig"); const PackedGroup = @import("PackedGroup.zig"); const GroupStored = PackedGroup.GroupStored; @@ -267,13 +267,8 @@ fn getGroup( }; } -// getgrtnam returns a Group entry by name. The Group must be -// deinit'ed by caller. -fn getgrnam( - self: *const DB, - name: []const u8, - buf: *[]u8, -) error{OutOfMemory}!?CGroup { +// get a CGroup entry by name. +fn getgrnam(self: *const DB, name: []const u8, buf: *[]u8) error{OutOfMemory}!?CGroup { const idx = bdz.search(self.bdz_groupname, name); const offset = self.idx_groupname2group[idx]; const group = PackedGroup.fromBytes(self.groups[offset << 3 ..]).group; @@ -591,26 +586,43 @@ test "read/write via iovec" { } test "high-level API" { - const allocator = testing.allocator; - var corpus = try Corpus.testCorpus(allocator); + var corpus = try Corpus.testCorpus(testing.allocator); defer corpus.deinit(); + var db = try DB.fromCorpus(testing.allocator, &corpus); + defer db.deinit(testing.allocator); + var buf = try testing.allocator.alloc(u8, db.header.getgr_bufsize); + defer testing.allocator.free(buf); - var db = try DB.fromCorpus(allocator, &corpus); - defer db.deinit(allocator); - - // TODO: should accept a buffer instead of an allocator instead. - var buf = try allocator.alloc(u8, db.header.getgr_bufsize); - defer allocator.free(buf); const all = try db.getgrnam("all", &buf); try testing.expect(all != null); try testing.expectEqual(all.?.gid, 9999); - try testing.expectEqualStrings(all.?.name, "all"); + try testing.expectEqualStrings(mem.sliceTo(all.?.name, 0), "all"); const members = all.?.members; try testing.expectEqualStrings(mem.sliceTo(members[0].?, 0), "Name" ** 8); try testing.expectEqualStrings(mem.sliceTo(members[1].?, 0), "root"); try testing.expectEqualStrings(mem.sliceTo(members[2].?, 0), "svc-bar"); try testing.expectEqualStrings(mem.sliceTo(members[3].?, 0), "vidmantas"); try testing.expectEqual(members[4], null); + + const cgroup = all.?.ptr(); + try testing.expectEqual(cgroup.gid, all.?.gid); + try testing.expectEqual(cgroup.name[0], 'a'); + try testing.expectEqual(cgroup.name[1], 'l'); + try testing.expectEqual(cgroup.name[2], 'l'); + try testing.expectEqual(cgroup.name[3], 0); +} + +test "getgr_bufsize" { + var corpus = try Corpus.testCorpus(testing.allocator); + defer corpus.deinit(); + var db = try DB.fromCorpus(testing.allocator, &corpus); + defer db.deinit(testing.allocator); + var buf = try testing.allocator.alloc(u8, db.header.getgr_bufsize); + defer testing.allocator.free(buf); + + _ = try db.getgrnam("all", &buf); + buf.len -= 1; + try testing.expectError(error.OutOfMemory, db.getgrnam("all", &buf)); } test "additionalGids" { diff --git a/lib/Group.zig b/lib/Group.zig index f8b1b6d..e43ac8f 100644 --- a/lib/Group.zig +++ b/lib/Group.zig @@ -1,6 +1,8 @@ const std = @import("std"); +const ptr_size = @import("cgroup.zig").ptr_size; const mem = std.mem; +const meta = std.meta; const Allocator = mem.Allocator; const Group = @This(); @@ -56,9 +58,12 @@ pub fn deinit(self: *Group, allocator: Allocator) void { // buffer size in bytes if all strings were zero-terminated. pub fn strlenZ(self: *const Group) usize { - return self._buf.len + - self.members.len + // each membername sentinel - 1; // name sentinel + var count: usize = 0; + for (self.members) |member| + count += member.len + 1; + count += ptr_size * (self.members.len + 1); + count += self.name.len + 1; + return count; } const testing = std.testing; diff --git a/lib/cgroup.zig b/lib/cgroup.zig new file mode 100644 index 0000000..be29dd9 --- /dev/null +++ b/lib/cgroup.zig @@ -0,0 +1,32 @@ +const meta = @import("std").meta; + +// All string fields are 0-terminated, but that's not part of the type. +// members field is null-terminated, but that's also not part of the type. +// Making it part of the type crashes zig compiler in pre-0.10. +// https://github.com/ziglang/zig/issues/7517 + +pub const CGroup = struct { + gid: u32, + // should be [*:0]const u8 + name: []const u8, + // Should be [*:null]align(1) const ?[*:0]const u8 + members: []align(1) const ?[*:0]const u8, + + pub fn ptr(self: *const CGroup) CGroupPtr { + return CGroupPtr{ + .gid = self.gid, + .name = self.name.ptr, + .members = @intToPtr([*]const u8, @ptrToInt(self.members.ptr)), + }; + } +}; + +// size of the pointer to a single member. +pub const ptr_size = @sizeOf(meta.Child(meta.fieldInfo(CGroup, .members).field_type)); + +// a workaround of not having sentinel-terminated `name` and `members`. +pub const CGroupPtr = extern struct { + gid: u32, + name: [*]const u8, + members: [*]const u8, +}; diff --git a/lib/header.zig b/lib/header.zig index 08812f1..96ea7a4 100644 --- a/lib/header.zig +++ b/lib/header.zig @@ -2,6 +2,7 @@ const std = @import("std"); const mem = std.mem; const math = std.math; const native_endian = @import("builtin").target.cpu.arch.endian(); +const ptr_size = @import("cgroup.zig").ptr_size; const max_shells = @import("shell.zig").max_shells; const magic = [4]u8{ 0xf0, 0x9f, 0xa4, 0xb7 }; @@ -33,7 +34,7 @@ pub const Header = packed struct { magic: [4]u8 = magic, version: u8 = version, endian: Endian = Endian.native(), - ptr_size: u4 = @sizeOf(?[*:0]const u8), + ptr_size: u4 = ptr_size, nblocks_shell_blob: u8, num_shells: u8, num_groups: u32, @@ -65,7 +66,7 @@ pub const Header = packed struct { // when ptr size is larger than on the host that constructed it the DB, // getgr_bufsize/getpw_bufsize may return insufficient values, causing // OutOfMemory for getgr* and getpw* calls. - if (self.ptr_size < @sizeOf(?[*:0]const u8)) + if (self.ptr_size < ptr_size) return error.InvalidPointerSize; return self;