diff --git a/src/ErrCtx.zig b/src/ErrCtx.zig index d28f0f8..83d378d 100644 --- a/src/ErrCtx.zig +++ b/src/ErrCtx.zig @@ -12,8 +12,10 @@ const ErrCtx = @This(); buf: BoundedArray(u8, capacity) = BoundedArray(u8, capacity).init(0) catch unreachable, overflow: bool = false, +dirty: bool = false, pub fn write(self: *ErrCtx, bytes: []const u8) error{}!usize { + self.dirty = true; const can_add = capacity - self.buf.len; if (can_add == 0) return bytes.len; @@ -32,6 +34,10 @@ pub fn writer(self: *ErrCtx) Writer { return Writer{ .context = self }; } +pub fn err(self: *ErrCtx, comptime format: []const u8, args: anytype) void { + self.writer().print(format, args) catch unreachable; +} + pub fn iterator(self: *const ErrCtx) mem.SplitIterator(u8) { const slice = self.buf.constSlice(); const last_byte = if (slice[slice.len - 1] == '0') slice.len - 1 else slice.len; diff --git a/src/User.zig b/src/User.zig index 36a59d2..db3e30b 100644 --- a/src/User.zig +++ b/src/User.zig @@ -46,34 +46,51 @@ pub fn clone( // fromLine accepts a line of /etc/passwd (with or without the EOL) and makes a // User. -fn fromLine(allocator: Allocator, err_ctx: anytype, line: []const u8) error{ InvalidRecord, OutOfMemory }!User { - _ = err_ctx; +fn fromLine(allocator: Allocator, err_ctx: *ErrCtx, line: []const u8) error{ InvalidRecord, OutOfMemory }!User { var it = mem.split(u8, line, ":"); - const name = it.next() orelse return error.InvalidRecord; - _ = it.next() orelse return error.InvalidRecord; // password - const uids = it.next() orelse return error.InvalidRecord; - const gids = it.next() orelse return error.InvalidRecord; - const gecos = it.next() orelse return error.InvalidRecord; - const home = it.next() orelse return error.InvalidRecord; - const shell = it.next() orelse return error.InvalidRecord; + const name = it.next(); + _ = it.next(); // password + const uids = it.next(); + const gids = it.next(); + const gecos = it.next(); + const home = it.next(); + const shell = it.next(); + + // all fields are set + if (shell == null) { + err_ctx.err("too few user fields in line: {s}", .{line}); + return error.InvalidRecord; + } + // the line must be exhaustive. - if (it.next() != null) return error.InvalidRecord; + if (it.next() != null) + return error.InvalidRecord; - const uid = fmt.parseInt(u32, uids, 10) catch return error.InvalidRecord; - const gid = fmt.parseInt(u32, gids, 10) catch return error.InvalidRecord; + const uid = fmt.parseInt(u32, uids.?, 10) catch { + err_ctx.err("bad uid: {s}", .{uids.?}); + return error.InvalidRecord; + }; - try validate.utf8(name); - try validate.utf8(gecos); - try validate.utf8(home); - try validate.utf8(shell); + const gid = fmt.parseInt(u32, gids.?, 10) catch { + err_ctx.err("bad uid: {s}", .{gids.?}); + return error.InvalidRecord; + }; + + validate.utf8(name.?) catch err_ctx.err("name is invalid utf8: {s}", .{name.?}); + validate.utf8(gecos.?) catch err_ctx.err("gecos is invalid utf8: {s}", .{gecos.?}); + validate.utf8(home.?) catch err_ctx.err("home is invalid utf8: {s}", .{home.?}); + validate.utf8(shell.?) catch err_ctx.err("shell is invalid utf8: {s}", .{shell.?}); + + if (err_ctx.dirty) + return error.InvalidRecord; const user = User{ .uid = uid, .gid = gid, - .name = name, - .gecos = gecos, - .home = home, - .shell = shell, + .name = name.?, + .gecos = gecos.?, + .home = home.?, + .shell = shell.?, }; return try user.clone(allocator); } @@ -124,7 +141,7 @@ pub fn deinit(self: *User, allocator: Allocator) void { self.* = undefined; } -pub fn fromReader(allocator: Allocator, err_ctx: anytype, reader: anytype) ![]User { +pub fn fromReader(allocator: Allocator, err_ctx: *ErrCtx, reader: anytype) ![]User { var users = ArrayList(User).init(allocator); errdefer { for (users.items) |*user| user.deinit(allocator); diff --git a/src/unix2db/main.zig b/src/unix2db/main.zig index 02a0a58..4409e6d 100644 --- a/src/unix2db/main.zig +++ b/src/unix2db/main.zig @@ -73,12 +73,16 @@ fn execute( defer groupFile.close(); var err_ctx = ErrCtx{}; - var users = try User.fromReader( allocator, - err_ctx.writer(), + &err_ctx, passwdFile.reader(), ); + errdefer { + var it = err_ctx.iterator(); + while (it.next()) |err| + std.debug.print("ERROR: {s}\n", .{err}); + } defer for (users) |*user| user.deinit(allocator); defer allocator.free(users);