Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/cli/dump.zig
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ fn formatTimestamp(buf: *[23]u8, epoch_secs: u64) []const u8 {
const ds = es.getDaySeconds();
const result = std.fmt.bufPrint(buf, "{d:0>4}-{d:0>2}-{d:0>2}T{d:0>2}:{d:0>2}:{d:0>2}.000", .{
yd.year,
@as(u32, @intFromEnum(md.month)) + 1,
@as(u32, @intFromEnum(md.month)),
@as(u32, md.day_index) + 1,
ds.getHoursIntoDay(),
ds.getMinutesIntoHour(),
Expand Down Expand Up @@ -918,6 +918,13 @@ test "dump: parsePeriod valid durations" {
try testing.expectEqual(@as(u64, 86400 * 30), parsePeriod("30d").?);
}

test "dump: formatTimestamp month is 1-based (June prints 06 not 07)" {
var buf: [23]u8 = undefined;
// 1717200000 == 2024-06-01T00:00:00 UTC.
const out = formatTimestamp(&buf, 1717200000);
try testing.expectEqualStrings("2024-06-01T00:00:00.000", out);
}

test "dump: parsePeriod rejects invalid" {
try testing.expectEqual(@as(?u64, null), parsePeriod("0m"));
try testing.expectEqual(@as(?u64, null), parsePeriod("abc"));
Expand Down
4 changes: 4 additions & 0 deletions src/cli/install/plan.zig
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,12 @@ pub fn dirIsNonEmpty(path: []const u8) bool {
}

// Atomic copy (temp + rename) preserving source mode. No partial dst on failure.
// copyFileAbsolute creates dst via open(O_CREAT) subject to umask, so the source
// permission bits are re-applied explicitly to keep the result deterministic.
pub fn copyFile(src: []const u8, dst: []const u8) !void {
const src_stat = try std.fs.cwd().statFile(src);
try std.fs.copyFileAbsolute(src, dst, .{});
try std.posix.fchmodat(std.posix.AT.FDCWD, dst, @intCast(src_stat.mode & 0o777), 0);
}

// Write to {dst}.new then rename(2) over dst — avoids ETXTBSY when dst is currently executing.
Expand Down
31 changes: 31 additions & 0 deletions src/cli/install/tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const parseYesNoDefaultYes = plan_mod.parseYesNoDefaultYes;
const planSystemctlUser = plan_mod.planSystemctlUser;
const installWillStartUserService = plan_mod.installWillStartUserService;
const atomicInstallBinary = plan_mod.atomicInstallBinary;
const copyFile = plan_mod.copyFile;
const userInGroup = plan_mod.userInGroup;
const ensureDirAll = plan_mod.ensureDirAll;
const SystemctlUserMode = plan_mod.SystemctlUserMode;
Expand Down Expand Up @@ -2716,6 +2717,36 @@ test "install: atomicInstallBinary replaces destination atomically" {
try testing.expectEqual(@as(u32, 0o755), stat.mode & 0o777);
}

test "install: copyFile preserves source mode regardless of umask" {
const testing = std.testing;
const allocator = testing.allocator;
var tmp = testing.tmpDir(.{});
defer tmp.cleanup();
const dir = try tmp.dir.realpathAlloc(allocator, ".");
defer allocator.free(dir);

const src_path = try std.fmt.allocPrint(allocator, "{s}/src.toml", .{dir});
defer allocator.free(src_path);
const dst_path = try std.fmt.allocPrint(allocator, "{s}/dst.toml", .{dir});
defer allocator.free(dst_path);

{
var f = try std.fs.createFileAbsolute(src_path, .{ .mode = 0o644 });
defer f.close();
try f.writeAll("name = \"x\"\n");
}
try std.posix.fchmodat(std.posix.AT.FDCWD, src_path, 0o644, 0);

// Restrictive umask would mask 0o644 down to 0o600 without an explicit chmod.
const prev_umask = std.os.linux.syscall1(.umask, 0o077);
defer _ = std.os.linux.syscall1(.umask, prev_umask);

try copyFile(src_path, dst_path);

const stat = try std.fs.cwd().statFile(dst_path);
try testing.expectEqual(@as(u32, 0o644), stat.mode & 0o777);
}

test "install: atomicInstallBinary rename succeeds while dst has open readers" {
// Verifies rename(2) over an open read fd succeeds — regression lock for the atomic-rename path.
const testing = std.testing;
Expand Down
2 changes: 1 addition & 1 deletion src/event_loop.zig
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ fn armRumbleStopFd(fd: posix.fd_t, deadline_ns: ?i128) void {
};
const rc = linux.timerfd_settime(fd, .{ .ABSTIME = true }, &spec, null);
if (rc != 0) {
const errno = std.posix.errno(rc);
const errno = linux.E.init(rc);
rumble_log.debug("TIMERFD: timerfd_settime FAILED errno={s} deadline={d}", .{
@tagName(errno), target,
});
Expand Down
1 change: 1 addition & 0 deletions src/io/hidraw.zig
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ pub const HidrawDevice = struct {
if (dev_vid != vid or dev_pid != pid) continue;

const owned = try allocator.dupe(u8, path);
errdefer allocator.free(owned);
try paths.append(allocator, owned);
}

Expand Down
2 changes: 1 addition & 1 deletion src/log.zig
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ fn wallClockTimestamp(buf: *[32]u8) []const u8 {

const result = std.fmt.bufPrint(buf, "{d:0>4}-{d:0>2}-{d:0>2}T{d:0>2}:{d:0>2}:{d:0>2}.{d:0>3}", .{
yd.year,
@as(u32, @intFromEnum(md.month)) + 1,
@as(u32, @intFromEnum(md.month)),
@as(u32, md.day_index) + 1,
ds.getHoursIntoDay(),
ds.getMinutesIntoHour(),
Expand Down
8 changes: 7 additions & 1 deletion src/supervisor.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1801,13 +1801,16 @@ pub const Supervisor = struct {
return;
};
parsed_ptr.* = parsed;
const new_mapper = Mapper.init(&parsed_ptr.value, m.instance.loop.macro_timer_fd, self.allocator) catch {
var new_mapper = Mapper.init(&parsed_ptr.value, m.instance.loop.macro_timer_fd, self.allocator) catch {
parsed_ptr.deinit();
self.allocator.destroy(parsed_ptr);
cs.sendResponse(fd, "ERR switch-failed\n");
return;
};
const stem_copy = self.allocator.dupe(u8, resolved_stem) catch {
new_mapper.deinit();
parsed_ptr.deinit();
self.allocator.destroy(parsed_ptr);
cs.sendResponse(fd, "ERR oom\n");
return;
};
Expand All @@ -1818,6 +1821,9 @@ pub const Supervisor = struct {
.path_stem = stem_copy,
}) catch {
self.allocator.free(stem_copy);
new_mapper.deinit();
parsed_ptr.deinit();
self.allocator.destroy(parsed_ptr);
cs.sendResponse(fd, "ERR oom\n");
return;
};
Expand Down
Loading