tests for initgroups_dyn

This commit is contained in:
2022-07-09 13:00:45 +03:00
parent 5fa4a71ddf
commit f327fb24ba
4 changed files with 106 additions and 22 deletions

View File

@@ -3,9 +3,11 @@ const os = std.os;
const fmt = std.fmt;
const log = std.log;
const mem = std.mem;
const process = std.process;
const math = std.math;
const heap = std.heap;
const once = std.once;
const Mutex = std.Thread.Mutex;
const Allocator = mem.Allocator;
const DB = @import("DB.zig");
const File = @import("File.zig");
@@ -39,6 +41,9 @@ const State = struct {
getgrent_iterator_mu: Mutex = Mutex{},
getgrent_iterator: ?PackedGroup.Iterator = null,
// allocator for
initgroups_dyn_allocator: Allocator,
};
// global_state is initialized on first call to an nss function
@@ -98,6 +103,7 @@ fn init() void {
global_state = State{
.file = file,
.omit_members = omit_members,
.initgroups_dyn_allocator = heap.raw_c_allocator,
};
}
@@ -329,24 +335,56 @@ export fn _nss_turbo_initgroups_dyn(
_: u32,
start: *c_long,
size: *c_long,
groups: [*]u32,
groupsp: *[*]u32,
limit: c_long,
errnop: *c_int,
) c.enum_nss_status {
const db = getDBErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL;
const state = getStateErrno(errnop) orelse return c.NSS_STATUS_UNAVAIL;
const db = state.file.db;
const user = db.getUser(mem.sliceTo(user_name, 0)) orelse {
errnop.* = @enumToInt(os.E.NOENT);
return c.NSS_STATUS_NOTFOUND;
};
_ = user;
_ = start;
_ = size;
_ = groups;
_ = limit;
var gids = db.userGids(user.additional_gids_offset);
if (size.* < gids.remaining()) {
const oldsize = @intCast(usize, size.*);
const newsize = if (limit <= 0)
oldsize + gids.remaining()
else
math.min(@intCast(usize, limit), oldsize + gids.remaining());
return c.NSS_STATUS_SUCCESS;
var buf = groupsp.*[0..oldsize];
const new_groups = state.initgroups_dyn_allocator.realloc(buf, newsize);
if (new_groups) |newgroups| {
groupsp.* = newgroups.ptr;
size.* = @intCast(c_long, newsize);
} else |err| switch (err) {
error.OutOfMemory => {
errnop.* = @enumToInt(os.E.NOMEM);
return c.NSS_STATUS_TRYAGAIN;
},
}
}
// I was not able to understand the expected behavior of limit. In glibc
// compat-initgroups.c limit is used to malloc the buffer. Something like
// this is all over the place:
// if (limit > 0 && *size == limit) /* We reached the maximum. */
// return;
// Our implementation will thus limit the number of *added* entries.
var added: usize = 0;
while (gids.nextMust()) |gid| {
if (limit > 0 and added == limit) break;
added += 1;
groupsp.*[@intCast(usize, start.*)] = @intCast(u32, gid);
start.* += 1;
}
return if (added > 0) c.NSS_STATUS_SUCCESS else c.NSS_STATUS_NOTFOUND;
}
fn getState() ?State {
@@ -459,6 +497,39 @@ test "getgrgid_r and getgrnam_r" {
try testing.expectEqual(@enumToInt(os.E.NOENT), @intCast(u16, errno));
}
test "initgroups_dyn" {
var tf = try File.TestDB.init(testing.allocator);
defer tf.deinit();
const turbonss_db_path_old = turbonss_db_path;
turbonss_db_path = tf.path;
defer {
turbonss_db_path = turbonss_db_path_old;
}
const allocator = testing.allocator;
global_state.?.initgroups_dyn_allocator = allocator;
var size: c_long = 3; // size of the gids array is this
var groups = try allocator.alloc(u32, @intCast(usize, size)); // buffer too small
defer allocator.free(groups);
var errno: c_int = 42; // canary
groups[0] = 42; // canary
var start: c_long = 1; // canary is there
const status = _nss_turbo_initgroups_dyn(
"vidmantas",
0, // gid, ignored
&start,
&size,
&groups.ptr,
3, // limit: besides canary accept 2 groups
&errno,
);
try testing.expectEqual(c.NSS_STATUS_SUCCESS, status);
try testing.expectEqual(@as(c_int, 42), errno);
try testing.expectEqual(@as(u32, 42), groups[0]);
try testing.expectEqual(@as(u32, 128), groups[1]);
}
fn testVidmantasGroup(g: CGroup) !void {
try testing.expectEqual(@as(u32, 128), g.gid);
try testing.expectEqualStrings("vidmantas", mem.sliceTo(g.name, 0));