diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index c777c258127e4d72f38ff461f24c415d74b6e99b..44a716743d2a79ff675a4e39ccf630189acb1860 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -7191,9 +7191,9 @@ fn realPathPosix(fd: posix.fd_t, out_buffer: []u8) File.RealPathError!usize { return n; }, .linux, .serenity, .illumos => { - var procfs_buf: ["/proc/self/path/-2147483648\x00".len]u8 = undefined; + var proc_buf: [std.fmt.count("/proc/self/path/{d}\x00", .{std.math.minInt(posix.fd_t)})]u8 = undefined; const template = if (native_os == .illumos) "/proc/self/path/{d}" else "/proc/self/fd/{d}"; - const proc_path = std.mem.printSentinel(&procfs_buf, template, .{fd}, 0) catch unreachable; + const proc_path = std.mem.printSentinel(&proc_buf, template, .{fd}, 0) catch unreachable; const syscall: Syscall = try .start(); while (true) { const rc = posix.system.readlink(proc_path, out_buffer.ptr, out_buffer.len); @@ -7266,27 +7266,32 @@ fn fileHardLink( new_sub_path: []const u8, options: File.HardLinkOptions, ) File.HardLinkError!void { - _ = userdata; - if (native_os != .linux) return error.OperationUnsupported; + const t: *Threaded = @ptrCast(@alignCast(userdata)); + _ = t; var new_path_buffer: [posix.PATH_MAX]u8 = undefined; const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer); - const flags: u32 = if (options.follow_symlinks) - posix.AT.SYMLINK_FOLLOW | posix.AT.EMPTY_PATH - else - posix.AT.EMPTY_PATH; + switch (native_os) { + else => return error.OperationUnsupported, + .freebsd, .linux => { + const flags: u32 = if (options.follow_symlinks) + posix.AT.SYMLINK_FOLLOW | posix.AT.EMPTY_PATH + else + posix.AT.EMPTY_PATH; - return linkat(file.handle, "", new_dir.handle, new_sub_path_posix, flags) catch |err| switch (err) { - error.FileNotFound => { - if (options.follow_symlinks) return error.FileNotFound; - var proc_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined; - const proc_path = std.mem.printSentinel(&proc_buf, "/proc/self/fd/{d}", .{file.handle}, 0) catch - unreachable; - return linkat(posix.AT.FDCWD, proc_path, new_dir.handle, new_sub_path_posix, posix.AT.SYMLINK_FOLLOW); + return linkat(file.handle, "", new_dir.handle, new_sub_path_posix, flags) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; }, - else => |e| return e, - }; + .netbsd, .openbsd => {}, + } + + if (options.follow_symlinks) return error.FileNotFound; + var dev_buf: [std.fmt.count("/dev/fd/{d}\x00", .{std.math.minInt(posix.fd_t)})]u8 = undefined; + const dev_path = std.mem.printSentinel(&dev_buf, "/dev/fd/{d}", .{file.handle}, 0) catch unreachable; + return linkat(posix.AT.FDCWD, dev_path, new_dir.handle, new_sub_path_posix, posix.AT.SYMLINK_FOLLOW); } fn linkat( @@ -8732,12 +8737,12 @@ fn fchmodatFallback( ) Dir.SetFilePermissionsError!void { comptime assert(native_os == .linux); - // Fallback to changing permissions using procfs: + // Fallback to changing permissions using devfs: // // 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`. + // 3. Generate the devfs reference to the fd via `/dev/fd/{fd}`. + // 4. Pass the devfs path to `chmod` with the `mode`. const path_fd: posix.fd_t = fd: { const syscall: Syscall = try .start(); while (true) { @@ -8808,11 +8813,11 @@ fn fchmodatFallback( if ((path_mode & posix.S.IFMT) == posix.S.IFLNK) return error.OperationUnsupported; - var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined; - const proc_path = std.mem.printSentinel(&procfs_buf, "/proc/self/fd/{d}", .{path_fd}, 0) catch unreachable; + var dev_buf: [std.fmt.count("/dev/fd/{d}\x00", .{std.math.minInt(posix.fd_t)})]u8 = undefined; + const dev_path = std.mem.printSentinel(&dev_buf, "/dev/fd/{d}", .{path_fd}, 0) catch unreachable; const syscall: Syscall = try .start(); while (true) { - switch (posix.errno(posix.system.chmod(proc_path, mode))) { + switch (posix.errno(posix.system.chmod(dev_path, mode))) { .SUCCESS => return syscall.finish(), .INTR => { try syscall.checkCancel(); @@ -8821,7 +8826,7 @@ fn fchmodatFallback( else => |e| { syscall.finish(); switch (e) { - .NOENT => return error.OperationUnsupported, // procfs not mounted. + .NOENT => return error.OperationUnsupported, // devfs not mounted. .BADF => |err| return errnoBug(err), .FAULT => |err| return errnoBug(err), .INVAL => |err| return errnoBug(err), @@ -17446,9 +17451,6 @@ pub fn posixExecveat( ) process.ReplaceError { try Thread.checkCancel(); switch (err: { - if (path) |p| if (dir == posix.AT.FDCWD or Dir.path.isAbsoluteZ(p)) { - break :err posix.errno(posix.system.execve(p, argv, env_block.slice.ptr)); - }; if (comptime native_os == .linux and (!builtin.link_libc or (builtin.target.isGnuLibC() and builtin.target.os.version_range.linux.glibc.order( .{ .major = 2, .minor = 34, .patch = 0 }, @@ -17462,34 +17464,57 @@ pub fn posixExecveat( .NOSYS => {}, } } - var path_buffer: [posix.PATH_MAX]u8 = undefined; - var path_len = realPathPosix(dir, &path_buffer) catch |err| switch (err) { - else => |e| return e, - error.InputOutput, - error.FileTooBig, - error.NoSpaceLeft, - error.PathAlreadyExists, - error.SymLinkLoop, - error.NetworkNotFound, - error.PipeBusy, - error.AntivirusInterference, - => return error.FileNotFound, - error.DeviceBusy, - error.NoDevice, - error.UnrecognizedVolume, - => return error.FileSystem, + if (path) |p| if (dir == posix.AT.FDCWD or Dir.path.isAbsoluteZ(p)) { + break :err posix.errno(posix.system.execve(p, argv, env_block.slice.ptr)); }; - if (path) |p| { - const path_slice = std.mem.span(p); - path_buffer[path_len] = Dir.path.sep; - path_len += 1; - @memcpy(path_buffer[path_len..][0..path_slice.len], path_slice); - path_len += path_slice.len; + const exe: posix.fd_t = if (path) |p| exe: { + var flags: posix.O = .{}; + if (@hasField(posix.O, "CLOEXEC")) flags.CLOEXEC = true; + if (@hasField(posix.O, "LARGEFILE")) flags.LARGEFILE = true; + if (@hasField(posix.O, "NOCTTY")) flags.NOCTTY = true; + if (@hasField(posix.O, "PATH")) flags.PATH = true; + if (@hasField(posix.O, "EXEC")) flags.EXEC = true; + const rc = openat_sym(dir, p, flags, @as(posix.mode_t, 0)); + while (true) switch (posix.errno(rc)) { + .SUCCESS => break :exe @intCast(rc), + .INTR, .AGAIN => {}, + .FAULT => |err| return errnoBug(err), + .INVAL => return error.BadPathName, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .ACCES => return error.AccessDenied, + .FBIG => return error.InvalidExe, + .OVERFLOW => return error.InvalidExe, + .ISDIR => return error.IsDir, + .LOOP => return error.InvalidExe, + .MFILE => return error.ProcessFdQuotaExceeded, + .NAMETOOLONG => return error.NameTooLong, + .NFILE => return error.SystemFdQuotaExceeded, + .NODEV => return error.FileSystem, + .NOENT => return error.FileNotFound, + .SRCH => return error.FileNotFound, // Linux when opening procfs files. + .NOMEM => return error.SystemResources, + .NOTDIR => return error.NotDir, + .PERM => return error.PermissionDenied, + .BUSY => return error.FileSystem, + .TXTBSY => return error.FileBusy, + .NXIO => return error.FileSystem, + .ILSEQ => return error.BadPathName, + else => |err| return posix.unexpectedErrno(err), + }; + } else dir; + defer closeFd(exe); + if (builtin.link_libc and @TypeOf(posix.system.fexecve) != void) { + break :err posix.errno(posix.system.fexecve(exe, argv, env_block.slice.ptr)); + } + switch (native_os) { + else => return error.OperationUnsupported, + .linux, .serenity, .illumos => { + var proc_buf: [std.fmt.count("/proc/self/path/{d}\x00", .{std.math.minInt(posix.fd_t)})]u8 = undefined; + const template = if (native_os == .illumos) "/proc/self/path/{d}" else "/proc/self/fd/{d}"; + const proc_path = std.mem.printSentinel(&proc_buf, template, .{exe}, 0) catch unreachable; + break :err posix.errno(posix.system.execve(proc_path, argv, env_block.slice.ptr)); + }, } - path_buffer[path_len] = 0; - break :err posix.errno( - posix.system.execve(path_buffer[0..path_len :0], argv, env_block.slice.ptr), - ); }) { .FAULT => |err| return errnoBug(err), // Bad pointer parameter. .@"2BIG" => return error.SystemResources, diff --git a/lib/std/c.zig b/lib/std/c.zig index 6969f4df8a4b1f3be8daf11923312c2f9a7c8a52..093cab3db16ad24142617d63aa02ad06c1a3e743 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -8387,6 +8387,8 @@ pub const AT = switch (native_os) { /// to determine the target of relative file paths in the openat() and /// similar syscalls. pub const FDCWD = -100; + /// Fail rename if target exists + pub const RENAME_NOREPLACE = 0x0001; /// Check access using effective user and group ID pub const EACCESS = 0x0100; /// Do not follow symbolic links @@ -8397,6 +8399,10 @@ pub const AT = switch (native_os) { pub const REMOVEDIR = 0x0800; /// Fail if not under dirfd pub const BENEATH = 0x1000; + /// Do not allow name resolution to walk out of dirfd + pub const RESOLVE_BENEATH = 0x2000; + /// Operate on dirfd if path is empty + pub const EMPTY_PATH = 0x4000; }, .netbsd => struct { /// Magic value that specify the use of the current working directory @@ -8552,7 +8558,11 @@ pub const O = switch (native_os) { DIRECTORY: bool = false, CLOEXEC: bool = false, SEARCH: bool = false, - _: u8 = 0, + NOSIGPIPE: bool = false, + REGULAR: bool = false, + EXEC: bool = false, + CLOFORK: bool = false, + _: u4 = 0, }, .openbsd => packed struct(u32) { ACCMODE: std.posix.ACCMODE = .RDONLY, @@ -10937,9 +10947,13 @@ pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: mode_t) c_int; pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int; pub extern "c" fn symlinkat(oldpath: [*:0]const u8, newdirfd: fd_t, newpath: [*:0]const u8) c_int; pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int; -pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, new: [*:0]const u8) c_int; +pub extern "c" fn renameat(olddirfd: fd_t, oldpath: [*:0]const u8, newdirfd: fd_t, newpath: [*:0]const u8) c_int; pub extern "c" fn chdir(path: [*:0]const u8) c_int; pub extern "c" fn fchdir(fd: fd_t) c_int; +pub const fexecve = switch (native_os) { + else => {}, + .freebsd, .linux, .netbsd => private.fexecve, +}; pub extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) c_int; pub extern "c" fn execveat(dirfd: fd_t, path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8, flags: EXECVEAT) c_int; pub extern "c" fn dup(fd: fd_t) c_int; @@ -11791,34 +11805,35 @@ pub const setkeymap = serenity.setkeymap; /// External definitions shared by two or more operating systems. const private = struct { - pub extern "c" fn strdup(s: [*:0]const c_char) ?[*:0]c_char; - pub extern "c" fn _strdup(s: [*:0]const c_char) ?[*:0]c_char; - pub extern "c" fn wcsdup(s: [*:0]const wchar_t) ?[*:0]wchar_t; - pub extern "c" fn _wcsdup(s: [*:0]const wchar_t) ?[*:0]wchar_t; + extern "c" fn strdup(s: [*:0]const c_char) ?[*:0]c_char; + extern "c" fn _strdup(s: [*:0]const c_char) ?[*:0]c_char; + extern "c" fn wcsdup(s: [*:0]const wchar_t) ?[*:0]wchar_t; + extern "c" fn _wcsdup(s: [*:0]const wchar_t) ?[*:0]wchar_t; - pub extern "c" fn swab(noalias from: *const anyopaque, noalias to: *anyopaque, n: isize) void; - pub extern "c" fn _swab(noalias from: *const anyopaque, noalias to: *anyopaque, n: isize) void; + extern "c" fn swab(noalias from: *const anyopaque, noalias to: *anyopaque, n: isize) void; + extern "c" fn _swab(noalias from: *const anyopaque, noalias to: *anyopaque, n: isize) void; - extern "c" fn close(fd: fd_t) c_int; extern "c" fn clock_getres(clk_id: clockid_t, tp: *timespec) c_int; extern "c" fn clock_gettime(clk_id: clockid_t, tp: *timespec) c_int; + extern "c" fn clock_nanosleep(clockid: clockid_t, flags: TIMER, t: *const timespec, remain: ?*timespec) c_int; + extern "c" fn close(fd: fd_t) c_int; extern "c" fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_out: ?*i64, len: usize, flags: c_uint) isize; + extern "c" fn fexecve(fd: fd_t, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) c_int; extern "c" fn flock(fd: fd_t, operation: c_int) c_int; extern "c" fn fork() c_int; extern "c" fn fstat(fd: fd_t, buf: *Stat) c_int; extern "c" fn fstatat(dirfd: fd_t, path: [*:0]const u8, buf: *Stat, flag: u32) c_int; extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize; extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) switch (native_os) { + else => c_int, .freebsd => isize, .illumos => usize, - else => c_int, }; extern "c" fn getrusage(who: c_int, usage: *rusage) c_int; extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; extern "c" fn ioctl(fd: fd_t, request: c_int, ...) c_int; extern "c" fn msync(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int; extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; - extern "c" fn clock_nanosleep(clockid: clockid_t, flags: TIMER, t: *const timespec, remain: ?*timespec) c_int; extern "c" fn pipe2(fds: *[2]fd_t, flags: O) c_int; extern "c" fn readdir(dir: *DIR) ?*dirent; extern "c" fn realpath(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8; diff --git a/test/src/Runner.zig b/test/src/Runner.zig index 0598fbaecd48053c1f8a166bc0eaefb516d4a435..66e204b4c5d1799ccbbd3ad956cfff1d72b635f6 100644 --- a/test/src/Runner.zig +++ b/test/src/Runner.zig @@ -822,7 +822,7 @@ fn handleCommand( .pic = pic, }; }) catch |err| switch (err) { - error.Canceled, error.OutOfMemory, error.AlreadyReported => |e| return e, + error.Canceled, error.OutOfMemory, error.AlreadyReported, error.SkipTest => |e| return e, error.ReadFailed => return error.ContentsReadFailed, }, .write => { @@ -1006,7 +1006,7 @@ fn spawnCompiler( output_mode: std.lang.OutputMode, args_r: *std.Io.Reader, target: Compiler.Target, -) (std.Io.Reader.ShortError || FailError)!void { +) (std.Io.Reader.ShortError || TestError)!void { const gpa = runner.gpa; const arena = runner.arena.allocator(); if (compiler.*) |*comp| { @@ -1118,6 +1118,7 @@ fn spawnCompiler( .stderr = .pipe, }) catch |err| switch (err) { error.Canceled, error.OutOfMemory => |e| return e, + error.OperationUnsupported => return error.SkipTest, else => |e| return runner.fail("unable to spawn compiler: {t}", .{e}), }; compiler.* = .{ @@ -1222,7 +1223,7 @@ const Compiler = struct { expected: []const u8, src_dir: std.Io.Dir, digest: *const std.Build.Cache.BinDigest, - ) FailError!void { + ) TestError!void { const runner = comp.runner; const gpa = runner.gpa; const arena = runner.arena.allocator(); @@ -1560,7 +1561,7 @@ const Compiler = struct { } } - fn compileC(parent_comp: *Compiler, src_dir: std.Io.Dir, c_path: []const u8) FailError![]const u8 { + fn compileC(parent_comp: *Compiler, src_dir: std.Io.Dir, c_path: []const u8) TestError![]const u8 { const runner = parent_comp.runner; const arena = runner.arena.allocator(); var compiler: ?Compiler = null; @@ -1577,7 +1578,7 @@ const Compiler = struct { .linker = .lld, .pic = parent_comp.target.pic, }) catch |err| switch (err) { - error.Canceled, error.OutOfMemory, error.AlreadyReported => |e| return e, + error.Canceled, error.OutOfMemory, error.AlreadyReported, error.SkipTest => |e| return e, error.ReadFailed => unreachable, // .fixed }; const comp = &compiler.?;