authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-03 20:10:13-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-04 00:27:09-08:00
log854c076ff7560de9b3f05ee15e1e30a90d738839
treebaa345e1a493f263dd1195cdd33d286163a5e776
parentfa315b1060ac2550e3479775cd22871b3df164ad

std.Io.Threaded: improve posix spawning

* avoid unreachable when the OS does something unexpected * make waiting for the fork/exec error report cancelable

4 files changed, 129 insertions(+), 208 deletions(-)

lib/std/Io/Threaded.zig+126-62
...@@ -1456,8 +1456,8 @@ pub fn io(t: *Threaded) Io {...@@ -1456,8 +1456,8 @@ pub fn io(t: *Threaded) Io {
1456 .processReplacePath = processReplacePath,1456 .processReplacePath = processReplacePath,
1457 .processSpawn = processSpawn,1457 .processSpawn = processSpawn,
1458 .processSpawnPath = processSpawnPath,1458 .processSpawnPath = processSpawnPath,
1459 .childWait = childWait, // TODO audit for cancelation and unreachable1459 .childWait = childWait,
1460 .childKill = childKill, // TODO audit for cancelation and unreachable1460 .childKill = childKill,
14611461
1462 .progressParentFile = progressParentFile,1462 .progressParentFile = progressParentFile,
14631463
...@@ -11853,38 +11853,7 @@ fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirEr...@@ -11853,38 +11853,7 @@ fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirEr
11853 };11853 };
11854 }11854 }
1185511855
11856 if (dir.handle == posix.AT.FDCWD) return;11856 return fchdir(dir.handle);
11857
11858 const syscall: Syscall = try .start();
11859 while (true) {
11860 switch (posix.errno(posix.system.fchdir(dir.handle))) {
11861 .SUCCESS => return syscall.finish(),
11862 .INTR => {
11863 try syscall.checkCancel();
11864 continue;
11865 },
11866 .ACCES => {
11867 syscall.finish();
11868 return error.AccessDenied;
11869 },
11870 .BADF => |err| {
11871 syscall.finish();
11872 return errnoBug(err);
11873 },
11874 .NOTDIR => {
11875 syscall.finish();
11876 return error.NotDir;
11877 },
11878 .IO => {
11879 syscall.finish();
11880 return error.FileSystem;
11881 },
11882 else => |err| {
11883 syscall.finish();
11884 return posix.unexpectedErrno(err);
11885 },
11886 }
11887 }
11888}11857}
1188911858
11890pub const PosixAddress = extern union {11859pub const PosixAddress = extern union {
...@@ -12960,57 +12929,64 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp...@@ -12960,57 +12929,64 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp
12960 };12929 };
1296112930
12962 if (pid_result == 0) {12931 if (pid_result == 0) {
12963 // We are the child.12932 defer comptime unreachable; // We are the child.
12964 if (Thread.current) |current_thread| current_thread.cancel_protection = .blocked;12933 if (Thread.current) |current_thread| current_thread.cancel_protection = .blocked;
12934 const ep1 = err_pipe[1];
1296512935
12966 setUpChildIo(options.stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkBail(err_pipe[1], err);12936 setUpChildIo(options.stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkBail(ep1, err);
12967 setUpChildIo(options.stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkBail(err_pipe[1], err);12937 setUpChildIo(options.stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkBail(ep1, err);
12968 setUpChildIo(options.stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkBail(err_pipe[1], err);12938 setUpChildIo(options.stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkBail(ep1, err);
1296912939
12970 if (options.cwd_dir) |cwd| {12940 if (options.cwd_dir) |cwd| {
12971 posix.fchdir(cwd.handle) catch |err| forkBail(err_pipe[1], err);12941 fchdir(cwd.handle) catch |err| forkBail(ep1, err);
12972 } else if (options.cwd) |cwd| {12942 } else if (options.cwd) |cwd| {
12973 posix.chdir(cwd) catch |err| forkBail(err_pipe[1], err);12943 chdir(cwd) catch |err| forkBail(ep1, err);
12974 }12944 }
1297512945
12976 // Must happen after fchdir above, the cwd file descriptor might be12946 // Must happen after fchdir above, the cwd file descriptor might be
12977 // equal to prog_fileno and be clobbered by this dup2 call.12947 // equal to prog_fileno and be clobbered by this dup2 call.
12978 if (prog_pipe[1] != -1) posix.dup2(prog_pipe[1], prog_fileno) catch |err| forkBail(err_pipe[1], err);12948 if (prog_pipe[1] != -1) dup2(prog_pipe[1], prog_fileno) catch |err| forkBail(ep1, err);
1297912949
12980 if (options.gid) |gid| {12950 if (options.gid) |gid| {
12981 posix.setregid(gid, gid) catch |err| forkBail(err_pipe[1], err);12951 switch (posix.errno(posix.system.setregid(gid, gid))) {
12952 .SUCCESS => {},
12953 .AGAIN => forkBail(ep1, error.ResourceLimitReached),
12954 .INVAL => forkBail(ep1, error.InvalidUserId),
12955 .PERM => forkBail(ep1, error.PermissionDenied),
12956 else => forkBail(ep1, error.Unexpected),
12957 }
12982 }12958 }
1298312959
12984 if (options.uid) |uid| {12960 if (options.uid) |uid| {
12985 switch (posix.errno(posix.system.setreuid(uid, uid))) {12961 switch (posix.errno(posix.system.setreuid(uid, uid))) {
12986 .SUCCESS => {},12962 .SUCCESS => {},
12987 .AGAIN => forkBail(err_pipe[1], error.ResourceLimitReached),12963 .AGAIN => forkBail(ep1, error.ResourceLimitReached),
12988 .INVAL => forkBail(err_pipe[1], error.InvalidUserId),12964 .INVAL => forkBail(ep1, error.InvalidUserId),
12989 .PERM => forkBail(err_pipe[1], error.PermissionDenied),12965 .PERM => forkBail(ep1, error.PermissionDenied),
12990 else => forkBail(err_pipe[1], error.Unexpected),12966 else => forkBail(ep1, error.Unexpected),
12991 }12967 }
12992 }12968 }
1299312969
12994 if (options.pgid) |pid| {12970 if (options.pgid) |pid| {
12995 switch (posix.errno(posix.system.setpgid(0, pid))) {12971 switch (posix.errno(posix.system.setpgid(0, pid))) {
12996 .SUCCESS => {},12972 .SUCCESS => {},
12997 .ACCES => forkBail(err_pipe[1], error.ProcessAlreadyExec),12973 .ACCES => forkBail(ep1, error.ProcessAlreadyExec),
12998 .INVAL => forkBail(err_pipe[1], error.InvalidProcessGroupId),12974 .INVAL => forkBail(ep1, error.InvalidProcessGroupId),
12999 .PERM => forkBail(err_pipe[1], error.PermissionDenied),12975 .PERM => forkBail(ep1, error.PermissionDenied),
13000 else => forkBail(err_pipe[1], error.Unexpected),12976 else => forkBail(ep1, error.Unexpected),
13001 }12977 }
13002 }12978 }
1300312979
13004 if (options.start_suspended) {12980 if (options.start_suspended) {
13005 switch (posix.errno(posix.system.kill(posix.system.getpid(), .STOP))) {12981 switch (posix.errno(posix.system.kill(posix.system.getpid(), .STOP))) {
13006 .SUCCESS => {},12982 .SUCCESS => {},
13007 .PERM => forkBail(err_pipe[1], error.PermissionDenied),12983 .PERM => forkBail(ep1, error.PermissionDenied),
13008 else => forkBail(err_pipe[1], error.Unexpected),12984 else => forkBail(ep1, error.Unexpected),
13009 }12985 }
13010 }12986 }
1301112987
13012 const err = posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH);12988 const err = posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH);
13013 forkBail(err_pipe[1], err);12989 forkBail(ep1, err);
13014 }12990 }
1301512991
13016 const pid: posix.pid_t = @intCast(pid_result); // We are the parent.12992 const pid: posix.pid_t = @intCast(pid_result); // We are the parent.
...@@ -13050,9 +13026,10 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t {...@@ -13050,9 +13026,10 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t {
13050 defer t.mutex.unlock();13026 defer t.mutex.unlock();
13051 if (t.null_file.fd != -1) return t.null_file.fd;13027 if (t.null_file.fd != -1) return t.null_file.fd;
13052 }13028 }
13029 const mode: u32 = 0;
13053 const syscall: Syscall = try .start();13030 const syscall: Syscall = try .start();
13054 while (true) {13031 while (true) {
13055 const rc = open_sym("/dev/null", .{ .ACCMODE = .RDWR }, 0);13032 const rc = open_sym("/dev/null", .{ .ACCMODE = .RDWR }, mode);
13056 switch (posix.errno(rc)) {13033 switch (posix.errno(rc)) {
13057 .SUCCESS => {13034 .SUCCESS => {
13058 syscall.finish();13035 syscall.finish();
...@@ -13089,10 +13066,15 @@ fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) proce...@@ -13089,10 +13066,15 @@ fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) proce
13089 defer posix.close(spawned.err_fd);13066 defer posix.close(spawned.err_fd);
1309013067
13091 // Wait for the child to report any errors in or before `execvpe`.13068 // Wait for the child to report any errors in or before `execvpe`.
13092 if (readIntFd(t, spawned.err_fd)) |child_err_int| {13069 if (readIntFd(spawned.err_fd)) |child_err_int| {
13093 const child_err: process.SpawnError = @errorCast(@errorFromInt(child_err_int));13070 const child_err: process.SpawnError = @errorCast(@errorFromInt(child_err_int));
13094 return child_err;13071 return child_err;
13095 } else |read_err| switch (read_err) {13072 } else |read_err| switch (read_err) {
13073 error.Canceled => {
13074 // We don't want to wait for the error to be reported, but we do
13075 // need to return the child so that it can be cleaned up.
13076 recancelInner();
13077 },
13096 error.EndOfStream => {13078 error.EndOfStream => {
13097 // Write end closed by CLOEXEC at the time of the `execvpe` call,13079 // Write end closed by CLOEXEC at the time of the `execvpe` call,
13098 // indicating success.13080 // indicating success.
...@@ -13159,7 +13141,7 @@ fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT...@@ -13159,7 +13141,7 @@ fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT
13159fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term {13141fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term {
13160 const handle = child.id.?;13142 const handle = child.id.?;
1316113143
13162 var syscall: Syscall = try .start();13144 const syscall: Syscall = try .start();
13163 while (true) switch (windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE)) {13145 while (true) switch (windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE)) {
13164 windows.WAIT_OBJECT_0 => break syscall.finish(),13146 windows.WAIT_OBJECT_0 => break syscall.finish(),
13165 windows.WAIT_ABANDONED, windows.WAIT_TIMEOUT => {13147 windows.WAIT_ABANDONED, windows.WAIT_TIMEOUT => {
...@@ -13393,21 +13375,25 @@ fn writeIntFd(fd: posix.fd_t, value: ErrInt) !void {...@@ -13393,21 +13375,25 @@ fn writeIntFd(fd: posix.fd_t, value: ErrInt) !void {
13393 }13375 }
13394}13376}
1339513377
13396fn readIntFd(t: *Threaded, fd: posix.fd_t) !ErrInt {13378fn readIntFd(fd: posix.fd_t) !ErrInt {
13397 _ = t; // TODO cancelation
13398 var buffer: [8]u8 = undefined;13379 var buffer: [8]u8 = undefined;
13399 var i: usize = 0;13380 var i: usize = 0;
13381 const syscall: Syscall = try .start();
13400 while (true) {13382 while (true) {
13401 const rc = posix.system.read(fd, buffer[i..].ptr, buffer.len - i);13383 const rc = posix.system.read(fd, buffer[i..].ptr, buffer.len - i);
13402 switch (posix.errno(rc)) {13384 switch (posix.errno(rc)) {
13403 .SUCCESS => {13385 .SUCCESS => {
13386 syscall.finish();
13404 const n: usize = @intCast(rc);13387 const n: usize = @intCast(rc);
13405 if (n == 0) break;13388 if (n == 0) break;
13406 i += n;13389 i += n;
13407 continue;13390 continue;
13408 },13391 },
13409 .INTR => continue,13392 .INTR => {
13410 else => |err| return posix.unexpectedErrno(err),13393 try syscall.checkCancel();
13394 continue;
13395 },
13396 else => |err| return syscall.unexpectedErrno(err),
13411 }13397 }
13412 }13398 }
13413 if (buffer.len - i != 0) return error.EndOfStream;13399 if (buffer.len - i != 0) return error.EndOfStream;
...@@ -13423,10 +13409,10 @@ fn destroyPipe(pipe: [2]posix.fd_t) void {...@@ -13423,10 +13409,10 @@ fn destroyPipe(pipe: [2]posix.fd_t) void {
1342313409
13424fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void {13410fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void {
13425 switch (stdio) {13411 switch (stdio) {
13426 .pipe => try posix.dup2(pipe_fd, std_fileno),13412 .pipe => try dup2(pipe_fd, std_fileno),
13427 .close => posix.close(std_fileno),13413 .close => posix.close(std_fileno),
13428 .inherit => {},13414 .inherit => {},
13429 .ignore => try posix.dup2(dev_null_fd, std_fileno),13415 .ignore => try dup2(dev_null_fd, std_fileno),
13430 .file => @panic("TODO implement setUpChildIo when file is used"),13416 .file => @panic("TODO implement setUpChildIo when file is used"),
13431 }13417 }
13432}13418}
...@@ -15289,3 +15275,81 @@ pub fn pipe2(flags: posix.O) PipeError![2]posix.fd_t {...@@ -15289,3 +15275,81 @@ pub fn pipe2(flags: posix.O) PipeError![2]posix.fd_t {
1528915275
15290 return fds;15276 return fds;
15291}15277}
15278
15279pub const DupError = error{
15280 ProcessFdQuotaExceeded,
15281 SystemResources,
15282} || Io.UnexpectedError || Io.Cancelable;
15283
15284pub fn dup2(old_fd: posix.fd_t, new_fd: posix.fd_t) DupError!void {
15285 const syscall: Syscall = try .start();
15286 while (true) switch (posix.errno(posix.system.dup2(old_fd, new_fd))) {
15287 .SUCCESS => return syscall.finish(),
15288 .BUSY, .INTR => {
15289 try syscall.checkCancel();
15290 continue;
15291 },
15292 .INVAL => |err| return syscall.errnoBug(err), // invalid parameters
15293 .BADF => |err| return syscall.errnoBug(err), // use after free
15294 .MFILE => return syscall.fail(error.ProcessFdQuotaExceeded),
15295 .NOMEM => return syscall.fail(error.SystemResources),
15296 else => |err| return syscall.unexpectedErrno(err),
15297 };
15298}
15299
15300pub const FchdirError = error{
15301 AccessDenied,
15302 NotDir,
15303 FileSystem,
15304} || Io.Cancelable || Io.UnexpectedError;
15305
15306pub fn fchdir(fd: posix.fd_t) FchdirError!void {
15307 if (fd == posix.AT.FDCWD) return;
15308 const syscall: Syscall = try .start();
15309 while (true) switch (posix.errno(posix.system.fchdir(fd))) {
15310 .SUCCESS => return syscall.finish(),
15311 .INTR => {
15312 try syscall.checkCancel();
15313 continue;
15314 },
15315 .ACCES => return syscall.fail(error.AccessDenied),
15316 .NOTDIR => return syscall.fail(error.NotDir),
15317 .IO => return syscall.fail(error.FileSystem),
15318 .BADF => |err| return syscall.errnoBug(err),
15319 else => |err| return syscall.unexpectedErrno(err),
15320 };
15321}
15322
15323pub const ChdirError = error{
15324 AccessDenied,
15325 FileSystem,
15326 SymLinkLoop,
15327 NameTooLong,
15328 FileNotFound,
15329 SystemResources,
15330 NotDir,
15331 BadPathName,
15332} || Io.Cancelable || Io.UnexpectedError;
15333
15334pub fn chdir(dir_path: []const u8) ChdirError!void {
15335 var path_buffer: [posix.PATH_MAX]u8 = undefined;
15336 const dir_path_posix = try pathToPosix(dir_path, &path_buffer);
15337 const syscall: Syscall = try .start();
15338 while (true) switch (posix.errno(posix.system.chdir(dir_path_posix))) {
15339 .SUCCESS => return syscall.finish(),
15340 .INTR => {
15341 try syscall.checkCancel();
15342 continue;
15343 },
15344 .ACCES => return syscall.fail(error.AccessDenied),
15345 .IO => return syscall.fail(error.FileSystem),
15346 .LOOP => return syscall.fail(error.SymLinkLoop),
15347 .NAMETOOLONG => return syscall.fail(error.NameTooLong),
15348 .NOENT => return syscall.fail(error.FileNotFound),
15349 .NOMEM => return syscall.fail(error.SystemResources),
15350 .NOTDIR => return syscall.fail(error.NotDir),
15351 .ILSEQ => return syscall.fail(error.BadPathName),
15352 .FAULT => |err| return syscall.errnoBug(err),
15353 else => |err| return syscall.unexpectedErrno(err),
15354 };
15355}
lib/std/posix.zig-112
...@@ -772,29 +772,6 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: O, mode: mode_t) O...@@ -772,29 +772,6 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: O, mode: mode_t) O
772 }772 }
773}773}
774774
775pub fn dup(old_fd: fd_t) !fd_t {
776 const rc = system.dup(old_fd);
777 return switch (errno(rc)) {
778 .SUCCESS => return @intCast(rc),
779 .MFILE => error.ProcessFdQuotaExceeded,
780 .BADF => unreachable, // invalid file descriptor
781 else => |err| return unexpectedErrno(err),
782 };
783}
784
785pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
786 while (true) {
787 switch (errno(system.dup2(old_fd, new_fd))) {
788 .SUCCESS => return,
789 .BUSY, .INTR => continue,
790 .MFILE => return error.ProcessFdQuotaExceeded,
791 .INVAL => unreachable, // invalid parameters passed to dup2
792 .BADF => unreachable, // invalid file descriptor
793 else => |err| return unexpectedErrno(err),
794 }
795 }
796}
797
798pub fn getppid() pid_t {775pub fn getppid() pid_t {
799 return system.getppid();776 return system.getppid();
800}777}
...@@ -832,85 +809,6 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {...@@ -832,85 +809,6 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
832 }809 }
833}810}
834811
835/// Same as `mkdir` but the parameter is null-terminated.
836/// On Windows, `dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
837/// On WASI, `dir_path` should be encoded as valid UTF-8.
838/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
839pub const ChangeCurDirError = error{
840 AccessDenied,
841 FileSystem,
842 SymLinkLoop,
843 NameTooLong,
844 FileNotFound,
845 SystemResources,
846 NotDir,
847 /// WASI: file paths must be valid UTF-8.
848 /// Windows: file paths provided by the user must be valid WTF-8.
849 /// https://wtf-8.codeberg.page/
850 BadPathName,
851} || UnexpectedError;
852
853/// Changes the current working directory of the calling process.
854/// On Windows, `dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
855/// On WASI, `dir_path` should be encoded as valid UTF-8.
856/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
857pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
858 if (native_os == .wasi and !builtin.link_libc) {
859 @compileError("unsupported OS");
860 } else if (native_os == .windows) {
861 @compileError("unsupported OS");
862 } else {
863 const dir_path_c = try toPosixPath(dir_path);
864 return chdirZ(&dir_path_c);
865 }
866}
867
868/// Same as `chdir` except the parameter is null-terminated.
869/// On Windows, `dir_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
870/// On WASI, `dir_path` should be encoded as valid UTF-8.
871/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
872pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void {
873 if (native_os == .windows) {
874 @compileError("unsupported OS");
875 } else if (native_os == .wasi and !builtin.link_libc) {
876 @compileError("unsupported OS");
877 }
878 switch (errno(system.chdir(dir_path))) {
879 .SUCCESS => return,
880 .ACCES => return error.AccessDenied,
881 .FAULT => unreachable,
882 .IO => return error.FileSystem,
883 .LOOP => return error.SymLinkLoop,
884 .NAMETOOLONG => return error.NameTooLong,
885 .NOENT => return error.FileNotFound,
886 .NOMEM => return error.SystemResources,
887 .NOTDIR => return error.NotDir,
888 .ILSEQ => return error.BadPathName,
889 else => |err| return unexpectedErrno(err),
890 }
891}
892
893pub const FchdirError = error{
894 AccessDenied,
895 NotDir,
896 FileSystem,
897} || UnexpectedError;
898
899pub fn fchdir(dirfd: fd_t) FchdirError!void {
900 if (dirfd == AT.FDCWD) return;
901 while (true) {
902 switch (errno(system.fchdir(dirfd))) {
903 .SUCCESS => return,
904 .ACCES => return error.AccessDenied,
905 .BADF => unreachable,
906 .NOTDIR => return error.NotDir,
907 .INTR => continue,
908 .IO => return error.FileSystem,
909 else => |err| return unexpectedErrno(err),
910 }
911 }
912}
913
914pub const SetEidError = error{812pub const SetEidError = error{
915 InvalidUserId,813 InvalidUserId,
916 PermissionDenied,814 PermissionDenied,
...@@ -956,16 +854,6 @@ pub fn setegid(uid: uid_t) SetEidError!void {...@@ -956,16 +854,6 @@ pub fn setegid(uid: uid_t) SetEidError!void {
956 }854 }
957}855}
958856
959pub fn setregid(rgid: gid_t, egid: gid_t) SetIdError!void {
960 switch (errno(system.setregid(rgid, egid))) {
961 .SUCCESS => return,
962 .AGAIN => return error.ResourceLimitReached,
963 .INVAL => return error.InvalidUserId,
964 .PERM => return error.PermissionDenied,
965 else => |err| return unexpectedErrno(err),
966 }
967}
968
969pub fn getuid() uid_t {857pub fn getuid() uid_t {
970 return system.getuid();858 return system.getuid();
971}859}
lib/std/posix/test.zig-31
...@@ -433,37 +433,6 @@ test "sigset add/del" {...@@ -433,37 +433,6 @@ test "sigset add/del" {
433 }433 }
434}434}
435435
436test "dup & dup2" {
437 switch (native_os) {
438 .linux, .illumos => {},
439 else => return error.SkipZigTest,
440 }
441
442 const io = testing.io;
443
444 var tmp = tmpDir(.{});
445 defer tmp.cleanup();
446
447 {
448 var file = try tmp.dir.createFile(io, "os_dup_test", .{});
449 defer file.close(io);
450
451 var duped = Io.File{ .handle = try posix.dup(file.handle) };
452 defer duped.close(io);
453 try duped.writeStreamingAll(io, "dup");
454
455 // Tests aren't run in parallel so using the next fd shouldn't be an issue.
456 const new_fd = duped.handle + 1;
457 try posix.dup2(file.handle, new_fd);
458 var dup2ed = Io.File{ .handle = new_fd };
459 defer dup2ed.close(io);
460 try dup2ed.writeStreamingAll(io, "dup2");
461 }
462
463 var buffer: [8]u8 = undefined;
464 try expectEqualStrings("dupdup2", try tmp.dir.readFile(io, "os_dup_test", &buffer));
465}
466
467test "getpid" {436test "getpid" {
468 if (native_os == .wasi) return error.SkipZigTest;437 if (native_os == .wasi) return error.SkipZigTest;
469 if (native_os == .windows) return error.SkipZigTest;438 if (native_os == .windows) return error.SkipZigTest;
test/standalone/posix/cwd.zig+3-3
...@@ -31,7 +31,7 @@ fn test_chdir_self() !void {...@@ -31,7 +31,7 @@ fn test_chdir_self() !void {
31 const old_cwd = try std.posix.getcwd(old_cwd_buf[0..]);31 const old_cwd = try std.posix.getcwd(old_cwd_buf[0..]);
3232
33 // Try changing to the current directory33 // Try changing to the current directory
34 try std.posix.chdir(old_cwd);34 try std.Io.Threaded.chdir(old_cwd);
35 try expect_cwd(old_cwd);35 try expect_cwd(old_cwd);
36}36}
3737
...@@ -42,7 +42,7 @@ fn test_chdir_absolute() !void {...@@ -42,7 +42,7 @@ fn test_chdir_absolute() !void {
42 const parent = std.fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute42 const parent = std.fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute
4343
44 // Try changing to the parent via a full path44 // Try changing to the parent via a full path
45 try std.posix.chdir(parent);45 try std.Io.Threaded.chdir(parent);
4646
47 try expect_cwd(parent);47 try expect_cwd(parent);
48}48}
...@@ -63,7 +63,7 @@ fn test_chdir_relative(gpa: Allocator, io: Io) !void {...@@ -63,7 +63,7 @@ fn test_chdir_relative(gpa: Allocator, io: Io) !void {
63 defer gpa.free(expected_path);63 defer gpa.free(expected_path);
6464
65 // change current working directory to new test directory65 // change current working directory to new test directory
66 try std.posix.chdir(relative_dir_name);66 try std.Io.Threaded.chdir(relative_dir_name);
6767
68 var new_cwd_buf: [path_max]u8 = undefined;68 var new_cwd_buf: [path_max]u8 = undefined;
69 const new_cwd = try std.posix.getcwd(new_cwd_buf[0..]);69 const new_cwd = try std.posix.getcwd(new_cwd_buf[0..]);