diff --git a/src/buffer/Buffer.zig b/src/buffer/Buffer.zig index 9da402e2..110e9524 100644 --- a/src/buffer/Buffer.zig +++ b/src/buffer/Buffer.zig @@ -1610,37 +1610,14 @@ pub fn store_to_existing_file_const(self: *const Self, file_path_: []const u8) S file_path = link; } - var write_buffer: [4096]u8 = undefined; - - if (builtin.os.tag == .windows) { - // windows uses ACLs for ownership so we preserve mode only - var atomic = blk: { - const stat = cwd().statFile(file_path) catch - break :blk try cwd().atomicFile(file_path, .{ .write_buffer = &write_buffer }); - break :blk try cwd().atomicFile(file_path, .{ .mode = stat.mode, .write_buffer = &write_buffer }); - }; - defer atomic.deinit(); - try self.store_to_file_const(&atomic.file_writer.interface); - return atomic.finish(); - } - - // use fstat to get uid/gid, which std.fs.File.Stat omits. - const orig_stat: ?std.posix.Stat = blk: { - const f = cwd().openFile(file_path, .{}) catch break :blk null; - defer f.close(); - break :blk std.posix.fstat(f.handle) catch null; + var atomic = blk: { + var write_buffer: [4096]u8 = undefined; + const stat = cwd().statFile(file_path) catch + break :blk try cwd().atomicFile(file_path, .{ .write_buffer = &write_buffer }); + break :blk try cwd().atomicFile(file_path, .{ .mode = stat.mode, .write_buffer = &write_buffer }); }; - const mode: std.fs.File.Mode = if (orig_stat) |s| s.mode else std.fs.File.default_mode; - var atomic = try cwd().atomicFile(file_path, .{ .mode = mode, .write_buffer = &write_buffer }); defer atomic.deinit(); try self.store_to_file_const(&atomic.file_writer.interface); - // fchmod bypasses the process umask preserving the exact original mode - // fchown restores original owner/group - // EPERM is silently ignored when we lack sufficient privileges - if (orig_stat) |s| { - atomic.file_writer.file.chmod(s.mode) catch {}; - std.posix.fchown(atomic.file_writer.file.handle, s.uid, s.gid) catch {}; - } try atomic.finish(); } diff --git a/test/tests_buffer.zig b/test/tests_buffer.zig index 28ed4918..18b0c58f 100644 --- a/test/tests_buffer.zig +++ b/test/tests_buffer.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const builtin = @import("builtin"); const Buffer = @import("Buffer"); const ArrayList = std.ArrayList; @@ -100,39 +99,6 @@ test "buffer.store_to_file_and_clean" { try std.testing.expectEqualStrings(input, output); } -test "buffer.store_to_file_and_clean preserves file mode" { - if (comptime builtin.os.tag == .windows) return error.SkipZigTest; - - const tmp_path = "test/tmp_mode_test.txt"; - { - const f = try std.fs.cwd().createFile(tmp_path, .{}); - defer f.close(); - try f.writeAll("hello\n"); - try f.chmod(0o644); - } - defer std.fs.cwd().deleteFile(tmp_path) catch {}; - - // Set a umask that would strip group/other read bits (0o644 -> 0o600 without the fix) - // to verify that fchmod bypasses the process umask on save. - const prev_umask = if (comptime builtin.os.tag == .linux) - std.os.linux.syscall1(.umask, 0o077) - else - @as(usize, 0); - defer if (comptime builtin.os.tag == .linux) { - _ = std.os.linux.syscall1(.umask, prev_umask); - }; - - const buffer = try Buffer.create(a); - defer buffer.deinit(); - try buffer.load_from_file_and_update(tmp_path); - try buffer.store_to_file_and_clean(tmp_path); - - const f = try std.fs.cwd().openFile(tmp_path, .{}); - defer f.close(); - const stat = try std.posix.fstat(f.handle); - try std.testing.expectEqual(@as(std.posix.mode_t, 0o644), stat.mode & 0o777); -} - fn get_line(buf: *const Buffer, line: usize) ![]const u8 { var result: std.Io.Writer.Allocating = .init(a); try buf.root.get_line(line, &result.writer, metrics());