diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-12-11 21:00:14 -0800 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2025-12-23 22:15:09 -0800 |
| commit | 0e230993d51d0ecded40d5235ada4f2f64036b26 (patch) | |
| tree | 4caa6e7f003c791cd96a54b63226b78eaa649282 /lib/std | |
| parent | c4cefd68358470d464bf66bc41985cadd874db73 (diff) | |
| download | zig-0e230993d51d0ecded40d5235ada4f2f64036b26.tar.gz zig-0e230993d51d0ecded40d5235ada4f2f64036b26.zip | |
std.Io.Dir: add setFilePermissions and setFileOwner
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/Io.zig | 2 | ||||
| -rw-r--r-- | lib/std/Io/Dir.zig | 41 | ||||
| -rw-r--r-- | lib/std/Io/Threaded.zig | 179 | ||||
| -rw-r--r-- | lib/std/os/linux.zig | 4 | ||||
| -rw-r--r-- | lib/std/posix.zig | 184 | ||||
| -rw-r--r-- | lib/std/posix/test.zig | 2 | ||||
| -rw-r--r-- | lib/std/tar.zig | 6 | ||||
| -rw-r--r-- | lib/std/zig/parser_test.zig | 14 |
8 files changed, 232 insertions, 200 deletions
diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 9db215c747..b76cbebe3a 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -684,7 +684,9 @@ pub const VTable = struct { dirSymLink: *const fn (?*anyopaque, Dir, target_path: []const u8, sym_link_path: []const u8, Dir.SymLinkFlags) Dir.SymLinkError!void, dirReadLink: *const fn (?*anyopaque, Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize, dirSetOwner: *const fn (?*anyopaque, Dir, ?File.Uid, ?File.Gid) Dir.SetOwnerError!void, + dirSetFileOwner: *const fn (?*anyopaque, Dir, []const u8, ?File.Uid, ?File.Gid, Dir.SetFileOwnerOptions) Dir.SetFileOwnerError!void, dirSetPermissions: *const fn (?*anyopaque, Dir, Dir.Permissions) Dir.SetPermissionsError!void, + dirSetFilePermissions: *const fn (?*anyopaque, Dir, []const u8, File.Permissions, Dir.SetFilePermissionsOptions) Dir.SetFilePermissionsError!void, dirSetTimestamps: *const fn (?*anyopaque, Dir, []const u8, last_accessed: Timestamp, last_modified: Timestamp, Dir.SetTimestampsOptions) Dir.SetTimestampsError!void, dirSetTimestampsNow: *const fn (?*anyopaque, Dir, []const u8, Dir.SetTimestampsOptions) Dir.SetTimestampsError!void, diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig index 4e70a7be89..085667edca 100644 --- a/lib/std/Io/Dir.zig +++ b/lib/std/Io/Dir.zig @@ -1698,6 +1698,29 @@ pub fn setPermissions(dir: Dir, io: Io, new_permissions: File.Permissions) SetPe return io.vtable.dirSetPermissions(io.userdata, dir, new_permissions); } +pub const SetFilePermissionsError = PathNameError || SetPermissionsError || error{ + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + /// `SetFilePermissionsOptions.follow_symlinks` was set to false, which is + /// not allowed by the file system or operating system. + OperationNotSupported, +}; + +pub const SetFilePermissionsOptions = struct { + follow_symlinks: bool = true, +}; + +/// Also known as "chmodat". +pub fn setFilePermissions( + dir: Dir, + io: Io, + sub_path: []const u8, + new_permissions: File.Permissions, + options: SetFilePermissionsOptions, +) SetFilePermissionsError!void { + return io.vtable.dirSetFilePermissions(io.userdata, sub_path, dir, new_permissions, options); +} + pub const SetOwnerError = File.SetOwnerError; /// Also known as "chown". @@ -1711,6 +1734,24 @@ pub fn setOwner(dir: Dir, io: Io, owner: ?File.Uid, group: ?File.Gid) SetOwnerEr return io.vtable.dirSetOwner(io.userdata, dir, owner, group); } +pub const SetFileOwnerError = PathNameError || SetOwnerError; + +pub const SetFileOwnerOptions = struct { + follow_symlinks: bool = true, +}; + +/// Also known as "fchownat". +pub fn setFileOwner( + dir: Dir, + io: Io, + sub_path: []const u8, + owner: ?File.Uid, + group: ?File.Gid, + options: SetFileOwnerOptions, +) SetOwnerError!void { + return io.vtable.dirSetFileOwner(io.userdata, dir, sub_path, owner, group, options); +} + pub const SetTimestampsError = File.SetTimestampsError || PathNameError; pub const SetTimestampsOptions = struct { diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 61974f4717..2fb0f3ad1f 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -76,6 +76,7 @@ old_sig_pipe: if (have_sig_pipe) posix.Sigaction else void, use_sendfile: UseSendfile = .default, use_copy_file_range: UseCopyFileRange = .default, use_fcopyfile: UseFcopyfile = .default, +use_fchmodat2: UseFchmodat2 = .default, stderr_writer: File.Writer = .{ .io = undefined, @@ -124,6 +125,15 @@ pub const UseFcopyfile = if (have_fcopyfile) enum { pub const default: UseFcopyfile = .disabled; }; +pub const UseFchmodat2 = if (have_fchmodat2 and !have_fchmodat_flags) enum { + enabled, + disabled, + pub const default: UseFchmodat2 = .enabled; +} else enum { + disabled, + pub const default: UseFchmodat2 = .disabled; +}; + const Thread = struct { /// The value that needs to be passed to pthread_kill or tgkill in order to /// send a signal. @@ -712,7 +722,9 @@ pub fn io(t: *Threaded) Io { .dirSymLink = dirSymLink, .dirReadLink = dirReadLink, .dirSetOwner = dirSetOwner, + .dirSetFileOwner = dirSetFileOwner, .dirSetPermissions = dirSetPermissions, + .dirSetFilePermissions = dirSetFilePermissions, .dirSetTimestamps = dirSetTimestamps, .dirSetTimestampsNow = dirSetTimestampsNow, @@ -842,7 +854,9 @@ pub fn ioBasic(t: *Threaded) Io { .dirSymLink = dirSymLink, .dirReadLink = dirReadLink, .dirSetOwner = dirSetOwner, + .dirSetFileOwner = dirSetFileOwner, .dirSetPermissions = dirSetPermissions, + .dirSetFilePermissions = dirSetFilePermissions, .dirSetTimestamps = dirSetTimestamps, .dirSetTimestampsNow = dirSetTimestampsNow, @@ -921,6 +935,11 @@ const have_copy_file_range = switch (native_os) { else => false, }; const have_fcopyfile = is_darwin; +const have_fchmodat2 = native_os == .linux and + (builtin.os.isAtLeast(.linux, .{ .major = 6, .minor = 6, .patch = 0 }) orelse true) and + (builtin.abi.isAndroid() or !std.c.versionCheck(.{ .major = 2, .minor = 32, .patch = 0 })); +const have_fchmodat_flags = native_os != .linux or + (!builtin.abi.isAndroid() and std.c.versionCheck(.{ .major = 2, .minor = 32, .patch = 0 })); const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat; const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat; @@ -4862,11 +4881,11 @@ const dirSetPermissions = switch (native_os) { }; fn dirSetPermissionsWindows(userdata: ?*anyopaque, dir: Dir, permissions: Dir.Permissions) Dir.SetPermissionsError!void { - // TODO I think we can actually set permissions on a dir on windows? - _ = userdata; + const t: *Threaded = @ptrCast(@alignCast(userdata)); + _ = t; _ = dir; _ = permissions; - return error.Unexpected; + @panic("TODO"); } fn dirSetPermissionsPosix(userdata: ?*anyopaque, dir: Dir, permissions: Dir.Permissions) Dir.SetPermissionsError!void { @@ -4875,6 +4894,133 @@ fn dirSetPermissionsPosix(userdata: ?*anyopaque, dir: Dir, permissions: Dir.Perm return setPermissionsPosix(current_thread, dir.handle, permissions.toMode()); } +fn dirSetFilePermissions( + userdata: ?*anyopaque, + dir: Dir, + sub_path: []const u8, + permissions: Dir.Permissions, + options: Dir.SetFilePermissionsOptions, +) Dir.SetFilePermissionsError!void { + if (!Dir.Permissions.has_executable_bit) return error.Unexpected; + if (is_windows) @panic("TODO"); + const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); + + var path_buffer: [posix.PATH_MAX]u8 = undefined; + const sub_path_posix = try pathToPosix(sub_path, &path_buffer); + + const mode = permissions.toMode(); + const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0; + + return posixFchmodat(t, current_thread, dir.handle, sub_path_posix, mode, flags); +} + +fn posixFchmodat( + t: *Threaded, + current_thread: *Thread, + dir_fd: posix.fd_t, + path: [*:0]const u8, + mode: posix.mode_t, + flags: u32, +) Dir.SetFilePermissionsError!void { + // No special handling for linux is needed if we can use the libc fallback + // or `flags` is empty. Glibc only added the fallback in 2.32. + if (have_fchmodat_flags or flags == 0) { + try current_thread.beginSyscall(); + while (true) { + const rc = if (have_fchmodat_flags) + posix.system.fchmodat(dir_fd, path, mode, flags) + else + posix.system.fchmodat(dir_fd, path, mode); + switch (posix.errno(rc)) { + .SUCCESS => return current_thread.endSyscall(), + .CANCELED => return current_thread.endSyscallCanceled(), + .INTR => { + try current_thread.checkCancel(); + continue; + }, + else => |e| { + current_thread.endSyscall(); + switch (e) { + .BADF => |err| return errnoBug(err), + .FAULT => |err| return errnoBug(err), + .INVAL => |err| return errnoBug(err), + .ACCES => return error.AccessDenied, + .IO => return error.InputOutput, + .LOOP => return error.SymLinkLoop, + .MFILE => return error.ProcessFdQuotaExceeded, + .NAMETOOLONG => return error.NameTooLong, + .NFILE => return error.SystemFdQuotaExceeded, + .NOENT => return error.FileNotFound, + .NOTDIR => return error.FileNotFound, + .NOMEM => return error.SystemResources, + .OPNOTSUPP => return error.OperationNotSupported, + .PERM => return error.PermissionDenied, + .ROFS => return error.ReadOnlyFileSystem, + else => |err| return posix.unexpectedErrno(err), + } + }, + } + } + } + + if (@atomicLoad(UseFchmodat2, &t.use_fchmodat2, .monotonic) == .disabled) + return fchmodatFallback(current_thread, dir_fd, path, mode, flags); + + comptime assert(native_os == .linux); + + try current_thread.beginSyscall(); + while (true) { + switch (std.os.linux.errno(std.os.linux.fchmodat2(dir_fd, path, mode, flags))) { + .SUCCESS => return current_thread.endSyscall(), + .CANCELED => return current_thread.endSyscallCanceled(), + .INTR => { + try current_thread.checkCancel(); + continue; + }, + else => |e| { + current_thread.endSyscall(); + switch (e) { + .BADF => |err| return errnoBug(err), + .FAULT => |err| return errnoBug(err), + .INVAL => |err| return errnoBug(err), + .ACCES => return error.AccessDenied, + .IO => return error.InputOutput, + .LOOP => return error.SymLinkLoop, + .NOENT => return error.FileNotFound, + .NOMEM => return error.SystemResources, + .NOTDIR => return error.FileNotFound, + .OPNOTSUPP => return error.OperationNotSupported, + .PERM => return error.PermissionDenied, + .ROFS => return error.ReadOnlyFileSystem, + .NOSYS => { + @atomicStore(UseFchmodat2, &t.use_fchmodat2, .disabled, .monotonic); + return fchmodatFallback(current_thread, dir_fd, path, mode, flags); + }, + else => |err| return posix.unexpectedErrno(err), + } + }, + } + } +} + +fn fchmodatFallback( + current_thread: *Thread, + dir_fd: posix.fd_t, + path: [*:0]const u8, + mode: posix.mode_t, + flags: u32, +) Dir.SetFilePermissionsError!void { + _ = current_thread; + _ = dir_fd; + _ = path; + _ = mode; + _ = flags; + // I deleted the previous fallback implementation because it looked wrong to me. Please cross-reference + // fhmodat.c in musl libc before blindly restoring the implementation. + @panic("TODO"); +} + const dirSetOwner = switch (native_os) { .windows => dirSetOwnerUnsupported, else => dirSetOwnerPosix, @@ -4927,6 +5073,33 @@ fn setOwnerPosix(current_thread: *Thread, fd: posix.fd_t, uid: posix.uid_t, gid: } } +fn dirSetFileOwner( + userdata: ?*anyopaque, + dir: Dir, + sub_path: []const u8, + owner: ?File.Uid, + group: ?File.Gid, + options: Dir.SetFileOwnerOptions, +) Dir.SetFileOwnerError!void { + const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); + + if (is_windows) { + @panic("TODO"); + } + + var path_buffer: [posix.PATH_MAX]u8 = undefined; + const sub_path_posix = try pathToPosix(sub_path, &path_buffer); + + _ = current_thread; + _ = dir; + _ = sub_path_posix; + _ = owner; + _ = group; + _ = options; + @panic("TODO"); +} + const fileSync = switch (native_os) { .windows => fileSyncWindows, else => fileSyncPosix, diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index a12654ee44..c8e9180dbf 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1420,7 +1420,7 @@ pub fn chmod(path: [*:0]const u8, mode: mode_t) usize { if (@hasField(SYS, "chmod")) { return syscall2(.chmod, @intFromPtr(path), mode); } else { - return fchmodat(AT.FDCWD, path, mode, 0); + return fchmodat(AT.FDCWD, path, mode); } } @@ -1432,7 +1432,7 @@ pub fn fchown(fd: i32, owner: uid_t, group: gid_t) usize { } } -pub fn fchmodat(fd: i32, path: [*:0]const u8, mode: mode_t, _: u32) usize { +pub fn fchmodat(fd: i32, path: [*:0]const u8, mode: mode_t) usize { return syscall3(.fchmodat, @bitCast(@as(isize, fd)), @intFromPtr(path), mode); } diff --git a/lib/std/posix.zig b/lib/std/posix.zig index aa270e611d..da2b0e8786 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -310,190 +310,6 @@ pub fn close(fd: fd_t) void { } } -pub const FChmodAtError = std.Io.File.SetPermissionsError || error{ - /// A component of `path` exceeded `NAME_MAX`, or the entire path exceeded - /// `PATH_MAX`. - NameTooLong, - /// `path` resolves to a symbolic link, and `AT.SYMLINK_NOFOLLOW` was set - /// in `flags`. This error only occurs on Linux, where changing the mode of - /// a symbolic link has no meaning and can cause undefined behaviour on - /// certain filesystems. - /// - /// The procfs fallback was used but procfs was not mounted. - OperationNotSupported, - /// The procfs fallback was used but the process exceeded its open file - /// limit. - ProcessFdQuotaExceeded, - /// The procfs fallback was used but the system exceeded it open file limit. - SystemFdQuotaExceeded, -}; - -/// Changes the `mode` of `path` relative to the directory referred to by -/// `dirfd`. The process must have the correct privileges in order to do this -/// successfully, or must have the effective user ID matching the owner of the -/// file. -/// -/// On Linux the `fchmodat2` syscall will be used if available, otherwise a -/// workaround using procfs will be employed. Changing the mode of a symbolic -/// link with `AT.SYMLINK_NOFOLLOW` set will also return -/// `OperationNotSupported`, as: -/// -/// 1. Permissions on the link are ignored when resolving its target. -/// 2. This operation has been known to invoke undefined behaviour across -/// different filesystems[1]. -/// -/// [1]: https://sourceware.org/legacy-ml/libc-alpha/2020-02/msg00467.html. -pub inline fn fchmodat(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void { - if (!fs.has_executable_bit) @compileError("fchmodat unsupported by target OS"); - - // No special handling for linux is needed if we can use the libc fallback - // or `flags` is empty. Glibc only added the fallback in 2.32. - const skip_fchmodat_fallback = native_os != .linux or - (!builtin.abi.isAndroid() and std.c.versionCheck(.{ .major = 2, .minor = 32, .patch = 0 })) or - flags == 0; - - // This function is marked inline so that when flags is comptime-known, - // skip_fchmodat_fallback will be comptime-known true. - if (skip_fchmodat_fallback) - return fchmodat1(dirfd, path, mode, flags); - - return fchmodat2(dirfd, path, mode, flags); -} - -fn fchmodat1(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void { - const path_c = try toPosixPath(path); - while (true) { - const res = system.fchmodat(dirfd, &path_c, mode, flags); - switch (errno(res)) { - .SUCCESS => return, - .INTR => continue, - .BADF => unreachable, - .FAULT => unreachable, - .INVAL => unreachable, - .ACCES => return error.AccessDenied, - .IO => return error.InputOutput, - .LOOP => return error.SymLinkLoop, - .MFILE => return error.ProcessFdQuotaExceeded, - .NAMETOOLONG => return error.NameTooLong, - .NFILE => return error.SystemFdQuotaExceeded, - .NOENT => return error.FileNotFound, - .NOTDIR => return error.FileNotFound, - .NOMEM => return error.SystemResources, - .OPNOTSUPP => return error.OperationNotSupported, - .PERM => return error.PermissionDenied, - .ROFS => return error.ReadOnlyFileSystem, - else => |err| return unexpectedErrno(err), - } - } -} - -fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void { - const global = struct { - var has_fchmodat2: bool = true; - }; - const path_c = try toPosixPath(path); - const use_fchmodat2 = (builtin.os.isAtLeast(.linux, .{ .major = 6, .minor = 6, .patch = 0 }) orelse false) and - @atomicLoad(bool, &global.has_fchmodat2, .monotonic); - while (use_fchmodat2) { - // Later on this should be changed to `system.fchmodat2` - // when the musl/glibc add a wrapper. - const res = linux.fchmodat2(dirfd, &path_c, mode, flags); - switch (linux.errno(res)) { - .SUCCESS => return, - .INTR => continue, - .BADF => unreachable, - .FAULT => unreachable, - .INVAL => unreachable, - .ACCES => return error.AccessDenied, - .IO => return error.InputOutput, - .LOOP => return error.SymLinkLoop, - .NOENT => return error.FileNotFound, - .NOMEM => return error.SystemResources, - .NOTDIR => return error.FileNotFound, - .OPNOTSUPP => return error.OperationNotSupported, - .PERM => return error.PermissionDenied, - .ROFS => return error.ReadOnlyFileSystem, - - .NOSYS => { - @atomicStore(bool, &global.has_fchmodat2, false, .monotonic); - break; - }, - else => |err| return unexpectedErrno(err), - } - } - - // Fallback to changing permissions using procfs: - // - // 1. Open `path` as a `PATH` descriptor. - // 2. Stat the fd and check if it isn't a symbolic link. - // 3. Generate the procfs reference to the fd via `/proc/self/fd/{fd}`. - // 4. Pass the procfs path to `chmod` with the `mode`. - var pathfd: fd_t = undefined; - while (true) { - const rc = system.openat(dirfd, &path_c, .{ .PATH = true, .NOFOLLOW = true, .CLOEXEC = true }, @as(mode_t, 0)); - switch (errno(rc)) { - .SUCCESS => { - pathfd = @intCast(rc); - break; - }, - .INTR => continue, - .FAULT => unreachable, - .INVAL => unreachable, - .ACCES => return error.AccessDenied, - .PERM => return error.PermissionDenied, - .LOOP => return error.SymLinkLoop, - .MFILE => return error.ProcessFdQuotaExceeded, - .NAMETOOLONG => return error.NameTooLong, - .NFILE => return error.SystemFdQuotaExceeded, - .NOENT => return error.FileNotFound, - .NOMEM => return error.SystemResources, - else => |err| return unexpectedErrno(err), - } - } - defer close(pathfd); - - const path_mode = if (linux.wrapped.statx( - pathfd, - "", - AT.EMPTY_PATH, - .{ .TYPE = true }, - )) |stx| blk: { - assert(stx.mask.TYPE); - break :blk stx.mode; - } else |err| switch (err) { - error.NameTooLong => unreachable, - error.FileNotFound => unreachable, - else => |e| return e, - }; - // Even though we only wanted TYPE, the kernel can still fill in the additional bits. - if ((path_mode & S.IFMT) == S.IFLNK) - return error.OperationNotSupported; - - var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined; - const proc_path = std.fmt.bufPrintSentinel(procfs_buf[0..], "/proc/self/fd/{d}", .{pathfd}, 0) catch unreachable; - while (true) { - const res = system.chmod(proc_path, mode); - switch (errno(res)) { - // Getting NOENT here means that procfs isn't mounted. - .NOENT => return error.OperationNotSupported, - - .SUCCESS => return, - .INTR => continue, - .BADF => unreachable, - .FAULT => unreachable, - .INVAL => unreachable, - .ACCES => return error.AccessDenied, - .IO => return error.InputOutput, - .LOOP => return error.SymLinkLoop, - .NOMEM => return error.SystemResources, - .NOTDIR => return error.FileNotFound, - .PERM => return error.PermissionDenied, - .ROFS => return error.ReadOnlyFileSystem, - else => |err| return unexpectedErrno(err), - } - } -} - pub const RebootError = error{ PermissionDenied, } || UnexpectedError; diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index b5255ad2a1..1764d7ad35 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -895,7 +895,7 @@ fn expectMode(dir: posix.fd_t, file: []const u8, mode: posix.mode_t) !void { } test "fchmodat smoke test" { - if (!std.fs.has_executable_bit) return error.SkipZigTest; + if (!Io.File.Permissions.has_executable_bit) return error.SkipZigTest; var tmp = tmpDir(.{}); defer tmp.cleanup(); diff --git a/lib/std/tar.zig b/lib/std/tar.zig index 7bb20a9959..92dc8b88f9 100644 --- a/lib/std/tar.zig +++ b/lib/std/tar.zig @@ -1122,7 +1122,7 @@ fn normalizePath(bytes: []u8) []u8 { fn filePermissions(mode: u32, options: PipeOptions) Io.File.Permissions { const default_mode = 0o666; - if (!std.fs.has_executable_bit or options.mode_mode == .ignore) + if (!Io.File.Permissions.has_executable_bit or options.mode_mode == .ignore) return .fromMode(default_mode); const S = std.posix.S; @@ -1137,7 +1137,7 @@ fn filePermissions(mode: u32, options: PipeOptions) Io.File.Permissions { } test filePermissions { - if (!std.fs.has_executable_bit) return error.SkipZigTest; + if (!Io.File.Permissions.has_executable_bit) return error.SkipZigTest; try testing.expectEqual(0o666, filePermissions(0o744, PipeOptions{ .mode_mode = .ignore })); try testing.expectEqual(0o777, filePermissions(0o744, PipeOptions{})); try testing.expectEqual(0o666, filePermissions(0o644, PipeOptions{})); @@ -1145,7 +1145,7 @@ test filePermissions { } test "executable bit" { - if (!std.fs.has_executable_bit) return error.SkipZigTest; + if (!Io.File.Permissions.has_executable_bit) return error.SkipZigTest; const io = testing.io; const S = std.posix.S; diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 3ce2603947..6064d3b540 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -6334,7 +6334,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined; fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *bool) ![]u8 { var buffer: [64]u8 = undefined; - const stderr, _ = std.debug.lockStderrWriter(&buffer); + const stderr = std.debug.lockStderrWriter(&buffer); defer std.debug.unlockStderrWriter(); var tree = try std.zig.Ast.parse(allocator, source, .zig); @@ -6342,17 +6342,17 @@ fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: * for (tree.errors) |parse_error| { const loc = tree.tokenLocation(0, parse_error.token); - try stderr.print("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 }); - try tree.renderError(parse_error, stderr); - try stderr.print("\n{s}\n", .{source[loc.line_start..loc.line_end]}); + try stderr.printUnescaped("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 }); + try tree.renderError(parse_error, &stderr.interface); + try stderr.interface.print("\n{s}\n", .{source[loc.line_start..loc.line_end]}); { var i: usize = 0; while (i < loc.column) : (i += 1) { - try stderr.writeAll(" "); + try stderr.writeAllUnescaped(" "); } - try stderr.writeAll("^"); + try stderr.writeAllUnescaped("^"); } - try stderr.writeAll("\n"); + try stderr.writeAllUnescaped("\n"); } if (tree.errors.len != 0) { return error.ParseError; |
