authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-03-18 22:39:59-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-03-19 11:45:09-07:00
logcd62005f19ff966d2c42de4daeb9a1e4b644bf76
tree4bb316708afaf79c971808df792d8fe86274789b
parent7057bffc14602add697eb566b83934b7ad3fd81c

extract std.posix from std.os

closes #5019

95 files changed, 11455 insertions(+), 11520 deletions(-)

CMakeLists.txt-1
......@@ -286,7 +286,6 @@ set(ZIG_STAGE2_SOURCES
286286 "${CMAKE_SOURCE_DIR}/lib/std/multi_array_list.zig"
287287 "${CMAKE_SOURCE_DIR}/lib/std/os.zig"
288288 "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"
289 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/errno/generic.zig"
290289 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig"
291290 "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"
292291 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/IoUring.zig"
build.zig+1-1
......@@ -882,7 +882,7 @@ fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
882882 return path;
883883 } else |_| {
884884 std.log.err("Could not open provided config.h: \"{s}\"", .{path});
885 std.os.exit(1);
885 std.process.exit(1);
886886 }
887887 }
888888
lib/compiler/objcopy.zig+1-1
......@@ -1285,7 +1285,7 @@ const ElfFileHelper = struct {
12851285 for (consolidated.items) |cmd| {
12861286 switch (cmd) {
12871287 .write_data => |data| {
1288 var iovec = [_]std.os.iovec_const{.{ .iov_base = data.data.ptr, .iov_len = data.data.len }};
1288 var iovec = [_]std.posix.iovec_const{.{ .iov_base = data.data.ptr, .iov_len = data.data.len }};
12891289 try out_file.pwritevAll(&iovec, data.out_offset);
12901290 },
12911291 .copy_range => |range| {
lib/std/Random/benchmark.zig+3-3
......@@ -144,7 +144,7 @@ pub fn main() !void {
144144 i += 1;
145145 if (i == args.len) {
146146 usage();
147 std.os.exit(1);
147 std.process.exit(1);
148148 }
149149
150150 filter = args[i];
......@@ -152,7 +152,7 @@ pub fn main() !void {
152152 i += 1;
153153 if (i == args.len) {
154154 usage();
155 std.os.exit(1);
155 std.process.exit(1);
156156 }
157157
158158 const c = try std.fmt.parseUnsigned(usize, args[i], 10);
......@@ -170,7 +170,7 @@ pub fn main() !void {
170170 return;
171171 } else {
172172 usage();
173 std.os.exit(1);
173 std.process.exit(1);
174174 }
175175 }
176176
lib/std/Thread.zig+72-72
......@@ -5,9 +5,11 @@
55const std = @import("std.zig");
66const builtin = @import("builtin");
77const math = std.math;
8const os = std.os;
98const assert = std.debug.assert;
109const target = builtin.target;
10const native_os = builtin.os.tag;
11const posix = std.posix;
12const windows = std.os.windows;
1113
1214pub const Futex = @import("Thread/Futex.zig");
1315pub const ResetEvent = @import("Thread/ResetEvent.zig");
......@@ -18,23 +20,23 @@ pub const RwLock = @import("Thread/RwLock.zig");
1820pub const Pool = @import("Thread/Pool.zig");
1921pub const WaitGroup = @import("Thread/WaitGroup.zig");
2022
21pub const use_pthreads = target.os.tag != .windows and target.os.tag != .wasi and builtin.link_libc;
23pub const use_pthreads = native_os != .windows and native_os != .wasi and builtin.link_libc;
2224
2325const Thread = @This();
24const Impl = if (target.os.tag == .windows)
26const Impl = if (native_os == .windows)
2527 WindowsThreadImpl
2628else if (use_pthreads)
2729 PosixThreadImpl
28else if (target.os.tag == .linux)
30else if (native_os == .linux)
2931 LinuxThreadImpl
30else if (target.os.tag == .wasi)
32else if (native_os == .wasi)
3133 WasiThreadImpl
3234else
3335 UnsupportedImpl;
3436
3537impl: Impl,
3638
37pub const max_name_len = switch (target.os.tag) {
39pub const max_name_len = switch (native_os) {
3840 .linux => 15,
3941 .windows => 31,
4042 .macos, .ios, .watchos, .tvos => 63,
......@@ -50,7 +52,7 @@ pub const SetNameError = error{
5052 NameTooLong,
5153 Unsupported,
5254 Unexpected,
53} || os.PrctlError || os.WriteError || std.fs.File.OpenError || std.fmt.BufPrintError;
55} || posix.PrctlError || posix.WriteError || std.fs.File.OpenError || std.fmt.BufPrintError;
5456
5557pub fn setName(self: Thread, name: []const u8) SetNameError!void {
5658 if (name.len > max_name_len) return error.NameTooLong;
......@@ -62,21 +64,21 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
6264 break :blk name_buf[0..name.len :0];
6365 };
6466
65 switch (target.os.tag) {
67 switch (native_os) {
6668 .linux => if (use_pthreads) {
6769 if (self.getHandle() == std.c.pthread_self()) {
6870 // Set the name of the calling thread (no thread id required).
69 const err = try os.prctl(.SET_NAME, .{@intFromPtr(name_with_terminator.ptr)});
70 switch (@as(os.E, @enumFromInt(err))) {
71 const err = try posix.prctl(.SET_NAME, .{@intFromPtr(name_with_terminator.ptr)});
72 switch (@as(posix.E, @enumFromInt(err))) {
7173 .SUCCESS => return,
72 else => |e| return os.unexpectedErrno(e),
74 else => |e| return posix.unexpectedErrno(e),
7375 }
7476 } else {
7577 const err = std.c.pthread_setname_np(self.getHandle(), name_with_terminator.ptr);
7678 switch (err) {
7779 .SUCCESS => return,
7880 .RANGE => unreachable,
79 else => |e| return os.unexpectedErrno(e),
81 else => |e| return posix.unexpectedErrno(e),
8082 }
8183 }
8284 } else {
......@@ -95,21 +97,21 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
9597 const byte_len = math.cast(c_ushort, len * 2) orelse return error.NameTooLong;
9698
9799 // Note: NT allocates its own copy, no use-after-free here.
98 const unicode_string = os.windows.UNICODE_STRING{
100 const unicode_string = windows.UNICODE_STRING{
99101 .Length = byte_len,
100102 .MaximumLength = byte_len,
101103 .Buffer = &buf,
102104 };
103105
104 switch (os.windows.ntdll.NtSetInformationThread(
106 switch (windows.ntdll.NtSetInformationThread(
105107 self.getHandle(),
106108 .ThreadNameInformation,
107109 &unicode_string,
108 @sizeOf(os.windows.UNICODE_STRING),
110 @sizeOf(windows.UNICODE_STRING),
109111 )) {
110112 .SUCCESS => return,
111113 .NOT_IMPLEMENTED => return error.Unsupported,
112 else => |err| return os.windows.unexpectedStatus(err),
114 else => |err| return windows.unexpectedStatus(err),
113115 }
114116 },
115117 .macos, .ios, .watchos, .tvos => if (use_pthreads) {
......@@ -119,7 +121,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
119121 const err = std.c.pthread_setname_np(name_with_terminator.ptr);
120122 switch (err) {
121123 .SUCCESS => return,
122 else => |e| return os.unexpectedErrno(e),
124 else => |e| return posix.unexpectedErrno(e),
123125 }
124126 },
125127 .netbsd, .solaris, .illumos => if (use_pthreads) {
......@@ -129,7 +131,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
129131 .INVAL => unreachable,
130132 .SRCH => unreachable,
131133 .NOMEM => unreachable,
132 else => |e| return os.unexpectedErrno(e),
134 else => |e| return posix.unexpectedErrno(e),
133135 }
134136 },
135137 .freebsd, .openbsd => if (use_pthreads) {
......@@ -148,7 +150,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
148150 .FAULT => unreachable,
149151 .NAMETOOLONG => unreachable, // already checked
150152 .SRCH => unreachable,
151 else => |e| return os.unexpectedErrno(e),
153 else => |e| return posix.unexpectedErrno(e),
152154 }
153155 },
154156 else => {},
......@@ -159,7 +161,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
159161pub const GetNameError = error{
160162 Unsupported,
161163 Unexpected,
162} || os.PrctlError || os.ReadError || std.fs.File.OpenError || std.fmt.BufPrintError;
164} || posix.PrctlError || posix.ReadError || std.fs.File.OpenError || std.fmt.BufPrintError;
163165
164166/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
165167/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
......@@ -167,21 +169,21 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
167169 buffer_ptr[max_name_len] = 0;
168170 var buffer: [:0]u8 = buffer_ptr;
169171
170 switch (target.os.tag) {
172 switch (native_os) {
171173 .linux => if (use_pthreads) {
172174 if (self.getHandle() == std.c.pthread_self()) {
173175 // Get the name of the calling thread (no thread id required).
174 const err = try os.prctl(.GET_NAME, .{@intFromPtr(buffer.ptr)});
175 switch (@as(os.E, @enumFromInt(err))) {
176 const err = try posix.prctl(.GET_NAME, .{@intFromPtr(buffer.ptr)});
177 switch (@as(posix.E, @enumFromInt(err))) {
176178 .SUCCESS => return std.mem.sliceTo(buffer, 0),
177 else => |e| return os.unexpectedErrno(e),
179 else => |e| return posix.unexpectedErrno(e),
178180 }
179181 } else {
180182 const err = std.c.pthread_getname_np(self.getHandle(), buffer.ptr, max_name_len + 1);
181183 switch (err) {
182184 .SUCCESS => return std.mem.sliceTo(buffer, 0),
183185 .RANGE => unreachable,
184 else => |e| return os.unexpectedErrno(e),
186 else => |e| return posix.unexpectedErrno(e),
185187 }
186188 }
187189 } else {
......@@ -196,10 +198,10 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
196198 return if (data_len >= 1) buffer[0 .. data_len - 1] else null;
197199 },
198200 .windows => {
199 const buf_capacity = @sizeOf(os.windows.UNICODE_STRING) + (@sizeOf(u16) * max_name_len);
200 var buf: [buf_capacity]u8 align(@alignOf(os.windows.UNICODE_STRING)) = undefined;
201 const buf_capacity = @sizeOf(windows.UNICODE_STRING) + (@sizeOf(u16) * max_name_len);
202 var buf: [buf_capacity]u8 align(@alignOf(windows.UNICODE_STRING)) = undefined;
201203
202 switch (os.windows.ntdll.NtQueryInformationThread(
204 switch (windows.ntdll.NtQueryInformationThread(
203205 self.getHandle(),
204206 .ThreadNameInformation,
205207 &buf,
......@@ -207,12 +209,12 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
207209 null,
208210 )) {
209211 .SUCCESS => {
210 const string = @as(*const os.windows.UNICODE_STRING, @ptrCast(&buf));
212 const string = @as(*const windows.UNICODE_STRING, @ptrCast(&buf));
211213 const len = std.unicode.wtf16LeToWtf8(buffer, string.Buffer.?[0 .. string.Length / 2]);
212214 return if (len > 0) buffer[0..len] else null;
213215 },
214216 .NOT_IMPLEMENTED => return error.Unsupported,
215 else => |err| return os.windows.unexpectedStatus(err),
217 else => |err| return windows.unexpectedStatus(err),
216218 }
217219 },
218220 .macos, .ios, .watchos, .tvos => if (use_pthreads) {
......@@ -220,7 +222,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
220222 switch (err) {
221223 .SUCCESS => return std.mem.sliceTo(buffer, 0),
222224 .SRCH => unreachable,
223 else => |e| return os.unexpectedErrno(e),
225 else => |e| return posix.unexpectedErrno(e),
224226 }
225227 },
226228 .netbsd, .solaris, .illumos => if (use_pthreads) {
......@@ -229,7 +231,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
229231 .SUCCESS => return std.mem.sliceTo(buffer, 0),
230232 .INVAL => unreachable,
231233 .SRCH => unreachable,
232 else => |e| return os.unexpectedErrno(e),
234 else => |e| return posix.unexpectedErrno(e),
233235 }
234236 },
235237 .freebsd, .openbsd => if (use_pthreads) {
......@@ -246,7 +248,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
246248 .INVAL => unreachable,
247249 .FAULT => unreachable,
248250 .SRCH => unreachable,
249 else => |e| return os.unexpectedErrno(e),
251 else => |e| return posix.unexpectedErrno(e),
250252 }
251253 },
252254 else => {},
......@@ -255,7 +257,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
255257}
256258
257259/// Represents an ID per thread guaranteed to be unique only within a process.
258pub const Id = switch (target.os.tag) {
260pub const Id = switch (native_os) {
259261 .linux,
260262 .dragonfly,
261263 .netbsd,
......@@ -265,7 +267,7 @@ pub const Id = switch (target.os.tag) {
265267 .wasi,
266268 => u32,
267269 .macos, .ios, .watchos, .tvos => u64,
268 .windows => os.windows.DWORD,
270 .windows => windows.DWORD,
269271 else => usize,
270272};
271273
......@@ -368,13 +370,13 @@ pub const YieldError = error{
368370
369371/// Yields the current thread potentially allowing other threads to run.
370372pub fn yield() YieldError!void {
371 if (builtin.os.tag == .windows) {
373 if (native_os == .windows) {
372374 // The return value has to do with how many other threads there are; it is not
373375 // an error condition on Windows.
374 _ = os.windows.kernel32.SwitchToThread();
376 _ = windows.kernel32.SwitchToThread();
375377 return;
376378 }
377 switch (os.errno(os.system.sched_yield())) {
379 switch (posix.errno(posix.system.sched_yield())) {
378380 .SUCCESS => return,
379381 .NOSYS => return error.SystemCannotYield,
380382 else => return error.SystemCannotYield,
......@@ -390,7 +392,7 @@ const Completion = std.atomic.Value(enum(u8) {
390392
391393/// Used by the Thread implementations to call the spawned function with the arguments.
392394fn callFn(comptime f: anytype, args: anytype) switch (Impl) {
393 WindowsThreadImpl => std.os.windows.DWORD,
395 WindowsThreadImpl => windows.DWORD,
394396 LinuxThreadImpl => u8,
395397 PosixThreadImpl => ?*anyopaque,
396398 else => unreachable,
......@@ -470,13 +472,11 @@ const UnsupportedImpl = struct {
470472
471473 fn unsupported(unused: anytype) noreturn {
472474 _ = unused;
473 @compileError("Unsupported operating system " ++ @tagName(target.os.tag));
475 @compileError("Unsupported operating system " ++ @tagName(native_os));
474476 }
475477};
476478
477479const WindowsThreadImpl = struct {
478 const windows = os.windows;
479
480480 pub const ThreadHandle = windows.HANDLE;
481481
482482 fn getCurrentId() windows.DWORD {
......@@ -584,7 +584,7 @@ const PosixThreadImpl = struct {
584584 pub const ThreadHandle = c.pthread_t;
585585
586586 fn getCurrentId() Id {
587 switch (target.os.tag) {
587 switch (native_os) {
588588 .linux => {
589589 return LinuxThreadImpl.getCurrentId();
590590 },
......@@ -616,15 +616,15 @@ const PosixThreadImpl = struct {
616616 }
617617
618618 fn getCpuCount() !usize {
619 switch (target.os.tag) {
619 switch (native_os) {
620620 .linux => {
621621 return LinuxThreadImpl.getCpuCount();
622622 },
623623 .openbsd => {
624624 var count: c_int = undefined;
625625 var count_size: usize = @sizeOf(c_int);
626 const mib = [_]c_int{ os.CTL.HW, os.system.HW.NCPUONLINE };
627 os.sysctl(&mib, &count, &count_size, null, 0) catch |err| switch (err) {
626 const mib = [_]c_int{ std.c.CTL.HW, std.c.HW.NCPUONLINE };
627 std.c.sysctl(&mib, &count, &count_size, null, 0) catch |err| switch (err) {
628628 error.NameTooLong, error.UnknownName => unreachable,
629629 else => |e| return e,
630630 };
......@@ -634,25 +634,25 @@ const PosixThreadImpl = struct {
634634 // The "proper" way to get the cpu count would be to query
635635 // /dev/kstat via ioctls, and traverse a linked list for each
636636 // cpu.
637 const rc = c.sysconf(os._SC.NPROCESSORS_ONLN);
638 return switch (os.errno(rc)) {
637 const rc = c.sysconf(std.c._SC.NPROCESSORS_ONLN);
638 return switch (posix.errno(rc)) {
639639 .SUCCESS => @as(usize, @intCast(rc)),
640 else => |err| os.unexpectedErrno(err),
640 else => |err| posix.unexpectedErrno(err),
641641 };
642642 },
643643 .haiku => {
644 var system_info: os.system.system_info = undefined;
645 const rc = os.system.get_system_info(&system_info); // always returns B_OK
646 return switch (os.errno(rc)) {
644 var system_info: std.c.system_info = undefined;
645 const rc = std.c.get_system_info(&system_info); // always returns B_OK
646 return switch (posix.errno(rc)) {
647647 .SUCCESS => @as(usize, @intCast(system_info.cpu_count)),
648 else => |err| os.unexpectedErrno(err),
648 else => |err| posix.unexpectedErrno(err),
649649 };
650650 },
651651 else => {
652652 var count: c_int = undefined;
653653 var count_len: usize = @sizeOf(c_int);
654654 const name = if (comptime target.isDarwin()) "hw.logicalcpu" else "hw.ncpu";
655 os.sysctlbynameZ(name, &count, &count_len, null, 0) catch |err| switch (err) {
655 posix.sysctlbynameZ(name, &count, &count_len, null, 0) catch |err| switch (err) {
656656 error.NameTooLong, error.UnknownName => unreachable,
657657 else => |e| return e,
658658 };
......@@ -699,7 +699,7 @@ const PosixThreadImpl = struct {
699699 .AGAIN => return error.SystemResources,
700700 .PERM => unreachable,
701701 .INVAL => unreachable,
702 else => |err| return os.unexpectedErrno(err),
702 else => |err| return posix.unexpectedErrno(err),
703703 }
704704 }
705705
......@@ -1013,7 +1013,7 @@ const WasiThreadImpl = struct {
10131013};
10141014
10151015const LinuxThreadImpl = struct {
1016 const linux = os.linux;
1016 const linux = std.os.linux;
10171017
10181018 pub const ThreadHandle = i32;
10191019
......@@ -1028,9 +1028,9 @@ const LinuxThreadImpl = struct {
10281028 }
10291029
10301030 fn getCpuCount() !usize {
1031 const cpu_set = try os.sched_getaffinity(0);
1031 const cpu_set = try posix.sched_getaffinity(0);
10321032 // TODO: should not need this usize cast
1033 return @as(usize, os.CPU_COUNT(cpu_set));
1033 return @as(usize, posix.CPU_COUNT(cpu_set));
10341034 }
10351035
10361036 thread: *ThreadCompletion,
......@@ -1228,10 +1228,10 @@ const LinuxThreadImpl = struct {
12281228 // map all memory needed without read/write permissions
12291229 // to avoid committing the whole region right away
12301230 // anonymous mapping ensures file descriptor limits are not exceeded
1231 const mapped = os.mmap(
1231 const mapped = posix.mmap(
12321232 null,
12331233 map_bytes,
1234 os.PROT.NONE,
1234 posix.PROT.NONE,
12351235 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
12361236 -1,
12371237 0,
......@@ -1244,24 +1244,24 @@ const LinuxThreadImpl = struct {
12441244 else => |e| return e,
12451245 };
12461246 assert(mapped.len >= map_bytes);
1247 errdefer os.munmap(mapped);
1247 errdefer posix.munmap(mapped);
12481248
12491249 // map everything but the guard page as read/write
1250 os.mprotect(
1250 posix.mprotect(
12511251 @alignCast(mapped[guard_offset..]),
1252 os.PROT.READ | os.PROT.WRITE,
1252 posix.PROT.READ | posix.PROT.WRITE,
12531253 ) catch |err| switch (err) {
12541254 error.AccessDenied => unreachable,
12551255 else => |e| return e,
12561256 };
12571257
12581258 // Prepare the TLS segment and prepare a user_desc struct when needed on x86
1259 var tls_ptr = os.linux.tls.prepareTLS(mapped[tls_offset..]);
1260 var user_desc: if (target.cpu.arch == .x86) os.linux.user_desc else void = undefined;
1259 var tls_ptr = linux.tls.prepareTLS(mapped[tls_offset..]);
1260 var user_desc: if (target.cpu.arch == .x86) linux.user_desc else void = undefined;
12611261 if (target.cpu.arch == .x86) {
12621262 defer tls_ptr = @intFromPtr(&user_desc);
12631263 user_desc = .{
1264 .entry_number = os.linux.tls.tls_image.gdt_entry_number,
1264 .entry_number = linux.tls.tls_image.gdt_entry_number,
12651265 .base_addr = tls_ptr,
12661266 .limit = 0xfffff,
12671267 .flags = .{
......@@ -1286,7 +1286,7 @@ const LinuxThreadImpl = struct {
12861286 linux.CLONE.PARENT_SETTID | linux.CLONE.CHILD_CLEARTID |
12871287 linux.CLONE.SIGHAND | linux.CLONE.SYSVSEM | linux.CLONE.SETTLS;
12881288
1289 switch (linux.getErrno(linux.clone(
1289 switch (linux.E.init(linux.clone(
12901290 Instance.entryFn,
12911291 @intFromPtr(&mapped[stack_offset]),
12921292 flags,
......@@ -1302,7 +1302,7 @@ const LinuxThreadImpl = struct {
13021302 .NOSPC => unreachable,
13031303 .PERM => unreachable,
13041304 .USERS => unreachable,
1305 else => |err| return os.unexpectedErrno(err),
1305 else => |err| return posix.unexpectedErrno(err),
13061306 }
13071307 }
13081308
......@@ -1319,7 +1319,7 @@ const LinuxThreadImpl = struct {
13191319 }
13201320
13211321 fn join(self: Impl) void {
1322 defer os.munmap(self.thread.mapped);
1322 defer posix.munmap(self.thread.mapped);
13231323
13241324 var spin: u8 = 10;
13251325 while (true) {
......@@ -1334,7 +1334,7 @@ const LinuxThreadImpl = struct {
13341334 continue;
13351335 }
13361336
1337 switch (linux.getErrno(linux.futex_wait(
1337 switch (linux.E.init(linux.futex_wait(
13381338 &self.thread.child_tid.raw,
13391339 linux.FUTEX.WAIT,
13401340 tid,
......@@ -1383,7 +1383,7 @@ test "setName, getName" {
13831383 // Wait for the main thread to have set the thread field in the context.
13841384 ctx.start_wait_event.wait();
13851385
1386 switch (target.os.tag) {
1386 switch (native_os) {
13871387 .windows => testThreadName(&ctx.thread) catch |err| switch (err) {
13881388 error.Unsupported => return error.SkipZigTest,
13891389 else => return err,
......@@ -1406,7 +1406,7 @@ test "setName, getName" {
14061406 context.start_wait_event.set();
14071407 context.test_done_event.wait();
14081408
1409 switch (target.os.tag) {
1409 switch (native_os) {
14101410 .macos, .ios, .watchos, .tvos => {
14111411 const res = thread.setName("foobar");
14121412 try std.testing.expectError(error.Unsupported, res);
lib/std/Thread/Futex.zig+71-64
......@@ -1,13 +1,20 @@
1//! Futex is a mechanism used to block (`wait`) and unblock (`wake`) threads using a 32bit memory address as hints.
2//! Blocking a thread is acknowledged only if the 32bit memory address is equal to a given value.
3//! This check helps avoid block/unblock deadlocks which occur if a `wake()` happens before a `wait()`.
4//! Using Futex, other Thread synchronization primitives can be built which efficiently wait for cross-thread events or signals.
1//! A mechanism used to block (`wait`) and unblock (`wake`) threads using a
2//! 32bit memory address as hints.
3//!
4//! Blocking a thread is acknowledged only if the 32bit memory address is equal
5//! to a given value. This check helps avoid block/unblock deadlocks which
6//! occur if a `wake()` happens before a `wait()`.
7//!
8//! Using Futex, other Thread synchronization primitives can be built which
9//! efficiently wait for cross-thread events or signals.
510
611const std = @import("../std.zig");
712const builtin = @import("builtin");
813const Futex = @This();
14const windows = std.os.windows;
15const linux = std.os.linux;
16const c = std.c;
917
10const os = std.os;
1118const assert = std.debug.assert;
1219const testing = std.testing;
1320const atomic = std.atomic;
......@@ -124,18 +131,18 @@ const SingleThreadedImpl = struct {
124131// as it's generally already a linked target and is autoloaded into all processes anyway.
125132const WindowsImpl = struct {
126133 fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void {
127 var timeout_value: os.windows.LARGE_INTEGER = undefined;
128 var timeout_ptr: ?*const os.windows.LARGE_INTEGER = null;
134 var timeout_value: windows.LARGE_INTEGER = undefined;
135 var timeout_ptr: ?*const windows.LARGE_INTEGER = null;
129136
130137 // NTDLL functions work with time in units of 100 nanoseconds.
131138 // Positive values are absolute deadlines while negative values are relative durations.
132139 if (timeout) |delay| {
133 timeout_value = @as(os.windows.LARGE_INTEGER, @intCast(delay / 100));
140 timeout_value = @as(windows.LARGE_INTEGER, @intCast(delay / 100));
134141 timeout_value = -timeout_value;
135142 timeout_ptr = &timeout_value;
136143 }
137144
138 const rc = os.windows.ntdll.RtlWaitOnAddress(
145 const rc = windows.ntdll.RtlWaitOnAddress(
139146 ptr,
140147 &expect,
141148 @sizeOf(@TypeOf(expect)),
......@@ -157,8 +164,8 @@ const WindowsImpl = struct {
157164 assert(max_waiters != 0);
158165
159166 switch (max_waiters) {
160 1 => os.windows.ntdll.RtlWakeAddressSingle(address),
161 else => os.windows.ntdll.RtlWakeAddressAll(address),
167 1 => windows.ntdll.RtlWakeAddressSingle(address),
168 else => windows.ntdll.RtlWakeAddressAll(address),
162169 }
163170 }
164171};
......@@ -189,10 +196,10 @@ const DarwinImpl = struct {
189196 var timeout_overflowed = false;
190197
191198 const addr: *const anyopaque = ptr;
192 const flags = os.darwin.UL_COMPARE_AND_WAIT | os.darwin.ULF_NO_ERRNO;
199 const flags = c.UL_COMPARE_AND_WAIT | c.ULF_NO_ERRNO;
193200 const status = blk: {
194201 if (supports_ulock_wait2) {
195 break :blk os.darwin.__ulock_wait2(flags, addr, expect, timeout_ns, 0);
202 break :blk c.__ulock_wait2(flags, addr, expect, timeout_ns, 0);
196203 }
197204
198205 const timeout_us = std.math.cast(u32, timeout_ns / std.time.ns_per_us) orelse overflow: {
......@@ -200,11 +207,11 @@ const DarwinImpl = struct {
200207 break :overflow std.math.maxInt(u32);
201208 };
202209
203 break :blk os.darwin.__ulock_wait(flags, addr, expect, timeout_us);
210 break :blk c.__ulock_wait(flags, addr, expect, timeout_us);
204211 };
205212
206213 if (status >= 0) return;
207 switch (@as(std.os.E, @enumFromInt(-status))) {
214 switch (@as(c.E, @enumFromInt(-status))) {
208215 // Wait was interrupted by the OS or other spurious signalling.
209216 .INTR => {},
210217 // Address of the futex was paged out. This is unlikely, but possible in theory, and
......@@ -221,17 +228,17 @@ const DarwinImpl = struct {
221228 }
222229
223230 fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void {
224 var flags: u32 = os.darwin.UL_COMPARE_AND_WAIT | os.darwin.ULF_NO_ERRNO;
231 var flags: u32 = c.UL_COMPARE_AND_WAIT | c.ULF_NO_ERRNO;
225232 if (max_waiters > 1) {
226 flags |= os.darwin.ULF_WAKE_ALL;
233 flags |= c.ULF_WAKE_ALL;
227234 }
228235
229236 while (true) {
230237 const addr: *const anyopaque = ptr;
231 const status = os.darwin.__ulock_wake(flags, addr, 0);
238 const status = c.__ulock_wake(flags, addr, 0);
232239
233240 if (status >= 0) return;
234 switch (@as(std.os.E, @enumFromInt(-status))) {
241 switch (@as(c.E, @enumFromInt(-status))) {
235242 .INTR => continue, // spurious wake()
236243 .FAULT => unreachable, // __ulock_wake doesn't generate EFAULT according to darwin pthread_cond_t
237244 .NOENT => return, // nothing was woken up
......@@ -245,20 +252,20 @@ const DarwinImpl = struct {
245252// https://man7.org/linux/man-pages/man2/futex.2.html
246253const LinuxImpl = struct {
247254 fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void {
248 var ts: os.timespec = undefined;
255 var ts: linux.timespec = undefined;
249256 if (timeout) |timeout_ns| {
250257 ts.tv_sec = @as(@TypeOf(ts.tv_sec), @intCast(timeout_ns / std.time.ns_per_s));
251258 ts.tv_nsec = @as(@TypeOf(ts.tv_nsec), @intCast(timeout_ns % std.time.ns_per_s));
252259 }
253260
254 const rc = os.linux.futex_wait(
261 const rc = linux.futex_wait(
255262 @as(*const i32, @ptrCast(&ptr.raw)),
256 os.linux.FUTEX.PRIVATE_FLAG | os.linux.FUTEX.WAIT,
263 linux.FUTEX.PRIVATE_FLAG | linux.FUTEX.WAIT,
257264 @as(i32, @bitCast(expect)),
258265 if (timeout != null) &ts else null,
259266 );
260267
261 switch (os.linux.getErrno(rc)) {
268 switch (linux.E.init(rc)) {
262269 .SUCCESS => {}, // notified by `wake()`
263270 .INTR => {}, // spurious wakeup
264271 .AGAIN => {}, // ptr.* != expect
......@@ -273,13 +280,13 @@ const LinuxImpl = struct {
273280 }
274281
275282 fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void {
276 const rc = os.linux.futex_wake(
283 const rc = linux.futex_wake(
277284 @as(*const i32, @ptrCast(&ptr.raw)),
278 os.linux.FUTEX.PRIVATE_FLAG | os.linux.FUTEX.WAKE,
285 linux.FUTEX.PRIVATE_FLAG | linux.FUTEX.WAKE,
279286 std.math.cast(i32, max_waiters) orelse std.math.maxInt(i32),
280287 );
281288
282 switch (os.linux.getErrno(rc)) {
289 switch (linux.E.init(rc)) {
283290 .SUCCESS => {}, // successful wake up
284291 .INVAL => {}, // invalid futex_wait() on ptr done elsewhere
285292 .FAULT => {}, // pointer became invalid while doing the wake
......@@ -292,28 +299,28 @@ const LinuxImpl = struct {
292299const FreebsdImpl = struct {
293300 fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void {
294301 var tm_size: usize = 0;
295 var tm: os.freebsd._umtx_time = undefined;
296 var tm_ptr: ?*const os.freebsd._umtx_time = null;
302 var tm: c._umtx_time = undefined;
303 var tm_ptr: ?*const c._umtx_time = null;
297304
298305 if (timeout) |timeout_ns| {
299306 tm_ptr = &tm;
300307 tm_size = @sizeOf(@TypeOf(tm));
301308
302309 tm._flags = 0; // use relative time not UMTX_ABSTIME
303 tm._clockid = os.CLOCK.MONOTONIC;
310 tm._clockid = c.CLOCK.MONOTONIC;
304311 tm._timeout.tv_sec = @as(@TypeOf(tm._timeout.tv_sec), @intCast(timeout_ns / std.time.ns_per_s));
305312 tm._timeout.tv_nsec = @as(@TypeOf(tm._timeout.tv_nsec), @intCast(timeout_ns % std.time.ns_per_s));
306313 }
307314
308 const rc = os.freebsd._umtx_op(
315 const rc = c._umtx_op(
309316 @intFromPtr(&ptr.raw),
310 @intFromEnum(os.freebsd.UMTX_OP.WAIT_UINT_PRIVATE),
317 @intFromEnum(c.UMTX_OP.WAIT_UINT_PRIVATE),
311318 @as(c_ulong, expect),
312319 tm_size,
313320 @intFromPtr(tm_ptr),
314321 );
315322
316 switch (os.errno(rc)) {
323 switch (std.posix.errno(rc)) {
317324 .SUCCESS => {},
318325 .FAULT => unreachable, // one of the args points to invalid memory
319326 .INVAL => unreachable, // arguments should be correct
......@@ -327,15 +334,15 @@ const FreebsdImpl = struct {
327334 }
328335
329336 fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void {
330 const rc = os.freebsd._umtx_op(
337 const rc = c._umtx_op(
331338 @intFromPtr(&ptr.raw),
332 @intFromEnum(os.freebsd.UMTX_OP.WAKE_PRIVATE),
339 @intFromEnum(c.UMTX_OP.WAKE_PRIVATE),
333340 @as(c_ulong, max_waiters),
334341 0, // there is no timeout struct
335342 0, // there is no timeout struct pointer
336343 );
337344
338 switch (os.errno(rc)) {
345 switch (std.posix.errno(rc)) {
339346 .SUCCESS => {},
340347 .FAULT => {}, // it's ok if the ptr doesn't point to valid memory
341348 .INVAL => unreachable, // arguments should be correct
......@@ -347,21 +354,21 @@ const FreebsdImpl = struct {
347354// https://man.openbsd.org/futex.2
348355const OpenbsdImpl = struct {
349356 fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void {
350 var ts: os.timespec = undefined;
357 var ts: c.timespec = undefined;
351358 if (timeout) |timeout_ns| {
352359 ts.tv_sec = @as(@TypeOf(ts.tv_sec), @intCast(timeout_ns / std.time.ns_per_s));
353360 ts.tv_nsec = @as(@TypeOf(ts.tv_nsec), @intCast(timeout_ns % std.time.ns_per_s));
354361 }
355362
356 const rc = os.openbsd.futex(
363 const rc = c.futex(
357364 @as(*const volatile u32, @ptrCast(&ptr.raw)),
358 os.openbsd.FUTEX_WAIT | os.openbsd.FUTEX_PRIVATE_FLAG,
365 c.FUTEX_WAIT | c.FUTEX_PRIVATE_FLAG,
359366 @as(c_int, @bitCast(expect)),
360367 if (timeout != null) &ts else null,
361368 null, // FUTEX_WAIT takes no requeue address
362369 );
363370
364 switch (os.errno(rc)) {
371 switch (std.posix.errno(rc)) {
365372 .SUCCESS => {}, // woken up by wake
366373 .NOSYS => unreachable, // the futex operation shouldn't be invalid
367374 .FAULT => unreachable, // ptr was invalid
......@@ -378,9 +385,9 @@ const OpenbsdImpl = struct {
378385 }
379386
380387 fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void {
381 const rc = os.openbsd.futex(
388 const rc = c.futex(
382389 @as(*const volatile u32, @ptrCast(&ptr.raw)),
383 os.openbsd.FUTEX_WAKE | os.openbsd.FUTEX_PRIVATE_FLAG,
390 c.FUTEX_WAKE | c.FUTEX_PRIVATE_FLAG,
384391 std.math.cast(c_int, max_waiters) orelse std.math.maxInt(c_int),
385392 null, // FUTEX_WAKE takes no timeout ptr
386393 null, // FUTEX_WAKE takes no requeue address
......@@ -415,9 +422,9 @@ const DragonflyImpl = struct {
415422
416423 const value = @as(c_int, @bitCast(expect));
417424 const addr = @as(*const volatile c_int, @ptrCast(&ptr.raw));
418 const rc = os.dragonfly.umtx_sleep(addr, value, timeout_us);
425 const rc = c.umtx_sleep(addr, value, timeout_us);
419426
420 switch (os.errno(rc)) {
427 switch (std.posix.errno(rc)) {
421428 .SUCCESS => {},
422429 .BUSY => {}, // ptr != expect
423430 .AGAIN => { // maybe timed out, or paged out, or hit 2s kernel refresh
......@@ -444,7 +451,7 @@ const DragonflyImpl = struct {
444451 // > umtx_wakeup() will generally return 0 unless the address is bad.
445452 // We are fine with the address being bad (e.g. for Semaphore.post() where Semaphore.wait() frees the Semaphore)
446453 const addr = @as(*const volatile c_int, @ptrCast(&ptr.raw));
447 _ = os.dragonfly.umtx_wakeup(addr, to_wake);
454 _ = c.umtx_wakeup(addr, to_wake);
448455 }
449456};
450457
......@@ -496,8 +503,8 @@ const WasmImpl = struct {
496503/// https://go.dev/src/runtime/sema.go
497504const PosixImpl = struct {
498505 const Event = struct {
499 cond: std.c.pthread_cond_t,
500 mutex: std.c.pthread_mutex_t,
506 cond: c.pthread_cond_t,
507 mutex: c.pthread_mutex_t,
501508 state: enum { empty, waiting, notified },
502509
503510 fn init(self: *Event) void {
......@@ -509,18 +516,18 @@ const PosixImpl = struct {
509516
510517 fn deinit(self: *Event) void {
511518 // Some platforms reportedly give EINVAL for statically initialized pthread types.
512 const rc = std.c.pthread_cond_destroy(&self.cond);
519 const rc = c.pthread_cond_destroy(&self.cond);
513520 assert(rc == .SUCCESS or rc == .INVAL);
514521
515 const rm = std.c.pthread_mutex_destroy(&self.mutex);
522 const rm = c.pthread_mutex_destroy(&self.mutex);
516523 assert(rm == .SUCCESS or rm == .INVAL);
517524
518525 self.* = undefined;
519526 }
520527
521528 fn wait(self: *Event, timeout: ?u64) error{Timeout}!void {
522 assert(std.c.pthread_mutex_lock(&self.mutex) == .SUCCESS);
523 defer assert(std.c.pthread_mutex_unlock(&self.mutex) == .SUCCESS);
529 assert(c.pthread_mutex_lock(&self.mutex) == .SUCCESS);
530 defer assert(c.pthread_mutex_unlock(&self.mutex) == .SUCCESS);
524531
525532 // Early return if the event was already set.
526533 if (self.state == .notified) {
......@@ -530,9 +537,9 @@ const PosixImpl = struct {
530537 // Compute the absolute timeout if one was specified.
531538 // POSIX requires that REALTIME is used by default for the pthread timedwait functions.
532539 // This can be changed with pthread_condattr_setclock, but it's an extension and may not be available everywhere.
533 var ts: os.timespec = undefined;
540 var ts: c.timespec = undefined;
534541 if (timeout) |timeout_ns| {
535 os.clock_gettime(os.CLOCK.REALTIME, &ts) catch unreachable;
542 c.clock_gettime(c.CLOCK.REALTIME, &ts) catch unreachable;
536543 ts.tv_sec +|= @as(@TypeOf(ts.tv_sec), @intCast(timeout_ns / std.time.ns_per_s));
537544 ts.tv_nsec += @as(@TypeOf(ts.tv_nsec), @intCast(timeout_ns % std.time.ns_per_s));
538545
......@@ -549,8 +556,8 @@ const PosixImpl = struct {
549556 while (true) {
550557 // Block using either pthread_cond_wait or pthread_cond_timewait if there's an absolute timeout.
551558 const rc = blk: {
552 if (timeout == null) break :blk std.c.pthread_cond_wait(&self.cond, &self.mutex);
553 break :blk std.c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts);
559 if (timeout == null) break :blk c.pthread_cond_wait(&self.cond, &self.mutex);
560 break :blk c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts);
554561 };
555562
556563 // After waking up, check if the event was set.
......@@ -574,8 +581,8 @@ const PosixImpl = struct {
574581 }
575582
576583 fn set(self: *Event) void {
577 assert(std.c.pthread_mutex_lock(&self.mutex) == .SUCCESS);
578 defer assert(std.c.pthread_mutex_unlock(&self.mutex) == .SUCCESS);
584 assert(c.pthread_mutex_lock(&self.mutex) == .SUCCESS);
585 defer assert(c.pthread_mutex_unlock(&self.mutex) == .SUCCESS);
579586
580587 // Make sure that multiple calls to set() were not done on the same Event.
581588 const old_state = self.state;
......@@ -586,7 +593,7 @@ const PosixImpl = struct {
586593 // the condition variable once it observes the new state, potentially causing a UAF if done unlocked.
587594 self.state = .notified;
588595 if (old_state == .waiting) {
589 assert(std.c.pthread_cond_signal(&self.cond) == .SUCCESS);
596 assert(c.pthread_cond_signal(&self.cond) == .SUCCESS);
590597 }
591598 }
592599 };
......@@ -732,7 +739,7 @@ const PosixImpl = struct {
732739 };
733740
734741 const Bucket = struct {
735 mutex: std.c.pthread_mutex_t align(atomic.cache_line) = .{},
742 mutex: c.pthread_mutex_t align(atomic.cache_line) = .{},
736743 pending: atomic.Value(usize) = atomic.Value(usize).init(0),
737744 treap: Treap = .{},
738745
......@@ -798,8 +805,8 @@ const PosixImpl = struct {
798805
799806 var waiter: Waiter = undefined;
800807 {
801 assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);
802 defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);
808 assert(c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);
809 defer assert(c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);
803810
804811 cancelled = ptr.load(.monotonic) != expect;
805812 if (cancelled) {
......@@ -821,8 +828,8 @@ const PosixImpl = struct {
821828 // If we return early without waiting, the waiter on the stack would be invalidated and the wake() thread risks a UAF.
822829 defer if (!cancelled) waiter.event.wait(null) catch unreachable;
823830
824 assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);
825 defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);
831 assert(c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);
832 defer assert(c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);
826833
827834 cancelled = WaitQueue.tryRemove(&bucket.treap, address, &waiter);
828835 if (cancelled) {
......@@ -871,8 +878,8 @@ const PosixImpl = struct {
871878 }
872879 };
873880
874 assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);
875 defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);
881 assert(c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);
882 defer assert(c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);
876883
877884 // Another pending check again to avoid the WaitQueue lookup if not necessary.
878885 if (bucket.pending.load(.monotonic) > 0) {
lib/std/Thread/Mutex.zig+12-9
......@@ -23,7 +23,6 @@ const std = @import("../std.zig");
2323const builtin = @import("builtin");
2424const Mutex = @This();
2525
26const os = std.os;
2726const assert = std.debug.assert;
2827const testing = std.testing;
2928const Thread = std.Thread;
......@@ -117,36 +116,40 @@ const SingleThreadedImpl = struct {
117116// SRWLOCK on windows is almost always faster than Futex solution.
118117// It also implements an efficient Condition with requeue support for us.
119118const WindowsImpl = struct {
120 srwlock: os.windows.SRWLOCK = .{},
119 srwlock: windows.SRWLOCK = .{},
121120
122121 fn tryLock(self: *@This()) bool {
123 return os.windows.kernel32.TryAcquireSRWLockExclusive(&self.srwlock) != os.windows.FALSE;
122 return windows.kernel32.TryAcquireSRWLockExclusive(&self.srwlock) != windows.FALSE;
124123 }
125124
126125 fn lock(self: *@This()) void {
127 os.windows.kernel32.AcquireSRWLockExclusive(&self.srwlock);
126 windows.kernel32.AcquireSRWLockExclusive(&self.srwlock);
128127 }
129128
130129 fn unlock(self: *@This()) void {
131 os.windows.kernel32.ReleaseSRWLockExclusive(&self.srwlock);
130 windows.kernel32.ReleaseSRWLockExclusive(&self.srwlock);
132131 }
132
133 const windows = std.os.windows;
133134};
134135
135136// os_unfair_lock on darwin supports priority inheritance and is generally faster than Futex solutions.
136137const DarwinImpl = struct {
137 oul: os.darwin.os_unfair_lock = .{},
138 oul: c.os_unfair_lock = .{},
138139
139140 fn tryLock(self: *@This()) bool {
140 return os.darwin.os_unfair_lock_trylock(&self.oul);
141 return c.os_unfair_lock_trylock(&self.oul);
141142 }
142143
143144 fn lock(self: *@This()) void {
144 os.darwin.os_unfair_lock_lock(&self.oul);
145 c.os_unfair_lock_lock(&self.oul);
145146 }
146147
147148 fn unlock(self: *@This()) void {
148 os.darwin.os_unfair_lock_unlock(&self.oul);
149 c.os_unfair_lock_unlock(&self.oul);
149150 }
151
152 const c = std.c;
150153};
151154
152155const FutexImpl = struct {
lib/std/builtin.zig+3-3
......@@ -782,7 +782,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr
782782 },
783783 .wasi => {
784784 std.debug.print("{s}", .{msg});
785 std.os.abort();
785 std.posix.abort();
786786 },
787787 .uefi => {
788788 const uefi = std.os.uefi;
......@@ -830,9 +830,9 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr
830830 }
831831
832832 // Didn't have boot_services, just fallback to whatever.
833 std.os.abort();
833 std.posix.abort();
834834 },
835 .cuda, .amdhsa => std.os.abort(),
835 .cuda, .amdhsa => std.posix.abort(),
836836 .plan9 => {
837837 var status: [std.os.plan9.ERRMAX]u8 = undefined;
838838 const len = @min(msg.len, status.len - 1);
lib/std/c.zig+25-32
......@@ -2,12 +2,13 @@ const std = @import("std");
22const builtin = @import("builtin");
33const c = @This();
44const page_size = std.mem.page_size;
5const iovec = std.os.iovec;
6const iovec_const = std.os.iovec_const;
5const iovec = std.posix.iovec;
6const iovec_const = std.posix.iovec_const;
77const wasi = @import("c/wasi.zig");
88const native_abi = builtin.abi;
99const native_arch = builtin.cpu.arch;
1010const native_os = builtin.os.tag;
11const linux = std.os.linux;
1112
1213/// If not linking libc, returns false.
1314/// If linking musl libc, returns true.
......@@ -208,7 +209,7 @@ pub const pthread_rwlock_t = switch (native_os) {
208209};
209210
210211pub const AT = switch (native_os) {
211 .linux => std.os.linux.AT,
212 .linux => linux.AT,
212213 .windows => struct {
213214 /// Remove directory instead of unlinking file
214215 pub const REMOVEDIR = 0x200;
......@@ -326,9 +327,9 @@ pub const AT = switch (native_os) {
326327};
327328
328329pub const O = switch (native_os) {
329 .linux => std.os.linux.O,
330 .linux => linux.O,
330331 .emscripten => packed struct(u32) {
331 ACCMODE: std.os.ACCMODE = .RDONLY,
332 ACCMODE: std.posix.ACCMODE = .RDONLY,
332333 _2: u4 = 0,
333334 CREAT: bool = false,
334335 EXCL: bool = false,
......@@ -369,7 +370,7 @@ pub const O = switch (native_os) {
369370 _: u3 = 0,
370371 },
371372 .solaris, .illumos => packed struct(u32) {
372 ACCMODE: std.os.ACCMODE = .RDONLY,
373 ACCMODE: std.posix.ACCMODE = .RDONLY,
373374 NDELAY: bool = false,
374375 APPEND: bool = false,
375376 SYNC: bool = false,
......@@ -396,7 +397,7 @@ pub const O = switch (native_os) {
396397 _: u6 = 0,
397398 },
398399 .netbsd => packed struct(u32) {
399 ACCMODE: std.os.ACCMODE = .RDONLY,
400 ACCMODE: std.posix.ACCMODE = .RDONLY,
400401 NONBLOCK: bool = false,
401402 APPEND: bool = false,
402403 SHLOCK: bool = false,
......@@ -420,7 +421,7 @@ pub const O = switch (native_os) {
420421 _: u8 = 0,
421422 },
422423 .openbsd => packed struct(u32) {
423 ACCMODE: std.os.ACCMODE = .RDONLY,
424 ACCMODE: std.posix.ACCMODE = .RDONLY,
424425 NONBLOCK: bool = false,
425426 APPEND: bool = false,
426427 SHLOCK: bool = false,
......@@ -438,7 +439,7 @@ pub const O = switch (native_os) {
438439 _: u14 = 0,
439440 },
440441 .haiku => packed struct(u32) {
441 ACCMODE: std.os.ACCMODE = .RDONLY,
442 ACCMODE: std.posix.ACCMODE = .RDONLY,
442443 _2: u4 = 0,
443444 CLOEXEC: bool = false,
444445 NONBLOCK: bool = false,
......@@ -458,7 +459,7 @@ pub const O = switch (native_os) {
458459 _: u10 = 0,
459460 },
460461 .macos, .ios, .tvos, .watchos => packed struct(u32) {
461 ACCMODE: std.os.ACCMODE = .RDONLY,
462 ACCMODE: std.posix.ACCMODE = .RDONLY,
462463 NONBLOCK: bool = false,
463464 APPEND: bool = false,
464465 SHLOCK: bool = false,
......@@ -485,7 +486,7 @@ pub const O = switch (native_os) {
485486 POPUP: bool = false,
486487 },
487488 .dragonfly => packed struct(u32) {
488 ACCMODE: std.os.ACCMODE = .RDONLY,
489 ACCMODE: std.posix.ACCMODE = .RDONLY,
489490 NONBLOCK: bool = false,
490491 APPEND: bool = false,
491492 SHLOCK: bool = false,
......@@ -511,7 +512,7 @@ pub const O = switch (native_os) {
511512 _: u4 = 0,
512513 },
513514 .freebsd => packed struct(u32) {
514 ACCMODE: std.os.ACCMODE = .RDONLY,
515 ACCMODE: std.posix.ACCMODE = .RDONLY,
515516 NONBLOCK: bool = false,
516517 APPEND: bool = false,
517518 SHLOCK: bool = false,
......@@ -538,7 +539,7 @@ pub const O = switch (native_os) {
538539};
539540
540541pub const MAP = switch (native_os) {
541 .linux => std.os.linux.MAP,
542 .linux => linux.MAP,
542543 .emscripten => packed struct(u32) {
543544 TYPE: enum(u4) {
544545 SHARED = 0x01,
......@@ -683,7 +684,7 @@ pub const cc_t = u8;
683684
684685/// Indices into the `cc` array in the `termios` struct.
685686pub const V = switch (native_os) {
686 .linux => std.os.linux.V,
687 .linux => linux.V,
687688 .macos, .ios, .tvos, .watchos, .netbsd, .openbsd => enum {
688689 EOF,
689690 EOL,
......@@ -782,7 +783,7 @@ pub const V = switch (native_os) {
782783};
783784
784785pub const NCCS = switch (native_os) {
785 .linux => std.os.linux.NCCS,
786 .linux => linux.NCCS,
786787 .macos, .ios, .tvos, .watchos, .freebsd, .kfreebsd, .netbsd, .openbsd, .dragonfly => 20,
787788 .haiku => 11,
788789 .solaris, .illumos => 19,
......@@ -791,7 +792,7 @@ pub const NCCS = switch (native_os) {
791792};
792793
793794pub const termios = switch (native_os) {
794 .linux => std.os.linux.termios,
795 .linux => linux.termios,
795796 .macos, .ios, .tvos, .watchos => extern struct {
796797 iflag: tc_iflag_t,
797798 oflag: tc_oflag_t,
......@@ -841,7 +842,7 @@ pub const termios = switch (native_os) {
841842};
842843
843844pub const tc_iflag_t = switch (native_os) {
844 .linux => std.os.linux.tc_iflag_t,
845 .linux => linux.tc_iflag_t,
845846 .macos, .ios, .tvos, .watchos => packed struct(u64) {
846847 IGNBRK: bool = false,
847848 BRKINT: bool = false,
......@@ -951,7 +952,7 @@ pub const tc_iflag_t = switch (native_os) {
951952};
952953
953954pub const tc_oflag_t = switch (native_os) {
954 .linux => std.os.linux.tc_oflag_t,
955 .linux => linux.tc_oflag_t,
955956 .macos, .ios, .tvos, .watchos => packed struct(u64) {
956957 OPOST: bool = false,
957958 ONLCR: bool = false,
......@@ -1042,13 +1043,13 @@ pub const tc_oflag_t = switch (native_os) {
10421043};
10431044
10441045pub const CSIZE = switch (native_os) {
1045 .linux => std.os.linux.CSIZE,
1046 .linux => linux.CSIZE,
10461047 .haiku => enum(u1) { CS7, CS8 },
10471048 else => enum(u2) { CS5, CS6, CS7, CS8 },
10481049};
10491050
10501051pub const tc_cflag_t = switch (native_os) {
1051 .linux => std.os.linux.tc_cflag_t,
1052 .linux => linux.tc_cflag_t,
10521053 .macos, .ios, .tvos, .watchos => packed struct(u64) {
10531054 CIGNORE: bool = false,
10541055 _1: u5 = 0,
......@@ -1184,7 +1185,7 @@ pub const tc_cflag_t = switch (native_os) {
11841185};
11851186
11861187pub const tc_lflag_t = switch (native_os) {
1187 .linux => std.os.linux.tc_lflag_t,
1188 .linux => linux.tc_lflag_t,
11881189 .macos, .ios, .tvos, .watchos => packed struct(u64) {
11891190 ECHOKE: bool = false,
11901191 ECHOE: bool = false,
......@@ -1310,7 +1311,7 @@ pub const tc_lflag_t = switch (native_os) {
13101311};
13111312
13121313pub const speed_t = switch (native_os) {
1313 .linux => std.os.linux.speed_t,
1314 .linux => linux.speed_t,
13141315 .macos, .ios, .tvos, .watchos, .openbsd => enum(u64) {
13151316 B0 = 0,
13161317 B50 = 50,
......@@ -1605,14 +1606,6 @@ pub const stat = switch (native_os) {
16051606 else => private.stat,
16061607};
16071608
1608pub fn getErrno(rc: anytype) c.E {
1609 if (rc == -1) {
1610 return @enumFromInt(c._errno().*);
1611 } else {
1612 return .SUCCESS;
1613 }
1614}
1615
16161609pub extern "c" var environ: [*:null]?[*:0]u8;
16171610
16181611pub extern "c" fn fopen(noalias filename: [*:0]const u8, noalias modes: [*:0]const u8) ?*FILE;
......@@ -1905,10 +1898,10 @@ pub extern "c" fn if_nametoindex([*:0]const u8) c_int;
19051898pub const getcontext = if (builtin.target.isAndroid())
19061899 @compileError("android bionic libc does not implement getcontext")
19071900else if (native_os == .linux and builtin.target.isMusl())
1908 std.os.linux.getcontext
1901 linux.getcontext
19091902else
19101903 struct {
1911 extern fn getcontext(ucp: *std.os.ucontext_t) c_int;
1904 extern fn getcontext(ucp: *std.posix.ucontext_t) c_int;
19121905 }.getcontext;
19131906
19141907pub const max_align_t = if (native_abi == .msvc)
lib/std/c/darwin.zig+7-232
......@@ -4,7 +4,7 @@ const assert = std.debug.assert;
44const macho = std.macho;
55const native_arch = builtin.target.cpu.arch;
66const maxInt = std.math.maxInt;
7const iovec_const = std.os.iovec_const;
7const iovec_const = std.posix.iovec_const;
88
99pub const aarch64 = @import("darwin/aarch64.zig");
1010pub const x86_64 = @import("darwin/x86_64.zig");
......@@ -2826,237 +2826,12 @@ pub extern "c" fn posix_spawnp(
28262826 env: [*:null]?[*:0]const u8,
28272827) c_int;
28282828
2829pub const PosixSpawn = struct {
2830 const errno = std.os.errno;
2831 const unexpectedErrno = std.os.unexpectedErrno;
2832
2833 pub const Error = error{
2834 SystemResources,
2835 InvalidFileDescriptor,
2836 NameTooLong,
2837 TooBig,
2838 PermissionDenied,
2839 InputOutput,
2840 FileSystem,
2841 FileNotFound,
2842 InvalidExe,
2843 NotDir,
2844 FileBusy,
2845 /// Returned when the child fails to execute either in the pre-exec() initialization step, or
2846 /// when exec(3) is invoked.
2847 ChildExecFailed,
2848 } || std.os.UnexpectedError;
2849
2850 pub const Attr = struct {
2851 attr: posix_spawnattr_t,
2852
2853 pub fn init() Error!Attr {
2854 var attr: posix_spawnattr_t = undefined;
2855 switch (errno(posix_spawnattr_init(&attr))) {
2856 .SUCCESS => return Attr{ .attr = attr },
2857 .NOMEM => return error.SystemResources,
2858 .INVAL => unreachable,
2859 else => |err| return unexpectedErrno(err),
2860 }
2861 }
2862
2863 pub fn deinit(self: *Attr) void {
2864 defer self.* = undefined;
2865 switch (errno(posix_spawnattr_destroy(&self.attr))) {
2866 .SUCCESS => return,
2867 .INVAL => unreachable, // Invalid parameters.
2868 else => unreachable,
2869 }
2870 }
2871
2872 pub fn get(self: Attr) Error!u16 {
2873 var flags: c_short = undefined;
2874 switch (errno(posix_spawnattr_getflags(&self.attr, &flags))) {
2875 .SUCCESS => return @as(u16, @bitCast(flags)),
2876 .INVAL => unreachable,
2877 else => |err| return unexpectedErrno(err),
2878 }
2879 }
2880
2881 pub fn set(self: *Attr, flags: u16) Error!void {
2882 switch (errno(posix_spawnattr_setflags(&self.attr, @as(c_short, @bitCast(flags))))) {
2883 .SUCCESS => return,
2884 .INVAL => unreachable,
2885 else => |err| return unexpectedErrno(err),
2886 }
2887 }
2888 };
2889
2890 pub const Actions = struct {
2891 actions: posix_spawn_file_actions_t,
2892
2893 pub fn init() Error!Actions {
2894 var actions: posix_spawn_file_actions_t = undefined;
2895 switch (errno(posix_spawn_file_actions_init(&actions))) {
2896 .SUCCESS => return Actions{ .actions = actions },
2897 .NOMEM => return error.SystemResources,
2898 .INVAL => unreachable,
2899 else => |err| return unexpectedErrno(err),
2900 }
2901 }
2902
2903 pub fn deinit(self: *Actions) void {
2904 defer self.* = undefined;
2905 switch (errno(posix_spawn_file_actions_destroy(&self.actions))) {
2906 .SUCCESS => return,
2907 .INVAL => unreachable, // Invalid parameters.
2908 else => unreachable,
2909 }
2910 }
2911
2912 pub fn open(self: *Actions, fd: fd_t, path: []const u8, flags: u32, mode: mode_t) Error!void {
2913 const posix_path = try std.os.toPosixPath(path);
2914 return self.openZ(fd, &posix_path, flags, mode);
2915 }
2916
2917 pub fn openZ(self: *Actions, fd: fd_t, path: [*:0]const u8, flags: u32, mode: mode_t) Error!void {
2918 switch (errno(posix_spawn_file_actions_addopen(&self.actions, fd, path, @as(c_int, @bitCast(flags)), mode))) {
2919 .SUCCESS => return,
2920 .BADF => return error.InvalidFileDescriptor,
2921 .NOMEM => return error.SystemResources,
2922 .NAMETOOLONG => return error.NameTooLong,
2923 .INVAL => unreachable, // the value of file actions is invalid
2924 else => |err| return unexpectedErrno(err),
2925 }
2926 }
2927
2928 pub fn close(self: *Actions, fd: fd_t) Error!void {
2929 switch (errno(posix_spawn_file_actions_addclose(&self.actions, fd))) {
2930 .SUCCESS => return,
2931 .BADF => return error.InvalidFileDescriptor,
2932 .NOMEM => return error.SystemResources,
2933 .INVAL => unreachable, // the value of file actions is invalid
2934 .NAMETOOLONG => unreachable,
2935 else => |err| return unexpectedErrno(err),
2936 }
2937 }
2938
2939 pub fn dup2(self: *Actions, fd: fd_t, newfd: fd_t) Error!void {
2940 switch (errno(posix_spawn_file_actions_adddup2(&self.actions, fd, newfd))) {
2941 .SUCCESS => return,
2942 .BADF => return error.InvalidFileDescriptor,
2943 .NOMEM => return error.SystemResources,
2944 .INVAL => unreachable, // the value of file actions is invalid
2945 .NAMETOOLONG => unreachable,
2946 else => |err| return unexpectedErrno(err),
2947 }
2948 }
2949
2950 pub fn inherit(self: *Actions, fd: fd_t) Error!void {
2951 switch (errno(posix_spawn_file_actions_addinherit_np(&self.actions, fd))) {
2952 .SUCCESS => return,
2953 .BADF => return error.InvalidFileDescriptor,
2954 .NOMEM => return error.SystemResources,
2955 .INVAL => unreachable, // the value of file actions is invalid
2956 .NAMETOOLONG => unreachable,
2957 else => |err| return unexpectedErrno(err),
2958 }
2959 }
2960
2961 pub fn chdir(self: *Actions, path: []const u8) Error!void {
2962 const posix_path = try std.os.toPosixPath(path);
2963 return self.chdirZ(&posix_path);
2964 }
2965
2966 pub fn chdirZ(self: *Actions, path: [*:0]const u8) Error!void {
2967 switch (errno(posix_spawn_file_actions_addchdir_np(&self.actions, path))) {
2968 .SUCCESS => return,
2969 .NOMEM => return error.SystemResources,
2970 .NAMETOOLONG => return error.NameTooLong,
2971 .BADF => unreachable,
2972 .INVAL => unreachable, // the value of file actions is invalid
2973 else => |err| return unexpectedErrno(err),
2974 }
2975 }
2976
2977 pub fn fchdir(self: *Actions, fd: fd_t) Error!void {
2978 switch (errno(posix_spawn_file_actions_addfchdir_np(&self.actions, fd))) {
2979 .SUCCESS => return,
2980 .BADF => return error.InvalidFileDescriptor,
2981 .NOMEM => return error.SystemResources,
2982 .INVAL => unreachable, // the value of file actions is invalid
2983 .NAMETOOLONG => unreachable,
2984 else => |err| return unexpectedErrno(err),
2985 }
2986 }
2987 };
2988
2989 pub fn spawn(
2990 path: []const u8,
2991 actions: ?Actions,
2992 attr: ?Attr,
2993 argv: [*:null]?[*:0]const u8,
2994 envp: [*:null]?[*:0]const u8,
2995 ) Error!pid_t {
2996 const posix_path = try std.os.toPosixPath(path);
2997 return spawnZ(&posix_path, actions, attr, argv, envp);
2998 }
2999
3000 pub fn spawnZ(
3001 path: [*:0]const u8,
3002 actions: ?Actions,
3003 attr: ?Attr,
3004 argv: [*:null]?[*:0]const u8,
3005 envp: [*:null]?[*:0]const u8,
3006 ) Error!pid_t {
3007 var pid: pid_t = undefined;
3008 switch (errno(posix_spawn(
3009 &pid,
3010 path,
3011 if (actions) |a| &a.actions else null,
3012 if (attr) |a| &a.attr else null,
3013 argv,
3014 envp,
3015 ))) {
3016 .SUCCESS => return pid,
3017 .@"2BIG" => return error.TooBig,
3018 .NOMEM => return error.SystemResources,
3019 .BADF => return error.InvalidFileDescriptor,
3020 .ACCES => return error.PermissionDenied,
3021 .IO => return error.InputOutput,
3022 .LOOP => return error.FileSystem,
3023 .NAMETOOLONG => return error.NameTooLong,
3024 .NOENT => return error.FileNotFound,
3025 .NOEXEC => return error.InvalidExe,
3026 .NOTDIR => return error.NotDir,
3027 .TXTBSY => return error.FileBusy,
3028 .BADARCH => return error.InvalidExe,
3029 .BADEXEC => return error.InvalidExe,
3030 .FAULT => unreachable,
3031 .INVAL => unreachable,
3032 else => |err| return unexpectedErrno(err),
3033 }
3034 }
3035
3036 pub fn waitpid(pid: pid_t, flags: u32) Error!std.os.WaitPidResult {
3037 var status: c_int = undefined;
3038 while (true) {
3039 const rc = waitpid(pid, &status, @as(c_int, @intCast(flags)));
3040 switch (errno(rc)) {
3041 .SUCCESS => return std.os.WaitPidResult{
3042 .pid = @as(pid_t, @intCast(rc)),
3043 .status = @as(u32, @bitCast(status)),
3044 },
3045 .INTR => continue,
3046 .CHILD => return error.ChildExecFailed,
3047 .INVAL => unreachable, // Invalid flags.
3048 else => unreachable,
3049 }
3050 }
3051 }
3052};
3053
30542829pub fn getKernError(err: kern_return_t) KernE {
30552830 return @as(KernE, @enumFromInt(@as(u32, @truncate(@as(usize, @intCast(err))))));
30562831}
30572832
3058pub fn unexpectedKernError(err: KernE) std.os.UnexpectedError {
3059 if (std.os.unexpected_error_tracing) {
2833pub fn unexpectedKernError(err: KernE) std.posix.UnexpectedError {
2834 if (std.posix.unexpected_error_tracing) {
30602835 std.debug.print("unexpected error: {d}\n", .{@intFromEnum(err)});
30612836 std.debug.dumpCurrentStackTrace(null);
30622837 }
......@@ -3067,7 +2842,7 @@ pub const MachError = error{
30672842 /// Not enough permissions held to perform the requested kernel
30682843 /// call.
30692844 PermissionDenied,
3070} || std.os.UnexpectedError;
2845} || std.posix.UnexpectedError;
30712846
30722847pub const MachTask = extern struct {
30732848 port: mach_port_name_t,
......@@ -3076,8 +2851,8 @@ pub const MachTask = extern struct {
30762851 return self.port != TASK_NULL;
30772852 }
30782853
3079 pub fn pidForTask(self: MachTask) MachError!std.os.pid_t {
3080 var pid: std.os.pid_t = undefined;
2854 pub fn pidForTask(self: MachTask) MachError!std.c.pid_t {
2855 var pid: std.c.pid_t = undefined;
30812856 switch (getKernError(pid_for_task(self.port, &pid))) {
30822857 .SUCCESS => return pid,
30832858 .FAILURE => return error.PermissionDenied,
......@@ -3517,7 +3292,7 @@ pub const MachThread = extern struct {
35173292 }
35183293};
35193294
3520pub fn machTaskForPid(pid: std.os.pid_t) MachError!MachTask {
3295pub fn machTaskForPid(pid: std.c.pid_t) MachError!MachTask {
35213296 var port: mach_port_name_t = undefined;
35223297 switch (getKernError(task_for_pid(mach_task_self(), pid, &port))) {
35233298 .SUCCESS => {},
lib/std/c/dragonfly.zig+1-1
......@@ -2,7 +2,7 @@ const builtin = @import("builtin");
22const std = @import("../std.zig");
33const assert = std.debug.assert;
44const maxInt = std.math.maxInt;
5const iovec = std.os.iovec;
5const iovec = std.posix.iovec;
66
77extern "c" threadlocal var errno: c_int;
88pub fn _errno() *c_int {
lib/std/c/freebsd.zig+2-2
......@@ -2,8 +2,8 @@ const std = @import("../std.zig");
22const assert = std.debug.assert;
33const builtin = @import("builtin");
44const maxInt = std.math.maxInt;
5const iovec = std.os.iovec;
6const iovec_const = std.os.iovec_const;
5const iovec = std.posix.iovec;
6const iovec_const = std.posix.iovec_const;
77
88extern "c" fn __error() *c_int;
99pub const _errno = __error;
lib/std/c/haiku.zig+2-2
......@@ -2,8 +2,8 @@ const std = @import("../std.zig");
22const assert = std.debug.assert;
33const builtin = @import("builtin");
44const maxInt = std.math.maxInt;
5const iovec = std.os.iovec;
6const iovec_const = std.os.iovec_const;
5const iovec = std.posix.iovec;
6const iovec_const = std.posix.iovec_const;
77
88extern "c" fn _errnop() *c_int;
99
lib/std/c/linux.zig+2-2
......@@ -3,8 +3,8 @@ const builtin = @import("builtin");
33const native_abi = builtin.abi;
44const native_arch = builtin.cpu.arch;
55const linux = std.os.linux;
6const iovec = std.os.iovec;
7const iovec_const = std.os.iovec_const;
6const iovec = std.posix.iovec;
7const iovec_const = std.posix.iovec_const;
88const FILE = std.c.FILE;
99
1010pub const AF = linux.AF;
lib/std/c/netbsd.zig+2-2
......@@ -2,8 +2,8 @@ const std = @import("../std.zig");
22const assert = std.debug.assert;
33const builtin = @import("builtin");
44const maxInt = std.math.maxInt;
5const iovec = std.os.iovec;
6const iovec_const = std.os.iovec_const;
5const iovec = std.posix.iovec;
6const iovec_const = std.posix.iovec_const;
77const timezone = std.c.timezone;
88const rusage = std.c.rusage;
99
lib/std/c/openbsd.zig+2-2
......@@ -2,8 +2,8 @@ const std = @import("../std.zig");
22const assert = std.debug.assert;
33const maxInt = std.math.maxInt;
44const builtin = @import("builtin");
5const iovec = std.os.iovec;
6const iovec_const = std.os.iovec_const;
5const iovec = std.posix.iovec;
6const iovec_const = std.posix.iovec_const;
77
88extern "c" fn __errno() *c_int;
99pub const _errno = __errno;
lib/std/c/solaris.zig+2-2
......@@ -2,8 +2,8 @@ const std = @import("../std.zig");
22const assert = std.debug.assert;
33const builtin = @import("builtin");
44const maxInt = std.math.maxInt;
5const iovec = std.os.iovec;
6const iovec_const = std.os.iovec_const;
5const iovec = std.posix.iovec;
6const iovec_const = std.posix.iovec_const;
77const timezone = std.c.timezone;
88
99extern "c" fn ___errno() *c_int;
lib/std/child_process.zig+91-90
......@@ -3,30 +3,31 @@ const builtin = @import("builtin");
33const unicode = std.unicode;
44const io = std.io;
55const fs = std.fs;
6const os = std.os;
76const process = std.process;
87const File = std.fs.File;
9const windows = os.windows;
10const linux = os.linux;
8const windows = std.os.windows;
9const linux = std.os.linux;
10const posix = std.posix;
1111const mem = std.mem;
1212const math = std.math;
1313const debug = std.debug;
1414const EnvMap = process.EnvMap;
1515const maxInt = std.math.maxInt;
1616const assert = std.debug.assert;
17const native_os = builtin.os.tag;
1718
1819pub const ChildProcess = struct {
19 pub const Id = switch (builtin.os.tag) {
20 pub const Id = switch (native_os) {
2021 .windows => windows.HANDLE,
2122 .wasi => void,
22 else => os.pid_t,
23 else => posix.pid_t,
2324 };
2425
2526 /// Available after calling `spawn()`. This becomes `undefined` after calling `wait()`.
2627 /// On Windows this is the hProcess.
2728 /// On POSIX this is the pid.
2829 id: Id,
29 thread_handle: if (builtin.os.tag == .windows) windows.HANDLE else void,
30 thread_handle: if (native_os == .windows) windows.HANDLE else void,
3031
3132 allocator: mem.Allocator,
3233
......@@ -46,10 +47,10 @@ pub const ChildProcess = struct {
4647 stderr_behavior: StdIo,
4748
4849 /// Set to change the user id when spawning the child process.
49 uid: if (builtin.os.tag == .windows or builtin.os.tag == .wasi) void else ?os.uid_t,
50 uid: if (native_os == .windows or native_os == .wasi) void else ?posix.uid_t,
5051
5152 /// Set to change the group id when spawning the child process.
52 gid: if (builtin.os.tag == .windows or builtin.os.tag == .wasi) void else ?os.gid_t,
53 gid: if (native_os == .windows or native_os == .wasi) void else ?posix.gid_t,
5354
5455 /// Set to change the current working directory when spawning the child process.
5556 cwd: ?[]const u8,
......@@ -58,7 +59,7 @@ pub const ChildProcess = struct {
5859 /// Once that is done, `cwd` will be deprecated in favor of this field.
5960 cwd_dir: ?fs.Dir = null,
6061
61 err_pipe: ?if (builtin.os.tag == .windows) void else [2]os.fd_t,
62 err_pipe: ?if (native_os == .windows) void else [2]posix.fd_t,
6263
6364 expand_arg0: Arg0Expand,
6465
......@@ -87,7 +88,7 @@ pub const ChildProcess = struct {
8788 /// Returns the peak resident set size of the child process, in bytes,
8889 /// if available.
8990 pub inline fn getMaxRss(rus: ResourceUsageStatistics) ?usize {
90 switch (builtin.os.tag) {
91 switch (native_os) {
9192 .linux => {
9293 if (rus.rusage) |ru| {
9394 return @as(usize, @intCast(ru.maxrss)) * 1024;
......@@ -114,14 +115,14 @@ pub const ChildProcess = struct {
114115 }
115116 }
116117
117 const rusage_init = switch (builtin.os.tag) {
118 .linux, .macos, .ios => @as(?std.os.rusage, null),
118 const rusage_init = switch (native_os) {
119 .linux, .macos, .ios => @as(?posix.rusage, null),
119120 .windows => @as(?windows.VM_COUNTERS, null),
120121 else => {},
121122 };
122123 };
123124
124 pub const Arg0Expand = os.Arg0Expand;
125 pub const Arg0Expand = posix.Arg0Expand;
125126
126127 pub const SpawnError = error{
127128 OutOfMemory,
......@@ -136,9 +137,9 @@ pub const ChildProcess = struct {
136137 /// Windows-only. `cwd` was provided, but the path did not exist when spawning the child process.
137138 CurrentWorkingDirectoryUnlinked,
138139 } ||
139 os.ExecveError ||
140 os.SetIdError ||
141 os.ChangeCurDirError ||
140 posix.ExecveError ||
141 posix.SetIdError ||
142 posix.ChangeCurDirError ||
142143 windows.CreateProcessError ||
143144 windows.GetProcessMemoryInfoError ||
144145 windows.WaitForSingleObjectError;
......@@ -168,8 +169,8 @@ pub const ChildProcess = struct {
168169 .term = null,
169170 .env_map = null,
170171 .cwd = null,
171 .uid = if (builtin.os.tag == .windows or builtin.os.tag == .wasi) {} else null,
172 .gid = if (builtin.os.tag == .windows or builtin.os.tag == .wasi) {} else null,
172 .uid = if (native_os == .windows or native_os == .wasi) {} else null,
173 .gid = if (native_os == .windows or native_os == .wasi) {} else null,
173174 .stdin = null,
174175 .stdout = null,
175176 .stderr = null,
......@@ -193,7 +194,7 @@ pub const ChildProcess = struct {
193194 @compileError("the target operating system cannot spawn processes");
194195 }
195196
196 if (builtin.os.tag == .windows) {
197 if (native_os == .windows) {
197198 return self.spawnWindows();
198199 } else {
199200 return self.spawnPosix();
......@@ -207,7 +208,7 @@ pub const ChildProcess = struct {
207208
208209 /// Forcibly terminates child process and then cleans up all resources.
209210 pub fn kill(self: *ChildProcess) !Term {
210 if (builtin.os.tag == .windows) {
211 if (native_os == .windows) {
211212 return self.killWindows(1);
212213 } else {
213214 return self.killPosix();
......@@ -241,7 +242,7 @@ pub const ChildProcess = struct {
241242 self.cleanupStreams();
242243 return term;
243244 }
244 os.kill(self.id, os.SIG.TERM) catch |err| switch (err) {
245 posix.kill(self.id, posix.SIG.TERM) catch |err| switch (err) {
245246 error.ProcessNotFound => return error.AlreadyTerminated,
246247 else => return err,
247248 };
......@@ -251,7 +252,7 @@ pub const ChildProcess = struct {
251252
252253 /// Blocks until child process terminates and then cleans up all resources.
253254 pub fn wait(self: *ChildProcess) !Term {
254 const term = if (builtin.os.tag == .windows)
255 const term = if (native_os == .windows)
255256 try self.waitWindows()
256257 else
257258 try self.waitPosix();
......@@ -318,7 +319,7 @@ pub const ChildProcess = struct {
318319 stderr.* = fifoToOwnedArrayList(poller.fifo(.stderr));
319320 }
320321
321 pub const RunError = os.GetCwdError || os.ReadError || SpawnError || os.PollError || error{
322 pub const RunError = posix.GetCwdError || posix.ReadError || SpawnError || posix.PollError || error{
322323 StdoutStreamTooLong,
323324 StderrStreamTooLong,
324325 };
......@@ -396,19 +397,19 @@ pub const ChildProcess = struct {
396397 self.resource_usage_statistics.rusage = try windows.GetProcessMemoryInfo(self.id);
397398 }
398399
399 os.close(self.id);
400 os.close(self.thread_handle);
400 posix.close(self.id);
401 posix.close(self.thread_handle);
401402 self.cleanupStreams();
402403 return result;
403404 }
404405
405406 fn waitUnwrapped(self: *ChildProcess) !void {
406 const res: os.WaitPidResult = res: {
407 const res: posix.WaitPidResult = res: {
407408 if (self.request_resource_usage_statistics) {
408 switch (builtin.os.tag) {
409 switch (native_os) {
409410 .linux, .macos, .ios => {
410 var ru: std.os.rusage = undefined;
411 const res = os.wait4(self.id, 0, &ru);
411 var ru: posix.rusage = undefined;
412 const res = posix.wait4(self.id, 0, &ru);
412413 self.resource_usage_statistics.rusage = ru;
413414 break :res res;
414415 },
......@@ -416,7 +417,7 @@ pub const ChildProcess = struct {
416417 }
417418 }
418419
419 break :res os.waitpid(self.id, 0);
420 break :res posix.waitpid(self.id, 0);
420421 };
421422 const status = res.status;
422423 self.cleanupStreams();
......@@ -446,20 +447,20 @@ pub const ChildProcess = struct {
446447 if (self.err_pipe) |err_pipe| {
447448 defer destroyPipe(err_pipe);
448449
449 if (builtin.os.tag == .linux) {
450 var fd = [1]std.os.pollfd{std.os.pollfd{
450 if (native_os == .linux) {
451 var fd = [1]posix.pollfd{posix.pollfd{
451452 .fd = err_pipe[0],
452 .events = std.os.POLL.IN,
453 .events = posix.POLL.IN,
453454 .revents = undefined,
454455 }};
455456
456457 // Check if the eventfd buffer stores a non-zero value by polling
457458 // it, that's the error code returned by the child process.
458 _ = std.os.poll(&fd, 0) catch unreachable;
459 _ = posix.poll(&fd, 0) catch unreachable;
459460
460461 // According to eventfd(2) the descriptor is readable if the counter
461462 // has a value greater than 0
462 if ((fd[0].revents & std.os.POLL.IN) != 0) {
463 if ((fd[0].revents & posix.POLL.IN) != 0) {
463464 const err_int = try readIntFd(err_pipe[0]);
464465 return @as(SpawnError, @errorCast(@errorFromInt(err_int)));
465466 }
......@@ -483,36 +484,36 @@ pub const ChildProcess = struct {
483484 }
484485
485486 fn statusToTerm(status: u32) Term {
486 return if (os.W.IFEXITED(status))
487 Term{ .Exited = os.W.EXITSTATUS(status) }
488 else if (os.W.IFSIGNALED(status))
489 Term{ .Signal = os.W.TERMSIG(status) }
490 else if (os.W.IFSTOPPED(status))
491 Term{ .Stopped = os.W.STOPSIG(status) }
487 return if (posix.W.IFEXITED(status))
488 Term{ .Exited = posix.W.EXITSTATUS(status) }
489 else if (posix.W.IFSIGNALED(status))
490 Term{ .Signal = posix.W.TERMSIG(status) }
491 else if (posix.W.IFSTOPPED(status))
492 Term{ .Stopped = posix.W.STOPSIG(status) }
492493 else
493494 Term{ .Unknown = status };
494495 }
495496
496497 fn spawnPosix(self: *ChildProcess) SpawnError!void {
497 const pipe_flags: os.O = .{};
498 const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined;
498 const pipe_flags: posix.O = .{};
499 const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try posix.pipe2(pipe_flags) else undefined;
499500 errdefer if (self.stdin_behavior == StdIo.Pipe) {
500501 destroyPipe(stdin_pipe);
501502 };
502503
503 const stdout_pipe = if (self.stdout_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined;
504 const stdout_pipe = if (self.stdout_behavior == StdIo.Pipe) try posix.pipe2(pipe_flags) else undefined;
504505 errdefer if (self.stdout_behavior == StdIo.Pipe) {
505506 destroyPipe(stdout_pipe);
506507 };
507508
508 const stderr_pipe = if (self.stderr_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined;
509 const stderr_pipe = if (self.stderr_behavior == StdIo.Pipe) try posix.pipe2(pipe_flags) else undefined;
509510 errdefer if (self.stderr_behavior == StdIo.Pipe) {
510511 destroyPipe(stderr_pipe);
511512 };
512513
513514 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
514515 const dev_null_fd = if (any_ignore)
515 os.openZ("/dev/null", .{ .ACCMODE = .RDWR }, 0) catch |err| switch (err) {
516 posix.openZ("/dev/null", .{ .ACCMODE = .RDWR }, 0) catch |err| switch (err) {
516517 error.PathAlreadyExists => unreachable,
517518 error.NoSpaceLeft => unreachable,
518519 error.FileTooBig => unreachable,
......@@ -526,7 +527,7 @@ pub const ChildProcess = struct {
526527 else
527528 undefined;
528529 defer {
529 if (any_ignore) os.close(dev_null_fd);
530 if (any_ignore) posix.close(dev_null_fd);
530531 }
531532
532533 var arena_allocator = std.heap.ArenaAllocator.init(self.allocator);
......@@ -554,7 +555,7 @@ pub const ChildProcess = struct {
554555 } else if (builtin.output_mode == .Exe) {
555556 // Then we have Zig start code and this works.
556557 // TODO type-safety for null-termination of `os.environ`.
557 break :m @as([*:null]const ?[*:0]const u8, @ptrCast(os.environ.ptr));
558 break :m @as([*:null]const ?[*:0]const u8, @ptrCast(std.os.environ.ptr));
558559 } else {
559560 // TODO come up with a solution for this.
560561 @compileError("missing std lib enhancement: ChildProcess implementation has no way to collect the environment variables to forward to the child process");
......@@ -564,60 +565,60 @@ pub const ChildProcess = struct {
564565 // This pipe is used to communicate errors between the time of fork
565566 // and execve from the child process to the parent process.
566567 const err_pipe = blk: {
567 if (builtin.os.tag == .linux) {
568 const fd = try os.eventfd(0, linux.EFD.CLOEXEC);
568 if (native_os == .linux) {
569 const fd = try posix.eventfd(0, linux.EFD.CLOEXEC);
569570 // There's no distinction between the readable and the writeable
570571 // end with eventfd
571 break :blk [2]os.fd_t{ fd, fd };
572 break :blk [2]posix.fd_t{ fd, fd };
572573 } else {
573 break :blk try os.pipe2(.{ .CLOEXEC = true });
574 break :blk try posix.pipe2(.{ .CLOEXEC = true });
574575 }
575576 };
576577 errdefer destroyPipe(err_pipe);
577578
578 const pid_result = try os.fork();
579 const pid_result = try posix.fork();
579580 if (pid_result == 0) {
580581 // we are the child
581 setUpChildIo(self.stdin_behavior, stdin_pipe[0], os.STDIN_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
582 setUpChildIo(self.stdout_behavior, stdout_pipe[1], os.STDOUT_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
583 setUpChildIo(self.stderr_behavior, stderr_pipe[1], os.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
582 setUpChildIo(self.stdin_behavior, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
583 setUpChildIo(self.stdout_behavior, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
584 setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
584585
585586 if (self.stdin_behavior == .Pipe) {
586 os.close(stdin_pipe[0]);
587 os.close(stdin_pipe[1]);
587 posix.close(stdin_pipe[0]);
588 posix.close(stdin_pipe[1]);
588589 }
589590 if (self.stdout_behavior == .Pipe) {
590 os.close(stdout_pipe[0]);
591 os.close(stdout_pipe[1]);
591 posix.close(stdout_pipe[0]);
592 posix.close(stdout_pipe[1]);
592593 }
593594 if (self.stderr_behavior == .Pipe) {
594 os.close(stderr_pipe[0]);
595 os.close(stderr_pipe[1]);
595 posix.close(stderr_pipe[0]);
596 posix.close(stderr_pipe[1]);
596597 }
597598
598599 if (self.cwd_dir) |cwd| {
599 os.fchdir(cwd.fd) catch |err| forkChildErrReport(err_pipe[1], err);
600 posix.fchdir(cwd.fd) catch |err| forkChildErrReport(err_pipe[1], err);
600601 } else if (self.cwd) |cwd| {
601 os.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err);
602 posix.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err);
602603 }
603604
604605 if (self.gid) |gid| {
605 os.setregid(gid, gid) catch |err| forkChildErrReport(err_pipe[1], err);
606 posix.setregid(gid, gid) catch |err| forkChildErrReport(err_pipe[1], err);
606607 }
607608
608609 if (self.uid) |uid| {
609 os.setreuid(uid, uid) catch |err| forkChildErrReport(err_pipe[1], err);
610 posix.setreuid(uid, uid) catch |err| forkChildErrReport(err_pipe[1], err);
610611 }
611612
612613 const err = switch (self.expand_arg0) {
613 .expand => os.execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
614 .no_expand => os.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
614 .expand => posix.execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
615 .no_expand => posix.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
615616 };
616617 forkChildErrReport(err_pipe[1], err);
617618 }
618619
619620 // we are the parent
620 const pid = @as(i32, @intCast(pid_result));
621 const pid: i32 = @intCast(pid_result);
621622 if (self.stdin_behavior == StdIo.Pipe) {
622623 self.stdin = File{ .handle = stdin_pipe[1] };
623624 } else {
......@@ -639,13 +640,13 @@ pub const ChildProcess = struct {
639640 self.term = null;
640641
641642 if (self.stdin_behavior == StdIo.Pipe) {
642 os.close(stdin_pipe[0]);
643 posix.close(stdin_pipe[0]);
643644 }
644645 if (self.stdout_behavior == StdIo.Pipe) {
645 os.close(stdout_pipe[1]);
646 posix.close(stdout_pipe[1]);
646647 }
647648 if (self.stderr_behavior == StdIo.Pipe) {
648 os.close(stderr_pipe[1]);
649 posix.close(stderr_pipe[1]);
649650 }
650651 }
651652
......@@ -679,7 +680,7 @@ pub const ChildProcess = struct {
679680 else
680681 undefined;
681682 defer {
682 if (any_ignore) os.close(nul_handle);
683 if (any_ignore) posix.close(nul_handle);
683684 }
684685
685686 var g_hChildStd_IN_Rd: ?windows.HANDLE = null;
......@@ -821,8 +822,8 @@ pub const ChildProcess = struct {
821822 defer self.allocator.free(cmd_line_w);
822823
823824 run: {
824 const PATH: [:0]const u16 = std.os.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse &[_:0]u16{};
825 const PATHEXT: [:0]const u16 = std.os.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATHEXT")) orelse &[_:0]u16{};
825 const PATH: [:0]const u16 = std.process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse &[_:0]u16{};
826 const PATHEXT: [:0]const u16 = std.process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATHEXT")) orelse &[_:0]u16{};
826827
827828 var app_buf = std.ArrayListUnmanaged(u16){};
828829 defer app_buf.deinit(self.allocator);
......@@ -905,22 +906,22 @@ pub const ChildProcess = struct {
905906 self.term = null;
906907
907908 if (self.stdin_behavior == StdIo.Pipe) {
908 os.close(g_hChildStd_IN_Rd.?);
909 posix.close(g_hChildStd_IN_Rd.?);
909910 }
910911 if (self.stderr_behavior == StdIo.Pipe) {
911 os.close(g_hChildStd_ERR_Wr.?);
912 posix.close(g_hChildStd_ERR_Wr.?);
912913 }
913914 if (self.stdout_behavior == StdIo.Pipe) {
914 os.close(g_hChildStd_OUT_Wr.?);
915 posix.close(g_hChildStd_OUT_Wr.?);
915916 }
916917 }
917918
918919 fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void {
919920 switch (stdio) {
920 .Pipe => try os.dup2(pipe_fd, std_fileno),
921 .Close => os.close(std_fileno),
921 .Pipe => try posix.dup2(pipe_fd, std_fileno),
922 .Close => posix.close(std_fileno),
922923 .Inherit => {},
923 .Ignore => try os.dup2(dev_null_fd, std_fileno),
924 .Ignore => try posix.dup2(dev_null_fd, std_fileno),
924925 }
925926 }
926927};
......@@ -987,7 +988,7 @@ fn windowsCreateProcessPathExt(
987988
988989 // This 2048 is arbitrary, we just want it to be large enough to get multiple FILE_DIRECTORY_INFORMATION entries
989990 // returned per NtQueryDirectoryFile call.
990 var file_information_buf: [2048]u8 align(@alignOf(os.windows.FILE_DIRECTORY_INFORMATION)) = undefined;
991 var file_information_buf: [2048]u8 align(@alignOf(windows.FILE_DIRECTORY_INFORMATION)) = undefined;
991992 const file_info_maximum_single_entry_size = @sizeOf(windows.FILE_DIRECTORY_INFORMATION) + (windows.NAME_MAX * 2);
992993 if (file_information_buf.len < file_info_maximum_single_entry_size) {
993994 @compileError("file_information_buf must be large enough to contain at least one maximum size FILE_DIRECTORY_INFORMATION entry");
......@@ -1391,8 +1392,8 @@ fn testArgvToCommandLineWindows(argv: []const []const u8, expected_cmd_line: []c
13911392}
13921393
13931394fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void {
1394 if (rd) |h| os.close(h);
1395 if (wr) |h| os.close(h);
1395 if (rd) |h| posix.close(h);
1396 if (wr) |h| posix.close(h);
13961397}
13971398
13981399fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void {
......@@ -1443,7 +1444,7 @@ fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *cons
14431444 else => |err| return windows.unexpectedError(err),
14441445 }
14451446 }
1446 errdefer os.close(read_handle);
1447 errdefer posix.close(read_handle);
14471448
14481449 var sattr_copy = sattr.*;
14491450 const write_handle = windows.kernel32.CreateFileW(
......@@ -1460,7 +1461,7 @@ fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *cons
14601461 else => |err| return windows.unexpectedError(err),
14611462 }
14621463 }
1463 errdefer os.close(write_handle);
1464 errdefer posix.close(write_handle);
14641465
14651466 try windows.SetHandleInformation(read_handle, windows.HANDLE_FLAG_INHERIT, 0);
14661467
......@@ -1468,9 +1469,9 @@ fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *cons
14681469 wr.* = write_handle;
14691470}
14701471
1471fn destroyPipe(pipe: [2]os.fd_t) void {
1472 os.close(pipe[0]);
1473 if (pipe[0] != pipe[1]) os.close(pipe[1]);
1472fn destroyPipe(pipe: [2]posix.fd_t) void {
1473 posix.close(pipe[0]);
1474 if (pipe[0] != pipe[1]) posix.close(pipe[1]);
14741475}
14751476
14761477// Child of fork calls this to report an error to the fork parent.
......@@ -1485,7 +1486,7 @@ fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
14851486 // The _exit(2) function does nothing but make the exit syscall, unlike exit(3)
14861487 std.c._exit(1);
14871488 }
1488 os.exit(1);
1489 posix.exit(1);
14891490}
14901491
14911492const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8);
lib/std/crypto/Certificate/Bundle.zig+1-1
......@@ -125,7 +125,7 @@ fn rescanBSD(cb: *Bundle, gpa: Allocator, cert_file_path: []const u8) RescanBSDE
125125 cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);
126126}
127127
128const RescanWindowsError = Allocator.Error || ParseCertError || std.os.UnexpectedError || error{FileNotFound};
128const RescanWindowsError = Allocator.Error || ParseCertError || std.posix.UnexpectedError || error{FileNotFound};
129129
130130fn rescanWindows(cb: *Bundle, gpa: Allocator) RescanWindowsError!void {
131131 cb.bytes.clearRetainingCapacity();
lib/std/crypto/Certificate/Bundle/macos.zig+1-1
......@@ -42,7 +42,7 @@ pub fn rescanMac(cb: *Bundle, gpa: Allocator) RescanMacError!void {
4242
4343 const table_header = try reader.readStructEndian(TableHeader, .big);
4444
45 if (@as(std.os.darwin.cssm.DB_RECORDTYPE, @enumFromInt(table_header.table_id)) != .X509_CERTIFICATE) {
45 if (@as(std.c.cssm.DB_RECORDTYPE, @enumFromInt(table_header.table_id)) != .X509_CERTIFICATE) {
4646 continue;
4747 }
4848
lib/std/crypto/tlcsprng.zig+9-8
......@@ -6,7 +6,8 @@
66const std = @import("std");
77const builtin = @import("builtin");
88const mem = std.mem;
9const os = std.os;
9const native_os = builtin.os.tag;
10const posix = std.posix;
1011
1112/// We use this as a layer of indirection because global const pointers cannot
1213/// point to thread-local variables.
......@@ -15,7 +16,7 @@ pub const interface = std.Random{
1516 .fillFn = tlsCsprngFill,
1617};
1718
18const os_has_fork = switch (builtin.os.tag) {
19const os_has_fork = switch (native_os) {
1920 .dragonfly,
2021 .freebsd,
2122 .ios,
......@@ -41,7 +42,7 @@ const maybe_have_wipe_on_fork = builtin.os.isAtLeast(.linux, .{
4142 .minor = 14,
4243 .patch = 0,
4344}) orelse true;
44const is_haiku = builtin.os.tag == .haiku;
45const is_haiku = native_os == .haiku;
4546
4647const Rng = std.Random.DefaultCsprng;
4748
......@@ -79,10 +80,10 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
7980 if (want_fork_safety and maybe_have_wipe_on_fork or is_haiku) {
8081 // Allocate a per-process page, madvise operates with page
8182 // granularity.
82 wipe_mem = os.mmap(
83 wipe_mem = posix.mmap(
8384 null,
8485 @sizeOf(Context),
85 os.PROT.READ | os.PROT.WRITE,
86 posix.PROT.READ | posix.PROT.WRITE,
8687 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
8788 -1,
8889 0,
......@@ -115,11 +116,11 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
115116 // Qemu user-mode emulation ignores any valid/invalid madvise
116117 // hint and returns success. Check if this is the case by
117118 // passing bogus parameters, we expect EINVAL as result.
118 if (os.madvise(wipe_mem.ptr, 0, 0xffffffff)) |_| {
119 if (posix.madvise(wipe_mem.ptr, 0, 0xffffffff)) |_| {
119120 break :wof;
120121 } else |_| {}
121122
122 if (os.madvise(wipe_mem.ptr, wipe_mem.len, os.MADV.WIPEONFORK)) |_| {
123 if (posix.madvise(wipe_mem.ptr, wipe_mem.len, posix.MADV.WIPEONFORK)) |_| {
123124 return initAndFill(buffer);
124125 } else |_| {}
125126 }
......@@ -164,7 +165,7 @@ fn fillWithCsprng(buffer: []u8) void {
164165}
165166
166167pub fn defaultRandomSeed(buffer: []u8) void {
167 os.getrandom(buffer) catch @panic("getrandom() failed to provide entropy");
168 posix.getrandom(buffer) catch @panic("getrandom() failed to provide entropy");
168169}
169170
170171fn initAndFill(buffer: []u8) void {
lib/std/crypto/tls/Client.zig+14-14
......@@ -62,7 +62,7 @@ pub const StreamInterface = struct {
6262 /// The `iovecs` parameter is mutable because so that function may to
6363 /// mutate the fields in order to handle partial reads from the underlying
6464 /// stream layer.
65 pub fn readv(this: @This(), iovecs: []std.os.iovec) ReadError!usize {
65 pub fn readv(this: @This(), iovecs: []std.posix.iovec) ReadError!usize {
6666 _ = .{ this, iovecs };
6767 @panic("unimplemented");
6868 }
......@@ -72,7 +72,7 @@ pub const StreamInterface = struct {
7272
7373 /// Returns the number of bytes read, which may be less than the buffer
7474 /// space provided. A short read does not indicate end-of-stream.
75 pub fn writev(this: @This(), iovecs: []const std.os.iovec_const) WriteError!usize {
75 pub fn writev(this: @This(), iovecs: []const std.posix.iovec_const) WriteError!usize {
7676 _ = .{ this, iovecs };
7777 @panic("unimplemented");
7878 }
......@@ -81,7 +81,7 @@ pub const StreamInterface = struct {
8181 /// space provided, indicating end-of-stream.
8282 /// The `iovecs` parameter is mutable in case this function needs to mutate
8383 /// the fields in order to handle partial writes from the underlying layer.
84 pub fn writevAll(this: @This(), iovecs: []std.os.iovec_const) WriteError!usize {
84 pub fn writevAll(this: @This(), iovecs: []std.posix.iovec_const) WriteError!usize {
8585 // This can be implemented in terms of writev, or specialized if desired.
8686 _ = .{ this, iovecs };
8787 @panic("unimplemented");
......@@ -215,7 +215,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
215215 } ++ int2(@intCast(out_handshake.len + host_len)) ++ out_handshake;
216216
217217 {
218 var iovecs = [_]std.os.iovec_const{
218 var iovecs = [_]std.posix.iovec_const{
219219 .{
220220 .iov_base = &plaintext_header,
221221 .iov_len = plaintext_header.len,
......@@ -677,7 +677,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
677677 P.AEAD.encrypt(ciphertext, auth_tag, &out_cleartext, ad, nonce, p.client_handshake_key);
678678
679679 const both_msgs = client_change_cipher_spec_msg ++ finished_msg;
680 var both_msgs_vec = [_]std.os.iovec_const{.{
680 var both_msgs_vec = [_]std.posix.iovec_const{.{
681681 .iov_base = &both_msgs,
682682 .iov_len = both_msgs.len,
683683 }};
......@@ -755,7 +755,7 @@ pub fn writeAllEnd(c: *Client, stream: anytype, bytes: []const u8, end: bool) !v
755755/// TLS session, or a truncation attack.
756756pub fn writeEnd(c: *Client, stream: anytype, bytes: []const u8, end: bool) !usize {
757757 var ciphertext_buf: [tls.max_ciphertext_record_len * 4]u8 = undefined;
758 var iovecs_buf: [6]std.os.iovec_const = undefined;
758 var iovecs_buf: [6]std.posix.iovec_const = undefined;
759759 var prepared = prepareCiphertextRecord(c, &iovecs_buf, &ciphertext_buf, bytes, .application_data);
760760 if (end) {
761761 prepared.iovec_end += prepareCiphertextRecord(
......@@ -796,7 +796,7 @@ pub fn writeEnd(c: *Client, stream: anytype, bytes: []const u8, end: bool) !usiz
796796
797797fn prepareCiphertextRecord(
798798 c: *Client,
799 iovecs: []std.os.iovec_const,
799 iovecs: []std.posix.iovec_const,
800800 ciphertext_buf: []u8,
801801 bytes: []const u8,
802802 inner_content_type: tls.ContentType,
......@@ -885,7 +885,7 @@ pub fn eof(c: Client) bool {
885885/// If the number read is less than `len` it means the stream reached the end.
886886/// Reaching the end of the stream is not an error condition.
887887pub fn readAtLeast(c: *Client, stream: anytype, buffer: []u8, len: usize) !usize {
888 var iovecs = [1]std.os.iovec{.{ .iov_base = buffer.ptr, .iov_len = buffer.len }};
888 var iovecs = [1]std.posix.iovec{.{ .iov_base = buffer.ptr, .iov_len = buffer.len }};
889889 return readvAtLeast(c, stream, &iovecs, len);
890890}
891891
......@@ -908,7 +908,7 @@ pub fn readAll(c: *Client, stream: anytype, buffer: []u8) !usize {
908908/// stream is not an error condition.
909909/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
910910/// order to handle partial reads from the underlying stream layer.
911pub fn readv(c: *Client, stream: anytype, iovecs: []std.os.iovec) !usize {
911pub fn readv(c: *Client, stream: anytype, iovecs: []std.posix.iovec) !usize {
912912 return readvAtLeast(c, stream, iovecs, 1);
913913}
914914
......@@ -919,7 +919,7 @@ pub fn readv(c: *Client, stream: anytype, iovecs: []std.os.iovec) !usize {
919919/// Reaching the end of the stream is not an error condition.
920920/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
921921/// order to handle partial reads from the underlying stream layer.
922pub fn readvAtLeast(c: *Client, stream: anytype, iovecs: []std.os.iovec, len: usize) !usize {
922pub fn readvAtLeast(c: *Client, stream: anytype, iovecs: []std.posix.iovec, len: usize) !usize {
923923 if (c.eof()) return 0;
924924
925925 var off_i: usize = 0;
......@@ -945,7 +945,7 @@ pub fn readvAtLeast(c: *Client, stream: anytype, iovecs: []std.os.iovec, len: us
945945/// function asserts that `eof()` is `false`.
946946/// See `readv` for a higher level function that has the same, familiar API as
947947/// other read functions, such as `std.fs.File.read`.
948pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec) !usize {
948pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iovec) !usize {
949949 var vp: VecPut = .{ .iovecs = iovecs };
950950
951951 // Give away the buffered cleartext we have, if any.
......@@ -998,7 +998,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)
998998 c.partial_cleartext_idx = 0;
999999 const first_iov = c.partially_read_buffer[c.partial_ciphertext_end..];
10001000
1001 var ask_iovecs_buf: [2]std.os.iovec = .{
1001 var ask_iovecs_buf: [2]std.posix.iovec = .{
10021002 .{
10031003 .iov_base = first_iov.ptr,
10041004 .iov_len = first_iov.len,
......@@ -1352,7 +1352,7 @@ fn SchemeEddsa(comptime scheme: tls.SignatureScheme) type {
13521352
13531353/// Abstraction for sending multiple byte buffers to a slice of iovecs.
13541354const VecPut = struct {
1355 iovecs: []const std.os.iovec,
1355 iovecs: []const std.posix.iovec,
13561356 idx: usize = 0,
13571357 off: usize = 0,
13581358 total: usize = 0,
......@@ -1413,7 +1413,7 @@ const VecPut = struct {
14131413};
14141414
14151415/// Limit iovecs to a specific byte size.
1416fn limitVecs(iovecs: []std.os.iovec, len: usize) []std.os.iovec {
1416fn limitVecs(iovecs: []std.posix.iovec, len: usize) []std.posix.iovec {
14171417 var bytes_left: usize = len;
14181418 for (iovecs, 0..) |*iovec, vec_i| {
14191419 if (bytes_left <= iovec.iov_len) {
lib/std/debug.zig+57-60
......@@ -3,7 +3,7 @@ const builtin = @import("builtin");
33const math = std.math;
44const mem = std.mem;
55const io = std.io;
6const os = std.os;
6const posix = std.posix;
77const fs = std.fs;
88const testing = std.testing;
99const elf = std.elf;
......@@ -34,7 +34,7 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) {
3434 // "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address".
3535 .wasm32,
3636 .wasm64,
37 => builtin.os.tag == .emscripten,
37 => native_os == .emscripten,
3838
3939 // `@returnAddress()` is unsupported in LLVM 13.
4040 .bpfel,
......@@ -192,8 +192,8 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
192192 }
193193}
194194
195pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and
196 (builtin.os.tag != .linux or switch (builtin.cpu.arch) {
195pub const have_ucontext = @hasDecl(posix.system, "ucontext_t") and
196 (native_os != .linux or switch (builtin.cpu.arch) {
197197 .mips, .mipsel, .mips64, .mips64el, .riscv64 => false,
198198 else => true,
199199});
......@@ -203,9 +203,9 @@ pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and
203203/// use internal pointers within this structure. To make a copy, use `copyContext`.
204204pub const ThreadContext = blk: {
205205 if (native_os == .windows) {
206 break :blk std.os.windows.CONTEXT;
206 break :blk windows.CONTEXT;
207207 } else if (have_ucontext) {
208 break :blk os.ucontext_t;
208 break :blk posix.ucontext_t;
209209 } else {
210210 break :blk void;
211211 }
......@@ -228,9 +228,9 @@ pub fn relocateContext(context: *ThreadContext) void {
228228 };
229229}
230230
231pub const have_getcontext = @hasDecl(os.system, "getcontext") and
232 builtin.os.tag != .openbsd and
233 (builtin.os.tag != .linux or switch (builtin.cpu.arch) {
231pub const have_getcontext = @hasDecl(posix.system, "getcontext") and
232 native_os != .openbsd and
233 (native_os != .linux or switch (builtin.cpu.arch) {
234234 .x86,
235235 .x86_64,
236236 => true,
......@@ -249,7 +249,7 @@ pub inline fn getContext(context: *ThreadContext) bool {
249249 return true;
250250 }
251251
252 const result = have_getcontext and os.system.getcontext(context) == 0;
252 const result = have_getcontext and posix.system.getcontext(context) == 0;
253253 if (native_os == .macos) {
254254 assert(context.mcsize == @sizeOf(std.c.mcontext_t));
255255
......@@ -470,12 +470,12 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize
470470
471471 const stderr = io.getStdErr().writer();
472472 if (builtin.single_threaded) {
473 stderr.print("panic: ", .{}) catch os.abort();
473 stderr.print("panic: ", .{}) catch posix.abort();
474474 } else {
475475 const current_thread_id = std.Thread.getCurrentId();
476 stderr.print("thread {} panic: ", .{current_thread_id}) catch os.abort();
476 stderr.print("thread {} panic: ", .{current_thread_id}) catch posix.abort();
477477 }
478 stderr.print("{s}\n", .{msg}) catch os.abort();
478 stderr.print("{s}\n", .{msg}) catch posix.abort();
479479 if (trace) |t| {
480480 dumpStackTrace(t.*);
481481 }
......@@ -491,14 +491,14 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize
491491 // we're still holding the mutex but that's fine as we're going to
492492 // call abort()
493493 const stderr = io.getStdErr().writer();
494 stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort();
494 stderr.print("Panicked during a panic. Aborting.\n", .{}) catch posix.abort();
495495 },
496496 else => {
497497 // Panicked while printing "Panicked during a panic."
498498 },
499499 };
500500
501 os.abort();
501 posix.abort();
502502}
503503
504504/// Must be called only after adding 1 to `panicking`. There are three callsites.
......@@ -584,7 +584,7 @@ pub const StackIterator = struct {
584584 };
585585 }
586586
587 pub fn initWithContext(first_address: ?usize, debug_info: *DebugInfo, context: *const os.ucontext_t) !StackIterator {
587 pub fn initWithContext(first_address: ?usize, debug_info: *DebugInfo, context: *const posix.ucontext_t) !StackIterator {
588588 // The implementation of DWARF unwinding on aarch64-macos is not complete. However, Apple mandates that
589589 // the frame pointer register is always used, so on this platform we can safely use the FP-based unwinder.
590590 if (comptime builtin.target.isDarwin() and native_arch == .aarch64) {
......@@ -668,12 +668,11 @@ pub const StackIterator = struct {
668668 const aligned_memory = @as([*]align(mem.page_size) u8, @ptrFromInt(aligned_address))[0..mem.page_size];
669669
670670 if (native_os == .windows) {
671 const w = os.windows;
672 var memory_info: w.MEMORY_BASIC_INFORMATION = undefined;
671 var memory_info: windows.MEMORY_BASIC_INFORMATION = undefined;
673672
674673 // The only error this function can throw is ERROR_INVALID_PARAMETER.
675674 // supply an address that invalid i'll be thrown.
676 const rc = w.VirtualQuery(aligned_memory, &memory_info, aligned_memory.len) catch {
675 const rc = windows.VirtualQuery(aligned_memory, &memory_info, aligned_memory.len) catch {
677676 return false;
678677 };
679678
......@@ -683,17 +682,15 @@ pub const StackIterator = struct {
683682 }
684683
685684 // Free pages cannot be read, they are unmapped
686 if (memory_info.State == w.MEM_FREE) {
685 if (memory_info.State == windows.MEM_FREE) {
687686 return false;
688687 }
689688
690689 return true;
691 } else if (@hasDecl(os.system, "msync") and native_os != .wasi and native_os != .emscripten) {
692 os.msync(aligned_memory, os.MSF.ASYNC) catch |err| {
690 } else if (@hasDecl(posix.system, "msync") and native_os != .wasi and native_os != .emscripten) {
691 posix.msync(aligned_memory, posix.MSF.ASYNC) catch |err| {
693692 switch (err) {
694 os.MSyncError.UnmappedMemory => {
695 return false;
696 },
693 error.UnmappedMemory => return false,
697694 else => unreachable,
698695 }
699696 };
......@@ -1296,7 +1293,7 @@ pub fn readElfDebugInfo(
12961293 }
12971294
12981295 var cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
1299 const cwd_path = os.realpath(".", &cwd_buf) catch break :blk;
1296 const cwd_path = posix.realpath(".", &cwd_buf) catch break :blk;
13001297
13011298 // <global debug directory>/<absolute folder of current binary>/<gnu_debuglink>
13021299 for (global_debug_directories) |global_directory| {
......@@ -1651,15 +1648,15 @@ fn mapWholeFile(file: File) ![]align(mem.page_size) const u8 {
16511648 defer file.close();
16521649
16531650 const file_len = math.cast(usize, try file.getEndPos()) orelse math.maxInt(usize);
1654 const mapped_mem = try os.mmap(
1651 const mapped_mem = try posix.mmap(
16551652 null,
16561653 file_len,
1657 os.PROT.READ,
1654 posix.PROT.READ,
16581655 .{ .TYPE = .SHARED },
16591656 file.handle,
16601657 0,
16611658 );
1662 errdefer os.munmap(mapped_mem);
1659 errdefer posix.munmap(mapped_mem);
16631660
16641661 return mapped_mem;
16651662 }
......@@ -1997,8 +1994,8 @@ pub const DebugInfo = struct {
19971994 } = .{ .address = address };
19981995 const CtxTy = @TypeOf(ctx);
19991996
2000 if (os.dl_iterate_phdr(&ctx, error{Found}, struct {
2001 fn callback(info: *os.dl_phdr_info, size: usize, context: *CtxTy) !void {
1997 if (posix.dl_iterate_phdr(&ctx, error{Found}, struct {
1998 fn callback(info: *posix.dl_phdr_info, size: usize, context: *CtxTy) !void {
20021999 _ = size;
20032000 if (context.address < info.dlpi_addr) return;
20042001 const phdrs = info.dlpi_phdr[0..info.dlpi_phnum];
......@@ -2036,8 +2033,8 @@ pub const DebugInfo = struct {
20362033 } = .{ .address = address };
20372034 const CtxTy = @TypeOf(ctx);
20382035
2039 if (os.dl_iterate_phdr(&ctx, error{Found}, struct {
2040 fn callback(info: *os.dl_phdr_info, size: usize, context: *CtxTy) !void {
2036 if (posix.dl_iterate_phdr(&ctx, error{Found}, struct {
2037 fn callback(info: *posix.dl_phdr_info, size: usize, context: *CtxTy) !void {
20412038 _ = size;
20422039 // The base address is too high
20432040 if (context.address < info.dlpi_addr)
......@@ -2159,7 +2156,7 @@ pub const ModuleDebugInfo = switch (native_os) {
21592156 }
21602157 self.ofiles.deinit();
21612158 allocator.free(self.symbols);
2162 os.munmap(self.mapped_memory);
2159 posix.munmap(self.mapped_memory);
21632160 }
21642161
21652162 fn loadOFile(self: *@This(), allocator: mem.Allocator, o_file_path: []const u8) !*OFileInfo {
......@@ -2433,8 +2430,8 @@ pub const ModuleDebugInfo = switch (native_os) {
24332430
24342431 pub fn deinit(self: *@This(), allocator: mem.Allocator) void {
24352432 self.dwarf.deinit(allocator);
2436 os.munmap(self.mapped_memory);
2437 if (self.external_mapped_memory) |m| os.munmap(m);
2433 posix.munmap(self.mapped_memory);
2434 if (self.external_mapped_memory) |m| posix.munmap(m);
24382435 }
24392436
24402437 pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo {
......@@ -2514,7 +2511,7 @@ pub const have_segfault_handling_support = switch (native_os) {
25142511 .windows,
25152512 => true,
25162513
2517 .freebsd, .openbsd => @hasDecl(os.system, "ucontext_t"),
2514 .freebsd, .openbsd => @hasDecl(std.c, "ucontext_t"),
25182515 else => false,
25192516};
25202517
......@@ -2529,11 +2526,11 @@ pub fn maybeEnableSegfaultHandler() void {
25292526
25302527var windows_segfault_handle: ?windows.HANDLE = null;
25312528
2532pub fn updateSegfaultHandler(act: ?*const os.Sigaction) error{OperationNotSupported}!void {
2533 try os.sigaction(os.SIG.SEGV, act, null);
2534 try os.sigaction(os.SIG.ILL, act, null);
2535 try os.sigaction(os.SIG.BUS, act, null);
2536 try os.sigaction(os.SIG.FPE, act, null);
2529pub fn updateSegfaultHandler(act: ?*const posix.Sigaction) error{OperationNotSupported}!void {
2530 try posix.sigaction(posix.SIG.SEGV, act, null);
2531 try posix.sigaction(posix.SIG.ILL, act, null);
2532 try posix.sigaction(posix.SIG.BUS, act, null);
2533 try posix.sigaction(posix.SIG.FPE, act, null);
25372534}
25382535
25392536/// Attaches a global SIGSEGV handler which calls `@panic("segmentation fault");`
......@@ -2545,10 +2542,10 @@ pub fn attachSegfaultHandler() void {
25452542 windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
25462543 return;
25472544 }
2548 var act = os.Sigaction{
2545 var act = posix.Sigaction{
25492546 .handler = .{ .sigaction = handleSegfaultPosix },
2550 .mask = os.empty_sigset,
2551 .flags = (os.SA.SIGINFO | os.SA.RESTART | os.SA.RESETHAND),
2547 .mask = posix.empty_sigset,
2548 .flags = (posix.SA.SIGINFO | posix.SA.RESTART | posix.SA.RESETHAND),
25522549 };
25532550
25542551 updateSegfaultHandler(&act) catch {
......@@ -2564,16 +2561,16 @@ fn resetSegfaultHandler() void {
25642561 }
25652562 return;
25662563 }
2567 var act = os.Sigaction{
2568 .handler = .{ .handler = os.SIG.DFL },
2569 .mask = os.empty_sigset,
2564 var act = posix.Sigaction{
2565 .handler = .{ .handler = posix.SIG.DFL },
2566 .mask = posix.empty_sigset,
25702567 .flags = 0,
25712568 };
25722569 // To avoid a double-panic, do nothing if an error happens here.
25732570 updateSegfaultHandler(&act) catch {};
25742571}
25752572
2576fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const anyopaque) callconv(.C) noreturn {
2573fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*const anyopaque) callconv(.C) noreturn {
25772574 // Reset to the default handler so that if a segfault happens in this handler it will crash
25782575 // the process. Also when this handler returns, the original instruction will be repeated
25792576 // and the resulting segfault will crash the process rather than continually dump stack traces.
......@@ -2612,13 +2609,13 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any
26122609 // We cannot allow the signal handler to return because when it runs the original instruction
26132610 // again, the memory may be mapped and undefined behavior would occur rather than repeating
26142611 // the segfault. So we simply abort here.
2615 os.abort();
2612 posix.abort();
26162613}
26172614
26182615fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*const anyopaque) void {
26192616 const stderr = io.getStdErr().writer();
26202617 _ = switch (sig) {
2621 os.SIG.SEGV => if (native_arch == .x86_64 and native_os == .linux and code == 128) // SI_KERNEL
2618 posix.SIG.SEGV => if (native_arch == .x86_64 and native_os == .linux and code == 128) // SI_KERNEL
26222619 // x86_64 doesn't have a full 64-bit virtual address space.
26232620 // Addresses outside of that address space are non-canonical
26242621 // and the CPU won't provide the faulting address to us.
......@@ -2629,11 +2626,11 @@ fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*const anyo
26292626 stderr.print("General protection exception (no address available)\n", .{})
26302627 else
26312628 stderr.print("Segmentation fault at address 0x{x}\n", .{addr}),
2632 os.SIG.ILL => stderr.print("Illegal instruction at address 0x{x}\n", .{addr}),
2633 os.SIG.BUS => stderr.print("Bus error at address 0x{x}\n", .{addr}),
2634 os.SIG.FPE => stderr.print("Arithmetic exception at address 0x{x}\n", .{addr}),
2629 posix.SIG.ILL => stderr.print("Illegal instruction at address 0x{x}\n", .{addr}),
2630 posix.SIG.BUS => stderr.print("Bus error at address 0x{x}\n", .{addr}),
2631 posix.SIG.FPE => stderr.print("Arithmetic exception at address 0x{x}\n", .{addr}),
26352632 else => unreachable,
2636 } catch os.abort();
2633 } catch posix.abort();
26372634
26382635 switch (native_arch) {
26392636 .x86,
......@@ -2641,7 +2638,7 @@ fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*const anyo
26412638 .arm,
26422639 .aarch64,
26432640 => {
2644 const ctx: *const os.ucontext_t = @ptrCast(@alignCast(ctx_ptr));
2641 const ctx: *const posix.ucontext_t = @ptrCast(@alignCast(ctx_ptr));
26452642 dumpStackTraceFromBase(ctx);
26462643 },
26472644 else => {},
......@@ -2684,7 +2681,7 @@ fn handleSegfaultWindowsExtra(
26842681 dumpSegfaultInfoWindows(info, msg, label);
26852682 },
26862683 };
2687 os.abort();
2684 posix.abort();
26882685 } else {
26892686 switch (msg) {
26902687 0 => panicImpl(null, exception_address, "{s}", label.?),
......@@ -2707,7 +2704,7 @@ fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[
27072704 1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}),
27082705 2 => stderr.print("Illegal instruction at address 0x{x}\n", .{info.ContextRecord.getRegs().ip}),
27092706 else => unreachable,
2710 } catch os.abort();
2707 } catch posix.abort();
27112708
27122709 dumpStackTraceFromBase(info.ContextRecord);
27132710}
......@@ -2722,9 +2719,9 @@ pub fn dumpStackPointerAddr(prefix: []const u8) void {
27222719test "manage resources correctly" {
27232720 if (builtin.strip_debug_info) return error.SkipZigTest;
27242721
2725 if (builtin.os.tag == .wasi) return error.SkipZigTest;
2722 if (native_os == .wasi) return error.SkipZigTest;
27262723
2727 if (builtin.os.tag == .windows) {
2724 if (native_os == .windows) {
27282725 // https://github.com/ziglang/zig/issues/13963
27292726 return error.SkipZigTest;
27302727 }
lib/std/dwarf.zig+2-2
......@@ -2188,12 +2188,12 @@ pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void {
21882188/// This function is to make it handy to comment out the return and make it
21892189/// into a crash when working on this file.
21902190fn badDwarf() error{InvalidDebugInfo} {
2191 //std.os.abort(); // can be handy to uncomment when working on this file
2191 //if (true) @panic("badDwarf"); // can be handy to uncomment when working on this file
21922192 return error.InvalidDebugInfo;
21932193}
21942194
21952195fn missingDwarf() error{MissingDebugInfo} {
2196 //std.os.abort(); // can be handy to uncomment when working on this file
2196 //if (true) @panic("missingDwarf"); // can be handy to uncomment when working on this file
21972197 return error.MissingDebugInfo;
21982198}
21992199
lib/std/dwarf/abi.zig+43-42
......@@ -1,7 +1,8 @@
11const builtin = @import("builtin");
22const std = @import("../std.zig");
3const os = std.os;
43const mem = std.mem;
4const native_os = builtin.os.tag;
5const posix = std.posix;
56
67pub fn supportsUnwinding(target: std.Target) bool {
78 return switch (target.cpu.arch) {
......@@ -138,7 +139,7 @@ pub fn regBytes(
138139 reg_number: u8,
139140 reg_context: ?RegisterContext,
140141) AbiError!RegBytesReturnType(@TypeOf(thread_context_ptr)) {
141 if (builtin.os.tag == .windows) {
142 if (native_os == .windows) {
142143 return switch (builtin.cpu.arch) {
143144 .x86 => switch (reg_number) {
144145 0 => mem.asBytes(&thread_context_ptr.Eax),
......@@ -193,61 +194,61 @@ pub fn regBytes(
193194
194195 const ucontext_ptr = thread_context_ptr;
195196 return switch (builtin.cpu.arch) {
196 .x86 => switch (builtin.os.tag) {
197 .x86 => switch (native_os) {
197198 .linux, .netbsd, .solaris, .illumos => switch (reg_number) {
198 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EAX]),
199 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]),
200 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]),
201 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBX]),
199 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EAX]),
200 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ECX]),
201 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EDX]),
202 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBX]),
202203 4...5 => if (reg_context) |r| bytes: {
203204 if (reg_number == 4) {
204205 break :bytes if (r.eh_frame and r.is_macho)
205 mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP])
206 mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBP])
206207 else
207 mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESP]);
208 mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESP]);
208209 } else {
209210 break :bytes if (r.eh_frame and r.is_macho)
210 mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESP])
211 mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESP])
211212 else
212 mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP]);
213 mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBP]);
213214 }
214215 } else error.RegisterContextRequired,
215 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESI]),
216 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDI]),
217 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EIP]),
218 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EFL]),
219 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.CS]),
220 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.SS]),
221 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.DS]),
222 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ES]),
223 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]),
224 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]),
216 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESI]),
217 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EDI]),
218 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EIP]),
219 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EFL]),
220 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.CS]),
221 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.SS]),
222 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.DS]),
223 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ES]),
224 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.FS]),
225 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.GS]),
225226 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs
226227 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs
227228 else => error.InvalidRegister,
228229 },
229230 else => error.UnimplementedOs,
230231 },
231 .x86_64 => switch (builtin.os.tag) {
232 .x86_64 => switch (native_os) {
232233 .linux, .solaris, .illumos => switch (reg_number) {
233 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RAX]),
234 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RDX]),
235 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RCX]),
236 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RBX]),
237 4 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RSI]),
238 5 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RDI]),
239 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RBP]),
240 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RSP]),
241 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R8]),
242 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R9]),
243 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R10]),
244 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R11]),
245 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R12]),
246 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R13]),
247 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R14]),
248 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R15]),
249 16 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RIP]),
250 17...32 => |i| if (builtin.os.tag.isSolarish())
234 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RAX]),
235 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDX]),
236 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RCX]),
237 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RBX]),
238 4 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RSI]),
239 5 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDI]),
240 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RBP]),
241 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RSP]),
242 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R8]),
243 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R9]),
244 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R10]),
245 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R11]),
246 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R12]),
247 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R13]),
248 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R14]),
249 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R15]),
250 16 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RIP]),
251 17...32 => |i| if (native_os.isSolarish())
251252 mem.asBytes(&ucontext_ptr.mcontext.fpregs.chip_state.xmm[i - 17])
252253 else
253254 mem.asBytes(&ucontext_ptr.mcontext.fpregs.xmm[i - 17]),
......@@ -317,7 +318,7 @@ pub fn regBytes(
317318 },
318319 else => error.UnimplementedOs,
319320 },
320 .arm => switch (builtin.os.tag) {
321 .arm => switch (native_os) {
321322 .linux => switch (reg_number) {
322323 0 => mem.asBytes(&ucontext_ptr.mcontext.arm_r0),
323324 1 => mem.asBytes(&ucontext_ptr.mcontext.arm_r1),
......@@ -340,7 +341,7 @@ pub fn regBytes(
340341 },
341342 else => error.UnimplementedOs,
342343 },
343 .aarch64 => switch (builtin.os.tag) {
344 .aarch64 => switch (native_os) {
344345 .macos, .ios => switch (reg_number) {
345346 0...28 => mem.asBytes(&ucontext_ptr.mcontext.ss.regs[reg_number]),
346347 29 => mem.asBytes(&ucontext_ptr.mcontext.ss.fp),
lib/std/dynamic_library.zig+30-30
......@@ -1,16 +1,16 @@
11const std = @import("std.zig");
22const builtin = @import("builtin");
33const mem = std.mem;
4const os = std.os;
54const testing = std.testing;
65const elf = std.elf;
76const windows = std.os.windows;
8const system = std.os.system;
7const native_os = builtin.os.tag;
8const posix = std.posix;
99
1010/// Cross-platform dynamic library loading and symbol lookup.
1111/// Platform-specific functionality is available through the `inner` field.
1212pub const DynLib = struct {
13 const InnerType = switch (builtin.os.tag) {
13 const InnerType = switch (native_os) {
1414 .linux => if (!builtin.link_libc or builtin.abi == .musl and builtin.link_mode == .static)
1515 ElfDynLib
1616 else
......@@ -125,7 +125,7 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) error{InvalidExe}!LinkMap.Iterator {
125125pub const ElfDynLib = struct {
126126 strings: [*:0]u8,
127127 syms: [*]elf.Sym,
128 hashtab: [*]os.Elf_Symndx,
128 hashtab: [*]posix.Elf_Symndx,
129129 versym: ?[*]u16,
130130 verdef: ?*elf.Verdef,
131131 memory: []align(mem.page_size) u8,
......@@ -138,27 +138,27 @@ pub const ElfDynLib = struct {
138138 ElfStringSectionNotFound,
139139 ElfSymSectionNotFound,
140140 ElfHashTableNotFound,
141 } || os.OpenError || os.MMapError;
141 } || posix.OpenError || posix.MMapError;
142142
143143 /// Trusts the file. Malicious file will be able to execute arbitrary code.
144144 pub fn open(path: []const u8) Error!ElfDynLib {
145 const fd = try os.open(path, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
146 defer os.close(fd);
145 const fd = try posix.open(path, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
146 defer posix.close(fd);
147147
148 const stat = try os.fstat(fd);
148 const stat = try posix.fstat(fd);
149149 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;
150150
151151 // This one is to read the ELF info. We do more mmapping later
152152 // corresponding to the actual LOAD sections.
153 const file_bytes = try os.mmap(
153 const file_bytes = try posix.mmap(
154154 null,
155155 mem.alignForward(usize, size, mem.page_size),
156 os.PROT.READ,
156 posix.PROT.READ,
157157 .{ .TYPE = .PRIVATE },
158158 fd,
159159 0,
160160 );
161 defer os.munmap(file_bytes);
161 defer posix.munmap(file_bytes);
162162
163163 const eh = @as(*elf.Ehdr, @ptrCast(file_bytes.ptr));
164164 if (!mem.eql(u8, eh.e_ident[0..4], elf.MAGIC)) return error.NotElfFile;
......@@ -188,15 +188,15 @@ pub const ElfDynLib = struct {
188188 const dynv = maybe_dynv orelse return error.MissingDynamicLinkingInformation;
189189
190190 // Reserve the entire range (with no permissions) so that we can do MAP.FIXED below.
191 const all_loaded_mem = try os.mmap(
191 const all_loaded_mem = try posix.mmap(
192192 null,
193193 virt_addr_end,
194 os.PROT.NONE,
194 posix.PROT.NONE,
195195 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
196196 -1,
197197 0,
198198 );
199 errdefer os.munmap(all_loaded_mem);
199 errdefer posix.munmap(all_loaded_mem);
200200
201201 const base = @intFromPtr(all_loaded_mem.ptr);
202202
......@@ -220,7 +220,7 @@ pub const ElfDynLib = struct {
220220 const prot = elfToMmapProt(ph.p_flags);
221221 if ((ph.p_flags & elf.PF_W) == 0) {
222222 // If it does not need write access, it can be mapped from the fd.
223 _ = try os.mmap(
223 _ = try posix.mmap(
224224 ptr,
225225 extended_memsz,
226226 prot,
......@@ -229,7 +229,7 @@ pub const ElfDynLib = struct {
229229 ph.p_offset - extra_bytes,
230230 );
231231 } else {
232 const sect_mem = try os.mmap(
232 const sect_mem = try posix.mmap(
233233 ptr,
234234 extended_memsz,
235235 prot,
......@@ -247,7 +247,7 @@ pub const ElfDynLib = struct {
247247
248248 var maybe_strings: ?[*:0]u8 = null;
249249 var maybe_syms: ?[*]elf.Sym = null;
250 var maybe_hashtab: ?[*]os.Elf_Symndx = null;
250 var maybe_hashtab: ?[*]posix.Elf_Symndx = null;
251251 var maybe_versym: ?[*]u16 = null;
252252 var maybe_verdef: ?*elf.Verdef = null;
253253
......@@ -258,7 +258,7 @@ pub const ElfDynLib = struct {
258258 switch (dynv[i]) {
259259 elf.DT_STRTAB => maybe_strings = @as([*:0]u8, @ptrFromInt(p)),
260260 elf.DT_SYMTAB => maybe_syms = @as([*]elf.Sym, @ptrFromInt(p)),
261 elf.DT_HASH => maybe_hashtab = @as([*]os.Elf_Symndx, @ptrFromInt(p)),
261 elf.DT_HASH => maybe_hashtab = @as([*]posix.Elf_Symndx, @ptrFromInt(p)),
262262 elf.DT_VERSYM => maybe_versym = @as([*]u16, @ptrFromInt(p)),
263263 elf.DT_VERDEF => maybe_verdef = @as(*elf.Verdef, @ptrFromInt(p)),
264264 else => {},
......@@ -283,7 +283,7 @@ pub const ElfDynLib = struct {
283283
284284 /// Trusts the file
285285 pub fn close(self: *ElfDynLib) void {
286 os.munmap(self.memory);
286 posix.munmap(self.memory);
287287 self.* = undefined;
288288 }
289289
......@@ -320,10 +320,10 @@ pub const ElfDynLib = struct {
320320 }
321321
322322 fn elfToMmapProt(elf_prot: u64) u32 {
323 var result: u32 = os.PROT.NONE;
324 if ((elf_prot & elf.PF_R) != 0) result |= os.PROT.READ;
325 if ((elf_prot & elf.PF_W) != 0) result |= os.PROT.WRITE;
326 if ((elf_prot & elf.PF_X) != 0) result |= os.PROT.EXEC;
323 var result: u32 = posix.PROT.NONE;
324 if ((elf_prot & elf.PF_R) != 0) result |= posix.PROT.READ;
325 if ((elf_prot & elf.PF_W) != 0) result |= posix.PROT.WRITE;
326 if ((elf_prot & elf.PF_X) != 0) result |= posix.PROT.EXEC;
327327 return result;
328328 }
329329};
......@@ -343,7 +343,7 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [
343343}
344344
345345test "ElfDynLib" {
346 if (builtin.os.tag != .linux) {
346 if (native_os != .linux) {
347347 return error.SkipZigTest;
348348 }
349349
......@@ -419,20 +419,20 @@ pub const DlDynLib = struct {
419419 handle: *anyopaque,
420420
421421 pub fn open(path: []const u8) Error!DlDynLib {
422 const path_c = try os.toPosixPath(path);
422 const path_c = try posix.toPosixPath(path);
423423 return openZ(&path_c);
424424 }
425425
426426 pub fn openZ(path_c: [*:0]const u8) Error!DlDynLib {
427427 return .{
428 .handle = system.dlopen(path_c, system.RTLD.LAZY) orelse {
428 .handle = std.c.dlopen(path_c, std.c.RTLD.LAZY) orelse {
429429 return error.FileNotFound;
430430 },
431431 };
432432 }
433433
434434 pub fn close(self: *DlDynLib) void {
435 switch (std.os.errno(system.dlclose(self.handle))) {
435 switch (posix.errno(std.c.dlclose(self.handle))) {
436436 .SUCCESS => return,
437437 else => unreachable,
438438 }
......@@ -442,7 +442,7 @@ pub const DlDynLib = struct {
442442 pub fn lookup(self: *DlDynLib, comptime T: type, name: [:0]const u8) ?T {
443443 // dlsym (and other dl-functions) secretly take shadow parameter - return address on stack
444444 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66826
445 if (@call(.never_tail, system.dlsym, .{ self.handle, name.ptr })) |symbol| {
445 if (@call(.never_tail, std.c.dlsym, .{ self.handle, name.ptr })) |symbol| {
446446 return @as(T, @ptrCast(@alignCast(symbol)));
447447 } else {
448448 return null;
......@@ -453,12 +453,12 @@ pub const DlDynLib = struct {
453453 /// Returns human readable string describing most recent error than occurred from `lookup`
454454 /// or `null` if no error has occurred since initialization or when `getError` was last called.
455455 pub fn getError() ?[:0]const u8 {
456 return mem.span(system.dlerror());
456 return mem.span(std.c.dlerror());
457457 }
458458};
459459
460460test "dynamic_library" {
461 const libname = switch (builtin.os.tag) {
461 const libname = switch (native_os) {
462462 .linux, .freebsd, .openbsd, .solaris, .illumos => "invalid_so.so",
463463 .windows => "invalid_dll.dll",
464464 .macos, .tvos, .watchos, .ios => "invalid_dylib.dylib",
lib/std/fs.zig+89-80
......@@ -3,21 +3,23 @@
33const std = @import("std.zig");
44const builtin = @import("builtin");
55const root = @import("root");
6const os = std.os;
76const mem = std.mem;
87const base64 = std.base64;
98const crypto = std.crypto;
109const Allocator = std.mem.Allocator;
1110const assert = std.debug.assert;
11const native_os = builtin.os.tag;
12const posix = std.posix;
13const windows = std.os.windows;
1214
13const is_darwin = builtin.os.tag.isDarwin();
15const is_darwin = native_os.isDarwin();
1416
1517pub const AtomicFile = @import("fs/AtomicFile.zig");
1618pub const Dir = @import("fs/Dir.zig");
1719pub const File = @import("fs/File.zig");
1820pub const path = @import("fs/path.zig");
1921
20pub const has_executable_bit = switch (builtin.os.tag) {
22pub const has_executable_bit = switch (native_os) {
2123 .windows, .wasi => false,
2224 else => true,
2325};
......@@ -26,36 +28,43 @@ pub const wasi = @import("fs/wasi.zig");
2628
2729// TODO audit these APIs with respect to Dir and absolute paths
2830
29pub const realpath = os.realpath;
30pub const realpathZ = os.realpathZ;
31pub const realpathW = os.realpathW;
31pub const realpath = posix.realpath;
32pub const realpathZ = posix.realpathZ;
33pub const realpathW = posix.realpathW;
3234
3335pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir;
3436pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError;
3537
36/// This represents the maximum size of a `[]u8` file path that the
37/// operating system will accept. Paths, including those returned from file
38/// system operations, may be longer than this length, but such paths cannot
39/// be successfully passed back in other file system operations. However,
40/// all path components returned by file system operations are assumed to
41/// fit into a `u8` array of this length.
38/// Deprecated: use `max_path_bytes`.
39pub const MAX_PATH_BYTES = max_path_bytes;
40
41/// The maximum length of a file path that the operating system will accept.
42///
43/// Paths, including those returned from file system operations, may be longer
44/// than this length, but such paths cannot be successfully passed back in
45/// other file system operations. However, all path components returned by file
46/// system operations are assumed to fit into a `u8` array of this length.
47///
4248/// The byte count includes room for a null sentinel byte.
43/// On Windows, `[]u8` file paths are encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
44/// On WASI, `[]u8` file paths are encoded as valid UTF-8.
45/// On other platforms, `[]u8` file paths are opaque sequences of bytes with no particular encoding.
46pub const MAX_PATH_BYTES = switch (builtin.os.tag) {
47 .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .haiku, .solaris, .illumos, .plan9, .emscripten => os.PATH_MAX,
49///
50/// * On Windows, `[]u8` file paths are encoded as
51/// [WTF-8](https://simonsapin.github.io/wtf-8/).
52/// * On WASI, `[]u8` file paths are encoded as valid UTF-8.
53/// * On other platforms, `[]u8` file paths are opaque sequences of bytes with
54/// no particular encoding.
55pub const max_path_bytes = switch (native_os) {
56 .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .haiku, .solaris, .illumos, .plan9, .emscripten => posix.PATH_MAX,
4857 // Each WTF-16LE code unit may be expanded to 3 WTF-8 bytes.
4958 // If it would require 4 WTF-8 bytes, then there would be a surrogate
5059 // pair in the WTF-16LE, and we (over)account 3 bytes for it that way.
5160 // +1 for the null byte at the end, which can be encoded in 1 byte.
52 .windows => os.windows.PATH_MAX_WIDE * 3 + 1,
61 .windows => windows.PATH_MAX_WIDE * 3 + 1,
5362 // TODO work out what a reasonable value we should use here
5463 .wasi => 4096,
5564 else => if (@hasDecl(root, "os") and @hasDecl(root.os, "PATH_MAX"))
5665 root.os.PATH_MAX
5766 else
58 @compileError("PATH_MAX not implemented for " ++ @tagName(builtin.os.tag)),
67 @compileError("PATH_MAX not implemented for " ++ @tagName(native_os)),
5968};
6069
6170/// This represents the maximum size of a `[]u8` file name component that
......@@ -66,22 +75,22 @@ pub const MAX_PATH_BYTES = switch (builtin.os.tag) {
6675/// On Windows, `[]u8` file name components are encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
6776/// On WASI, file name components are encoded as valid UTF-8.
6877/// On other platforms, `[]u8` components are an opaque sequence of bytes with no particular encoding.
69pub const MAX_NAME_BYTES = switch (builtin.os.tag) {
70 .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .illumos => os.NAME_MAX,
78pub const MAX_NAME_BYTES = switch (native_os) {
79 .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .illumos => posix.NAME_MAX,
7180 // Haiku's NAME_MAX includes the null terminator, so subtract one.
72 .haiku => os.NAME_MAX - 1,
81 .haiku => posix.NAME_MAX - 1,
7382 // Each WTF-16LE character may be expanded to 3 WTF-8 bytes.
7483 // If it would require 4 WTF-8 bytes, then there would be a surrogate
7584 // pair in the WTF-16LE, and we (over)account 3 bytes for it that way.
76 .windows => os.windows.NAME_MAX * 3,
85 .windows => windows.NAME_MAX * 3,
7786 // For WASI, the MAX_NAME will depend on the host OS, so it needs to be
7887 // as large as the largest MAX_NAME_BYTES (Windows) in order to work on any host OS.
7988 // TODO determine if this is a reasonable approach
80 .wasi => os.windows.NAME_MAX * 3,
89 .wasi => windows.NAME_MAX * 3,
8190 else => if (@hasDecl(root, "os") and @hasDecl(root.os, "NAME_MAX"))
8291 root.os.NAME_MAX
8392 else
84 @compileError("NAME_MAX not implemented for " ++ @tagName(builtin.os.tag)),
93 @compileError("NAME_MAX not implemented for " ++ @tagName(native_os)),
8594};
8695
8796pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*;
......@@ -167,19 +176,19 @@ pub fn copyFileAbsolute(
167176/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding.
168177pub fn makeDirAbsolute(absolute_path: []const u8) !void {
169178 assert(path.isAbsolute(absolute_path));
170 return os.mkdir(absolute_path, Dir.default_mode);
179 return posix.mkdir(absolute_path, Dir.default_mode);
171180}
172181
173182/// Same as `makeDirAbsolute` except the parameter is null-terminated.
174183pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void {
175184 assert(path.isAbsoluteZ(absolute_path_z));
176 return os.mkdirZ(absolute_path_z, Dir.default_mode);
185 return posix.mkdirZ(absolute_path_z, Dir.default_mode);
177186}
178187
179188/// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 LE-encoded string.
180189pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {
181190 assert(path.isAbsoluteWindowsW(absolute_path_w));
182 return os.mkdirW(absolute_path_w, Dir.default_mode);
191 return posix.mkdirW(absolute_path_w, Dir.default_mode);
183192}
184193
185194/// Same as `Dir.deleteDir` except the path is absolute.
......@@ -188,19 +197,19 @@ pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {
188197/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
189198pub fn deleteDirAbsolute(dir_path: []const u8) !void {
190199 assert(path.isAbsolute(dir_path));
191 return os.rmdir(dir_path);
200 return posix.rmdir(dir_path);
192201}
193202
194203/// Same as `deleteDirAbsolute` except the path parameter is null-terminated.
195204pub fn deleteDirAbsoluteZ(dir_path: [*:0]const u8) !void {
196205 assert(path.isAbsoluteZ(dir_path));
197 return os.rmdirZ(dir_path);
206 return posix.rmdirZ(dir_path);
198207}
199208
200209/// Same as `deleteDirAbsolute` except the path parameter is WTF-16 and target OS is assumed Windows.
201210pub fn deleteDirAbsoluteW(dir_path: [*:0]const u16) !void {
202211 assert(path.isAbsoluteWindowsW(dir_path));
203 return os.rmdirW(dir_path);
212 return posix.rmdirW(dir_path);
204213}
205214
206215/// Same as `Dir.rename` except the paths are absolute.
......@@ -210,49 +219,49 @@ pub fn deleteDirAbsoluteW(dir_path: [*:0]const u16) !void {
210219pub fn renameAbsolute(old_path: []const u8, new_path: []const u8) !void {
211220 assert(path.isAbsolute(old_path));
212221 assert(path.isAbsolute(new_path));
213 return os.rename(old_path, new_path);
222 return posix.rename(old_path, new_path);
214223}
215224
216225/// Same as `renameAbsolute` except the path parameters are null-terminated.
217226pub fn renameAbsoluteZ(old_path: [*:0]const u8, new_path: [*:0]const u8) !void {
218227 assert(path.isAbsoluteZ(old_path));
219228 assert(path.isAbsoluteZ(new_path));
220 return os.renameZ(old_path, new_path);
229 return posix.renameZ(old_path, new_path);
221230}
222231
223232/// Same as `renameAbsolute` except the path parameters are WTF-16 and target OS is assumed Windows.
224233pub fn renameAbsoluteW(old_path: [*:0]const u16, new_path: [*:0]const u16) !void {
225234 assert(path.isAbsoluteWindowsW(old_path));
226235 assert(path.isAbsoluteWindowsW(new_path));
227 return os.renameW(old_path, new_path);
236 return posix.renameW(old_path, new_path);
228237}
229238
230239/// Same as `Dir.rename`, except `new_sub_path` is relative to `new_dir`
231240pub fn rename(old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8) !void {
232 return os.renameat(old_dir.fd, old_sub_path, new_dir.fd, new_sub_path);
241 return posix.renameat(old_dir.fd, old_sub_path, new_dir.fd, new_sub_path);
233242}
234243
235244/// Same as `rename` except the parameters are null-terminated.
236245pub fn renameZ(old_dir: Dir, old_sub_path_z: [*:0]const u8, new_dir: Dir, new_sub_path_z: [*:0]const u8) !void {
237 return os.renameatZ(old_dir.fd, old_sub_path_z, new_dir.fd, new_sub_path_z);
246 return posix.renameatZ(old_dir.fd, old_sub_path_z, new_dir.fd, new_sub_path_z);
238247}
239248
240249/// Same as `rename` except the parameters are WTF16LE, NT prefixed.
241250/// This function is Windows-only.
242251pub fn renameW(old_dir: Dir, old_sub_path_w: []const u16, new_dir: Dir, new_sub_path_w: []const u16) !void {
243 return os.renameatW(old_dir.fd, old_sub_path_w, new_dir.fd, new_sub_path_w);
252 return posix.renameatW(old_dir.fd, old_sub_path_w, new_dir.fd, new_sub_path_w);
244253}
245254
246255/// Returns a handle to the current working directory. It is not opened with iteration capability.
247256/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.
248257/// On POSIX targets, this function is comptime-callable.
249258pub fn cwd() Dir {
250 if (builtin.os.tag == .windows) {
251 return .{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
252 } else if (builtin.os.tag == .wasi) {
259 if (native_os == .windows) {
260 return .{ .fd = windows.peb().ProcessParameters.CurrentDirectory.Handle };
261 } else if (native_os == .wasi) {
253262 return .{ .fd = std.options.wasiCwd() };
254263 } else {
255 return .{ .fd = os.AT.FDCWD };
264 return .{ .fd = posix.AT.FDCWD };
256265 }
257266}
258267
......@@ -412,20 +421,20 @@ pub fn deleteTreeAbsolute(absolute_path: []const u8) !void {
412421/// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding.
413422pub fn readLinkAbsolute(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
414423 assert(path.isAbsolute(pathname));
415 return os.readlink(pathname, buffer);
424 return posix.readlink(pathname, buffer);
416425}
417426
418427/// Windows-only. Same as `readlinkW`, except the path parameter is null-terminated, WTF16
419428/// encoded.
420429pub fn readlinkAbsoluteW(pathname_w: [*:0]const u16, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
421430 assert(path.isAbsoluteWindowsW(pathname_w));
422 return os.readlinkW(pathname_w, buffer);
431 return posix.readlinkW(pathname_w, buffer);
423432}
424433
425434/// Same as `readLink`, except the path parameter is null-terminated.
426435pub fn readLinkAbsoluteZ(pathname_c: [*:0]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
427436 assert(path.isAbsoluteZ(pathname_c));
428 return os.readlinkZ(pathname_c, buffer);
437 return posix.readlinkZ(pathname_c, buffer);
429438}
430439
431440/// Creates a symbolic link named `sym_link_path` which contains the string `target_path`.
......@@ -443,12 +452,12 @@ pub fn symLinkAbsolute(
443452) !void {
444453 assert(path.isAbsolute(target_path));
445454 assert(path.isAbsolute(sym_link_path));
446 if (builtin.os.tag == .windows) {
447 const target_path_w = try os.windows.sliceToPrefixedFileW(null, target_path);
448 const sym_link_path_w = try os.windows.sliceToPrefixedFileW(null, sym_link_path);
449 return os.windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
455 if (native_os == .windows) {
456 const target_path_w = try windows.sliceToPrefixedFileW(null, target_path);
457 const sym_link_path_w = try windows.sliceToPrefixedFileW(null, sym_link_path);
458 return windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
450459 }
451 return os.symlink(target_path, sym_link_path);
460 return posix.symlink(target_path, sym_link_path);
452461}
453462
454463/// Windows-only. Same as `symLinkAbsolute` except the parameters are null-terminated, WTF16 LE encoded.
......@@ -462,7 +471,7 @@ pub fn symLinkAbsoluteW(
462471) !void {
463472 assert(path.isAbsoluteWindowsWTF16(target_path_w));
464473 assert(path.isAbsoluteWindowsWTF16(sym_link_path_w));
465 return os.windows.CreateSymbolicLink(null, sym_link_path_w, target_path_w, flags.is_directory);
474 return windows.CreateSymbolicLink(null, sym_link_path_w, target_path_w, flags.is_directory);
466475}
467476
468477/// Same as `symLinkAbsolute` except the parameters are null-terminated pointers.
......@@ -474,27 +483,27 @@ pub fn symLinkAbsoluteZ(
474483) !void {
475484 assert(path.isAbsoluteZ(target_path_c));
476485 assert(path.isAbsoluteZ(sym_link_path_c));
477 if (builtin.os.tag == .windows) {
478 const target_path_w = try os.windows.cStrToPrefixedFileW(null, target_path_c);
479 const sym_link_path_w = try os.windows.cStrToPrefixedFileW(null, sym_link_path_c);
480 return os.windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
486 if (native_os == .windows) {
487 const target_path_w = try windows.cStrToPrefixedFileW(null, target_path_c);
488 const sym_link_path_w = try windows.cStrToPrefixedFileW(null, sym_link_path_c);
489 return windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
481490 }
482 return os.symlinkZ(target_path_c, sym_link_path_c);
491 return posix.symlinkZ(target_path_c, sym_link_path_c);
483492}
484493
485pub const OpenSelfExeError = os.OpenError || SelfExePathError || os.FlockError;
494pub const OpenSelfExeError = posix.OpenError || SelfExePathError || posix.FlockError;
486495
487496pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {
488 if (builtin.os.tag == .linux) {
497 if (native_os == .linux) {
489498 return openFileAbsoluteZ("/proc/self/exe", flags);
490499 }
491 if (builtin.os.tag == .windows) {
500 if (native_os == .windows) {
492501 // If ImagePathName is a symlink, then it will contain the path of the symlink,
493502 // not the path that the symlink points to. However, because we are opening
494503 // the file, we can let the openFileW call follow the symlink for us.
495 const image_path_unicode_string = &os.windows.peb().ProcessParameters.ImagePathName;
504 const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
496505 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
497 const prefixed_path_w = try os.windows.wToPrefixedFileW(null, image_path_name);
506 const prefixed_path_w = try windows.wToPrefixedFileW(null, image_path_name);
498507 return cwd().openFileW(prefixed_path_w.span(), flags);
499508 }
500509 // Use of MAX_PATH_BYTES here is valid as the resulting path is immediately
......@@ -505,7 +514,7 @@ pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {
505514 return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, flags);
506515}
507516
508// This is os.ReadLinkError || os.RealPathError with impossible errors excluded
517// This is `posix.ReadLinkError || posix.RealPathError` with impossible errors excluded
509518pub const SelfExePathError = error{
510519 FileNotFound,
511520 AccessDenied,
......@@ -542,7 +551,7 @@ pub const SelfExePathError = error{
542551 /// On Windows, the volume does not contain a recognized file system. File
543552 /// system drivers might not be loaded, or the volume may be corrupt.
544553 UnrecognizedVolume,
545} || os.SysCtlError;
554} || posix.SysCtlError;
546555
547556/// `selfExePath` except allocates the result on the heap.
548557/// Caller owns returned memory.
......@@ -580,7 +589,7 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
580589 if (rc != 0) return error.NameTooLong;
581590
582591 var real_path_buf: [MAX_PATH_BYTES]u8 = undefined;
583 const real_path = std.os.realpathZ(&symlink_path_buf, &real_path_buf) catch |err| switch (err) {
592 const real_path = std.posix.realpathZ(&symlink_path_buf, &real_path_buf) catch |err| switch (err) {
584593 error.InvalidWtf8 => unreachable, // Windows-only
585594 error.NetworkNotFound => unreachable, // Windows-only
586595 else => |e| return e,
......@@ -590,15 +599,15 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
590599 @memcpy(result, real_path);
591600 return result;
592601 }
593 switch (builtin.os.tag) {
594 .linux => return os.readlinkZ("/proc/self/exe", out_buffer) catch |err| switch (err) {
602 switch (native_os) {
603 .linux => return posix.readlinkZ("/proc/self/exe", out_buffer) catch |err| switch (err) {
595604 error.InvalidUtf8 => unreachable, // WASI-only
596605 error.InvalidWtf8 => unreachable, // Windows-only
597606 error.UnsupportedReparsePointType => unreachable, // Windows-only
598607 error.NetworkNotFound => unreachable, // Windows-only
599608 else => |e| return e,
600609 },
601 .solaris, .illumos => return os.readlinkZ("/proc/self/path/a.out", out_buffer) catch |err| switch (err) {
610 .solaris, .illumos => return posix.readlinkZ("/proc/self/path/a.out", out_buffer) catch |err| switch (err) {
602611 error.InvalidUtf8 => unreachable, // WASI-only
603612 error.InvalidWtf8 => unreachable, // Windows-only
604613 error.UnsupportedReparsePointType => unreachable, // Windows-only
......@@ -606,29 +615,29 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
606615 else => |e| return e,
607616 },
608617 .freebsd, .dragonfly => {
609 var mib = [4]c_int{ os.CTL.KERN, os.KERN.PROC, os.KERN.PROC_PATHNAME, -1 };
618 var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC, posix.KERN.PROC_PATHNAME, -1 };
610619 var out_len: usize = out_buffer.len;
611 try os.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
620 try posix.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
612621 // TODO could this slice from 0 to out_len instead?
613622 return mem.sliceTo(out_buffer, 0);
614623 },
615624 .netbsd => {
616 var mib = [4]c_int{ os.CTL.KERN, os.KERN.PROC_ARGS, -1, os.KERN.PROC_PATHNAME };
625 var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC_ARGS, -1, posix.KERN.PROC_PATHNAME };
617626 var out_len: usize = out_buffer.len;
618 try os.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
627 try posix.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
619628 // TODO could this slice from 0 to out_len instead?
620629 return mem.sliceTo(out_buffer, 0);
621630 },
622631 .openbsd, .haiku => {
623632 // OpenBSD doesn't support getting the path of a running process, so try to guess it
624 if (os.argv.len == 0)
633 if (std.os.argv.len == 0)
625634 return error.FileNotFound;
626635
627 const argv0 = mem.span(os.argv[0]);
636 const argv0 = mem.span(std.os.argv[0]);
628637 if (mem.indexOf(u8, argv0, "/") != null) {
629638 // argv[0] is a path (relative or absolute): use realpath(3) directly
630639 var real_path_buf: [MAX_PATH_BYTES]u8 = undefined;
631 const real_path = os.realpathZ(os.argv[0], &real_path_buf) catch |err| switch (err) {
640 const real_path = posix.realpathZ(std.os.argv[0], &real_path_buf) catch |err| switch (err) {
632641 error.InvalidWtf8 => unreachable, // Windows-only
633642 error.NetworkNotFound => unreachable, // Windows-only
634643 else => |e| return e,
......@@ -640,17 +649,17 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
640649 return result;
641650 } else if (argv0.len != 0) {
642651 // argv[0] is not empty (and not a path): search it inside PATH
643 const PATH = std.os.getenvZ("PATH") orelse return error.FileNotFound;
652 const PATH = posix.getenvZ("PATH") orelse return error.FileNotFound;
644653 var path_it = mem.tokenizeScalar(u8, PATH, path.delimiter);
645654 while (path_it.next()) |a_path| {
646655 var resolved_path_buf: [MAX_PATH_BYTES - 1:0]u8 = undefined;
647656 const resolved_path = std.fmt.bufPrintZ(&resolved_path_buf, "{s}/{s}", .{
648657 a_path,
649 os.argv[0],
658 std.os.argv[0],
650659 }) catch continue;
651660
652661 var real_path_buf: [MAX_PATH_BYTES]u8 = undefined;
653 if (os.realpathZ(resolved_path, &real_path_buf)) |real_path| {
662 if (posix.realpathZ(resolved_path, &real_path_buf)) |real_path| {
654663 // found a file, and hope it is the right file
655664 if (real_path.len > out_buffer.len)
656665 return error.NameTooLong;
......@@ -663,13 +672,13 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
663672 return error.FileNotFound;
664673 },
665674 .windows => {
666 const image_path_unicode_string = &os.windows.peb().ProcessParameters.ImagePathName;
675 const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
667676 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
668677
669678 // If ImagePathName is a symlink, then it will contain the path of the
670679 // symlink, not the path that the symlink points to. We want the path
671680 // that the symlink points to, though, so we need to get the realpath.
672 const pathname_w = try os.windows.wToPrefixedFileW(null, image_path_name);
681 const pathname_w = try windows.wToPrefixedFileW(null, image_path_name);
673682 return std.fs.cwd().realpathW(pathname_w.span(), out_buffer) catch |err| switch (err) {
674683 error.InvalidWtf8 => unreachable,
675684 else => |e| return e,
......@@ -718,11 +727,11 @@ pub fn realpathAlloc(allocator: Allocator, pathname: []const u8) ![]u8 {
718727 // paths. musl supports passing NULL but restricts the output to PATH_MAX
719728 // anyway.
720729 var buf: [MAX_PATH_BYTES]u8 = undefined;
721 return allocator.dupe(u8, try os.realpath(pathname, &buf));
730 return allocator.dupe(u8, try posix.realpath(pathname, &buf));
722731}
723732
724733test {
725 if (builtin.os.tag != .wasi) {
734 if (native_os != .wasi) {
726735 _ = &makeDirAbsolute;
727736 _ = &makeDirAbsoluteZ;
728737 _ = &copyFileAbsolute;
lib/std/fs/AtomicFile.zig+1-2
......@@ -85,5 +85,4 @@ const File = std.fs.File;
8585const Dir = std.fs.Dir;
8686const fs = std.fs;
8787const assert = std.debug.assert;
88// https://github.com/ziglang/zig/issues/5019
89const posix = std.os;
88const posix = std.posix;
lib/std/fs/Dir.zig+75-80
......@@ -18,7 +18,7 @@ const IteratorError = error{
1818 InvalidUtf8,
1919} || posix.UnexpectedError;
2020
21pub const Iterator = switch (builtin.os.tag) {
21pub const Iterator = switch (native_os) {
2222 .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris, .illumos => struct {
2323 dir: Dir,
2424 seek: i64,
......@@ -34,7 +34,7 @@ pub const Iterator = switch (builtin.os.tag) {
3434 /// Memory such as file names referenced in this returned entry becomes invalid
3535 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
3636 pub fn next(self: *Self) Error!?Entry {
37 switch (builtin.os.tag) {
37 switch (native_os) {
3838 .macos, .ios => return self.nextDarwin(),
3939 .freebsd, .netbsd, .dragonfly, .openbsd => return self.nextBsd(),
4040 .solaris, .illumos => return self.nextSolaris(),
......@@ -183,7 +183,7 @@ pub const Iterator = switch (builtin.os.tag) {
183183
184184 const name = @as([*]u8, @ptrCast(&bsd_entry.name))[0..bsd_entry.namlen];
185185
186 const skip_zero_fileno = switch (builtin.os.tag) {
186 const skip_zero_fileno = switch (native_os) {
187187 // fileno=0 is used to mark invalid entries or deleted files.
188188 .openbsd, .netbsd => true,
189189 else => false,
......@@ -315,7 +315,7 @@ pub const Iterator = switch (builtin.os.tag) {
315315 dir: Dir,
316316 // The if guard is solely there to prevent compile errors from missing `linux.dirent64`
317317 // definition when compiling for other OSes. It doesn't do anything when compiling for Linux.
318 buf: [1024]u8 align(if (builtin.os.tag != .linux) 1 else @alignOf(linux.dirent64)),
318 buf: [1024]u8 align(if (native_os != .linux) 1 else @alignOf(linux.dirent64)),
319319 index: usize,
320320 end_index: usize,
321321 first_iter: bool,
......@@ -348,7 +348,7 @@ pub const Iterator = switch (builtin.os.tag) {
348348 self.first_iter = false;
349349 }
350350 const rc = linux.getdents64(self.dir.fd, &self.buf, self.buf.len);
351 switch (linux.getErrno(rc)) {
351 switch (linux.E.init(rc)) {
352352 .SUCCESS => {},
353353 .BADF => unreachable, // Dir is invalid or was opened without iteration ability
354354 .FAULT => unreachable,
......@@ -398,7 +398,7 @@ pub const Iterator = switch (builtin.os.tag) {
398398 },
399399 .windows => struct {
400400 dir: Dir,
401 buf: [1024]u8 align(@alignOf(std.os.windows.FILE_BOTH_DIR_INFORMATION)),
401 buf: [1024]u8 align(@alignOf(windows.FILE_BOTH_DIR_INFORMATION)),
402402 index: usize,
403403 end_index: usize,
404404 first_iter: bool,
......@@ -411,8 +411,8 @@ pub const Iterator = switch (builtin.os.tag) {
411411 /// Memory such as file names referenced in this returned entry becomes invalid
412412 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
413413 pub fn next(self: *Self) Error!?Entry {
414 const w = windows;
414415 while (true) {
415 const w = std.os.windows;
416416 if (self.index >= self.end_index) {
417417 var io: w.IO_STATUS_BLOCK = undefined;
418418 const rc = w.ntdll.NtQueryDirectoryFile(
......@@ -582,7 +582,7 @@ pub fn iterateAssumeFirstIteration(self: Dir) Iterator {
582582}
583583
584584fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator {
585 switch (builtin.os.tag) {
585 switch (native_os) {
586586 .macos,
587587 .ios,
588588 .freebsd,
......@@ -770,11 +770,11 @@ pub fn close(self: *Dir) void {
770770/// On WASI, `sub_path` should be encoded as valid UTF-8.
771771/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
772772pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
773 if (builtin.os.tag == .windows) {
774 const path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path);
773 if (native_os == .windows) {
774 const path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
775775 return self.openFileW(path_w.span(), flags);
776776 }
777 if (builtin.os.tag == .wasi) {
777 if (native_os == .wasi) {
778778 var base: std.os.wasi.rights_t = .{};
779779 if (flags.isRead()) {
780780 base.FD_READ = true;
......@@ -803,9 +803,9 @@ pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.Ope
803803
804804/// Same as `openFile` but the path parameter is null-terminated.
805805pub fn openFileZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File {
806 switch (builtin.os.tag) {
806 switch (native_os) {
807807 .windows => {
808 const path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, sub_path);
808 const path_w = try windows.cStrToPrefixedFileW(self.fd, sub_path);
809809 return self.openFileW(path_w.span(), flags);
810810 },
811811 .wasi => {
......@@ -884,7 +884,7 @@ pub fn openFileZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File
884884/// Same as `openFile` but Windows-only and the path parameter is
885885/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
886886pub fn openFileW(self: Dir, sub_path_w: []const u16, flags: File.OpenFlags) File.OpenError!File {
887 const w = std.os.windows;
887 const w = windows;
888888 const file: File = .{
889889 .handle = try w.OpenFile(sub_path_w, .{
890890 .dir = self.fd,
......@@ -925,11 +925,11 @@ pub fn openFileW(self: Dir, sub_path_w: []const u16, flags: File.OpenFlags) File
925925/// On WASI, `sub_path` should be encoded as valid UTF-8.
926926/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
927927pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
928 if (builtin.os.tag == .windows) {
929 const path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path);
928 if (native_os == .windows) {
929 const path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
930930 return self.createFileW(path_w.span(), flags);
931931 }
932 if (builtin.os.tag == .wasi) {
932 if (native_os == .wasi) {
933933 return .{
934934 .handle = try posix.openatWasi(self.fd, sub_path, .{}, .{
935935 .CREAT = true,
......@@ -957,9 +957,9 @@ pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File
957957
958958/// Same as `createFile` but the path parameter is null-terminated.
959959pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File {
960 switch (builtin.os.tag) {
960 switch (native_os) {
961961 .windows => {
962 const path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, sub_path_c);
962 const path_w = try windows.cStrToPrefixedFileW(self.fd, sub_path_c);
963963 return self.createFileW(path_w.span(), flags);
964964 },
965965 .wasi => {
......@@ -968,7 +968,7 @@ pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags
968968 else => {},
969969 }
970970
971 var os_flags: std.os.O = .{
971 var os_flags: posix.O = .{
972972 .ACCMODE = if (flags.read) .RDWR else .WRONLY,
973973 .CREAT = true,
974974 .TRUNC = flags.truncate,
......@@ -1032,7 +1032,7 @@ pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags
10321032/// Same as `createFile` but Windows-only and the path parameter is
10331033/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
10341034pub fn createFileW(self: Dir, sub_path_w: []const u16, flags: File.CreateFlags) File.OpenError!File {
1035 const w = std.os.windows;
1035 const w = windows;
10361036 const read_flag = if (flags.read) @as(u32, w.GENERIC_READ) else 0;
10371037 const file: File = .{
10381038 .handle = try w.OpenFile(sub_path_w, .{
......@@ -1145,7 +1145,7 @@ pub fn makePath(self: Dir, sub_path: []const u8) !void {
11451145/// have been modified regardless.
11461146/// `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
11471147fn makeOpenPathAccessMaskW(self: Dir, sub_path: []const u8, access_mask: u32, no_follow: bool) OpenError!Dir {
1148 const w = std.os.windows;
1148 const w = windows;
11491149 var it = try fs.path.componentIterator(sub_path);
11501150 // If there are no components in the path, then create a dummy component with the full path.
11511151 var component = it.last() orelse fs.path.NativeComponentIterator.Component{
......@@ -1180,9 +1180,9 @@ fn makeOpenPathAccessMaskW(self: Dir, sub_path: []const u8, access_mask: u32, no
11801180/// On WASI, `sub_path` should be encoded as valid UTF-8.
11811181/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
11821182pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOptions) !Dir {
1183 return switch (builtin.os.tag) {
1183 return switch (native_os) {
11841184 .windows => {
1185 const w = std.os.windows;
1185 const w = windows;
11861186 const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |
11871187 w.SYNCHRONIZE | w.FILE_TRAVERSE |
11881188 (if (open_dir_options.iterate) w.FILE_LIST_DIRECTORY else @as(u32, 0));
......@@ -1215,11 +1215,11 @@ pub const RealPathError = posix.RealPathError;
12151215/// Currently supported hosts are: Linux, macOS, and Windows.
12161216/// See also `Dir.realpathZ`, `Dir.realpathW`, and `Dir.realpathAlloc`.
12171217pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError![]u8 {
1218 if (builtin.os.tag == .wasi) {
1218 if (native_os == .wasi) {
12191219 @compileError("realpath is not available on WASI");
12201220 }
1221 if (builtin.os.tag == .windows) {
1222 const pathname_w = try std.os.windows.sliceToPrefixedFileW(self.fd, pathname);
1221 if (native_os == .windows) {
1222 const pathname_w = try windows.sliceToPrefixedFileW(self.fd, pathname);
12231223 return self.realpathW(pathname_w.span(), out_buffer);
12241224 }
12251225 const pathname_c = try posix.toPosixPath(pathname);
......@@ -1229,12 +1229,12 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError
12291229/// Same as `Dir.realpath` except `pathname` is null-terminated.
12301230/// See also `Dir.realpath`, `realpathZ`.
12311231pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathError![]u8 {
1232 if (builtin.os.tag == .windows) {
1233 const pathname_w = try posix.windows.cStrToPrefixedFileW(self.fd, pathname);
1232 if (native_os == .windows) {
1233 const pathname_w = try windows.cStrToPrefixedFileW(self.fd, pathname);
12341234 return self.realpathW(pathname_w.span(), out_buffer);
12351235 }
12361236
1237 const flags: posix.O = switch (builtin.os.tag) {
1237 const flags: posix.O = switch (native_os) {
12381238 .linux => .{
12391239 .NONBLOCK = true,
12401240 .CLOEXEC = true,
......@@ -1255,14 +1255,8 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathE
12551255 };
12561256 defer posix.close(fd);
12571257
1258 // Use of MAX_PATH_BYTES here is valid as the realpath function does not
1259 // have a variant that takes an arbitrary-size buffer.
1260 // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008
1261 // NULL out parameter (GNU's canonicalize_file_name) to handle overelong
1262 // paths. musl supports passing NULL but restricts the output to PATH_MAX
1263 // anyway.
12641258 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
1265 const out_path = try posix.getFdPath(fd, &buffer);
1259 const out_path = try std.os.getFdPath(fd, &buffer);
12661260
12671261 if (out_path.len > out_buffer.len) {
12681262 return error.NameTooLong;
......@@ -1277,7 +1271,7 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathE
12771271/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
12781272/// See also `Dir.realpath`, `realpathW`.
12791273pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) RealPathError![]u8 {
1280 const w = std.os.windows;
1274 const w = windows;
12811275
12821276 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
12831277 const share_access = w.FILE_SHARE_READ;
......@@ -1331,16 +1325,16 @@ pub fn realpathAlloc(self: Dir, allocator: Allocator, pathname: []const u8) Real
13311325/// Not all targets support this. For example, WASI does not have the concept
13321326/// of a current working directory.
13331327pub fn setAsCwd(self: Dir) !void {
1334 if (builtin.os.tag == .wasi) {
1328 if (native_os == .wasi) {
13351329 @compileError("changing cwd is not currently possible in WASI");
13361330 }
1337 if (builtin.os.tag == .windows) {
1338 var dir_path_buffer: [std.os.windows.PATH_MAX_WIDE]u16 = undefined;
1339 const dir_path = try std.os.windows.GetFinalPathNameByHandle(self.fd, .{}, &dir_path_buffer);
1331 if (native_os == .windows) {
1332 var dir_path_buffer: [windows.PATH_MAX_WIDE]u16 = undefined;
1333 const dir_path = try windows.GetFinalPathNameByHandle(self.fd, .{}, &dir_path_buffer);
13401334 if (builtin.link_libc) {
13411335 return posix.chdirW(dir_path);
13421336 }
1343 return std.os.windows.SetCurrentDirectory(dir_path);
1337 return windows.SetCurrentDirectory(dir_path);
13441338 }
13451339 try posix.fchdir(self.fd);
13461340}
......@@ -1368,9 +1362,9 @@ pub const OpenDirOptions = struct {
13681362/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
13691363/// Asserts that the path parameter has no null bytes.
13701364pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir {
1371 switch (builtin.os.tag) {
1365 switch (native_os) {
13721366 .windows => {
1373 const sub_path_w = try posix.windows.sliceToPrefixedFileW(self.fd, sub_path);
1367 const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
13741368 return self.openDirW(sub_path_w.span().ptr, args);
13751369 },
13761370 .wasi => {
......@@ -1427,9 +1421,9 @@ pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!
14271421
14281422/// Same as `openDir` except the parameter is null-terminated.
14291423pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir {
1430 switch (builtin.os.tag) {
1424 switch (native_os) {
14311425 .windows => {
1432 const sub_path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, sub_path_c);
1426 const sub_path_w = try windows.cStrToPrefixedFileW(self.fd, sub_path_c);
14331427 return self.openDirW(sub_path_w.span().ptr, args);
14341428 },
14351429 .wasi => {
......@@ -1453,7 +1447,7 @@ pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) Open
14531447/// Same as `openDir` except the path parameter is WTF-16 LE encoded, NT-prefixed.
14541448/// This function asserts the target OS is Windows.
14551449pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenDirOptions) OpenError!Dir {
1456 const w = std.os.windows;
1450 const w = windows;
14571451 // TODO remove some of these flags if args.access_sub_paths is false
14581452 const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |
14591453 w.SYNCHRONIZE | w.FILE_TRAVERSE;
......@@ -1487,7 +1481,7 @@ const MakeOpenDirAccessMaskWOptions = struct {
14871481};
14881482
14891483fn makeOpenDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32, flags: MakeOpenDirAccessMaskWOptions) OpenError!Dir {
1490 const w = std.os.windows;
1484 const w = windows;
14911485
14921486 var result = Dir{
14931487 .fd = undefined,
......@@ -1545,10 +1539,10 @@ pub const DeleteFileError = posix.UnlinkError;
15451539/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
15461540/// Asserts that the path parameter has no null bytes.
15471541pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
1548 if (builtin.os.tag == .windows) {
1549 const sub_path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path);
1542 if (native_os == .windows) {
1543 const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
15501544 return self.deleteFileW(sub_path_w.span());
1551 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
1545 } else if (native_os == .wasi and !builtin.link_libc) {
15521546 posix.unlinkat(self.fd, sub_path, 0) catch |err| switch (err) {
15531547 error.DirNotEmpty => unreachable, // not passing AT.REMOVEDIR
15541548 else => |e| return e,
......@@ -1563,7 +1557,7 @@ pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
15631557pub fn deleteFileZ(self: Dir, sub_path_c: [*:0]const u8) DeleteFileError!void {
15641558 posix.unlinkatZ(self.fd, sub_path_c, 0) catch |err| switch (err) {
15651559 error.DirNotEmpty => unreachable, // not passing AT.REMOVEDIR
1566 error.AccessDenied => |e| switch (builtin.os.tag) {
1560 error.AccessDenied => |e| switch (native_os) {
15671561 // non-Linux POSIX systems return EPERM when trying to delete a directory, so
15681562 // we need to handle that case specifically and translate the error
15691563 .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris, .illumos => {
......@@ -1615,10 +1609,10 @@ pub const DeleteDirError = error{
16151609/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
16161610/// Asserts that the path parameter has no null bytes.
16171611pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
1618 if (builtin.os.tag == .windows) {
1619 const sub_path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path);
1612 if (native_os == .windows) {
1613 const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
16201614 return self.deleteDirW(sub_path_w.span());
1621 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
1615 } else if (native_os == .wasi and !builtin.link_libc) {
16221616 posix.unlinkat(self.fd, sub_path, posix.AT.REMOVEDIR) catch |err| switch (err) {
16231617 error.IsDir => unreachable, // not possible since we pass AT.REMOVEDIR
16241618 else => |e| return e,
......@@ -1691,15 +1685,15 @@ pub fn symLink(
16911685 sym_link_path: []const u8,
16921686 flags: SymLinkFlags,
16931687) !void {
1694 if (builtin.os.tag == .wasi and !builtin.link_libc) {
1688 if (native_os == .wasi and !builtin.link_libc) {
16951689 return self.symLinkWasi(target_path, sym_link_path, flags);
16961690 }
1697 if (builtin.os.tag == .windows) {
1691 if (native_os == .windows) {
16981692 // Target path does not use sliceToPrefixedFileW because certain paths
16991693 // are handled differently when creating a symlink than they would be
17001694 // when converting to an NT namespaced path. CreateSymbolicLink in
17011695 // symLinkW will handle the necessary conversion.
1702 var target_path_w: std.os.windows.PathSpace = undefined;
1696 var target_path_w: windows.PathSpace = undefined;
17031697 target_path_w.len = try std.unicode.wtf8ToWtf16Le(&target_path_w.data, target_path);
17041698 target_path_w.data[target_path_w.len] = 0;
17051699 // However, we need to canonicalize any path separators to `\`, since if
......@@ -1711,7 +1705,7 @@ pub fn symLink(
17111705 mem.nativeToLittle(u16, '\\'),
17121706 );
17131707
1714 const sym_link_path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sym_link_path);
1708 const sym_link_path_w = try windows.sliceToPrefixedFileW(self.fd, sym_link_path);
17151709 return self.symLinkW(target_path_w.span(), sym_link_path_w.span(), flags);
17161710 }
17171711 const target_path_c = try posix.toPosixPath(target_path);
......@@ -1736,9 +1730,9 @@ pub fn symLinkZ(
17361730 sym_link_path_c: [*:0]const u8,
17371731 flags: SymLinkFlags,
17381732) !void {
1739 if (builtin.os.tag == .windows) {
1740 const target_path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, target_path_c);
1741 const sym_link_path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, sym_link_path_c);
1733 if (native_os == .windows) {
1734 const target_path_w = try windows.cStrToPrefixedFileW(self.fd, target_path_c);
1735 const sym_link_path_w = try windows.cStrToPrefixedFileW(self.fd, sym_link_path_c);
17421736 return self.symLinkW(target_path_w.span(), sym_link_path_w.span(), flags);
17431737 }
17441738 return posix.symlinkatZ(target_path_c, self.fd, sym_link_path_c);
......@@ -1756,7 +1750,7 @@ pub fn symLinkW(
17561750 sym_link_path_w: []const u16,
17571751 flags: SymLinkFlags,
17581752) !void {
1759 return std.os.windows.CreateSymbolicLink(self.fd, sym_link_path_w, target_path_w, flags.is_directory);
1753 return windows.CreateSymbolicLink(self.fd, sym_link_path_w, target_path_w, flags.is_directory);
17601754}
17611755
17621756pub const ReadLinkError = posix.ReadLinkError;
......@@ -1768,11 +1762,11 @@ pub const ReadLinkError = posix.ReadLinkError;
17681762/// On WASI, `sub_path` should be encoded as valid UTF-8.
17691763/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
17701764pub fn readLink(self: Dir, sub_path: []const u8, buffer: []u8) ReadLinkError![]u8 {
1771 if (builtin.os.tag == .wasi and !builtin.link_libc) {
1765 if (native_os == .wasi and !builtin.link_libc) {
17721766 return self.readLinkWasi(sub_path, buffer);
17731767 }
1774 if (builtin.os.tag == .windows) {
1775 const sub_path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path);
1768 if (native_os == .windows) {
1769 const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
17761770 return self.readLinkW(sub_path_w.span(), buffer);
17771771 }
17781772 const sub_path_c = try posix.toPosixPath(sub_path);
......@@ -1786,8 +1780,8 @@ pub fn readLinkWasi(self: Dir, sub_path: []const u8, buffer: []u8) ![]u8 {
17861780
17871781/// Same as `readLink`, except the `sub_path_c` parameter is null-terminated.
17881782pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {
1789 if (builtin.os.tag == .windows) {
1790 const sub_path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, sub_path_c);
1783 if (native_os == .windows) {
1784 const sub_path_w = try windows.cStrToPrefixedFileW(self.fd, sub_path_c);
17911785 return self.readLinkW(sub_path_w.span(), buffer);
17921786 }
17931787 return posix.readlinkatZ(self.fd, sub_path_c, buffer);
......@@ -1796,7 +1790,7 @@ pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {
17961790/// Windows-only. Same as `readLink` except the pathname parameter
17971791/// is WTF16 LE encoded.
17981792pub fn readLinkW(self: Dir, sub_path_w: []const u16, buffer: []u8) ![]u8 {
1799 return std.os.windows.ReadLink(self.fd, sub_path_w, buffer);
1793 return windows.ReadLink(self.fd, sub_path_w, buffer);
18001794}
18011795
18021796/// Read all of file contents using a preallocated buffer.
......@@ -2319,8 +2313,8 @@ pub const AccessError = posix.AccessError;
23192313/// For example, instead of testing if a file exists and then opening it, just
23202314/// open it and handle the error for file not found.
23212315pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void {
2322 if (builtin.os.tag == .windows) {
2323 const sub_path_w = std.os.windows.sliceToPrefixedFileW(self.fd, sub_path) catch |err| switch (err) {
2316 if (native_os == .windows) {
2317 const sub_path_w = windows.sliceToPrefixedFileW(self.fd, sub_path) catch |err| switch (err) {
23242318 error.AccessDenied => return error.PermissionDenied,
23252319 else => |e| return e,
23262320 };
......@@ -2332,8 +2326,8 @@ pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessErro
23322326
23332327/// Same as `access` except the path parameter is null-terminated.
23342328pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void {
2335 if (builtin.os.tag == .windows) {
2336 const sub_path_w = std.os.windows.cStrToPrefixedFileW(self.fd, sub_path) catch |err| switch (err) {
2329 if (native_os == .windows) {
2330 const sub_path_w = windows.cStrToPrefixedFileW(self.fd, sub_path) catch |err| switch (err) {
23372331 error.AccessDenied => return error.PermissionDenied,
23382332 else => |e| return e,
23392333 };
......@@ -2355,7 +2349,7 @@ pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) Access
23552349/// TODO currently this ignores `flags`.
23562350pub fn accessW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) AccessError!void {
23572351 _ = flags;
2358 return posix.faccessatW(self.fd, sub_path_w, 0, 0);
2352 return posix.faccessatW(self.fd, sub_path_w);
23592353}
23602354
23612355pub const CopyFileOptions = struct {
......@@ -2473,7 +2467,7 @@ fn copy_file(fd_in: posix.fd_t, fd_out: posix.fd_t, maybe_size: ?u64) CopyFileRa
24732467 }
24742468 }
24752469
2476 if (builtin.os.tag == .linux) {
2470 if (native_os == .linux) {
24772471 // Try copy_file_range first as that works at the FS level and is the
24782472 // most efficient method (if available).
24792473 var offset: u64 = 0;
......@@ -2555,12 +2549,12 @@ pub const StatFileError = File.OpenError || File.StatError || posix.FStatAtError
25552549/// On WASI, `sub_path` should be encoded as valid UTF-8.
25562550/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
25572551pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat {
2558 if (builtin.os.tag == .windows) {
2552 if (native_os == .windows) {
25592553 var file = try self.openFile(sub_path, .{});
25602554 defer file.close();
25612555 return file.stat();
25622556 }
2563 if (builtin.os.tag == .wasi and !builtin.link_libc) {
2557 if (native_os == .wasi and !builtin.link_libc) {
25642558 const st = try posix.fstatat_wasi(self.fd, sub_path, .{ .SYMLINK_FOLLOW = true });
25652559 return Stat.fromWasi(st);
25662560 }
......@@ -2617,9 +2611,10 @@ const builtin = @import("builtin");
26172611const std = @import("../std.zig");
26182612const File = std.fs.File;
26192613const AtomicFile = std.fs.AtomicFile;
2620// https://github.com/ziglang/zig/issues/5019
2621const posix = std.os;
2614const posix = std.posix;
26222615const mem = std.mem;
26232616const fs = std.fs;
26242617const Allocator = std.mem.Allocator;
26252618const assert = std.debug.assert;
2619const windows = std.os.windows;
2620const native_os = builtin.os.tag;
lib/std/fs/File.zig+54-3
......@@ -193,6 +193,58 @@ pub fn isTty(self: File) bool {
193193 return posix.isatty(self.handle);
194194}
195195
196pub fn isCygwinPty(file: File) bool {
197 if (builtin.os.tag != .windows) return false;
198
199 const handle = file.handle;
200
201 // If this is a MSYS2/cygwin pty, then it will be a named pipe with a name in one of these formats:
202 // msys-[...]-ptyN-[...]
203 // cygwin-[...]-ptyN-[...]
204 //
205 // Example: msys-1888ae32e00d56aa-pty0-to-master
206
207 // First, just check that the handle is a named pipe.
208 // This allows us to avoid the more costly NtQueryInformationFile call
209 // for handles that aren't named pipes.
210 {
211 var io_status: windows.IO_STATUS_BLOCK = undefined;
212 var device_info: windows.FILE_FS_DEVICE_INFORMATION = undefined;
213 const rc = windows.ntdll.NtQueryVolumeInformationFile(handle, &io_status, &device_info, @sizeOf(windows.FILE_FS_DEVICE_INFORMATION), .FileFsDeviceInformation);
214 switch (rc) {
215 .SUCCESS => {},
216 else => return false,
217 }
218 if (device_info.DeviceType != windows.FILE_DEVICE_NAMED_PIPE) return false;
219 }
220
221 const name_bytes_offset = @offsetOf(windows.FILE_NAME_INFO, "FileName");
222 // `NAME_MAX` UTF-16 code units (2 bytes each)
223 // This buffer may not be long enough to handle *all* possible paths
224 // (PATH_MAX_WIDE would be necessary for that), but because we only care
225 // about certain paths and we know they must be within a reasonable length,
226 // we can use this smaller buffer and just return false on any error from
227 // NtQueryInformationFile.
228 const num_name_bytes = windows.MAX_PATH * 2;
229 var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (name_bytes_offset + num_name_bytes);
230
231 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
232 const rc = windows.ntdll.NtQueryInformationFile(handle, &io_status_block, &name_info_bytes, @intCast(name_info_bytes.len), .FileNameInformation);
233 switch (rc) {
234 .SUCCESS => {},
235 .INVALID_PARAMETER => unreachable,
236 else => return false,
237 }
238
239 const name_info: *const windows.FILE_NAME_INFO = @ptrCast(&name_info_bytes);
240 const name_bytes = name_info_bytes[name_bytes_offset .. name_bytes_offset + name_info.FileNameLength];
241 const name_wide = std.mem.bytesAsSlice(u16, name_bytes);
242 // The name we get from NtQueryInformationFile will be prefixed with a '\', e.g. \msys-1888ae32e00d56aa-pty0-to-master
243 return (std.mem.startsWith(u16, name_wide, &[_]u16{ '\\', 'm', 's', 'y', 's', '-' }) or
244 std.mem.startsWith(u16, name_wide, &[_]u16{ '\\', 'c', 'y', 'g', 'w', 'i', 'n', '-' })) and
245 std.mem.indexOf(u16, name_wide, &[_]u16{ '-', 'p', 't', 'y' }) != null;
246}
247
196248/// Test whether ANSI escape codes will be treated as such.
197249pub fn supportsAnsiEscapeCodes(self: File) bool {
198250 if (builtin.os.tag == .windows) {
......@@ -201,7 +253,7 @@ pub fn supportsAnsiEscapeCodes(self: File) bool {
201253 if (console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return true;
202254 }
203255
204 return posix.isCygwinPty(self.handle);
256 return self.isCygwinPty();
205257 }
206258 if (builtin.os.tag == .wasi) {
207259 // WASI sanitizes stdout when fd is a tty so ANSI escape codes
......@@ -1634,8 +1686,7 @@ const File = @This();
16341686const std = @import("../std.zig");
16351687const builtin = @import("builtin");
16361688const Allocator = std.mem.Allocator;
1637// https://github.com/ziglang/zig/issues/5019
1638const posix = std.os;
1689const posix = std.posix;
16391690const io = std.io;
16401691const math = std.math;
16411692const assert = std.debug.assert;
lib/std/fs/get_app_data_dir.zig+8-7
......@@ -3,7 +3,8 @@ const builtin = @import("builtin");
33const unicode = std.unicode;
44const mem = std.mem;
55const fs = std.fs;
6const os = std.os;
6const native_os = builtin.os.tag;
7const posix = std.posix;
78
89pub const GetAppDataDirError = error{
910 OutOfMemory,
......@@ -13,7 +14,7 @@ pub const GetAppDataDirError = error{
1314/// Caller owns returned memory.
1415/// TODO determine if we can remove the allocator requirement
1516pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
16 switch (builtin.os.tag) {
17 switch (native_os) {
1718 .windows => {
1819 const local_app_data_dir = std.process.getEnvVarOwned(allocator, "LOCALAPPDATA") catch |err| switch (err) {
1920 error.OutOfMemory => |e| return e,
......@@ -23,18 +24,18 @@ pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDi
2324 return fs.path.join(allocator, &[_][]const u8{ local_app_data_dir, appname });
2425 },
2526 .macos => {
26 const home_dir = os.getenv("HOME") orelse {
27 const home_dir = posix.getenv("HOME") orelse {
2728 // TODO look in /etc/passwd
2829 return error.AppDataDirUnavailable;
2930 };
3031 return fs.path.join(allocator, &[_][]const u8{ home_dir, "Library", "Application Support", appname });
3132 },
3233 .linux, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris, .illumos => {
33 if (os.getenv("XDG_DATA_HOME")) |xdg| {
34 if (posix.getenv("XDG_DATA_HOME")) |xdg| {
3435 return fs.path.join(allocator, &[_][]const u8{ xdg, appname });
3536 }
3637
37 const home_dir = os.getenv("HOME") orelse {
38 const home_dir = posix.getenv("HOME") orelse {
3839 // TODO look in /etc/passwd
3940 return error.AppDataDirUnavailable;
4041 };
......@@ -48,7 +49,7 @@ pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDi
4849 }
4950 // TODO look into directory_which
5051 const be_user_settings = 0xbbe;
51 const rc = os.system.find_directory(be_user_settings, -1, true, dir_path_ptr, 1);
52 const rc = std.c.find_directory(be_user_settings, -1, true, dir_path_ptr, 1);
5253 const settings_dir = try allocator.dupeZ(u8, mem.sliceTo(dir_path_ptr, 0));
5354 defer allocator.free(settings_dir);
5455 switch (rc) {
......@@ -61,7 +62,7 @@ pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDi
6162}
6263
6364test "getAppDataDir" {
64 if (builtin.os.tag == .wasi) return error.SkipZigTest;
65 if (native_os == .wasi) return error.SkipZigTest;
6566
6667 // We can't actually validate the result
6768 const dir = getAppDataDir(std.testing.allocator, "zig") catch return;
lib/std/fs/test.zig+71-71
......@@ -1,10 +1,12 @@
11const std = @import("../std.zig");
22const builtin = @import("builtin");
33const testing = std.testing;
4const os = std.os;
54const fs = std.fs;
65const mem = std.mem;
76const wasi = std.os.wasi;
7const native_os = builtin.os.tag;
8const windows = std.os.windows;
9const posix = std.posix;
810
911const ArenaAllocator = std.heap.ArenaAllocator;
1012const Dir = std.fs.Dir;
......@@ -25,7 +27,7 @@ const PathType = enum {
2527 };
2628 }
2729
28 pub const TransformError = std.os.RealPathError || error{OutOfMemory};
30 pub const TransformError = posix.RealPathError || error{OutOfMemory};
2931 pub const TransformFn = fn (allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8;
3032
3133 pub fn getTransformFn(comptime path_type: PathType) TransformFn {
......@@ -42,7 +44,7 @@ const PathType = enum {
4244 // The final path may not actually exist which would cause realpath to fail.
4345 // So instead, we get the path of the dir and join it with the relative path.
4446 var fd_path_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
45 const dir_path = try os.getFdPath(dir.fd, &fd_path_buf);
47 const dir_path = try std.os.getFdPath(dir.fd, &fd_path_buf);
4648 return fs.path.joinZ(allocator, &.{ dir_path, relative_path });
4749 }
4850 }.transform,
......@@ -51,8 +53,8 @@ const PathType = enum {
5153 // Any drive absolute path (C:\foo) can be converted into a UNC path by
5254 // using '127.0.0.1' as the server name and '<drive letter>$' as the share name.
5355 var fd_path_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
54 const dir_path = try os.getFdPath(dir.fd, &fd_path_buf);
55 const windows_path_type = std.os.windows.getUnprefixedPathType(u8, dir_path);
56 const dir_path = try std.os.getFdPath(dir.fd, &fd_path_buf);
57 const windows_path_type = windows.getUnprefixedPathType(u8, dir_path);
5658 switch (windows_path_type) {
5759 .unc_absolute => return fs.path.joinZ(allocator, &.{ dir_path, relative_path }),
5860 .drive_absolute => {
......@@ -102,7 +104,7 @@ const TestContext = struct {
102104 pub fn transformPath(self: *TestContext, relative_path: [:0]const u8) ![:0]const u8 {
103105 const allocator = self.arena.allocator();
104106 const transformed_path = try self.transform_fn(allocator, self.dir, relative_path);
105 if (builtin.os.tag == .windows) {
107 if (native_os == .windows) {
106108 const transformed_sep_path = try allocator.dupeZ(u8, transformed_path);
107109 std.mem.replaceScalar(u8, transformed_sep_path, switch (self.path_sep) {
108110 '/' => '\\',
......@@ -119,7 +121,7 @@ const TestContext = struct {
119121 /// If path separators are replaced, then the result is allocated by the
120122 /// TestContext's arena and will be free'd during `TestContext.deinit`.
121123 pub fn toCanonicalPathSep(self: *TestContext, path: [:0]const u8) ![:0]const u8 {
122 if (builtin.os.tag == .windows) {
124 if (native_os == .windows) {
123125 const allocator = self.arena.allocator();
124126 const transformed_sep_path = try allocator.dupeZ(u8, path);
125127 std.mem.replaceScalar(u8, transformed_sep_path, '/', '\\');
......@@ -157,7 +159,7 @@ fn testWithPathTypeIfSupported(comptime path_type: PathType, comptime path_sep:
157159fn setupSymlink(dir: Dir, target: []const u8, link: []const u8, flags: SymLinkFlags) !void {
158160 return dir.symLink(target, link, flags) catch |err| switch (err) {
159161 // Symlink requires admin privileges on windows, so this test can legitimately fail.
160 error.AccessDenied => if (builtin.os.tag == .windows) return error.SkipZigTest else return err,
162 error.AccessDenied => if (native_os == .windows) return error.SkipZigTest else return err,
161163 else => return err,
162164 };
163165}
......@@ -166,7 +168,7 @@ fn setupSymlink(dir: Dir, target: []const u8, link: []const u8, flags: SymLinkFl
166168// AccessDenied, then make the test failure silent (it is not a Zig failure).
167169fn setupSymlinkAbsolute(target: []const u8, link: []const u8, flags: SymLinkFlags) !void {
168170 return fs.symLinkAbsolute(target, link, flags) catch |err| switch (err) {
169 error.AccessDenied => if (builtin.os.tag == .windows) return error.SkipZigTest else return err,
171 error.AccessDenied => if (native_os == .windows) return error.SkipZigTest else return err,
170172 else => return err,
171173 };
172174}
......@@ -232,60 +234,58 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" {
232234
233235 var symlink = switch (builtin.target.os.tag) {
234236 .windows => windows_symlink: {
235 const w = std.os.windows;
236
237 const sub_path_w = try std.os.windows.cStrToPrefixedFileW(ctx.dir.fd, "symlink");
237 const sub_path_w = try windows.cStrToPrefixedFileW(ctx.dir.fd, "symlink");
238238
239239 var result = Dir{
240240 .fd = undefined,
241241 };
242242
243243 const path_len_bytes = @as(u16, @intCast(sub_path_w.span().len * 2));
244 var nt_name = w.UNICODE_STRING{
244 var nt_name = windows.UNICODE_STRING{
245245 .Length = path_len_bytes,
246246 .MaximumLength = path_len_bytes,
247247 .Buffer = @constCast(&sub_path_w.data),
248248 };
249 var attr = w.OBJECT_ATTRIBUTES{
250 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
249 var attr = windows.OBJECT_ATTRIBUTES{
250 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
251251 .RootDirectory = if (fs.path.isAbsoluteWindowsW(sub_path_w.span())) null else ctx.dir.fd,
252252 .Attributes = 0,
253253 .ObjectName = &nt_name,
254254 .SecurityDescriptor = null,
255255 .SecurityQualityOfService = null,
256256 };
257 var io: w.IO_STATUS_BLOCK = undefined;
258 const rc = w.ntdll.NtCreateFile(
257 var io: windows.IO_STATUS_BLOCK = undefined;
258 const rc = windows.ntdll.NtCreateFile(
259259 &result.fd,
260 w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | w.SYNCHRONIZE | w.FILE_TRAVERSE,
260 windows.STANDARD_RIGHTS_READ | windows.FILE_READ_ATTRIBUTES | windows.FILE_READ_EA | windows.SYNCHRONIZE | windows.FILE_TRAVERSE,
261261 &attr,
262262 &io,
263263 null,
264 w.FILE_ATTRIBUTE_NORMAL,
265 w.FILE_SHARE_READ | w.FILE_SHARE_WRITE,
266 w.FILE_OPEN,
264 windows.FILE_ATTRIBUTE_NORMAL,
265 windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE,
266 windows.FILE_OPEN,
267267 // FILE_OPEN_REPARSE_POINT is the important thing here
268 w.FILE_OPEN_REPARSE_POINT | w.FILE_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT | w.FILE_OPEN_FOR_BACKUP_INTENT,
268 windows.FILE_OPEN_REPARSE_POINT | windows.FILE_DIRECTORY_FILE | windows.FILE_SYNCHRONOUS_IO_NONALERT | windows.FILE_OPEN_FOR_BACKUP_INTENT,
269269 null,
270270 0,
271271 );
272272
273273 switch (rc) {
274274 .SUCCESS => break :windows_symlink result,
275 else => return w.unexpectedStatus(rc),
275 else => return windows.unexpectedStatus(rc),
276276 }
277277 },
278278 .linux => linux_symlink: {
279 const sub_path_c = try os.toPosixPath("symlink");
279 const sub_path_c = try posix.toPosixPath("symlink");
280280 // the O_NOFOLLOW | O_PATH combination can obtain a fd to a symlink
281281 // note that if O_DIRECTORY is set, then this will error with ENOTDIR
282 const flags: os.O = .{
282 const flags: posix.O = .{
283283 .NOFOLLOW = true,
284284 .PATH = true,
285285 .ACCMODE = .RDONLY,
286286 .CLOEXEC = true,
287287 };
288 const fd = try os.openatZ(ctx.dir.fd, &sub_path_c, flags, 0);
288 const fd = try posix.openatZ(ctx.dir.fd, &sub_path_c, flags, 0);
289289 break :linux_symlink Dir{ .fd = fd };
290290 },
291291 else => unreachable,
......@@ -315,7 +315,7 @@ test "openDir" {
315315}
316316
317317test "accessAbsolute" {
318 if (builtin.os.tag == .wasi) return error.SkipZigTest;
318 if (native_os == .wasi) return error.SkipZigTest;
319319
320320 var tmp = tmpDir(.{});
321321 defer tmp.cleanup();
......@@ -333,7 +333,7 @@ test "accessAbsolute" {
333333}
334334
335335test "openDirAbsolute" {
336 if (builtin.os.tag == .wasi) return error.SkipZigTest;
336 if (native_os == .wasi) return error.SkipZigTest;
337337
338338 var tmp = tmpDir(.{});
339339 defer tmp.cleanup();
......@@ -361,14 +361,14 @@ test "openDirAbsolute" {
361361}
362362
363363test "openDir cwd parent '..'" {
364 if (builtin.os.tag == .wasi) return error.SkipZigTest;
364 if (native_os == .wasi) return error.SkipZigTest;
365365
366366 var dir = try fs.cwd().openDir("..", .{});
367367 defer dir.close();
368368}
369369
370370test "openDir non-cwd parent '..'" {
371 switch (builtin.os.tag) {
371 switch (native_os) {
372372 .wasi, .netbsd, .openbsd => return error.SkipZigTest,
373373 else => {},
374374 }
......@@ -392,7 +392,7 @@ test "openDir non-cwd parent '..'" {
392392}
393393
394394test "readLinkAbsolute" {
395 if (builtin.os.tag == .wasi) return error.SkipZigTest;
395 if (native_os == .wasi) return error.SkipZigTest;
396396
397397 var tmp = tmpDir(.{});
398398 defer tmp.cleanup();
......@@ -587,7 +587,7 @@ test "Dir.Iterator but dir is deleted during iteration" {
587587 try std.testing.expect(entry == null);
588588
589589 // On Linux, we can opt-in to receiving a more specific error by calling `nextLinux`
590 if (builtin.os.tag == .linux) {
590 if (native_os == .linux) {
591591 try std.testing.expectError(error.DirNotFound, iterator.nextLinux());
592592 }
593593}
......@@ -744,7 +744,7 @@ test "directory operations on files" {
744744
745745test "file operations on directories" {
746746 // TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759
747 if (builtin.os.tag == .freebsd) return error.SkipZigTest;
747 if (native_os == .freebsd) return error.SkipZigTest;
748748
749749 try testWithAllSupportedPathTypes(struct {
750750 fn impl(ctx: *TestContext) !void {
......@@ -754,7 +754,7 @@ test "file operations on directories" {
754754
755755 try testing.expectError(error.IsDir, ctx.dir.createFile(test_dir_name, .{}));
756756 try testing.expectError(error.IsDir, ctx.dir.deleteFile(test_dir_name));
757 switch (builtin.os.tag) {
757 switch (native_os) {
758758 // no error when reading a directory.
759759 .dragonfly, .netbsd => {},
760760 // Currently, WASI will return error.Unexpected (via ENOTCAPABLE) when attempting fd_read on a directory handle.
......@@ -895,7 +895,7 @@ test "Dir.rename directories" {
895895
896896test "Dir.rename directory onto empty dir" {
897897 // TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364
898 if (builtin.os.tag == .windows) return error.SkipZigTest;
898 if (native_os == .windows) return error.SkipZigTest;
899899
900900 try testWithAllSupportedPathTypes(struct {
901901 fn impl(ctx: *TestContext) !void {
......@@ -916,7 +916,7 @@ test "Dir.rename directory onto empty dir" {
916916
917917test "Dir.rename directory onto non-empty dir" {
918918 // TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364
919 if (builtin.os.tag == .windows) return error.SkipZigTest;
919 if (native_os == .windows) return error.SkipZigTest;
920920
921921 try testWithAllSupportedPathTypes(struct {
922922 fn impl(ctx: *TestContext) !void {
......@@ -942,7 +942,7 @@ test "Dir.rename directory onto non-empty dir" {
942942
943943test "Dir.rename file <-> dir" {
944944 // TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364
945 if (builtin.os.tag == .windows) return error.SkipZigTest;
945 if (native_os == .windows) return error.SkipZigTest;
946946
947947 try testWithAllSupportedPathTypes(struct {
948948 fn impl(ctx: *TestContext) !void {
......@@ -979,7 +979,7 @@ test "rename" {
979979}
980980
981981test "renameAbsolute" {
982 if (builtin.os.tag == .wasi) return error.SkipZigTest;
982 if (native_os == .wasi) return error.SkipZigTest;
983983
984984 var tmp_dir = tmpDir(.{});
985985 defer tmp_dir.cleanup();
......@@ -1032,14 +1032,14 @@ test "renameAbsolute" {
10321032}
10331033
10341034test "openSelfExe" {
1035 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1035 if (native_os == .wasi) return error.SkipZigTest;
10361036
10371037 const self_exe_file = try std.fs.openSelfExe(.{});
10381038 self_exe_file.close();
10391039}
10401040
10411041test "selfExePath" {
1042 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1042 if (native_os == .wasi) return error.SkipZigTest;
10431043
10441044 var buf: [fs.MAX_PATH_BYTES]u8 = undefined;
10451045 const buf_self_exe_path = try std.fs.selfExePath(&buf);
......@@ -1120,7 +1120,7 @@ test "makePath, put some files in it, deleteTreeMinStackSize" {
11201120}
11211121
11221122test "makePath in a directory that no longer exists" {
1123 if (builtin.os.tag == .windows) return error.SkipZigTest; // Windows returns FileBusy if attempting to remove an open dir
1123 if (native_os == .windows) return error.SkipZigTest; // Windows returns FileBusy if attempting to remove an open dir
11241124
11251125 var tmp = tmpDir(.{});
11261126 defer tmp.cleanup();
......@@ -1182,7 +1182,7 @@ test "makepath relative walks" {
11821182 try tmp.dir.makePath(relPath);
11831183
11841184 // How .. is handled is different on Windows than non-Windows
1185 switch (builtin.os.tag) {
1185 switch (native_os) {
11861186 .windows => {
11871187 // On Windows, .. is resolved before passing the path to NtCreateFile,
11881188 // meaning everything except `first/C` drops out.
......@@ -1248,12 +1248,12 @@ test "max file name component lengths" {
12481248 var tmp = tmpDir(.{ .iterate = true });
12491249 defer tmp.cleanup();
12501250
1251 if (builtin.os.tag == .windows) {
1251 if (native_os == .windows) {
12521252 // U+FFFF is the character with the largest code point that is encoded as a single
12531253 // UTF-16 code unit, so Windows allows for NAME_MAX of them.
1254 const maxed_windows_filename = ("\u{FFFF}".*) ** std.os.windows.NAME_MAX;
1254 const maxed_windows_filename = ("\u{FFFF}".*) ** windows.NAME_MAX;
12551255 try testFilenameLimits(tmp.dir, &maxed_windows_filename);
1256 } else if (builtin.os.tag == .wasi) {
1256 } else if (native_os == .wasi) {
12571257 // On WASI, the maxed filename depends on the host OS, so in order for this test to
12581258 // work on any host, we need to use a length that will work for all platforms
12591259 // (i.e. the minimum MAX_NAME_BYTES of all supported platforms).
......@@ -1274,7 +1274,7 @@ test "writev, readv" {
12741274
12751275 var buf1: [line1.len]u8 = undefined;
12761276 var buf2: [line2.len]u8 = undefined;
1277 var write_vecs = [_]std.os.iovec_const{
1277 var write_vecs = [_]posix.iovec_const{
12781278 .{
12791279 .iov_base = line1,
12801280 .iov_len = line1.len,
......@@ -1284,7 +1284,7 @@ test "writev, readv" {
12841284 .iov_len = line2.len,
12851285 },
12861286 };
1287 var read_vecs = [_]std.os.iovec{
1287 var read_vecs = [_]posix.iovec{
12881288 .{
12891289 .iov_base = &buf2,
12901290 .iov_len = buf2.len,
......@@ -1316,7 +1316,7 @@ test "pwritev, preadv" {
13161316
13171317 var buf1: [line1.len]u8 = undefined;
13181318 var buf2: [line2.len]u8 = undefined;
1319 var write_vecs = [_]std.os.iovec_const{
1319 var write_vecs = [_]posix.iovec_const{
13201320 .{
13211321 .iov_base = line1,
13221322 .iov_len = line1.len,
......@@ -1326,7 +1326,7 @@ test "pwritev, preadv" {
13261326 .iov_len = line2.len,
13271327 },
13281328 };
1329 var read_vecs = [_]std.os.iovec{
1329 var read_vecs = [_]posix.iovec{
13301330 .{
13311331 .iov_base = &buf2,
13321332 .iov_len = buf2.len,
......@@ -1376,7 +1376,7 @@ test "sendfile" {
13761376
13771377 const line1 = "line1\n";
13781378 const line2 = "second line\n";
1379 var vecs = [_]std.os.iovec_const{
1379 var vecs = [_]posix.iovec_const{
13801380 .{
13811381 .iov_base = line1,
13821382 .iov_len = line1.len,
......@@ -1399,7 +1399,7 @@ test "sendfile" {
13991399 const header2 = "second header\n";
14001400 const trailer1 = "trailer1\n";
14011401 const trailer2 = "second trailer\n";
1402 var hdtr = [_]std.os.iovec_const{
1402 var hdtr = [_]posix.iovec_const{
14031403 .{
14041404 .iov_base = header1,
14051405 .iov_len = header1.len,
......@@ -1510,7 +1510,7 @@ test "AtomicFile" {
15101510}
15111511
15121512test "open file with exclusive nonblocking lock twice" {
1513 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1513 if (native_os == .wasi) return error.SkipZigTest;
15141514
15151515 try testWithAllSupportedPathTypes(struct {
15161516 fn impl(ctx: *TestContext) !void {
......@@ -1526,7 +1526,7 @@ test "open file with exclusive nonblocking lock twice" {
15261526}
15271527
15281528test "open file with shared and exclusive nonblocking lock" {
1529 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1529 if (native_os == .wasi) return error.SkipZigTest;
15301530
15311531 try testWithAllSupportedPathTypes(struct {
15321532 fn impl(ctx: *TestContext) !void {
......@@ -1542,7 +1542,7 @@ test "open file with shared and exclusive nonblocking lock" {
15421542}
15431543
15441544test "open file with exclusive and shared nonblocking lock" {
1545 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1545 if (native_os == .wasi) return error.SkipZigTest;
15461546
15471547 try testWithAllSupportedPathTypes(struct {
15481548 fn impl(ctx: *TestContext) !void {
......@@ -1601,7 +1601,7 @@ test "open file with exclusive lock twice, make sure second lock waits" {
16011601}
16021602
16031603test "open file with exclusive nonblocking lock twice (absolute paths)" {
1604 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1604 if (native_os == .wasi) return error.SkipZigTest;
16051605
16061606 var random_bytes: [12]u8 = undefined;
16071607 std.crypto.random.bytes(&random_bytes);
......@@ -1634,7 +1634,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
16341634}
16351635
16361636test "walker" {
1637 if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
1637 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
16381638
16391639 var tmp = tmpDir(.{ .iterate = true });
16401640 defer tmp.cleanup();
......@@ -1687,7 +1687,7 @@ test "walker" {
16871687}
16881688
16891689test "walker without fully iterating" {
1690 if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
1690 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
16911691
16921692 var tmp = tmpDir(.{ .iterate = true });
16931693 defer tmp.cleanup();
......@@ -1710,9 +1710,9 @@ test "walker without fully iterating" {
17101710}
17111711
17121712test "'.' and '..' in fs.Dir functions" {
1713 if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
1713 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
17141714
1715 if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64) {
1715 if (native_os == .windows and builtin.cpu.arch == .aarch64) {
17161716 // https://github.com/ziglang/zig/issues/17134
17171717 return error.SkipZigTest;
17181718 }
......@@ -1750,7 +1750,7 @@ test "'.' and '..' in fs.Dir functions" {
17501750}
17511751
17521752test "'.' and '..' in absolute functions" {
1753 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1753 if (native_os == .wasi) return error.SkipZigTest;
17541754
17551755 var tmp = tmpDir(.{});
17561756 defer tmp.cleanup();
......@@ -1794,7 +1794,7 @@ test "'.' and '..' in absolute functions" {
17941794}
17951795
17961796test "chmod" {
1797 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
1797 if (native_os == .windows or native_os == .wasi)
17981798 return error.SkipZigTest;
17991799
18001800 var tmp = tmpDir(.{});
......@@ -1816,7 +1816,7 @@ test "chmod" {
18161816}
18171817
18181818test "chown" {
1819 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
1819 if (native_os == .windows or native_os == .wasi)
18201820 return error.SkipZigTest;
18211821
18221822 var tmp = tmpDir(.{});
......@@ -1849,7 +1849,7 @@ test "File.Metadata" {
18491849}
18501850
18511851test "File.Permissions" {
1852 if (builtin.os.tag == .wasi)
1852 if (native_os == .wasi)
18531853 return error.SkipZigTest;
18541854
18551855 var tmp = tmpDir(.{});
......@@ -1875,7 +1875,7 @@ test "File.Permissions" {
18751875}
18761876
18771877test "File.PermissionsUnix" {
1878 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
1878 if (native_os == .windows or native_os == .wasi)
18791879 return error.SkipZigTest;
18801880
18811881 var tmp = tmpDir(.{});
......@@ -1910,7 +1910,7 @@ test "File.PermissionsUnix" {
19101910}
19111911
19121912test "delete a read-only file on windows" {
1913 if (builtin.os.tag != .windows)
1913 if (native_os != .windows)
19141914 return error.SkipZigTest;
19151915
19161916 var tmp = testing.tmpDir(.{});
......@@ -1941,7 +1941,7 @@ test "delete a read-only file on windows" {
19411941}
19421942
19431943test "delete a setAsCwd directory on Windows" {
1944 if (builtin.os.tag != .windows) return error.SkipZigTest;
1944 if (native_os != .windows) return error.SkipZigTest;
19451945
19461946 var tmp = tmpDir(.{});
19471947 // Set tmp dir as current working directory.
......@@ -1956,7 +1956,7 @@ test "delete a setAsCwd directory on Windows" {
19561956}
19571957
19581958test "invalid UTF-8/WTF-8 paths" {
1959 const expected_err = switch (builtin.os.tag) {
1959 const expected_err = switch (native_os) {
19601960 .wasi => error.InvalidUtf8,
19611961 .windows => error.InvalidWtf8,
19621962 else => return error.SkipZigTest,
......@@ -1993,13 +1993,13 @@ test "invalid UTF-8/WTF-8 paths" {
19931993
19941994 try testing.expectError(expected_err, ctx.dir.symLink(invalid_path, invalid_path, .{}));
19951995 try testing.expectError(expected_err, ctx.dir.symLinkZ(invalid_path, invalid_path, .{}));
1996 if (builtin.os.tag == .wasi) {
1996 if (native_os == .wasi) {
19971997 try testing.expectError(expected_err, ctx.dir.symLinkWasi(invalid_path, invalid_path, .{}));
19981998 }
19991999
20002000 try testing.expectError(expected_err, ctx.dir.readLink(invalid_path, &[_]u8{}));
20012001 try testing.expectError(expected_err, ctx.dir.readLinkZ(invalid_path, &[_]u8{}));
2002 if (builtin.os.tag == .wasi) {
2002 if (native_os == .wasi) {
20032003 try testing.expectError(expected_err, ctx.dir.readLinkWasi(invalid_path, &[_]u8{}));
20042004 }
20052005
......@@ -2023,7 +2023,7 @@ test "invalid UTF-8/WTF-8 paths" {
20232023
20242024 try testing.expectError(expected_err, ctx.dir.statFile(invalid_path));
20252025
2026 if (builtin.os.tag != .wasi) {
2026 if (native_os != .wasi) {
20272027 try testing.expectError(expected_err, ctx.dir.realpath(invalid_path, &[_]u8{}));
20282028 try testing.expectError(expected_err, ctx.dir.realpathZ(invalid_path, &[_]u8{}));
20292029 try testing.expectError(expected_err, ctx.dir.realpathAlloc(testing.allocator, invalid_path));
......@@ -2032,7 +2032,7 @@ test "invalid UTF-8/WTF-8 paths" {
20322032 try testing.expectError(expected_err, fs.rename(ctx.dir, invalid_path, ctx.dir, invalid_path));
20332033 try testing.expectError(expected_err, fs.renameZ(ctx.dir, invalid_path, ctx.dir, invalid_path));
20342034
2035 if (builtin.os.tag != .wasi and ctx.path_type != .relative) {
2035 if (native_os != .wasi and ctx.path_type != .relative) {
20362036 try testing.expectError(expected_err, fs.updateFileAbsolute(invalid_path, invalid_path, .{}));
20372037 try testing.expectError(expected_err, fs.copyFileAbsolute(invalid_path, invalid_path, .{}));
20382038 try testing.expectError(expected_err, fs.makeDirAbsolute(invalid_path));
lib/std/hash/benchmark.zig+6-6
......@@ -367,7 +367,7 @@ pub fn main() !void {
367367 i += 1;
368368 if (i == args.len) {
369369 usage();
370 std.os.exit(1);
370 std.process.exit(1);
371371 }
372372
373373 seed = try std.fmt.parseUnsigned(u32, args[i], 10);
......@@ -376,7 +376,7 @@ pub fn main() !void {
376376 i += 1;
377377 if (i == args.len) {
378378 usage();
379 std.os.exit(1);
379 std.process.exit(1);
380380 }
381381
382382 filter = args[i];
......@@ -384,7 +384,7 @@ pub fn main() !void {
384384 i += 1;
385385 if (i == args.len) {
386386 usage();
387 std.os.exit(1);
387 std.process.exit(1);
388388 }
389389
390390 const c = try std.fmt.parseUnsigned(usize, args[i], 10);
......@@ -393,13 +393,13 @@ pub fn main() !void {
393393 i += 1;
394394 if (i == args.len) {
395395 usage();
396 std.os.exit(1);
396 std.process.exit(1);
397397 }
398398
399399 key_size = try std.fmt.parseUnsigned(usize, args[i], 10);
400400 if (key_size.? > block_size) {
401401 try stdout.print("key_size cannot exceed block size of {}\n", .{block_size});
402 std.os.exit(1);
402 std.process.exit(1);
403403 }
404404 } else if (std.mem.eql(u8, args[i], "--iterative-only")) {
405405 test_iterative_only = true;
......@@ -410,7 +410,7 @@ pub fn main() !void {
410410 return;
411411 } else {
412412 usage();
413 std.os.exit(1);
413 std.process.exit(1);
414414 }
415415 }
416416
lib/std/heap.zig+10-10
......@@ -4,9 +4,9 @@ const root = @import("root");
44const assert = std.debug.assert;
55const testing = std.testing;
66const mem = std.mem;
7const os = std.os;
87const c = std.c;
98const Allocator = std.mem.Allocator;
9const windows = std.os.windows;
1010
1111pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;
1212pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;
......@@ -263,7 +263,7 @@ pub const HeapAllocator = switch (builtin.os.tag) {
263263 .windows => struct {
264264 heap_handle: ?HeapHandle,
265265
266 const HeapHandle = os.windows.HANDLE;
266 const HeapHandle = windows.HANDLE;
267267
268268 pub fn init() HeapAllocator {
269269 return HeapAllocator{
......@@ -284,7 +284,7 @@ pub const HeapAllocator = switch (builtin.os.tag) {
284284
285285 pub fn deinit(self: *HeapAllocator) void {
286286 if (self.heap_handle) |heap_handle| {
287 os.windows.HeapDestroy(heap_handle);
287 windows.HeapDestroy(heap_handle);
288288 }
289289 }
290290
......@@ -305,13 +305,13 @@ pub const HeapAllocator = switch (builtin.os.tag) {
305305 const amt = n + ptr_align - 1 + @sizeOf(usize);
306306 const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, .seq_cst);
307307 const heap_handle = optional_heap_handle orelse blk: {
308 const options = if (builtin.single_threaded) os.windows.HEAP_NO_SERIALIZE else 0;
309 const hh = os.windows.kernel32.HeapCreate(options, amt, 0) orelse return null;
308 const options = if (builtin.single_threaded) windows.HEAP_NO_SERIALIZE else 0;
309 const hh = windows.kernel32.HeapCreate(options, amt, 0) orelse return null;
310310 const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, .seq_cst, .seq_cst) orelse break :blk hh;
311 os.windows.HeapDestroy(hh);
311 windows.HeapDestroy(hh);
312312 break :blk other_hh.?; // can't be null because of the cmpxchg
313313 };
314 const ptr = os.windows.kernel32.HeapAlloc(heap_handle, 0, amt) orelse return null;
314 const ptr = windows.kernel32.HeapAlloc(heap_handle, 0, amt) orelse return null;
315315 const root_addr = @intFromPtr(ptr);
316316 const aligned_addr = mem.alignForward(usize, root_addr, ptr_align);
317317 const buf = @as([*]u8, @ptrFromInt(aligned_addr))[0..n];
......@@ -333,9 +333,9 @@ pub const HeapAllocator = switch (builtin.os.tag) {
333333 const root_addr = getRecordPtr(buf).*;
334334 const align_offset = @intFromPtr(buf.ptr) - root_addr;
335335 const amt = align_offset + new_size + @sizeOf(usize);
336 const new_ptr = os.windows.kernel32.HeapReAlloc(
336 const new_ptr = windows.kernel32.HeapReAlloc(
337337 self.heap_handle.?,
338 os.windows.HEAP_REALLOC_IN_PLACE_ONLY,
338 windows.HEAP_REALLOC_IN_PLACE_ONLY,
339339 @as(*anyopaque, @ptrFromInt(root_addr)),
340340 amt,
341341 ) orelse return false;
......@@ -353,7 +353,7 @@ pub const HeapAllocator = switch (builtin.os.tag) {
353353 _ = log2_buf_align;
354354 _ = return_address;
355355 const self: *HeapAllocator = @ptrCast(@alignCast(ctx));
356 os.windows.HeapFree(self.heap_handle.?, 0, @as(*anyopaque, @ptrFromInt(getRecordPtr(buf).*)));
356 windows.HeapFree(self.heap_handle.?, 0, @as(*anyopaque, @ptrFromInt(getRecordPtr(buf).*)));
357357 }
358358 },
359359 else => @compileError("Unsupported OS"),
lib/std/heap/PageAllocator.zig+16-16
......@@ -2,9 +2,11 @@ const std = @import("../std.zig");
22const builtin = @import("builtin");
33const Allocator = std.mem.Allocator;
44const mem = std.mem;
5const os = std.os;
65const maxInt = std.math.maxInt;
76const assert = std.debug.assert;
7const native_os = builtin.os.tag;
8const windows = std.os.windows;
9const posix = std.posix;
810
911pub const vtable = Allocator.VTable{
1012 .alloc = alloc,
......@@ -19,22 +21,21 @@ fn alloc(_: *anyopaque, n: usize, log2_align: u8, ra: usize) ?[*]u8 {
1921 if (n > maxInt(usize) - (mem.page_size - 1)) return null;
2022 const aligned_len = mem.alignForward(usize, n, mem.page_size);
2123
22 if (builtin.os.tag == .windows) {
23 const w = os.windows;
24 const addr = w.VirtualAlloc(
24 if (native_os == .windows) {
25 const addr = windows.VirtualAlloc(
2526 null,
2627 aligned_len,
27 w.MEM_COMMIT | w.MEM_RESERVE,
28 w.PAGE_READWRITE,
28 windows.MEM_COMMIT | windows.MEM_RESERVE,
29 windows.PAGE_READWRITE,
2930 ) catch return null;
3031 return @ptrCast(addr);
3132 }
3233
3334 const hint = @atomicLoad(@TypeOf(std.heap.next_mmap_addr_hint), &std.heap.next_mmap_addr_hint, .unordered);
34 const slice = os.mmap(
35 const slice = posix.mmap(
3536 hint,
3637 aligned_len,
37 os.PROT.READ | os.PROT.WRITE,
38 posix.PROT.READ | posix.PROT.WRITE,
3839 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
3940 -1,
4041 0,
......@@ -56,8 +57,7 @@ fn resize(
5657 _ = return_address;
5758 const new_size_aligned = mem.alignForward(usize, new_size, mem.page_size);
5859
59 if (builtin.os.tag == .windows) {
60 const w = os.windows;
60 if (native_os == .windows) {
6161 if (new_size <= buf_unaligned.len) {
6262 const base_addr = @intFromPtr(buf_unaligned.ptr);
6363 const old_addr_end = base_addr + buf_unaligned.len;
......@@ -65,10 +65,10 @@ fn resize(
6565 if (old_addr_end > new_addr_end) {
6666 // For shrinking that is not releasing, we will only
6767 // decommit the pages not needed anymore.
68 w.VirtualFree(
68 windows.VirtualFree(
6969 @as(*anyopaque, @ptrFromInt(new_addr_end)),
7070 old_addr_end - new_addr_end,
71 w.MEM_DECOMMIT,
71 windows.MEM_DECOMMIT,
7272 );
7373 }
7474 return true;
......@@ -87,7 +87,7 @@ fn resize(
8787 if (new_size_aligned < buf_aligned_len) {
8888 const ptr = buf_unaligned.ptr + new_size_aligned;
8989 // TODO: if the next_mmap_addr_hint is within the unmapped range, update it
90 os.munmap(@alignCast(ptr[0 .. buf_aligned_len - new_size_aligned]));
90 posix.munmap(@alignCast(ptr[0 .. buf_aligned_len - new_size_aligned]));
9191 return true;
9292 }
9393
......@@ -100,10 +100,10 @@ fn free(_: *anyopaque, slice: []u8, log2_buf_align: u8, return_address: usize) v
100100 _ = log2_buf_align;
101101 _ = return_address;
102102
103 if (builtin.os.tag == .windows) {
104 os.windows.VirtualFree(slice.ptr, 0, os.windows.MEM_RELEASE);
103 if (native_os == .windows) {
104 windows.VirtualFree(slice.ptr, 0, windows.MEM_RELEASE);
105105 } else {
106106 const buf_aligned_len = mem.alignForward(usize, slice.len, mem.page_size);
107 os.munmap(@alignCast(slice.ptr[0..buf_aligned_len]));
107 posix.munmap(@alignCast(slice.ptr[0..buf_aligned_len]));
108108 }
109109}
lib/std/http/Client.zig+5-5
......@@ -220,7 +220,7 @@ pub const Connection = struct {
220220
221221 pub const Protocol = enum { plain, tls };
222222
223 pub fn readvDirectTls(conn: *Connection, buffers: []std.os.iovec) ReadError!usize {
223 pub fn readvDirectTls(conn: *Connection, buffers: []std.posix.iovec) ReadError!usize {
224224 return conn.tls_client.readv(conn.stream, buffers) catch |err| {
225225 // https://github.com/ziglang/zig/issues/2473
226226 if (mem.startsWith(u8, @errorName(err), "TlsAlert")) return error.TlsAlert;
......@@ -234,7 +234,7 @@ pub const Connection = struct {
234234 };
235235 }
236236
237 pub fn readvDirect(conn: *Connection, buffers: []std.os.iovec) ReadError!usize {
237 pub fn readvDirect(conn: *Connection, buffers: []std.posix.iovec) ReadError!usize {
238238 if (conn.protocol == .tls) {
239239 if (disable_tls) unreachable;
240240
......@@ -252,7 +252,7 @@ pub const Connection = struct {
252252 pub fn fill(conn: *Connection) ReadError!void {
253253 if (conn.read_end != conn.read_start) return;
254254
255 var iovecs = [1]std.os.iovec{
255 var iovecs = [1]std.posix.iovec{
256256 .{ .iov_base = &conn.read_buf, .iov_len = conn.read_buf.len },
257257 };
258258 const nread = try conn.readvDirect(&iovecs);
......@@ -288,7 +288,7 @@ pub const Connection = struct {
288288 return available_read;
289289 }
290290
291 var iovecs = [2]std.os.iovec{
291 var iovecs = [2]std.posix.iovec{
292292 .{ .iov_base = buffer.ptr, .iov_len = buffer.len },
293293 .{ .iov_base = &conn.read_buf, .iov_len = conn.read_buf.len },
294294 };
......@@ -1387,7 +1387,7 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec
13871387 return &conn.data;
13881388}
13891389
1390pub const ConnectUnixError = Allocator.Error || std.os.SocketError || error{NameTooLong} || std.os.ConnectError;
1390pub const ConnectUnixError = Allocator.Error || std.posix.SocketError || error{NameTooLong} || std.posix.ConnectError;
13911391
13921392/// Connect to `path` as a unix domain socket. This will reuse a connection if one is already open.
13931393///
lib/std/io.zig+54-52
......@@ -2,74 +2,76 @@ const std = @import("std.zig");
22const builtin = @import("builtin");
33const root = @import("root");
44const c = std.c;
5const is_windows = builtin.os.tag == .windows;
6const windows = std.os.windows;
7const posix = std.posix;
58
69const math = std.math;
710const assert = std.debug.assert;
8const os = std.os;
911const fs = std.fs;
1012const mem = std.mem;
1113const meta = std.meta;
1214const File = std.fs.File;
1315const Allocator = std.mem.Allocator;
1416
15fn getStdOutHandle() os.fd_t {
16 if (builtin.os.tag == .windows) {
17fn getStdOutHandle() posix.fd_t {
18 if (is_windows) {
1719 if (builtin.zig_backend == .stage2_aarch64) {
1820 // TODO: this is just a temporary workaround until we advance aarch64 backend further along.
19 return os.windows.GetStdHandle(os.windows.STD_OUTPUT_HANDLE) catch os.windows.INVALID_HANDLE_VALUE;
21 return windows.GetStdHandle(windows.STD_OUTPUT_HANDLE) catch windows.INVALID_HANDLE_VALUE;
2022 }
21 return os.windows.peb().ProcessParameters.hStdOutput;
23 return windows.peb().ProcessParameters.hStdOutput;
2224 }
2325
2426 if (@hasDecl(root, "os") and @hasDecl(root.os, "io") and @hasDecl(root.os.io, "getStdOutHandle")) {
2527 return root.os.io.getStdOutHandle();
2628 }
2729
28 return os.STDOUT_FILENO;
30 return posix.STDOUT_FILENO;
2931}
3032
3133pub fn getStdOut() File {
32 return File{ .handle = getStdOutHandle() };
34 return .{ .handle = getStdOutHandle() };
3335}
3436
35fn getStdErrHandle() os.fd_t {
36 if (builtin.os.tag == .windows) {
37fn getStdErrHandle() posix.fd_t {
38 if (is_windows) {
3739 if (builtin.zig_backend == .stage2_aarch64) {
3840 // TODO: this is just a temporary workaround until we advance aarch64 backend further along.
39 return os.windows.GetStdHandle(os.windows.STD_ERROR_HANDLE) catch os.windows.INVALID_HANDLE_VALUE;
41 return windows.GetStdHandle(windows.STD_ERROR_HANDLE) catch windows.INVALID_HANDLE_VALUE;
4042 }
41 return os.windows.peb().ProcessParameters.hStdError;
43 return windows.peb().ProcessParameters.hStdError;
4244 }
4345
4446 if (@hasDecl(root, "os") and @hasDecl(root.os, "io") and @hasDecl(root.os.io, "getStdErrHandle")) {
4547 return root.os.io.getStdErrHandle();
4648 }
4749
48 return os.STDERR_FILENO;
50 return posix.STDERR_FILENO;
4951}
5052
5153pub fn getStdErr() File {
52 return File{ .handle = getStdErrHandle() };
54 return .{ .handle = getStdErrHandle() };
5355}
5456
55fn getStdInHandle() os.fd_t {
56 if (builtin.os.tag == .windows) {
57fn getStdInHandle() posix.fd_t {
58 if (is_windows) {
5759 if (builtin.zig_backend == .stage2_aarch64) {
5860 // TODO: this is just a temporary workaround until we advance aarch64 backend further along.
59 return os.windows.GetStdHandle(os.windows.STD_INPUT_HANDLE) catch os.windows.INVALID_HANDLE_VALUE;
61 return windows.GetStdHandle(windows.STD_INPUT_HANDLE) catch windows.INVALID_HANDLE_VALUE;
6062 }
61 return os.windows.peb().ProcessParameters.hStdInput;
63 return windows.peb().ProcessParameters.hStdInput;
6264 }
6365
6466 if (@hasDecl(root, "os") and @hasDecl(root.os, "io") and @hasDecl(root.os.io, "getStdInHandle")) {
6567 return root.os.io.getStdInHandle();
6668 }
6769
68 return os.STDIN_FILENO;
70 return posix.STDIN_FILENO;
6971}
7072
7173pub fn getStdIn() File {
72 return File{ .handle = getStdInHandle() };
74 return .{ .handle = getStdInHandle() };
7375}
7476
7577pub fn GenericReader(
......@@ -434,10 +436,10 @@ pub fn poll(
434436 const enum_fields = @typeInfo(StreamEnum).Enum.fields;
435437 var result: Poller(StreamEnum) = undefined;
436438
437 if (builtin.os.tag == .windows) result.windows = .{
439 if (is_windows) result.windows = .{
438440 .first_read_done = false,
439 .overlapped = [1]os.windows.OVERLAPPED{
440 mem.zeroes(os.windows.OVERLAPPED),
441 .overlapped = [1]windows.OVERLAPPED{
442 mem.zeroes(windows.OVERLAPPED),
441443 } ** enum_fields.len,
442444 .active = .{
443445 .count = 0,
......@@ -453,12 +455,12 @@ pub fn poll(
453455 .head = 0,
454456 .count = 0,
455457 };
456 if (builtin.os.tag == .windows) {
458 if (is_windows) {
457459 result.windows.active.handles_buf[i] = @field(files, enum_fields[i].name).handle;
458460 } else {
459461 result.poll_fds[i] = .{
460462 .fd = @field(files, enum_fields[i].name).handle,
461 .events = os.POLL.IN,
463 .events = posix.POLL.IN,
462464 .revents = undefined,
463465 };
464466 }
......@@ -471,16 +473,16 @@ pub const PollFifo = std.fifo.LinearFifo(u8, .Dynamic);
471473pub fn Poller(comptime StreamEnum: type) type {
472474 return struct {
473475 const enum_fields = @typeInfo(StreamEnum).Enum.fields;
474 const PollFd = if (builtin.os.tag == .windows) void else std.os.pollfd;
476 const PollFd = if (is_windows) void else posix.pollfd;
475477
476478 fifos: [enum_fields.len]PollFifo,
477479 poll_fds: [enum_fields.len]PollFd,
478 windows: if (builtin.os.tag == .windows) struct {
480 windows: if (is_windows) struct {
479481 first_read_done: bool,
480 overlapped: [enum_fields.len]os.windows.OVERLAPPED,
482 overlapped: [enum_fields.len]windows.OVERLAPPED,
481483 active: struct {
482484 count: math.IntFittingRange(0, enum_fields.len),
483 handles_buf: [enum_fields.len]os.windows.HANDLE,
485 handles_buf: [enum_fields.len]windows.HANDLE,
484486 stream_map: [enum_fields.len]StreamEnum,
485487
486488 pub fn removeAt(self: *@This(), index: u32) void {
......@@ -497,10 +499,10 @@ pub fn Poller(comptime StreamEnum: type) type {
497499 const Self = @This();
498500
499501 pub fn deinit(self: *Self) void {
500 if (builtin.os.tag == .windows) {
502 if (is_windows) {
501503 // cancel any pending IO to prevent clobbering OVERLAPPED value
502504 for (self.windows.active.handles_buf[0..self.windows.active.count]) |h| {
503 _ = os.windows.kernel32.CancelIo(h);
505 _ = windows.kernel32.CancelIo(h);
504506 }
505507 }
506508 inline for (&self.fifos) |*q| q.deinit();
......@@ -508,7 +510,7 @@ pub fn Poller(comptime StreamEnum: type) type {
508510 }
509511
510512 pub fn poll(self: *Self) !bool {
511 if (builtin.os.tag == .windows) {
513 if (is_windows) {
512514 return pollWindows(self, null);
513515 } else {
514516 return pollPosix(self, null);
......@@ -516,7 +518,7 @@ pub fn Poller(comptime StreamEnum: type) type {
516518 }
517519
518520 pub fn pollTimeout(self: *Self, nanoseconds: u64) !bool {
519 if (builtin.os.tag == .windows) {
521 if (is_windows) {
520522 return pollWindows(self, nanoseconds);
521523 } else {
522524 return pollPosix(self, nanoseconds);
......@@ -554,39 +556,39 @@ pub fn Poller(comptime StreamEnum: type) type {
554556 while (true) {
555557 if (self.windows.active.count == 0) return false;
556558
557 const status = os.windows.kernel32.WaitForMultipleObjects(
559 const status = windows.kernel32.WaitForMultipleObjects(
558560 self.windows.active.count,
559561 &self.windows.active.handles_buf,
560562 0,
561563 if (nanoseconds) |ns|
562 @min(std.math.cast(u32, ns / std.time.ns_per_ms) orelse (os.windows.INFINITE - 1), os.windows.INFINITE - 1)
564 @min(std.math.cast(u32, ns / std.time.ns_per_ms) orelse (windows.INFINITE - 1), windows.INFINITE - 1)
563565 else
564 os.windows.INFINITE,
566 windows.INFINITE,
565567 );
566 if (status == os.windows.WAIT_FAILED)
567 return os.windows.unexpectedError(os.windows.kernel32.GetLastError());
568 if (status == os.windows.WAIT_TIMEOUT)
568 if (status == windows.WAIT_FAILED)
569 return windows.unexpectedError(windows.kernel32.GetLastError());
570 if (status == windows.WAIT_TIMEOUT)
569571 return true;
570572
571 if (status < os.windows.WAIT_OBJECT_0 or status > os.windows.WAIT_OBJECT_0 + enum_fields.len - 1)
573 if (status < windows.WAIT_OBJECT_0 or status > windows.WAIT_OBJECT_0 + enum_fields.len - 1)
572574 unreachable;
573575
574 const active_idx = status - os.windows.WAIT_OBJECT_0;
576 const active_idx = status - windows.WAIT_OBJECT_0;
575577
576578 const handle = self.windows.active.handles_buf[active_idx];
577579 const stream_idx = @intFromEnum(self.windows.active.stream_map[active_idx]);
578580 var read_bytes: u32 = undefined;
579 if (0 == os.windows.kernel32.GetOverlappedResult(
581 if (0 == windows.kernel32.GetOverlappedResult(
580582 handle,
581583 &self.windows.overlapped[stream_idx],
582584 &read_bytes,
583585 0,
584 )) switch (os.windows.kernel32.GetLastError()) {
586 )) switch (windows.kernel32.GetLastError()) {
585587 .BROKEN_PIPE => {
586588 self.windows.active.removeAt(active_idx);
587589 continue;
588590 },
589 else => |err| return os.windows.unexpectedError(err),
591 else => |err| return windows.unexpectedError(err),
590592 };
591593
592594 self.fifos[stream_idx].update(read_bytes);
......@@ -611,9 +613,9 @@ pub fn Poller(comptime StreamEnum: type) type {
611613 // allocate grows exponentially.
612614 const bump_amt = 512;
613615
614 const err_mask = os.POLL.ERR | os.POLL.NVAL | os.POLL.HUP;
616 const err_mask = posix.POLL.ERR | posix.POLL.NVAL | posix.POLL.HUP;
615617
616 const events_len = try os.poll(&self.poll_fds, if (nanoseconds) |ns|
618 const events_len = try posix.poll(&self.poll_fds, if (nanoseconds) |ns|
617619 std.math.cast(i32, ns / std.time.ns_per_ms) orelse std.math.maxInt(i32)
618620 else
619621 -1);
......@@ -629,9 +631,9 @@ pub fn Poller(comptime StreamEnum: type) type {
629631 // conditions.
630632 // It's still possible to read after a POLL.HUP is received,
631633 // always check if there's some data waiting to be read first.
632 if (poll_fd.revents & os.POLL.IN != 0) {
634 if (poll_fd.revents & posix.POLL.IN != 0) {
633635 const buf = try q.writableWithSize(bump_amt);
634 const amt = try os.read(poll_fd.fd, buf);
636 const amt = try posix.read(poll_fd.fd, buf);
635637 q.update(amt);
636638 if (amt == 0) {
637639 // Remove the fd when the EOF condition is met.
......@@ -652,19 +654,19 @@ pub fn Poller(comptime StreamEnum: type) type {
652654}
653655
654656fn windowsAsyncRead(
655 handle: os.windows.HANDLE,
656 overlapped: *os.windows.OVERLAPPED,
657 handle: windows.HANDLE,
658 overlapped: *windows.OVERLAPPED,
657659 fifo: *PollFifo,
658660 bump_amt: usize,
659661) !enum { pending, closed } {
660662 while (true) {
661663 const buf = try fifo.writableWithSize(bump_amt);
662664 var read_bytes: u32 = undefined;
663 const read_result = os.windows.kernel32.ReadFile(handle, buf.ptr, math.cast(u32, buf.len) orelse math.maxInt(u32), &read_bytes, overlapped);
664 if (read_result == 0) return switch (os.windows.kernel32.GetLastError()) {
665 const read_result = windows.kernel32.ReadFile(handle, buf.ptr, math.cast(u32, buf.len) orelse math.maxInt(u32), &read_bytes, overlapped);
666 if (read_result == 0) return switch (windows.kernel32.GetLastError()) {
665667 .IO_PENDING => .pending,
666668 .BROKEN_PIPE => .closed,
667 else => |err| os.windows.unexpectedError(err),
669 else => |err| windows.unexpectedError(err),
668670 };
669671 fifo.update(read_bytes);
670672 }
lib/std/io/c_writer.zig+3-4
......@@ -2,7 +2,6 @@ const std = @import("../std.zig");
22const builtin = @import("builtin");
33const io = std.io;
44const testing = std.testing;
5const os = std.os;
65
76pub const CWriter = io.Writer(*std.c.FILE, std.fs.File.WriteError, cWriterWrite);
87
......@@ -13,7 +12,7 @@ pub fn cWriter(c_file: *std.c.FILE) CWriter {
1312fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!usize {
1413 const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, c_file);
1514 if (amt_written >= 0) return amt_written;
16 switch (@as(os.E, @enumFromInt(std.c._errno().*))) {
15 switch (@as(std.c.E, @enumFromInt(std.c._errno().*))) {
1716 .SUCCESS => unreachable,
1817 .INVAL => unreachable,
1918 .FAULT => unreachable,
......@@ -26,11 +25,11 @@ fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!u
2625 .NOSPC => return error.NoSpaceLeft,
2726 .PERM => return error.AccessDenied,
2827 .PIPE => return error.BrokenPipe,
29 else => |err| return os.unexpectedErrno(err),
28 else => |err| return std.posix.unexpectedErrno(err),
3029 }
3130}
3231
33test "C Writer" {
32test cWriter {
3433 if (!builtin.link_libc or builtin.os.tag == .wasi) return error.SkipZigTest;
3534
3635 const filename = "tmp_io_test_file.txt";
lib/std/net.zig+167-166
......@@ -5,15 +5,16 @@ const builtin = @import("builtin");
55const assert = std.debug.assert;
66const net = @This();
77const mem = std.mem;
8const os = std.os;
98const posix = std.posix;
109const fs = std.fs;
1110const io = std.io;
1211const native_endian = builtin.target.cpu.arch.endian();
12const native_os = builtin.os.tag;
13const windows = std.os.windows;
1314
1415// Windows 10 added support for unix sockets in build 17063, redstone 4 is the
1516// first release to support them.
16pub const has_unix_sockets = switch (builtin.os.tag) {
17pub const has_unix_sockets = switch (native_os) {
1718 .windows => builtin.os.version_range.windows.isAtLeast(.win10_rs4) orelse false,
1819 else => true,
1920};
......@@ -28,14 +29,14 @@ pub const IPParseError = error{
2829pub const IPv4ParseError = IPParseError || error{NonCanonical};
2930
3031pub const IPv6ParseError = IPParseError || error{InvalidIpv4Mapping};
31pub const IPv6InterfaceError = os.SocketError || os.IoCtl_SIOCGIFINDEX_Error || error{NameTooLong};
32pub const IPv6InterfaceError = posix.SocketError || posix.IoCtl_SIOCGIFINDEX_Error || error{NameTooLong};
3233pub const IPv6ResolveError = IPv6ParseError || IPv6InterfaceError;
3334
3435pub const Address = extern union {
35 any: os.sockaddr,
36 any: posix.sockaddr,
3637 in: Ip4Address,
3738 in6: Ip6Address,
38 un: if (has_unix_sockets) os.sockaddr.un else void,
39 un: if (has_unix_sockets) posix.sockaddr.un else void,
3940
4041 /// Parse the given IP address string into an Address value.
4142 /// It is recommended to use `resolveIp` instead, to handle
......@@ -85,38 +86,38 @@ pub const Address = extern union {
8586 return error.InvalidIPAddressFormat;
8687 }
8788
88 pub fn parseExpectingFamily(name: []const u8, family: os.sa_family_t, port: u16) !Address {
89 pub fn parseExpectingFamily(name: []const u8, family: posix.sa_family_t, port: u16) !Address {
8990 switch (family) {
90 os.AF.INET => return parseIp4(name, port),
91 os.AF.INET6 => return parseIp6(name, port),
92 os.AF.UNSPEC => return parseIp(name, port),
91 posix.AF.INET => return parseIp4(name, port),
92 posix.AF.INET6 => return parseIp6(name, port),
93 posix.AF.UNSPEC => return parseIp(name, port),
9394 else => unreachable,
9495 }
9596 }
9697
9798 pub fn parseIp6(buf: []const u8, port: u16) IPv6ParseError!Address {
98 return Address{ .in6 = try Ip6Address.parse(buf, port) };
99 return .{ .in6 = try Ip6Address.parse(buf, port) };
99100 }
100101
101102 pub fn resolveIp6(buf: []const u8, port: u16) IPv6ResolveError!Address {
102 return Address{ .in6 = try Ip6Address.resolve(buf, port) };
103 return .{ .in6 = try Ip6Address.resolve(buf, port) };
103104 }
104105
105106 pub fn parseIp4(buf: []const u8, port: u16) IPv4ParseError!Address {
106 return Address{ .in = try Ip4Address.parse(buf, port) };
107 return .{ .in = try Ip4Address.parse(buf, port) };
107108 }
108109
109110 pub fn initIp4(addr: [4]u8, port: u16) Address {
110 return Address{ .in = Ip4Address.init(addr, port) };
111 return .{ .in = Ip4Address.init(addr, port) };
111112 }
112113
113114 pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Address {
114 return Address{ .in6 = Ip6Address.init(addr, port, flowinfo, scope_id) };
115 return .{ .in6 = Ip6Address.init(addr, port, flowinfo, scope_id) };
115116 }
116117
117118 pub fn initUnix(path: []const u8) !Address {
118 var sock_addr = os.sockaddr.un{
119 .family = os.AF.UNIX,
119 var sock_addr = posix.sockaddr.un{
120 .family = posix.AF.UNIX,
120121 .path = undefined,
121122 };
122123
......@@ -133,8 +134,8 @@ pub const Address = extern union {
133134 /// Asserts that the address is ip4 or ip6.
134135 pub fn getPort(self: Address) u16 {
135136 return switch (self.any.family) {
136 os.AF.INET => self.in.getPort(),
137 os.AF.INET6 => self.in6.getPort(),
137 posix.AF.INET => self.in.getPort(),
138 posix.AF.INET6 => self.in6.getPort(),
138139 else => unreachable,
139140 };
140141 }
......@@ -143,8 +144,8 @@ pub const Address = extern union {
143144 /// Asserts that the address is ip4 or ip6.
144145 pub fn setPort(self: *Address, port: u16) void {
145146 switch (self.any.family) {
146 os.AF.INET => self.in.setPort(port),
147 os.AF.INET6 => self.in6.setPort(port),
147 posix.AF.INET => self.in.setPort(port),
148 posix.AF.INET6 => self.in6.setPort(port),
148149 else => unreachable,
149150 }
150151 }
......@@ -152,10 +153,10 @@ pub const Address = extern union {
152153 /// Asserts that `addr` is an IP address.
153154 /// This function will read past the end of the pointer, with a size depending
154155 /// on the address family.
155 pub fn initPosix(addr: *align(4) const os.sockaddr) Address {
156 pub fn initPosix(addr: *align(4) const posix.sockaddr) Address {
156157 switch (addr.family) {
157 os.AF.INET => return Address{ .in = Ip4Address{ .sa = @as(*const os.sockaddr.in, @ptrCast(addr)).* } },
158 os.AF.INET6 => return Address{ .in6 = Ip6Address{ .sa = @as(*const os.sockaddr.in6, @ptrCast(addr)).* } },
158 posix.AF.INET => return Address{ .in = Ip4Address{ .sa = @as(*const posix.sockaddr.in, @ptrCast(addr)).* } },
159 posix.AF.INET6 => return Address{ .in6 = Ip6Address{ .sa = @as(*const posix.sockaddr.in6, @ptrCast(addr)).* } },
159160 else => unreachable,
160161 }
161162 }
......@@ -168,9 +169,9 @@ pub const Address = extern union {
168169 ) !void {
169170 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
170171 switch (self.any.family) {
171 os.AF.INET => try self.in.format(fmt, options, out_stream),
172 os.AF.INET6 => try self.in6.format(fmt, options, out_stream),
173 os.AF.UNIX => {
172 posix.AF.INET => try self.in.format(fmt, options, out_stream),
173 posix.AF.INET6 => try self.in6.format(fmt, options, out_stream),
174 posix.AF.UNIX => {
174175 if (!has_unix_sockets) {
175176 unreachable;
176177 }
......@@ -187,11 +188,11 @@ pub const Address = extern union {
187188 return mem.eql(u8, a_bytes, b_bytes);
188189 }
189190
190 pub fn getOsSockLen(self: Address) os.socklen_t {
191 pub fn getOsSockLen(self: Address) posix.socklen_t {
191192 switch (self.any.family) {
192 os.AF.INET => return self.in.getOsSockLen(),
193 os.AF.INET6 => return self.in6.getOsSockLen(),
194 os.AF.UNIX => {
193 posix.AF.INET => return self.in.getOsSockLen(),
194 posix.AF.INET6 => return self.in6.getOsSockLen(),
195 posix.AF.UNIX => {
195196 if (!has_unix_sockets) {
196197 unreachable;
197198 }
......@@ -204,7 +205,7 @@ pub const Address = extern union {
204205 // provide the full buffer size (e.g. getsockname, getpeername, recvfrom, accept).
205206 //
206207 // To access the path, std.mem.sliceTo(&address.un.path, 0) should be used.
207 return @as(os.socklen_t, @intCast(@sizeOf(os.sockaddr.un)));
208 return @as(posix.socklen_t, @intCast(@sizeOf(posix.sockaddr.un)));
208209 },
209210
210211 else => unreachable,
......@@ -247,7 +248,7 @@ pub const Address = extern union {
247248 posix.SO.REUSEADDR,
248249 &mem.toBytes(@as(c_int, 1)),
249250 );
250 switch (builtin.os.tag) {
251 switch (native_os) {
251252 .windows => {},
252253 else => try posix.setsockopt(
253254 sockfd,
......@@ -267,7 +268,7 @@ pub const Address = extern union {
267268};
268269
269270pub const Ip4Address = extern struct {
270 sa: os.sockaddr.in,
271 sa: posix.sockaddr.in,
271272
272273 pub fn parse(buf: []const u8, port: u16) IPv4ParseError!Ip4Address {
273274 var result = Ip4Address{
......@@ -330,7 +331,7 @@ pub const Ip4Address = extern struct {
330331
331332 pub fn init(addr: [4]u8, port: u16) Ip4Address {
332333 return Ip4Address{
333 .sa = os.sockaddr.in{
334 .sa = posix.sockaddr.in{
334335 .port = mem.nativeToBig(u16, port),
335336 .addr = @as(*align(1) const u32, @ptrCast(&addr)).*,
336337 },
......@@ -367,21 +368,21 @@ pub const Ip4Address = extern struct {
367368 });
368369 }
369370
370 pub fn getOsSockLen(self: Ip4Address) os.socklen_t {
371 pub fn getOsSockLen(self: Ip4Address) posix.socklen_t {
371372 _ = self;
372 return @sizeOf(os.sockaddr.in);
373 return @sizeOf(posix.sockaddr.in);
373374 }
374375};
375376
376377pub const Ip6Address = extern struct {
377 sa: os.sockaddr.in6,
378 sa: posix.sockaddr.in6,
378379
379380 /// Parse a given IPv6 address string into an Address.
380381 /// Assumes the Scope ID of the address is fully numeric.
381382 /// For non-numeric addresses, see `resolveIp6`.
382383 pub fn parse(buf: []const u8, port: u16) IPv6ParseError!Ip6Address {
383384 var result = Ip6Address{
384 .sa = os.sockaddr.in6{
385 .sa = posix.sockaddr.in6{
385386 .scope_id = 0,
386387 .port = mem.nativeToBig(u16, port),
387388 .flowinfo = 0,
......@@ -499,7 +500,7 @@ pub const Ip6Address = extern struct {
499500 pub fn resolve(buf: []const u8, port: u16) IPv6ResolveError!Ip6Address {
500501 // TODO: Unify the implementations of resolveIp6 and parseIp6.
501502 var result = Ip6Address{
502 .sa = os.sockaddr.in6{
503 .sa = posix.sockaddr.in6{
503504 .scope_id = 0,
504505 .port = mem.nativeToBig(u16, port),
505506 .flowinfo = 0,
......@@ -516,7 +517,7 @@ pub const Ip6Address = extern struct {
516517 var abbrv = false;
517518
518519 var scope_id = false;
519 var scope_id_value: [os.IFNAMESIZE - 1]u8 = undefined;
520 var scope_id_value: [posix.IFNAMESIZE - 1]u8 = undefined;
520521 var scope_id_index: usize = 0;
521522
522523 for (buf, 0..) |c, i| {
......@@ -632,7 +633,7 @@ pub const Ip6Address = extern struct {
632633
633634 pub fn init(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Ip6Address {
634635 return Ip6Address{
635 .sa = os.sockaddr.in6{
636 .sa = posix.sockaddr.in6{
636637 .addr = addr,
637638 .port = mem.nativeToBig(u16, port),
638639 .flowinfo = flowinfo,
......@@ -702,51 +703,51 @@ pub const Ip6Address = extern struct {
702703 try std.fmt.format(out_stream, "]:{}", .{port});
703704 }
704705
705 pub fn getOsSockLen(self: Ip6Address) os.socklen_t {
706 pub fn getOsSockLen(self: Ip6Address) posix.socklen_t {
706707 _ = self;
707 return @sizeOf(os.sockaddr.in6);
708 return @sizeOf(posix.sockaddr.in6);
708709 }
709710};
710711
711712pub fn connectUnixSocket(path: []const u8) !Stream {
712713 const opt_non_block = 0;
713 const sockfd = try os.socket(
714 os.AF.UNIX,
715 os.SOCK.STREAM | os.SOCK.CLOEXEC | opt_non_block,
714 const sockfd = try posix.socket(
715 posix.AF.UNIX,
716 posix.SOCK.STREAM | posix.SOCK.CLOEXEC | opt_non_block,
716717 0,
717718 );
718719 errdefer Stream.close(.{ .handle = sockfd });
719720
720721 var addr = try std.net.Address.initUnix(path);
721 try os.connect(sockfd, &addr.any, addr.getOsSockLen());
722 try posix.connect(sockfd, &addr.any, addr.getOsSockLen());
722723
723 return Stream{ .handle = sockfd };
724 return .{ .handle = sockfd };
724725}
725726
726727fn if_nametoindex(name: []const u8) IPv6InterfaceError!u32 {
727 if (builtin.target.os.tag == .linux) {
728 var ifr: os.ifreq = undefined;
729 const sockfd = try os.socket(os.AF.UNIX, os.SOCK.DGRAM | os.SOCK.CLOEXEC, 0);
728 if (native_os == .linux) {
729 var ifr: posix.ifreq = undefined;
730 const sockfd = try posix.socket(posix.AF.UNIX, posix.SOCK.DGRAM | posix.SOCK.CLOEXEC, 0);
730731 defer Stream.close(.{ .handle = sockfd });
731732
732733 @memcpy(ifr.ifrn.name[0..name.len], name);
733734 ifr.ifrn.name[name.len] = 0;
734735
735736 // TODO investigate if this needs to be integrated with evented I/O.
736 try os.ioctl_SIOCGIFINDEX(sockfd, &ifr);
737 try posix.ioctl_SIOCGIFINDEX(sockfd, &ifr);
737738
738 return @as(u32, @bitCast(ifr.ifru.ivalue));
739 return @bitCast(ifr.ifru.ivalue);
739740 }
740741
741 if (comptime builtin.target.os.tag.isDarwin()) {
742 if (name.len >= os.IFNAMESIZE)
742 if (native_os.isDarwin()) {
743 if (name.len >= posix.IFNAMESIZE)
743744 return error.NameTooLong;
744745
745 var if_name: [os.IFNAMESIZE:0]u8 = undefined;
746 var if_name: [posix.IFNAMESIZE:0]u8 = undefined;
746747 @memcpy(if_name[0..name.len], name);
747748 if_name[name.len] = 0;
748749 const if_slice = if_name[0..name.len :0];
749 const index = os.system.if_nametoindex(if_slice);
750 const index = std.c.if_nametoindex(if_slice);
750751 if (index == 0)
751752 return error.InterfaceNotFound;
752753 return @as(u32, @bitCast(index));
......@@ -786,24 +787,24 @@ pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) T
786787 else => return err,
787788 };
788789 }
789 return std.os.ConnectError.ConnectionRefused;
790 return posix.ConnectError.ConnectionRefused;
790791}
791792
792pub const TcpConnectToAddressError = std.os.SocketError || std.os.ConnectError;
793pub const TcpConnectToAddressError = posix.SocketError || posix.ConnectError;
793794
794795pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream {
795796 const nonblock = 0;
796 const sock_flags = os.SOCK.STREAM | nonblock |
797 (if (builtin.target.os.tag == .windows) 0 else os.SOCK.CLOEXEC);
798 const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO.TCP);
797 const sock_flags = posix.SOCK.STREAM | nonblock |
798 (if (native_os == .windows) 0 else posix.SOCK.CLOEXEC);
799 const sockfd = try posix.socket(address.any.family, sock_flags, posix.IPPROTO.TCP);
799800 errdefer Stream.close(.{ .handle = sockfd });
800801
801 try os.connect(sockfd, &address.any, address.getOsSockLen());
802 try posix.connect(sockfd, &address.any, address.getOsSockLen());
802803
803804 return Stream{ .handle = sockfd };
804805}
805806
806const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || std.os.SocketError || std.os.BindError || std.os.SetSockOptError || error{
807const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || posix.SocketError || posix.BindError || posix.SetSockOptError || error{
807808 // TODO: break this up into error sets from the various underlying functions
808809
809810 TemporaryNameServerFailure,
......@@ -844,30 +845,30 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
844845 const arena = result.arena.allocator();
845846 errdefer result.deinit();
846847
847 if (builtin.target.os.tag == .windows) {
848 if (native_os == .windows) {
848849 const name_c = try allocator.dupeZ(u8, name);
849850 defer allocator.free(name_c);
850851
851852 const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port});
852853 defer allocator.free(port_c);
853854
854 const ws2_32 = os.windows.ws2_32;
855 const hints = os.addrinfo{
855 const ws2_32 = windows.ws2_32;
856 const hints = posix.addrinfo{
856857 .flags = ws2_32.AI.NUMERICSERV,
857 .family = os.AF.UNSPEC,
858 .socktype = os.SOCK.STREAM,
859 .protocol = os.IPPROTO.TCP,
858 .family = posix.AF.UNSPEC,
859 .socktype = posix.SOCK.STREAM,
860 .protocol = posix.IPPROTO.TCP,
860861 .canonname = null,
861862 .addr = null,
862863 .addrlen = 0,
863864 .next = null,
864865 };
865 var res: ?*os.addrinfo = null;
866 var res: ?*posix.addrinfo = null;
866867 var first = true;
867868 while (true) {
868869 const rc = ws2_32.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res);
869 switch (@as(os.windows.ws2_32.WinsockError, @enumFromInt(@as(u16, @intCast(rc))))) {
870 @as(os.windows.ws2_32.WinsockError, @enumFromInt(0)) => break,
870 switch (@as(windows.ws2_32.WinsockError, @enumFromInt(@as(u16, @intCast(rc))))) {
871 @as(windows.ws2_32.WinsockError, @enumFromInt(0)) => break,
871872 .WSATRY_AGAIN => return error.TemporaryNameServerFailure,
872873 .WSANO_RECOVERY => return error.NameServerFailure,
873874 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
......@@ -879,10 +880,10 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
879880 .WSANOTINITIALISED => {
880881 if (!first) return error.Unexpected;
881882 first = false;
882 try os.windows.callWSAStartup();
883 try windows.callWSAStartup();
883884 continue;
884885 },
885 else => |err| return os.windows.unexpectedWSAError(err),
886 else => |err| return windows.unexpectedWSAError(err),
886887 }
887888 }
888889 defer ws2_32.freeaddrinfo(res);
......@@ -923,18 +924,18 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
923924 const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port});
924925 defer allocator.free(port_c);
925926
926 const sys = if (builtin.target.os.tag == .windows) os.windows.ws2_32 else os.system;
927 const hints = os.addrinfo{
927 const sys = if (native_os == .windows) windows.ws2_32 else posix.system;
928 const hints = posix.addrinfo{
928929 .flags = sys.AI.NUMERICSERV,
929 .family = os.AF.UNSPEC,
930 .socktype = os.SOCK.STREAM,
931 .protocol = os.IPPROTO.TCP,
930 .family = posix.AF.UNSPEC,
931 .socktype = posix.SOCK.STREAM,
932 .protocol = posix.IPPROTO.TCP,
932933 .canonname = null,
933934 .addr = null,
934935 .addrlen = 0,
935936 .next = null,
936937 };
937 var res: ?*os.addrinfo = null;
938 var res: ?*posix.addrinfo = null;
938939 switch (sys.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) {
939940 @as(sys.EAI, @enumFromInt(0)) => {},
940941 .ADDRFAMILY => return error.HostLacksNetworkAddresses,
......@@ -947,8 +948,8 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
947948 .NONAME => return error.UnknownHostName,
948949 .SERVICE => return error.ServiceUnavailable,
949950 .SOCKTYPE => unreachable, // Invalid socket type requested in hints
950 .SYSTEM => switch (os.errno(-1)) {
951 else => |e| return os.unexpectedErrno(e),
951 .SYSTEM => switch (posix.errno(-1)) {
952 else => |e| return posix.unexpectedErrno(e),
952953 },
953954 else => unreachable,
954955 }
......@@ -983,9 +984,9 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
983984 return result;
984985 }
985986
986 if (builtin.target.os.tag == .linux) {
987 if (native_os == .linux) {
987988 const flags = std.c.AI.NUMERICSERV;
988 const family = os.AF.UNSPEC;
989 const family = posix.AF.UNSPEC;
989990 var lookup_addrs = std.ArrayList(LookupAddr).init(allocator);
990991 defer lookup_addrs.deinit();
991992
......@@ -1026,7 +1027,7 @@ fn linuxLookupName(
10261027 addrs: *std.ArrayList(LookupAddr),
10271028 canon: *std.ArrayList(u8),
10281029 opt_name: ?[]const u8,
1029 family: os.sa_family_t,
1030 family: posix.sa_family_t,
10301031 flags: u32,
10311032 port: u16,
10321033) !void {
......@@ -1066,9 +1067,9 @@ fn linuxLookupName(
10661067
10671068 // No further processing is needed if there are fewer than 2
10681069 // results or if there are only IPv4 results.
1069 if (addrs.items.len == 1 or family == os.AF.INET) return;
1070 if (addrs.items.len == 1 or family == posix.AF.INET) return;
10701071 const all_ip4 = for (addrs.items) |addr| {
1071 if (addr.addr.any.family != os.AF.INET) break false;
1072 if (addr.addr.any.family != posix.AF.INET) break false;
10721073 } else true;
10731074 if (all_ip4) return;
10741075
......@@ -1081,42 +1082,42 @@ fn linuxLookupName(
10811082 // A more idiomatic "ziggy" implementation would be welcome.
10821083 for (addrs.items, 0..) |*addr, i| {
10831084 var key: i32 = 0;
1084 var sa6: os.sockaddr.in6 = undefined;
1085 @memset(@as([*]u8, @ptrCast(&sa6))[0..@sizeOf(os.sockaddr.in6)], 0);
1086 var da6 = os.sockaddr.in6{
1087 .family = os.AF.INET6,
1085 var sa6: posix.sockaddr.in6 = undefined;
1086 @memset(@as([*]u8, @ptrCast(&sa6))[0..@sizeOf(posix.sockaddr.in6)], 0);
1087 var da6 = posix.sockaddr.in6{
1088 .family = posix.AF.INET6,
10881089 .scope_id = addr.addr.in6.sa.scope_id,
10891090 .port = 65535,
10901091 .flowinfo = 0,
10911092 .addr = [1]u8{0} ** 16,
10921093 };
1093 var sa4: os.sockaddr.in = undefined;
1094 @memset(@as([*]u8, @ptrCast(&sa4))[0..@sizeOf(os.sockaddr.in)], 0);
1095 var da4 = os.sockaddr.in{
1096 .family = os.AF.INET,
1094 var sa4: posix.sockaddr.in = undefined;
1095 @memset(@as([*]u8, @ptrCast(&sa4))[0..@sizeOf(posix.sockaddr.in)], 0);
1096 var da4 = posix.sockaddr.in{
1097 .family = posix.AF.INET,
10971098 .port = 65535,
10981099 .addr = 0,
10991100 .zero = [1]u8{0} ** 8,
11001101 };
1101 var sa: *align(4) os.sockaddr = undefined;
1102 var da: *align(4) os.sockaddr = undefined;
1103 var salen: os.socklen_t = undefined;
1104 var dalen: os.socklen_t = undefined;
1105 if (addr.addr.any.family == os.AF.INET6) {
1102 var sa: *align(4) posix.sockaddr = undefined;
1103 var da: *align(4) posix.sockaddr = undefined;
1104 var salen: posix.socklen_t = undefined;
1105 var dalen: posix.socklen_t = undefined;
1106 if (addr.addr.any.family == posix.AF.INET6) {
11061107 da6.addr = addr.addr.in6.sa.addr;
11071108 da = @ptrCast(&da6);
1108 dalen = @sizeOf(os.sockaddr.in6);
1109 dalen = @sizeOf(posix.sockaddr.in6);
11091110 sa = @ptrCast(&sa6);
1110 salen = @sizeOf(os.sockaddr.in6);
1111 salen = @sizeOf(posix.sockaddr.in6);
11111112 } else {
11121113 sa6.addr[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*;
11131114 da6.addr[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*;
11141115 mem.writeInt(u32, da6.addr[12..], addr.addr.in.sa.addr, native_endian);
11151116 da4.addr = addr.addr.in.sa.addr;
11161117 da = @ptrCast(&da4);
1117 dalen = @sizeOf(os.sockaddr.in);
1118 dalen = @sizeOf(posix.sockaddr.in);
11181119 sa = @ptrCast(&sa4);
1119 salen = @sizeOf(os.sockaddr.in);
1120 salen = @sizeOf(posix.sockaddr.in);
11201121 }
11211122 const dpolicy = policyOf(da6.addr);
11221123 const dscope: i32 = scopeOf(da6.addr);
......@@ -1124,13 +1125,13 @@ fn linuxLookupName(
11241125 const dprec: i32 = dpolicy.prec;
11251126 const MAXADDRS = 3;
11261127 var prefixlen: i32 = 0;
1127 const sock_flags = os.SOCK.DGRAM | os.SOCK.CLOEXEC;
1128 if (os.socket(addr.addr.any.family, sock_flags, os.IPPROTO.UDP)) |fd| syscalls: {
1128 const sock_flags = posix.SOCK.DGRAM | posix.SOCK.CLOEXEC;
1129 if (posix.socket(addr.addr.any.family, sock_flags, posix.IPPROTO.UDP)) |fd| syscalls: {
11291130 defer Stream.close(.{ .handle = fd });
1130 os.connect(fd, da, dalen) catch break :syscalls;
1131 posix.connect(fd, da, dalen) catch break :syscalls;
11311132 key |= DAS_USABLE;
1132 os.getsockname(fd, sa, &salen) catch break :syscalls;
1133 if (addr.addr.any.family == os.AF.INET) {
1133 posix.getsockname(fd, sa, &salen) catch break :syscalls;
1134 if (addr.addr.any.family == posix.AF.INET) {
11341135 mem.writeInt(u32, sa6.addr[12..16], sa4.addr, native_endian);
11351136 }
11361137 if (dscope == @as(i32, scopeOf(sa6.addr))) key |= DAS_MATCHINGSCOPE;
......@@ -1267,28 +1268,28 @@ fn addrCmpLessThan(context: void, b: LookupAddr, a: LookupAddr) bool {
12671268
12681269fn linuxLookupNameFromNull(
12691270 addrs: *std.ArrayList(LookupAddr),
1270 family: os.sa_family_t,
1271 family: posix.sa_family_t,
12711272 flags: u32,
12721273 port: u16,
12731274) !void {
12741275 if ((flags & std.c.AI.PASSIVE) != 0) {
1275 if (family != os.AF.INET6) {
1276 if (family != posix.AF.INET6) {
12761277 (try addrs.addOne()).* = LookupAddr{
12771278 .addr = Address.initIp4([1]u8{0} ** 4, port),
12781279 };
12791280 }
1280 if (family != os.AF.INET) {
1281 if (family != posix.AF.INET) {
12811282 (try addrs.addOne()).* = LookupAddr{
12821283 .addr = Address.initIp6([1]u8{0} ** 16, port, 0, 0),
12831284 };
12841285 }
12851286 } else {
1286 if (family != os.AF.INET6) {
1287 if (family != posix.AF.INET6) {
12871288 (try addrs.addOne()).* = LookupAddr{
12881289 .addr = Address.initIp4([4]u8{ 127, 0, 0, 1 }, port),
12891290 };
12901291 }
1291 if (family != os.AF.INET) {
1292 if (family != posix.AF.INET) {
12921293 (try addrs.addOne()).* = LookupAddr{
12931294 .addr = Address.initIp6(([1]u8{0} ** 15) ++ [1]u8{1}, port, 0, 0),
12941295 };
......@@ -1300,7 +1301,7 @@ fn linuxLookupNameFromHosts(
13001301 addrs: *std.ArrayList(LookupAddr),
13011302 canon: *std.ArrayList(u8),
13021303 name: []const u8,
1303 family: os.sa_family_t,
1304 family: posix.sa_family_t,
13041305 port: u16,
13051306) !void {
13061307 const file = fs.openFileAbsoluteZ("/etc/hosts", .{}) catch |err| switch (err) {
......@@ -1374,7 +1375,7 @@ fn linuxLookupNameFromDnsSearch(
13741375 addrs: *std.ArrayList(LookupAddr),
13751376 canon: *std.ArrayList(u8),
13761377 name: []const u8,
1377 family: os.sa_family_t,
1378 family: posix.sa_family_t,
13781379 port: u16,
13791380) !void {
13801381 var rc: ResolvConf = undefined;
......@@ -1429,7 +1430,7 @@ fn linuxLookupNameFromDns(
14291430 addrs: *std.ArrayList(LookupAddr),
14301431 canon: *std.ArrayList(u8),
14311432 name: []const u8,
1432 family: os.sa_family_t,
1433 family: posix.sa_family_t,
14331434 rc: ResolvConf,
14341435 port: u16,
14351436) !void {
......@@ -1439,12 +1440,12 @@ fn linuxLookupNameFromDns(
14391440 .port = port,
14401441 };
14411442 const AfRr = struct {
1442 af: os.sa_family_t,
1443 af: posix.sa_family_t,
14431444 rr: u8,
14441445 };
14451446 const afrrs = [_]AfRr{
1446 AfRr{ .af = os.AF.INET6, .rr = os.RR.A },
1447 AfRr{ .af = os.AF.INET, .rr = os.RR.AAAA },
1447 AfRr{ .af = posix.AF.INET6, .rr = posix.RR.A },
1448 AfRr{ .af = posix.AF.INET, .rr = posix.RR.AAAA },
14481449 };
14491450 var qbuf: [2][280]u8 = undefined;
14501451 var abuf: [2][512]u8 = undefined;
......@@ -1454,7 +1455,7 @@ fn linuxLookupNameFromDns(
14541455
14551456 for (afrrs) |afrr| {
14561457 if (family != afrr.af) {
1457 const len = os.res_mkquery(0, name, 1, afrr.rr, &[_]u8{}, null, &qbuf[nq]);
1458 const len = posix.res_mkquery(0, name, 1, afrr.rr, &[_]u8{}, null, &qbuf[nq]);
14581459 qp[nq] = qbuf[nq][0..len];
14591460 nq += 1;
14601461 }
......@@ -1582,8 +1583,8 @@ fn resMSendRc(
15821583 const timeout = 1000 * rc.timeout;
15831584 const attempts = rc.attempts;
15841585
1585 var sl: os.socklen_t = @sizeOf(os.sockaddr.in);
1586 var family: os.sa_family_t = os.AF.INET;
1586 var sl: posix.socklen_t = @sizeOf(posix.sockaddr.in);
1587 var family: posix.sa_family_t = posix.AF.INET;
15871588
15881589 var ns_list = std.ArrayList(Address).init(rc.ns.allocator);
15891590 defer ns_list.deinit();
......@@ -1594,18 +1595,18 @@ fn resMSendRc(
15941595 for (rc.ns.items, 0..) |iplit, i| {
15951596 ns[i] = iplit.addr;
15961597 assert(ns[i].getPort() == 53);
1597 if (iplit.addr.any.family != os.AF.INET) {
1598 family = os.AF.INET6;
1598 if (iplit.addr.any.family != posix.AF.INET) {
1599 family = posix.AF.INET6;
15991600 }
16001601 }
16011602
1602 const flags = os.SOCK.DGRAM | os.SOCK.CLOEXEC | os.SOCK.NONBLOCK;
1603 const fd = os.socket(family, flags, 0) catch |err| switch (err) {
1603 const flags = posix.SOCK.DGRAM | posix.SOCK.CLOEXEC | posix.SOCK.NONBLOCK;
1604 const fd = posix.socket(family, flags, 0) catch |err| switch (err) {
16041605 error.AddressFamilyNotSupported => blk: {
16051606 // Handle case where system lacks IPv6 support
1606 if (family == os.AF.INET6) {
1607 family = os.AF.INET;
1608 break :blk try os.socket(os.AF.INET, flags, 0);
1607 if (family == posix.AF.INET6) {
1608 family = posix.AF.INET;
1609 break :blk try posix.socket(posix.AF.INET, flags, 0);
16091610 }
16101611 return err;
16111612 },
......@@ -1618,33 +1619,33 @@ fn resMSendRc(
16181619 // packet which is up to the caller to interpret.
16191620
16201621 // Convert any IPv4 addresses in a mixed environment to v4-mapped
1621 if (family == os.AF.INET6) {
1622 try os.setsockopt(
1622 if (family == posix.AF.INET6) {
1623 try posix.setsockopt(
16231624 fd,
1624 os.SOL.IPV6,
1625 os.linux.IPV6.V6ONLY,
1625 posix.SOL.IPV6,
1626 std.os.linux.IPV6.V6ONLY,
16261627 &mem.toBytes(@as(c_int, 0)),
16271628 );
16281629 for (0..ns.len) |i| {
1629 if (ns[i].any.family != os.AF.INET) continue;
1630 if (ns[i].any.family != posix.AF.INET) continue;
16301631 mem.writeInt(u32, ns[i].in6.sa.addr[12..], ns[i].in.sa.addr, native_endian);
16311632 ns[i].in6.sa.addr[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*;
1632 ns[i].any.family = os.AF.INET6;
1633 ns[i].any.family = posix.AF.INET6;
16331634 ns[i].in6.sa.flowinfo = 0;
16341635 ns[i].in6.sa.scope_id = 0;
16351636 }
1636 sl = @sizeOf(os.sockaddr.in6);
1637 sl = @sizeOf(posix.sockaddr.in6);
16371638 }
16381639
16391640 // Get local address and open/bind a socket
16401641 var sa: Address = undefined;
16411642 @memset(@as([*]u8, @ptrCast(&sa))[0..@sizeOf(Address)], 0);
16421643 sa.any.family = family;
1643 try os.bind(fd, &sa.any, sl);
1644 try posix.bind(fd, &sa.any, sl);
16441645
1645 var pfd = [1]os.pollfd{os.pollfd{
1646 var pfd = [1]posix.pollfd{posix.pollfd{
16461647 .fd = fd,
1647 .events = os.POLL.IN,
1648 .events = posix.POLL.IN,
16481649 .revents = undefined,
16491650 }};
16501651 const retry_interval = timeout / attempts;
......@@ -1663,7 +1664,7 @@ fn resMSendRc(
16631664 if (answers[i].len == 0) {
16641665 var j: usize = 0;
16651666 while (j < ns.len) : (j += 1) {
1666 _ = os.sendto(fd, queries[i], os.MSG.NOSIGNAL, &ns[j].any, sl) catch undefined;
1667 _ = posix.sendto(fd, queries[i], posix.MSG.NOSIGNAL, &ns[j].any, sl) catch undefined;
16671668 }
16681669 }
16691670 }
......@@ -1673,12 +1674,12 @@ fn resMSendRc(
16731674
16741675 // Wait for a response, or until time to retry
16751676 const clamped_timeout = @min(@as(u31, std.math.maxInt(u31)), t1 + retry_interval - t2);
1676 const nevents = os.poll(&pfd, clamped_timeout) catch 0;
1677 const nevents = posix.poll(&pfd, clamped_timeout) catch 0;
16771678 if (nevents == 0) continue;
16781679
16791680 while (true) {
16801681 var sl_copy = sl;
1681 const rlen = os.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break;
1682 const rlen = posix.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break;
16821683
16831684 // Ignore non-identifiable packets
16841685 if (rlen < 4) continue;
......@@ -1704,7 +1705,7 @@ fn resMSendRc(
17041705 0, 3 => {},
17051706 2 => if (servfail_retry != 0) {
17061707 servfail_retry -= 1;
1707 _ = os.sendto(fd, queries[i], os.MSG.NOSIGNAL, &ns[j].any, sl) catch undefined;
1708 _ = posix.sendto(fd, queries[i], posix.MSG.NOSIGNAL, &ns[j].any, sl) catch undefined;
17081709 },
17091710 else => continue,
17101711 }
......@@ -1758,24 +1759,24 @@ fn dnsParse(
17581759
17591760fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) !void {
17601761 switch (rr) {
1761 os.RR.A => {
1762 posix.RR.A => {
17621763 if (data.len != 4) return error.InvalidDnsARecord;
17631764 const new_addr = try ctx.addrs.addOne();
17641765 new_addr.* = LookupAddr{
17651766 .addr = Address.initIp4(data[0..4].*, ctx.port),
17661767 };
17671768 },
1768 os.RR.AAAA => {
1769 posix.RR.AAAA => {
17691770 if (data.len != 16) return error.InvalidDnsAAAARecord;
17701771 const new_addr = try ctx.addrs.addOne();
17711772 new_addr.* = LookupAddr{
17721773 .addr = Address.initIp6(data[0..16].*, ctx.port, 0, 0),
17731774 };
17741775 },
1775 os.RR.CNAME => {
1776 posix.RR.CNAME => {
17761777 var tmp: [256]u8 = undefined;
17771778 // Returns len of compressed name. strlen to get canon name.
1778 _ = try os.dn_expand(packet, data, &tmp);
1779 _ = try posix.dn_expand(packet, data, &tmp);
17791780 const canon_name = mem.sliceTo(&tmp, 0);
17801781 if (isValidHostName(canon_name)) {
17811782 ctx.canon.items.len = 0;
......@@ -1792,14 +1793,14 @@ pub const Stream = struct {
17921793 handle: posix.socket_t,
17931794
17941795 pub fn close(s: Stream) void {
1795 switch (builtin.os.tag) {
1796 .windows => std.os.windows.closesocket(s.handle) catch unreachable,
1796 switch (native_os) {
1797 .windows => windows.closesocket(s.handle) catch unreachable,
17971798 else => posix.close(s.handle),
17981799 }
17991800 }
18001801
1801 pub const ReadError = os.ReadError;
1802 pub const WriteError = os.WriteError;
1802 pub const ReadError = posix.ReadError;
1803 pub const WriteError = posix.WriteError;
18031804
18041805 pub const Reader = io.Reader(Stream, ReadError, read);
18051806 pub const Writer = io.Writer(Stream, WriteError, write);
......@@ -1813,22 +1814,22 @@ pub const Stream = struct {
18131814 }
18141815
18151816 pub fn read(self: Stream, buffer: []u8) ReadError!usize {
1816 if (builtin.os.tag == .windows) {
1817 return os.windows.ReadFile(self.handle, buffer, null);
1817 if (native_os == .windows) {
1818 return windows.ReadFile(self.handle, buffer, null);
18181819 }
18191820
1820 return os.read(self.handle, buffer);
1821 return posix.read(self.handle, buffer);
18211822 }
18221823
1823 pub fn readv(s: Stream, iovecs: []const os.iovec) ReadError!usize {
1824 if (builtin.os.tag == .windows) {
1824 pub fn readv(s: Stream, iovecs: []const posix.iovec) ReadError!usize {
1825 if (native_os == .windows) {
18251826 // TODO improve this to use ReadFileScatter
18261827 if (iovecs.len == 0) return @as(usize, 0);
18271828 const first = iovecs[0];
1828 return os.windows.ReadFile(s.handle, first.iov_base[0..first.iov_len], null);
1829 return windows.ReadFile(s.handle, first.iov_base[0..first.iov_len], null);
18291830 }
18301831
1831 return os.readv(s.handle, iovecs);
1832 return posix.readv(s.handle, iovecs);
18321833 }
18331834
18341835 /// Returns the number of bytes read. If the number read is smaller than
......@@ -1858,11 +1859,11 @@ pub const Stream = struct {
18581859 /// file system thread instead of non-blocking. It needs to be reworked to properly
18591860 /// use non-blocking I/O.
18601861 pub fn write(self: Stream, buffer: []const u8) WriteError!usize {
1861 if (builtin.os.tag == .windows) {
1862 return os.windows.WriteFile(self.handle, buffer, null);
1862 if (native_os == .windows) {
1863 return windows.WriteFile(self.handle, buffer, null);
18631864 }
18641865
1865 return os.write(self.handle, buffer);
1866 return posix.write(self.handle, buffer);
18661867 }
18671868
18681869 pub fn writeAll(self: Stream, bytes: []const u8) WriteError!void {
......@@ -1874,15 +1875,15 @@ pub const Stream = struct {
18741875
18751876 /// See https://github.com/ziglang/zig/issues/7699
18761877 /// See equivalent function: `std.fs.File.writev`.
1877 pub fn writev(self: Stream, iovecs: []const os.iovec_const) WriteError!usize {
1878 return os.writev(self.handle, iovecs);
1878 pub fn writev(self: Stream, iovecs: []const posix.iovec_const) WriteError!usize {
1879 return posix.writev(self.handle, iovecs);
18791880 }
18801881
18811882 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
18821883 /// order to handle partial writes from the underlying OS layer.
18831884 /// See https://github.com/ziglang/zig/issues/7699
18841885 /// See equivalent function: `std.fs.File.writevAll`.
1885 pub fn writevAll(self: Stream, iovecs: []os.iovec_const) WriteError!void {
1886 pub fn writevAll(self: Stream, iovecs: []posix.iovec_const) WriteError!void {
18861887 if (iovecs.len == 0) return;
18871888
18881889 var i: usize = 0;
lib/std/os.zig+155-7613
......@@ -11,8 +11,6 @@
1111//! On Linux libc can be side-stepped by using `std.os.linux` directly.
1212//! * For Windows, this file represents the API that libc would provide for
1313//! Windows. For thin wrappers around Windows-specific APIs, see `std.os.windows`.
14//! Note: The Zig standard library does not support POSIX thread cancellation, and
15//! in general EINTR is handled by trying again.
1614
1715const root = @import("root");
1816const std = @import("std.zig");
......@@ -25,14 +23,6 @@ const fs = std.fs;
2523const dl = @import("dynamic_library.zig");
2624const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES;
2725
28pub const darwin = std.c;
29pub const dragonfly = std.c;
30pub const freebsd = std.c;
31pub const haiku = std.c;
32pub const netbsd = std.c;
33pub const openbsd = std.c;
34pub const solaris = std.c;
35pub const illumos = std.c;
3626pub const linux = @import("os/linux.zig");
3727pub const plan9 = @import("os/plan9.zig");
3828pub const uefi = @import("os/uefi.zig");
......@@ -40,214 +30,15 @@ pub const wasi = @import("os/wasi.zig");
4030pub const emscripten = @import("os/emscripten.zig");
4131pub const windows = @import("os/windows.zig");
4232
43comptime {
44 assert(@import("std") == std); // std lib tests require --zig-lib-dir
45}
46
4733test {
48 _ = darwin;
4934 _ = linux;
5035 if (builtin.os.tag == .uefi) {
5136 _ = uefi;
5237 }
5338 _ = wasi;
5439 _ = windows;
55
56 _ = @import("os/test.zig");
5740}
5841
59/// Applications can override the `system` API layer in their root source file.
60/// Otherwise, when linking libc, this is the C API.
61/// When not linking libc, it is the OS-specific system interface.
62pub const system = if (@hasDecl(root, "os") and @hasDecl(root.os, "system") and root.os != @This())
63 root.os.system
64else if (use_libc)
65 std.c
66else switch (builtin.os.tag) {
67 .linux => linux,
68 .plan9 => plan9,
69 .uefi => uefi,
70 else => struct {},
71};
72
73/// Whether to use libc for the POSIX API layer.
74const use_libc = builtin.link_libc or switch (builtin.os.tag) {
75 .windows, .wasi => true,
76 else => false,
77};
78
79pub const AF = system.AF;
80pub const AF_SUN = system.AF_SUN;
81pub const ARCH = system.ARCH;
82pub const AT = system.AT;
83pub const AT_SUN = system.AT_SUN;
84pub const CLOCK = system.CLOCK;
85pub const CPU_COUNT = system.CPU_COUNT;
86pub const CTL = system.CTL;
87pub const DT = system.DT;
88pub const E = system.E;
89pub const Elf_Symndx = system.Elf_Symndx;
90pub const F = system.F;
91pub const FD_CLOEXEC = system.FD_CLOEXEC;
92pub const Flock = system.Flock;
93pub const HOST_NAME_MAX = system.HOST_NAME_MAX;
94pub const HW = system.HW;
95pub const IFNAMESIZE = system.IFNAMESIZE;
96pub const IOV_MAX = system.IOV_MAX;
97pub const IPPROTO = system.IPPROTO;
98pub const KERN = system.KERN;
99pub const Kevent = system.Kevent;
100pub const LOCK = system.LOCK;
101pub const MADV = system.MADV;
102pub const MAP = system.MAP;
103pub const MSF = system.MSF;
104pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN;
105pub const MFD = system.MFD;
106pub const MMAP2_UNIT = system.MMAP2_UNIT;
107pub const MSG = system.MSG;
108pub const NAME_MAX = system.NAME_MAX;
109pub const O = system.O;
110pub const PATH_MAX = system.PATH_MAX;
111pub const POLL = system.POLL;
112pub const POSIX_FADV = system.POSIX_FADV;
113pub const PR = system.PR;
114pub const PROT = system.PROT;
115pub const REG = system.REG;
116pub const RLIM = system.RLIM;
117pub const RR = system.RR;
118pub const S = system.S;
119pub const SA = system.SA;
120pub const SC = system.SC;
121pub const _SC = system._SC;
122pub const SEEK = system.SEEK;
123pub const SHUT = system.SHUT;
124pub const SIG = system.SIG;
125pub const SIOCGIFINDEX = system.SIOCGIFINDEX;
126pub const SO = system.SO;
127pub const SOCK = system.SOCK;
128pub const SOL = system.SOL;
129pub const STDERR_FILENO = system.STDERR_FILENO;
130pub const STDIN_FILENO = system.STDIN_FILENO;
131pub const STDOUT_FILENO = system.STDOUT_FILENO;
132pub const SYS = system.SYS;
133pub const Sigaction = system.Sigaction;
134pub const Stat = system.Stat;
135pub const T = system.T;
136pub const TCSA = system.TCSA;
137pub const TCP = system.TCP;
138pub const VDSO = system.VDSO;
139pub const W = system.W;
140pub const addrinfo = system.addrinfo;
141pub const blkcnt_t = system.blkcnt_t;
142pub const blksize_t = system.blksize_t;
143pub const clock_t = system.clock_t;
144pub const cpu_set_t = system.cpu_set_t;
145pub const dev_t = system.dev_t;
146pub const dl_phdr_info = system.dl_phdr_info;
147pub const empty_sigset = system.empty_sigset;
148pub const filled_sigset = system.filled_sigset;
149pub const fd_t = system.fd_t;
150pub const gid_t = system.gid_t;
151pub const ifreq = system.ifreq;
152pub const ino_t = system.ino_t;
153pub const mcontext_t = system.mcontext_t;
154pub const mode_t = system.mode_t;
155pub const msghdr = system.msghdr;
156pub const msghdr_const = system.msghdr_const;
157pub const nfds_t = system.nfds_t;
158pub const nlink_t = system.nlink_t;
159pub const off_t = system.off_t;
160pub const pid_t = system.pid_t;
161pub const pollfd = system.pollfd;
162pub const port_t = system.port_t;
163pub const port_event = system.port_event;
164pub const port_notify = system.port_notify;
165pub const file_obj = system.file_obj;
166pub const rlim_t = system.rlim_t;
167pub const rlimit = system.rlimit;
168pub const rlimit_resource = system.rlimit_resource;
169pub const rusage = system.rusage;
170pub const sa_family_t = system.sa_family_t;
171pub const siginfo_t = system.siginfo_t;
172pub const sigset_t = system.sigset_t;
173pub const sockaddr = system.sockaddr;
174pub const socklen_t = system.socklen_t;
175pub const stack_t = system.stack_t;
176pub const time_t = system.time_t;
177pub const timespec = system.timespec;
178pub const timestamp_t = system.timestamp_t;
179pub const timeval = system.timeval;
180pub const timezone = system.timezone;
181pub const ucontext_t = system.ucontext_t;
182pub const uid_t = system.uid_t;
183pub const user_desc = system.user_desc;
184pub const utsname = system.utsname;
185pub const winsize = system.winsize;
186
187pub const termios = system.termios;
188pub const CSIZE = system.CSIZE;
189pub const NCCS = system.NCCS;
190pub const cc_t = system.cc_t;
191pub const V = system.V;
192pub const speed_t = system.speed_t;
193pub const tc_iflag_t = system.tc_iflag_t;
194pub const tc_oflag_t = system.tc_oflag_t;
195pub const tc_cflag_t = system.tc_cflag_t;
196pub const tc_lflag_t = system.tc_lflag_t;
197
198pub const F_OK = system.F_OK;
199pub const R_OK = system.R_OK;
200pub const W_OK = system.W_OK;
201pub const X_OK = system.X_OK;
202
203pub const iovec = extern struct {
204 iov_base: [*]u8,
205 iov_len: usize,
206};
207
208pub const iovec_const = extern struct {
209 iov_base: [*]const u8,
210 iov_len: usize,
211};
212
213pub const ACCMODE = enum(u2) {
214 RDONLY = 0,
215 WRONLY = 1,
216 RDWR = 2,
217};
218
219pub const LOG = struct {
220 /// system is unusable
221 pub const EMERG = 0;
222 /// action must be taken immediately
223 pub const ALERT = 1;
224 /// critical conditions
225 pub const CRIT = 2;
226 /// error conditions
227 pub const ERR = 3;
228 /// warning conditions
229 pub const WARNING = 4;
230 /// normal but significant condition
231 pub const NOTICE = 5;
232 /// informational
233 pub const INFO = 6;
234 /// debug-level messages
235 pub const DEBUG = 7;
236};
237
238/// An fd-relative file path
239///
240/// This is currently only used for WASI-specific functionality, but the concept
241/// is the same as the dirfd/pathname pairs in the `*at(...)` POSIX functions.
242pub const RelativePathWasi = struct {
243 /// Handle to directory
244 dir_fd: fd_t,
245 /// Path to resource within `dir_fd`.
246 relative_path: []const u8,
247};
248
249pub const socket_t = if (builtin.os.tag == .windows) windows.ws2_32.SOCKET else fd_t;
250
25142/// See also `getenv`. Populated by startup code before main().
25243/// TODO this is a footgun because the value will be undefined when using `zig build-lib`.
25344/// https://github.com/ziglang/zig/issues/4524
......@@ -262,7433 +53,184 @@ pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (builtin.
26253 else => undefined,
26354};
26455
265pub const have_sigpipe_support = @hasDecl(@This(), "SIG") and @hasDecl(SIG, "PIPE");
266
267fn noopSigHandler(_: c_int) callconv(.C) void {}
268
269/// On default executed by posix startup code before main(), if SIGPIPE is supported.
270pub fn maybeIgnoreSigpipe() void {
271 if (have_sigpipe_support and !std.options.keep_sigpipe) {
272 const act = Sigaction{
273 // We set handler to a noop function instead of SIG.IGN so we don't leak our
274 // signal disposition to a child process
275 .handler = .{ .handler = noopSigHandler },
276 .mask = empty_sigset,
277 .flags = 0,
278 };
279 sigaction(SIG.PIPE, &act, null) catch |err|
280 std.debug.panic("failed to install noop SIGPIPE handler with '{s}'", .{@errorName(err)});
281 }
282}
283
284/// To obtain errno, call this function with the return value of the
285/// system function call. For some systems this will obtain the value directly
286/// from the return code; for others it will use a thread-local errno variable.
287/// Therefore, this function only returns a well-defined value when it is called
288/// directly after the system function call which one wants to learn the errno
289/// value of.
290pub const errno = system.getErrno;
291
292/// Closes the file descriptor.
293/// This function is not capable of returning any indication of failure. An
294/// application which wants to ensure writes have succeeded before closing
295/// must call `fsync` before `close`.
296/// Note: The Zig standard library does not support POSIX thread cancellation.
297pub fn close(fd: fd_t) void {
298 if (builtin.os.tag == .windows) {
299 return windows.CloseHandle(fd);
300 }
301 if (builtin.os.tag == .wasi and !builtin.link_libc) {
302 _ = wasi.fd_close(fd);
56/// Call from Windows-specific code if you already have a WTF-16LE encoded, null terminated string.
57/// Otherwise use `access` or `accessZ`.
58pub fn accessW(path: [*:0]const u16) windows.GetFileAttributesError!void {
59 const ret = try windows.GetFileAttributesW(path);
60 if (ret != windows.INVALID_FILE_ATTRIBUTES) {
30361 return;
30462 }
305 if (builtin.target.isDarwin()) {
306 // This avoids the EINTR problem.
307 switch (darwin.getErrno(darwin.@"close$NOCANCEL"(fd))) {
308 .BADF => unreachable, // Always a race condition.
309 else => return,
310 }
311 }
312 switch (errno(system.close(fd))) {
313 .BADF => unreachable, // Always a race condition.
314 .INTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425
315 else => return,
63 switch (windows.kernel32.GetLastError()) {
64 .FILE_NOT_FOUND => return error.FileNotFound,
65 .PATH_NOT_FOUND => return error.FileNotFound,
66 .ACCESS_DENIED => return error.PermissionDenied,
67 else => |err| return windows.unexpectedError(err),
31668 }
31769}
31870
319pub const FChmodError = error{
320 AccessDenied,
321 InputOutput,
322 SymLinkLoop,
323 FileNotFound,
324 SystemResources,
325 ReadOnlyFileSystem,
326} || UnexpectedError;
327
328/// Changes the mode of the file referred to by the file descriptor.
329/// The process must have the correct privileges in order to do this
330/// successfully, or must have the effective user ID matching the owner
331/// of the file.
332pub fn fchmod(fd: fd_t, mode: mode_t) FChmodError!void {
333 if (!std.fs.has_executable_bit) @compileError("fchmod unsupported by target OS");
334
335 while (true) {
336 const res = system.fchmod(fd, mode);
71pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool {
72 return switch (os.tag) {
73 .windows,
74 .macos,
75 .ios,
76 .watchos,
77 .tvos,
78 .linux,
79 .solaris,
80 .illumos,
81 .freebsd,
82 => true,
33783
338 switch (system.getErrno(res)) {
339 .SUCCESS => return,
340 .INTR => continue,
341 .BADF => unreachable,
342 .FAULT => unreachable,
343 .INVAL => unreachable,
344 .ACCES => return error.AccessDenied,
345 .IO => return error.InputOutput,
346 .LOOP => return error.SymLinkLoop,
347 .NOENT => return error.FileNotFound,
348 .NOMEM => return error.SystemResources,
349 .NOTDIR => return error.FileNotFound,
350 .PERM => return error.AccessDenied,
351 .ROFS => return error.ReadOnlyFileSystem,
352 else => |err| return unexpectedErrno(err),
353 }
354 }
84 .dragonfly => os.version_range.semver.max.order(.{ .major = 6, .minor = 0, .patch = 0 }) != .lt,
85 .netbsd => os.version_range.semver.max.order(.{ .major = 10, .minor = 0, .patch = 0 }) != .lt,
86 else => false,
87 };
35588}
35689
357const FChmodAtError = FChmodError || error{
358 /// A component of `path` exceeded `NAME_MAX`, or the entire path exceeded
359 /// `PATH_MAX`.
360 NameTooLong,
361 /// `path` resolves to a symbolic link, and `AT.SYMLINK_NOFOLLOW` was set
362 /// in `flags`. This error only occurs on Linux, where changing the mode of
363 /// a symbolic link has no meaning and can cause undefined behaviour on
364 /// certain filesystems.
365 ///
366 /// The procfs fallback was used but procfs was not mounted.
367 OperationNotSupported,
368 /// The procfs fallback was used but the process exceeded its open file
369 /// limit.
370 ProcessFdQuotaExceeded,
371 /// The procfs fallback was used but the system exceeded it open file limit.
372 SystemFdQuotaExceeded,
373};
374
375var has_fchmodat2_syscall = std.atomic.Value(bool).init(true);
376
377/// Changes the `mode` of `path` relative to the directory referred to by
378/// `dirfd`. The process must have the correct privileges in order to do this
379/// successfully, or must have the effective user ID matching the owner of the
380/// file.
90/// Return canonical path of handle `fd`.
38191///
382/// On Linux the `fchmodat2` syscall will be used if available, otherwise a
383/// workaround using procfs will be employed. Changing the mode of a symbolic
384/// link with `AT.SYMLINK_NOFOLLOW` set will also return
385/// `OperationNotSupported`, as:
92/// This function is very host-specific and is not universally supported by all hosts.
93/// For example, while it generally works on Linux, macOS, FreeBSD or Windows, it is
94/// unsupported on WASI.
38695///
387/// 1. Permissions on the link are ignored when resolving its target.
388/// 2. This operation has been known to invoke undefined behaviour across
389/// different filesystems[1].
96/// * On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
97/// * On other platforms, the result is an opaque sequence of bytes with no particular encoding.
39098///
391/// [1]: https://sourceware.org/legacy-ml/libc-alpha/2020-02/msg00467.html.
392pub inline fn fchmodat(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void {
393 if (!std.fs.has_executable_bit) @compileError("fchmodat unsupported by target OS");
394
395 // No special handling for linux is needed if we can use the libc fallback
396 // or `flags` is empty. Glibc only added the fallback in 2.32.
397 const skip_fchmodat_fallback = builtin.os.tag != .linux or
398 std.c.versionCheck(.{ .major = 2, .minor = 32, .patch = 0 }) or
399 flags == 0;
400
401 // This function is marked inline so that when flags is comptime-known,
402 // skip_fchmodat_fallback will be comptime-known true.
403 if (skip_fchmodat_fallback)
404 return fchmodat1(dirfd, path, mode, flags);
405
406 return fchmodat2(dirfd, path, mode, flags);
407}
408
409fn fchmodat1(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void {
410 const path_c = try toPosixPath(path);
411 while (true) {
412 const res = system.fchmodat(dirfd, &path_c, mode, flags);
413 switch (system.getErrno(res)) {
414 .SUCCESS => return,
415 .INTR => continue,
416 .BADF => unreachable,
417 .FAULT => unreachable,
418 .INVAL => unreachable,
419 .ACCES => return error.AccessDenied,
420 .IO => return error.InputOutput,
421 .LOOP => return error.SymLinkLoop,
422 .MFILE => return error.ProcessFdQuotaExceeded,
423 .NAMETOOLONG => return error.NameTooLong,
424 .NFILE => return error.SystemFdQuotaExceeded,
425 .NOENT => return error.FileNotFound,
426 .NOTDIR => return error.FileNotFound,
427 .NOMEM => return error.SystemResources,
428 .OPNOTSUPP => return error.OperationNotSupported,
429 .PERM => return error.AccessDenied,
430 .ROFS => return error.ReadOnlyFileSystem,
431 else => |err| return unexpectedErrno(err),
432 }
433 }
434}
435
436fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void {
437 const path_c = try toPosixPath(path);
438 const use_fchmodat2 = (builtin.os.isAtLeast(.linux, .{ .major = 6, .minor = 6, .patch = 0 }) orelse false) and
439 has_fchmodat2_syscall.load(.monotonic);
440 while (use_fchmodat2) {
441 // Later on this should be changed to `system.fchmodat2`
442 // when the musl/glibc add a wrapper.
443 const res = linux.fchmodat2(dirfd, &path_c, mode, flags);
444 switch (linux.getErrno(res)) {
445 .SUCCESS => return,
446 .INTR => continue,
447 .BADF => unreachable,
448 .FAULT => unreachable,
449 .INVAL => unreachable,
450 .ACCES => return error.AccessDenied,
451 .IO => return error.InputOutput,
452 .LOOP => return error.SymLinkLoop,
453 .NOENT => return error.FileNotFound,
454 .NOMEM => return error.SystemResources,
455 .NOTDIR => return error.FileNotFound,
456 .OPNOTSUPP => return error.OperationNotSupported,
457 .PERM => return error.AccessDenied,
458 .ROFS => return error.ReadOnlyFileSystem,
459
460 .NOSYS => { // Use fallback.
461 has_fchmodat2_syscall.store(false, .monotonic);
462 break;
463 },
464 else => |err| return unexpectedErrno(err),
465 }
466 }
467
468 // Fallback to changing permissions using procfs:
469 //
470 // 1. Open `path` as a `PATH` descriptor.
471 // 2. Stat the fd and check if it isn't a symbolic link.
472 // 3. Generate the procfs reference to the fd via `/proc/self/fd/{fd}`.
473 // 4. Pass the procfs path to `chmod` with the `mode`.
474 var pathfd: fd_t = undefined;
475 while (true) {
476 const rc = system.openat(dirfd, &path_c, .{ .PATH = true, .NOFOLLOW = true, .CLOEXEC = true }, @as(mode_t, 0));
477 switch (system.getErrno(rc)) {
478 .SUCCESS => {
479 pathfd = @intCast(rc);
480 break;
481 },
482 .INTR => continue,
483 .FAULT => unreachable,
484 .INVAL => unreachable,
485 .ACCES => return error.AccessDenied,
486 .PERM => return error.AccessDenied,
487 .LOOP => return error.SymLinkLoop,
488 .MFILE => return error.ProcessFdQuotaExceeded,
489 .NAMETOOLONG => return error.NameTooLong,
490 .NFILE => return error.SystemFdQuotaExceeded,
491 .NOENT => return error.FileNotFound,
492 .NOMEM => return error.SystemResources,
493 else => |err| return unexpectedErrno(err),
494 }
495 }
496 defer close(pathfd);
497
498 const stat = fstatatZ(pathfd, "", AT.EMPTY_PATH) catch |err| switch (err) {
499 error.NameTooLong => unreachable,
500 error.FileNotFound => unreachable,
501 error.InvalidUtf8 => unreachable,
502 else => |e| return e,
503 };
504 if ((stat.mode & S.IFMT) == S.IFLNK)
505 return error.OperationNotSupported;
506
507 var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
508 const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/fd/{d}", .{pathfd}) catch unreachable;
509 while (true) {
510 const res = system.chmod(proc_path, mode);
511 switch (system.getErrno(res)) {
512 // Getting NOENT here means that procfs isn't mounted.
513 .NOENT => return error.OperationNotSupported,
514
515 .SUCCESS => return,
516 .INTR => continue,
517 .BADF => unreachable,
518 .FAULT => unreachable,
519 .INVAL => unreachable,
520 .ACCES => return error.AccessDenied,
521 .IO => return error.InputOutput,
522 .LOOP => return error.SymLinkLoop,
523 .NOMEM => return error.SystemResources,
524 .NOTDIR => return error.FileNotFound,
525 .PERM => return error.AccessDenied,
526 .ROFS => return error.ReadOnlyFileSystem,
527 else => |err| return unexpectedErrno(err),
528 }
99/// Calling this function is usually a bug.
100pub fn getFdPath(fd: std.posix.fd_t, out_buffer: *[MAX_PATH_BYTES]u8) std.posix.RealPathError![]u8 {
101 const posix = std.posix;
102 if (!comptime isGetFdPathSupportedOnTarget(builtin.os)) {
103 @compileError("querying for canonical path of a handle is unsupported on this host");
529104 }
530}
531
532pub const FChownError = error{
533 AccessDenied,
534 InputOutput,
535 SymLinkLoop,
536 FileNotFound,
537 SystemResources,
538 ReadOnlyFileSystem,
539} || UnexpectedError;
540
541/// Changes the owner and group of the file referred to by the file descriptor.
542/// The process must have the correct privileges in order to do this
543/// successfully. The group may be changed by the owner of the directory to
544/// any group of which the owner is a member. If the owner or group is
545/// specified as `null`, the ID is not changed.
546pub fn fchown(fd: fd_t, owner: ?uid_t, group: ?gid_t) FChownError!void {
547105 switch (builtin.os.tag) {
548 .windows, .wasi => @compileError("Unsupported OS"),
549 else => {},
550 }
551
552 while (true) {
553 const res = system.fchown(fd, owner orelse ~@as(uid_t, 0), group orelse ~@as(gid_t, 0));
554
555 switch (system.getErrno(res)) {
556 .SUCCESS => return,
557 .INTR => continue,
558 .BADF => unreachable, // Can be reached if the fd refers to a directory opened without `OpenDirOptions{ .iterate = true }`
559
560 .FAULT => unreachable,
561 .INVAL => unreachable,
562 .ACCES => return error.AccessDenied,
563 .IO => return error.InputOutput,
564 .LOOP => return error.SymLinkLoop,
565 .NOENT => return error.FileNotFound,
566 .NOMEM => return error.SystemResources,
567 .NOTDIR => return error.FileNotFound,
568 .PERM => return error.AccessDenied,
569 .ROFS => return error.ReadOnlyFileSystem,
570 else => |err| return unexpectedErrno(err),
571 }
572 }
573}
574
575pub const RebootError = error{
576 PermissionDenied,
577} || UnexpectedError;
578
579pub const RebootCommand = switch (builtin.os.tag) {
580 .linux => union(linux.LINUX_REBOOT.CMD) {
581 RESTART: void,
582 HALT: void,
583 CAD_ON: void,
584 CAD_OFF: void,
585 POWER_OFF: void,
586 RESTART2: [*:0]const u8,
587 SW_SUSPEND: void,
588 KEXEC: void,
589 },
590 else => @compileError("Unsupported OS"),
591};
106 .windows => {
107 var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
108 const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]);
592109
593pub fn reboot(cmd: RebootCommand) RebootError!void {
594 switch (builtin.os.tag) {
595 .linux => {
596 switch (system.getErrno(linux.reboot(
597 .MAGIC1,
598 .MAGIC2,
599 cmd,
600 switch (cmd) {
601 .RESTART2 => |s| s,
602 else => null,
603 },
604 ))) {
110 const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
111 return out_buffer[0..end_index];
112 },
113 .macos, .ios, .watchos, .tvos => {
114 // On macOS, we can use F.GETPATH fcntl command to query the OS for
115 // the path to the file descriptor.
116 @memset(out_buffer[0..MAX_PATH_BYTES], 0);
117 switch (posix.errno(posix.system.fcntl(fd, posix.F.GETPATH, out_buffer))) {
605118 .SUCCESS => {},
606 .PERM => return error.PermissionDenied,
607 else => |err| return std.os.unexpectedErrno(err),
608 }
609 switch (cmd) {
610 .CAD_OFF => {},
611 .CAD_ON => {},
612 .SW_SUSPEND => {},
613
614 .HALT => unreachable,
615 .KEXEC => unreachable,
616 .POWER_OFF => unreachable,
617 .RESTART => unreachable,
618 .RESTART2 => unreachable,
119 .BADF => return error.FileNotFound,
120 .NOSPC => return error.NameTooLong,
121 // TODO man pages for fcntl on macOS don't really tell you what
122 // errno values to expect when command is F.GETPATH...
123 else => |err| return posix.unexpectedErrno(err),
619124 }
125 const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse MAX_PATH_BYTES;
126 return out_buffer[0..len];
620127 },
621 else => @compileError("Unsupported OS"),
622 }
623}
624
625pub const GetRandomError = OpenError;
128 .linux => {
129 var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
130 const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/fd/{d}", .{fd}) catch unreachable;
626131
627/// Obtain a series of random bytes. These bytes can be used to seed user-space
628/// random number generators or for cryptographic purposes.
629/// When linking against libc, this calls the
630/// appropriate OS-specific library call. Otherwise it uses the zig standard
631/// library implementation.
632pub fn getrandom(buffer: []u8) GetRandomError!void {
633 if (builtin.os.tag == .windows) {
634 return windows.RtlGenRandom(buffer);
635 }
636 if (builtin.os.tag == .linux or builtin.os.tag == .freebsd) {
637 var buf = buffer;
638 const use_c = builtin.os.tag != .linux or
639 std.c.versionCheck(std.SemanticVersion{ .major = 2, .minor = 25, .patch = 0 });
132 const target = posix.readlinkZ(proc_path, out_buffer) catch |err| {
133 switch (err) {
134 error.NotLink => unreachable,
135 error.BadPathName => unreachable,
136 error.InvalidUtf8 => unreachable, // WASI-only
137 error.InvalidWtf8 => unreachable, // Windows-only
138 error.UnsupportedReparsePointType => unreachable, // Windows-only
139 error.NetworkNotFound => unreachable, // Windows-only
140 else => |e| return e,
141 }
142 };
143 return target;
144 },
145 .solaris, .illumos => {
146 var procfs_buf: ["/proc/self/path/-2147483648\x00".len]u8 = undefined;
147 const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/path/{d}", .{fd}) catch unreachable;
640148
641 while (buf.len != 0) {
642 const num_read: usize, const err = if (use_c) res: {
643 const rc = std.c.getrandom(buf.ptr, buf.len, 0);
644 break :res .{
645 @bitCast(rc),
646 std.c.getErrno(rc),
149 const target = posix.readlinkZ(proc_path, out_buffer) catch |err| switch (err) {
150 error.UnsupportedReparsePointType => unreachable,
151 error.NotLink => unreachable,
152 else => |e| return e,
153 };
154 return target;
155 },
156 .freebsd => {
157 if (comptime builtin.os.isAtLeast(.freebsd, .{ .major = 13, .minor = 0, .patch = 0 }) orelse false) {
158 var kfile: std.c.kinfo_file = undefined;
159 kfile.structsize = std.c.KINFO_FILE_SIZE;
160 switch (posix.errno(std.c.fcntl(fd, std.c.F.KINFO, @intFromPtr(&kfile)))) {
161 .SUCCESS => {},
162 .BADF => return error.FileNotFound,
163 else => |err| return posix.unexpectedErrno(err),
164 }
165 const len = mem.indexOfScalar(u8, &kfile.path, 0) orelse MAX_PATH_BYTES;
166 if (len == 0) return error.NameTooLong;
167 const result = out_buffer[0..len];
168 @memcpy(result, kfile.path[0..len]);
169 return result;
170 } else {
171 // This fallback implementation reimplements libutil's `kinfo_getfile()`.
172 // The motivation is to avoid linking -lutil when building zig or general
173 // user executables.
174 var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC, posix.KERN.PROC_FILEDESC, std.c.getpid() };
175 var len: usize = undefined;
176 posix.sysctl(&mib, null, &len, null, 0) catch |err| switch (err) {
177 error.PermissionDenied => unreachable,
178 error.SystemResources => return error.SystemResources,
179 error.NameTooLong => unreachable,
180 error.UnknownName => unreachable,
181 else => return error.Unexpected,
647182 };
648 } else res: {
649 const rc = linux.getrandom(buf.ptr, buf.len, 0);
650 break :res .{
651 rc,
652 linux.getErrno(rc),
183 len = len * 4 / 3;
184 const buf = std.heap.c_allocator.alloc(u8, len) catch return error.SystemResources;
185 defer std.heap.c_allocator.free(buf);
186 len = buf.len;
187 posix.sysctl(&mib, &buf[0], &len, null, 0) catch |err| switch (err) {
188 error.PermissionDenied => unreachable,
189 error.SystemResources => return error.SystemResources,
190 error.NameTooLong => unreachable,
191 error.UnknownName => unreachable,
192 else => return error.Unexpected,
653193 };
654 };
655
656 switch (err) {
657 .SUCCESS => buf = buf[num_read..],
658 .INVAL => unreachable,
659 .FAULT => unreachable,
660 .INTR => continue,
661 .NOSYS => return getRandomBytesDevURandom(buf),
662 else => return unexpectedErrno(err),
194 var i: usize = 0;
195 while (i < len) {
196 const kf: *align(1) std.c.kinfo_file = @ptrCast(&buf[i]);
197 if (kf.fd == fd) {
198 len = mem.indexOfScalar(u8, &kf.path, 0) orelse MAX_PATH_BYTES;
199 if (len == 0) return error.NameTooLong;
200 const result = out_buffer[0..len];
201 @memcpy(result, kf.path[0..len]);
202 return result;
203 }
204 i += @intCast(kf.structsize);
205 }
206 return error.FileNotFound;
663207 }
664 }
665 return;
666 }
667 if (builtin.os.tag == .emscripten) {
668 const err = std.c.getErrno(std.c.getentropy(buffer.ptr, buffer.len));
669 switch (err) {
670 .SUCCESS => return,
671 else => return unexpectedErrno(err),
672 }
673 }
674 switch (builtin.os.tag) {
675 .netbsd, .openbsd, .macos, .ios, .tvos, .watchos => {
676 system.arc4random_buf(buffer.ptr, buffer.len);
677 return;
678208 },
679 .wasi => switch (wasi.random_get(buffer.ptr, buffer.len)) {
680 .SUCCESS => return,
681 else => |err| return unexpectedErrno(err),
209 .dragonfly => {
210 @memset(out_buffer[0..MAX_PATH_BYTES], 0);
211 switch (posix.errno(std.c.fcntl(fd, posix.F.GETPATH, out_buffer))) {
212 .SUCCESS => {},
213 .BADF => return error.FileNotFound,
214 .RANGE => return error.NameTooLong,
215 else => |err| return posix.unexpectedErrno(err),
216 }
217 const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse MAX_PATH_BYTES;
218 return out_buffer[0..len];
219 },
220 .netbsd => {
221 @memset(out_buffer[0..MAX_PATH_BYTES], 0);
222 switch (posix.errno(std.c.fcntl(fd, posix.F.GETPATH, out_buffer))) {
223 .SUCCESS => {},
224 .ACCES => return error.AccessDenied,
225 .BADF => return error.FileNotFound,
226 .NOENT => return error.FileNotFound,
227 .NOMEM => return error.SystemResources,
228 .RANGE => return error.NameTooLong,
229 else => |err| return posix.unexpectedErrno(err),
230 }
231 const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse MAX_PATH_BYTES;
232 return out_buffer[0..len];
682233 },
683 else => return getRandomBytesDevURandom(buffer),
684 }
685}
686
687fn getRandomBytesDevURandom(buf: []u8) !void {
688 const fd = try openZ("/dev/urandom", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
689 defer close(fd);
690
691 const st = try fstat(fd);
692 if (!S.ISCHR(st.mode)) {
693 return error.NoDevice;
694 }
695
696 const file = std.fs.File{ .handle = fd };
697 const stream = file.reader();
698 stream.readNoEof(buf) catch return error.Unexpected;
699}
700
701/// Causes abnormal process termination.
702/// If linking against libc, this calls the abort() libc function. Otherwise
703/// it raises SIGABRT followed by SIGKILL and finally lo
704/// Invokes the current signal handler for SIGABRT, if any.
705pub fn abort() noreturn {
706 @setCold(true);
707 // MSVCRT abort() sometimes opens a popup window which is undesirable, so
708 // even when linking libc on Windows we use our own abort implementation.
709 // See https://github.com/ziglang/zig/issues/2071 for more details.
710 if (builtin.os.tag == .windows) {
711 if (builtin.mode == .Debug) {
712 @breakpoint();
713 }
714 windows.kernel32.ExitProcess(3);
715 }
716 if (!builtin.link_libc and builtin.os.tag == .linux) {
717 // The Linux man page says that the libc abort() function
718 // "first unblocks the SIGABRT signal", but this is a footgun
719 // for user-defined signal handlers that want to restore some state in
720 // some program sections and crash in others.
721 // So, the user-installed SIGABRT handler is run, if present.
722 raise(SIG.ABRT) catch {};
723
724 // Disable all signal handlers.
725 sigprocmask(SIG.BLOCK, &linux.all_mask, null);
726
727 // Only one thread may proceed to the rest of abort().
728 if (!builtin.single_threaded) {
729 const global = struct {
730 var abort_entered: bool = false;
731 };
732 while (@cmpxchgWeak(bool, &global.abort_entered, false, true, .seq_cst, .seq_cst)) |_| {}
733 }
734
735 // Install default handler so that the tkill below will terminate.
736 const sigact = Sigaction{
737 .handler = .{ .handler = SIG.DFL },
738 .mask = empty_sigset,
739 .flags = 0,
740 };
741 sigaction(SIG.ABRT, &sigact, null) catch |err| switch (err) {
742 error.OperationNotSupported => unreachable,
743 };
744
745 _ = linux.tkill(linux.gettid(), SIG.ABRT);
746
747 const sigabrtmask: linux.sigset_t = [_]u32{0} ** 31 ++ [_]u32{1 << (SIG.ABRT - 1)};
748 sigprocmask(SIG.UNBLOCK, &sigabrtmask, null);
749
750 // Beyond this point should be unreachable.
751 @as(*allowzero volatile u8, @ptrFromInt(0)).* = 0;
752 raise(SIG.KILL) catch {};
753 exit(127); // Pid 1 might not be signalled in some containers.
754 }
755 switch (builtin.os.tag) {
756 .uefi, .wasi, .emscripten, .cuda, .amdhsa => @trap(),
757 else => system.abort(),
758 }
759}
760
761pub const RaiseError = UnexpectedError;
762
763pub fn raise(sig: u8) RaiseError!void {
764 if (builtin.link_libc) {
765 switch (errno(system.raise(sig))) {
766 .SUCCESS => return,
767 else => |err| return unexpectedErrno(err),
768 }
769 }
770
771 if (builtin.os.tag == .linux) {
772 var set: sigset_t = undefined;
773 // block application signals
774 sigprocmask(SIG.BLOCK, &linux.app_mask, &set);
775
776 const tid = linux.gettid();
777 const rc = linux.tkill(tid, sig);
778
779 // restore signal mask
780 sigprocmask(SIG.SETMASK, &set, null);
781
782 switch (errno(rc)) {
783 .SUCCESS => return,
784 else => |err| return unexpectedErrno(err),
785 }
786 }
787
788 @compileError("std.os.raise unimplemented for this target");
789}
790
791pub const KillError = error{ ProcessNotFound, PermissionDenied } || UnexpectedError;
792
793pub fn kill(pid: pid_t, sig: u8) KillError!void {
794 switch (errno(system.kill(pid, sig))) {
795 .SUCCESS => return,
796 .INVAL => unreachable, // invalid signal
797 .PERM => return error.PermissionDenied,
798 .SRCH => return error.ProcessNotFound,
799 else => |err| return unexpectedErrno(err),
800 }
801}
802
803/// Exits the program cleanly with the specified status code.
804pub fn exit(status: u8) noreturn {
805 if (builtin.link_libc) {
806 system.exit(status);
807 }
808 if (builtin.os.tag == .windows) {
809 windows.kernel32.ExitProcess(status);
810 }
811 if (builtin.os.tag == .wasi) {
812 wasi.proc_exit(status);
813 }
814 if (builtin.os.tag == .linux and !builtin.single_threaded) {
815 linux.exit_group(status);
816 }
817 if (builtin.os.tag == .uefi) {
818 // exit() is only available if exitBootServices() has not been called yet.
819 // This call to exit should not fail, so we don't care about its return value.
820 if (uefi.system_table.boot_services) |bs| {
821 _ = bs.exit(uefi.handle, @enumFromInt(status), 0, null);
822 }
823 // If we can't exit, reboot the system instead.
824 uefi.system_table.runtime_services.resetSystem(.ResetCold, @enumFromInt(status), 0, null);
234 else => unreachable, // made unreachable by isGetFdPathSupportedOnTarget above
825235 }
826 system.exit(status);
827236}
828
829pub const ReadError = error{
830 InputOutput,
831 SystemResources,
832 IsDir,
833 OperationAborted,
834 BrokenPipe,
835 ConnectionResetByPeer,
836 ConnectionTimedOut,
837 NotOpenForReading,
838 SocketNotConnected,
839
840 /// This error occurs when no global event loop is configured,
841 /// and reading from the file descriptor would block.
842 WouldBlock,
843
844 /// In WASI, this error occurs when the file descriptor does
845 /// not hold the required rights to read from it.
846 AccessDenied,
847} || UnexpectedError;
848
849/// Returns the number of bytes that were read, which can be less than
850/// buf.len. If 0 bytes were read, that means EOF.
851/// If `fd` is opened in non blocking mode, the function will return error.WouldBlock
852/// when EAGAIN is received.
853///
854/// Linux has a limit on how many bytes may be transferred in one `read` call, which is `0x7ffff000`
855/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
856/// well as stuffing the errno codes into the last `4096` values. This is noted on the `read` man page.
857/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
858/// The corresponding POSIX limit is `math.maxInt(isize)`.
859pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
860 if (buf.len == 0) return 0;
861 if (builtin.os.tag == .windows) {
862 return windows.ReadFile(fd, buf, null);
863 }
864 if (builtin.os.tag == .wasi and !builtin.link_libc) {
865 const iovs = [1]iovec{iovec{
866 .iov_base = buf.ptr,
867 .iov_len = buf.len,
868 }};
869
870 var nread: usize = undefined;
871 switch (wasi.fd_read(fd, &iovs, iovs.len, &nread)) {
872 .SUCCESS => return nread,
873 .INTR => unreachable,
874 .INVAL => unreachable,
875 .FAULT => unreachable,
876 .AGAIN => unreachable,
877 .BADF => return error.NotOpenForReading, // Can be a race condition.
878 .IO => return error.InputOutput,
879 .ISDIR => return error.IsDir,
880 .NOBUFS => return error.SystemResources,
881 .NOMEM => return error.SystemResources,
882 .NOTCONN => return error.SocketNotConnected,
883 .CONNRESET => return error.ConnectionResetByPeer,
884 .TIMEDOUT => return error.ConnectionTimedOut,
885 .NOTCAPABLE => return error.AccessDenied,
886 else => |err| return unexpectedErrno(err),
887 }
888 }
889
890 // Prevents EINVAL.
891 const max_count = switch (builtin.os.tag) {
892 .linux => 0x7ffff000,
893 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
894 else => math.maxInt(isize),
895 };
896 while (true) {
897 const rc = system.read(fd, buf.ptr, @min(buf.len, max_count));
898 switch (errno(rc)) {
899 .SUCCESS => return @intCast(rc),
900 .INTR => continue,
901 .INVAL => unreachable,
902 .FAULT => unreachable,
903 .AGAIN => return error.WouldBlock,
904 .BADF => return error.NotOpenForReading, // Can be a race condition.
905 .IO => return error.InputOutput,
906 .ISDIR => return error.IsDir,
907 .NOBUFS => return error.SystemResources,
908 .NOMEM => return error.SystemResources,
909 .NOTCONN => return error.SocketNotConnected,
910 .CONNRESET => return error.ConnectionResetByPeer,
911 .TIMEDOUT => return error.ConnectionTimedOut,
912 else => |err| return unexpectedErrno(err),
913 }
914 }
915}
916
917/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
918///
919/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
920/// return error.WouldBlock when EAGAIN is received.
921/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
922/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
923///
924/// This operation is non-atomic on the following systems:
925/// * Windows
926/// On these systems, the read races with concurrent writes to the same file descriptor.
927///
928/// This function assumes that all vectors, including zero-length vectors, have
929/// a pointer within the address space of the application.
930pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
931 if (builtin.os.tag == .windows) {
932 // TODO improve this to use ReadFileScatter
933 if (iov.len == 0) return 0;
934 const first = iov[0];
935 return read(fd, first.iov_base[0..first.iov_len]);
936 }
937 if (builtin.os.tag == .wasi and !builtin.link_libc) {
938 var nread: usize = undefined;
939 switch (wasi.fd_read(fd, iov.ptr, iov.len, &nread)) {
940 .SUCCESS => return nread,
941 .INTR => unreachable,
942 .INVAL => unreachable,
943 .FAULT => unreachable,
944 .AGAIN => unreachable, // currently not support in WASI
945 .BADF => return error.NotOpenForReading, // can be a race condition
946 .IO => return error.InputOutput,
947 .ISDIR => return error.IsDir,
948 .NOBUFS => return error.SystemResources,
949 .NOMEM => return error.SystemResources,
950 .NOTCONN => return error.SocketNotConnected,
951 .CONNRESET => return error.ConnectionResetByPeer,
952 .TIMEDOUT => return error.ConnectionTimedOut,
953 .NOTCAPABLE => return error.AccessDenied,
954 else => |err| return unexpectedErrno(err),
955 }
956 }
957
958 while (true) {
959 const rc = system.readv(fd, iov.ptr, @min(iov.len, IOV_MAX));
960 switch (errno(rc)) {
961 .SUCCESS => return @intCast(rc),
962 .INTR => continue,
963 .INVAL => unreachable,
964 .FAULT => unreachable,
965 .AGAIN => return error.WouldBlock,
966 .BADF => return error.NotOpenForReading, // can be a race condition
967 .IO => return error.InputOutput,
968 .ISDIR => return error.IsDir,
969 .NOBUFS => return error.SystemResources,
970 .NOMEM => return error.SystemResources,
971 .NOTCONN => return error.SocketNotConnected,
972 .CONNRESET => return error.ConnectionResetByPeer,
973 .TIMEDOUT => return error.ConnectionTimedOut,
974 else => |err| return unexpectedErrno(err),
975 }
976 }
977}
978
979pub const PReadError = ReadError || error{Unseekable};
980
981/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
982///
983/// Retries when interrupted by a signal.
984///
985/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
986/// return error.WouldBlock when EAGAIN is received.
987/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
988/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
989///
990/// Linux has a limit on how many bytes may be transferred in one `pread` call, which is `0x7ffff000`
991/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
992/// well as stuffing the errno codes into the last `4096` values. This is noted on the `read` man page.
993/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
994/// The corresponding POSIX limit is `math.maxInt(isize)`.
995pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
996 if (buf.len == 0) return 0;
997 if (builtin.os.tag == .windows) {
998 return windows.ReadFile(fd, buf, offset);
999 }
1000 if (builtin.os.tag == .wasi and !builtin.link_libc) {
1001 const iovs = [1]iovec{iovec{
1002 .iov_base = buf.ptr,
1003 .iov_len = buf.len,
1004 }};
1005
1006 var nread: usize = undefined;
1007 switch (wasi.fd_pread(fd, &iovs, iovs.len, offset, &nread)) {
1008 .SUCCESS => return nread,
1009 .INTR => unreachable,
1010 .INVAL => unreachable,
1011 .FAULT => unreachable,
1012 .AGAIN => unreachable,
1013 .BADF => return error.NotOpenForReading, // Can be a race condition.
1014 .IO => return error.InputOutput,
1015 .ISDIR => return error.IsDir,
1016 .NOBUFS => return error.SystemResources,
1017 .NOMEM => return error.SystemResources,
1018 .NOTCONN => return error.SocketNotConnected,
1019 .CONNRESET => return error.ConnectionResetByPeer,
1020 .TIMEDOUT => return error.ConnectionTimedOut,
1021 .NXIO => return error.Unseekable,
1022 .SPIPE => return error.Unseekable,
1023 .OVERFLOW => return error.Unseekable,
1024 .NOTCAPABLE => return error.AccessDenied,
1025 else => |err| return unexpectedErrno(err),
1026 }
1027 }
1028
1029 // Prevent EINVAL.
1030 const max_count = switch (builtin.os.tag) {
1031 .linux => 0x7ffff000,
1032 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
1033 else => math.maxInt(isize),
1034 };
1035
1036 const pread_sym = if (lfs64_abi) system.pread64 else system.pread;
1037 while (true) {
1038 const rc = pread_sym(fd, buf.ptr, @min(buf.len, max_count), @bitCast(offset));
1039 switch (errno(rc)) {
1040 .SUCCESS => return @intCast(rc),
1041 .INTR => continue,
1042 .INVAL => unreachable,
1043 .FAULT => unreachable,
1044 .AGAIN => return error.WouldBlock,
1045 .BADF => return error.NotOpenForReading, // Can be a race condition.
1046 .IO => return error.InputOutput,
1047 .ISDIR => return error.IsDir,
1048 .NOBUFS => return error.SystemResources,
1049 .NOMEM => return error.SystemResources,
1050 .NOTCONN => return error.SocketNotConnected,
1051 .CONNRESET => return error.ConnectionResetByPeer,
1052 .TIMEDOUT => return error.ConnectionTimedOut,
1053 .NXIO => return error.Unseekable,
1054 .SPIPE => return error.Unseekable,
1055 .OVERFLOW => return error.Unseekable,
1056 else => |err| return unexpectedErrno(err),
1057 }
1058 }
1059}
1060
1061pub const TruncateError = error{
1062 FileTooBig,
1063 InputOutput,
1064 FileBusy,
1065
1066 /// In WASI, this error occurs when the file descriptor does
1067 /// not hold the required rights to call `ftruncate` on it.
1068 AccessDenied,
1069} || UnexpectedError;
1070
1071pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
1072 if (builtin.os.tag == .windows) {
1073 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
1074 var eof_info = windows.FILE_END_OF_FILE_INFORMATION{
1075 .EndOfFile = @bitCast(length),
1076 };
1077
1078 const rc = windows.ntdll.NtSetInformationFile(
1079 fd,
1080 &io_status_block,
1081 &eof_info,
1082 @sizeOf(windows.FILE_END_OF_FILE_INFORMATION),
1083 .FileEndOfFileInformation,
1084 );
1085
1086 switch (rc) {
1087 .SUCCESS => return,
1088 .INVALID_HANDLE => unreachable, // Handle not open for writing
1089 .ACCESS_DENIED => return error.AccessDenied,
1090 else => return windows.unexpectedStatus(rc),
1091 }
1092 }
1093 if (builtin.os.tag == .wasi and !builtin.link_libc) {
1094 switch (wasi.fd_filestat_set_size(fd, length)) {
1095 .SUCCESS => return,
1096 .INTR => unreachable,
1097 .FBIG => return error.FileTooBig,
1098 .IO => return error.InputOutput,
1099 .PERM => return error.AccessDenied,
1100 .TXTBSY => return error.FileBusy,
1101 .BADF => unreachable, // Handle not open for writing
1102 .INVAL => unreachable, // Handle not open for writing
1103 .NOTCAPABLE => return error.AccessDenied,
1104 else => |err| return unexpectedErrno(err),
1105 }
1106 }
1107
1108 const ftruncate_sym = if (lfs64_abi) system.ftruncate64 else system.ftruncate;
1109 while (true) {
1110 switch (errno(ftruncate_sym(fd, @bitCast(length)))) {
1111 .SUCCESS => return,
1112 .INTR => continue,
1113 .FBIG => return error.FileTooBig,
1114 .IO => return error.InputOutput,
1115 .PERM => return error.AccessDenied,
1116 .TXTBSY => return error.FileBusy,
1117 .BADF => unreachable, // Handle not open for writing
1118 .INVAL => unreachable, // Handle not open for writing
1119 else => |err| return unexpectedErrno(err),
1120 }
1121 }
1122}
1123
1124/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
1125///
1126/// Retries when interrupted by a signal.
1127///
1128/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
1129/// return error.WouldBlock when EAGAIN is received.
1130/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
1131/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
1132///
1133/// This operation is non-atomic on the following systems:
1134/// * Darwin
1135/// * Windows
1136/// On these systems, the read races with concurrent writes to the same file descriptor.
1137pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {
1138 const have_pread_but_not_preadv = switch (builtin.os.tag) {
1139 .windows, .macos, .ios, .watchos, .tvos, .haiku => true,
1140 else => false,
1141 };
1142 if (have_pread_but_not_preadv) {
1143 // We could loop here; but proper usage of `preadv` must handle partial reads anyway.
1144 // So we simply read into the first vector only.
1145 if (iov.len == 0) return 0;
1146 const first = iov[0];
1147 return pread(fd, first.iov_base[0..first.iov_len], offset);
1148 }
1149 if (builtin.os.tag == .wasi and !builtin.link_libc) {
1150 var nread: usize = undefined;
1151 switch (wasi.fd_pread(fd, iov.ptr, iov.len, offset, &nread)) {
1152 .SUCCESS => return nread,
1153 .INTR => unreachable,
1154 .INVAL => unreachable,
1155 .FAULT => unreachable,
1156 .AGAIN => unreachable,
1157 .BADF => return error.NotOpenForReading, // can be a race condition
1158 .IO => return error.InputOutput,
1159 .ISDIR => return error.IsDir,
1160 .NOBUFS => return error.SystemResources,
1161 .NOMEM => return error.SystemResources,
1162 .NOTCONN => return error.SocketNotConnected,
1163 .CONNRESET => return error.ConnectionResetByPeer,
1164 .TIMEDOUT => return error.ConnectionTimedOut,
1165 .NXIO => return error.Unseekable,
1166 .SPIPE => return error.Unseekable,
1167 .OVERFLOW => return error.Unseekable,
1168 .NOTCAPABLE => return error.AccessDenied,
1169 else => |err| return unexpectedErrno(err),
1170 }
1171 }
1172
1173 const preadv_sym = if (lfs64_abi) system.preadv64 else system.preadv;
1174 while (true) {
1175 const rc = preadv_sym(fd, iov.ptr, @min(iov.len, IOV_MAX), @bitCast(offset));
1176 switch (errno(rc)) {
1177 .SUCCESS => return @bitCast(rc),
1178 .INTR => continue,
1179 .INVAL => unreachable,
1180 .FAULT => unreachable,
1181 .AGAIN => return error.WouldBlock,
1182 .BADF => return error.NotOpenForReading, // can be a race condition
1183 .IO => return error.InputOutput,
1184 .ISDIR => return error.IsDir,
1185 .NOBUFS => return error.SystemResources,
1186 .NOMEM => return error.SystemResources,
1187 .NOTCONN => return error.SocketNotConnected,
1188 .CONNRESET => return error.ConnectionResetByPeer,
1189 .TIMEDOUT => return error.ConnectionTimedOut,
1190 .NXIO => return error.Unseekable,
1191 .SPIPE => return error.Unseekable,
1192 .OVERFLOW => return error.Unseekable,
1193 else => |err| return unexpectedErrno(err),
1194 }
1195 }
1196}
1197
1198pub const WriteError = error{
1199 DiskQuota,
1200 FileTooBig,
1201 InputOutput,
1202 NoSpaceLeft,
1203 DeviceBusy,
1204 InvalidArgument,
1205
1206 /// In WASI, this error may occur when the file descriptor does
1207 /// not hold the required rights to write to it.
1208 AccessDenied,
1209 BrokenPipe,
1210 SystemResources,
1211 OperationAborted,
1212 NotOpenForWriting,
1213
1214 /// The process cannot access the file because another process has locked
1215 /// a portion of the file. Windows-only.
1216 LockViolation,
1217
1218 /// This error occurs when no global event loop is configured,
1219 /// and reading from the file descriptor would block.
1220 WouldBlock,
1221
1222 /// Connection reset by peer.
1223 ConnectionResetByPeer,
1224} || UnexpectedError;
1225
1226/// Write to a file descriptor.
1227/// Retries when interrupted by a signal.
1228/// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
1229///
1230/// Note that a successful write() may transfer fewer than count bytes. Such partial writes can
1231/// occur for various reasons; for example, because there was insufficient space on the disk
1232/// device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or
1233/// similar was interrupted by a signal handler after it had transferred some, but before it had
1234/// transferred all of the requested bytes. In the event of a partial write, the caller can make
1235/// another write() call to transfer the remaining bytes. The subsequent call will either
1236/// transfer further bytes or may result in an error (e.g., if the disk is now full).
1237///
1238/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
1239/// return error.WouldBlock when EAGAIN is received.
1240/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
1241/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
1242///
1243/// Linux has a limit on how many bytes may be transferred in one `write` call, which is `0x7ffff000`
1244/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
1245/// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page.
1246/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
1247/// The corresponding POSIX limit is `math.maxInt(isize)`.
1248pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
1249 if (bytes.len == 0) return 0;
1250 if (builtin.os.tag == .windows) {
1251 return windows.WriteFile(fd, bytes, null);
1252 }
1253
1254 if (builtin.os.tag == .wasi and !builtin.link_libc) {
1255 const ciovs = [_]iovec_const{iovec_const{
1256 .iov_base = bytes.ptr,
1257 .iov_len = bytes.len,
1258 }};
1259 var nwritten: usize = undefined;
1260 switch (wasi.fd_write(fd, &ciovs, ciovs.len, &nwritten)) {
1261 .SUCCESS => return nwritten,
1262 .INTR => unreachable,
1263 .INVAL => unreachable,
1264 .FAULT => unreachable,
1265 .AGAIN => unreachable,
1266 .BADF => return error.NotOpenForWriting, // can be a race condition.
1267 .DESTADDRREQ => unreachable, // `connect` was never called.
1268 .DQUOT => return error.DiskQuota,
1269 .FBIG => return error.FileTooBig,
1270 .IO => return error.InputOutput,
1271 .NOSPC => return error.NoSpaceLeft,
1272 .PERM => return error.AccessDenied,
1273 .PIPE => return error.BrokenPipe,
1274 .NOTCAPABLE => return error.AccessDenied,
1275 else => |err| return unexpectedErrno(err),
1276 }
1277 }
1278
1279 const max_count = switch (builtin.os.tag) {
1280 .linux => 0x7ffff000,
1281 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
1282 else => math.maxInt(isize),
1283 };
1284 while (true) {
1285 const rc = system.write(fd, bytes.ptr, @min(bytes.len, max_count));
1286 switch (errno(rc)) {
1287 .SUCCESS => return @intCast(rc),
1288 .INTR => continue,
1289 .INVAL => return error.InvalidArgument,
1290 .FAULT => unreachable,
1291 .AGAIN => return error.WouldBlock,
1292 .BADF => return error.NotOpenForWriting, // can be a race condition.
1293 .DESTADDRREQ => unreachable, // `connect` was never called.
1294 .DQUOT => return error.DiskQuota,
1295 .FBIG => return error.FileTooBig,
1296 .IO => return error.InputOutput,
1297 .NOSPC => return error.NoSpaceLeft,
1298 .PERM => return error.AccessDenied,
1299 .PIPE => return error.BrokenPipe,
1300 .CONNRESET => return error.ConnectionResetByPeer,
1301 .BUSY => return error.DeviceBusy,
1302 else => |err| return unexpectedErrno(err),
1303 }
1304 }
1305}
1306
1307/// Write multiple buffers to a file descriptor.
1308/// Retries when interrupted by a signal.
1309/// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
1310///
1311/// Note that a successful write() may transfer fewer bytes than supplied. Such partial writes can
1312/// occur for various reasons; for example, because there was insufficient space on the disk
1313/// device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or
1314/// similar was interrupted by a signal handler after it had transferred some, but before it had
1315/// transferred all of the requested bytes. In the event of a partial write, the caller can make
1316/// another write() call to transfer the remaining bytes. The subsequent call will either
1317/// transfer further bytes or may result in an error (e.g., if the disk is now full).
1318///
1319/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
1320/// return error.WouldBlock when EAGAIN is received.
1321/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
1322/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
1323///
1324/// If `iov.len` is larger than `IOV_MAX`, a partial write will occur.
1325///
1326/// This function assumes that all vectors, including zero-length vectors, have
1327/// a pointer within the address space of the application.
1328pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
1329 if (builtin.os.tag == .windows) {
1330 // TODO improve this to use WriteFileScatter
1331 if (iov.len == 0) return 0;
1332 const first = iov[0];
1333 return write(fd, first.iov_base[0..first.iov_len]);
1334 }
1335 if (builtin.os.tag == .wasi and !builtin.link_libc) {
1336 var nwritten: usize = undefined;
1337 switch (wasi.fd_write(fd, iov.ptr, iov.len, &nwritten)) {
1338 .SUCCESS => return nwritten,
1339 .INTR => unreachable,
1340 .INVAL => unreachable,
1341 .FAULT => unreachable,
1342 .AGAIN => unreachable,
1343 .BADF => return error.NotOpenForWriting, // can be a race condition.
1344 .DESTADDRREQ => unreachable, // `connect` was never called.
1345 .DQUOT => return error.DiskQuota,
1346 .FBIG => return error.FileTooBig,
1347 .IO => return error.InputOutput,
1348 .NOSPC => return error.NoSpaceLeft,
1349 .PERM => return error.AccessDenied,
1350 .PIPE => return error.BrokenPipe,
1351 .NOTCAPABLE => return error.AccessDenied,
1352 else => |err| return unexpectedErrno(err),
1353 }
1354 }
1355
1356 while (true) {
1357 const rc = system.writev(fd, iov.ptr, @min(iov.len, IOV_MAX));
1358 switch (errno(rc)) {
1359 .SUCCESS => return @intCast(rc),
1360 .INTR => continue,
1361 .INVAL => return error.InvalidArgument,
1362 .FAULT => unreachable,
1363 .AGAIN => return error.WouldBlock,
1364 .BADF => return error.NotOpenForWriting, // Can be a race condition.
1365 .DESTADDRREQ => unreachable, // `connect` was never called.
1366 .DQUOT => return error.DiskQuota,
1367 .FBIG => return error.FileTooBig,
1368 .IO => return error.InputOutput,
1369 .NOSPC => return error.NoSpaceLeft,
1370 .PERM => return error.AccessDenied,
1371 .PIPE => return error.BrokenPipe,
1372 .CONNRESET => return error.ConnectionResetByPeer,
1373 .BUSY => return error.DeviceBusy,
1374 else => |err| return unexpectedErrno(err),
1375 }
1376 }
1377}
1378
1379pub const PWriteError = WriteError || error{Unseekable};
1380
1381/// Write to a file descriptor, with a position offset.
1382/// Retries when interrupted by a signal.
1383/// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
1384///
1385/// Note that a successful write() may transfer fewer bytes than supplied. Such partial writes can
1386/// occur for various reasons; for example, because there was insufficient space on the disk
1387/// device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or
1388/// similar was interrupted by a signal handler after it had transferred some, but before it had
1389/// transferred all of the requested bytes. In the event of a partial write, the caller can make
1390/// another write() call to transfer the remaining bytes. The subsequent call will either
1391/// transfer further bytes or may result in an error (e.g., if the disk is now full).
1392///
1393/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
1394/// return error.WouldBlock when EAGAIN is received.
1395/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
1396/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
1397///
1398/// Linux has a limit on how many bytes may be transferred in one `pwrite` call, which is `0x7ffff000`
1399/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
1400/// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page.
1401/// The limit on Darwin is `0x7fffffff`, trying to write more than that returns EINVAL.
1402/// The corresponding POSIX limit is `math.maxInt(isize)`.
1403pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
1404 if (bytes.len == 0) return 0;
1405 if (builtin.os.tag == .windows) {
1406 return windows.WriteFile(fd, bytes, offset);
1407 }
1408 if (builtin.os.tag == .wasi and !builtin.link_libc) {
1409 const ciovs = [1]iovec_const{iovec_const{
1410 .iov_base = bytes.ptr,
1411 .iov_len = bytes.len,
1412 }};
1413
1414 var nwritten: usize = undefined;
1415 switch (wasi.fd_pwrite(fd, &ciovs, ciovs.len, offset, &nwritten)) {
1416 .SUCCESS => return nwritten,
1417 .INTR => unreachable,
1418 .INVAL => unreachable,
1419 .FAULT => unreachable,
1420 .AGAIN => unreachable,
1421 .BADF => return error.NotOpenForWriting, // can be a race condition.
1422 .DESTADDRREQ => unreachable, // `connect` was never called.
1423 .DQUOT => return error.DiskQuota,
1424 .FBIG => return error.FileTooBig,
1425 .IO => return error.InputOutput,
1426 .NOSPC => return error.NoSpaceLeft,
1427 .PERM => return error.AccessDenied,
1428 .PIPE => return error.BrokenPipe,
1429 .NXIO => return error.Unseekable,
1430 .SPIPE => return error.Unseekable,
1431 .OVERFLOW => return error.Unseekable,
1432 .NOTCAPABLE => return error.AccessDenied,
1433 else => |err| return unexpectedErrno(err),
1434 }
1435 }
1436
1437 // Prevent EINVAL.
1438 const max_count = switch (builtin.os.tag) {
1439 .linux => 0x7ffff000,
1440 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
1441 else => math.maxInt(isize),
1442 };
1443
1444 const pwrite_sym = if (lfs64_abi) system.pwrite64 else system.pwrite;
1445 while (true) {
1446 const rc = pwrite_sym(fd, bytes.ptr, @min(bytes.len, max_count), @bitCast(offset));
1447 switch (errno(rc)) {
1448 .SUCCESS => return @intCast(rc),
1449 .INTR => continue,
1450 .INVAL => return error.InvalidArgument,
1451 .FAULT => unreachable,
1452 .AGAIN => return error.WouldBlock,
1453 .BADF => return error.NotOpenForWriting, // Can be a race condition.
1454 .DESTADDRREQ => unreachable, // `connect` was never called.
1455 .DQUOT => return error.DiskQuota,
1456 .FBIG => return error.FileTooBig,
1457 .IO => return error.InputOutput,
1458 .NOSPC => return error.NoSpaceLeft,
1459 .PERM => return error.AccessDenied,
1460 .PIPE => return error.BrokenPipe,
1461 .NXIO => return error.Unseekable,
1462 .SPIPE => return error.Unseekable,
1463 .OVERFLOW => return error.Unseekable,
1464 .BUSY => return error.DeviceBusy,
1465 else => |err| return unexpectedErrno(err),
1466 }
1467 }
1468}
1469
1470/// Write multiple buffers to a file descriptor, with a position offset.
1471/// Retries when interrupted by a signal.
1472/// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
1473///
1474/// Note that a successful write() may transfer fewer than count bytes. Such partial writes can
1475/// occur for various reasons; for example, because there was insufficient space on the disk
1476/// device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or
1477/// similar was interrupted by a signal handler after it had transferred some, but before it had
1478/// transferred all of the requested bytes. In the event of a partial write, the caller can make
1479/// another write() call to transfer the remaining bytes. The subsequent call will either
1480/// transfer further bytes or may result in an error (e.g., if the disk is now full).
1481///
1482/// If `fd` is opened in non blocking mode, the function will
1483/// return error.WouldBlock when EAGAIN is received.
1484///
1485/// The following systems do not have this syscall, and will return partial writes if more than one
1486/// vector is provided:
1487/// * Darwin
1488/// * Windows
1489///
1490/// If `iov.len` is larger than `IOV_MAX`, a partial write will occur.
1491pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usize {
1492 const have_pwrite_but_not_pwritev = switch (builtin.os.tag) {
1493 .windows, .macos, .ios, .watchos, .tvos, .haiku => true,
1494 else => false,
1495 };
1496
1497 if (have_pwrite_but_not_pwritev) {
1498 // We could loop here; but proper usage of `pwritev` must handle partial writes anyway.
1499 // So we simply write the first vector only.
1500 if (iov.len == 0) return 0;
1501 const first = iov[0];
1502 return pwrite(fd, first.iov_base[0..first.iov_len], offset);
1503 }
1504 if (builtin.os.tag == .wasi and !builtin.link_libc) {
1505 var nwritten: usize = undefined;
1506 switch (wasi.fd_pwrite(fd, iov.ptr, iov.len, offset, &nwritten)) {
1507 .SUCCESS => return nwritten,
1508 .INTR => unreachable,
1509 .INVAL => unreachable,
1510 .FAULT => unreachable,
1511 .AGAIN => unreachable,
1512 .BADF => return error.NotOpenForWriting, // Can be a race condition.
1513 .DESTADDRREQ => unreachable, // `connect` was never called.
1514 .DQUOT => return error.DiskQuota,
1515 .FBIG => return error.FileTooBig,
1516 .IO => return error.InputOutput,
1517 .NOSPC => return error.NoSpaceLeft,
1518 .PERM => return error.AccessDenied,
1519 .PIPE => return error.BrokenPipe,
1520 .NXIO => return error.Unseekable,
1521 .SPIPE => return error.Unseekable,
1522 .OVERFLOW => return error.Unseekable,
1523 .NOTCAPABLE => return error.AccessDenied,
1524 else => |err| return unexpectedErrno(err),
1525 }
1526 }
1527
1528 const pwritev_sym = if (lfs64_abi) system.pwritev64 else system.pwritev;
1529 while (true) {
1530 const rc = pwritev_sym(fd, iov.ptr, @min(iov.len, IOV_MAX), @bitCast(offset));
1531 switch (errno(rc)) {
1532 .SUCCESS => return @intCast(rc),
1533 .INTR => continue,
1534 .INVAL => return error.InvalidArgument,
1535 .FAULT => unreachable,
1536 .AGAIN => return error.WouldBlock,
1537 .BADF => return error.NotOpenForWriting, // Can be a race condition.
1538 .DESTADDRREQ => unreachable, // `connect` was never called.
1539 .DQUOT => return error.DiskQuota,
1540 .FBIG => return error.FileTooBig,
1541 .IO => return error.InputOutput,
1542 .NOSPC => return error.NoSpaceLeft,
1543 .PERM => return error.AccessDenied,
1544 .PIPE => return error.BrokenPipe,
1545 .NXIO => return error.Unseekable,
1546 .SPIPE => return error.Unseekable,
1547 .OVERFLOW => return error.Unseekable,
1548 .BUSY => return error.DeviceBusy,
1549 else => |err| return unexpectedErrno(err),
1550 }
1551 }
1552}
1553
1554pub const OpenError = error{
1555 /// In WASI, this error may occur when the file descriptor does
1556 /// not hold the required rights to open a new resource relative to it.
1557 AccessDenied,
1558 SymLinkLoop,
1559 ProcessFdQuotaExceeded,
1560 SystemFdQuotaExceeded,
1561 NoDevice,
1562 FileNotFound,
1563
1564 /// The path exceeded `MAX_PATH_BYTES` bytes.
1565 NameTooLong,
1566
1567 /// Insufficient kernel memory was available, or
1568 /// the named file is a FIFO and per-user hard limit on
1569 /// memory allocation for pipes has been reached.
1570 SystemResources,
1571
1572 /// The file is too large to be opened. This error is unreachable
1573 /// for 64-bit targets, as well as when opening directories.
1574 FileTooBig,
1575
1576 /// The path refers to directory but the `DIRECTORY` flag was not provided.
1577 IsDir,
1578
1579 /// A new path cannot be created because the device has no room for the new file.
1580 /// This error is only reachable when the `CREAT` flag is provided.
1581 NoSpaceLeft,
1582
1583 /// A component used as a directory in the path was not, in fact, a directory, or
1584 /// `DIRECTORY` was specified and the path was not a directory.
1585 NotDir,
1586
1587 /// The path already exists and the `CREAT` and `EXCL` flags were provided.
1588 PathAlreadyExists,
1589 DeviceBusy,
1590
1591 /// The underlying filesystem does not support file locks
1592 FileLocksNotSupported,
1593
1594 /// Path contains characters that are disallowed by the underlying filesystem.
1595 BadPathName,
1596
1597 /// WASI-only; file paths must be valid UTF-8.
1598 InvalidUtf8,
1599
1600 /// Windows-only; file paths provided by the user must be valid WTF-8.
1601 /// https://simonsapin.github.io/wtf-8/
1602 InvalidWtf8,
1603
1604 /// On Windows, `\\server` or `\\server\share` was not found.
1605 NetworkNotFound,
1606
1607 /// One of these three things:
1608 /// * pathname refers to an executable image which is currently being
1609 /// executed and write access was requested.
1610 /// * pathname refers to a file that is currently in use as a swap
1611 /// file, and the O_TRUNC flag was specified.
1612 /// * pathname refers to a file that is currently being read by the
1613 /// kernel (e.g., for module/firmware loading), and write access was
1614 /// requested.
1615 FileBusy,
1616
1617 WouldBlock,
1618} || UnexpectedError;
1619
1620/// Open and possibly create a file. Keeps trying if it gets interrupted.
1621/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
1622/// On WASI, `file_path` should be encoded as valid UTF-8.
1623/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
1624/// See also `openZ`.
1625pub fn open(file_path: []const u8, flags: O, perm: mode_t) OpenError!fd_t {
1626 if (builtin.os.tag == .windows) {
1627 @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
1628 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
1629 return openat(AT.FDCWD, file_path, flags, perm);
1630 }
1631 const file_path_c = try toPosixPath(file_path);
1632 return openZ(&file_path_c, flags, perm);
1633}
1634
1635/// Open and possibly create a file. Keeps trying if it gets interrupted.
1636/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
1637/// On WASI, `file_path` should be encoded as valid UTF-8.
1638/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
1639/// See also `open`.
1640pub fn openZ(file_path: [*:0]const u8, flags: O, perm: mode_t) OpenError!fd_t {
1641 if (builtin.os.tag == .windows) {
1642 @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
1643 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
1644 return open(mem.sliceTo(file_path, 0), flags, perm);
1645 }
1646
1647 const open_sym = if (lfs64_abi) system.open64 else system.open;
1648 while (true) {
1649 const rc = open_sym(file_path, flags, perm);
1650 switch (errno(rc)) {
1651 .SUCCESS => return @intCast(rc),
1652 .INTR => continue,
1653
1654 .FAULT => unreachable,
1655 .INVAL => unreachable,
1656 .ACCES => return error.AccessDenied,
1657 .FBIG => return error.FileTooBig,
1658 .OVERFLOW => return error.FileTooBig,
1659 .ISDIR => return error.IsDir,
1660 .LOOP => return error.SymLinkLoop,
1661 .MFILE => return error.ProcessFdQuotaExceeded,
1662 .NAMETOOLONG => return error.NameTooLong,
1663 .NFILE => return error.SystemFdQuotaExceeded,
1664 .NODEV => return error.NoDevice,
1665 .NOENT => return error.FileNotFound,
1666 .NOMEM => return error.SystemResources,
1667 .NOSPC => return error.NoSpaceLeft,
1668 .NOTDIR => return error.NotDir,
1669 .PERM => return error.AccessDenied,
1670 .EXIST => return error.PathAlreadyExists,
1671 .BUSY => return error.DeviceBusy,
1672 else => |err| return unexpectedErrno(err),
1673 }
1674 }
1675}
1676
1677/// Open and possibly create a file. Keeps trying if it gets interrupted.
1678/// `file_path` is relative to the open directory handle `dir_fd`.
1679/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
1680/// On WASI, `file_path` should be encoded as valid UTF-8.
1681/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
1682/// See also `openatZ`.
1683pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: O, mode: mode_t) OpenError!fd_t {
1684 if (builtin.os.tag == .windows) {
1685 @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
1686 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
1687 // `mode` is ignored on WASI, which does not support unix-style file permissions
1688 const opts = try openOptionsFromFlagsWasi(flags);
1689 const fd = try openatWasi(
1690 dir_fd,
1691 file_path,
1692 opts.lookup_flags,
1693 opts.oflags,
1694 opts.fs_flags,
1695 opts.fs_rights_base,
1696 opts.fs_rights_inheriting,
1697 );
1698 errdefer close(fd);
1699
1700 if (flags.write) {
1701 const info = try fstat_wasi(fd);
1702 if (info.filetype == .DIRECTORY)
1703 return error.IsDir;
1704 }
1705
1706 return fd;
1707 }
1708 const file_path_c = try toPosixPath(file_path);
1709 return openatZ(dir_fd, &file_path_c, flags, mode);
1710}
1711
1712pub const CommonOpenFlags = packed struct {
1713 ACCMODE: ACCMODE = .RDONLY,
1714 CREAT: bool = false,
1715 EXCL: bool = false,
1716 LARGEFILE: bool = false,
1717 DIRECTORY: bool = false,
1718 CLOEXEC: bool = false,
1719 NONBLOCK: bool = false,
1720
1721 pub fn lower(cof: CommonOpenFlags) O {
1722 if (builtin.os.tag == .wasi) return .{
1723 .read = cof.ACCMODE != .WRONLY,
1724 .write = cof.ACCMODE != .RDONLY,
1725 .CREAT = cof.CREAT,
1726 .EXCL = cof.EXCL,
1727 .DIRECTORY = cof.DIRECTORY,
1728 .NONBLOCK = cof.NONBLOCK,
1729 };
1730 var result: O = .{
1731 .ACCMODE = cof.ACCMODE,
1732 .CREAT = cof.CREAT,
1733 .EXCL = cof.EXCL,
1734 .DIRECTORY = cof.DIRECTORY,
1735 .NONBLOCK = cof.NONBLOCK,
1736 .CLOEXEC = cof.CLOEXEC,
1737 };
1738 if (@hasField(O, "LARGEFILE")) result.LARGEFILE = cof.LARGEFILE;
1739 return result;
1740 }
1741};
1742
1743/// A struct to contain all lookup/rights flags accepted by `wasi.path_open`
1744const WasiOpenOptions = struct {
1745 oflags: wasi.oflags_t,
1746 lookup_flags: wasi.lookupflags_t,
1747 fs_rights_base: wasi.rights_t,
1748 fs_rights_inheriting: wasi.rights_t,
1749 fs_flags: wasi.fdflags_t,
1750};
1751
1752/// Compute rights + flags corresponding to the provided POSIX access mode.
1753fn openOptionsFromFlagsWasi(oflag: O) OpenError!WasiOpenOptions {
1754 const w = std.os.wasi;
1755
1756 // Next, calculate the read/write rights to request, depending on the
1757 // provided POSIX access mode
1758 var rights: w.rights_t = .{};
1759 if (oflag.read) {
1760 rights.FD_READ = true;
1761 rights.FD_READDIR = true;
1762 }
1763 if (oflag.write) {
1764 rights.FD_DATASYNC = true;
1765 rights.FD_WRITE = true;
1766 rights.FD_ALLOCATE = true;
1767 rights.FD_FILESTAT_SET_SIZE = true;
1768 }
1769
1770 // https://github.com/ziglang/zig/issues/18882
1771 const flag_bits: u32 = @bitCast(oflag);
1772 const oflags_int: u16 = @as(u12, @truncate(flag_bits >> 12));
1773 const fs_flags_int: u16 = @as(u12, @truncate(flag_bits));
1774
1775 return .{
1776 // https://github.com/ziglang/zig/issues/18882
1777 .oflags = @bitCast(oflags_int),
1778 .lookup_flags = .{
1779 .SYMLINK_FOLLOW = !oflag.NOFOLLOW,
1780 },
1781 .fs_rights_base = rights,
1782 .fs_rights_inheriting = rights,
1783 // https://github.com/ziglang/zig/issues/18882
1784 .fs_flags = @bitCast(fs_flags_int),
1785 };
1786}
1787
1788/// Open and possibly create a file in WASI.
1789pub fn openatWasi(
1790 dir_fd: fd_t,
1791 file_path: []const u8,
1792 lookup_flags: wasi.lookupflags_t,
1793 oflags: wasi.oflags_t,
1794 fdflags: wasi.fdflags_t,
1795 base: wasi.rights_t,
1796 inheriting: wasi.rights_t,
1797) OpenError!fd_t {
1798 while (true) {
1799 var fd: fd_t = undefined;
1800 switch (wasi.path_open(dir_fd, lookup_flags, file_path.ptr, file_path.len, oflags, base, inheriting, fdflags, &fd)) {
1801 .SUCCESS => return fd,
1802 .INTR => continue,
1803
1804 .FAULT => unreachable,
1805 .INVAL => unreachable,
1806 .BADF => unreachable,
1807 .ACCES => return error.AccessDenied,
1808 .FBIG => return error.FileTooBig,
1809 .OVERFLOW => return error.FileTooBig,
1810 .ISDIR => return error.IsDir,
1811 .LOOP => return error.SymLinkLoop,
1812 .MFILE => return error.ProcessFdQuotaExceeded,
1813 .NAMETOOLONG => return error.NameTooLong,
1814 .NFILE => return error.SystemFdQuotaExceeded,
1815 .NODEV => return error.NoDevice,
1816 .NOENT => return error.FileNotFound,
1817 .NOMEM => return error.SystemResources,
1818 .NOSPC => return error.NoSpaceLeft,
1819 .NOTDIR => return error.NotDir,
1820 .PERM => return error.AccessDenied,
1821 .EXIST => return error.PathAlreadyExists,
1822 .BUSY => return error.DeviceBusy,
1823 .NOTCAPABLE => return error.AccessDenied,
1824 .ILSEQ => return error.InvalidUtf8,
1825 else => |err| return unexpectedErrno(err),
1826 }
1827 }
1828}
1829
1830/// Open and possibly create a file. Keeps trying if it gets interrupted.
1831/// `file_path` is relative to the open directory handle `dir_fd`.
1832/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
1833/// On WASI, `file_path` should be encoded as valid UTF-8.
1834/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
1835/// See also `openat`.
1836pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: O, mode: mode_t) OpenError!fd_t {
1837 if (builtin.os.tag == .windows) {
1838 @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
1839 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
1840 return openat(dir_fd, mem.sliceTo(file_path, 0), flags, mode);
1841 }
1842
1843 const openat_sym = if (lfs64_abi) system.openat64 else system.openat;
1844 while (true) {
1845 const rc = openat_sym(dir_fd, file_path, flags, mode);
1846 switch (errno(rc)) {
1847 .SUCCESS => return @intCast(rc),
1848 .INTR => continue,
1849
1850 .FAULT => unreachable,
1851 .INVAL => unreachable,
1852 .BADF => unreachable,
1853 .ACCES => return error.AccessDenied,
1854 .FBIG => return error.FileTooBig,
1855 .OVERFLOW => return error.FileTooBig,
1856 .ISDIR => return error.IsDir,
1857 .LOOP => return error.SymLinkLoop,
1858 .MFILE => return error.ProcessFdQuotaExceeded,
1859 .NAMETOOLONG => return error.NameTooLong,
1860 .NFILE => return error.SystemFdQuotaExceeded,
1861 .NODEV => return error.NoDevice,
1862 .NOENT => return error.FileNotFound,
1863 .NOMEM => return error.SystemResources,
1864 .NOSPC => return error.NoSpaceLeft,
1865 .NOTDIR => return error.NotDir,
1866 .PERM => return error.AccessDenied,
1867 .EXIST => return error.PathAlreadyExists,
1868 .BUSY => return error.DeviceBusy,
1869 .OPNOTSUPP => return error.FileLocksNotSupported,
1870 .AGAIN => return error.WouldBlock,
1871 .TXTBSY => return error.FileBusy,
1872 else => |err| return unexpectedErrno(err),
1873 }
1874 }
1875}
1876
1877pub fn dup(old_fd: fd_t) !fd_t {
1878 const rc = system.dup(old_fd);
1879 return switch (errno(rc)) {
1880 .SUCCESS => return @intCast(rc),
1881 .MFILE => error.ProcessFdQuotaExceeded,
1882 .BADF => unreachable, // invalid file descriptor
1883 else => |err| return unexpectedErrno(err),
1884 };
1885}
1886
1887pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
1888 while (true) {
1889 switch (errno(system.dup2(old_fd, new_fd))) {
1890 .SUCCESS => return,
1891 .BUSY, .INTR => continue,
1892 .MFILE => return error.ProcessFdQuotaExceeded,
1893 .INVAL => unreachable, // invalid parameters passed to dup2
1894 .BADF => unreachable, // invalid file descriptor
1895 else => |err| return unexpectedErrno(err),
1896 }
1897 }
1898}
1899
1900pub const ExecveError = error{
1901 SystemResources,
1902 AccessDenied,
1903 InvalidExe,
1904 FileSystem,
1905 IsDir,
1906 FileNotFound,
1907 NotDir,
1908 FileBusy,
1909 ProcessFdQuotaExceeded,
1910 SystemFdQuotaExceeded,
1911 NameTooLong,
1912} || UnexpectedError;
1913
1914/// This function ignores PATH environment variable. See `execvpeZ` for that.
1915pub fn execveZ(
1916 path: [*:0]const u8,
1917 child_argv: [*:null]const ?[*:0]const u8,
1918 envp: [*:null]const ?[*:0]const u8,
1919) ExecveError {
1920 switch (errno(system.execve(path, child_argv, envp))) {
1921 .SUCCESS => unreachable,
1922 .FAULT => unreachable,
1923 .@"2BIG" => return error.SystemResources,
1924 .MFILE => return error.ProcessFdQuotaExceeded,
1925 .NAMETOOLONG => return error.NameTooLong,
1926 .NFILE => return error.SystemFdQuotaExceeded,
1927 .NOMEM => return error.SystemResources,
1928 .ACCES => return error.AccessDenied,
1929 .PERM => return error.AccessDenied,
1930 .INVAL => return error.InvalidExe,
1931 .NOEXEC => return error.InvalidExe,
1932 .IO => return error.FileSystem,
1933 .LOOP => return error.FileSystem,
1934 .ISDIR => return error.IsDir,
1935 .NOENT => return error.FileNotFound,
1936 .NOTDIR => return error.NotDir,
1937 .TXTBSY => return error.FileBusy,
1938 else => |err| switch (builtin.os.tag) {
1939 .macos, .ios, .tvos, .watchos => switch (err) {
1940 .BADEXEC => return error.InvalidExe,
1941 .BADARCH => return error.InvalidExe,
1942 else => return unexpectedErrno(err),
1943 },
1944 .linux => switch (err) {
1945 .LIBBAD => return error.InvalidExe,
1946 else => return unexpectedErrno(err),
1947 },
1948 else => return unexpectedErrno(err),
1949 },
1950 }
1951}
1952
1953pub const Arg0Expand = enum {
1954 expand,
1955 no_expand,
1956};
1957
1958/// Like `execvpeZ` except if `arg0_expand` is `.expand`, then `argv` is mutable,
1959/// and `argv[0]` is expanded to be the same absolute path that is passed to the execve syscall.
1960/// If this function returns with an error, `argv[0]` will be restored to the value it was when it was passed in.
1961pub fn execvpeZ_expandArg0(
1962 comptime arg0_expand: Arg0Expand,
1963 file: [*:0]const u8,
1964 child_argv: switch (arg0_expand) {
1965 .expand => [*:null]?[*:0]const u8,
1966 .no_expand => [*:null]const ?[*:0]const u8,
1967 },
1968 envp: [*:null]const ?[*:0]const u8,
1969) ExecveError {
1970 const file_slice = mem.sliceTo(file, 0);
1971 if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveZ(file, child_argv, envp);
1972
1973 const PATH = getenvZ("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
1974 // Use of MAX_PATH_BYTES here is valid as the path_buf will be passed
1975 // directly to the operating system in execveZ.
1976 var path_buf: [MAX_PATH_BYTES]u8 = undefined;
1977 var it = mem.tokenizeScalar(u8, PATH, ':');
1978 var seen_eacces = false;
1979 var err: ExecveError = error.FileNotFound;
1980
1981 // In case of expanding arg0 we must put it back if we return with an error.
1982 const prev_arg0 = child_argv[0];
1983 defer switch (arg0_expand) {
1984 .expand => child_argv[0] = prev_arg0,
1985 .no_expand => {},
1986 };
1987
1988 while (it.next()) |search_path| {
1989 const path_len = search_path.len + file_slice.len + 1;
1990 if (path_buf.len < path_len + 1) return error.NameTooLong;
1991 @memcpy(path_buf[0..search_path.len], search_path);
1992 path_buf[search_path.len] = '/';
1993 @memcpy(path_buf[search_path.len + 1 ..][0..file_slice.len], file_slice);
1994 path_buf[path_len] = 0;
1995 const full_path = path_buf[0..path_len :0].ptr;
1996 switch (arg0_expand) {
1997 .expand => child_argv[0] = full_path,
1998 .no_expand => {},
1999 }
2000 err = execveZ(full_path, child_argv, envp);
2001 switch (err) {
2002 error.AccessDenied => seen_eacces = true,
2003 error.FileNotFound, error.NotDir => {},
2004 else => |e| return e,
2005 }
2006 }
2007 if (seen_eacces) return error.AccessDenied;
2008 return err;
2009}
2010
2011/// This function also uses the PATH environment variable to get the full path to the executable.
2012/// If `file` is an absolute path, this is the same as `execveZ`.
2013pub fn execvpeZ(
2014 file: [*:0]const u8,
2015 argv_ptr: [*:null]const ?[*:0]const u8,
2016 envp: [*:null]const ?[*:0]const u8,
2017) ExecveError {
2018 return execvpeZ_expandArg0(.no_expand, file, argv_ptr, envp);
2019}
2020
2021/// Get an environment variable.
2022/// See also `getenvZ`.
2023pub fn getenv(key: []const u8) ?[:0]const u8 {
2024 if (builtin.os.tag == .windows) {
2025 @compileError("std.os.getenv is unavailable for Windows because environment strings are in WTF-16 format. See std.process.getEnvVarOwned for a cross-platform API or std.os.getenvW for a Windows-specific API.");
2026 }
2027 if (builtin.link_libc) {
2028 var ptr = std.c.environ;
2029 while (ptr[0]) |line| : (ptr += 1) {
2030 var line_i: usize = 0;
2031 while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
2032 const this_key = line[0..line_i];
2033
2034 if (!mem.eql(u8, this_key, key)) continue;
2035
2036 return mem.sliceTo(line + line_i + 1, 0);
2037 }
2038 return null;
2039 }
2040 if (builtin.os.tag == .wasi) {
2041 @compileError("std.os.getenv is unavailable for WASI. See std.process.getEnvMap or std.process.getEnvVarOwned for a cross-platform API.");
2042 }
2043 // The simplified start logic doesn't populate environ.
2044 if (std.start.simplified_logic) return null;
2045 // TODO see https://github.com/ziglang/zig/issues/4524
2046 for (environ) |ptr| {
2047 var line_i: usize = 0;
2048 while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
2049 const this_key = ptr[0..line_i];
2050 if (!mem.eql(u8, key, this_key)) continue;
2051
2052 return mem.sliceTo(ptr + line_i + 1, 0);
2053 }
2054 return null;
2055}
2056
2057/// Get an environment variable with a null-terminated name.
2058/// See also `getenv`.
2059pub fn getenvZ(key: [*:0]const u8) ?[:0]const u8 {
2060 if (builtin.link_libc) {
2061 const value = system.getenv(key) orelse return null;
2062 return mem.sliceTo(value, 0);
2063 }
2064 if (builtin.os.tag == .windows) {
2065 @compileError("std.os.getenvZ is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.os.getenvW for Windows-specific API.");
2066 }
2067 return getenv(mem.sliceTo(key, 0));
2068}
2069
2070/// Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name.
2071/// See also `getenv`.
2072/// This function performs a Unicode-aware case-insensitive lookup using RtlEqualUnicodeString.
2073pub fn getenvW(key: [*:0]const u16) ?[:0]const u16 {
2074 if (builtin.os.tag != .windows) {
2075 @compileError("std.os.getenvW is a Windows-only API");
2076 }
2077 const key_slice = mem.sliceTo(key, 0);
2078 const ptr = windows.peb().ProcessParameters.Environment;
2079 var i: usize = 0;
2080 while (ptr[i] != 0) {
2081 const key_start = i;
2082
2083 // There are some special environment variables that start with =,
2084 // so we need a special case to not treat = as a key/value separator
2085 // if it's the first character.
2086 // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
2087 if (ptr[key_start] == '=') i += 1;
2088
2089 while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {}
2090 const this_key = ptr[key_start..i];
2091
2092 if (ptr[i] == '=') i += 1;
2093
2094 const value_start = i;
2095 while (ptr[i] != 0) : (i += 1) {}
2096 const this_value = ptr[value_start..i :0];
2097
2098 if (windows.eqlIgnoreCaseWTF16(key_slice, this_key)) {
2099 return this_value;
2100 }
2101
2102 i += 1; // skip over null byte
2103 }
2104 return null;
2105}
2106
2107pub const GetCwdError = error{
2108 NameTooLong,
2109 CurrentWorkingDirectoryUnlinked,
2110} || UnexpectedError;
2111
2112/// The result is a slice of out_buffer, indexed from 0.
2113pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
2114 if (builtin.os.tag == .windows) {
2115 return windows.GetCurrentDirectory(out_buffer);
2116 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
2117 const path = ".";
2118 if (out_buffer.len < path.len) return error.NameTooLong;
2119 const result = out_buffer[0..path.len];
2120 @memcpy(result, path);
2121 return result;
2122 }
2123
2124 const err: E = if (builtin.link_libc) err: {
2125 const c_err = if (std.c.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else std.c._errno().*;
2126 break :err @enumFromInt(c_err);
2127 } else err: {
2128 break :err errno(system.getcwd(out_buffer.ptr, out_buffer.len));
2129 };
2130 switch (err) {
2131 .SUCCESS => return mem.sliceTo(out_buffer, 0),
2132 .FAULT => unreachable,
2133 .INVAL => unreachable,
2134 .NOENT => return error.CurrentWorkingDirectoryUnlinked,
2135 .RANGE => return error.NameTooLong,
2136 else => return unexpectedErrno(err),
2137 }
2138}
2139
2140pub const SymLinkError = error{
2141 /// In WASI, this error may occur when the file descriptor does
2142 /// not hold the required rights to create a new symbolic link relative to it.
2143 AccessDenied,
2144 DiskQuota,
2145 PathAlreadyExists,
2146 FileSystem,
2147 SymLinkLoop,
2148 FileNotFound,
2149 SystemResources,
2150 NoSpaceLeft,
2151 ReadOnlyFileSystem,
2152 NotDir,
2153 NameTooLong,
2154
2155 /// WASI-only; file paths must be valid UTF-8.
2156 InvalidUtf8,
2157
2158 /// Windows-only; file paths provided by the user must be valid WTF-8.
2159 /// https://simonsapin.github.io/wtf-8/
2160 InvalidWtf8,
2161
2162 BadPathName,
2163} || UnexpectedError;
2164
2165/// Creates a symbolic link named `sym_link_path` which contains the string `target_path`.
2166/// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
2167/// one; the latter case is known as a dangling link.
2168/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2169/// On WASI, both paths should be encoded as valid UTF-8.
2170/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
2171/// If `sym_link_path` exists, it will not be overwritten.
2172/// See also `symlinkZ.
2173pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {
2174 if (builtin.os.tag == .windows) {
2175 @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
2176 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
2177 return symlinkat(target_path, wasi.AT.FDCWD, sym_link_path);
2178 }
2179 const target_path_c = try toPosixPath(target_path);
2180 const sym_link_path_c = try toPosixPath(sym_link_path);
2181 return symlinkZ(&target_path_c, &sym_link_path_c);
2182}
2183
2184/// This is the same as `symlink` except the parameters are null-terminated pointers.
2185/// See also `symlink`.
2186pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLinkError!void {
2187 if (builtin.os.tag == .windows) {
2188 @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
2189 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
2190 return symlinkatZ(target_path, fs.cwd().fd, sym_link_path);
2191 }
2192 switch (errno(system.symlink(target_path, sym_link_path))) {
2193 .SUCCESS => return,
2194 .FAULT => unreachable,
2195 .INVAL => unreachable,
2196 .ACCES => return error.AccessDenied,
2197 .PERM => return error.AccessDenied,
2198 .DQUOT => return error.DiskQuota,
2199 .EXIST => return error.PathAlreadyExists,
2200 .IO => return error.FileSystem,
2201 .LOOP => return error.SymLinkLoop,
2202 .NAMETOOLONG => return error.NameTooLong,
2203 .NOENT => return error.FileNotFound,
2204 .NOTDIR => return error.NotDir,
2205 .NOMEM => return error.SystemResources,
2206 .NOSPC => return error.NoSpaceLeft,
2207 .ROFS => return error.ReadOnlyFileSystem,
2208 .ILSEQ => |err| if (builtin.os.tag == .wasi)
2209 return error.InvalidUtf8
2210 else
2211 return unexpectedErrno(err),
2212 else => |err| return unexpectedErrno(err),
2213 }
2214}
2215
2216/// Similar to `symlink`, however, creates a symbolic link named `sym_link_path` which contains the string
2217/// `target_path` **relative** to `newdirfd` directory handle.
2218/// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
2219/// one; the latter case is known as a dangling link.
2220/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2221/// On WASI, both paths should be encoded as valid UTF-8.
2222/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
2223/// If `sym_link_path` exists, it will not be overwritten.
2224/// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`.
2225pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
2226 if (builtin.os.tag == .windows) {
2227 @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
2228 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
2229 return symlinkatWasi(target_path, newdirfd, sym_link_path);
2230 }
2231 const target_path_c = try toPosixPath(target_path);
2232 const sym_link_path_c = try toPosixPath(sym_link_path);
2233 return symlinkatZ(&target_path_c, newdirfd, &sym_link_path_c);
2234}
2235
2236/// WASI-only. The same as `symlinkat` but targeting WASI.
2237/// See also `symlinkat`.
2238pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
2239 switch (wasi.path_symlink(target_path.ptr, target_path.len, newdirfd, sym_link_path.ptr, sym_link_path.len)) {
2240 .SUCCESS => {},
2241 .FAULT => unreachable,
2242 .INVAL => unreachable,
2243 .BADF => unreachable,
2244 .ACCES => return error.AccessDenied,
2245 .PERM => return error.AccessDenied,
2246 .DQUOT => return error.DiskQuota,
2247 .EXIST => return error.PathAlreadyExists,
2248 .IO => return error.FileSystem,
2249 .LOOP => return error.SymLinkLoop,
2250 .NAMETOOLONG => return error.NameTooLong,
2251 .NOENT => return error.FileNotFound,
2252 .NOTDIR => return error.NotDir,
2253 .NOMEM => return error.SystemResources,
2254 .NOSPC => return error.NoSpaceLeft,
2255 .ROFS => return error.ReadOnlyFileSystem,
2256 .NOTCAPABLE => return error.AccessDenied,
2257 .ILSEQ => return error.InvalidUtf8,
2258 else => |err| return unexpectedErrno(err),
2259 }
2260}
2261
2262/// The same as `symlinkat` except the parameters are null-terminated pointers.
2263/// See also `symlinkat`.
2264pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void {
2265 if (builtin.os.tag == .windows) {
2266 @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
2267 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
2268 return symlinkat(mem.sliceTo(target_path, 0), newdirfd, mem.sliceTo(sym_link_path, 0));
2269 }
2270 switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) {
2271 .SUCCESS => return,
2272 .FAULT => unreachable,
2273 .INVAL => unreachable,
2274 .ACCES => return error.AccessDenied,
2275 .PERM => return error.AccessDenied,
2276 .DQUOT => return error.DiskQuota,
2277 .EXIST => return error.PathAlreadyExists,
2278 .IO => return error.FileSystem,
2279 .LOOP => return error.SymLinkLoop,
2280 .NAMETOOLONG => return error.NameTooLong,
2281 .NOENT => return error.FileNotFound,
2282 .NOTDIR => return error.NotDir,
2283 .NOMEM => return error.SystemResources,
2284 .NOSPC => return error.NoSpaceLeft,
2285 .ROFS => return error.ReadOnlyFileSystem,
2286 .ILSEQ => |err| if (builtin.os.tag == .wasi)
2287 return error.InvalidUtf8
2288 else
2289 return unexpectedErrno(err),
2290 else => |err| return unexpectedErrno(err),
2291 }
2292}
2293
2294pub const LinkError = UnexpectedError || error{
2295 AccessDenied,
2296 DiskQuota,
2297 PathAlreadyExists,
2298 FileSystem,
2299 SymLinkLoop,
2300 LinkQuotaExceeded,
2301 NameTooLong,
2302 FileNotFound,
2303 SystemResources,
2304 NoSpaceLeft,
2305 ReadOnlyFileSystem,
2306 NotSameFileSystem,
2307
2308 /// WASI-only; file paths must be valid UTF-8.
2309 InvalidUtf8,
2310};
2311
2312/// On WASI, both paths should be encoded as valid UTF-8.
2313/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
2314pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) LinkError!void {
2315 if (builtin.os.tag == .wasi and !builtin.link_libc) {
2316 return link(mem.sliceTo(oldpath, 0), mem.sliceTo(newpath, 0), flags);
2317 }
2318 switch (errno(system.link(oldpath, newpath, flags))) {
2319 .SUCCESS => return,
2320 .ACCES => return error.AccessDenied,
2321 .DQUOT => return error.DiskQuota,
2322 .EXIST => return error.PathAlreadyExists,
2323 .FAULT => unreachable,
2324 .IO => return error.FileSystem,
2325 .LOOP => return error.SymLinkLoop,
2326 .MLINK => return error.LinkQuotaExceeded,
2327 .NAMETOOLONG => return error.NameTooLong,
2328 .NOENT => return error.FileNotFound,
2329 .NOMEM => return error.SystemResources,
2330 .NOSPC => return error.NoSpaceLeft,
2331 .PERM => return error.AccessDenied,
2332 .ROFS => return error.ReadOnlyFileSystem,
2333 .XDEV => return error.NotSameFileSystem,
2334 .INVAL => unreachable,
2335 .ILSEQ => |err| if (builtin.os.tag == .wasi)
2336 return error.InvalidUtf8
2337 else
2338 return unexpectedErrno(err),
2339 else => |err| return unexpectedErrno(err),
2340 }
2341}
2342
2343/// On WASI, both paths should be encoded as valid UTF-8.
2344/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
2345pub fn link(oldpath: []const u8, newpath: []const u8, flags: i32) LinkError!void {
2346 if (builtin.os.tag == .wasi and !builtin.link_libc) {
2347 return linkat(wasi.AT.FDCWD, oldpath, wasi.AT.FDCWD, newpath, flags) catch |err| switch (err) {
2348 error.NotDir => unreachable, // link() does not support directories
2349 else => |e| return e,
2350 };
2351 }
2352 const old = try toPosixPath(oldpath);
2353 const new = try toPosixPath(newpath);
2354 return try linkZ(&old, &new, flags);
2355}
2356
2357pub const LinkatError = LinkError || error{NotDir};
2358
2359/// On WASI, both paths should be encoded as valid UTF-8.
2360/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
2361pub fn linkatZ(
2362 olddir: fd_t,
2363 oldpath: [*:0]const u8,
2364 newdir: fd_t,
2365 newpath: [*:0]const u8,
2366 flags: i32,
2367) LinkatError!void {
2368 if (builtin.os.tag == .wasi and !builtin.link_libc) {
2369 return linkat(olddir, mem.sliceTo(oldpath, 0), newdir, mem.sliceTo(newpath, 0), flags);
2370 }
2371 switch (errno(system.linkat(olddir, oldpath, newdir, newpath, flags))) {
2372 .SUCCESS => return,
2373 .ACCES => return error.AccessDenied,
2374 .DQUOT => return error.DiskQuota,
2375 .EXIST => return error.PathAlreadyExists,
2376 .FAULT => unreachable,
2377 .IO => return error.FileSystem,
2378 .LOOP => return error.SymLinkLoop,
2379 .MLINK => return error.LinkQuotaExceeded,
2380 .NAMETOOLONG => return error.NameTooLong,
2381 .NOENT => return error.FileNotFound,
2382 .NOMEM => return error.SystemResources,
2383 .NOSPC => return error.NoSpaceLeft,
2384 .NOTDIR => return error.NotDir,
2385 .PERM => return error.AccessDenied,
2386 .ROFS => return error.ReadOnlyFileSystem,
2387 .XDEV => return error.NotSameFileSystem,
2388 .INVAL => unreachable,
2389 .ILSEQ => |err| if (builtin.os.tag == .wasi)
2390 return error.InvalidUtf8
2391 else
2392 return unexpectedErrno(err),
2393 else => |err| return unexpectedErrno(err),
2394 }
2395}
2396
2397/// On WASI, both paths should be encoded as valid UTF-8.
2398/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
2399pub fn linkat(
2400 olddir: fd_t,
2401 oldpath: []const u8,
2402 newdir: fd_t,
2403 newpath: []const u8,
2404 flags: i32,
2405) LinkatError!void {
2406 if (builtin.os.tag == .wasi and !builtin.link_libc) {
2407 const old: RelativePathWasi = .{ .dir_fd = olddir, .relative_path = oldpath };
2408 const new: RelativePathWasi = .{ .dir_fd = newdir, .relative_path = newpath };
2409 const old_flags: wasi.lookupflags_t = .{
2410 .SYMLINK_FOLLOW = (flags & AT.SYMLINK_FOLLOW) != 0,
2411 };
2412 switch (wasi.path_link(
2413 old.dir_fd,
2414 old_flags,
2415 old.relative_path.ptr,
2416 old.relative_path.len,
2417 new.dir_fd,
2418 new.relative_path.ptr,
2419 new.relative_path.len,
2420 )) {
2421 .SUCCESS => return,
2422 .ACCES => return error.AccessDenied,
2423 .DQUOT => return error.DiskQuota,
2424 .EXIST => return error.PathAlreadyExists,
2425 .FAULT => unreachable,
2426 .IO => return error.FileSystem,
2427 .LOOP => return error.SymLinkLoop,
2428 .MLINK => return error.LinkQuotaExceeded,
2429 .NAMETOOLONG => return error.NameTooLong,
2430 .NOENT => return error.FileNotFound,
2431 .NOMEM => return error.SystemResources,
2432 .NOSPC => return error.NoSpaceLeft,
2433 .NOTDIR => return error.NotDir,
2434 .PERM => return error.AccessDenied,
2435 .ROFS => return error.ReadOnlyFileSystem,
2436 .XDEV => return error.NotSameFileSystem,
2437 .INVAL => unreachable,
2438 .ILSEQ => return error.InvalidUtf8,
2439 else => |err| return unexpectedErrno(err),
2440 }
2441 }
2442 const old = try toPosixPath(oldpath);
2443 const new = try toPosixPath(newpath);
2444 return try linkatZ(olddir, &old, newdir, &new, flags);
2445}
2446
2447pub const UnlinkError = error{
2448 FileNotFound,
2449
2450 /// In WASI, this error may occur when the file descriptor does
2451 /// not hold the required rights to unlink a resource by path relative to it.
2452 AccessDenied,
2453 FileBusy,
2454 FileSystem,
2455 IsDir,
2456 SymLinkLoop,
2457 NameTooLong,
2458 NotDir,
2459 SystemResources,
2460 ReadOnlyFileSystem,
2461
2462 /// WASI-only; file paths must be valid UTF-8.
2463 InvalidUtf8,
2464
2465 /// Windows-only; file paths provided by the user must be valid WTF-8.
2466 /// https://simonsapin.github.io/wtf-8/
2467 InvalidWtf8,
2468
2469 /// On Windows, file paths cannot contain these characters:
2470 /// '/', '*', '?', '"', '<', '>', '|'
2471 BadPathName,
2472
2473 /// On Windows, `\\server` or `\\server\share` was not found.
2474 NetworkNotFound,
2475} || UnexpectedError;
2476
2477/// Delete a name and possibly the file it refers to.
2478/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2479/// On WASI, `file_path` should be encoded as valid UTF-8.
2480/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
2481/// See also `unlinkZ`.
2482pub fn unlink(file_path: []const u8) UnlinkError!void {
2483 if (builtin.os.tag == .wasi and !builtin.link_libc) {
2484 return unlinkat(wasi.AT.FDCWD, file_path, 0) catch |err| switch (err) {
2485 error.DirNotEmpty => unreachable, // only occurs when targeting directories
2486 else => |e| return e,
2487 };
2488 } else if (builtin.os.tag == .windows) {
2489 const file_path_w = try windows.sliceToPrefixedFileW(null, file_path);
2490 return unlinkW(file_path_w.span());
2491 } else {
2492 const file_path_c = try toPosixPath(file_path);
2493 return unlinkZ(&file_path_c);
2494 }
2495}
2496
2497/// Same as `unlink` except the parameter is null terminated.
2498pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {
2499 if (builtin.os.tag == .windows) {
2500 const file_path_w = try windows.cStrToPrefixedFileW(null, file_path);
2501 return unlinkW(file_path_w.span());
2502 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
2503 return unlink(mem.sliceTo(file_path, 0));
2504 }
2505 switch (errno(system.unlink(file_path))) {
2506 .SUCCESS => return,
2507 .ACCES => return error.AccessDenied,
2508 .PERM => return error.AccessDenied,
2509 .BUSY => return error.FileBusy,
2510 .FAULT => unreachable,
2511 .INVAL => unreachable,
2512 .IO => return error.FileSystem,
2513 .ISDIR => return error.IsDir,
2514 .LOOP => return error.SymLinkLoop,
2515 .NAMETOOLONG => return error.NameTooLong,
2516 .NOENT => return error.FileNotFound,
2517 .NOTDIR => return error.NotDir,
2518 .NOMEM => return error.SystemResources,
2519 .ROFS => return error.ReadOnlyFileSystem,
2520 .ILSEQ => |err| if (builtin.os.tag == .wasi)
2521 return error.InvalidUtf8
2522 else
2523 return unexpectedErrno(err),
2524 else => |err| return unexpectedErrno(err),
2525 }
2526}
2527
2528/// Windows-only. Same as `unlink` except the parameter is null-terminated, WTF16 LE encoded.
2529pub fn unlinkW(file_path_w: []const u16) UnlinkError!void {
2530 windows.DeleteFile(file_path_w, .{ .dir = std.fs.cwd().fd }) catch |err| switch (err) {
2531 error.DirNotEmpty => unreachable, // we're not passing .remove_dir = true
2532 else => |e| return e,
2533 };
2534}
2535
2536pub const UnlinkatError = UnlinkError || error{
2537 /// When passing `AT.REMOVEDIR`, this error occurs when the named directory is not empty.
2538 DirNotEmpty,
2539};
2540
2541/// Delete a file name and possibly the file it refers to, based on an open directory handle.
2542/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2543/// On WASI, `file_path` should be encoded as valid UTF-8.
2544/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
2545/// Asserts that the path parameter has no null bytes.
2546pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
2547 if (builtin.os.tag == .windows) {
2548 const file_path_w = try windows.sliceToPrefixedFileW(dirfd, file_path);
2549 return unlinkatW(dirfd, file_path_w.span(), flags);
2550 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
2551 return unlinkatWasi(dirfd, file_path, flags);
2552 } else {
2553 const file_path_c = try toPosixPath(file_path);
2554 return unlinkatZ(dirfd, &file_path_c, flags);
2555 }
2556}
2557
2558/// WASI-only. Same as `unlinkat` but targeting WASI.
2559/// See also `unlinkat`.
2560pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
2561 const remove_dir = (flags & AT.REMOVEDIR) != 0;
2562 const res = if (remove_dir)
2563 wasi.path_remove_directory(dirfd, file_path.ptr, file_path.len)
2564 else
2565 wasi.path_unlink_file(dirfd, file_path.ptr, file_path.len);
2566 switch (res) {
2567 .SUCCESS => return,
2568 .ACCES => return error.AccessDenied,
2569 .PERM => return error.AccessDenied,
2570 .BUSY => return error.FileBusy,
2571 .FAULT => unreachable,
2572 .IO => return error.FileSystem,
2573 .ISDIR => return error.IsDir,
2574 .LOOP => return error.SymLinkLoop,
2575 .NAMETOOLONG => return error.NameTooLong,
2576 .NOENT => return error.FileNotFound,
2577 .NOTDIR => return error.NotDir,
2578 .NOMEM => return error.SystemResources,
2579 .ROFS => return error.ReadOnlyFileSystem,
2580 .NOTEMPTY => return error.DirNotEmpty,
2581 .NOTCAPABLE => return error.AccessDenied,
2582 .ILSEQ => return error.InvalidUtf8,
2583
2584 .INVAL => unreachable, // invalid flags, or pathname has . as last component
2585 .BADF => unreachable, // always a race condition
2586
2587 else => |err| return unexpectedErrno(err),
2588 }
2589}
2590
2591/// Same as `unlinkat` but `file_path` is a null-terminated string.
2592pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void {
2593 if (builtin.os.tag == .windows) {
2594 const file_path_w = try windows.cStrToPrefixedFileW(dirfd, file_path_c);
2595 return unlinkatW(dirfd, file_path_w.span(), flags);
2596 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
2597 return unlinkat(dirfd, mem.sliceTo(file_path_c, 0), flags);
2598 }
2599 switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {
2600 .SUCCESS => return,
2601 .ACCES => return error.AccessDenied,
2602 .PERM => return error.AccessDenied,
2603 .BUSY => return error.FileBusy,
2604 .FAULT => unreachable,
2605 .IO => return error.FileSystem,
2606 .ISDIR => return error.IsDir,
2607 .LOOP => return error.SymLinkLoop,
2608 .NAMETOOLONG => return error.NameTooLong,
2609 .NOENT => return error.FileNotFound,
2610 .NOTDIR => return error.NotDir,
2611 .NOMEM => return error.SystemResources,
2612 .ROFS => return error.ReadOnlyFileSystem,
2613 .EXIST => return error.DirNotEmpty,
2614 .NOTEMPTY => return error.DirNotEmpty,
2615 .ILSEQ => |err| if (builtin.os.tag == .wasi)
2616 return error.InvalidUtf8
2617 else
2618 return unexpectedErrno(err),
2619
2620 .INVAL => unreachable, // invalid flags, or pathname has . as last component
2621 .BADF => unreachable, // always a race condition
2622
2623 else => |err| return unexpectedErrno(err),
2624 }
2625}
2626
2627/// Same as `unlinkat` but `sub_path_w` is WTF16LE, NT prefixed. Windows only.
2628pub fn unlinkatW(dirfd: fd_t, sub_path_w: []const u16, flags: u32) UnlinkatError!void {
2629 const remove_dir = (flags & AT.REMOVEDIR) != 0;
2630 return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir });
2631}
2632
2633pub const RenameError = error{
2634 /// In WASI, this error may occur when the file descriptor does
2635 /// not hold the required rights to rename a resource by path relative to it.
2636 ///
2637 /// On Windows, this error may be returned instead of PathAlreadyExists when
2638 /// renaming a directory over an existing directory.
2639 AccessDenied,
2640 FileBusy,
2641 DiskQuota,
2642 IsDir,
2643 SymLinkLoop,
2644 LinkQuotaExceeded,
2645 NameTooLong,
2646 FileNotFound,
2647 NotDir,
2648 SystemResources,
2649 NoSpaceLeft,
2650 PathAlreadyExists,
2651 ReadOnlyFileSystem,
2652 RenameAcrossMountPoints,
2653 /// WASI-only; file paths must be valid UTF-8.
2654 InvalidUtf8,
2655 /// Windows-only; file paths provided by the user must be valid WTF-8.
2656 /// https://simonsapin.github.io/wtf-8/
2657 InvalidWtf8,
2658 BadPathName,
2659 NoDevice,
2660 SharingViolation,
2661 PipeBusy,
2662 /// On Windows, `\\server` or `\\server\share` was not found.
2663 NetworkNotFound,
2664 /// On Windows, antivirus software is enabled by default. It can be
2665 /// disabled, but Windows Update sometimes ignores the user's preference
2666 /// and re-enables it. When enabled, antivirus software on Windows
2667 /// intercepts file system operations and makes them significantly slower
2668 /// in addition to possibly failing with this error code.
2669 AntivirusInterference,
2670} || UnexpectedError;
2671
2672/// Change the name or location of a file.
2673/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2674/// On WASI, both paths should be encoded as valid UTF-8.
2675/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
2676pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
2677 if (builtin.os.tag == .wasi and !builtin.link_libc) {
2678 return renameat(wasi.AT.FDCWD, old_path, wasi.AT.FDCWD, new_path);
2679 } else if (builtin.os.tag == .windows) {
2680 const old_path_w = try windows.sliceToPrefixedFileW(null, old_path);
2681 const new_path_w = try windows.sliceToPrefixedFileW(null, new_path);
2682 return renameW(old_path_w.span().ptr, new_path_w.span().ptr);
2683 } else {
2684 const old_path_c = try toPosixPath(old_path);
2685 const new_path_c = try toPosixPath(new_path);
2686 return renameZ(&old_path_c, &new_path_c);
2687 }
2688}
2689
2690/// Same as `rename` except the parameters are null-terminated.
2691pub fn renameZ(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!void {
2692 if (builtin.os.tag == .windows) {
2693 const old_path_w = try windows.cStrToPrefixedFileW(null, old_path);
2694 const new_path_w = try windows.cStrToPrefixedFileW(null, new_path);
2695 return renameW(old_path_w.span().ptr, new_path_w.span().ptr);
2696 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
2697 return rename(mem.sliceTo(old_path, 0), mem.sliceTo(new_path, 0));
2698 }
2699 switch (errno(system.rename(old_path, new_path))) {
2700 .SUCCESS => return,
2701 .ACCES => return error.AccessDenied,
2702 .PERM => return error.AccessDenied,
2703 .BUSY => return error.FileBusy,
2704 .DQUOT => return error.DiskQuota,
2705 .FAULT => unreachable,
2706 .INVAL => unreachable,
2707 .ISDIR => return error.IsDir,
2708 .LOOP => return error.SymLinkLoop,
2709 .MLINK => return error.LinkQuotaExceeded,
2710 .NAMETOOLONG => return error.NameTooLong,
2711 .NOENT => return error.FileNotFound,
2712 .NOTDIR => return error.NotDir,
2713 .NOMEM => return error.SystemResources,
2714 .NOSPC => return error.NoSpaceLeft,
2715 .EXIST => return error.PathAlreadyExists,
2716 .NOTEMPTY => return error.PathAlreadyExists,
2717 .ROFS => return error.ReadOnlyFileSystem,
2718 .XDEV => return error.RenameAcrossMountPoints,
2719 .ILSEQ => |err| if (builtin.os.tag == .wasi)
2720 return error.InvalidUtf8
2721 else
2722 return unexpectedErrno(err),
2723 else => |err| return unexpectedErrno(err),
2724 }
2725}
2726
2727/// Same as `rename` except the parameters are null-terminated and WTF16LE encoded.
2728/// Assumes target is Windows.
2729pub fn renameW(old_path: [*:0]const u16, new_path: [*:0]const u16) RenameError!void {
2730 const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
2731 return windows.MoveFileExW(old_path, new_path, flags);
2732}
2733
2734/// Change the name or location of a file based on an open directory handle.
2735/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2736/// On WASI, both paths should be encoded as valid UTF-8.
2737/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
2738pub fn renameat(
2739 old_dir_fd: fd_t,
2740 old_path: []const u8,
2741 new_dir_fd: fd_t,
2742 new_path: []const u8,
2743) RenameError!void {
2744 if (builtin.os.tag == .windows) {
2745 const old_path_w = try windows.sliceToPrefixedFileW(old_dir_fd, old_path);
2746 const new_path_w = try windows.sliceToPrefixedFileW(new_dir_fd, new_path);
2747 return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
2748 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
2749 const old: RelativePathWasi = .{ .dir_fd = old_dir_fd, .relative_path = old_path };
2750 const new: RelativePathWasi = .{ .dir_fd = new_dir_fd, .relative_path = new_path };
2751 return renameatWasi(old, new);
2752 } else {
2753 const old_path_c = try toPosixPath(old_path);
2754 const new_path_c = try toPosixPath(new_path);
2755 return renameatZ(old_dir_fd, &old_path_c, new_dir_fd, &new_path_c);
2756 }
2757}
2758
2759/// WASI-only. Same as `renameat` expect targeting WASI.
2760/// See also `renameat`.
2761pub fn renameatWasi(old: RelativePathWasi, new: RelativePathWasi) RenameError!void {
2762 switch (wasi.path_rename(old.dir_fd, old.relative_path.ptr, old.relative_path.len, new.dir_fd, new.relative_path.ptr, new.relative_path.len)) {
2763 .SUCCESS => return,
2764 .ACCES => return error.AccessDenied,
2765 .PERM => return error.AccessDenied,
2766 .BUSY => return error.FileBusy,
2767 .DQUOT => return error.DiskQuota,
2768 .FAULT => unreachable,
2769 .INVAL => unreachable,
2770 .ISDIR => return error.IsDir,
2771 .LOOP => return error.SymLinkLoop,
2772 .MLINK => return error.LinkQuotaExceeded,
2773 .NAMETOOLONG => return error.NameTooLong,
2774 .NOENT => return error.FileNotFound,
2775 .NOTDIR => return error.NotDir,
2776 .NOMEM => return error.SystemResources,
2777 .NOSPC => return error.NoSpaceLeft,
2778 .EXIST => return error.PathAlreadyExists,
2779 .NOTEMPTY => return error.PathAlreadyExists,
2780 .ROFS => return error.ReadOnlyFileSystem,
2781 .XDEV => return error.RenameAcrossMountPoints,
2782 .NOTCAPABLE => return error.AccessDenied,
2783 .ILSEQ => return error.InvalidUtf8,
2784 else => |err| return unexpectedErrno(err),
2785 }
2786}
2787
2788/// Same as `renameat` except the parameters are null-terminated.
2789pub fn renameatZ(
2790 old_dir_fd: fd_t,
2791 old_path: [*:0]const u8,
2792 new_dir_fd: fd_t,
2793 new_path: [*:0]const u8,
2794) RenameError!void {
2795 if (builtin.os.tag == .windows) {
2796 const old_path_w = try windows.cStrToPrefixedFileW(old_dir_fd, old_path);
2797 const new_path_w = try windows.cStrToPrefixedFileW(new_dir_fd, new_path);
2798 return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
2799 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
2800 return renameat(old_dir_fd, mem.sliceTo(old_path, 0), new_dir_fd, mem.sliceTo(new_path, 0));
2801 }
2802
2803 switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) {
2804 .SUCCESS => return,
2805 .ACCES => return error.AccessDenied,
2806 .PERM => return error.AccessDenied,
2807 .BUSY => return error.FileBusy,
2808 .DQUOT => return error.DiskQuota,
2809 .FAULT => unreachable,
2810 .INVAL => unreachable,
2811 .ISDIR => return error.IsDir,
2812 .LOOP => return error.SymLinkLoop,
2813 .MLINK => return error.LinkQuotaExceeded,
2814 .NAMETOOLONG => return error.NameTooLong,
2815 .NOENT => return error.FileNotFound,
2816 .NOTDIR => return error.NotDir,
2817 .NOMEM => return error.SystemResources,
2818 .NOSPC => return error.NoSpaceLeft,
2819 .EXIST => return error.PathAlreadyExists,
2820 .NOTEMPTY => return error.PathAlreadyExists,
2821 .ROFS => return error.ReadOnlyFileSystem,
2822 .XDEV => return error.RenameAcrossMountPoints,
2823 .ILSEQ => |err| if (builtin.os.tag == .wasi)
2824 return error.InvalidUtf8
2825 else
2826 return unexpectedErrno(err),
2827 else => |err| return unexpectedErrno(err),
2828 }
2829}
2830
2831/// Same as `renameat` but Windows-only and the path parameters are
2832/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
2833pub fn renameatW(
2834 old_dir_fd: fd_t,
2835 old_path_w: []const u16,
2836 new_dir_fd: fd_t,
2837 new_path_w: []const u16,
2838 ReplaceIfExists: windows.BOOLEAN,
2839) RenameError!void {
2840 const src_fd = windows.OpenFile(old_path_w, .{
2841 .dir = old_dir_fd,
2842 .access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE,
2843 .creation = windows.FILE_OPEN,
2844 .filter = .any, // This function is supposed to rename both files and directories.
2845 .follow_symlinks = false,
2846 }) catch |err| switch (err) {
2847 error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`.
2848 else => |e| return e,
2849 };
2850 defer windows.CloseHandle(src_fd);
2851
2852 var need_fallback = true;
2853 var rc: windows.NTSTATUS = undefined;
2854 // FILE_RENAME_INFORMATION_EX and FILE_RENAME_POSIX_SEMANTICS require >= win10_rs1,
2855 // but FILE_RENAME_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5. We check >= rs5 here
2856 // so that we only use POSIX_SEMANTICS when we know IGNORE_READONLY_ATTRIBUTE will also be
2857 // supported in order to avoid either (1) using a redundant call that we can know in advance will return
2858 // STATUS_NOT_SUPPORTED or (2) only setting IGNORE_READONLY_ATTRIBUTE when >= rs5
2859 // and therefore having different behavior when the Windows version is >= rs1 but < rs5.
2860 if (builtin.target.os.isAtLeast(.windows, .win10_rs5) orelse false) {
2861 const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION_EX) + (MAX_PATH_BYTES - 1);
2862 var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION_EX)) = undefined;
2863 const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION_EX) - 1 + new_path_w.len * 2;
2864 if (struct_len > struct_buf_len) return error.NameTooLong;
2865
2866 const rename_info: *windows.FILE_RENAME_INFORMATION_EX = @ptrCast(&rename_info_buf);
2867 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
2868
2869 var flags: windows.ULONG = windows.FILE_RENAME_POSIX_SEMANTICS | windows.FILE_RENAME_IGNORE_READONLY_ATTRIBUTE;
2870 if (ReplaceIfExists == windows.TRUE) flags |= windows.FILE_RENAME_REPLACE_IF_EXISTS;
2871 rename_info.* = .{
2872 .Flags = flags,
2873 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd,
2874 .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong
2875 .FileName = undefined,
2876 };
2877 @memcpy((&rename_info.FileName).ptr, new_path_w);
2878 rc = windows.ntdll.NtSetInformationFile(
2879 src_fd,
2880 &io_status_block,
2881 rename_info,
2882 @intCast(struct_len), // already checked for error.NameTooLong
2883 .FileRenameInformationEx,
2884 );
2885 switch (rc) {
2886 .SUCCESS => return,
2887 // INVALID_PARAMETER here means that the filesystem does not support FileRenameInformationEx
2888 .INVALID_PARAMETER => {},
2889 .DIRECTORY_NOT_EMPTY => return error.PathAlreadyExists,
2890 .FILE_IS_A_DIRECTORY => return error.IsDir,
2891 .NOT_A_DIRECTORY => return error.NotDir,
2892 // For all other statuses, fall down to the switch below to handle them.
2893 else => need_fallback = false,
2894 }
2895 }
2896
2897 if (need_fallback) {
2898 const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1);
2899 var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION)) = undefined;
2900 const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path_w.len * 2;
2901 if (struct_len > struct_buf_len) return error.NameTooLong;
2902
2903 const rename_info: *windows.FILE_RENAME_INFORMATION = @ptrCast(&rename_info_buf);
2904 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
2905
2906 rename_info.* = .{
2907 .Flags = ReplaceIfExists,
2908 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd,
2909 .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong
2910 .FileName = undefined,
2911 };
2912 @memcpy((&rename_info.FileName).ptr, new_path_w);
2913
2914 rc =
2915 windows.ntdll.NtSetInformationFile(
2916 src_fd,
2917 &io_status_block,
2918 rename_info,
2919 @intCast(struct_len), // already checked for error.NameTooLong
2920 .FileRenameInformation,
2921 );
2922 }
2923
2924 switch (rc) {
2925 .SUCCESS => {},
2926 .INVALID_HANDLE => unreachable,
2927 .INVALID_PARAMETER => unreachable,
2928 .OBJECT_PATH_SYNTAX_BAD => unreachable,
2929 .ACCESS_DENIED => return error.AccessDenied,
2930 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
2931 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
2932 .NOT_SAME_DEVICE => return error.RenameAcrossMountPoints,
2933 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
2934 else => return windows.unexpectedStatus(rc),
2935 }
2936}
2937
2938/// On Windows, `sub_dir_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2939/// On WASI, `sub_dir_path` should be encoded as valid UTF-8.
2940/// On other platforms, `sub_dir_path` is an opaque sequence of bytes with no particular encoding.
2941pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
2942 if (builtin.os.tag == .windows) {
2943 const sub_dir_path_w = try windows.sliceToPrefixedFileW(dir_fd, sub_dir_path);
2944 return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
2945 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
2946 return mkdiratWasi(dir_fd, sub_dir_path, mode);
2947 } else {
2948 const sub_dir_path_c = try toPosixPath(sub_dir_path);
2949 return mkdiratZ(dir_fd, &sub_dir_path_c, mode);
2950 }
2951}
2952
2953pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
2954 _ = mode;
2955 switch (wasi.path_create_directory(dir_fd, sub_dir_path.ptr, sub_dir_path.len)) {
2956 .SUCCESS => return,
2957 .ACCES => return error.AccessDenied,
2958 .BADF => unreachable,
2959 .PERM => return error.AccessDenied,
2960 .DQUOT => return error.DiskQuota,
2961 .EXIST => return error.PathAlreadyExists,
2962 .FAULT => unreachable,
2963 .LOOP => return error.SymLinkLoop,
2964 .MLINK => return error.LinkQuotaExceeded,
2965 .NAMETOOLONG => return error.NameTooLong,
2966 .NOENT => return error.FileNotFound,
2967 .NOMEM => return error.SystemResources,
2968 .NOSPC => return error.NoSpaceLeft,
2969 .NOTDIR => return error.NotDir,
2970 .ROFS => return error.ReadOnlyFileSystem,
2971 .NOTCAPABLE => return error.AccessDenied,
2972 .ILSEQ => return error.InvalidUtf8,
2973 else => |err| return unexpectedErrno(err),
2974 }
2975}
2976
2977/// Same as `mkdirat` except the parameters are null-terminated.
2978pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
2979 if (builtin.os.tag == .windows) {
2980 const sub_dir_path_w = try windows.cStrToPrefixedFileW(dir_fd, sub_dir_path);
2981 return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
2982 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
2983 return mkdirat(dir_fd, mem.sliceTo(sub_dir_path, 0), mode);
2984 }
2985 switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) {
2986 .SUCCESS => return,
2987 .ACCES => return error.AccessDenied,
2988 .BADF => unreachable,
2989 .PERM => return error.AccessDenied,
2990 .DQUOT => return error.DiskQuota,
2991 .EXIST => return error.PathAlreadyExists,
2992 .FAULT => unreachable,
2993 .LOOP => return error.SymLinkLoop,
2994 .MLINK => return error.LinkQuotaExceeded,
2995 .NAMETOOLONG => return error.NameTooLong,
2996 .NOENT => return error.FileNotFound,
2997 .NOMEM => return error.SystemResources,
2998 .NOSPC => return error.NoSpaceLeft,
2999 .NOTDIR => return error.NotDir,
3000 .ROFS => return error.ReadOnlyFileSystem,
3001 // dragonfly: when dir_fd is unlinked from filesystem
3002 .NOTCONN => return error.FileNotFound,
3003 .ILSEQ => |err| if (builtin.os.tag == .wasi)
3004 return error.InvalidUtf8
3005 else
3006 return unexpectedErrno(err),
3007 else => |err| return unexpectedErrno(err),
3008 }
3009}
3010
3011/// Windows-only. Same as `mkdirat` except the parameter WTF16 LE encoded.
3012pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!void {
3013 _ = mode;
3014 const sub_dir_handle = windows.OpenFile(sub_path_w, .{
3015 .dir = dir_fd,
3016 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
3017 .creation = windows.FILE_CREATE,
3018 .filter = .dir_only,
3019 }) catch |err| switch (err) {
3020 error.IsDir => return error.Unexpected,
3021 error.PipeBusy => return error.Unexpected,
3022 error.WouldBlock => return error.Unexpected,
3023 error.AntivirusInterference => return error.Unexpected,
3024 else => |e| return e,
3025 };
3026 windows.CloseHandle(sub_dir_handle);
3027}
3028
3029pub const MakeDirError = error{
3030 /// In WASI, this error may occur when the file descriptor does
3031 /// not hold the required rights to create a new directory relative to it.
3032 AccessDenied,
3033 DiskQuota,
3034 PathAlreadyExists,
3035 SymLinkLoop,
3036 LinkQuotaExceeded,
3037 NameTooLong,
3038 FileNotFound,
3039 SystemResources,
3040 NoSpaceLeft,
3041 NotDir,
3042 ReadOnlyFileSystem,
3043 /// WASI-only; file paths must be valid UTF-8.
3044 InvalidUtf8,
3045 /// Windows-only; file paths provided by the user must be valid WTF-8.
3046 /// https://simonsapin.github.io/wtf-8/
3047 InvalidWtf8,
3048 BadPathName,
3049 NoDevice,
3050 /// On Windows, `\\server` or `\\server\share` was not found.
3051 NetworkNotFound,
3052} || UnexpectedError;
3053
3054/// Create a directory.
3055/// `mode` is ignored on Windows and WASI.
3056/// On Windows, `dir_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3057/// On WASI, `dir_path` should be encoded as valid UTF-8.
3058/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
3059pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
3060 if (builtin.os.tag == .wasi and !builtin.link_libc) {
3061 return mkdirat(wasi.AT.FDCWD, dir_path, mode);
3062 } else if (builtin.os.tag == .windows) {
3063 const dir_path_w = try windows.sliceToPrefixedFileW(null, dir_path);
3064 return mkdirW(dir_path_w.span(), mode);
3065 } else {
3066 const dir_path_c = try toPosixPath(dir_path);
3067 return mkdirZ(&dir_path_c, mode);
3068 }
3069}
3070
3071/// Same as `mkdir` but the parameter is null-terminated.
3072/// On Windows, `dir_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3073/// On WASI, `dir_path` should be encoded as valid UTF-8.
3074/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
3075pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
3076 if (builtin.os.tag == .windows) {
3077 const dir_path_w = try windows.cStrToPrefixedFileW(null, dir_path);
3078 return mkdirW(dir_path_w.span(), mode);
3079 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
3080 return mkdir(mem.sliceTo(dir_path, 0), mode);
3081 }
3082 switch (errno(system.mkdir(dir_path, mode))) {
3083 .SUCCESS => return,
3084 .ACCES => return error.AccessDenied,
3085 .PERM => return error.AccessDenied,
3086 .DQUOT => return error.DiskQuota,
3087 .EXIST => return error.PathAlreadyExists,
3088 .FAULT => unreachable,
3089 .LOOP => return error.SymLinkLoop,
3090 .MLINK => return error.LinkQuotaExceeded,
3091 .NAMETOOLONG => return error.NameTooLong,
3092 .NOENT => return error.FileNotFound,
3093 .NOMEM => return error.SystemResources,
3094 .NOSPC => return error.NoSpaceLeft,
3095 .NOTDIR => return error.NotDir,
3096 .ROFS => return error.ReadOnlyFileSystem,
3097 .ILSEQ => |err| if (builtin.os.tag == .wasi)
3098 return error.InvalidUtf8
3099 else
3100 return unexpectedErrno(err),
3101 else => |err| return unexpectedErrno(err),
3102 }
3103}
3104
3105/// Windows-only. Same as `mkdir` but the parameters is WTF16LE encoded.
3106pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void {
3107 _ = mode;
3108 const sub_dir_handle = windows.OpenFile(dir_path_w, .{
3109 .dir = std.fs.cwd().fd,
3110 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
3111 .creation = windows.FILE_CREATE,
3112 .filter = .dir_only,
3113 }) catch |err| switch (err) {
3114 error.IsDir => return error.Unexpected,
3115 error.PipeBusy => return error.Unexpected,
3116 error.WouldBlock => return error.Unexpected,
3117 error.AntivirusInterference => return error.Unexpected,
3118 else => |e| return e,
3119 };
3120 windows.CloseHandle(sub_dir_handle);
3121}
3122
3123pub const DeleteDirError = error{
3124 AccessDenied,
3125 FileBusy,
3126 SymLinkLoop,
3127 NameTooLong,
3128 FileNotFound,
3129 SystemResources,
3130 NotDir,
3131 DirNotEmpty,
3132 ReadOnlyFileSystem,
3133 /// WASI-only; file paths must be valid UTF-8.
3134 InvalidUtf8,
3135 /// Windows-only; file paths provided by the user must be valid WTF-8.
3136 /// https://simonsapin.github.io/wtf-8/
3137 InvalidWtf8,
3138 BadPathName,
3139 /// On Windows, `\\server` or `\\server\share` was not found.
3140 NetworkNotFound,
3141} || UnexpectedError;
3142
3143/// Deletes an empty directory.
3144/// On Windows, `dir_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3145/// On WASI, `dir_path` should be encoded as valid UTF-8.
3146/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
3147pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
3148 if (builtin.os.tag == .wasi and !builtin.link_libc) {
3149 return unlinkat(wasi.AT.FDCWD, dir_path, AT.REMOVEDIR) catch |err| switch (err) {
3150 error.FileSystem => unreachable, // only occurs when targeting files
3151 error.IsDir => unreachable, // only occurs when targeting files
3152 else => |e| return e,
3153 };
3154 } else if (builtin.os.tag == .windows) {
3155 const dir_path_w = try windows.sliceToPrefixedFileW(null, dir_path);
3156 return rmdirW(dir_path_w.span());
3157 } else {
3158 const dir_path_c = try toPosixPath(dir_path);
3159 return rmdirZ(&dir_path_c);
3160 }
3161}
3162
3163/// Same as `rmdir` except the parameter is null-terminated.
3164/// On Windows, `dir_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3165/// On WASI, `dir_path` should be encoded as valid UTF-8.
3166/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
3167pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {
3168 if (builtin.os.tag == .windows) {
3169 const dir_path_w = try windows.cStrToPrefixedFileW(null, dir_path);
3170 return rmdirW(dir_path_w.span());
3171 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
3172 return rmdir(mem.sliceTo(dir_path, 0));
3173 }
3174 switch (errno(system.rmdir(dir_path))) {
3175 .SUCCESS => return,
3176 .ACCES => return error.AccessDenied,
3177 .PERM => return error.AccessDenied,
3178 .BUSY => return error.FileBusy,
3179 .FAULT => unreachable,
3180 .INVAL => return error.BadPathName,
3181 .LOOP => return error.SymLinkLoop,
3182 .NAMETOOLONG => return error.NameTooLong,
3183 .NOENT => return error.FileNotFound,
3184 .NOMEM => return error.SystemResources,
3185 .NOTDIR => return error.NotDir,
3186 .EXIST => return error.DirNotEmpty,
3187 .NOTEMPTY => return error.DirNotEmpty,
3188 .ROFS => return error.ReadOnlyFileSystem,
3189 .ILSEQ => |err| if (builtin.os.tag == .wasi)
3190 return error.InvalidUtf8
3191 else
3192 return unexpectedErrno(err),
3193 else => |err| return unexpectedErrno(err),
3194 }
3195}
3196
3197/// Windows-only. Same as `rmdir` except the parameter is WTF-16 LE encoded.
3198pub fn rmdirW(dir_path_w: []const u16) DeleteDirError!void {
3199 return windows.DeleteFile(dir_path_w, .{ .dir = std.fs.cwd().fd, .remove_dir = true }) catch |err| switch (err) {
3200 error.IsDir => unreachable,
3201 else => |e| return e,
3202 };
3203}
3204
3205pub const ChangeCurDirError = error{
3206 AccessDenied,
3207 FileSystem,
3208 SymLinkLoop,
3209 NameTooLong,
3210 FileNotFound,
3211 SystemResources,
3212 NotDir,
3213 BadPathName,
3214 /// WASI-only; file paths must be valid UTF-8.
3215 InvalidUtf8,
3216 /// Windows-only; file paths provided by the user must be valid WTF-8.
3217 /// https://simonsapin.github.io/wtf-8/
3218 InvalidWtf8,
3219} || UnexpectedError;
3220
3221/// Changes the current working directory of the calling process.
3222/// On Windows, `dir_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3223/// On WASI, `dir_path` should be encoded as valid UTF-8.
3224/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
3225pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
3226 if (builtin.os.tag == .wasi and !builtin.link_libc) {
3227 @compileError("WASI does not support os.chdir");
3228 } else if (builtin.os.tag == .windows) {
3229 var wtf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;
3230 const len = try std.unicode.wtf8ToWtf16Le(wtf16_dir_path[0..], dir_path);
3231 if (len > wtf16_dir_path.len) return error.NameTooLong;
3232 return chdirW(wtf16_dir_path[0..len]);
3233 } else {
3234 const dir_path_c = try toPosixPath(dir_path);
3235 return chdirZ(&dir_path_c);
3236 }
3237}
3238
3239/// Same as `chdir` except the parameter is null-terminated.
3240/// On Windows, `dir_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3241/// On WASI, `dir_path` should be encoded as valid UTF-8.
3242/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
3243pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void {
3244 if (builtin.os.tag == .windows) {
3245 var wtf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;
3246 const len = try std.unicode.wtf8ToWtf16Le(wtf16_dir_path[0..], mem.span(dir_path));
3247 if (len > wtf16_dir_path.len) return error.NameTooLong;
3248 return chdirW(wtf16_dir_path[0..len]);
3249 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
3250 return chdir(mem.span(dir_path));
3251 }
3252 switch (errno(system.chdir(dir_path))) {
3253 .SUCCESS => return,
3254 .ACCES => return error.AccessDenied,
3255 .FAULT => unreachable,
3256 .IO => return error.FileSystem,
3257 .LOOP => return error.SymLinkLoop,
3258 .NAMETOOLONG => return error.NameTooLong,
3259 .NOENT => return error.FileNotFound,
3260 .NOMEM => return error.SystemResources,
3261 .NOTDIR => return error.NotDir,
3262 .ILSEQ => |err| if (builtin.os.tag == .wasi)
3263 return error.InvalidUtf8
3264 else
3265 return unexpectedErrno(err),
3266 else => |err| return unexpectedErrno(err),
3267 }
3268}
3269
3270/// Windows-only. Same as `chdir` except the parameter is WTF16 LE encoded.
3271pub fn chdirW(dir_path: []const u16) ChangeCurDirError!void {
3272 windows.SetCurrentDirectory(dir_path) catch |err| switch (err) {
3273 error.NoDevice => return error.FileSystem,
3274 else => |e| return e,
3275 };
3276}
3277
3278pub const FchdirError = error{
3279 AccessDenied,
3280 NotDir,
3281 FileSystem,
3282} || UnexpectedError;
3283
3284pub fn fchdir(dirfd: fd_t) FchdirError!void {
3285 if (dirfd == AT.FDCWD) return;
3286 while (true) {
3287 switch (errno(system.fchdir(dirfd))) {
3288 .SUCCESS => return,
3289 .ACCES => return error.AccessDenied,
3290 .BADF => unreachable,
3291 .NOTDIR => return error.NotDir,
3292 .INTR => continue,
3293 .IO => return error.FileSystem,
3294 else => |err| return unexpectedErrno(err),
3295 }
3296 }
3297}
3298
3299pub const ReadLinkError = error{
3300 /// In WASI, this error may occur when the file descriptor does
3301 /// not hold the required rights to read value of a symbolic link relative to it.
3302 AccessDenied,
3303 FileSystem,
3304 SymLinkLoop,
3305 NameTooLong,
3306 FileNotFound,
3307 SystemResources,
3308 NotLink,
3309 NotDir,
3310 /// WASI-only; file paths must be valid UTF-8.
3311 InvalidUtf8,
3312 /// Windows-only; file paths provided by the user must be valid WTF-8.
3313 /// https://simonsapin.github.io/wtf-8/
3314 InvalidWtf8,
3315 BadPathName,
3316 /// Windows-only. This error may occur if the opened reparse point is
3317 /// of unsupported type.
3318 UnsupportedReparsePointType,
3319 /// On Windows, `\\server` or `\\server\share` was not found.
3320 NetworkNotFound,
3321} || UnexpectedError;
3322
3323/// Read value of a symbolic link.
3324/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3325/// On WASI, `file_path` should be encoded as valid UTF-8.
3326/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
3327/// The return value is a slice of `out_buffer` from index 0.
3328/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3329/// On WASI, the result is encoded as UTF-8.
3330/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
3331pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
3332 if (builtin.os.tag == .wasi and !builtin.link_libc) {
3333 return readlinkat(wasi.AT.FDCWD, file_path, out_buffer);
3334 } else if (builtin.os.tag == .windows) {
3335 const file_path_w = try windows.sliceToPrefixedFileW(null, file_path);
3336 return readlinkW(file_path_w.span(), out_buffer);
3337 } else {
3338 const file_path_c = try toPosixPath(file_path);
3339 return readlinkZ(&file_path_c, out_buffer);
3340 }
3341}
3342
3343/// Windows-only. Same as `readlink` except `file_path` is WTF16 LE encoded.
3344/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3345/// See also `readlinkZ`.
3346pub fn readlinkW(file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
3347 return windows.ReadLink(std.fs.cwd().fd, file_path, out_buffer);
3348}
3349
3350/// Same as `readlink` except `file_path` is null-terminated.
3351pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
3352 if (builtin.os.tag == .windows) {
3353 const file_path_w = try windows.cStrToPrefixedFileW(null, file_path);
3354 return readlinkW(file_path_w.span(), out_buffer);
3355 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
3356 return readlink(mem.sliceTo(file_path, 0), out_buffer);
3357 }
3358 const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
3359 switch (errno(rc)) {
3360 .SUCCESS => return out_buffer[0..@bitCast(rc)],
3361 .ACCES => return error.AccessDenied,
3362 .FAULT => unreachable,
3363 .INVAL => return error.NotLink,
3364 .IO => return error.FileSystem,
3365 .LOOP => return error.SymLinkLoop,
3366 .NAMETOOLONG => return error.NameTooLong,
3367 .NOENT => return error.FileNotFound,
3368 .NOMEM => return error.SystemResources,
3369 .NOTDIR => return error.NotDir,
3370 .ILSEQ => |err| if (builtin.os.tag == .wasi)
3371 return error.InvalidUtf8
3372 else
3373 return unexpectedErrno(err),
3374 else => |err| return unexpectedErrno(err),
3375 }
3376}
3377
3378/// Similar to `readlink` except reads value of a symbolink link **relative** to `dirfd` directory handle.
3379/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3380/// On WASI, `file_path` should be encoded as valid UTF-8.
3381/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
3382/// The return value is a slice of `out_buffer` from index 0.
3383/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3384/// On WASI, the result is encoded as UTF-8.
3385/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
3386/// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`.
3387pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
3388 if (builtin.os.tag == .wasi and !builtin.link_libc) {
3389 return readlinkatWasi(dirfd, file_path, out_buffer);
3390 }
3391 if (builtin.os.tag == .windows) {
3392 const file_path_w = try windows.sliceToPrefixedFileW(dirfd, file_path);
3393 return readlinkatW(dirfd, file_path_w.span(), out_buffer);
3394 }
3395 const file_path_c = try toPosixPath(file_path);
3396 return readlinkatZ(dirfd, &file_path_c, out_buffer);
3397}
3398
3399/// WASI-only. Same as `readlinkat` but targets WASI.
3400/// See also `readlinkat`.
3401pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
3402 var bufused: usize = undefined;
3403 switch (wasi.path_readlink(dirfd, file_path.ptr, file_path.len, out_buffer.ptr, out_buffer.len, &bufused)) {
3404 .SUCCESS => return out_buffer[0..bufused],
3405 .ACCES => return error.AccessDenied,
3406 .FAULT => unreachable,
3407 .INVAL => return error.NotLink,
3408 .IO => return error.FileSystem,
3409 .LOOP => return error.SymLinkLoop,
3410 .NAMETOOLONG => return error.NameTooLong,
3411 .NOENT => return error.FileNotFound,
3412 .NOMEM => return error.SystemResources,
3413 .NOTDIR => return error.NotDir,
3414 .NOTCAPABLE => return error.AccessDenied,
3415 .ILSEQ => return error.InvalidUtf8,
3416 else => |err| return unexpectedErrno(err),
3417 }
3418}
3419
3420/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 LE encoded.
3421/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3422/// See also `readlinkat`.
3423pub fn readlinkatW(dirfd: fd_t, file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
3424 return windows.ReadLink(dirfd, file_path, out_buffer);
3425}
3426
3427/// Same as `readlinkat` except `file_path` is null-terminated.
3428/// See also `readlinkat`.
3429pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
3430 if (builtin.os.tag == .windows) {
3431 const file_path_w = try windows.cStrToPrefixedFileW(dirfd, file_path);
3432 return readlinkatW(dirfd, file_path_w.span(), out_buffer);
3433 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
3434 return readlinkat(dirfd, mem.sliceTo(file_path, 0), out_buffer);
3435 }
3436 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
3437 switch (errno(rc)) {
3438 .SUCCESS => return out_buffer[0..@bitCast(rc)],
3439 .ACCES => return error.AccessDenied,
3440 .FAULT => unreachable,
3441 .INVAL => return error.NotLink,
3442 .IO => return error.FileSystem,
3443 .LOOP => return error.SymLinkLoop,
3444 .NAMETOOLONG => return error.NameTooLong,
3445 .NOENT => return error.FileNotFound,
3446 .NOMEM => return error.SystemResources,
3447 .NOTDIR => return error.NotDir,
3448 .ILSEQ => |err| if (builtin.os.tag == .wasi)
3449 return error.InvalidUtf8
3450 else
3451 return unexpectedErrno(err),
3452 else => |err| return unexpectedErrno(err),
3453 }
3454}
3455
3456pub const SetEidError = error{
3457 InvalidUserId,
3458 PermissionDenied,
3459} || UnexpectedError;
3460
3461pub const SetIdError = error{ResourceLimitReached} || SetEidError;
3462
3463pub fn setuid(uid: uid_t) SetIdError!void {
3464 switch (errno(system.setuid(uid))) {
3465 .SUCCESS => return,
3466 .AGAIN => return error.ResourceLimitReached,
3467 .INVAL => return error.InvalidUserId,
3468 .PERM => return error.PermissionDenied,
3469 else => |err| return unexpectedErrno(err),
3470 }
3471}
3472
3473pub fn seteuid(uid: uid_t) SetEidError!void {
3474 switch (errno(system.seteuid(uid))) {
3475 .SUCCESS => return,
3476 .INVAL => return error.InvalidUserId,
3477 .PERM => return error.PermissionDenied,
3478 else => |err| return unexpectedErrno(err),
3479 }
3480}
3481
3482pub fn setreuid(ruid: uid_t, euid: uid_t) SetIdError!void {
3483 switch (errno(system.setreuid(ruid, euid))) {
3484 .SUCCESS => return,
3485 .AGAIN => return error.ResourceLimitReached,
3486 .INVAL => return error.InvalidUserId,
3487 .PERM => return error.PermissionDenied,
3488 else => |err| return unexpectedErrno(err),
3489 }
3490}
3491
3492pub fn setgid(gid: gid_t) SetIdError!void {
3493 switch (errno(system.setgid(gid))) {
3494 .SUCCESS => return,
3495 .AGAIN => return error.ResourceLimitReached,
3496 .INVAL => return error.InvalidUserId,
3497 .PERM => return error.PermissionDenied,
3498 else => |err| return unexpectedErrno(err),
3499 }
3500}
3501
3502pub fn setegid(uid: uid_t) SetEidError!void {
3503 switch (errno(system.setegid(uid))) {
3504 .SUCCESS => return,
3505 .INVAL => return error.InvalidUserId,
3506 .PERM => return error.PermissionDenied,
3507 else => |err| return unexpectedErrno(err),
3508 }
3509}
3510
3511pub fn setregid(rgid: gid_t, egid: gid_t) SetIdError!void {
3512 switch (errno(system.setregid(rgid, egid))) {
3513 .SUCCESS => return,
3514 .AGAIN => return error.ResourceLimitReached,
3515 .INVAL => return error.InvalidUserId,
3516 .PERM => return error.PermissionDenied,
3517 else => |err| return unexpectedErrno(err),
3518 }
3519}
3520
3521/// Test whether a file descriptor refers to a terminal.
3522pub fn isatty(handle: fd_t) bool {
3523 if (builtin.os.tag == .windows) {
3524 if (isCygwinPty(handle))
3525 return true;
3526
3527 var out: windows.DWORD = undefined;
3528 return windows.kernel32.GetConsoleMode(handle, &out) != 0;
3529 }
3530 if (builtin.link_libc) {
3531 return system.isatty(handle) != 0;
3532 }
3533 if (builtin.os.tag == .wasi) {
3534 var statbuf: wasi.fdstat_t = undefined;
3535 const err = wasi.fd_fdstat_get(handle, &statbuf);
3536 if (err != .SUCCESS)
3537 return false;
3538
3539 // A tty is a character device that we can't seek or tell on.
3540 if (statbuf.fs_filetype != .CHARACTER_DEVICE)
3541 return false;
3542 if (statbuf.fs_rights_base.FD_SEEK or statbuf.fs_rights_base.FD_TELL)
3543 return false;
3544
3545 return true;
3546 }
3547 if (builtin.os.tag == .linux) {
3548 while (true) {
3549 var wsz: linux.winsize = undefined;
3550 const fd: usize = @bitCast(@as(isize, handle));
3551 const rc = linux.syscall3(.ioctl, fd, linux.T.IOCGWINSZ, @intFromPtr(&wsz));
3552 switch (linux.getErrno(rc)) {
3553 .SUCCESS => return true,
3554 .INTR => continue,
3555 else => return false,
3556 }
3557 }
3558 }
3559 return system.isatty(handle) != 0;
3560}
3561
3562pub fn isCygwinPty(handle: fd_t) bool {
3563 if (builtin.os.tag != .windows) return false;
3564
3565 // If this is a MSYS2/cygwin pty, then it will be a named pipe with a name in one of these formats:
3566 // msys-[...]-ptyN-[...]
3567 // cygwin-[...]-ptyN-[...]
3568 //
3569 // Example: msys-1888ae32e00d56aa-pty0-to-master
3570
3571 // First, just check that the handle is a named pipe.
3572 // This allows us to avoid the more costly NtQueryInformationFile call
3573 // for handles that aren't named pipes.
3574 {
3575 var io_status: windows.IO_STATUS_BLOCK = undefined;
3576 var device_info: windows.FILE_FS_DEVICE_INFORMATION = undefined;
3577 const rc = windows.ntdll.NtQueryVolumeInformationFile(handle, &io_status, &device_info, @sizeOf(windows.FILE_FS_DEVICE_INFORMATION), .FileFsDeviceInformation);
3578 switch (rc) {
3579 .SUCCESS => {},
3580 else => return false,
3581 }
3582 if (device_info.DeviceType != windows.FILE_DEVICE_NAMED_PIPE) return false;
3583 }
3584
3585 const name_bytes_offset = @offsetOf(windows.FILE_NAME_INFO, "FileName");
3586 // `NAME_MAX` UTF-16 code units (2 bytes each)
3587 // Note: This buffer may not be long enough to handle *all* possible paths (PATH_MAX_WIDE would be necessary for that),
3588 // but because we only care about certain paths and we know they must be within a reasonable length,
3589 // we can use this smaller buffer and just return false on any error from NtQueryInformationFile.
3590 const num_name_bytes = windows.MAX_PATH * 2;
3591 var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (name_bytes_offset + num_name_bytes);
3592
3593 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
3594 const rc = windows.ntdll.NtQueryInformationFile(handle, &io_status_block, &name_info_bytes, @intCast(name_info_bytes.len), .FileNameInformation);
3595 switch (rc) {
3596 .SUCCESS => {},
3597 .INVALID_PARAMETER => unreachable,
3598 else => return false,
3599 }
3600
3601 const name_info: *const windows.FILE_NAME_INFO = @ptrCast(&name_info_bytes);
3602 const name_bytes = name_info_bytes[name_bytes_offset .. name_bytes_offset + name_info.FileNameLength];
3603 const name_wide = mem.bytesAsSlice(u16, name_bytes);
3604 // Note: The name we get from NtQueryInformationFile will be prefixed with a '\', e.g. \msys-1888ae32e00d56aa-pty0-to-master
3605 return (mem.startsWith(u16, name_wide, &[_]u16{ '\\', 'm', 's', 'y', 's', '-' }) or
3606 mem.startsWith(u16, name_wide, &[_]u16{ '\\', 'c', 'y', 'g', 'w', 'i', 'n', '-' })) and
3607 mem.indexOf(u16, name_wide, &[_]u16{ '-', 'p', 't', 'y' }) != null;
3608}
3609
3610pub const SocketError = error{
3611 /// Permission to create a socket of the specified type and/or
3612 /// pro‐tocol is denied.
3613 PermissionDenied,
3614
3615 /// The implementation does not support the specified address family.
3616 AddressFamilyNotSupported,
3617
3618 /// Unknown protocol, or protocol family not available.
3619 ProtocolFamilyNotAvailable,
3620
3621 /// The per-process limit on the number of open file descriptors has been reached.
3622 ProcessFdQuotaExceeded,
3623
3624 /// The system-wide limit on the total number of open files has been reached.
3625 SystemFdQuotaExceeded,
3626
3627 /// Insufficient memory is available. The socket cannot be created until sufficient
3628 /// resources are freed.
3629 SystemResources,
3630
3631 /// The protocol type or the specified protocol is not supported within this domain.
3632 ProtocolNotSupported,
3633
3634 /// The socket type is not supported by the protocol.
3635 SocketTypeNotSupported,
3636} || UnexpectedError;
3637
3638pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t {
3639 if (builtin.os.tag == .windows) {
3640 // NOTE: windows translates the SOCK.NONBLOCK/SOCK.CLOEXEC flags into
3641 // windows-analagous operations
3642 const filtered_sock_type = socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC);
3643 const flags: u32 = if ((socket_type & SOCK.CLOEXEC) != 0)
3644 windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT
3645 else
3646 0;
3647 const rc = try windows.WSASocketW(
3648 @bitCast(domain),
3649 @bitCast(filtered_sock_type),
3650 @bitCast(protocol),
3651 null,
3652 0,
3653 flags,
3654 );
3655 errdefer windows.closesocket(rc) catch unreachable;
3656 if ((socket_type & SOCK.NONBLOCK) != 0) {
3657 var mode: c_ulong = 1; // nonblocking
3658 if (windows.ws2_32.SOCKET_ERROR == windows.ws2_32.ioctlsocket(rc, windows.ws2_32.FIONBIO, &mode)) {
3659 switch (windows.ws2_32.WSAGetLastError()) {
3660 // have not identified any error codes that should be handled yet
3661 else => unreachable,
3662 }
3663 }
3664 }
3665 return rc;
3666 }
3667
3668 const have_sock_flags = !builtin.target.isDarwin();
3669 const filtered_sock_type = if (!have_sock_flags)
3670 socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC)
3671 else
3672 socket_type;
3673 const rc = system.socket(domain, filtered_sock_type, protocol);
3674 switch (errno(rc)) {
3675 .SUCCESS => {
3676 const fd: fd_t = @intCast(rc);
3677 errdefer close(fd);
3678 if (!have_sock_flags) {
3679 try setSockFlags(fd, socket_type);
3680 }
3681 return fd;
3682 },
3683 .ACCES => return error.PermissionDenied,
3684 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
3685 .INVAL => return error.ProtocolFamilyNotAvailable,
3686 .MFILE => return error.ProcessFdQuotaExceeded,
3687 .NFILE => return error.SystemFdQuotaExceeded,
3688 .NOBUFS => return error.SystemResources,
3689 .NOMEM => return error.SystemResources,
3690 .PROTONOSUPPORT => return error.ProtocolNotSupported,
3691 .PROTOTYPE => return error.SocketTypeNotSupported,
3692 else => |err| return unexpectedErrno(err),
3693 }
3694}
3695
3696pub const ShutdownError = error{
3697 ConnectionAborted,
3698
3699 /// Connection was reset by peer, application should close socket as it is no longer usable.
3700 ConnectionResetByPeer,
3701 BlockingOperationInProgress,
3702
3703 /// The network subsystem has failed.
3704 NetworkSubsystemFailed,
3705
3706 /// The socket is not connected (connection-oriented sockets only).
3707 SocketNotConnected,
3708 SystemResources,
3709} || UnexpectedError;
3710
3711pub const ShutdownHow = enum { recv, send, both };
3712
3713/// Shutdown socket send/receive operations
3714pub fn shutdown(sock: socket_t, how: ShutdownHow) ShutdownError!void {
3715 if (builtin.os.tag == .windows) {
3716 const result = windows.ws2_32.shutdown(sock, switch (how) {
3717 .recv => windows.ws2_32.SD_RECEIVE,
3718 .send => windows.ws2_32.SD_SEND,
3719 .both => windows.ws2_32.SD_BOTH,
3720 });
3721 if (0 != result) switch (windows.ws2_32.WSAGetLastError()) {
3722 .WSAECONNABORTED => return error.ConnectionAborted,
3723 .WSAECONNRESET => return error.ConnectionResetByPeer,
3724 .WSAEINPROGRESS => return error.BlockingOperationInProgress,
3725 .WSAEINVAL => unreachable,
3726 .WSAENETDOWN => return error.NetworkSubsystemFailed,
3727 .WSAENOTCONN => return error.SocketNotConnected,
3728 .WSAENOTSOCK => unreachable,
3729 .WSANOTINITIALISED => unreachable,
3730 else => |err| return windows.unexpectedWSAError(err),
3731 };
3732 } else {
3733 const rc = system.shutdown(sock, switch (how) {
3734 .recv => SHUT.RD,
3735 .send => SHUT.WR,
3736 .both => SHUT.RDWR,
3737 });
3738 switch (errno(rc)) {
3739 .SUCCESS => return,
3740 .BADF => unreachable,
3741 .INVAL => unreachable,
3742 .NOTCONN => return error.SocketNotConnected,
3743 .NOTSOCK => unreachable,
3744 .NOBUFS => return error.SystemResources,
3745 else => |err| return unexpectedErrno(err),
3746 }
3747 }
3748}
3749
3750pub const BindError = error{
3751 /// The address is protected, and the user is not the superuser.
3752 /// For UNIX domain sockets: Search permission is denied on a component
3753 /// of the path prefix.
3754 AccessDenied,
3755
3756 /// The given address is already in use, or in the case of Internet domain sockets,
3757 /// The port number was specified as zero in the socket
3758 /// address structure, but, upon attempting to bind to an ephemeral port, it was
3759 /// determined that all port numbers in the ephemeral port range are currently in
3760 /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7).
3761 AddressInUse,
3762
3763 /// A nonexistent interface was requested or the requested address was not local.
3764 AddressNotAvailable,
3765
3766 /// The address is not valid for the address family of socket.
3767 AddressFamilyNotSupported,
3768
3769 /// Too many symbolic links were encountered in resolving addr.
3770 SymLinkLoop,
3771
3772 /// addr is too long.
3773 NameTooLong,
3774
3775 /// A component in the directory prefix of the socket pathname does not exist.
3776 FileNotFound,
3777
3778 /// Insufficient kernel memory was available.
3779 SystemResources,
3780
3781 /// A component of the path prefix is not a directory.
3782 NotDir,
3783
3784 /// The socket inode would reside on a read-only filesystem.
3785 ReadOnlyFileSystem,
3786
3787 /// The network subsystem has failed.
3788 NetworkSubsystemFailed,
3789
3790 FileDescriptorNotASocket,
3791
3792 AlreadyBound,
3793} || UnexpectedError;
3794
3795/// addr is `*const T` where T is one of the sockaddr
3796pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!void {
3797 if (builtin.os.tag == .windows) {
3798 const rc = windows.bind(sock, addr, len);
3799 if (rc == windows.ws2_32.SOCKET_ERROR) {
3800 switch (windows.ws2_32.WSAGetLastError()) {
3801 .WSANOTINITIALISED => unreachable, // not initialized WSA
3802 .WSAEACCES => return error.AccessDenied,
3803 .WSAEADDRINUSE => return error.AddressInUse,
3804 .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
3805 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
3806 .WSAEFAULT => unreachable, // invalid pointers
3807 .WSAEINVAL => return error.AlreadyBound,
3808 .WSAENOBUFS => return error.SystemResources,
3809 .WSAENETDOWN => return error.NetworkSubsystemFailed,
3810 else => |err| return windows.unexpectedWSAError(err),
3811 }
3812 unreachable;
3813 }
3814 return;
3815 } else {
3816 const rc = system.bind(sock, addr, len);
3817 switch (errno(rc)) {
3818 .SUCCESS => return,
3819 .ACCES, .PERM => return error.AccessDenied,
3820 .ADDRINUSE => return error.AddressInUse,
3821 .BADF => unreachable, // always a race condition if this error is returned
3822 .INVAL => unreachable, // invalid parameters
3823 .NOTSOCK => unreachable, // invalid `sockfd`
3824 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
3825 .ADDRNOTAVAIL => return error.AddressNotAvailable,
3826 .FAULT => unreachable, // invalid `addr` pointer
3827 .LOOP => return error.SymLinkLoop,
3828 .NAMETOOLONG => return error.NameTooLong,
3829 .NOENT => return error.FileNotFound,
3830 .NOMEM => return error.SystemResources,
3831 .NOTDIR => return error.NotDir,
3832 .ROFS => return error.ReadOnlyFileSystem,
3833 else => |err| return unexpectedErrno(err),
3834 }
3835 }
3836 unreachable;
3837}
3838
3839pub const ListenError = error{
3840 /// Another socket is already listening on the same port.
3841 /// For Internet domain sockets, the socket referred to by sockfd had not previously
3842 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it
3843 /// was determined that all port numbers in the ephemeral port range are currently in
3844 /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
3845 AddressInUse,
3846
3847 /// The file descriptor sockfd does not refer to a socket.
3848 FileDescriptorNotASocket,
3849
3850 /// The socket is not of a type that supports the listen() operation.
3851 OperationNotSupported,
3852
3853 /// The network subsystem has failed.
3854 NetworkSubsystemFailed,
3855
3856 /// Ran out of system resources
3857 /// On Windows it can either run out of socket descriptors or buffer space
3858 SystemResources,
3859
3860 /// Already connected
3861 AlreadyConnected,
3862
3863 /// Socket has not been bound yet
3864 SocketNotBound,
3865} || UnexpectedError;
3866
3867pub fn listen(sock: socket_t, backlog: u31) ListenError!void {
3868 if (builtin.os.tag == .windows) {
3869 const rc = windows.listen(sock, backlog);
3870 if (rc == windows.ws2_32.SOCKET_ERROR) {
3871 switch (windows.ws2_32.WSAGetLastError()) {
3872 .WSANOTINITIALISED => unreachable, // not initialized WSA
3873 .WSAENETDOWN => return error.NetworkSubsystemFailed,
3874 .WSAEADDRINUSE => return error.AddressInUse,
3875 .WSAEISCONN => return error.AlreadyConnected,
3876 .WSAEINVAL => return error.SocketNotBound,
3877 .WSAEMFILE, .WSAENOBUFS => return error.SystemResources,
3878 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
3879 .WSAEOPNOTSUPP => return error.OperationNotSupported,
3880 .WSAEINPROGRESS => unreachable,
3881 else => |err| return windows.unexpectedWSAError(err),
3882 }
3883 }
3884 return;
3885 } else {
3886 const rc = system.listen(sock, backlog);
3887 switch (errno(rc)) {
3888 .SUCCESS => return,
3889 .ADDRINUSE => return error.AddressInUse,
3890 .BADF => unreachable,
3891 .NOTSOCK => return error.FileDescriptorNotASocket,
3892 .OPNOTSUPP => return error.OperationNotSupported,
3893 else => |err| return unexpectedErrno(err),
3894 }
3895 }
3896}
3897
3898pub const AcceptError = error{
3899 ConnectionAborted,
3900
3901 /// The file descriptor sockfd does not refer to a socket.
3902 FileDescriptorNotASocket,
3903
3904 /// The per-process limit on the number of open file descriptors has been reached.
3905 ProcessFdQuotaExceeded,
3906
3907 /// The system-wide limit on the total number of open files has been reached.
3908 SystemFdQuotaExceeded,
3909
3910 /// Not enough free memory. This often means that the memory allocation is limited
3911 /// by the socket buffer limits, not by the system memory.
3912 SystemResources,
3913
3914 /// Socket is not listening for new connections.
3915 SocketNotListening,
3916
3917 ProtocolFailure,
3918
3919 /// Firewall rules forbid connection.
3920 BlockedByFirewall,
3921
3922 /// This error occurs when no global event loop is configured,
3923 /// and accepting from the socket would block.
3924 WouldBlock,
3925
3926 /// An incoming connection was indicated, but was subsequently terminated by the
3927 /// remote peer prior to accepting the call.
3928 ConnectionResetByPeer,
3929
3930 /// The network subsystem has failed.
3931 NetworkSubsystemFailed,
3932
3933 /// The referenced socket is not a type that supports connection-oriented service.
3934 OperationNotSupported,
3935} || UnexpectedError;
3936
3937/// Accept a connection on a socket.
3938/// If `sockfd` is opened in non blocking mode, the function will
3939/// return error.WouldBlock when EAGAIN is received.
3940pub fn accept(
3941 /// This argument is a socket that has been created with `socket`, bound to a local address
3942 /// with `bind`, and is listening for connections after a `listen`.
3943 sock: socket_t,
3944 /// This argument is a pointer to a sockaddr structure. This structure is filled in with the
3945 /// address of the peer socket, as known to the communications layer. The exact format of the
3946 /// address returned addr is determined by the socket's address family (see `socket` and the
3947 /// respective protocol man pages).
3948 addr: ?*sockaddr,
3949 /// This argument is a value-result argument: the caller must initialize it to contain the
3950 /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size
3951 /// of the peer address.
3952 ///
3953 /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size`
3954 /// will return a value greater than was supplied to the call.
3955 addr_size: ?*socklen_t,
3956 /// The following values can be bitwise ORed in flags to obtain different behavior:
3957 /// * `SOCK.NONBLOCK` - Set the `NONBLOCK` file status flag on the open file description (see `open`)
3958 /// referred to by the new file descriptor. Using this flag saves extra calls to `fcntl` to achieve
3959 /// the same result.
3960 /// * `SOCK.CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the
3961 /// description of the `CLOEXEC` flag in `open` for reasons why this may be useful.
3962 flags: u32,
3963) AcceptError!socket_t {
3964 const have_accept4 = !(builtin.target.isDarwin() or builtin.os.tag == .windows);
3965 assert(0 == (flags & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC))); // Unsupported flag(s)
3966
3967 const accepted_sock: socket_t = while (true) {
3968 const rc = if (have_accept4)
3969 system.accept4(sock, addr, addr_size, flags)
3970 else if (builtin.os.tag == .windows)
3971 windows.accept(sock, addr, addr_size)
3972 else
3973 system.accept(sock, addr, addr_size);
3974
3975 if (builtin.os.tag == .windows) {
3976 if (rc == windows.ws2_32.INVALID_SOCKET) {
3977 switch (windows.ws2_32.WSAGetLastError()) {
3978 .WSANOTINITIALISED => unreachable, // not initialized WSA
3979 .WSAECONNRESET => return error.ConnectionResetByPeer,
3980 .WSAEFAULT => unreachable,
3981 .WSAEINVAL => return error.SocketNotListening,
3982 .WSAEMFILE => return error.ProcessFdQuotaExceeded,
3983 .WSAENETDOWN => return error.NetworkSubsystemFailed,
3984 .WSAENOBUFS => return error.FileDescriptorNotASocket,
3985 .WSAEOPNOTSUPP => return error.OperationNotSupported,
3986 .WSAEWOULDBLOCK => return error.WouldBlock,
3987 else => |err| return windows.unexpectedWSAError(err),
3988 }
3989 } else {
3990 break rc;
3991 }
3992 } else {
3993 switch (errno(rc)) {
3994 .SUCCESS => break @intCast(rc),
3995 .INTR => continue,
3996 .AGAIN => return error.WouldBlock,
3997 .BADF => unreachable, // always a race condition
3998 .CONNABORTED => return error.ConnectionAborted,
3999 .FAULT => unreachable,
4000 .INVAL => return error.SocketNotListening,
4001 .NOTSOCK => unreachable,
4002 .MFILE => return error.ProcessFdQuotaExceeded,
4003 .NFILE => return error.SystemFdQuotaExceeded,
4004 .NOBUFS => return error.SystemResources,
4005 .NOMEM => return error.SystemResources,
4006 .OPNOTSUPP => unreachable,
4007 .PROTO => return error.ProtocolFailure,
4008 .PERM => return error.BlockedByFirewall,
4009 else => |err| return unexpectedErrno(err),
4010 }
4011 }
4012 };
4013
4014 errdefer switch (builtin.os.tag) {
4015 .windows => windows.closesocket(accepted_sock) catch unreachable,
4016 else => close(accepted_sock),
4017 };
4018 if (!have_accept4) {
4019 try setSockFlags(accepted_sock, flags);
4020 }
4021 return accepted_sock;
4022}
4023
4024pub const EpollCreateError = error{
4025 /// The per-user limit on the number of epoll instances imposed by
4026 /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further
4027 /// details.
4028 /// Or, The per-process limit on the number of open file descriptors has been reached.
4029 ProcessFdQuotaExceeded,
4030
4031 /// The system-wide limit on the total number of open files has been reached.
4032 SystemFdQuotaExceeded,
4033
4034 /// There was insufficient memory to create the kernel object.
4035 SystemResources,
4036} || UnexpectedError;
4037
4038pub fn epoll_create1(flags: u32) EpollCreateError!i32 {
4039 const rc = system.epoll_create1(flags);
4040 switch (errno(rc)) {
4041 .SUCCESS => return @intCast(rc),
4042 else => |err| return unexpectedErrno(err),
4043
4044 .INVAL => unreachable,
4045 .MFILE => return error.ProcessFdQuotaExceeded,
4046 .NFILE => return error.SystemFdQuotaExceeded,
4047 .NOMEM => return error.SystemResources,
4048 }
4049}
4050
4051pub const EpollCtlError = error{
4052 /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered
4053 /// with this epoll instance.
4054 FileDescriptorAlreadyPresentInSet,
4055
4056 /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a
4057 /// circular loop of epoll instances monitoring one another.
4058 OperationCausesCircularLoop,
4059
4060 /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll
4061 /// instance.
4062 FileDescriptorNotRegistered,
4063
4064 /// There was insufficient memory to handle the requested op control operation.
4065 SystemResources,
4066
4067 /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while
4068 /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance.
4069 /// See epoll(7) for further details.
4070 UserResourceLimitReached,
4071
4072 /// The target file fd does not support epoll. This error can occur if fd refers to,
4073 /// for example, a regular file or a directory.
4074 FileDescriptorIncompatibleWithEpoll,
4075} || UnexpectedError;
4076
4077pub fn epoll_ctl(epfd: i32, op: u32, fd: i32, event: ?*linux.epoll_event) EpollCtlError!void {
4078 const rc = system.epoll_ctl(epfd, op, fd, event);
4079 switch (errno(rc)) {
4080 .SUCCESS => return,
4081 else => |err| return unexpectedErrno(err),
4082
4083 .BADF => unreachable, // always a race condition if this happens
4084 .EXIST => return error.FileDescriptorAlreadyPresentInSet,
4085 .INVAL => unreachable,
4086 .LOOP => return error.OperationCausesCircularLoop,
4087 .NOENT => return error.FileDescriptorNotRegistered,
4088 .NOMEM => return error.SystemResources,
4089 .NOSPC => return error.UserResourceLimitReached,
4090 .PERM => return error.FileDescriptorIncompatibleWithEpoll,
4091 }
4092}
4093
4094/// Waits for an I/O event on an epoll file descriptor.
4095/// Returns the number of file descriptors ready for the requested I/O,
4096/// or zero if no file descriptor became ready during the requested timeout milliseconds.
4097pub fn epoll_wait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize {
4098 while (true) {
4099 // TODO get rid of the @intCast
4100 const rc = system.epoll_wait(epfd, events.ptr, @intCast(events.len), timeout);
4101 switch (errno(rc)) {
4102 .SUCCESS => return @intCast(rc),
4103 .INTR => continue,
4104 .BADF => unreachable,
4105 .FAULT => unreachable,
4106 .INVAL => unreachable,
4107 else => unreachable,
4108 }
4109 }
4110}
4111
4112pub const EventFdError = error{
4113 SystemResources,
4114 ProcessFdQuotaExceeded,
4115 SystemFdQuotaExceeded,
4116} || UnexpectedError;
4117
4118pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 {
4119 const rc = system.eventfd(initval, flags);
4120 switch (errno(rc)) {
4121 .SUCCESS => return @intCast(rc),
4122 else => |err| return unexpectedErrno(err),
4123
4124 .INVAL => unreachable, // invalid parameters
4125 .MFILE => return error.ProcessFdQuotaExceeded,
4126 .NFILE => return error.SystemFdQuotaExceeded,
4127 .NODEV => return error.SystemResources,
4128 .NOMEM => return error.SystemResources,
4129 }
4130}
4131
4132pub const GetSockNameError = error{
4133 /// Insufficient resources were available in the system to perform the operation.
4134 SystemResources,
4135
4136 /// The network subsystem has failed.
4137 NetworkSubsystemFailed,
4138
4139 /// Socket hasn't been bound yet
4140 SocketNotBound,
4141
4142 FileDescriptorNotASocket,
4143} || UnexpectedError;
4144
4145pub fn getsockname(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
4146 if (builtin.os.tag == .windows) {
4147 const rc = windows.getsockname(sock, addr, addrlen);
4148 if (rc == windows.ws2_32.SOCKET_ERROR) {
4149 switch (windows.ws2_32.WSAGetLastError()) {
4150 .WSANOTINITIALISED => unreachable,
4151 .WSAENETDOWN => return error.NetworkSubsystemFailed,
4152 .WSAEFAULT => unreachable, // addr or addrlen have invalid pointers or addrlen points to an incorrect value
4153 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
4154 .WSAEINVAL => return error.SocketNotBound,
4155 else => |err| return windows.unexpectedWSAError(err),
4156 }
4157 }
4158 return;
4159 } else {
4160 const rc = system.getsockname(sock, addr, addrlen);
4161 switch (errno(rc)) {
4162 .SUCCESS => return,
4163 else => |err| return unexpectedErrno(err),
4164
4165 .BADF => unreachable, // always a race condition
4166 .FAULT => unreachable,
4167 .INVAL => unreachable, // invalid parameters
4168 .NOTSOCK => return error.FileDescriptorNotASocket,
4169 .NOBUFS => return error.SystemResources,
4170 }
4171 }
4172}
4173
4174pub fn getpeername(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
4175 if (builtin.os.tag == .windows) {
4176 const rc = windows.getpeername(sock, addr, addrlen);
4177 if (rc == windows.ws2_32.SOCKET_ERROR) {
4178 switch (windows.ws2_32.WSAGetLastError()) {
4179 .WSANOTINITIALISED => unreachable,
4180 .WSAENETDOWN => return error.NetworkSubsystemFailed,
4181 .WSAEFAULT => unreachable, // addr or addrlen have invalid pointers or addrlen points to an incorrect value
4182 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
4183 .WSAEINVAL => return error.SocketNotBound,
4184 else => |err| return windows.unexpectedWSAError(err),
4185 }
4186 }
4187 return;
4188 } else {
4189 const rc = system.getpeername(sock, addr, addrlen);
4190 switch (errno(rc)) {
4191 .SUCCESS => return,
4192 else => |err| return unexpectedErrno(err),
4193
4194 .BADF => unreachable, // always a race condition
4195 .FAULT => unreachable,
4196 .INVAL => unreachable, // invalid parameters
4197 .NOTSOCK => return error.FileDescriptorNotASocket,
4198 .NOBUFS => return error.SystemResources,
4199 }
4200 }
4201}
4202
4203pub const ConnectError = error{
4204 /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket
4205 /// file, or search permission is denied for one of the directories in the path prefix.
4206 /// or
4207 /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or
4208 /// the connection request failed because of a local firewall rule.
4209 PermissionDenied,
4210
4211 /// Local address is already in use.
4212 AddressInUse,
4213
4214 /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an
4215 /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers
4216 /// in the ephemeral port range are currently in use. See the discussion of
4217 /// /proc/sys/net/ipv4/ip_local_port_range in ip(7).
4218 AddressNotAvailable,
4219
4220 /// The passed address didn't have the correct address family in its sa_family field.
4221 AddressFamilyNotSupported,
4222
4223 /// Insufficient entries in the routing cache.
4224 SystemResources,
4225
4226 /// A connect() on a stream socket found no one listening on the remote address.
4227 ConnectionRefused,
4228
4229 /// Network is unreachable.
4230 NetworkUnreachable,
4231
4232 /// Timeout while attempting connection. The server may be too busy to accept new connections. Note
4233 /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
4234 ConnectionTimedOut,
4235
4236 /// This error occurs when no global event loop is configured,
4237 /// and connecting to the socket would block.
4238 WouldBlock,
4239
4240 /// The given path for the unix socket does not exist.
4241 FileNotFound,
4242
4243 /// Connection was reset by peer before connect could complete.
4244 ConnectionResetByPeer,
4245
4246 /// Socket is non-blocking and already has a pending connection in progress.
4247 ConnectionPending,
4248} || UnexpectedError;
4249
4250/// Initiate a connection on a socket.
4251/// If `sockfd` is opened in non blocking mode, the function will
4252/// return error.WouldBlock when EAGAIN or EINPROGRESS is received.
4253pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
4254 if (builtin.os.tag == .windows) {
4255 const rc = windows.ws2_32.connect(sock, sock_addr, @intCast(len));
4256 if (rc == 0) return;
4257 switch (windows.ws2_32.WSAGetLastError()) {
4258 .WSAEADDRINUSE => return error.AddressInUse,
4259 .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
4260 .WSAECONNREFUSED => return error.ConnectionRefused,
4261 .WSAECONNRESET => return error.ConnectionResetByPeer,
4262 .WSAETIMEDOUT => return error.ConnectionTimedOut,
4263 .WSAEHOSTUNREACH, // TODO: should we return NetworkUnreachable in this case as well?
4264 .WSAENETUNREACH,
4265 => return error.NetworkUnreachable,
4266 .WSAEFAULT => unreachable,
4267 .WSAEINVAL => unreachable,
4268 .WSAEISCONN => unreachable,
4269 .WSAENOTSOCK => unreachable,
4270 .WSAEWOULDBLOCK => return error.WouldBlock,
4271 .WSAEACCES => unreachable,
4272 .WSAENOBUFS => return error.SystemResources,
4273 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
4274 else => |err| return windows.unexpectedWSAError(err),
4275 }
4276 return;
4277 }
4278
4279 while (true) {
4280 switch (errno(system.connect(sock, sock_addr, len))) {
4281 .SUCCESS => return,
4282 .ACCES => return error.PermissionDenied,
4283 .PERM => return error.PermissionDenied,
4284 .ADDRINUSE => return error.AddressInUse,
4285 .ADDRNOTAVAIL => return error.AddressNotAvailable,
4286 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
4287 .AGAIN, .INPROGRESS => return error.WouldBlock,
4288 .ALREADY => return error.ConnectionPending,
4289 .BADF => unreachable, // sockfd is not a valid open file descriptor.
4290 .CONNREFUSED => return error.ConnectionRefused,
4291 .CONNRESET => return error.ConnectionResetByPeer,
4292 .FAULT => unreachable, // The socket structure address is outside the user's address space.
4293 .INTR => continue,
4294 .ISCONN => unreachable, // The socket is already connected.
4295 .HOSTUNREACH => return error.NetworkUnreachable,
4296 .NETUNREACH => return error.NetworkUnreachable,
4297 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
4298 .PROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
4299 .TIMEDOUT => return error.ConnectionTimedOut,
4300 .NOENT => return error.FileNotFound, // Returned when socket is AF.UNIX and the given path does not exist.
4301 .CONNABORTED => unreachable, // Tried to reuse socket that previously received error.ConnectionRefused.
4302 else => |err| return unexpectedErrno(err),
4303 }
4304 }
4305}
4306
4307pub fn getsockoptError(sockfd: fd_t) ConnectError!void {
4308 var err_code: i32 = undefined;
4309 var size: u32 = @sizeOf(u32);
4310 const rc = system.getsockopt(sockfd, SOL.SOCKET, SO.ERROR, @ptrCast(&err_code), &size);
4311 assert(size == 4);
4312 switch (errno(rc)) {
4313 .SUCCESS => switch (@as(E, @enumFromInt(err_code))) {
4314 .SUCCESS => return,
4315 .ACCES => return error.PermissionDenied,
4316 .PERM => return error.PermissionDenied,
4317 .ADDRINUSE => return error.AddressInUse,
4318 .ADDRNOTAVAIL => return error.AddressNotAvailable,
4319 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
4320 .AGAIN => return error.SystemResources,
4321 .ALREADY => return error.ConnectionPending,
4322 .BADF => unreachable, // sockfd is not a valid open file descriptor.
4323 .CONNREFUSED => return error.ConnectionRefused,
4324 .FAULT => unreachable, // The socket structure address is outside the user's address space.
4325 .ISCONN => unreachable, // The socket is already connected.
4326 .HOSTUNREACH => return error.NetworkUnreachable,
4327 .NETUNREACH => return error.NetworkUnreachable,
4328 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
4329 .PROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
4330 .TIMEDOUT => return error.ConnectionTimedOut,
4331 .CONNRESET => return error.ConnectionResetByPeer,
4332 else => |err| return unexpectedErrno(err),
4333 },
4334 .BADF => unreachable, // The argument sockfd is not a valid file descriptor.
4335 .FAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space.
4336 .INVAL => unreachable,
4337 .NOPROTOOPT => unreachable, // The option is unknown at the level indicated.
4338 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
4339 else => |err| return unexpectedErrno(err),
4340 }
4341}
4342
4343pub const WaitPidResult = struct {
4344 pid: pid_t,
4345 status: u32,
4346};
4347
4348/// Use this version of the `waitpid` wrapper if you spawned your child process using explicit
4349/// `fork` and `execve` method.
4350pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult {
4351 var status: if (builtin.link_libc) c_int else u32 = undefined;
4352 while (true) {
4353 const rc = system.waitpid(pid, &status, @intCast(flags));
4354 switch (errno(rc)) {
4355 .SUCCESS => return .{
4356 .pid = @intCast(rc),
4357 .status = @bitCast(status),
4358 },
4359 .INTR => continue,
4360 .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
4361 .INVAL => unreachable, // Invalid flags.
4362 else => unreachable,
4363 }
4364 }
4365}
4366
4367pub fn wait4(pid: pid_t, flags: u32, ru: ?*rusage) WaitPidResult {
4368 var status: if (builtin.link_libc) c_int else u32 = undefined;
4369 while (true) {
4370 const rc = system.wait4(pid, &status, @intCast(flags), ru);
4371 switch (errno(rc)) {
4372 .SUCCESS => return .{
4373 .pid = @intCast(rc),
4374 .status = @bitCast(status),
4375 },
4376 .INTR => continue,
4377 .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
4378 .INVAL => unreachable, // Invalid flags.
4379 else => unreachable,
4380 }
4381 }
4382}
4383
4384pub const FStatError = error{
4385 SystemResources,
4386
4387 /// In WASI, this error may occur when the file descriptor does
4388 /// not hold the required rights to get its filestat information.
4389 AccessDenied,
4390} || UnexpectedError;
4391
4392/// Return information about a file descriptor.
4393pub fn fstat(fd: fd_t) FStatError!Stat {
4394 if (builtin.os.tag == .wasi and !builtin.link_libc) {
4395 return Stat.fromFilestat(try fstat_wasi(fd));
4396 }
4397 if (builtin.os.tag == .windows) {
4398 @compileError("fstat is not yet implemented on Windows");
4399 }
4400
4401 const fstat_sym = if (lfs64_abi) system.fstat64 else system.fstat;
4402 var stat = mem.zeroes(Stat);
4403 switch (errno(fstat_sym(fd, &stat))) {
4404 .SUCCESS => return stat,
4405 .INVAL => unreachable,
4406 .BADF => unreachable, // Always a race condition.
4407 .NOMEM => return error.SystemResources,
4408 .ACCES => return error.AccessDenied,
4409 else => |err| return unexpectedErrno(err),
4410 }
4411}
4412
4413pub fn fstat_wasi(fd: fd_t) FStatError!wasi.filestat_t {
4414 var stat: wasi.filestat_t = undefined;
4415 switch (wasi.fd_filestat_get(fd, &stat)) {
4416 .SUCCESS => return stat,
4417 .INVAL => unreachable,
4418 .BADF => unreachable, // Always a race condition.
4419 .NOMEM => return error.SystemResources,
4420 .ACCES => return error.AccessDenied,
4421 .NOTCAPABLE => return error.AccessDenied,
4422 else => |err| return unexpectedErrno(err),
4423 }
4424}
4425
4426pub const FStatAtError = FStatError || error{
4427 NameTooLong,
4428 FileNotFound,
4429 SymLinkLoop,
4430 /// WASI-only; file paths must be valid UTF-8.
4431 InvalidUtf8,
4432};
4433
4434/// Similar to `fstat`, but returns stat of a resource pointed to by `pathname`
4435/// which is relative to `dirfd` handle.
4436/// On WASI, `pathname` should be encoded as valid UTF-8.
4437/// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding.
4438/// See also `fstatatZ` and `fstatat_wasi`.
4439pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
4440 if (builtin.os.tag == .wasi and !builtin.link_libc) {
4441 const filestat = try fstatat_wasi(dirfd, pathname, .{
4442 .SYMLINK_FOLLOW = (flags & AT.SYMLINK_NOFOLLOW) == 0,
4443 });
4444 return Stat.fromFilestat(filestat);
4445 } else if (builtin.os.tag == .windows) {
4446 @compileError("fstatat is not yet implemented on Windows");
4447 } else {
4448 const pathname_c = try toPosixPath(pathname);
4449 return fstatatZ(dirfd, &pathname_c, flags);
4450 }
4451}
4452
4453/// WASI-only. Same as `fstatat` but targeting WASI.
4454/// `pathname` should be encoded as valid UTF-8.
4455/// See also `fstatat`.
4456pub fn fstatat_wasi(dirfd: fd_t, pathname: []const u8, flags: wasi.lookupflags_t) FStatAtError!wasi.filestat_t {
4457 var stat: wasi.filestat_t = undefined;
4458 switch (wasi.path_filestat_get(dirfd, flags, pathname.ptr, pathname.len, &stat)) {
4459 .SUCCESS => return stat,
4460 .INVAL => unreachable,
4461 .BADF => unreachable, // Always a race condition.
4462 .NOMEM => return error.SystemResources,
4463 .ACCES => return error.AccessDenied,
4464 .FAULT => unreachable,
4465 .NAMETOOLONG => return error.NameTooLong,
4466 .NOENT => return error.FileNotFound,
4467 .NOTDIR => return error.FileNotFound,
4468 .NOTCAPABLE => return error.AccessDenied,
4469 .ILSEQ => return error.InvalidUtf8,
4470 else => |err| return unexpectedErrno(err),
4471 }
4472}
4473
4474/// Same as `fstatat` but `pathname` is null-terminated.
4475/// See also `fstatat`.
4476pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat {
4477 if (builtin.os.tag == .wasi and !builtin.link_libc) {
4478 const filestat = try fstatat_wasi(dirfd, mem.sliceTo(pathname, 0), .{
4479 .SYMLINK_FOLLOW = (flags & AT.SYMLINK_NOFOLLOW) == 0,
4480 });
4481 return Stat.fromFilestat(filestat);
4482 }
4483
4484 const fstatat_sym = if (lfs64_abi) system.fstatat64 else system.fstatat;
4485 var stat = mem.zeroes(Stat);
4486 switch (errno(fstatat_sym(dirfd, pathname, &stat, flags))) {
4487 .SUCCESS => return stat,
4488 .INVAL => unreachable,
4489 .BADF => unreachable, // Always a race condition.
4490 .NOMEM => return error.SystemResources,
4491 .ACCES => return error.AccessDenied,
4492 .PERM => return error.AccessDenied,
4493 .FAULT => unreachable,
4494 .NAMETOOLONG => return error.NameTooLong,
4495 .LOOP => return error.SymLinkLoop,
4496 .NOENT => return error.FileNotFound,
4497 .NOTDIR => return error.FileNotFound,
4498 .ILSEQ => |err| if (builtin.os.tag == .wasi)
4499 return error.InvalidUtf8
4500 else
4501 return unexpectedErrno(err),
4502 else => |err| return unexpectedErrno(err),
4503 }
4504}
4505
4506pub const KQueueError = error{
4507 /// The per-process limit on the number of open file descriptors has been reached.
4508 ProcessFdQuotaExceeded,
4509
4510 /// The system-wide limit on the total number of open files has been reached.
4511 SystemFdQuotaExceeded,
4512} || UnexpectedError;
4513
4514pub fn kqueue() KQueueError!i32 {
4515 const rc = system.kqueue();
4516 switch (errno(rc)) {
4517 .SUCCESS => return @intCast(rc),
4518 .MFILE => return error.ProcessFdQuotaExceeded,
4519 .NFILE => return error.SystemFdQuotaExceeded,
4520 else => |err| return unexpectedErrno(err),
4521 }
4522}
4523
4524pub const KEventError = error{
4525 /// The process does not have permission to register a filter.
4526 AccessDenied,
4527
4528 /// The event could not be found to be modified or deleted.
4529 EventNotFound,
4530
4531 /// No memory was available to register the event.
4532 SystemResources,
4533
4534 /// The specified process to attach to does not exist.
4535 ProcessNotFound,
4536
4537 /// changelist or eventlist had too many items on it.
4538 /// TODO remove this possibility
4539 Overflow,
4540};
4541
4542pub fn kevent(
4543 kq: i32,
4544 changelist: []const Kevent,
4545 eventlist: []Kevent,
4546 timeout: ?*const timespec,
4547) KEventError!usize {
4548 while (true) {
4549 const rc = system.kevent(
4550 kq,
4551 changelist.ptr,
4552 math.cast(c_int, changelist.len) orelse return error.Overflow,
4553 eventlist.ptr,
4554 math.cast(c_int, eventlist.len) orelse return error.Overflow,
4555 timeout,
4556 );
4557 switch (errno(rc)) {
4558 .SUCCESS => return @intCast(rc),
4559 .ACCES => return error.AccessDenied,
4560 .FAULT => unreachable,
4561 .BADF => unreachable, // Always a race condition.
4562 .INTR => continue,
4563 .INVAL => unreachable,
4564 .NOENT => return error.EventNotFound,
4565 .NOMEM => return error.SystemResources,
4566 .SRCH => return error.ProcessNotFound,
4567 else => unreachable,
4568 }
4569 }
4570}
4571
4572pub const INotifyInitError = error{
4573 ProcessFdQuotaExceeded,
4574 SystemFdQuotaExceeded,
4575 SystemResources,
4576} || UnexpectedError;
4577
4578/// initialize an inotify instance
4579pub fn inotify_init1(flags: u32) INotifyInitError!i32 {
4580 const rc = system.inotify_init1(flags);
4581 switch (errno(rc)) {
4582 .SUCCESS => return @intCast(rc),
4583 .INVAL => unreachable,
4584 .MFILE => return error.ProcessFdQuotaExceeded,
4585 .NFILE => return error.SystemFdQuotaExceeded,
4586 .NOMEM => return error.SystemResources,
4587 else => |err| return unexpectedErrno(err),
4588 }
4589}
4590
4591pub const INotifyAddWatchError = error{
4592 AccessDenied,
4593 NameTooLong,
4594 FileNotFound,
4595 SystemResources,
4596 UserResourceLimitReached,
4597 NotDir,
4598 WatchAlreadyExists,
4599} || UnexpectedError;
4600
4601/// add a watch to an initialized inotify instance
4602pub fn inotify_add_watch(inotify_fd: i32, pathname: []const u8, mask: u32) INotifyAddWatchError!i32 {
4603 const pathname_c = try toPosixPath(pathname);
4604 return inotify_add_watchZ(inotify_fd, &pathname_c, mask);
4605}
4606
4607/// Same as `inotify_add_watch` except pathname is null-terminated.
4608pub fn inotify_add_watchZ(inotify_fd: i32, pathname: [*:0]const u8, mask: u32) INotifyAddWatchError!i32 {
4609 const rc = system.inotify_add_watch(inotify_fd, pathname, mask);
4610 switch (errno(rc)) {
4611 .SUCCESS => return @intCast(rc),
4612 .ACCES => return error.AccessDenied,
4613 .BADF => unreachable,
4614 .FAULT => unreachable,
4615 .INVAL => unreachable,
4616 .NAMETOOLONG => return error.NameTooLong,
4617 .NOENT => return error.FileNotFound,
4618 .NOMEM => return error.SystemResources,
4619 .NOSPC => return error.UserResourceLimitReached,
4620 .NOTDIR => return error.NotDir,
4621 .EXIST => return error.WatchAlreadyExists,
4622 else => |err| return unexpectedErrno(err),
4623 }
4624}
4625
4626/// remove an existing watch from an inotify instance
4627pub fn inotify_rm_watch(inotify_fd: i32, wd: i32) void {
4628 switch (errno(system.inotify_rm_watch(inotify_fd, wd))) {
4629 .SUCCESS => return,
4630 .BADF => unreachable,
4631 .INVAL => unreachable,
4632 else => unreachable,
4633 }
4634}
4635
4636pub const FanotifyInitError = error{
4637 ProcessFdQuotaExceeded,
4638 SystemFdQuotaExceeded,
4639 SystemResources,
4640 OperationNotSupported,
4641 PermissionDenied,
4642} || UnexpectedError;
4643
4644pub fn fanotify_init(flags: u32, event_f_flags: u32) FanotifyInitError!i32 {
4645 const rc = system.fanotify_init(flags, event_f_flags);
4646 switch (errno(rc)) {
4647 .SUCCESS => return @intCast(rc),
4648 .INVAL => unreachable,
4649 .MFILE => return error.ProcessFdQuotaExceeded,
4650 .NFILE => return error.SystemFdQuotaExceeded,
4651 .NOMEM => return error.SystemResources,
4652 .NOSYS => return error.OperationNotSupported,
4653 .PERM => return error.PermissionDenied,
4654 else => |err| return unexpectedErrno(err),
4655 }
4656}
4657
4658pub const FanotifyMarkError = error{
4659 MarkAlreadyExists,
4660 IsDir,
4661 NotAssociatedWithFileSystem,
4662 FileNotFound,
4663 SystemResources,
4664 UserMarkQuotaExceeded,
4665 NotImplemented,
4666 NotDir,
4667 OperationNotSupported,
4668 PermissionDenied,
4669 NotSameFileSystem,
4670 NameTooLong,
4671} || UnexpectedError;
4672
4673pub fn fanotify_mark(fanotify_fd: i32, flags: u32, mask: u64, dirfd: i32, pathname: ?[]const u8) FanotifyMarkError!void {
4674 if (pathname) |path| {
4675 const path_c = try toPosixPath(path);
4676 return fanotify_markZ(fanotify_fd, flags, mask, dirfd, &path_c);
4677 }
4678
4679 return fanotify_markZ(fanotify_fd, flags, mask, dirfd, null);
4680}
4681
4682pub fn fanotify_markZ(fanotify_fd: i32, flags: u32, mask: u64, dirfd: i32, pathname: ?[*:0]const u8) FanotifyMarkError!void {
4683 const rc = system.fanotify_mark(fanotify_fd, flags, mask, dirfd, pathname);
4684 switch (errno(rc)) {
4685 .SUCCESS => return,
4686 .BADF => unreachable,
4687 .EXIST => return error.MarkAlreadyExists,
4688 .INVAL => unreachable,
4689 .ISDIR => return error.IsDir,
4690 .NODEV => return error.NotAssociatedWithFileSystem,
4691 .NOENT => return error.FileNotFound,
4692 .NOMEM => return error.SystemResources,
4693 .NOSPC => return error.UserMarkQuotaExceeded,
4694 .NOSYS => return error.NotImplemented,
4695 .NOTDIR => return error.NotDir,
4696 .OPNOTSUPP => return error.OperationNotSupported,
4697 .PERM => return error.PermissionDenied,
4698 .XDEV => return error.NotSameFileSystem,
4699 else => |err| return unexpectedErrno(err),
4700 }
4701}
4702
4703pub const MProtectError = error{
4704 /// The memory cannot be given the specified access. This can happen, for example, if you
4705 /// mmap(2) a file to which you have read-only access, then ask mprotect() to mark it
4706 /// PROT_WRITE.
4707 AccessDenied,
4708
4709 /// Changing the protection of a memory region would result in the total number of map‐
4710 /// pings with distinct attributes (e.g., read versus read/write protection) exceeding the
4711 /// allowed maximum. (For example, making the protection of a range PROT_READ in the mid‐
4712 /// dle of a region currently protected as PROT_READ|PROT_WRITE would result in three map‐
4713 /// pings: two read/write mappings at each end and a read-only mapping in the middle.)
4714 OutOfMemory,
4715} || UnexpectedError;
4716
4717/// `memory.len` must be page-aligned.
4718pub fn mprotect(memory: []align(mem.page_size) u8, protection: u32) MProtectError!void {
4719 assert(mem.isAligned(memory.len, mem.page_size));
4720 if (builtin.os.tag == .windows) {
4721 const win_prot: windows.DWORD = switch (@as(u3, @truncate(protection))) {
4722 0b000 => windows.PAGE_NOACCESS,
4723 0b001 => windows.PAGE_READONLY,
4724 0b010 => unreachable, // +w -r not allowed
4725 0b011 => windows.PAGE_READWRITE,
4726 0b100 => windows.PAGE_EXECUTE,
4727 0b101 => windows.PAGE_EXECUTE_READ,
4728 0b110 => unreachable, // +w -r not allowed
4729 0b111 => windows.PAGE_EXECUTE_READWRITE,
4730 };
4731 var old: windows.DWORD = undefined;
4732 windows.VirtualProtect(memory.ptr, memory.len, win_prot, &old) catch |err| switch (err) {
4733 error.InvalidAddress => return error.AccessDenied,
4734 error.Unexpected => return error.Unexpected,
4735 };
4736 } else {
4737 switch (errno(system.mprotect(memory.ptr, memory.len, protection))) {
4738 .SUCCESS => return,
4739 .INVAL => unreachable,
4740 .ACCES => return error.AccessDenied,
4741 .NOMEM => return error.OutOfMemory,
4742 else => |err| return unexpectedErrno(err),
4743 }
4744 }
4745}
4746
4747pub const ForkError = error{SystemResources} || UnexpectedError;
4748
4749pub fn fork() ForkError!pid_t {
4750 const rc = system.fork();
4751 switch (errno(rc)) {
4752 .SUCCESS => return @intCast(rc),
4753 .AGAIN => return error.SystemResources,
4754 .NOMEM => return error.SystemResources,
4755 else => |err| return unexpectedErrno(err),
4756 }
4757}
4758
4759pub const MMapError = error{
4760 /// The underlying filesystem of the specified file does not support memory mapping.
4761 MemoryMappingNotSupported,
4762
4763 /// A file descriptor refers to a non-regular file. Or a file mapping was requested,
4764 /// but the file descriptor is not open for reading. Or `MAP.SHARED` was requested
4765 /// and `PROT_WRITE` is set, but the file descriptor is not open in `RDWR` mode.
4766 /// Or `PROT_WRITE` is set, but the file is append-only.
4767 AccessDenied,
4768
4769 /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on
4770 /// a filesystem that was mounted no-exec.
4771 PermissionDenied,
4772 LockedMemoryLimitExceeded,
4773 ProcessFdQuotaExceeded,
4774 SystemFdQuotaExceeded,
4775 OutOfMemory,
4776} || UnexpectedError;
4777
4778/// Map files or devices into memory.
4779/// `length` does not need to be aligned.
4780/// Use of a mapped region can result in these signals:
4781/// * SIGSEGV - Attempted write into a region mapped as read-only.
4782/// * SIGBUS - Attempted access to a portion of the buffer that does not correspond to the file
4783pub fn mmap(
4784 ptr: ?[*]align(mem.page_size) u8,
4785 length: usize,
4786 prot: u32,
4787 flags: system.MAP,
4788 fd: fd_t,
4789 offset: u64,
4790) MMapError![]align(mem.page_size) u8 {
4791 const mmap_sym = if (lfs64_abi) system.mmap64 else system.mmap;
4792 const rc = mmap_sym(ptr, length, prot, @bitCast(flags), fd, @bitCast(offset));
4793 const err: E = if (builtin.link_libc) blk: {
4794 if (rc != std.c.MAP_FAILED) return @as([*]align(mem.page_size) u8, @ptrCast(@alignCast(rc)))[0..length];
4795 break :blk @enumFromInt(system._errno().*);
4796 } else blk: {
4797 const err = errno(rc);
4798 if (err == .SUCCESS) return @as([*]align(mem.page_size) u8, @ptrFromInt(rc))[0..length];
4799 break :blk err;
4800 };
4801 switch (err) {
4802 .SUCCESS => unreachable,
4803 .TXTBSY => return error.AccessDenied,
4804 .ACCES => return error.AccessDenied,
4805 .PERM => return error.PermissionDenied,
4806 .AGAIN => return error.LockedMemoryLimitExceeded,
4807 .BADF => unreachable, // Always a race condition.
4808 .OVERFLOW => unreachable, // The number of pages used for length + offset would overflow.
4809 .NODEV => return error.MemoryMappingNotSupported,
4810 .INVAL => unreachable, // Invalid parameters to mmap()
4811 .MFILE => return error.ProcessFdQuotaExceeded,
4812 .NFILE => return error.SystemFdQuotaExceeded,
4813 .NOMEM => return error.OutOfMemory,
4814 else => return unexpectedErrno(err),
4815 }
4816}
4817
4818/// Deletes the mappings for the specified address range, causing
4819/// further references to addresses within the range to generate invalid memory references.
4820/// Note that while POSIX allows unmapping a region in the middle of an existing mapping,
4821/// Zig's munmap function does not, for two reasons:
4822/// * It violates the Zig principle that resource deallocation must succeed.
4823/// * The Windows function, VirtualFree, has this restriction.
4824pub fn munmap(memory: []align(mem.page_size) const u8) void {
4825 switch (errno(system.munmap(memory.ptr, memory.len))) {
4826 .SUCCESS => return,
4827 .INVAL => unreachable, // Invalid parameters.
4828 .NOMEM => unreachable, // Attempted to unmap a region in the middle of an existing mapping.
4829 else => unreachable,
4830 }
4831}
4832
4833pub const MSyncError = error{
4834 UnmappedMemory,
4835} || UnexpectedError;
4836
4837pub fn msync(memory: []align(mem.page_size) u8, flags: i32) MSyncError!void {
4838 switch (errno(system.msync(memory.ptr, memory.len, flags))) {
4839 .SUCCESS => return,
4840 .NOMEM => return error.UnmappedMemory, // Unsuccessful, provided pointer does not point mapped memory
4841 .INVAL => unreachable, // Invalid parameters.
4842 else => unreachable,
4843 }
4844}
4845
4846pub const AccessError = error{
4847 PermissionDenied,
4848 FileNotFound,
4849 NameTooLong,
4850 InputOutput,
4851 SystemResources,
4852 BadPathName,
4853 FileBusy,
4854 SymLinkLoop,
4855 ReadOnlyFileSystem,
4856 /// WASI-only; file paths must be valid UTF-8.
4857 InvalidUtf8,
4858 /// Windows-only; file paths provided by the user must be valid WTF-8.
4859 /// https://simonsapin.github.io/wtf-8/
4860 InvalidWtf8,
4861} || UnexpectedError;
4862
4863/// check user's permissions for a file
4864/// On Windows, `path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
4865/// On WASI, `path` should be encoded as valid UTF-8.
4866/// On other platforms, `path` is an opaque sequence of bytes with no particular encoding.
4867/// TODO currently this assumes `mode` is `F.OK` on Windows.
4868pub fn access(path: []const u8, mode: u32) AccessError!void {
4869 if (builtin.os.tag == .windows) {
4870 const path_w = windows.sliceToPrefixedFileW(null, path) catch |err| switch (err) {
4871 error.AccessDenied => return error.PermissionDenied,
4872 else => |e| return e,
4873 };
4874 _ = try windows.GetFileAttributesW(path_w.span().ptr);
4875 return;
4876 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
4877 return faccessat(wasi.AT.FDCWD, path, mode, 0);
4878 }
4879 const path_c = try toPosixPath(path);
4880 return accessZ(&path_c, mode);
4881}
4882
4883/// Same as `access` except `path` is null-terminated.
4884pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void {
4885 if (builtin.os.tag == .windows) {
4886 const path_w = windows.cStrToPrefixedFileW(null, path) catch |err| switch (err) {
4887 error.AccessDenied => return error.PermissionDenied,
4888 else => |e| return e,
4889 };
4890 _ = try windows.GetFileAttributesW(path_w.span().ptr);
4891 return;
4892 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
4893 return access(mem.sliceTo(path, 0), mode);
4894 }
4895 switch (errno(system.access(path, mode))) {
4896 .SUCCESS => return,
4897 .ACCES => return error.PermissionDenied,
4898 .ROFS => return error.ReadOnlyFileSystem,
4899 .LOOP => return error.SymLinkLoop,
4900 .TXTBSY => return error.FileBusy,
4901 .NOTDIR => return error.FileNotFound,
4902 .NOENT => return error.FileNotFound,
4903 .NAMETOOLONG => return error.NameTooLong,
4904 .INVAL => unreachable,
4905 .FAULT => unreachable,
4906 .IO => return error.InputOutput,
4907 .NOMEM => return error.SystemResources,
4908 .ILSEQ => |err| if (builtin.os.tag == .wasi)
4909 return error.InvalidUtf8
4910 else
4911 return unexpectedErrno(err),
4912 else => |err| return unexpectedErrno(err),
4913 }
4914}
4915
4916/// Call from Windows-specific code if you already have a WTF-16LE encoded, null terminated string.
4917/// Otherwise use `access` or `accessZ`.
4918/// TODO currently this ignores `mode`.
4919pub fn accessW(path: [*:0]const u16, mode: u32) windows.GetFileAttributesError!void {
4920 _ = mode;
4921 const ret = try windows.GetFileAttributesW(path);
4922 if (ret != windows.INVALID_FILE_ATTRIBUTES) {
4923 return;
4924 }
4925 switch (windows.kernel32.GetLastError()) {
4926 .FILE_NOT_FOUND => return error.FileNotFound,
4927 .PATH_NOT_FOUND => return error.FileNotFound,
4928 .ACCESS_DENIED => return error.PermissionDenied,
4929 else => |err| return windows.unexpectedError(err),
4930 }
4931}
4932
4933/// Check user's permissions for a file, based on an open directory handle.
4934/// On Windows, `path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
4935/// On WASI, `path` should be encoded as valid UTF-8.
4936/// On other platforms, `path` is an opaque sequence of bytes with no particular encoding.
4937/// TODO currently this ignores `mode` and `flags` on Windows.
4938pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void {
4939 if (builtin.os.tag == .windows) {
4940 const path_w = try windows.sliceToPrefixedFileW(dirfd, path);
4941 return faccessatW(dirfd, path_w.span().ptr, mode, flags);
4942 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
4943 const resolved: RelativePathWasi = .{ .dir_fd = dirfd, .relative_path = path };
4944
4945 const st = blk: {
4946 break :blk fstatat_wasi(dirfd, path, .{
4947 .SYMLINK_FOLLOW = (flags & AT.SYMLINK_NOFOLLOW) == 0,
4948 });
4949 } catch |err| switch (err) {
4950 error.AccessDenied => return error.PermissionDenied,
4951 else => |e| return e,
4952 };
4953
4954 if (mode != F_OK) {
4955 var directory: wasi.fdstat_t = undefined;
4956 if (wasi.fd_fdstat_get(resolved.dir_fd, &directory) != .SUCCESS) {
4957 return error.PermissionDenied;
4958 }
4959
4960 var rights: wasi.rights_t = .{};
4961 if (mode & R_OK != 0) {
4962 if (st.filetype == .DIRECTORY) {
4963 rights.FD_READDIR = true;
4964 } else {
4965 rights.FD_READ = true;
4966 }
4967 }
4968 if (mode & W_OK != 0) {
4969 rights.FD_WRITE = true;
4970 }
4971 // No validation for X_OK
4972
4973 // https://github.com/ziglang/zig/issues/18882
4974 const rights_int: u64 = @bitCast(rights);
4975 const inheriting_int: u64 = @bitCast(directory.fs_rights_inheriting);
4976 if ((rights_int & inheriting_int) != rights_int) {
4977 return error.PermissionDenied;
4978 }
4979 }
4980 return;
4981 }
4982 const path_c = try toPosixPath(path);
4983 return faccessatZ(dirfd, &path_c, mode, flags);
4984}
4985
4986/// Same as `faccessat` except the path parameter is null-terminated.
4987pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void {
4988 if (builtin.os.tag == .windows) {
4989 const path_w = try windows.cStrToPrefixedFileW(dirfd, path);
4990 return faccessatW(dirfd, path_w.span().ptr, mode, flags);
4991 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
4992 return faccessat(dirfd, mem.sliceTo(path, 0), mode, flags);
4993 }
4994 switch (errno(system.faccessat(dirfd, path, mode, flags))) {
4995 .SUCCESS => return,
4996 .ACCES => return error.PermissionDenied,
4997 .ROFS => return error.ReadOnlyFileSystem,
4998 .LOOP => return error.SymLinkLoop,
4999 .TXTBSY => return error.FileBusy,
5000 .NOTDIR => return error.FileNotFound,
5001 .NOENT => return error.FileNotFound,
5002 .NAMETOOLONG => return error.NameTooLong,
5003 .INVAL => unreachable,
5004 .FAULT => unreachable,
5005 .IO => return error.InputOutput,
5006 .NOMEM => return error.SystemResources,
5007 .ILSEQ => |err| if (builtin.os.tag == .wasi)
5008 return error.InvalidUtf8
5009 else
5010 return unexpectedErrno(err),
5011 else => |err| return unexpectedErrno(err),
5012 }
5013}
5014
5015/// Same as `faccessat` except asserts the target is Windows and the path parameter
5016/// is NtDll-prefixed, null-terminated, WTF-16 encoded.
5017/// TODO currently this ignores `mode` and `flags`
5018pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32) AccessError!void {
5019 _ = mode;
5020 _ = flags;
5021 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
5022 return;
5023 }
5024 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
5025 return;
5026 }
5027
5028 const path_len_bytes = math.cast(u16, mem.sliceTo(sub_path_w, 0).len * 2) orelse return error.NameTooLong;
5029 var nt_name = windows.UNICODE_STRING{
5030 .Length = path_len_bytes,
5031 .MaximumLength = path_len_bytes,
5032 .Buffer = @constCast(sub_path_w),
5033 };
5034 var attr = windows.OBJECT_ATTRIBUTES{
5035 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
5036 .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd,
5037 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
5038 .ObjectName = &nt_name,
5039 .SecurityDescriptor = null,
5040 .SecurityQualityOfService = null,
5041 };
5042 var basic_info: windows.FILE_BASIC_INFORMATION = undefined;
5043 switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) {
5044 .SUCCESS => return,
5045 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
5046 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
5047 .OBJECT_NAME_INVALID => unreachable,
5048 .INVALID_PARAMETER => unreachable,
5049 .ACCESS_DENIED => return error.PermissionDenied,
5050 .OBJECT_PATH_SYNTAX_BAD => unreachable,
5051 else => |rc| return windows.unexpectedStatus(rc),
5052 }
5053}
5054
5055pub const PipeError = error{
5056 SystemFdQuotaExceeded,
5057 ProcessFdQuotaExceeded,
5058} || UnexpectedError;
5059
5060/// Creates a unidirectional data channel that can be used for interprocess communication.
5061pub fn pipe() PipeError![2]fd_t {
5062 var fds: [2]fd_t = undefined;
5063 switch (errno(system.pipe(&fds))) {
5064 .SUCCESS => return fds,
5065 .INVAL => unreachable, // Invalid parameters to pipe()
5066 .FAULT => unreachable, // Invalid fds pointer
5067 .NFILE => return error.SystemFdQuotaExceeded,
5068 .MFILE => return error.ProcessFdQuotaExceeded,
5069 else => |err| return unexpectedErrno(err),
5070 }
5071}
5072
5073pub fn pipe2(flags: O) PipeError![2]fd_t {
5074 if (@hasDecl(system, "pipe2")) {
5075 var fds: [2]fd_t = undefined;
5076 switch (errno(system.pipe2(&fds, flags))) {
5077 .SUCCESS => return fds,
5078 .INVAL => unreachable, // Invalid flags
5079 .FAULT => unreachable, // Invalid fds pointer
5080 .NFILE => return error.SystemFdQuotaExceeded,
5081 .MFILE => return error.ProcessFdQuotaExceeded,
5082 else => |err| return unexpectedErrno(err),
5083 }
5084 }
5085
5086 const fds: [2]fd_t = try pipe();
5087 errdefer {
5088 close(fds[0]);
5089 close(fds[1]);
5090 }
5091
5092 // https://github.com/ziglang/zig/issues/18882
5093 if (@as(u32, @bitCast(flags)) == 0)
5094 return fds;
5095
5096 // CLOEXEC is special, it's a file descriptor flag and must be set using
5097 // F.SETFD.
5098 if (flags.CLOEXEC) {
5099 for (fds) |fd| {
5100 switch (errno(system.fcntl(fd, F.SETFD, @as(u32, FD_CLOEXEC)))) {
5101 .SUCCESS => {},
5102 .INVAL => unreachable, // Invalid flags
5103 .BADF => unreachable, // Always a race condition
5104 else => |err| return unexpectedErrno(err),
5105 }
5106 }
5107 }
5108
5109 const new_flags: u32 = f: {
5110 var new_flags = flags;
5111 new_flags.CLOEXEC = false;
5112 break :f @bitCast(new_flags);
5113 };
5114 // Set every other flag affecting the file status using F.SETFL.
5115 if (new_flags != 0) {
5116 for (fds) |fd| {
5117 switch (errno(system.fcntl(fd, F.SETFL, new_flags))) {
5118 .SUCCESS => {},
5119 .INVAL => unreachable, // Invalid flags
5120 .BADF => unreachable, // Always a race condition
5121 else => |err| return unexpectedErrno(err),
5122 }
5123 }
5124 }
5125
5126 return fds;
5127}
5128
5129pub const SysCtlError = error{
5130 PermissionDenied,
5131 SystemResources,
5132 NameTooLong,
5133 UnknownName,
5134} || UnexpectedError;
5135
5136pub fn sysctl(
5137 name: []const c_int,
5138 oldp: ?*anyopaque,
5139 oldlenp: ?*usize,
5140 newp: ?*anyopaque,
5141 newlen: usize,
5142) SysCtlError!void {
5143 if (builtin.os.tag == .wasi) {
5144 @panic("unsupported"); // TODO should be compile error, not panic
5145 }
5146 if (builtin.os.tag == .haiku) {
5147 @panic("unsupported"); // TODO should be compile error, not panic
5148 }
5149
5150 const name_len = math.cast(c_uint, name.len) orelse return error.NameTooLong;
5151 switch (errno(system.sysctl(name.ptr, name_len, oldp, oldlenp, newp, newlen))) {
5152 .SUCCESS => return,
5153 .FAULT => unreachable,
5154 .PERM => return error.PermissionDenied,
5155 .NOMEM => return error.SystemResources,
5156 .NOENT => return error.UnknownName,
5157 else => |err| return unexpectedErrno(err),
5158 }
5159}
5160
5161pub fn sysctlbynameZ(
5162 name: [*:0]const u8,
5163 oldp: ?*anyopaque,
5164 oldlenp: ?*usize,
5165 newp: ?*anyopaque,
5166 newlen: usize,
5167) SysCtlError!void {
5168 if (builtin.os.tag == .wasi) {
5169 @panic("unsupported"); // TODO should be compile error, not panic
5170 }
5171 if (builtin.os.tag == .haiku) {
5172 @panic("unsupported"); // TODO should be compile error, not panic
5173 }
5174
5175 switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) {
5176 .SUCCESS => return,
5177 .FAULT => unreachable,
5178 .PERM => return error.PermissionDenied,
5179 .NOMEM => return error.SystemResources,
5180 .NOENT => return error.UnknownName,
5181 else => |err| return unexpectedErrno(err),
5182 }
5183}
5184
5185pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void {
5186 switch (errno(system.gettimeofday(tv, tz))) {
5187 .SUCCESS => return,
5188 .INVAL => unreachable,
5189 else => unreachable,
5190 }
5191}
5192
5193pub const SeekError = error{
5194 Unseekable,
5195
5196 /// In WASI, this error may occur when the file descriptor does
5197 /// not hold the required rights to seek on it.
5198 AccessDenied,
5199} || UnexpectedError;
5200
5201/// Repositions read/write file offset relative to the beginning.
5202pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
5203 if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
5204 var result: u64 = undefined;
5205 switch (errno(system.llseek(fd, offset, &result, SEEK.SET))) {
5206 .SUCCESS => return,
5207 .BADF => unreachable, // always a race condition
5208 .INVAL => return error.Unseekable,
5209 .OVERFLOW => return error.Unseekable,
5210 .SPIPE => return error.Unseekable,
5211 .NXIO => return error.Unseekable,
5212 else => |err| return unexpectedErrno(err),
5213 }
5214 }
5215 if (builtin.os.tag == .windows) {
5216 return windows.SetFilePointerEx_BEGIN(fd, offset);
5217 }
5218 if (builtin.os.tag == .wasi and !builtin.link_libc) {
5219 var new_offset: wasi.filesize_t = undefined;
5220 switch (wasi.fd_seek(fd, @bitCast(offset), .SET, &new_offset)) {
5221 .SUCCESS => return,
5222 .BADF => unreachable, // always a race condition
5223 .INVAL => return error.Unseekable,
5224 .OVERFLOW => return error.Unseekable,
5225 .SPIPE => return error.Unseekable,
5226 .NXIO => return error.Unseekable,
5227 .NOTCAPABLE => return error.AccessDenied,
5228 else => |err| return unexpectedErrno(err),
5229 }
5230 }
5231
5232 const lseek_sym = if (lfs64_abi) system.lseek64 else system.lseek;
5233 switch (errno(lseek_sym(fd, @bitCast(offset), SEEK.SET))) {
5234 .SUCCESS => return,
5235 .BADF => unreachable, // always a race condition
5236 .INVAL => return error.Unseekable,
5237 .OVERFLOW => return error.Unseekable,
5238 .SPIPE => return error.Unseekable,
5239 .NXIO => return error.Unseekable,
5240 else => |err| return unexpectedErrno(err),
5241 }
5242}
5243
5244/// Repositions read/write file offset relative to the current offset.
5245pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
5246 if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
5247 var result: u64 = undefined;
5248 switch (errno(system.llseek(fd, @bitCast(offset), &result, SEEK.CUR))) {
5249 .SUCCESS => return,
5250 .BADF => unreachable, // always a race condition
5251 .INVAL => return error.Unseekable,
5252 .OVERFLOW => return error.Unseekable,
5253 .SPIPE => return error.Unseekable,
5254 .NXIO => return error.Unseekable,
5255 else => |err| return unexpectedErrno(err),
5256 }
5257 }
5258 if (builtin.os.tag == .windows) {
5259 return windows.SetFilePointerEx_CURRENT(fd, offset);
5260 }
5261 if (builtin.os.tag == .wasi and !builtin.link_libc) {
5262 var new_offset: wasi.filesize_t = undefined;
5263 switch (wasi.fd_seek(fd, offset, .CUR, &new_offset)) {
5264 .SUCCESS => return,
5265 .BADF => unreachable, // always a race condition
5266 .INVAL => return error.Unseekable,
5267 .OVERFLOW => return error.Unseekable,
5268 .SPIPE => return error.Unseekable,
5269 .NXIO => return error.Unseekable,
5270 .NOTCAPABLE => return error.AccessDenied,
5271 else => |err| return unexpectedErrno(err),
5272 }
5273 }
5274 const lseek_sym = if (lfs64_abi) system.lseek64 else system.lseek;
5275 switch (errno(lseek_sym(fd, @bitCast(offset), SEEK.CUR))) {
5276 .SUCCESS => return,
5277 .BADF => unreachable, // always a race condition
5278 .INVAL => return error.Unseekable,
5279 .OVERFLOW => return error.Unseekable,
5280 .SPIPE => return error.Unseekable,
5281 .NXIO => return error.Unseekable,
5282 else => |err| return unexpectedErrno(err),
5283 }
5284}
5285
5286/// Repositions read/write file offset relative to the end.
5287pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
5288 if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
5289 var result: u64 = undefined;
5290 switch (errno(system.llseek(fd, @bitCast(offset), &result, SEEK.END))) {
5291 .SUCCESS => return,
5292 .BADF => unreachable, // always a race condition
5293 .INVAL => return error.Unseekable,
5294 .OVERFLOW => return error.Unseekable,
5295 .SPIPE => return error.Unseekable,
5296 .NXIO => return error.Unseekable,
5297 else => |err| return unexpectedErrno(err),
5298 }
5299 }
5300 if (builtin.os.tag == .windows) {
5301 return windows.SetFilePointerEx_END(fd, offset);
5302 }
5303 if (builtin.os.tag == .wasi and !builtin.link_libc) {
5304 var new_offset: wasi.filesize_t = undefined;
5305 switch (wasi.fd_seek(fd, offset, .END, &new_offset)) {
5306 .SUCCESS => return,
5307 .BADF => unreachable, // always a race condition
5308 .INVAL => return error.Unseekable,
5309 .OVERFLOW => return error.Unseekable,
5310 .SPIPE => return error.Unseekable,
5311 .NXIO => return error.Unseekable,
5312 .NOTCAPABLE => return error.AccessDenied,
5313 else => |err| return unexpectedErrno(err),
5314 }
5315 }
5316 const lseek_sym = if (lfs64_abi) system.lseek64 else system.lseek;
5317 switch (errno(lseek_sym(fd, @bitCast(offset), SEEK.END))) {
5318 .SUCCESS => return,
5319 .BADF => unreachable, // always a race condition
5320 .INVAL => return error.Unseekable,
5321 .OVERFLOW => return error.Unseekable,
5322 .SPIPE => return error.Unseekable,
5323 .NXIO => return error.Unseekable,
5324 else => |err| return unexpectedErrno(err),
5325 }
5326}
5327
5328/// Returns the read/write file offset relative to the beginning.
5329pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
5330 if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
5331 var result: u64 = undefined;
5332 switch (errno(system.llseek(fd, 0, &result, SEEK.CUR))) {
5333 .SUCCESS => return result,
5334 .BADF => unreachable, // always a race condition
5335 .INVAL => return error.Unseekable,
5336 .OVERFLOW => return error.Unseekable,
5337 .SPIPE => return error.Unseekable,
5338 .NXIO => return error.Unseekable,
5339 else => |err| return unexpectedErrno(err),
5340 }
5341 }
5342 if (builtin.os.tag == .windows) {
5343 return windows.SetFilePointerEx_CURRENT_get(fd);
5344 }
5345 if (builtin.os.tag == .wasi and !builtin.link_libc) {
5346 var new_offset: wasi.filesize_t = undefined;
5347 switch (wasi.fd_seek(fd, 0, .CUR, &new_offset)) {
5348 .SUCCESS => return new_offset,
5349 .BADF => unreachable, // always a race condition
5350 .INVAL => return error.Unseekable,
5351 .OVERFLOW => return error.Unseekable,
5352 .SPIPE => return error.Unseekable,
5353 .NXIO => return error.Unseekable,
5354 .NOTCAPABLE => return error.AccessDenied,
5355 else => |err| return unexpectedErrno(err),
5356 }
5357 }
5358 const lseek_sym = if (lfs64_abi) system.lseek64 else system.lseek;
5359 const rc = lseek_sym(fd, 0, SEEK.CUR);
5360 switch (errno(rc)) {
5361 .SUCCESS => return @bitCast(rc),
5362 .BADF => unreachable, // always a race condition
5363 .INVAL => return error.Unseekable,
5364 .OVERFLOW => return error.Unseekable,
5365 .SPIPE => return error.Unseekable,
5366 .NXIO => return error.Unseekable,
5367 else => |err| return unexpectedErrno(err),
5368 }
5369}
5370
5371pub const FcntlError = error{
5372 PermissionDenied,
5373 FileBusy,
5374 ProcessFdQuotaExceeded,
5375 Locked,
5376 DeadLock,
5377 LockedRegionLimitExceeded,
5378} || UnexpectedError;
5379
5380pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
5381 while (true) {
5382 const rc = system.fcntl(fd, cmd, arg);
5383 switch (errno(rc)) {
5384 .SUCCESS => return @intCast(rc),
5385 .INTR => continue,
5386 .AGAIN, .ACCES => return error.Locked,
5387 .BADF => unreachable,
5388 .BUSY => return error.FileBusy,
5389 .INVAL => unreachable, // invalid parameters
5390 .PERM => return error.PermissionDenied,
5391 .MFILE => return error.ProcessFdQuotaExceeded,
5392 .NOTDIR => unreachable, // invalid parameter
5393 .DEADLK => return error.DeadLock,
5394 .NOLCK => return error.LockedRegionLimitExceeded,
5395 else => |err| return unexpectedErrno(err),
5396 }
5397 }
5398}
5399
5400fn setSockFlags(sock: socket_t, flags: u32) !void {
5401 if ((flags & SOCK.CLOEXEC) != 0) {
5402 if (builtin.os.tag == .windows) {
5403 // TODO: Find out if this is supported for sockets
5404 } else {
5405 var fd_flags = fcntl(sock, F.GETFD, 0) catch |err| switch (err) {
5406 error.FileBusy => unreachable,
5407 error.Locked => unreachable,
5408 error.PermissionDenied => unreachable,
5409 error.DeadLock => unreachable,
5410 error.LockedRegionLimitExceeded => unreachable,
5411 else => |e| return e,
5412 };
5413 fd_flags |= FD_CLOEXEC;
5414 _ = fcntl(sock, F.SETFD, fd_flags) catch |err| switch (err) {
5415 error.FileBusy => unreachable,
5416 error.Locked => unreachable,
5417 error.PermissionDenied => unreachable,
5418 error.DeadLock => unreachable,
5419 error.LockedRegionLimitExceeded => unreachable,
5420 else => |e| return e,
5421 };
5422 }
5423 }
5424 if ((flags & SOCK.NONBLOCK) != 0) {
5425 if (builtin.os.tag == .windows) {
5426 var mode: c_ulong = 1;
5427 if (windows.ws2_32.ioctlsocket(sock, windows.ws2_32.FIONBIO, &mode) == windows.ws2_32.SOCKET_ERROR) {
5428 switch (windows.ws2_32.WSAGetLastError()) {
5429 .WSANOTINITIALISED => unreachable,
5430 .WSAENETDOWN => return error.NetworkSubsystemFailed,
5431 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
5432 // TODO: handle more errors
5433 else => |err| return windows.unexpectedWSAError(err),
5434 }
5435 }
5436 } else {
5437 var fl_flags = fcntl(sock, F.GETFL, 0) catch |err| switch (err) {
5438 error.FileBusy => unreachable,
5439 error.Locked => unreachable,
5440 error.PermissionDenied => unreachable,
5441 error.DeadLock => unreachable,
5442 error.LockedRegionLimitExceeded => unreachable,
5443 else => |e| return e,
5444 };
5445 fl_flags |= 1 << @bitOffsetOf(O, "NONBLOCK");
5446 _ = fcntl(sock, F.SETFL, fl_flags) catch |err| switch (err) {
5447 error.FileBusy => unreachable,
5448 error.Locked => unreachable,
5449 error.PermissionDenied => unreachable,
5450 error.DeadLock => unreachable,
5451 error.LockedRegionLimitExceeded => unreachable,
5452 else => |e| return e,
5453 };
5454 }
5455 }
5456}
5457
5458pub const FlockError = error{
5459 WouldBlock,
5460
5461 /// The kernel ran out of memory for allocating file locks
5462 SystemResources,
5463
5464 /// The underlying filesystem does not support file locks
5465 FileLocksNotSupported,
5466} || UnexpectedError;
5467
5468/// Depending on the operating system `flock` may or may not interact with
5469/// `fcntl` locks made by other processes.
5470pub fn flock(fd: fd_t, operation: i32) FlockError!void {
5471 while (true) {
5472 const rc = system.flock(fd, operation);
5473 switch (errno(rc)) {
5474 .SUCCESS => return,
5475 .BADF => unreachable,
5476 .INTR => continue,
5477 .INVAL => unreachable, // invalid parameters
5478 .NOLCK => return error.SystemResources,
5479 .AGAIN => return error.WouldBlock, // TODO: integrate with async instead of just returning an error
5480 .OPNOTSUPP => return error.FileLocksNotSupported,
5481 else => |err| return unexpectedErrno(err),
5482 }
5483 }
5484}
5485
5486pub const RealPathError = error{
5487 FileNotFound,
5488 AccessDenied,
5489 NameTooLong,
5490 NotSupported,
5491 NotDir,
5492 SymLinkLoop,
5493 InputOutput,
5494 FileTooBig,
5495 IsDir,
5496 ProcessFdQuotaExceeded,
5497 SystemFdQuotaExceeded,
5498 NoDevice,
5499 SystemResources,
5500 NoSpaceLeft,
5501 FileSystem,
5502 BadPathName,
5503 DeviceBusy,
5504
5505 SharingViolation,
5506 PipeBusy,
5507
5508 /// Windows-only; file paths provided by the user must be valid WTF-8.
5509 /// https://simonsapin.github.io/wtf-8/
5510 InvalidWtf8,
5511
5512 /// On Windows, `\\server` or `\\server\share` was not found.
5513 NetworkNotFound,
5514
5515 PathAlreadyExists,
5516
5517 /// On Windows, antivirus software is enabled by default. It can be
5518 /// disabled, but Windows Update sometimes ignores the user's preference
5519 /// and re-enables it. When enabled, antivirus software on Windows
5520 /// intercepts file system operations and makes them significantly slower
5521 /// in addition to possibly failing with this error code.
5522 AntivirusInterference,
5523
5524 /// On Windows, the volume does not contain a recognized file system. File
5525 /// system drivers might not be loaded, or the volume may be corrupt.
5526 UnrecognizedVolume,
5527} || UnexpectedError;
5528
5529/// Return the canonicalized absolute pathname.
5530/// Expands all symbolic links and resolves references to `.`, `..`, and
5531/// extra `/` characters in `pathname`.
5532/// On Windows, `pathname` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
5533/// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding.
5534/// The return value is a slice of `out_buffer`, but not necessarily from the beginning.
5535/// See also `realpathZ` and `realpathW`.
5536/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
5537/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
5538/// Calling this function is usually a bug.
5539pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
5540 if (builtin.os.tag == .windows) {
5541 const pathname_w = try windows.sliceToPrefixedFileW(null, pathname);
5542 return realpathW(pathname_w.span(), out_buffer);
5543 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
5544 @compileError("WASI does not support os.realpath");
5545 }
5546 const pathname_c = try toPosixPath(pathname);
5547 return realpathZ(&pathname_c, out_buffer);
5548}
5549
5550/// Same as `realpath` except `pathname` is null-terminated.
5551/// Calling this function is usually a bug.
5552pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
5553 if (builtin.os.tag == .windows) {
5554 const pathname_w = try windows.cStrToPrefixedFileW(null, pathname);
5555 return realpathW(pathname_w.span(), out_buffer);
5556 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
5557 return realpath(mem.sliceTo(pathname, 0), out_buffer);
5558 }
5559 if (!builtin.link_libc) {
5560 const flags: O = switch (builtin.os.tag) {
5561 .linux => .{
5562 .NONBLOCK = true,
5563 .CLOEXEC = true,
5564 .PATH = true,
5565 },
5566 else => .{
5567 .NONBLOCK = true,
5568 .CLOEXEC = true,
5569 },
5570 };
5571 const fd = openZ(pathname, flags, 0) catch |err| switch (err) {
5572 error.FileLocksNotSupported => unreachable,
5573 error.WouldBlock => unreachable,
5574 error.FileBusy => unreachable, // not asking for write permissions
5575 error.InvalidUtf8 => unreachable, // WASI-only
5576 else => |e| return e,
5577 };
5578 defer close(fd);
5579
5580 return getFdPath(fd, out_buffer);
5581 }
5582 const result_path = std.c.realpath(pathname, out_buffer) orelse switch (@as(E, @enumFromInt(std.c._errno().*))) {
5583 .SUCCESS => unreachable,
5584 .INVAL => unreachable,
5585 .BADF => unreachable,
5586 .FAULT => unreachable,
5587 .ACCES => return error.AccessDenied,
5588 .NOENT => return error.FileNotFound,
5589 .OPNOTSUPP => return error.NotSupported,
5590 .NOTDIR => return error.NotDir,
5591 .NAMETOOLONG => return error.NameTooLong,
5592 .LOOP => return error.SymLinkLoop,
5593 .IO => return error.InputOutput,
5594 else => |err| return unexpectedErrno(err),
5595 };
5596 return mem.sliceTo(result_path, 0);
5597}
5598
5599/// Same as `realpath` except `pathname` is WTF16LE-encoded.
5600/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
5601/// Calling this function is usually a bug.
5602pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
5603 const w = windows;
5604
5605 const dir = std.fs.cwd().fd;
5606 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
5607 const share_access = w.FILE_SHARE_READ;
5608 const creation = w.FILE_OPEN;
5609 const h_file = blk: {
5610 const res = w.OpenFile(pathname, .{
5611 .dir = dir,
5612 .access_mask = access_mask,
5613 .share_access = share_access,
5614 .creation = creation,
5615 .filter = .any,
5616 }) catch |err| switch (err) {
5617 error.WouldBlock => unreachable,
5618 else => |e| return e,
5619 };
5620 break :blk res;
5621 };
5622 defer w.CloseHandle(h_file);
5623
5624 return getFdPath(h_file, out_buffer);
5625}
5626
5627pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool {
5628 return switch (os.tag) {
5629 .windows,
5630 .macos,
5631 .ios,
5632 .watchos,
5633 .tvos,
5634 .linux,
5635 .solaris,
5636 .illumos,
5637 .freebsd,
5638 => true,
5639
5640 .dragonfly => os.version_range.semver.max.order(.{ .major = 6, .minor = 0, .patch = 0 }) != .lt,
5641 .netbsd => os.version_range.semver.max.order(.{ .major = 10, .minor = 0, .patch = 0 }) != .lt,
5642 else => false,
5643 };
5644}
5645
5646/// Return canonical path of handle `fd`.
5647/// This function is very host-specific and is not universally supported by all hosts.
5648/// For example, while it generally works on Linux, macOS, FreeBSD or Windows, it is
5649/// unsupported on WASI.
5650/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
5651/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
5652/// Calling this function is usually a bug.
5653pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
5654 if (!comptime isGetFdPathSupportedOnTarget(builtin.os)) {
5655 @compileError("querying for canonical path of a handle is unsupported on this host");
5656 }
5657 switch (builtin.os.tag) {
5658 .windows => {
5659 var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
5660 const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]);
5661
5662 const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
5663 return out_buffer[0..end_index];
5664 },
5665 .macos, .ios, .watchos, .tvos => {
5666 // On macOS, we can use F.GETPATH fcntl command to query the OS for
5667 // the path to the file descriptor.
5668 @memset(out_buffer[0..MAX_PATH_BYTES], 0);
5669 switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {
5670 .SUCCESS => {},
5671 .BADF => return error.FileNotFound,
5672 .NOSPC => return error.NameTooLong,
5673 // TODO man pages for fcntl on macOS don't really tell you what
5674 // errno values to expect when command is F.GETPATH...
5675 else => |err| return unexpectedErrno(err),
5676 }
5677 const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse MAX_PATH_BYTES;
5678 return out_buffer[0..len];
5679 },
5680 .linux => {
5681 var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
5682 const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/fd/{d}", .{fd}) catch unreachable;
5683
5684 const target = readlinkZ(proc_path, out_buffer) catch |err| {
5685 switch (err) {
5686 error.NotLink => unreachable,
5687 error.BadPathName => unreachable,
5688 error.InvalidUtf8 => unreachable, // WASI-only
5689 error.InvalidWtf8 => unreachable, // Windows-only
5690 error.UnsupportedReparsePointType => unreachable, // Windows-only
5691 error.NetworkNotFound => unreachable, // Windows-only
5692 else => |e| return e,
5693 }
5694 };
5695 return target;
5696 },
5697 .solaris, .illumos => {
5698 var procfs_buf: ["/proc/self/path/-2147483648\x00".len]u8 = undefined;
5699 const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/path/{d}", .{fd}) catch unreachable;
5700
5701 const target = readlinkZ(proc_path, out_buffer) catch |err| switch (err) {
5702 error.UnsupportedReparsePointType => unreachable,
5703 error.NotLink => unreachable,
5704 else => |e| return e,
5705 };
5706 return target;
5707 },
5708 .freebsd => {
5709 if (comptime builtin.os.isAtLeast(.freebsd, .{ .major = 13, .minor = 0, .patch = 0 }) orelse false) {
5710 var kfile: system.kinfo_file = undefined;
5711 kfile.structsize = system.KINFO_FILE_SIZE;
5712 switch (errno(system.fcntl(fd, system.F.KINFO, @intFromPtr(&kfile)))) {
5713 .SUCCESS => {},
5714 .BADF => return error.FileNotFound,
5715 else => |err| return unexpectedErrno(err),
5716 }
5717 const len = mem.indexOfScalar(u8, &kfile.path, 0) orelse MAX_PATH_BYTES;
5718 if (len == 0) return error.NameTooLong;
5719 const result = out_buffer[0..len];
5720 @memcpy(result, kfile.path[0..len]);
5721 return result;
5722 } else {
5723 // This fallback implementation reimplements libutil's `kinfo_getfile()`.
5724 // The motivation is to avoid linking -lutil when building zig or general
5725 // user executables.
5726 var mib = [4]c_int{ CTL.KERN, KERN.PROC, KERN.PROC_FILEDESC, system.getpid() };
5727 var len: usize = undefined;
5728 sysctl(&mib, null, &len, null, 0) catch |err| switch (err) {
5729 error.PermissionDenied => unreachable,
5730 error.SystemResources => return error.SystemResources,
5731 error.NameTooLong => unreachable,
5732 error.UnknownName => unreachable,
5733 else => return error.Unexpected,
5734 };
5735 len = len * 4 / 3;
5736 const buf = std.heap.c_allocator.alloc(u8, len) catch return error.SystemResources;
5737 defer std.heap.c_allocator.free(buf);
5738 len = buf.len;
5739 sysctl(&mib, &buf[0], &len, null, 0) catch |err| switch (err) {
5740 error.PermissionDenied => unreachable,
5741 error.SystemResources => return error.SystemResources,
5742 error.NameTooLong => unreachable,
5743 error.UnknownName => unreachable,
5744 else => return error.Unexpected,
5745 };
5746 var i: usize = 0;
5747 while (i < len) {
5748 const kf: *align(1) system.kinfo_file = @ptrCast(&buf[i]);
5749 if (kf.fd == fd) {
5750 len = mem.indexOfScalar(u8, &kf.path, 0) orelse MAX_PATH_BYTES;
5751 if (len == 0) return error.NameTooLong;
5752 const result = out_buffer[0..len];
5753 @memcpy(result, kf.path[0..len]);
5754 return result;
5755 }
5756 i += @intCast(kf.structsize);
5757 }
5758 return error.FileNotFound;
5759 }
5760 },
5761 .dragonfly => {
5762 @memset(out_buffer[0..MAX_PATH_BYTES], 0);
5763 switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {
5764 .SUCCESS => {},
5765 .BADF => return error.FileNotFound,
5766 .RANGE => return error.NameTooLong,
5767 else => |err| return unexpectedErrno(err),
5768 }
5769 const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse MAX_PATH_BYTES;
5770 return out_buffer[0..len];
5771 },
5772 .netbsd => {
5773 @memset(out_buffer[0..MAX_PATH_BYTES], 0);
5774 switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {
5775 .SUCCESS => {},
5776 .ACCES => return error.AccessDenied,
5777 .BADF => return error.FileNotFound,
5778 .NOENT => return error.FileNotFound,
5779 .NOMEM => return error.SystemResources,
5780 .RANGE => return error.NameTooLong,
5781 else => |err| return unexpectedErrno(err),
5782 }
5783 const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse MAX_PATH_BYTES;
5784 return out_buffer[0..len];
5785 },
5786 else => unreachable, // made unreachable by isGetFdPathSupportedOnTarget above
5787 }
5788}
5789
5790/// Spurious wakeups are possible and no precision of timing is guaranteed.
5791pub fn nanosleep(seconds: u64, nanoseconds: u64) void {
5792 var req = timespec{
5793 .tv_sec = math.cast(isize, seconds) orelse math.maxInt(isize),
5794 .tv_nsec = math.cast(isize, nanoseconds) orelse math.maxInt(isize),
5795 };
5796 var rem: timespec = undefined;
5797 while (true) {
5798 switch (errno(system.nanosleep(&req, &rem))) {
5799 .FAULT => unreachable,
5800 .INVAL => {
5801 // Sometimes Darwin returns EINVAL for no reason.
5802 // We treat it as a spurious wakeup.
5803 return;
5804 },
5805 .INTR => {
5806 req = rem;
5807 continue;
5808 },
5809 // This prong handles success as well as unexpected errors.
5810 else => return,
5811 }
5812 }
5813}
5814
5815pub fn dl_iterate_phdr(
5816 context: anytype,
5817 comptime Error: type,
5818 comptime callback: fn (info: *dl_phdr_info, size: usize, context: @TypeOf(context)) Error!void,
5819) Error!void {
5820 const Context = @TypeOf(context);
5821
5822 switch (builtin.object_format) {
5823 .elf, .c => {},
5824 else => @compileError("dl_iterate_phdr is not available for this target"),
5825 }
5826
5827 if (builtin.link_libc) {
5828 switch (system.dl_iterate_phdr(struct {
5829 fn callbackC(info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.C) c_int {
5830 const context_ptr: *const Context = @ptrCast(@alignCast(data));
5831 callback(info, size, context_ptr.*) catch |err| return @intFromError(err);
5832 return 0;
5833 }
5834 }.callbackC, @ptrCast(@constCast(&context)))) {
5835 0 => return,
5836 else => |err| return @as(Error, @errorCast(@errorFromInt(@as(std.meta.Int(.unsigned, @bitSizeOf(anyerror)), @intCast(err))))),
5837 }
5838 }
5839
5840 const elf_base = std.process.getBaseAddress();
5841 const ehdr: *elf.Ehdr = @ptrFromInt(elf_base);
5842 // Make sure the base address points to an ELF image.
5843 assert(mem.eql(u8, ehdr.e_ident[0..4], elf.MAGIC));
5844 const n_phdr = ehdr.e_phnum;
5845 const phdrs = (@as([*]elf.Phdr, @ptrFromInt(elf_base + ehdr.e_phoff)))[0..n_phdr];
5846
5847 var it = dl.linkmap_iterator(phdrs) catch unreachable;
5848
5849 // The executable has no dynamic link segment, create a single entry for
5850 // the whole ELF image.
5851 if (it.end()) {
5852 // Find the base address for the ELF image, if this is a PIE the value
5853 // is non-zero.
5854 const base_address = for (phdrs) |*phdr| {
5855 if (phdr.p_type == elf.PT_PHDR) {
5856 break @intFromPtr(phdrs.ptr) - phdr.p_vaddr;
5857 // We could try computing the difference between _DYNAMIC and
5858 // the p_vaddr of the PT_DYNAMIC section, but using the phdr is
5859 // good enough (Is it?).
5860 }
5861 } else unreachable;
5862
5863 var info = dl_phdr_info{
5864 .dlpi_addr = base_address,
5865 .dlpi_name = "/proc/self/exe",
5866 .dlpi_phdr = phdrs.ptr,
5867 .dlpi_phnum = ehdr.e_phnum,
5868 };
5869
5870 return callback(&info, @sizeOf(dl_phdr_info), context);
5871 }
5872
5873 // Last return value from the callback function.
5874 while (it.next()) |entry| {
5875 var dlpi_phdr: [*]elf.Phdr = undefined;
5876 var dlpi_phnum: u16 = undefined;
5877
5878 if (entry.l_addr != 0) {
5879 const elf_header: *elf.Ehdr = @ptrFromInt(entry.l_addr);
5880 dlpi_phdr = @ptrFromInt(entry.l_addr + elf_header.e_phoff);
5881 dlpi_phnum = elf_header.e_phnum;
5882 } else {
5883 // This is the running ELF image
5884 dlpi_phdr = @ptrFromInt(elf_base + ehdr.e_phoff);
5885 dlpi_phnum = ehdr.e_phnum;
5886 }
5887
5888 var info = dl_phdr_info{
5889 .dlpi_addr = entry.l_addr,
5890 .dlpi_name = entry.l_name,
5891 .dlpi_phdr = dlpi_phdr,
5892 .dlpi_phnum = dlpi_phnum,
5893 };
5894
5895 try callback(&info, @sizeOf(dl_phdr_info), context);
5896 }
5897}
5898
5899pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError;
5900
5901/// TODO: change this to return the timespec as a return value
5902/// TODO: look into making clk_id an enum
5903pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
5904 if (builtin.os.tag == .wasi and !builtin.link_libc) {
5905 var ts: timestamp_t = undefined;
5906 switch (system.clock_time_get(@bitCast(clk_id), 1, &ts)) {
5907 .SUCCESS => {
5908 tp.* = .{
5909 .tv_sec = @intCast(ts / std.time.ns_per_s),
5910 .tv_nsec = @intCast(ts % std.time.ns_per_s),
5911 };
5912 },
5913 .INVAL => return error.UnsupportedClock,
5914 else => |err| return unexpectedErrno(err),
5915 }
5916 return;
5917 }
5918 if (builtin.os.tag == .windows) {
5919 if (clk_id == CLOCK.REALTIME) {
5920 var ft: windows.FILETIME = undefined;
5921 windows.kernel32.GetSystemTimeAsFileTime(&ft);
5922 // FileTime has a granularity of 100 nanoseconds and uses the NTFS/Windows epoch.
5923 const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
5924 const ft_per_s = std.time.ns_per_s / 100;
5925 tp.* = .{
5926 .tv_sec = @as(i64, @intCast(ft64 / ft_per_s)) + std.time.epoch.windows,
5927 .tv_nsec = @as(c_long, @intCast(ft64 % ft_per_s)) * 100,
5928 };
5929 return;
5930 } else {
5931 // TODO POSIX implementation of CLOCK.MONOTONIC on Windows.
5932 return error.UnsupportedClock;
5933 }
5934 }
5935
5936 switch (errno(system.clock_gettime(clk_id, tp))) {
5937 .SUCCESS => return,
5938 .FAULT => unreachable,
5939 .INVAL => return error.UnsupportedClock,
5940 else => |err| return unexpectedErrno(err),
5941 }
5942}
5943
5944pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void {
5945 if (builtin.os.tag == .wasi and !builtin.link_libc) {
5946 var ts: timestamp_t = undefined;
5947 switch (system.clock_res_get(@bitCast(clk_id), &ts)) {
5948 .SUCCESS => res.* = .{
5949 .tv_sec = @intCast(ts / std.time.ns_per_s),
5950 .tv_nsec = @intCast(ts % std.time.ns_per_s),
5951 },
5952 .INVAL => return error.UnsupportedClock,
5953 else => |err| return unexpectedErrno(err),
5954 }
5955 return;
5956 }
5957
5958 switch (errno(system.clock_getres(clk_id, res))) {
5959 .SUCCESS => return,
5960 .FAULT => unreachable,
5961 .INVAL => return error.UnsupportedClock,
5962 else => |err| return unexpectedErrno(err),
5963 }
5964}
5965
5966pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError;
5967
5968pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t {
5969 var set: cpu_set_t = undefined;
5970 switch (errno(system.sched_getaffinity(pid, @sizeOf(cpu_set_t), &set))) {
5971 .SUCCESS => return set,
5972 .FAULT => unreachable,
5973 .INVAL => unreachable,
5974 .SRCH => unreachable,
5975 .PERM => return error.PermissionDenied,
5976 else => |err| return unexpectedErrno(err),
5977 }
5978}
5979
5980/// Used to convert a slice to a null terminated slice on the stack.
5981/// TODO https://github.com/ziglang/zig/issues/287
5982pub fn toPosixPath(file_path: []const u8) error{NameTooLong}![MAX_PATH_BYTES - 1:0]u8 {
5983 if (std.debug.runtime_safety) assert(std.mem.indexOfScalar(u8, file_path, 0) == null);
5984 var path_with_null: [MAX_PATH_BYTES - 1:0]u8 = undefined;
5985 // >= rather than > to make room for the null byte
5986 if (file_path.len >= MAX_PATH_BYTES) return error.NameTooLong;
5987 @memcpy(path_with_null[0..file_path.len], file_path);
5988 path_with_null[file_path.len] = 0;
5989 return path_with_null;
5990}
5991
5992/// Whether or not error.Unexpected will print its value and a stack trace.
5993/// if this happens the fix is to add the error code to the corresponding
5994/// switch expression, possibly introduce a new error in the error set, and
5995/// send a patch to Zig.
5996pub const unexpected_error_tracing = builtin.zig_backend == .stage2_llvm and builtin.mode == .Debug;
5997
5998pub const UnexpectedError = error{
5999 /// The Operating System returned an undocumented error code.
6000 /// This error is in theory not possible, but it would be better
6001 /// to handle this error than to invoke undefined behavior.
6002 Unexpected,
6003};
6004
6005/// Call this when you made a syscall or something that sets errno
6006/// and you get an unexpected error.
6007pub fn unexpectedErrno(err: E) UnexpectedError {
6008 if (unexpected_error_tracing) {
6009 std.debug.print("unexpected errno: {d}\n", .{@intFromEnum(err)});
6010 std.debug.dumpCurrentStackTrace(null);
6011 }
6012 return error.Unexpected;
6013}
6014
6015pub const SigaltstackError = error{
6016 /// The supplied stack size was less than MINSIGSTKSZ.
6017 SizeTooSmall,
6018
6019 /// Attempted to change the signal stack while it was active.
6020 PermissionDenied,
6021} || UnexpectedError;
6022
6023pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void {
6024 switch (errno(system.sigaltstack(ss, old_ss))) {
6025 .SUCCESS => return,
6026 .FAULT => unreachable,
6027 .INVAL => unreachable,
6028 .NOMEM => return error.SizeTooSmall,
6029 .PERM => return error.PermissionDenied,
6030 else => |err| return unexpectedErrno(err),
6031 }
6032}
6033
6034/// Examine and change a signal action.
6035pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) error{OperationNotSupported}!void {
6036 switch (errno(system.sigaction(sig, act, oact))) {
6037 .SUCCESS => return,
6038 .INVAL, .NOSYS => return error.OperationNotSupported,
6039 else => unreachable,
6040 }
6041}
6042
6043/// Sets the thread signal mask.
6044pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*sigset_t) void {
6045 switch (errno(system.sigprocmask(@bitCast(flags), set, oldset))) {
6046 .SUCCESS => return,
6047 .FAULT => unreachable,
6048 .INVAL => unreachable,
6049 else => unreachable,
6050 }
6051}
6052
6053pub const FutimensError = error{
6054 /// times is NULL, or both tv_nsec values are UTIME_NOW, and either:
6055 /// * the effective user ID of the caller does not match the owner
6056 /// of the file, the caller does not have write access to the
6057 /// file, and the caller is not privileged (Linux: does not have
6058 /// either the CAP_FOWNER or the CAP_DAC_OVERRIDE capability);
6059 /// or,
6060 /// * the file is marked immutable (see chattr(1)).
6061 AccessDenied,
6062
6063 /// The caller attempted to change one or both timestamps to a value
6064 /// other than the current time, or to change one of the timestamps
6065 /// to the current time while leaving the other timestamp unchanged,
6066 /// (i.e., times is not NULL, neither tv_nsec field is UTIME_NOW,
6067 /// and neither tv_nsec field is UTIME_OMIT) and either:
6068 /// * the caller's effective user ID does not match the owner of
6069 /// file, and the caller is not privileged (Linux: does not have
6070 /// the CAP_FOWNER capability); or,
6071 /// * the file is marked append-only or immutable (see chattr(1)).
6072 PermissionDenied,
6073
6074 ReadOnlyFileSystem,
6075} || UnexpectedError;
6076
6077pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void {
6078 if (builtin.os.tag == .wasi and !builtin.link_libc) {
6079 // TODO WASI encodes `wasi.fstflags` to signify magic values
6080 // similar to UTIME_NOW and UTIME_OMIT. Currently, we ignore
6081 // this here, but we should really handle it somehow.
6082 const atim = times[0].toTimestamp();
6083 const mtim = times[1].toTimestamp();
6084 switch (wasi.fd_filestat_set_times(fd, atim, mtim, .{
6085 .ATIM = true,
6086 .MTIM = true,
6087 })) {
6088 .SUCCESS => return,
6089 .ACCES => return error.AccessDenied,
6090 .PERM => return error.PermissionDenied,
6091 .BADF => unreachable, // always a race condition
6092 .FAULT => unreachable,
6093 .INVAL => unreachable,
6094 .ROFS => return error.ReadOnlyFileSystem,
6095 else => |err| return unexpectedErrno(err),
6096 }
6097 }
6098
6099 switch (errno(system.futimens(fd, times))) {
6100 .SUCCESS => return,
6101 .ACCES => return error.AccessDenied,
6102 .PERM => return error.PermissionDenied,
6103 .BADF => unreachable, // always a race condition
6104 .FAULT => unreachable,
6105 .INVAL => unreachable,
6106 .ROFS => return error.ReadOnlyFileSystem,
6107 else => |err| return unexpectedErrno(err),
6108 }
6109}
6110
6111pub const GetHostNameError = error{PermissionDenied} || UnexpectedError;
6112
6113pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
6114 if (builtin.link_libc) {
6115 switch (errno(system.gethostname(name_buffer, name_buffer.len))) {
6116 .SUCCESS => return mem.sliceTo(name_buffer, 0),
6117 .FAULT => unreachable,
6118 .NAMETOOLONG => unreachable, // HOST_NAME_MAX prevents this
6119 .PERM => return error.PermissionDenied,
6120 else => |err| return unexpectedErrno(err),
6121 }
6122 }
6123 if (builtin.os.tag == .linux) {
6124 const uts = uname();
6125 const hostname = mem.sliceTo(&uts.nodename, 0);
6126 const result = name_buffer[0..hostname.len];
6127 @memcpy(result, hostname);
6128 return result;
6129 }
6130
6131 @compileError("TODO implement gethostname for this OS");
6132}
6133
6134pub fn uname() utsname {
6135 var uts: utsname = undefined;
6136 switch (errno(system.uname(&uts))) {
6137 .SUCCESS => return uts,
6138 .FAULT => unreachable,
6139 else => unreachable,
6140 }
6141}
6142
6143pub fn res_mkquery(
6144 op: u4,
6145 dname: []const u8,
6146 class: u8,
6147 ty: u8,
6148 data: []const u8,
6149 newrr: ?[*]const u8,
6150 buf: []u8,
6151) usize {
6152 _ = data;
6153 _ = newrr;
6154 // This implementation is ported from musl libc.
6155 // A more idiomatic "ziggy" implementation would be welcome.
6156 var name = dname;
6157 if (mem.endsWith(u8, name, ".")) name.len -= 1;
6158 assert(name.len <= 253);
6159 const n = 17 + name.len + @intFromBool(name.len != 0);
6160
6161 // Construct query template - ID will be filled later
6162 var q: [280]u8 = undefined;
6163 @memset(q[0..n], 0);
6164 q[2] = @as(u8, op) * 8 + 1;
6165 q[5] = 1;
6166 @memcpy(q[13..][0..name.len], name);
6167 var i: usize = 13;
6168 var j: usize = undefined;
6169 while (q[i] != 0) : (i = j + 1) {
6170 j = i;
6171 while (q[j] != 0 and q[j] != '.') : (j += 1) {}
6172 // TODO determine the circumstances for this and whether or
6173 // not this should be an error.
6174 if (j - i - 1 > 62) unreachable;
6175 q[i - 1] = @intCast(j - i);
6176 }
6177 q[i + 1] = ty;
6178 q[i + 3] = class;
6179
6180 // Make a reasonably unpredictable id
6181 var ts: timespec = undefined;
6182 clock_gettime(CLOCK.REALTIME, &ts) catch {};
6183 const UInt = std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(ts.tv_nsec)));
6184 const unsec: UInt = @bitCast(ts.tv_nsec);
6185 const id: u32 = @truncate(unsec + unsec / 65536);
6186 q[0] = @truncate(id / 256);
6187 q[1] = @truncate(id);
6188
6189 @memcpy(buf[0..n], q[0..n]);
6190 return n;
6191}
6192
6193pub const SendError = error{
6194 /// (For UNIX domain sockets, which are identified by pathname) Write permission is denied
6195 /// on the destination socket file, or search permission is denied for one of the
6196 /// directories the path prefix. (See path_resolution(7).)
6197 /// (For UDP sockets) An attempt was made to send to a network/broadcast address as though
6198 /// it was a unicast address.
6199 AccessDenied,
6200
6201 /// The socket is marked nonblocking and the requested operation would block, and
6202 /// there is no global event loop configured.
6203 /// It's also possible to get this error under the following condition:
6204 /// (Internet domain datagram sockets) The socket referred to by sockfd had not previously
6205 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it was
6206 /// determined that all port numbers in the ephemeral port range are currently in use. See
6207 /// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
6208 WouldBlock,
6209
6210 /// Another Fast Open is already in progress.
6211 FastOpenAlreadyInProgress,
6212
6213 /// Connection reset by peer.
6214 ConnectionResetByPeer,
6215
6216 /// The socket type requires that message be sent atomically, and the size of the message
6217 /// to be sent made this impossible. The message is not transmitted.
6218 MessageTooBig,
6219
6220 /// The output queue for a network interface was full. This generally indicates that the
6221 /// interface has stopped sending, but may be caused by transient congestion. (Normally,
6222 /// this does not occur in Linux. Packets are just silently dropped when a device queue
6223 /// overflows.)
6224 /// This is also caused when there is not enough kernel memory available.
6225 SystemResources,
6226
6227 /// The local end has been shut down on a connection oriented socket. In this case, the
6228 /// process will also receive a SIGPIPE unless MSG.NOSIGNAL is set.
6229 BrokenPipe,
6230
6231 FileDescriptorNotASocket,
6232
6233 /// Network is unreachable.
6234 NetworkUnreachable,
6235
6236 /// The local network interface used to reach the destination is down.
6237 NetworkSubsystemFailed,
6238} || UnexpectedError;
6239
6240pub const SendMsgError = SendError || error{
6241 /// The passed address didn't have the correct address family in its sa_family field.
6242 AddressFamilyNotSupported,
6243
6244 /// Returned when socket is AF.UNIX and the given path has a symlink loop.
6245 SymLinkLoop,
6246
6247 /// Returned when socket is AF.UNIX and the given path length exceeds `MAX_PATH_BYTES` bytes.
6248 NameTooLong,
6249
6250 /// Returned when socket is AF.UNIX and the given path does not point to an existing file.
6251 FileNotFound,
6252 NotDir,
6253
6254 /// The socket is not connected (connection-oriented sockets only).
6255 SocketNotConnected,
6256 AddressNotAvailable,
6257};
6258
6259pub fn sendmsg(
6260 /// The file descriptor of the sending socket.
6261 sockfd: socket_t,
6262 /// Message header and iovecs
6263 msg: *const msghdr_const,
6264 flags: u32,
6265) SendMsgError!usize {
6266 while (true) {
6267 const rc = system.sendmsg(sockfd, msg, flags);
6268 if (builtin.os.tag == .windows) {
6269 if (rc == windows.ws2_32.SOCKET_ERROR) {
6270 switch (windows.ws2_32.WSAGetLastError()) {
6271 .WSAEACCES => return error.AccessDenied,
6272 .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
6273 .WSAECONNRESET => return error.ConnectionResetByPeer,
6274 .WSAEMSGSIZE => return error.MessageTooBig,
6275 .WSAENOBUFS => return error.SystemResources,
6276 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
6277 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
6278 .WSAEDESTADDRREQ => unreachable, // A destination address is required.
6279 .WSAEFAULT => unreachable, // The lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent, or lpCompletionRoutine parameters are not part of the user address space, or the lpTo parameter is too small.
6280 .WSAEHOSTUNREACH => return error.NetworkUnreachable,
6281 // TODO: WSAEINPROGRESS, WSAEINTR
6282 .WSAEINVAL => unreachable,
6283 .WSAENETDOWN => return error.NetworkSubsystemFailed,
6284 .WSAENETRESET => return error.ConnectionResetByPeer,
6285 .WSAENETUNREACH => return error.NetworkUnreachable,
6286 .WSAENOTCONN => return error.SocketNotConnected,
6287 .WSAESHUTDOWN => unreachable, // The socket has been shut down; it is not possible to WSASendTo on a socket after shutdown has been invoked with how set to SD_SEND or SD_BOTH.
6288 .WSAEWOULDBLOCK => return error.WouldBlock,
6289 .WSANOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function.
6290 else => |err| return windows.unexpectedWSAError(err),
6291 }
6292 } else {
6293 return @intCast(rc);
6294 }
6295 } else {
6296 switch (errno(rc)) {
6297 .SUCCESS => return @intCast(rc),
6298
6299 .ACCES => return error.AccessDenied,
6300 .AGAIN => return error.WouldBlock,
6301 .ALREADY => return error.FastOpenAlreadyInProgress,
6302 .BADF => unreachable, // always a race condition
6303 .CONNRESET => return error.ConnectionResetByPeer,
6304 .DESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
6305 .FAULT => unreachable, // An invalid user space address was specified for an argument.
6306 .INTR => continue,
6307 .INVAL => unreachable, // Invalid argument passed.
6308 .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
6309 .MSGSIZE => return error.MessageTooBig,
6310 .NOBUFS => return error.SystemResources,
6311 .NOMEM => return error.SystemResources,
6312 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
6313 .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
6314 .PIPE => return error.BrokenPipe,
6315 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
6316 .LOOP => return error.SymLinkLoop,
6317 .NAMETOOLONG => return error.NameTooLong,
6318 .NOENT => return error.FileNotFound,
6319 .NOTDIR => return error.NotDir,
6320 .HOSTUNREACH => return error.NetworkUnreachable,
6321 .NETUNREACH => return error.NetworkUnreachable,
6322 .NOTCONN => return error.SocketNotConnected,
6323 .NETDOWN => return error.NetworkSubsystemFailed,
6324 else => |err| return unexpectedErrno(err),
6325 }
6326 }
6327 }
6328}
6329
6330pub const SendToError = SendMsgError || error{
6331 /// The destination address is not reachable by the bound address.
6332 UnreachableAddress,
6333};
6334
6335/// Transmit a message to another socket.
6336///
6337/// The `sendto` call may be used only when the socket is in a connected state (so that the intended
6338/// recipient is known). The following call
6339///
6340/// send(sockfd, buf, len, flags);
6341///
6342/// is equivalent to
6343///
6344/// sendto(sockfd, buf, len, flags, NULL, 0);
6345///
6346/// If sendto() is used on a connection-mode (`SOCK.STREAM`, `SOCK.SEQPACKET`) socket, the arguments
6347/// `dest_addr` and `addrlen` are asserted to be `null` and `0` respectively, and asserted
6348/// that the socket was actually connected.
6349/// Otherwise, the address of the target is given by `dest_addr` with `addrlen` specifying its size.
6350///
6351/// If the message is too long to pass atomically through the underlying protocol,
6352/// `SendError.MessageTooBig` is returned, and the message is not transmitted.
6353///
6354/// There is no indication of failure to deliver.
6355///
6356/// When the message does not fit into the send buffer of the socket, `sendto` normally blocks,
6357/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail
6358/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is
6359/// possible to send more data.
6360pub fn sendto(
6361 /// The file descriptor of the sending socket.
6362 sockfd: socket_t,
6363 /// Message to send.
6364 buf: []const u8,
6365 flags: u32,
6366 dest_addr: ?*const sockaddr,
6367 addrlen: socklen_t,
6368) SendToError!usize {
6369 if (builtin.os.tag == .windows) {
6370 switch (windows.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen)) {
6371 windows.ws2_32.SOCKET_ERROR => switch (windows.ws2_32.WSAGetLastError()) {
6372 .WSAEACCES => return error.AccessDenied,
6373 .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
6374 .WSAECONNRESET => return error.ConnectionResetByPeer,
6375 .WSAEMSGSIZE => return error.MessageTooBig,
6376 .WSAENOBUFS => return error.SystemResources,
6377 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
6378 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
6379 .WSAEDESTADDRREQ => unreachable, // A destination address is required.
6380 .WSAEFAULT => unreachable, // The lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent, or lpCompletionRoutine parameters are not part of the user address space, or the lpTo parameter is too small.
6381 .WSAEHOSTUNREACH => return error.NetworkUnreachable,
6382 // TODO: WSAEINPROGRESS, WSAEINTR
6383 .WSAEINVAL => unreachable,
6384 .WSAENETDOWN => return error.NetworkSubsystemFailed,
6385 .WSAENETRESET => return error.ConnectionResetByPeer,
6386 .WSAENETUNREACH => return error.NetworkUnreachable,
6387 .WSAENOTCONN => return error.SocketNotConnected,
6388 .WSAESHUTDOWN => unreachable, // The socket has been shut down; it is not possible to WSASendTo on a socket after shutdown has been invoked with how set to SD_SEND or SD_BOTH.
6389 .WSAEWOULDBLOCK => return error.WouldBlock,
6390 .WSANOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function.
6391 else => |err| return windows.unexpectedWSAError(err),
6392 },
6393 else => |rc| return @intCast(rc),
6394 }
6395 }
6396 while (true) {
6397 const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);
6398 switch (errno(rc)) {
6399 .SUCCESS => return @intCast(rc),
6400
6401 .ACCES => return error.AccessDenied,
6402 .AGAIN => return error.WouldBlock,
6403 .ALREADY => return error.FastOpenAlreadyInProgress,
6404 .BADF => unreachable, // always a race condition
6405 .CONNRESET => return error.ConnectionResetByPeer,
6406 .DESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
6407 .FAULT => unreachable, // An invalid user space address was specified for an argument.
6408 .INTR => continue,
6409 .INVAL => return error.UnreachableAddress,
6410 .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
6411 .MSGSIZE => return error.MessageTooBig,
6412 .NOBUFS => return error.SystemResources,
6413 .NOMEM => return error.SystemResources,
6414 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
6415 .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
6416 .PIPE => return error.BrokenPipe,
6417 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
6418 .LOOP => return error.SymLinkLoop,
6419 .NAMETOOLONG => return error.NameTooLong,
6420 .NOENT => return error.FileNotFound,
6421 .NOTDIR => return error.NotDir,
6422 .HOSTUNREACH => return error.NetworkUnreachable,
6423 .NETUNREACH => return error.NetworkUnreachable,
6424 .NOTCONN => return error.SocketNotConnected,
6425 .NETDOWN => return error.NetworkSubsystemFailed,
6426 else => |err| return unexpectedErrno(err),
6427 }
6428 }
6429}
6430
6431/// Transmit a message to another socket.
6432///
6433/// The `send` call may be used only when the socket is in a connected state (so that the intended
6434/// recipient is known). The only difference between `send` and `write` is the presence of
6435/// flags. With a zero flags argument, `send` is equivalent to `write`. Also, the following
6436/// call
6437///
6438/// send(sockfd, buf, len, flags);
6439///
6440/// is equivalent to
6441///
6442/// sendto(sockfd, buf, len, flags, NULL, 0);
6443///
6444/// There is no indication of failure to deliver.
6445///
6446/// When the message does not fit into the send buffer of the socket, `send` normally blocks,
6447/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail
6448/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is
6449/// possible to send more data.
6450pub fn send(
6451 /// The file descriptor of the sending socket.
6452 sockfd: socket_t,
6453 buf: []const u8,
6454 flags: u32,
6455) SendError!usize {
6456 return sendto(sockfd, buf, flags, null, 0) catch |err| switch (err) {
6457 error.AddressFamilyNotSupported => unreachable,
6458 error.SymLinkLoop => unreachable,
6459 error.NameTooLong => unreachable,
6460 error.FileNotFound => unreachable,
6461 error.NotDir => unreachable,
6462 error.NetworkUnreachable => unreachable,
6463 error.AddressNotAvailable => unreachable,
6464 error.SocketNotConnected => unreachable,
6465 error.UnreachableAddress => unreachable,
6466 else => |e| return e,
6467 };
6468}
6469
6470pub const SendFileError = PReadError || WriteError || SendError;
6471
6472fn count_iovec_bytes(iovs: []const iovec_const) usize {
6473 var count: usize = 0;
6474 for (iovs) |iov| {
6475 count += iov.iov_len;
6476 }
6477 return count;
6478}
6479
6480/// Transfer data between file descriptors, with optional headers and trailers.
6481/// Returns the number of bytes written, which can be zero.
6482///
6483/// The `sendfile` call copies `in_len` bytes from one file descriptor to another. When possible,
6484/// this is done within the operating system kernel, which can provide better performance
6485/// characteristics than transferring data from kernel to user space and back, such as with
6486/// `read` and `write` calls. When `in_len` is `0`, it means to copy until the end of the input file has been
6487/// reached. Note, however, that partial writes are still possible in this case.
6488///
6489/// `in_fd` must be a file descriptor opened for reading, and `out_fd` must be a file descriptor
6490/// opened for writing. They may be any kind of file descriptor; however, if `in_fd` is not a regular
6491/// file system file, it may cause this function to fall back to calling `read` and `write`, in which case
6492/// atomicity guarantees no longer apply.
6493///
6494/// Copying begins reading at `in_offset`. The input file descriptor seek position is ignored and not updated.
6495/// If the output file descriptor has a seek position, it is updated as bytes are written. When
6496/// `in_offset` is past the end of the input file, it successfully reads 0 bytes.
6497///
6498/// `flags` has different meanings per operating system; refer to the respective man pages.
6499///
6500/// These systems support atomically sending everything, including headers and trailers:
6501/// * macOS
6502/// * FreeBSD
6503///
6504/// These systems support in-kernel data copying, but headers and trailers are not sent atomically:
6505/// * Linux
6506///
6507/// Other systems fall back to calling `read` / `write`.
6508///
6509/// Linux has a limit on how many bytes may be transferred in one `sendfile` call, which is `0x7ffff000`
6510/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
6511/// well as stuffing the errno codes into the last `4096` values. This is noted on the `sendfile` man page.
6512/// The limit on Darwin is `0x7fffffff`, trying to write more than that returns EINVAL.
6513/// The corresponding POSIX limit on this is `math.maxInt(isize)`.
6514pub fn sendfile(
6515 out_fd: fd_t,
6516 in_fd: fd_t,
6517 in_offset: u64,
6518 in_len: u64,
6519 headers: []const iovec_const,
6520 trailers: []const iovec_const,
6521 flags: u32,
6522) SendFileError!usize {
6523 var header_done = false;
6524 var total_written: usize = 0;
6525
6526 // Prevents EOVERFLOW.
6527 const size_t = std.meta.Int(.unsigned, @typeInfo(usize).Int.bits - 1);
6528 const max_count = switch (builtin.os.tag) {
6529 .linux => 0x7ffff000,
6530 .macos, .ios, .watchos, .tvos => math.maxInt(i32),
6531 else => math.maxInt(size_t),
6532 };
6533
6534 switch (builtin.os.tag) {
6535 .linux => sf: {
6536 // sendfile() first appeared in Linux 2.2, glibc 2.1.
6537 const call_sf = comptime if (builtin.link_libc)
6538 std.c.versionCheck(.{ .major = 2, .minor = 1, .patch = 0 })
6539 else
6540 builtin.os.version_range.linux.range.max.order(.{ .major = 2, .minor = 2, .patch = 0 }) != .lt;
6541 if (!call_sf) break :sf;
6542
6543 if (headers.len != 0) {
6544 const amt = try writev(out_fd, headers);
6545 total_written += amt;
6546 if (amt < count_iovec_bytes(headers)) return total_written;
6547 header_done = true;
6548 }
6549
6550 // Here we match BSD behavior, making a zero count value send as many bytes as possible.
6551 const adjusted_count = if (in_len == 0) max_count else @min(in_len, max_count);
6552
6553 const sendfile_sym = if (lfs64_abi) system.sendfile64 else system.sendfile;
6554 while (true) {
6555 var offset: off_t = @bitCast(in_offset);
6556 const rc = sendfile_sym(out_fd, in_fd, &offset, adjusted_count);
6557 switch (errno(rc)) {
6558 .SUCCESS => {
6559 const amt: usize = @bitCast(rc);
6560 total_written += amt;
6561 if (in_len == 0 and amt == 0) {
6562 // We have detected EOF from `in_fd`.
6563 break;
6564 } else if (amt < in_len) {
6565 return total_written;
6566 } else {
6567 break;
6568 }
6569 },
6570
6571 .BADF => unreachable, // Always a race condition.
6572 .FAULT => unreachable, // Segmentation fault.
6573 .OVERFLOW => unreachable, // We avoid passing too large of a `count`.
6574 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
6575
6576 .INVAL, .NOSYS => {
6577 // EINVAL could be any of the following situations:
6578 // * Descriptor is not valid or locked
6579 // * an mmap(2)-like operation is not available for in_fd
6580 // * count is negative
6581 // * out_fd has the APPEND flag set
6582 // Because of the "mmap(2)-like operation" possibility, we fall back to doing read/write
6583 // manually, the same as ENOSYS.
6584 break :sf;
6585 },
6586 .AGAIN => return error.WouldBlock,
6587 .IO => return error.InputOutput,
6588 .PIPE => return error.BrokenPipe,
6589 .NOMEM => return error.SystemResources,
6590 .NXIO => return error.Unseekable,
6591 .SPIPE => return error.Unseekable,
6592 else => |err| {
6593 unexpectedErrno(err) catch {};
6594 break :sf;
6595 },
6596 }
6597 }
6598
6599 if (trailers.len != 0) {
6600 total_written += try writev(out_fd, trailers);
6601 }
6602
6603 return total_written;
6604 },
6605 .freebsd => sf: {
6606 var hdtr_data: std.c.sf_hdtr = undefined;
6607 var hdtr: ?*std.c.sf_hdtr = null;
6608 if (headers.len != 0 or trailers.len != 0) {
6609 // Here we carefully avoid `@intCast` by returning partial writes when
6610 // too many io vectors are provided.
6611 const hdr_cnt = math.cast(u31, headers.len) orelse math.maxInt(u31);
6612 if (headers.len > hdr_cnt) return writev(out_fd, headers);
6613
6614 const trl_cnt = math.cast(u31, trailers.len) orelse math.maxInt(u31);
6615
6616 hdtr_data = std.c.sf_hdtr{
6617 .headers = headers.ptr,
6618 .hdr_cnt = hdr_cnt,
6619 .trailers = trailers.ptr,
6620 .trl_cnt = trl_cnt,
6621 };
6622 hdtr = &hdtr_data;
6623 }
6624
6625 while (true) {
6626 var sbytes: off_t = undefined;
6627 const err = errno(system.sendfile(in_fd, out_fd, @bitCast(in_offset), @min(in_len, max_count), hdtr, &sbytes, flags));
6628 const amt: usize = @bitCast(sbytes);
6629 switch (err) {
6630 .SUCCESS => return amt,
6631
6632 .BADF => unreachable, // Always a race condition.
6633 .FAULT => unreachable, // Segmentation fault.
6634 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
6635
6636 .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => {
6637 // EINVAL could be any of the following situations:
6638 // * The fd argument is not a regular file.
6639 // * The s argument is not a SOCK.STREAM type socket.
6640 // * The offset argument is negative.
6641 // Because of some of these possibilities, we fall back to doing read/write
6642 // manually, the same as ENOSYS.
6643 break :sf;
6644 },
6645
6646 .INTR => if (amt != 0) return amt else continue,
6647
6648 .AGAIN => if (amt != 0) {
6649 return amt;
6650 } else {
6651 return error.WouldBlock;
6652 },
6653
6654 .BUSY => if (amt != 0) {
6655 return amt;
6656 } else {
6657 return error.WouldBlock;
6658 },
6659
6660 .IO => return error.InputOutput,
6661 .NOBUFS => return error.SystemResources,
6662 .PIPE => return error.BrokenPipe,
6663
6664 else => {
6665 unexpectedErrno(err) catch {};
6666 if (amt != 0) {
6667 return amt;
6668 } else {
6669 break :sf;
6670 }
6671 },
6672 }
6673 }
6674 },
6675 .macos, .ios, .tvos, .watchos => sf: {
6676 var hdtr_data: std.c.sf_hdtr = undefined;
6677 var hdtr: ?*std.c.sf_hdtr = null;
6678 if (headers.len != 0 or trailers.len != 0) {
6679 // Here we carefully avoid `@intCast` by returning partial writes when
6680 // too many io vectors are provided.
6681 const hdr_cnt = math.cast(u31, headers.len) orelse math.maxInt(u31);
6682 if (headers.len > hdr_cnt) return writev(out_fd, headers);
6683
6684 const trl_cnt = math.cast(u31, trailers.len) orelse math.maxInt(u31);
6685
6686 hdtr_data = std.c.sf_hdtr{
6687 .headers = headers.ptr,
6688 .hdr_cnt = hdr_cnt,
6689 .trailers = trailers.ptr,
6690 .trl_cnt = trl_cnt,
6691 };
6692 hdtr = &hdtr_data;
6693 }
6694
6695 while (true) {
6696 var sbytes: off_t = @min(in_len, max_count);
6697 const err = errno(system.sendfile(in_fd, out_fd, @bitCast(in_offset), &sbytes, hdtr, flags));
6698 const amt: usize = @bitCast(sbytes);
6699 switch (err) {
6700 .SUCCESS => return amt,
6701
6702 .BADF => unreachable, // Always a race condition.
6703 .FAULT => unreachable, // Segmentation fault.
6704 .INVAL => unreachable,
6705 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
6706
6707 .OPNOTSUPP, .NOTSOCK, .NOSYS => break :sf,
6708
6709 .INTR => if (amt != 0) return amt else continue,
6710
6711 .AGAIN => if (amt != 0) {
6712 return amt;
6713 } else {
6714 return error.WouldBlock;
6715 },
6716
6717 .IO => return error.InputOutput,
6718 .PIPE => return error.BrokenPipe,
6719
6720 else => {
6721 unexpectedErrno(err) catch {};
6722 if (amt != 0) {
6723 return amt;
6724 } else {
6725 break :sf;
6726 }
6727 },
6728 }
6729 }
6730 },
6731 else => {}, // fall back to read/write
6732 }
6733
6734 if (headers.len != 0 and !header_done) {
6735 const amt = try writev(out_fd, headers);
6736 total_written += amt;
6737 if (amt < count_iovec_bytes(headers)) return total_written;
6738 }
6739
6740 rw: {
6741 var buf: [8 * 4096]u8 = undefined;
6742 // Here we match BSD behavior, making a zero count value send as many bytes as possible.
6743 const adjusted_count = if (in_len == 0) buf.len else @min(buf.len, in_len);
6744 const amt_read = try pread(in_fd, buf[0..adjusted_count], in_offset);
6745 if (amt_read == 0) {
6746 if (in_len == 0) {
6747 // We have detected EOF from `in_fd`.
6748 break :rw;
6749 } else {
6750 return total_written;
6751 }
6752 }
6753 const amt_written = try write(out_fd, buf[0..amt_read]);
6754 total_written += amt_written;
6755 if (amt_written < in_len or in_len == 0) return total_written;
6756 }
6757
6758 if (trailers.len != 0) {
6759 total_written += try writev(out_fd, trailers);
6760 }
6761
6762 return total_written;
6763}
6764
6765pub const CopyFileRangeError = error{
6766 FileTooBig,
6767 InputOutput,
6768 /// `fd_in` is not open for reading; or `fd_out` is not open for writing;
6769 /// or the `APPEND` flag is set for `fd_out`.
6770 FilesOpenedWithWrongFlags,
6771 IsDir,
6772 OutOfMemory,
6773 NoSpaceLeft,
6774 Unseekable,
6775 PermissionDenied,
6776 SwapFile,
6777 CorruptedData,
6778} || PReadError || PWriteError || UnexpectedError;
6779
6780var has_copy_file_range_syscall = std.atomic.Value(bool).init(true);
6781
6782/// Transfer data between file descriptors at specified offsets.
6783/// Returns the number of bytes written, which can less than requested.
6784///
6785/// The `copy_file_range` call copies `len` bytes from one file descriptor to another. When possible,
6786/// this is done within the operating system kernel, which can provide better performance
6787/// characteristics than transferring data from kernel to user space and back, such as with
6788/// `pread` and `pwrite` calls.
6789///
6790/// `fd_in` must be a file descriptor opened for reading, and `fd_out` must be a file descriptor
6791/// opened for writing. They may be any kind of file descriptor; however, if `fd_in` is not a regular
6792/// file system file, it may cause this function to fall back to calling `pread` and `pwrite`, in which case
6793/// atomicity guarantees no longer apply.
6794///
6795/// If `fd_in` and `fd_out` are the same, source and target ranges must not overlap.
6796/// The file descriptor seek positions are ignored and not updated.
6797/// When `off_in` is past the end of the input file, it successfully reads 0 bytes.
6798///
6799/// `flags` has different meanings per operating system; refer to the respective man pages.
6800///
6801/// These systems support in-kernel data copying:
6802/// * Linux 4.5 (cross-filesystem 5.3)
6803/// * FreeBSD 13.0
6804///
6805/// Other systems fall back to calling `pread` / `pwrite`.
6806///
6807/// Maximum offsets on Linux and FreeBSD are `math.maxInt(i64)`.
6808pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len: usize, flags: u32) CopyFileRangeError!usize {
6809 if ((comptime builtin.os.isAtLeast(.freebsd, .{ .major = 13, .minor = 0, .patch = 0 }) orelse false) or
6810 ((comptime builtin.os.isAtLeast(.linux, .{ .major = 4, .minor = 5, .patch = 0 }) orelse false and
6811 std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 })) and
6812 has_copy_file_range_syscall.load(.monotonic)))
6813 {
6814 var off_in_copy: i64 = @bitCast(off_in);
6815 var off_out_copy: i64 = @bitCast(off_out);
6816
6817 while (true) {
6818 const rc = system.copy_file_range(fd_in, &off_in_copy, fd_out, &off_out_copy, len, flags);
6819 if (builtin.os.tag == .freebsd) {
6820 switch (system.getErrno(rc)) {
6821 .SUCCESS => return @intCast(rc),
6822 .BADF => return error.FilesOpenedWithWrongFlags,
6823 .FBIG => return error.FileTooBig,
6824 .IO => return error.InputOutput,
6825 .ISDIR => return error.IsDir,
6826 .NOSPC => return error.NoSpaceLeft,
6827 .INVAL => break, // these may not be regular files, try fallback
6828 .INTEGRITY => return error.CorruptedData,
6829 .INTR => continue,
6830 else => |err| return unexpectedErrno(err),
6831 }
6832 } else { // assume linux
6833 switch (system.getErrno(rc)) {
6834 .SUCCESS => return @intCast(rc),
6835 .BADF => return error.FilesOpenedWithWrongFlags,
6836 .FBIG => return error.FileTooBig,
6837 .IO => return error.InputOutput,
6838 .ISDIR => return error.IsDir,
6839 .NOSPC => return error.NoSpaceLeft,
6840 .INVAL => break, // these may not be regular files, try fallback
6841 .NOMEM => return error.OutOfMemory,
6842 .OVERFLOW => return error.Unseekable,
6843 .PERM => return error.PermissionDenied,
6844 .TXTBSY => return error.SwapFile,
6845 .XDEV => break, // support for cross-filesystem copy added in Linux 5.3, use fallback
6846 .NOSYS => { // syscall added in Linux 4.5, use fallback
6847 has_copy_file_range_syscall.store(false, .monotonic);
6848 break;
6849 },
6850 else => |err| return unexpectedErrno(err),
6851 }
6852 }
6853 }
6854 }
6855
6856 var buf: [8 * 4096]u8 = undefined;
6857 const amt_read = try pread(fd_in, buf[0..@min(buf.len, len)], off_in);
6858 if (amt_read == 0) return 0;
6859 return pwrite(fd_out, buf[0..amt_read], off_out);
6860}
6861
6862pub const PollError = error{
6863 /// The network subsystem has failed.
6864 NetworkSubsystemFailed,
6865
6866 /// The kernel had no space to allocate file descriptor tables.
6867 SystemResources,
6868} || UnexpectedError;
6869
6870pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
6871 while (true) {
6872 const fds_count = math.cast(nfds_t, fds.len) orelse return error.SystemResources;
6873 const rc = system.poll(fds.ptr, fds_count, timeout);
6874 if (builtin.os.tag == .windows) {
6875 if (rc == windows.ws2_32.SOCKET_ERROR) {
6876 switch (windows.ws2_32.WSAGetLastError()) {
6877 .WSANOTINITIALISED => unreachable,
6878 .WSAENETDOWN => return error.NetworkSubsystemFailed,
6879 .WSAENOBUFS => return error.SystemResources,
6880 // TODO: handle more errors
6881 else => |err| return windows.unexpectedWSAError(err),
6882 }
6883 } else {
6884 return @intCast(rc);
6885 }
6886 } else {
6887 switch (errno(rc)) {
6888 .SUCCESS => return @intCast(rc),
6889 .FAULT => unreachable,
6890 .INTR => continue,
6891 .INVAL => unreachable,
6892 .NOMEM => return error.SystemResources,
6893 else => |err| return unexpectedErrno(err),
6894 }
6895 }
6896 unreachable;
6897 }
6898}
6899
6900pub const PPollError = error{
6901 /// The operation was interrupted by a delivery of a signal before it could complete.
6902 SignalInterrupt,
6903
6904 /// The kernel had no space to allocate file descriptor tables.
6905 SystemResources,
6906} || UnexpectedError;
6907
6908pub fn ppoll(fds: []pollfd, timeout: ?*const timespec, mask: ?*const sigset_t) PPollError!usize {
6909 var ts: timespec = undefined;
6910 var ts_ptr: ?*timespec = null;
6911 if (timeout) |timeout_ns| {
6912 ts_ptr = &ts;
6913 ts = timeout_ns.*;
6914 }
6915 const fds_count = math.cast(nfds_t, fds.len) orelse return error.SystemResources;
6916 const rc = system.ppoll(fds.ptr, fds_count, ts_ptr, mask);
6917 switch (errno(rc)) {
6918 .SUCCESS => return @intCast(rc),
6919 .FAULT => unreachable,
6920 .INTR => return error.SignalInterrupt,
6921 .INVAL => unreachable,
6922 .NOMEM => return error.SystemResources,
6923 else => |err| return unexpectedErrno(err),
6924 }
6925}
6926
6927pub const RecvFromError = error{
6928 /// The socket is marked nonblocking and the requested operation would block, and
6929 /// there is no global event loop configured.
6930 WouldBlock,
6931
6932 /// A remote host refused to allow the network connection, typically because it is not
6933 /// running the requested service.
6934 ConnectionRefused,
6935
6936 /// Could not allocate kernel memory.
6937 SystemResources,
6938
6939 ConnectionResetByPeer,
6940 ConnectionTimedOut,
6941
6942 /// The socket has not been bound.
6943 SocketNotBound,
6944
6945 /// The UDP message was too big for the buffer and part of it has been discarded
6946 MessageTooBig,
6947
6948 /// The network subsystem has failed.
6949 NetworkSubsystemFailed,
6950
6951 /// The socket is not connected (connection-oriented sockets only).
6952 SocketNotConnected,
6953} || UnexpectedError;
6954
6955pub fn recv(sock: socket_t, buf: []u8, flags: u32) RecvFromError!usize {
6956 return recvfrom(sock, buf, flags, null, null);
6957}
6958
6959/// If `sockfd` is opened in non blocking mode, the function will
6960/// return error.WouldBlock when EAGAIN is received.
6961pub fn recvfrom(
6962 sockfd: socket_t,
6963 buf: []u8,
6964 flags: u32,
6965 src_addr: ?*sockaddr,
6966 addrlen: ?*socklen_t,
6967) RecvFromError!usize {
6968 while (true) {
6969 const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen);
6970 if (builtin.os.tag == .windows) {
6971 if (rc == windows.ws2_32.SOCKET_ERROR) {
6972 switch (windows.ws2_32.WSAGetLastError()) {
6973 .WSANOTINITIALISED => unreachable,
6974 .WSAECONNRESET => return error.ConnectionResetByPeer,
6975 .WSAEINVAL => return error.SocketNotBound,
6976 .WSAEMSGSIZE => return error.MessageTooBig,
6977 .WSAENETDOWN => return error.NetworkSubsystemFailed,
6978 .WSAENOTCONN => return error.SocketNotConnected,
6979 .WSAEWOULDBLOCK => return error.WouldBlock,
6980 .WSAETIMEDOUT => return error.ConnectionTimedOut,
6981 // TODO: handle more errors
6982 else => |err| return windows.unexpectedWSAError(err),
6983 }
6984 } else {
6985 return @intCast(rc);
6986 }
6987 } else {
6988 switch (errno(rc)) {
6989 .SUCCESS => return @intCast(rc),
6990 .BADF => unreachable, // always a race condition
6991 .FAULT => unreachable,
6992 .INVAL => unreachable,
6993 .NOTCONN => return error.SocketNotConnected,
6994 .NOTSOCK => unreachable,
6995 .INTR => continue,
6996 .AGAIN => return error.WouldBlock,
6997 .NOMEM => return error.SystemResources,
6998 .CONNREFUSED => return error.ConnectionRefused,
6999 .CONNRESET => return error.ConnectionResetByPeer,
7000 .TIMEDOUT => return error.ConnectionTimedOut,
7001 else => |err| return unexpectedErrno(err),
7002 }
7003 }
7004 }
7005}
7006
7007pub const DnExpandError = error{InvalidDnsPacket};
7008
7009pub fn dn_expand(
7010 msg: []const u8,
7011 comp_dn: []const u8,
7012 exp_dn: []u8,
7013) DnExpandError!usize {
7014 // This implementation is ported from musl libc.
7015 // A more idiomatic "ziggy" implementation would be welcome.
7016 var p = comp_dn.ptr;
7017 var len: usize = std.math.maxInt(usize);
7018 const end = msg.ptr + msg.len;
7019 if (p == end or exp_dn.len == 0) return error.InvalidDnsPacket;
7020 var dest = exp_dn.ptr;
7021 const dend = dest + @min(exp_dn.len, 254);
7022 // detect reference loop using an iteration counter
7023 var i: usize = 0;
7024 while (i < msg.len) : (i += 2) {
7025 // loop invariants: p<end, dest<dend
7026 if ((p[0] & 0xc0) != 0) {
7027 if (p + 1 == end) return error.InvalidDnsPacket;
7028 const j = @as(usize, p[0] & 0x3f) << 8 | p[1];
7029 if (len == std.math.maxInt(usize)) len = @intFromPtr(p) + 2 - @intFromPtr(comp_dn.ptr);
7030 if (j >= msg.len) return error.InvalidDnsPacket;
7031 p = msg.ptr + j;
7032 } else if (p[0] != 0) {
7033 if (dest != exp_dn.ptr) {
7034 dest[0] = '.';
7035 dest += 1;
7036 }
7037 var j = p[0];
7038 p += 1;
7039 if (j >= @intFromPtr(end) - @intFromPtr(p) or j >= @intFromPtr(dend) - @intFromPtr(dest)) {
7040 return error.InvalidDnsPacket;
7041 }
7042 while (j != 0) {
7043 j -= 1;
7044 dest[0] = p[0];
7045 dest += 1;
7046 p += 1;
7047 }
7048 } else {
7049 dest[0] = 0;
7050 if (len == std.math.maxInt(usize)) len = @intFromPtr(p) + 1 - @intFromPtr(comp_dn.ptr);
7051 return len;
7052 }
7053 }
7054 return error.InvalidDnsPacket;
7055}
7056
7057pub const SetSockOptError = error{
7058 /// The socket is already connected, and a specified option cannot be set while the socket is connected.
7059 AlreadyConnected,
7060
7061 /// The option is not supported by the protocol.
7062 InvalidProtocolOption,
7063
7064 /// The send and receive timeout values are too big to fit into the timeout fields in the socket structure.
7065 TimeoutTooBig,
7066
7067 /// Insufficient resources are available in the system to complete the call.
7068 SystemResources,
7069
7070 // Setting the socket option requires more elevated permissions.
7071 PermissionDenied,
7072
7073 NetworkSubsystemFailed,
7074 FileDescriptorNotASocket,
7075 SocketNotBound,
7076 NoDevice,
7077} || UnexpectedError;
7078
7079/// Set a socket's options.
7080pub fn setsockopt(fd: socket_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void {
7081 if (builtin.os.tag == .windows) {
7082 const rc = windows.ws2_32.setsockopt(fd, @intCast(level), @intCast(optname), opt.ptr, @intCast(opt.len));
7083 if (rc == windows.ws2_32.SOCKET_ERROR) {
7084 switch (windows.ws2_32.WSAGetLastError()) {
7085 .WSANOTINITIALISED => unreachable,
7086 .WSAENETDOWN => return error.NetworkSubsystemFailed,
7087 .WSAEFAULT => unreachable,
7088 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
7089 .WSAEINVAL => return error.SocketNotBound,
7090 else => |err| return windows.unexpectedWSAError(err),
7091 }
7092 }
7093 return;
7094 } else {
7095 switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(opt.len)))) {
7096 .SUCCESS => {},
7097 .BADF => unreachable, // always a race condition
7098 .NOTSOCK => unreachable, // always a race condition
7099 .INVAL => unreachable,
7100 .FAULT => unreachable,
7101 .DOM => return error.TimeoutTooBig,
7102 .ISCONN => return error.AlreadyConnected,
7103 .NOPROTOOPT => return error.InvalidProtocolOption,
7104 .NOMEM => return error.SystemResources,
7105 .NOBUFS => return error.SystemResources,
7106 .PERM => return error.PermissionDenied,
7107 .NODEV => return error.NoDevice,
7108 else => |err| return unexpectedErrno(err),
7109 }
7110 }
7111}
7112
7113pub const MemFdCreateError = error{
7114 SystemFdQuotaExceeded,
7115 ProcessFdQuotaExceeded,
7116 OutOfMemory,
7117
7118 /// memfd_create is available in Linux 3.17 and later. This error is returned
7119 /// for older kernel versions.
7120 SystemOutdated,
7121} || UnexpectedError;
7122
7123pub fn memfd_createZ(name: [*:0]const u8, flags: u32) MemFdCreateError!fd_t {
7124 switch (builtin.os.tag) {
7125 .linux => {
7126 // memfd_create is available only in glibc versions starting with 2.27.
7127 const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 });
7128 const sys = if (use_c) std.c else linux;
7129 const getErrno = if (use_c) std.c.getErrno else linux.getErrno;
7130 const rc = sys.memfd_create(name, flags);
7131 switch (getErrno(rc)) {
7132 .SUCCESS => return @intCast(rc),
7133 .FAULT => unreachable, // name has invalid memory
7134 .INVAL => unreachable, // name/flags are faulty
7135 .NFILE => return error.SystemFdQuotaExceeded,
7136 .MFILE => return error.ProcessFdQuotaExceeded,
7137 .NOMEM => return error.OutOfMemory,
7138 .NOSYS => return error.SystemOutdated,
7139 else => |err| return unexpectedErrno(err),
7140 }
7141 },
7142 .freebsd => {
7143 if (comptime builtin.os.version_range.semver.max.order(.{ .major = 13, .minor = 0, .patch = 0 }) == .lt)
7144 @compileError("memfd_create is unavailable on FreeBSD < 13.0");
7145 const rc = system.memfd_create(name, flags);
7146 switch (errno(rc)) {
7147 .SUCCESS => return rc,
7148 .BADF => unreachable, // name argument NULL
7149 .INVAL => unreachable, // name too long or invalid/unsupported flags.
7150 .MFILE => return error.ProcessFdQuotaExceeded,
7151 .NFILE => return error.SystemFdQuotaExceeded,
7152 .NOSYS => return error.SystemOutdated,
7153 else => |err| return unexpectedErrno(err),
7154 }
7155 },
7156 else => @compileError("target OS does not support memfd_create()"),
7157 }
7158}
7159
7160pub const MFD_NAME_PREFIX = "memfd:";
7161pub const MFD_MAX_NAME_LEN = NAME_MAX - MFD_NAME_PREFIX.len;
7162fn toMemFdPath(name: []const u8) ![MFD_MAX_NAME_LEN:0]u8 {
7163 var path_with_null: [MFD_MAX_NAME_LEN:0]u8 = undefined;
7164 // >= rather than > to make room for the null byte
7165 if (name.len >= MFD_MAX_NAME_LEN) return error.NameTooLong;
7166 @memcpy(path_with_null[0..name.len], name);
7167 path_with_null[name.len] = 0;
7168 return path_with_null;
7169}
7170
7171pub fn memfd_create(name: []const u8, flags: u32) !fd_t {
7172 const name_t = try toMemFdPath(name);
7173 return memfd_createZ(&name_t, flags);
7174}
7175
7176pub fn getrusage(who: i32) rusage {
7177 var result: rusage = undefined;
7178 const rc = system.getrusage(who, &result);
7179 switch (errno(rc)) {
7180 .SUCCESS => return result,
7181 .INVAL => unreachable,
7182 .FAULT => unreachable,
7183 else => unreachable,
7184 }
7185}
7186
7187pub const TIOCError = error{NotATerminal};
7188
7189pub const TermiosGetError = TIOCError || UnexpectedError;
7190
7191pub fn tcgetattr(handle: fd_t) TermiosGetError!termios {
7192 while (true) {
7193 var term: termios = undefined;
7194 switch (errno(system.tcgetattr(handle, &term))) {
7195 .SUCCESS => return term,
7196 .INTR => continue,
7197 .BADF => unreachable,
7198 .NOTTY => return error.NotATerminal,
7199 else => |err| return unexpectedErrno(err),
7200 }
7201 }
7202}
7203
7204pub const TermiosSetError = TermiosGetError || error{ProcessOrphaned};
7205
7206pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) TermiosSetError!void {
7207 while (true) {
7208 switch (errno(system.tcsetattr(handle, optional_action, &termios_p))) {
7209 .SUCCESS => return,
7210 .BADF => unreachable,
7211 .INTR => continue,
7212 .INVAL => unreachable,
7213 .NOTTY => return error.NotATerminal,
7214 .IO => return error.ProcessOrphaned,
7215 else => |err| return unexpectedErrno(err),
7216 }
7217 }
7218}
7219
7220pub const TermioGetPgrpError = TIOCError || UnexpectedError;
7221
7222/// Returns the process group ID for the TTY associated with the given handle.
7223pub fn tcgetpgrp(handle: fd_t) TermioGetPgrpError!pid_t {
7224 while (true) {
7225 var pgrp: pid_t = undefined;
7226 switch (errno(system.tcgetpgrp(handle, &pgrp))) {
7227 .SUCCESS => return pgrp,
7228 .BADF => unreachable,
7229 .INVAL => unreachable,
7230 .INTR => continue,
7231 .NOTTY => return error.NotATerminal,
7232 else => |err| return unexpectedErrno(err),
7233 }
7234 }
7235}
7236
7237pub const TermioSetPgrpError = TermioGetPgrpError || error{NotAPgrpMember};
7238
7239/// Sets the controlling process group ID for given TTY.
7240/// handle must be valid fd_t to a TTY associated with calling process.
7241/// pgrp must be a valid process group, and the calling process must be a member
7242/// of that group.
7243pub fn tcsetpgrp(handle: fd_t, pgrp: pid_t) TermioSetPgrpError!void {
7244 while (true) {
7245 switch (errno(system.tcsetpgrp(handle, &pgrp))) {
7246 .SUCCESS => return,
7247 .BADF => unreachable,
7248 .INVAL => unreachable,
7249 .INTR => continue,
7250 .NOTTY => return error.NotATerminal,
7251 .PERM => return TermioSetPgrpError.NotAPgrpMember,
7252 else => |err| return unexpectedErrno(err),
7253 }
7254 }
7255}
7256
7257pub const IoCtl_SIOCGIFINDEX_Error = error{
7258 FileSystem,
7259 InterfaceNotFound,
7260} || UnexpectedError;
7261
7262pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void {
7263 while (true) {
7264 switch (errno(system.ioctl(fd, SIOCGIFINDEX, @intFromPtr(ifr)))) {
7265 .SUCCESS => return,
7266 .INVAL => unreachable, // Bad parameters.
7267 .NOTTY => unreachable,
7268 .NXIO => unreachable,
7269 .BADF => unreachable, // Always a race condition.
7270 .FAULT => unreachable, // Bad pointer parameter.
7271 .INTR => continue,
7272 .IO => return error.FileSystem,
7273 .NODEV => return error.InterfaceNotFound,
7274 else => |err| return unexpectedErrno(err),
7275 }
7276 }
7277}
7278
7279pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t {
7280 const rc = system.signalfd(fd, mask, flags);
7281 switch (errno(rc)) {
7282 .SUCCESS => return @intCast(rc),
7283 .BADF, .INVAL => unreachable,
7284 .NFILE => return error.SystemFdQuotaExceeded,
7285 .NOMEM => return error.SystemResources,
7286 .MFILE => return error.ProcessResources,
7287 .NODEV => return error.InodeMountFail,
7288 .NOSYS => return error.SystemOutdated,
7289 else => |err| return unexpectedErrno(err),
7290 }
7291}
7292
7293pub const SyncError = error{
7294 InputOutput,
7295 NoSpaceLeft,
7296 DiskQuota,
7297 AccessDenied,
7298} || UnexpectedError;
7299
7300/// Write all pending file contents and metadata modifications to all filesystems.
7301pub fn sync() void {
7302 system.sync();
7303}
7304
7305/// Write all pending file contents and metadata modifications to the filesystem which contains the specified file.
7306pub fn syncfs(fd: fd_t) SyncError!void {
7307 const rc = system.syncfs(fd);
7308 switch (errno(rc)) {
7309 .SUCCESS => return,
7310 .BADF, .INVAL, .ROFS => unreachable,
7311 .IO => return error.InputOutput,
7312 .NOSPC => return error.NoSpaceLeft,
7313 .DQUOT => return error.DiskQuota,
7314 else => |err| return unexpectedErrno(err),
7315 }
7316}
7317
7318/// Write all pending file contents and metadata modifications for the specified file descriptor to the underlying filesystem.
7319pub fn fsync(fd: fd_t) SyncError!void {
7320 if (builtin.os.tag == .windows) {
7321 if (windows.kernel32.FlushFileBuffers(fd) != 0)
7322 return;
7323 switch (windows.kernel32.GetLastError()) {
7324 .SUCCESS => return,
7325 .INVALID_HANDLE => unreachable,
7326 .ACCESS_DENIED => return error.AccessDenied, // a sync was performed but the system couldn't update the access time
7327 .UNEXP_NET_ERR => return error.InputOutput,
7328 else => return error.InputOutput,
7329 }
7330 }
7331 const rc = system.fsync(fd);
7332 switch (errno(rc)) {
7333 .SUCCESS => return,
7334 .BADF, .INVAL, .ROFS => unreachable,
7335 .IO => return error.InputOutput,
7336 .NOSPC => return error.NoSpaceLeft,
7337 .DQUOT => return error.DiskQuota,
7338 else => |err| return unexpectedErrno(err),
7339 }
7340}
7341
7342/// Write all pending file contents for the specified file descriptor to the underlying filesystem, but not necessarily the metadata.
7343pub fn fdatasync(fd: fd_t) SyncError!void {
7344 if (builtin.os.tag == .windows) {
7345 return fsync(fd) catch |err| switch (err) {
7346 SyncError.AccessDenied => return, // fdatasync doesn't promise that the access time was synced
7347 else => return err,
7348 };
7349 }
7350 const rc = system.fdatasync(fd);
7351 switch (errno(rc)) {
7352 .SUCCESS => return,
7353 .BADF, .INVAL, .ROFS => unreachable,
7354 .IO => return error.InputOutput,
7355 .NOSPC => return error.NoSpaceLeft,
7356 .DQUOT => return error.DiskQuota,
7357 else => |err| return unexpectedErrno(err),
7358 }
7359}
7360
7361pub const PrctlError = error{
7362 /// Can only occur with PR_SET_SECCOMP/SECCOMP_MODE_FILTER or
7363 /// PR_SET_MM/PR_SET_MM_EXE_FILE
7364 AccessDenied,
7365 /// Can only occur with PR_SET_MM/PR_SET_MM_EXE_FILE
7366 InvalidFileDescriptor,
7367 InvalidAddress,
7368 /// Can only occur with PR_SET_SPECULATION_CTRL, PR_MPX_ENABLE_MANAGEMENT,
7369 /// or PR_MPX_DISABLE_MANAGEMENT
7370 UnsupportedFeature,
7371 /// Can only occur with PR_SET_FP_MODE
7372 OperationNotSupported,
7373 PermissionDenied,
7374} || UnexpectedError;
7375
7376pub fn prctl(option: PR, args: anytype) PrctlError!u31 {
7377 if (@typeInfo(@TypeOf(args)) != .Struct)
7378 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
7379 if (args.len > 4)
7380 @compileError("prctl takes a maximum of 4 optional arguments");
7381
7382 var buf: [4]usize = undefined;
7383 {
7384 comptime var i = 0;
7385 inline while (i < args.len) : (i += 1) buf[i] = args[i];
7386 }
7387
7388 const rc = system.prctl(@intFromEnum(option), buf[0], buf[1], buf[2], buf[3]);
7389 switch (errno(rc)) {
7390 .SUCCESS => return @intCast(rc),
7391 .ACCES => return error.AccessDenied,
7392 .BADF => return error.InvalidFileDescriptor,
7393 .FAULT => return error.InvalidAddress,
7394 .INVAL => unreachable,
7395 .NODEV, .NXIO => return error.UnsupportedFeature,
7396 .OPNOTSUPP => return error.OperationNotSupported,
7397 .PERM, .BUSY => return error.PermissionDenied,
7398 .RANGE => unreachable,
7399 else => |err| return unexpectedErrno(err),
7400 }
7401}
7402
7403pub const GetrlimitError = UnexpectedError;
7404
7405pub fn getrlimit(resource: rlimit_resource) GetrlimitError!rlimit {
7406 const getrlimit_sym = if (lfs64_abi) system.getrlimit64 else system.getrlimit;
7407
7408 var limits: rlimit = undefined;
7409 switch (errno(getrlimit_sym(resource, &limits))) {
7410 .SUCCESS => return limits,
7411 .FAULT => unreachable, // bogus pointer
7412 .INVAL => unreachable,
7413 else => |err| return unexpectedErrno(err),
7414 }
7415}
7416
7417pub const SetrlimitError = error{ PermissionDenied, LimitTooBig } || UnexpectedError;
7418
7419pub fn setrlimit(resource: rlimit_resource, limits: rlimit) SetrlimitError!void {
7420 const setrlimit_sym = if (lfs64_abi) system.setrlimit64 else system.setrlimit;
7421
7422 switch (errno(setrlimit_sym(resource, &limits))) {
7423 .SUCCESS => return,
7424 .FAULT => unreachable, // bogus pointer
7425 .INVAL => return error.LimitTooBig, // this could also mean "invalid resource", but that would be unreachable
7426 .PERM => return error.PermissionDenied,
7427 else => |err| return unexpectedErrno(err),
7428 }
7429}
7430
7431pub const MincoreError = error{
7432 /// A kernel resource was temporarily unavailable.
7433 SystemResources,
7434 /// vec points to an invalid address.
7435 InvalidAddress,
7436 /// addr is not page-aligned.
7437 InvalidSyscall,
7438 /// One of the following:
7439 /// * length is greater than user space TASK_SIZE - addr
7440 /// * addr + length contains unmapped memory
7441 OutOfMemory,
7442 /// The mincore syscall is not available on this version and configuration
7443 /// of this UNIX-like kernel.
7444 MincoreUnavailable,
7445} || UnexpectedError;
7446
7447/// Determine whether pages are resident in memory.
7448pub fn mincore(ptr: [*]align(mem.page_size) u8, length: usize, vec: [*]u8) MincoreError!void {
7449 return switch (errno(system.mincore(ptr, length, vec))) {
7450 .SUCCESS => {},
7451 .AGAIN => error.SystemResources,
7452 .FAULT => error.InvalidAddress,
7453 .INVAL => error.InvalidSyscall,
7454 .NOMEM => error.OutOfMemory,
7455 .NOSYS => error.MincoreUnavailable,
7456 else => |err| unexpectedErrno(err),
7457 };
7458}
7459
7460pub const MadviseError = error{
7461 /// advice is MADV.REMOVE, but the specified address range is not a shared writable mapping.
7462 AccessDenied,
7463 /// advice is MADV.HWPOISON, but the caller does not have the CAP_SYS_ADMIN capability.
7464 PermissionDenied,
7465 /// A kernel resource was temporarily unavailable.
7466 SystemResources,
7467 /// One of the following:
7468 /// * addr is not page-aligned or length is negative
7469 /// * advice is not valid
7470 /// * advice is MADV.DONTNEED or MADV.REMOVE and the specified address range
7471 /// includes locked, Huge TLB pages, or VM_PFNMAP pages.
7472 /// * advice is MADV.MERGEABLE or MADV.UNMERGEABLE, but the kernel was not
7473 /// configured with CONFIG_KSM.
7474 /// * advice is MADV.FREE or MADV.WIPEONFORK but the specified address range
7475 /// includes file, Huge TLB, MAP.SHARED, or VM_PFNMAP ranges.
7476 InvalidSyscall,
7477 /// (for MADV.WILLNEED) Paging in this area would exceed the process's
7478 /// maximum resident set size.
7479 WouldExceedMaximumResidentSetSize,
7480 /// One of the following:
7481 /// * (for MADV.WILLNEED) Not enough memory: paging in failed.
7482 /// * Addresses in the specified range are not currently mapped, or
7483 /// are outside the address space of the process.
7484 OutOfMemory,
7485 /// The madvise syscall is not available on this version and configuration
7486 /// of the Linux kernel.
7487 MadviseUnavailable,
7488 /// The operating system returned an undocumented error code.
7489 Unexpected,
7490};
7491
7492/// Give advice about use of memory.
7493/// This syscall is optional and is sometimes configured to be disabled.
7494pub fn madvise(ptr: [*]align(mem.page_size) u8, length: usize, advice: u32) MadviseError!void {
7495 switch (errno(system.madvise(ptr, length, advice))) {
7496 .SUCCESS => return,
7497 .ACCES => return error.AccessDenied,
7498 .AGAIN => return error.SystemResources,
7499 .BADF => unreachable, // The map exists, but the area maps something that isn't a file.
7500 .INVAL => return error.InvalidSyscall,
7501 .IO => return error.WouldExceedMaximumResidentSetSize,
7502 .NOMEM => return error.OutOfMemory,
7503 .NOSYS => return error.MadviseUnavailable,
7504 else => |err| return unexpectedErrno(err),
7505 }
7506}
7507
7508pub const PerfEventOpenError = error{
7509 /// Returned if the perf_event_attr size value is too small (smaller
7510 /// than PERF_ATTR_SIZE_VER0), too big (larger than the page size),
7511 /// or larger than the kernel supports and the extra bytes are not
7512 /// zero. When E2BIG is returned, the perf_event_attr size field is
7513 /// overwritten by the kernel to be the size of the structure it was
7514 /// expecting.
7515 TooBig,
7516 /// Returned when the requested event requires CAP_SYS_ADMIN permis‐
7517 /// sions (or a more permissive perf_event paranoid setting). Some
7518 /// common cases where an unprivileged process may encounter this
7519 /// error: attaching to a process owned by a different user; moni‐
7520 /// toring all processes on a given CPU (i.e., specifying the pid
7521 /// argument as -1); and not setting exclude_kernel when the para‐
7522 /// noid setting requires it.
7523 /// Also:
7524 /// Returned on many (but not all) architectures when an unsupported
7525 /// exclude_hv, exclude_idle, exclude_user, or exclude_kernel set‐
7526 /// ting is specified.
7527 /// It can also happen, as with EACCES, when the requested event re‐
7528 /// quires CAP_SYS_ADMIN permissions (or a more permissive
7529 /// perf_event paranoid setting). This includes setting a break‐
7530 /// point on a kernel address, and (since Linux 3.13) setting a ker‐
7531 /// nel function-trace tracepoint.
7532 PermissionDenied,
7533 /// Returned if another event already has exclusive access to the
7534 /// PMU.
7535 DeviceBusy,
7536 /// Each opened event uses one file descriptor. If a large number
7537 /// of events are opened, the per-process limit on the number of
7538 /// open file descriptors will be reached, and no more events can be
7539 /// created.
7540 ProcessResources,
7541 EventRequiresUnsupportedCpuFeature,
7542 /// Returned if you try to add more breakpoint
7543 /// events than supported by the hardware.
7544 TooManyBreakpoints,
7545 /// Returned if PERF_SAMPLE_STACK_USER is set in sample_type and it
7546 /// is not supported by hardware.
7547 SampleStackNotSupported,
7548 /// Returned if an event requiring a specific hardware feature is
7549 /// requested but there is no hardware support. This includes re‐
7550 /// questing low-skid events if not supported, branch tracing if it
7551 /// is not available, sampling if no PMU interrupt is available, and
7552 /// branch stacks for software events.
7553 EventNotSupported,
7554 /// Returned if PERF_SAMPLE_CALLCHAIN is requested and sam‐
7555 /// ple_max_stack is larger than the maximum specified in
7556 /// /proc/sys/kernel/perf_event_max_stack.
7557 SampleMaxStackOverflow,
7558 /// Returned if attempting to attach to a process that does not exist.
7559 ProcessNotFound,
7560} || UnexpectedError;
7561
7562pub fn perf_event_open(
7563 attr: *linux.perf_event_attr,
7564 pid: pid_t,
7565 cpu: i32,
7566 group_fd: fd_t,
7567 flags: usize,
7568) PerfEventOpenError!fd_t {
7569 const rc = linux.perf_event_open(attr, pid, cpu, group_fd, flags);
7570 switch (errno(rc)) {
7571 .SUCCESS => return @intCast(rc),
7572 .@"2BIG" => return error.TooBig,
7573 .ACCES => return error.PermissionDenied,
7574 .BADF => unreachable, // group_fd file descriptor is not valid.
7575 .BUSY => return error.DeviceBusy,
7576 .FAULT => unreachable, // Segmentation fault.
7577 .INVAL => unreachable, // Bad attr settings.
7578 .INTR => unreachable, // Mixed perf and ftrace handling for a uprobe.
7579 .MFILE => return error.ProcessResources,
7580 .NODEV => return error.EventRequiresUnsupportedCpuFeature,
7581 .NOENT => unreachable, // Invalid type setting.
7582 .NOSPC => return error.TooManyBreakpoints,
7583 .NOSYS => return error.SampleStackNotSupported,
7584 .OPNOTSUPP => return error.EventNotSupported,
7585 .OVERFLOW => return error.SampleMaxStackOverflow,
7586 .PERM => return error.PermissionDenied,
7587 .SRCH => return error.ProcessNotFound,
7588 else => |err| return unexpectedErrno(err),
7589 }
7590}
7591
7592pub const TimerFdCreateError = error{
7593 AccessDenied,
7594 ProcessFdQuotaExceeded,
7595 SystemFdQuotaExceeded,
7596 NoDevice,
7597 SystemResources,
7598} || UnexpectedError;
7599
7600pub const TimerFdGetError = error{InvalidHandle} || UnexpectedError;
7601pub const TimerFdSetError = TimerFdGetError || error{Canceled};
7602
7603pub fn timerfd_create(clokid: i32, flags: linux.TFD) TimerFdCreateError!fd_t {
7604 const rc = linux.timerfd_create(clokid, flags);
7605 return switch (errno(rc)) {
7606 .SUCCESS => @intCast(rc),
7607 .INVAL => unreachable,
7608 .MFILE => return error.ProcessFdQuotaExceeded,
7609 .NFILE => return error.SystemFdQuotaExceeded,
7610 .NODEV => return error.NoDevice,
7611 .NOMEM => return error.SystemResources,
7612 .PERM => return error.AccessDenied,
7613 else => |err| return unexpectedErrno(err),
7614 };
7615}
7616
7617pub fn timerfd_settime(
7618 fd: i32,
7619 flags: linux.TFD.TIMER,
7620 new_value: *const linux.itimerspec,
7621 old_value: ?*linux.itimerspec,
7622) TimerFdSetError!void {
7623 const rc = linux.timerfd_settime(fd, flags, new_value, old_value);
7624 return switch (errno(rc)) {
7625 .SUCCESS => {},
7626 .BADF => error.InvalidHandle,
7627 .FAULT => unreachable,
7628 .INVAL => unreachable,
7629 .CANCELED => error.Canceled,
7630 else => |err| return unexpectedErrno(err),
7631 };
7632}
7633
7634pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec {
7635 var curr_value: linux.itimerspec = undefined;
7636 const rc = linux.timerfd_gettime(fd, &curr_value);
7637 return switch (errno(rc)) {
7638 .SUCCESS => return curr_value,
7639 .BADF => error.InvalidHandle,
7640 .FAULT => unreachable,
7641 .INVAL => unreachable,
7642 else => |err| return unexpectedErrno(err),
7643 };
7644}
7645
7646pub const PtraceError = error{
7647 DeviceBusy,
7648 InputOutput,
7649 ProcessNotFound,
7650 PermissionDenied,
7651} || UnexpectedError;
7652
7653pub fn ptrace(request: u32, pid: pid_t, addr: usize, signal: usize) PtraceError!void {
7654 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
7655 @compileError("Unsupported OS");
7656
7657 return switch (builtin.os.tag) {
7658 .linux => switch (errno(linux.ptrace(request, pid, addr, signal, 0))) {
7659 .SUCCESS => {},
7660 .SRCH => error.ProcessNotFound,
7661 .FAULT => unreachable,
7662 .INVAL => unreachable,
7663 .IO => return error.InputOutput,
7664 .PERM => error.PermissionDenied,
7665 .BUSY => error.DeviceBusy,
7666 else => |err| return unexpectedErrno(err),
7667 },
7668
7669 .macos, .ios, .tvos, .watchos => switch (errno(darwin.ptrace(
7670 @intCast(request),
7671 pid,
7672 @ptrFromInt(addr),
7673 @intCast(signal),
7674 ))) {
7675 .SUCCESS => {},
7676 .SRCH => error.ProcessNotFound,
7677 .INVAL => unreachable,
7678 .PERM => error.PermissionDenied,
7679 .BUSY => error.DeviceBusy,
7680 else => |err| return unexpectedErrno(err),
7681 },
7682
7683 else => switch (errno(system.ptrace(request, pid, addr, signal))) {
7684 .SUCCESS => {},
7685 .SRCH => error.ProcessNotFound,
7686 .INVAL => unreachable,
7687 .PERM => error.PermissionDenied,
7688 .BUSY => error.DeviceBusy,
7689 else => |err| return unexpectedErrno(err),
7690 },
7691 };
7692}
7693
7694const lfs64_abi = builtin.os.tag == .linux and builtin.link_libc and builtin.abi.isGnu();
lib/std/os/emscripten.zig+2-2
......@@ -1,8 +1,8 @@
11const std = @import("std");
22const builtin = @import("builtin");
33const wasi = std.os.wasi;
4const iovec = std.os.iovec;
5const iovec_const = std.os.iovec_const;
4const iovec = std.posix.iovec;
5const iovec_const = std.posix.iovec_const;
66const c = std.c;
77
88pub const FILE = c.FILE;
lib/std/os/linux.zig+610-13
......@@ -18,9 +18,9 @@ const is_mips = native_arch.isMIPS();
1818const is_ppc = native_arch.isPPC();
1919const is_ppc64 = native_arch.isPPC64();
2020const is_sparc = native_arch.isSPARC();
21const iovec = std.os.iovec;
22const iovec_const = std.os.iovec_const;
23const ACCMODE = std.os.ACCMODE;
21const iovec = std.posix.iovec;
22const iovec_const = std.posix.iovec_const;
23const ACCMODE = std.posix.ACCMODE;
2424
2525test {
2626 if (builtin.os.tag == .linux) {
......@@ -451,10 +451,11 @@ fn splitValue64(val: i64) [2]u32 {
451451}
452452
453453/// Get the errno from a syscall return value, or 0 for no error.
454pub fn getErrno(r: usize) E {
455 const signed_r = @as(isize, @bitCast(r));
454/// The public API is exposed via the `E` namespace.
455fn errnoFromSyscall(r: usize) E {
456 const signed_r: isize = @bitCast(r);
456457 const int = if (signed_r > -4096 and signed_r < 0) -signed_r else 0;
457 return @as(E, @enumFromInt(int));
458 return @enumFromInt(int);
458459}
459460
460461pub fn dup(old: i32) usize {
......@@ -1561,7 +1562,7 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact
15611562 .sparc, .sparc64 => syscall5(.rt_sigaction, sig, ksa_arg, oldksa_arg, @intFromPtr(ksa.restorer), mask_size),
15621563 else => syscall4(.rt_sigaction, sig, ksa_arg, oldksa_arg, mask_size),
15631564 };
1564 if (getErrno(result) != .SUCCESS) return result;
1565 if (E.init(result) != .SUCCESS) return result;
15651566
15661567 if (oact) |old| {
15671568 old.handler.handler = oldksa.handler;
......@@ -1648,12 +1649,12 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize
16481649 if (next_unsent < i) {
16491650 const batch_size = i - next_unsent;
16501651 const r = syscall4(.sendmmsg, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(&msgvec[next_unsent]), batch_size, flags);
1651 if (getErrno(r) != 0) return next_unsent;
1652 if (E.init(r) != 0) return next_unsent;
16521653 if (r < batch_size) return next_unsent + r;
16531654 }
16541655 // send current message as own packet
16551656 const r = sendmsg(fd, &msg.msg_hdr, flags);
1656 if (getErrno(r) != 0) return r;
1657 if (E.init(r) != 0) return r;
16571658 // Linux limits the total bytes sent by sendmsg to INT_MAX, so this cast is safe.
16581659 msg.msg_len = @as(u32, @intCast(r));
16591660 next_unsent = i + 1;
......@@ -1665,7 +1666,7 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize
16651666 if (next_unsent < kvlen or next_unsent == 0) { // want to make sure at least one syscall occurs (e.g. to trigger MSG.EOR)
16661667 const batch_size = kvlen - next_unsent;
16671668 const r = syscall4(.sendmmsg, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(&msgvec[next_unsent]), batch_size, flags);
1668 if (getErrno(r) != 0) return r;
1669 if (E.init(r) != 0) return r;
16691670 return next_unsent + r;
16701671 }
16711672 return kvlen;
......@@ -2263,13 +2264,609 @@ pub fn map_shadow_stack(addr: u64, size: u64, flags: u32) usize {
22632264}
22642265
22652266pub const E = switch (native_arch) {
2266 .mips, .mipsel => @import("linux/errno/mips.zig").E,
2267 .sparc, .sparcel, .sparc64 => @import("linux/errno/sparc.zig").E,
2268 else => @import("linux/errno/generic.zig").E,
2267 .mips, .mipsel => enum(i32) {
2268 /// No error occurred.
2269 SUCCESS = 0,
2270
2271 PERM = 1,
2272 NOENT = 2,
2273 SRCH = 3,
2274 INTR = 4,
2275 IO = 5,
2276 NXIO = 6,
2277 @"2BIG" = 7,
2278 NOEXEC = 8,
2279 BADF = 9,
2280 CHILD = 10,
2281 /// Also used for WOULDBLOCK.
2282 AGAIN = 11,
2283 NOMEM = 12,
2284 ACCES = 13,
2285 FAULT = 14,
2286 NOTBLK = 15,
2287 BUSY = 16,
2288 EXIST = 17,
2289 XDEV = 18,
2290 NODEV = 19,
2291 NOTDIR = 20,
2292 ISDIR = 21,
2293 INVAL = 22,
2294 NFILE = 23,
2295 MFILE = 24,
2296 NOTTY = 25,
2297 TXTBSY = 26,
2298 FBIG = 27,
2299 NOSPC = 28,
2300 SPIPE = 29,
2301 ROFS = 30,
2302 MLINK = 31,
2303 PIPE = 32,
2304 DOM = 33,
2305 RANGE = 34,
2306
2307 NOMSG = 35,
2308 IDRM = 36,
2309 CHRNG = 37,
2310 L2NSYNC = 38,
2311 L3HLT = 39,
2312 L3RST = 40,
2313 LNRNG = 41,
2314 UNATCH = 42,
2315 NOCSI = 43,
2316 L2HLT = 44,
2317 DEADLK = 45,
2318 NOLCK = 46,
2319 BADE = 50,
2320 BADR = 51,
2321 XFULL = 52,
2322 NOANO = 53,
2323 BADRQC = 54,
2324 BADSLT = 55,
2325 DEADLOCK = 56,
2326 BFONT = 59,
2327 NOSTR = 60,
2328 NODATA = 61,
2329 TIME = 62,
2330 NOSR = 63,
2331 NONET = 64,
2332 NOPKG = 65,
2333 REMOTE = 66,
2334 NOLINK = 67,
2335 ADV = 68,
2336 SRMNT = 69,
2337 COMM = 70,
2338 PROTO = 71,
2339 DOTDOT = 73,
2340 MULTIHOP = 74,
2341 BADMSG = 77,
2342 NAMETOOLONG = 78,
2343 OVERFLOW = 79,
2344 NOTUNIQ = 80,
2345 BADFD = 81,
2346 REMCHG = 82,
2347 LIBACC = 83,
2348 LIBBAD = 84,
2349 LIBSCN = 85,
2350 LIBMAX = 86,
2351 LIBEXEC = 87,
2352 ILSEQ = 88,
2353 NOSYS = 89,
2354 LOOP = 90,
2355 RESTART = 91,
2356 STRPIPE = 92,
2357 NOTEMPTY = 93,
2358 USERS = 94,
2359 NOTSOCK = 95,
2360 DESTADDRREQ = 96,
2361 MSGSIZE = 97,
2362 PROTOTYPE = 98,
2363 NOPROTOOPT = 99,
2364 PROTONOSUPPORT = 120,
2365 SOCKTNOSUPPORT = 121,
2366 OPNOTSUPP = 122,
2367 PFNOSUPPORT = 123,
2368 AFNOSUPPORT = 124,
2369 ADDRINUSE = 125,
2370 ADDRNOTAVAIL = 126,
2371 NETDOWN = 127,
2372 NETUNREACH = 128,
2373 NETRESET = 129,
2374 CONNABORTED = 130,
2375 CONNRESET = 131,
2376 NOBUFS = 132,
2377 ISCONN = 133,
2378 NOTCONN = 134,
2379 UCLEAN = 135,
2380 NOTNAM = 137,
2381 NAVAIL = 138,
2382 ISNAM = 139,
2383 REMOTEIO = 140,
2384 SHUTDOWN = 143,
2385 TOOMANYREFS = 144,
2386 TIMEDOUT = 145,
2387 CONNREFUSED = 146,
2388 HOSTDOWN = 147,
2389 HOSTUNREACH = 148,
2390 ALREADY = 149,
2391 INPROGRESS = 150,
2392 STALE = 151,
2393 CANCELED = 158,
2394 NOMEDIUM = 159,
2395 MEDIUMTYPE = 160,
2396 NOKEY = 161,
2397 KEYEXPIRED = 162,
2398 KEYREVOKED = 163,
2399 KEYREJECTED = 164,
2400 OWNERDEAD = 165,
2401 NOTRECOVERABLE = 166,
2402 RFKILL = 167,
2403 HWPOISON = 168,
2404 DQUOT = 1133,
2405 _,
2406
2407 pub const init = errnoFromSyscall;
2408 },
2409 .sparc, .sparcel, .sparc64 => enum(i32) {
2410 /// No error occurred.
2411 SUCCESS = 0,
2412
2413 PERM = 1,
2414 NOENT = 2,
2415 SRCH = 3,
2416 INTR = 4,
2417 IO = 5,
2418 NXIO = 6,
2419 @"2BIG" = 7,
2420 NOEXEC = 8,
2421 BADF = 9,
2422 CHILD = 10,
2423 /// Also used for WOULDBLOCK
2424 AGAIN = 11,
2425 NOMEM = 12,
2426 ACCES = 13,
2427 FAULT = 14,
2428 NOTBLK = 15,
2429 BUSY = 16,
2430 EXIST = 17,
2431 XDEV = 18,
2432 NODEV = 19,
2433 NOTDIR = 20,
2434 ISDIR = 21,
2435 INVAL = 22,
2436 NFILE = 23,
2437 MFILE = 24,
2438 NOTTY = 25,
2439 TXTBSY = 26,
2440 FBIG = 27,
2441 NOSPC = 28,
2442 SPIPE = 29,
2443 ROFS = 30,
2444 MLINK = 31,
2445 PIPE = 32,
2446 DOM = 33,
2447 RANGE = 34,
2448
2449 INPROGRESS = 36,
2450 ALREADY = 37,
2451 NOTSOCK = 38,
2452 DESTADDRREQ = 39,
2453 MSGSIZE = 40,
2454 PROTOTYPE = 41,
2455 NOPROTOOPT = 42,
2456 PROTONOSUPPORT = 43,
2457 SOCKTNOSUPPORT = 44,
2458 /// Also used for NOTSUP
2459 OPNOTSUPP = 45,
2460 PFNOSUPPORT = 46,
2461 AFNOSUPPORT = 47,
2462 ADDRINUSE = 48,
2463 ADDRNOTAVAIL = 49,
2464 NETDOWN = 50,
2465 NETUNREACH = 51,
2466 NETRESET = 52,
2467 CONNABORTED = 53,
2468 CONNRESET = 54,
2469 NOBUFS = 55,
2470 ISCONN = 56,
2471 NOTCONN = 57,
2472 SHUTDOWN = 58,
2473 TOOMANYREFS = 59,
2474 TIMEDOUT = 60,
2475 CONNREFUSED = 61,
2476 LOOP = 62,
2477 NAMETOOLONG = 63,
2478 HOSTDOWN = 64,
2479 HOSTUNREACH = 65,
2480 NOTEMPTY = 66,
2481 PROCLIM = 67,
2482 USERS = 68,
2483 DQUOT = 69,
2484 STALE = 70,
2485 REMOTE = 71,
2486 NOSTR = 72,
2487 TIME = 73,
2488 NOSR = 74,
2489 NOMSG = 75,
2490 BADMSG = 76,
2491 IDRM = 77,
2492 DEADLK = 78,
2493 NOLCK = 79,
2494 NONET = 80,
2495 RREMOTE = 81,
2496 NOLINK = 82,
2497 ADV = 83,
2498 SRMNT = 84,
2499 COMM = 85,
2500 PROTO = 86,
2501 MULTIHOP = 87,
2502 DOTDOT = 88,
2503 REMCHG = 89,
2504 NOSYS = 90,
2505 STRPIPE = 91,
2506 OVERFLOW = 92,
2507 BADFD = 93,
2508 CHRNG = 94,
2509 L2NSYNC = 95,
2510 L3HLT = 96,
2511 L3RST = 97,
2512 LNRNG = 98,
2513 UNATCH = 99,
2514 NOCSI = 100,
2515 L2HLT = 101,
2516 BADE = 102,
2517 BADR = 103,
2518 XFULL = 104,
2519 NOANO = 105,
2520 BADRQC = 106,
2521 BADSLT = 107,
2522 DEADLOCK = 108,
2523 BFONT = 109,
2524 LIBEXEC = 110,
2525 NODATA = 111,
2526 LIBBAD = 112,
2527 NOPKG = 113,
2528 LIBACC = 114,
2529 NOTUNIQ = 115,
2530 RESTART = 116,
2531 UCLEAN = 117,
2532 NOTNAM = 118,
2533 NAVAIL = 119,
2534 ISNAM = 120,
2535 REMOTEIO = 121,
2536 ILSEQ = 122,
2537 LIBMAX = 123,
2538 LIBSCN = 124,
2539 NOMEDIUM = 125,
2540 MEDIUMTYPE = 126,
2541 CANCELED = 127,
2542 NOKEY = 128,
2543 KEYEXPIRED = 129,
2544 KEYREVOKED = 130,
2545 KEYREJECTED = 131,
2546 OWNERDEAD = 132,
2547 NOTRECOVERABLE = 133,
2548 RFKILL = 134,
2549 HWPOISON = 135,
2550 _,
2551
2552 pub const init = errnoFromSyscall;
2553 },
2554 else => enum(u16) {
2555 /// No error occurred.
2556 /// Same code used for `NSROK`.
2557 SUCCESS = 0,
2558 /// Operation not permitted
2559 PERM = 1,
2560 /// No such file or directory
2561 NOENT = 2,
2562 /// No such process
2563 SRCH = 3,
2564 /// Interrupted system call
2565 INTR = 4,
2566 /// I/O error
2567 IO = 5,
2568 /// No such device or address
2569 NXIO = 6,
2570 /// Arg list too long
2571 @"2BIG" = 7,
2572 /// Exec format error
2573 NOEXEC = 8,
2574 /// Bad file number
2575 BADF = 9,
2576 /// No child processes
2577 CHILD = 10,
2578 /// Try again
2579 /// Also means: WOULDBLOCK: operation would block
2580 AGAIN = 11,
2581 /// Out of memory
2582 NOMEM = 12,
2583 /// Permission denied
2584 ACCES = 13,
2585 /// Bad address
2586 FAULT = 14,
2587 /// Block device required
2588 NOTBLK = 15,
2589 /// Device or resource busy
2590 BUSY = 16,
2591 /// File exists
2592 EXIST = 17,
2593 /// Cross-device link
2594 XDEV = 18,
2595 /// No such device
2596 NODEV = 19,
2597 /// Not a directory
2598 NOTDIR = 20,
2599 /// Is a directory
2600 ISDIR = 21,
2601 /// Invalid argument
2602 INVAL = 22,
2603 /// File table overflow
2604 NFILE = 23,
2605 /// Too many open files
2606 MFILE = 24,
2607 /// Not a typewriter
2608 NOTTY = 25,
2609 /// Text file busy
2610 TXTBSY = 26,
2611 /// File too large
2612 FBIG = 27,
2613 /// No space left on device
2614 NOSPC = 28,
2615 /// Illegal seek
2616 SPIPE = 29,
2617 /// Read-only file system
2618 ROFS = 30,
2619 /// Too many links
2620 MLINK = 31,
2621 /// Broken pipe
2622 PIPE = 32,
2623 /// Math argument out of domain of func
2624 DOM = 33,
2625 /// Math result not representable
2626 RANGE = 34,
2627 /// Resource deadlock would occur
2628 DEADLK = 35,
2629 /// File name too long
2630 NAMETOOLONG = 36,
2631 /// No record locks available
2632 NOLCK = 37,
2633 /// Function not implemented
2634 NOSYS = 38,
2635 /// Directory not empty
2636 NOTEMPTY = 39,
2637 /// Too many symbolic links encountered
2638 LOOP = 40,
2639 /// No message of desired type
2640 NOMSG = 42,
2641 /// Identifier removed
2642 IDRM = 43,
2643 /// Channel number out of range
2644 CHRNG = 44,
2645 /// Level 2 not synchronized
2646 L2NSYNC = 45,
2647 /// Level 3 halted
2648 L3HLT = 46,
2649 /// Level 3 reset
2650 L3RST = 47,
2651 /// Link number out of range
2652 LNRNG = 48,
2653 /// Protocol driver not attached
2654 UNATCH = 49,
2655 /// No CSI structure available
2656 NOCSI = 50,
2657 /// Level 2 halted
2658 L2HLT = 51,
2659 /// Invalid exchange
2660 BADE = 52,
2661 /// Invalid request descriptor
2662 BADR = 53,
2663 /// Exchange full
2664 XFULL = 54,
2665 /// No anode
2666 NOANO = 55,
2667 /// Invalid request code
2668 BADRQC = 56,
2669 /// Invalid slot
2670 BADSLT = 57,
2671 /// Bad font file format
2672 BFONT = 59,
2673 /// Device not a stream
2674 NOSTR = 60,
2675 /// No data available
2676 NODATA = 61,
2677 /// Timer expired
2678 TIME = 62,
2679 /// Out of streams resources
2680 NOSR = 63,
2681 /// Machine is not on the network
2682 NONET = 64,
2683 /// Package not installed
2684 NOPKG = 65,
2685 /// Object is remote
2686 REMOTE = 66,
2687 /// Link has been severed
2688 NOLINK = 67,
2689 /// Advertise error
2690 ADV = 68,
2691 /// Srmount error
2692 SRMNT = 69,
2693 /// Communication error on send
2694 COMM = 70,
2695 /// Protocol error
2696 PROTO = 71,
2697 /// Multihop attempted
2698 MULTIHOP = 72,
2699 /// RFS specific error
2700 DOTDOT = 73,
2701 /// Not a data message
2702 BADMSG = 74,
2703 /// Value too large for defined data type
2704 OVERFLOW = 75,
2705 /// Name not unique on network
2706 NOTUNIQ = 76,
2707 /// File descriptor in bad state
2708 BADFD = 77,
2709 /// Remote address changed
2710 REMCHG = 78,
2711 /// Can not access a needed shared library
2712 LIBACC = 79,
2713 /// Accessing a corrupted shared library
2714 LIBBAD = 80,
2715 /// .lib section in a.out corrupted
2716 LIBSCN = 81,
2717 /// Attempting to link in too many shared libraries
2718 LIBMAX = 82,
2719 /// Cannot exec a shared library directly
2720 LIBEXEC = 83,
2721 /// Illegal byte sequence
2722 ILSEQ = 84,
2723 /// Interrupted system call should be restarted
2724 RESTART = 85,
2725 /// Streams pipe error
2726 STRPIPE = 86,
2727 /// Too many users
2728 USERS = 87,
2729 /// Socket operation on non-socket
2730 NOTSOCK = 88,
2731 /// Destination address required
2732 DESTADDRREQ = 89,
2733 /// Message too long
2734 MSGSIZE = 90,
2735 /// Protocol wrong type for socket
2736 PROTOTYPE = 91,
2737 /// Protocol not available
2738 NOPROTOOPT = 92,
2739 /// Protocol not supported
2740 PROTONOSUPPORT = 93,
2741 /// Socket type not supported
2742 SOCKTNOSUPPORT = 94,
2743 /// Operation not supported on transport endpoint
2744 /// This code also means `NOTSUP`.
2745 OPNOTSUPP = 95,
2746 /// Protocol family not supported
2747 PFNOSUPPORT = 96,
2748 /// Address family not supported by protocol
2749 AFNOSUPPORT = 97,
2750 /// Address already in use
2751 ADDRINUSE = 98,
2752 /// Cannot assign requested address
2753 ADDRNOTAVAIL = 99,
2754 /// Network is down
2755 NETDOWN = 100,
2756 /// Network is unreachable
2757 NETUNREACH = 101,
2758 /// Network dropped connection because of reset
2759 NETRESET = 102,
2760 /// Software caused connection abort
2761 CONNABORTED = 103,
2762 /// Connection reset by peer
2763 CONNRESET = 104,
2764 /// No buffer space available
2765 NOBUFS = 105,
2766 /// Transport endpoint is already connected
2767 ISCONN = 106,
2768 /// Transport endpoint is not connected
2769 NOTCONN = 107,
2770 /// Cannot send after transport endpoint shutdown
2771 SHUTDOWN = 108,
2772 /// Too many references: cannot splice
2773 TOOMANYREFS = 109,
2774 /// Connection timed out
2775 TIMEDOUT = 110,
2776 /// Connection refused
2777 CONNREFUSED = 111,
2778 /// Host is down
2779 HOSTDOWN = 112,
2780 /// No route to host
2781 HOSTUNREACH = 113,
2782 /// Operation already in progress
2783 ALREADY = 114,
2784 /// Operation now in progress
2785 INPROGRESS = 115,
2786 /// Stale NFS file handle
2787 STALE = 116,
2788 /// Structure needs cleaning
2789 UCLEAN = 117,
2790 /// Not a XENIX named type file
2791 NOTNAM = 118,
2792 /// No XENIX semaphores available
2793 NAVAIL = 119,
2794 /// Is a named type file
2795 ISNAM = 120,
2796 /// Remote I/O error
2797 REMOTEIO = 121,
2798 /// Quota exceeded
2799 DQUOT = 122,
2800 /// No medium found
2801 NOMEDIUM = 123,
2802 /// Wrong medium type
2803 MEDIUMTYPE = 124,
2804 /// Operation canceled
2805 CANCELED = 125,
2806 /// Required key not available
2807 NOKEY = 126,
2808 /// Key has expired
2809 KEYEXPIRED = 127,
2810 /// Key has been revoked
2811 KEYREVOKED = 128,
2812 /// Key was rejected by service
2813 KEYREJECTED = 129,
2814 // for robust mutexes
2815 /// Owner died
2816 OWNERDEAD = 130,
2817 /// State not recoverable
2818 NOTRECOVERABLE = 131,
2819 /// Operation not possible due to RF-kill
2820 RFKILL = 132,
2821 /// Memory page has hardware error
2822 HWPOISON = 133,
2823 // nameserver query return codes
2824 /// DNS server returned answer with no data
2825 NSRNODATA = 160,
2826 /// DNS server claims query was misformatted
2827 NSRFORMERR = 161,
2828 /// DNS server returned general failure
2829 NSRSERVFAIL = 162,
2830 /// Domain name not found
2831 NSRNOTFOUND = 163,
2832 /// DNS server does not implement requested operation
2833 NSRNOTIMP = 164,
2834 /// DNS server refused query
2835 NSRREFUSED = 165,
2836 /// Misformatted DNS query
2837 NSRBADQUERY = 166,
2838 /// Misformatted domain name
2839 NSRBADNAME = 167,
2840 /// Unsupported address family
2841 NSRBADFAMILY = 168,
2842 /// Misformatted DNS reply
2843 NSRBADRESP = 169,
2844 /// Could not contact DNS servers
2845 NSRCONNREFUSED = 170,
2846 /// Timeout while contacting DNS servers
2847 NSRTIMEOUT = 171,
2848 /// End of file
2849 NSROF = 172,
2850 /// Error reading file
2851 NSRFILE = 173,
2852 /// Out of memory
2853 NSRNOMEM = 174,
2854 /// Application terminated lookup
2855 NSRDESTRUCTION = 175,
2856 /// Domain name is too long
2857 NSRQUERYDOMAINTOOLONG = 176,
2858 /// Domain name is too long
2859 NSRCNAMELOOP = 177,
2860
2861 _,
2862
2863 pub const init = errnoFromSyscall;
2864 },
22692865};
22702866
22712867pub const pid_t = i32;
22722868pub const fd_t = i32;
2869pub const socket_t = i32;
22732870pub const uid_t = u32;
22742871pub const gid_t = u32;
22752872pub const clock_t = isize;
lib/std/os/linux/IoUring.zig+251-251
......@@ -4,12 +4,12 @@ const builtin = @import("builtin");
44const assert = std.debug.assert;
55const mem = std.mem;
66const net = std.net;
7const os = std.os;
87const posix = std.posix;
9const linux = os.linux;
8const linux = std.os.linux;
109const testing = std.testing;
10const is_linux = builtin.os.tag == .linux;
1111
12fd: os.fd_t = -1,
12fd: posix.fd_t = -1,
1313sq: SubmissionQueue,
1414cq: CompletionQueue,
1515flags: u32,
......@@ -45,7 +45,7 @@ pub fn init_params(entries: u16, p: *linux.io_uring_params) !IoUring {
4545 assert(p.resv[2] == 0);
4646
4747 const res = linux.io_uring_setup(entries, p);
48 switch (linux.getErrno(res)) {
48 switch (linux.E.init(res)) {
4949 .SUCCESS => {},
5050 .FAULT => return error.ParamsOutsideAccessibleAddressSpace,
5151 // The resv array contains non-zero data, p.flags contains an unsupported flag,
......@@ -59,11 +59,11 @@ pub fn init_params(entries: u16, p: *linux.io_uring_params) !IoUring {
5959 // or a container seccomp policy prohibits io_uring syscalls:
6060 .PERM => return error.PermissionDenied,
6161 .NOSYS => return error.SystemOutdated,
62 else => |errno| return os.unexpectedErrno(errno),
62 else => |errno| return posix.unexpectedErrno(errno),
6363 }
64 const fd = @as(os.fd_t, @intCast(res));
64 const fd = @as(posix.fd_t, @intCast(res));
6565 assert(fd >= 0);
66 errdefer os.close(fd);
66 errdefer posix.close(fd);
6767
6868 // Kernel versions 5.4 and up use only one mmap() for the submission and completion queues.
6969 // This is not an optional feature for us... if the kernel does it, we have to do it.
......@@ -121,7 +121,7 @@ pub fn deinit(self: *IoUring) void {
121121 // The mmaps depend on the fd, so the order of these calls is important:
122122 self.cq.deinit();
123123 self.sq.deinit();
124 os.close(self.fd);
124 posix.close(self.fd);
125125 self.fd = -1;
126126}
127127
......@@ -174,7 +174,7 @@ pub fn submit_and_wait(self: *IoUring, wait_nr: u32) !u32 {
174174pub fn enter(self: *IoUring, to_submit: u32, min_complete: u32, flags: u32) !u32 {
175175 assert(self.fd >= 0);
176176 const res = linux.io_uring_enter(self.fd, to_submit, min_complete, flags, null);
177 switch (linux.getErrno(res)) {
177 switch (linux.E.init(res)) {
178178 .SUCCESS => {},
179179 // The kernel was unable to allocate memory or ran out of resources for the request.
180180 // The application should wait for some completions and try again:
......@@ -200,7 +200,7 @@ pub fn enter(self: *IoUring, to_submit: u32, min_complete: u32, flags: u32) !u32
200200 // The operation was interrupted by a delivery of a signal before it could complete.
201201 // This can happen while waiting for events with IORING_ENTER_GETEVENTS:
202202 .INTR => return error.SignalInterrupt,
203 else => |errno| return os.unexpectedErrno(errno),
203 else => |errno| return posix.unexpectedErrno(errno),
204204 }
205205 return @as(u32, @intCast(res));
206206}
......@@ -344,7 +344,7 @@ pub fn cq_advance(self: *IoUring, count: u32) void {
344344/// apply to the write, since the fsync may complete before the write is issued to the disk.
345345/// You should preferably use `link_with_next_sqe()` on a write's SQE to link it with an fsync,
346346/// or else insert a full write barrier using `drain_previous_sqes()` when queueing an fsync.
347pub fn fsync(self: *IoUring, user_data: u64, fd: os.fd_t, flags: u32) !*linux.io_uring_sqe {
347pub fn fsync(self: *IoUring, user_data: u64, fd: posix.fd_t, flags: u32) !*linux.io_uring_sqe {
348348 const sqe = try self.get_sqe();
349349 sqe.prep_fsync(fd, flags);
350350 sqe.user_data = user_data;
......@@ -369,7 +369,7 @@ pub const ReadBuffer = union(enum) {
369369 buffer: []u8,
370370
371371 /// io_uring will read directly into these buffers using readv.
372 iovecs: []const os.iovec,
372 iovecs: []const posix.iovec,
373373
374374 /// io_uring will select a buffer that has previously been provided with `provide_buffers`.
375375 /// The buffer group reference by `group_id` must contain at least one buffer for the read to work.
......@@ -389,7 +389,7 @@ pub const ReadBuffer = union(enum) {
389389pub fn read(
390390 self: *IoUring,
391391 user_data: u64,
392 fd: os.fd_t,
392 fd: posix.fd_t,
393393 buffer: ReadBuffer,
394394 offset: u64,
395395) !*linux.io_uring_sqe {
......@@ -412,7 +412,7 @@ pub fn read(
412412pub fn write(
413413 self: *IoUring,
414414 user_data: u64,
415 fd: os.fd_t,
415 fd: posix.fd_t,
416416 buffer: []const u8,
417417 offset: u64,
418418) !*linux.io_uring_sqe {
......@@ -436,7 +436,7 @@ pub fn write(
436436/// See https://github.com/axboe/liburing/issues/291
437437///
438438/// Returns a pointer to the SQE so that you can further modify the SQE for advanced use cases.
439pub fn splice(self: *IoUring, user_data: u64, fd_in: os.fd_t, off_in: u64, fd_out: os.fd_t, off_out: u64, len: usize) !*linux.io_uring_sqe {
439pub fn splice(self: *IoUring, user_data: u64, fd_in: posix.fd_t, off_in: u64, fd_out: posix.fd_t, off_out: u64, len: usize) !*linux.io_uring_sqe {
440440 const sqe = try self.get_sqe();
441441 sqe.prep_splice(fd_in, off_in, fd_out, off_out, len);
442442 sqe.user_data = user_data;
......@@ -451,8 +451,8 @@ pub fn splice(self: *IoUring, user_data: u64, fd_in: os.fd_t, off_in: u64, fd_ou
451451pub fn read_fixed(
452452 self: *IoUring,
453453 user_data: u64,
454 fd: os.fd_t,
455 buffer: *os.iovec,
454 fd: posix.fd_t,
455 buffer: *posix.iovec,
456456 offset: u64,
457457 buffer_index: u16,
458458) !*linux.io_uring_sqe {
......@@ -469,8 +469,8 @@ pub fn read_fixed(
469469pub fn writev(
470470 self: *IoUring,
471471 user_data: u64,
472 fd: os.fd_t,
473 iovecs: []const os.iovec_const,
472 fd: posix.fd_t,
473 iovecs: []const posix.iovec_const,
474474 offset: u64,
475475) !*linux.io_uring_sqe {
476476 const sqe = try self.get_sqe();
......@@ -487,8 +487,8 @@ pub fn writev(
487487pub fn write_fixed(
488488 self: *IoUring,
489489 user_data: u64,
490 fd: os.fd_t,
491 buffer: *os.iovec,
490 fd: posix.fd_t,
491 buffer: *posix.iovec,
492492 offset: u64,
493493 buffer_index: u16,
494494) !*linux.io_uring_sqe {
......@@ -504,9 +504,9 @@ pub fn write_fixed(
504504pub fn accept(
505505 self: *IoUring,
506506 user_data: u64,
507 fd: os.fd_t,
508 addr: ?*os.sockaddr,
509 addrlen: ?*os.socklen_t,
507 fd: posix.fd_t,
508 addr: ?*posix.sockaddr,
509 addrlen: ?*posix.socklen_t,
510510 flags: u32,
511511) !*linux.io_uring_sqe {
512512 const sqe = try self.get_sqe();
......@@ -526,9 +526,9 @@ pub fn accept(
526526pub fn accept_multishot(
527527 self: *IoUring,
528528 user_data: u64,
529 fd: os.fd_t,
530 addr: ?*os.sockaddr,
531 addrlen: ?*os.socklen_t,
529 fd: posix.fd_t,
530 addr: ?*posix.sockaddr,
531 addrlen: ?*posix.socklen_t,
532532 flags: u32,
533533) !*linux.io_uring_sqe {
534534 const sqe = try self.get_sqe();
......@@ -551,9 +551,9 @@ pub fn accept_multishot(
551551pub fn accept_direct(
552552 self: *IoUring,
553553 user_data: u64,
554 fd: os.fd_t,
555 addr: ?*os.sockaddr,
556 addrlen: ?*os.socklen_t,
554 fd: posix.fd_t,
555 addr: ?*posix.sockaddr,
556 addrlen: ?*posix.socklen_t,
557557 flags: u32,
558558) !*linux.io_uring_sqe {
559559 const sqe = try self.get_sqe();
......@@ -567,9 +567,9 @@ pub fn accept_direct(
567567pub fn accept_multishot_direct(
568568 self: *IoUring,
569569 user_data: u64,
570 fd: os.fd_t,
571 addr: ?*os.sockaddr,
572 addrlen: ?*os.socklen_t,
570 fd: posix.fd_t,
571 addr: ?*posix.sockaddr,
572 addrlen: ?*posix.socklen_t,
573573 flags: u32,
574574) !*linux.io_uring_sqe {
575575 const sqe = try self.get_sqe();
......@@ -583,9 +583,9 @@ pub fn accept_multishot_direct(
583583pub fn connect(
584584 self: *IoUring,
585585 user_data: u64,
586 fd: os.fd_t,
587 addr: *const os.sockaddr,
588 addrlen: os.socklen_t,
586 fd: posix.fd_t,
587 addr: *const posix.sockaddr,
588 addrlen: posix.socklen_t,
589589) !*linux.io_uring_sqe {
590590 const sqe = try self.get_sqe();
591591 sqe.prep_connect(fd, addr, addrlen);
......@@ -598,8 +598,8 @@ pub fn connect(
598598pub fn epoll_ctl(
599599 self: *IoUring,
600600 user_data: u64,
601 epfd: os.fd_t,
602 fd: os.fd_t,
601 epfd: posix.fd_t,
602 fd: posix.fd_t,
603603 op: u32,
604604 ev: ?*linux.epoll_event,
605605) !*linux.io_uring_sqe {
......@@ -629,7 +629,7 @@ pub const RecvBuffer = union(enum) {
629629pub fn recv(
630630 self: *IoUring,
631631 user_data: u64,
632 fd: os.fd_t,
632 fd: posix.fd_t,
633633 buffer: RecvBuffer,
634634 flags: u32,
635635) !*linux.io_uring_sqe {
......@@ -653,7 +653,7 @@ pub fn recv(
653653pub fn send(
654654 self: *IoUring,
655655 user_data: u64,
656 fd: os.fd_t,
656 fd: posix.fd_t,
657657 buffer: []const u8,
658658 flags: u32,
659659) !*linux.io_uring_sqe {
......@@ -681,7 +681,7 @@ pub fn send(
681681pub fn send_zc(
682682 self: *IoUring,
683683 user_data: u64,
684 fd: os.fd_t,
684 fd: posix.fd_t,
685685 buffer: []const u8,
686686 send_flags: u32,
687687 zc_flags: u16,
......@@ -698,7 +698,7 @@ pub fn send_zc(
698698pub fn send_zc_fixed(
699699 self: *IoUring,
700700 user_data: u64,
701 fd: os.fd_t,
701 fd: posix.fd_t,
702702 buffer: []const u8,
703703 send_flags: u32,
704704 zc_flags: u16,
......@@ -716,8 +716,8 @@ pub fn send_zc_fixed(
716716pub fn recvmsg(
717717 self: *IoUring,
718718 user_data: u64,
719 fd: os.fd_t,
720 msg: *os.msghdr,
719 fd: posix.fd_t,
720 msg: *posix.msghdr,
721721 flags: u32,
722722) !*linux.io_uring_sqe {
723723 const sqe = try self.get_sqe();
......@@ -732,8 +732,8 @@ pub fn recvmsg(
732732pub fn sendmsg(
733733 self: *IoUring,
734734 user_data: u64,
735 fd: os.fd_t,
736 msg: *const os.msghdr_const,
735 fd: posix.fd_t,
736 msg: *const posix.msghdr_const,
737737 flags: u32,
738738) !*linux.io_uring_sqe {
739739 const sqe = try self.get_sqe();
......@@ -748,8 +748,8 @@ pub fn sendmsg(
748748pub fn sendmsg_zc(
749749 self: *IoUring,
750750 user_data: u64,
751 fd: os.fd_t,
752 msg: *const os.msghdr_const,
751 fd: posix.fd_t,
752 msg: *const posix.msghdr_const,
753753 flags: u32,
754754) !*linux.io_uring_sqe {
755755 const sqe = try self.get_sqe();
......@@ -764,10 +764,10 @@ pub fn sendmsg_zc(
764764pub fn openat(
765765 self: *IoUring,
766766 user_data: u64,
767 fd: os.fd_t,
767 fd: posix.fd_t,
768768 path: [*:0]const u8,
769769 flags: linux.O,
770 mode: os.mode_t,
770 mode: posix.mode_t,
771771) !*linux.io_uring_sqe {
772772 const sqe = try self.get_sqe();
773773 sqe.prep_openat(fd, path, flags, mode);
......@@ -789,10 +789,10 @@ pub fn openat(
789789pub fn openat_direct(
790790 self: *IoUring,
791791 user_data: u64,
792 fd: os.fd_t,
792 fd: posix.fd_t,
793793 path: [*:0]const u8,
794794 flags: linux.O,
795 mode: os.mode_t,
795 mode: posix.mode_t,
796796 file_index: u32,
797797) !*linux.io_uring_sqe {
798798 const sqe = try self.get_sqe();
......@@ -804,7 +804,7 @@ pub fn openat_direct(
804804/// Queues (but does not submit) an SQE to perform a `close(2)`.
805805/// Returns a pointer to the SQE.
806806/// Available since 5.6.
807pub fn close(self: *IoUring, user_data: u64, fd: os.fd_t) !*linux.io_uring_sqe {
807pub fn close(self: *IoUring, user_data: u64, fd: posix.fd_t) !*linux.io_uring_sqe {
808808 const sqe = try self.get_sqe();
809809 sqe.prep_close(fd);
810810 sqe.user_data = user_data;
......@@ -836,7 +836,7 @@ pub fn close_direct(self: *IoUring, user_data: u64, file_index: u32) !*linux.io_
836836pub fn timeout(
837837 self: *IoUring,
838838 user_data: u64,
839 ts: *const os.linux.kernel_timespec,
839 ts: *const linux.kernel_timespec,
840840 count: u32,
841841 flags: u32,
842842) !*linux.io_uring_sqe {
......@@ -885,7 +885,7 @@ pub fn timeout_remove(
885885pub fn link_timeout(
886886 self: *IoUring,
887887 user_data: u64,
888 ts: *const os.linux.kernel_timespec,
888 ts: *const linux.kernel_timespec,
889889 flags: u32,
890890) !*linux.io_uring_sqe {
891891 const sqe = try self.get_sqe();
......@@ -899,7 +899,7 @@ pub fn link_timeout(
899899pub fn poll_add(
900900 self: *IoUring,
901901 user_data: u64,
902 fd: os.fd_t,
902 fd: posix.fd_t,
903903 poll_mask: u32,
904904) !*linux.io_uring_sqe {
905905 const sqe = try self.get_sqe();
......@@ -942,7 +942,7 @@ pub fn poll_update(
942942pub fn fallocate(
943943 self: *IoUring,
944944 user_data: u64,
945 fd: os.fd_t,
945 fd: posix.fd_t,
946946 mode: i32,
947947 offset: u64,
948948 len: u64,
......@@ -958,7 +958,7 @@ pub fn fallocate(
958958pub fn statx(
959959 self: *IoUring,
960960 user_data: u64,
961 fd: os.fd_t,
961 fd: posix.fd_t,
962962 path: [:0]const u8,
963963 flags: u32,
964964 mask: u32,
......@@ -997,7 +997,7 @@ pub fn cancel(
997997pub fn shutdown(
998998 self: *IoUring,
999999 user_data: u64,
1000 sockfd: os.socket_t,
1000 sockfd: posix.socket_t,
10011001 how: u32,
10021002) !*linux.io_uring_sqe {
10031003 const sqe = try self.get_sqe();
......@@ -1011,9 +1011,9 @@ pub fn shutdown(
10111011pub fn renameat(
10121012 self: *IoUring,
10131013 user_data: u64,
1014 old_dir_fd: os.fd_t,
1014 old_dir_fd: posix.fd_t,
10151015 old_path: [*:0]const u8,
1016 new_dir_fd: os.fd_t,
1016 new_dir_fd: posix.fd_t,
10171017 new_path: [*:0]const u8,
10181018 flags: u32,
10191019) !*linux.io_uring_sqe {
......@@ -1028,7 +1028,7 @@ pub fn renameat(
10281028pub fn unlinkat(
10291029 self: *IoUring,
10301030 user_data: u64,
1031 dir_fd: os.fd_t,
1031 dir_fd: posix.fd_t,
10321032 path: [*:0]const u8,
10331033 flags: u32,
10341034) !*linux.io_uring_sqe {
......@@ -1043,9 +1043,9 @@ pub fn unlinkat(
10431043pub fn mkdirat(
10441044 self: *IoUring,
10451045 user_data: u64,
1046 dir_fd: os.fd_t,
1046 dir_fd: posix.fd_t,
10471047 path: [*:0]const u8,
1048 mode: os.mode_t,
1048 mode: posix.mode_t,
10491049) !*linux.io_uring_sqe {
10501050 const sqe = try self.get_sqe();
10511051 sqe.prep_mkdirat(dir_fd, path, mode);
......@@ -1059,7 +1059,7 @@ pub fn symlinkat(
10591059 self: *IoUring,
10601060 user_data: u64,
10611061 target: [*:0]const u8,
1062 new_dir_fd: os.fd_t,
1062 new_dir_fd: posix.fd_t,
10631063 link_path: [*:0]const u8,
10641064) !*linux.io_uring_sqe {
10651065 const sqe = try self.get_sqe();
......@@ -1073,9 +1073,9 @@ pub fn symlinkat(
10731073pub fn linkat(
10741074 self: *IoUring,
10751075 user_data: u64,
1076 old_dir_fd: os.fd_t,
1076 old_dir_fd: posix.fd_t,
10771077 old_path: [*:0]const u8,
1078 new_dir_fd: os.fd_t,
1078 new_dir_fd: posix.fd_t,
10791079 new_path: [*:0]const u8,
10801080 flags: u32,
10811081) !*linux.io_uring_sqe {
......@@ -1147,7 +1147,7 @@ pub fn waitid(
11471147/// Registering file descriptors will wait for the ring to idle.
11481148/// Files are automatically unregistered by the kernel when the ring is torn down.
11491149/// An application need unregister only if it wants to register a new array of file descriptors.
1150pub fn register_files(self: *IoUring, fds: []const os.fd_t) !void {
1150pub fn register_files(self: *IoUring, fds: []const posix.fd_t) !void {
11511151 assert(self.fd >= 0);
11521152 const res = linux.io_uring_register(
11531153 self.fd,
......@@ -1166,7 +1166,7 @@ pub fn register_files(self: *IoUring, fds: []const os.fd_t) !void {
11661166/// * removing an existing entry (set the fd to -1)
11671167/// * replacing an existing entry with a new fd
11681168/// Adding new file descriptors must be done with `register_files`.
1169pub fn register_files_update(self: *IoUring, offset: u32, fds: []const os.fd_t) !void {
1169pub fn register_files_update(self: *IoUring, offset: u32, fds: []const posix.fd_t) !void {
11701170 assert(self.fd >= 0);
11711171
11721172 const FilesUpdate = extern struct {
......@@ -1192,7 +1192,7 @@ pub fn register_files_update(self: *IoUring, offset: u32, fds: []const os.fd_t)
11921192/// Registers the file descriptor for an eventfd that will be notified of completion events on
11931193/// an io_uring instance.
11941194/// Only a single a eventfd can be registered at any given point in time.
1195pub fn register_eventfd(self: *IoUring, fd: os.fd_t) !void {
1195pub fn register_eventfd(self: *IoUring, fd: posix.fd_t) !void {
11961196 assert(self.fd >= 0);
11971197 const res = linux.io_uring_register(
11981198 self.fd,
......@@ -1207,7 +1207,7 @@ pub fn register_eventfd(self: *IoUring, fd: os.fd_t) !void {
12071207/// an io_uring instance. Notifications are only posted for events that complete in an async manner.
12081208/// This means that events that complete inline while being submitted do not trigger a notification event.
12091209/// Only a single eventfd can be registered at any given point in time.
1210pub fn register_eventfd_async(self: *IoUring, fd: os.fd_t) !void {
1210pub fn register_eventfd_async(self: *IoUring, fd: posix.fd_t) !void {
12111211 assert(self.fd >= 0);
12121212 const res = linux.io_uring_register(
12131213 self.fd,
......@@ -1231,7 +1231,7 @@ pub fn unregister_eventfd(self: *IoUring) !void {
12311231}
12321232
12331233/// Registers an array of buffers for use with `read_fixed` and `write_fixed`.
1234pub fn register_buffers(self: *IoUring, buffers: []const os.iovec) !void {
1234pub fn register_buffers(self: *IoUring, buffers: []const posix.iovec) !void {
12351235 assert(self.fd >= 0);
12361236 const res = linux.io_uring_register(
12371237 self.fd,
......@@ -1246,15 +1246,15 @@ pub fn register_buffers(self: *IoUring, buffers: []const os.iovec) !void {
12461246pub fn unregister_buffers(self: *IoUring) !void {
12471247 assert(self.fd >= 0);
12481248 const res = linux.io_uring_register(self.fd, .UNREGISTER_BUFFERS, null, 0);
1249 switch (linux.getErrno(res)) {
1249 switch (linux.E.init(res)) {
12501250 .SUCCESS => {},
12511251 .NXIO => return error.BuffersNotRegistered,
1252 else => |errno| return os.unexpectedErrno(errno),
1252 else => |errno| return posix.unexpectedErrno(errno),
12531253 }
12541254}
12551255
12561256fn handle_registration_result(res: usize) !void {
1257 switch (linux.getErrno(res)) {
1257 switch (linux.E.init(res)) {
12581258 .SUCCESS => {},
12591259 // One or more fds in the array are invalid, or the kernel does not support sparse sets:
12601260 .BADF => return error.FileDescriptorInvalid,
......@@ -1271,7 +1271,7 @@ fn handle_registration_result(res: usize) !void {
12711271 .NOMEM => return error.SystemResources,
12721272 // Attempt to register files on a ring already registering files or being torn down:
12731273 .NXIO => return error.RingShuttingDownOrAlreadyRegisteringFiles,
1274 else => |errno| return os.unexpectedErrno(errno),
1274 else => |errno| return posix.unexpectedErrno(errno),
12751275 }
12761276}
12771277
......@@ -1279,10 +1279,10 @@ fn handle_registration_result(res: usize) !void {
12791279pub fn unregister_files(self: *IoUring) !void {
12801280 assert(self.fd >= 0);
12811281 const res = linux.io_uring_register(self.fd, .UNREGISTER_FILES, null, 0);
1282 switch (linux.getErrno(res)) {
1282 switch (linux.E.init(res)) {
12831283 .SUCCESS => {},
12841284 .NXIO => return error.FilesNotRegistered,
1285 else => |errno| return os.unexpectedErrno(errno),
1285 else => |errno| return posix.unexpectedErrno(errno),
12861286 }
12871287}
12881288
......@@ -1355,36 +1355,36 @@ pub const SubmissionQueue = struct {
13551355 sqe_head: u32 = 0,
13561356 sqe_tail: u32 = 0,
13571357
1358 pub fn init(fd: os.fd_t, p: linux.io_uring_params) !SubmissionQueue {
1358 pub fn init(fd: posix.fd_t, p: linux.io_uring_params) !SubmissionQueue {
13591359 assert(fd >= 0);
13601360 assert((p.features & linux.IORING_FEAT_SINGLE_MMAP) != 0);
13611361 const size = @max(
13621362 p.sq_off.array + p.sq_entries * @sizeOf(u32),
13631363 p.cq_off.cqes + p.cq_entries * @sizeOf(linux.io_uring_cqe),
13641364 );
1365 const mmap = try os.mmap(
1365 const mmap = try posix.mmap(
13661366 null,
13671367 size,
1368 os.PROT.READ | os.PROT.WRITE,
1368 posix.PROT.READ | posix.PROT.WRITE,
13691369 .{ .TYPE = .SHARED, .POPULATE = true },
13701370 fd,
13711371 linux.IORING_OFF_SQ_RING,
13721372 );
1373 errdefer os.munmap(mmap);
1373 errdefer posix.munmap(mmap);
13741374 assert(mmap.len == size);
13751375
13761376 // The motivation for the `sqes` and `array` indirection is to make it possible for the
13771377 // application to preallocate static linux.io_uring_sqe entries and then replay them when needed.
13781378 const size_sqes = p.sq_entries * @sizeOf(linux.io_uring_sqe);
1379 const mmap_sqes = try os.mmap(
1379 const mmap_sqes = try posix.mmap(
13801380 null,
13811381 size_sqes,
1382 os.PROT.READ | os.PROT.WRITE,
1382 posix.PROT.READ | posix.PROT.WRITE,
13831383 .{ .TYPE = .SHARED, .POPULATE = true },
13841384 fd,
13851385 linux.IORING_OFF_SQES,
13861386 );
1387 errdefer os.munmap(mmap_sqes);
1387 errdefer posix.munmap(mmap_sqes);
13881388 assert(mmap_sqes.len == size_sqes);
13891389
13901390 const array: [*]u32 = @ptrCast(@alignCast(&mmap[p.sq_off.array]));
......@@ -1406,8 +1406,8 @@ pub const SubmissionQueue = struct {
14061406 }
14071407
14081408 pub fn deinit(self: *SubmissionQueue) void {
1409 os.munmap(self.mmap_sqes);
1410 os.munmap(self.mmap);
1409 posix.munmap(self.mmap_sqes);
1410 posix.munmap(self.mmap);
14111411 }
14121412};
14131413
......@@ -1418,7 +1418,7 @@ pub const CompletionQueue = struct {
14181418 overflow: *u32,
14191419 cqes: []linux.io_uring_cqe,
14201420
1421 pub fn init(fd: os.fd_t, p: linux.io_uring_params, sq: SubmissionQueue) !CompletionQueue {
1421 pub fn init(fd: posix.fd_t, p: linux.io_uring_params, sq: SubmissionQueue) !CompletionQueue {
14221422 assert(fd >= 0);
14231423 assert((p.features & linux.IORING_FEAT_SINGLE_MMAP) != 0);
14241424 const mmap = sq.mmap;
......@@ -1506,7 +1506,7 @@ pub const BufferGroup = struct {
15061506 }
15071507
15081508 // Prepare recv operation which will select buffer from this group.
1509 pub fn recv(self: *BufferGroup, user_data: u64, fd: os.fd_t, flags: u32) !*linux.io_uring_sqe {
1509 pub fn recv(self: *BufferGroup, user_data: u64, fd: posix.fd_t, flags: u32) !*linux.io_uring_sqe {
15101510 var sqe = try self.ring.get_sqe();
15111511 sqe.prep_rw(.RECV, fd, 0, 0, 0);
15121512 sqe.rw_flags = flags;
......@@ -1517,7 +1517,7 @@ pub const BufferGroup = struct {
15171517 }
15181518
15191519 // Prepare multishot recv operation which will select buffer from this group.
1520 pub fn recv_multishot(self: *BufferGroup, user_data: u64, fd: os.fd_t, flags: u32) !*linux.io_uring_sqe {
1520 pub fn recv_multishot(self: *BufferGroup, user_data: u64, fd: posix.fd_t, flags: u32) !*linux.io_uring_sqe {
15211521 var sqe = try self.recv(user_data, fd, flags);
15221522 sqe.ioprio |= linux.IORING_RECV_MULTISHOT;
15231523 return sqe;
......@@ -1559,20 +1559,20 @@ pub const BufferGroup = struct {
15591559/// `fd` is IO_Uring.fd for which the provided buffer ring is being registered.
15601560/// `entries` is the number of entries requested in the buffer ring, must be power of 2.
15611561/// `group_id` is the chosen buffer group ID, unique in IO_Uring.
1562pub fn setup_buf_ring(fd: os.fd_t, entries: u16, group_id: u16) !*align(mem.page_size) linux.io_uring_buf_ring {
1562pub fn setup_buf_ring(fd: posix.fd_t, entries: u16, group_id: u16) !*align(mem.page_size) linux.io_uring_buf_ring {
15631563 if (entries == 0 or entries > 1 << 15) return error.EntriesNotInRange;
15641564 if (!std.math.isPowerOfTwo(entries)) return error.EntriesNotPowerOfTwo;
15651565
15661566 const mmap_size = entries * @sizeOf(linux.io_uring_buf);
1567 const mmap = try os.mmap(
1567 const mmap = try posix.mmap(
15681568 null,
15691569 mmap_size,
1570 os.PROT.READ | os.PROT.WRITE,
1570 posix.PROT.READ | posix.PROT.WRITE,
15711571 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
15721572 -1,
15731573 0,
15741574 );
1575 errdefer os.munmap(mmap);
1575 errdefer posix.munmap(mmap);
15761576 assert(mmap.len == mmap_size);
15771577
15781578 const br: *align(mem.page_size) linux.io_uring_buf_ring = @ptrCast(mmap.ptr);
......@@ -1580,7 +1580,7 @@ pub fn setup_buf_ring(fd: os.fd_t, entries: u16, group_id: u16) !*align(mem.page
15801580 return br;
15811581}
15821582
1583fn register_buf_ring(fd: os.fd_t, addr: u64, entries: u32, group_id: u16) !void {
1583fn register_buf_ring(fd: posix.fd_t, addr: u64, entries: u32, group_id: u16) !void {
15841584 var reg = mem.zeroInit(linux.io_uring_buf_reg, .{
15851585 .ring_addr = addr,
15861586 .ring_entries = entries,
......@@ -1595,7 +1595,7 @@ fn register_buf_ring(fd: os.fd_t, addr: u64, entries: u32, group_id: u16) !void
15951595 try handle_register_buf_ring_result(res);
15961596}
15971597
1598fn unregister_buf_ring(fd: os.fd_t, group_id: u16) !void {
1598fn unregister_buf_ring(fd: posix.fd_t, group_id: u16) !void {
15991599 var reg = mem.zeroInit(linux.io_uring_buf_reg, .{
16001600 .bgid = group_id,
16011601 });
......@@ -1609,20 +1609,20 @@ fn unregister_buf_ring(fd: os.fd_t, group_id: u16) !void {
16091609}
16101610
16111611fn handle_register_buf_ring_result(res: usize) !void {
1612 switch (linux.getErrno(res)) {
1612 switch (linux.E.init(res)) {
16131613 .SUCCESS => {},
16141614 .INVAL => return error.ArgumentsInvalid,
1615 else => |errno| return os.unexpectedErrno(errno),
1615 else => |errno| return posix.unexpectedErrno(errno),
16161616 }
16171617}
16181618
16191619// Unregisters a previously registered shared buffer ring, returned from io_uring_setup_buf_ring.
1620pub fn free_buf_ring(fd: os.fd_t, br: *align(mem.page_size) linux.io_uring_buf_ring, entries: u32, group_id: u16) void {
1620pub fn free_buf_ring(fd: posix.fd_t, br: *align(mem.page_size) linux.io_uring_buf_ring, entries: u32, group_id: u16) void {
16211621 unregister_buf_ring(fd, group_id) catch {};
16221622 var mmap: []align(mem.page_size) u8 = undefined;
16231623 mmap.ptr = @ptrCast(br);
16241624 mmap.len = entries * @sizeOf(linux.io_uring_buf);
1625 os.munmap(mmap);
1625 posix.munmap(mmap);
16261626}
16271627
16281628/// Initialises `br` so that it is ready to be used.
......@@ -1664,7 +1664,7 @@ pub fn buf_ring_advance(br: *linux.io_uring_buf_ring, count: u16) void {
16641664}
16651665
16661666test "structs/offsets/entries" {
1667 if (builtin.os.tag != .linux) return error.SkipZigTest;
1667 if (!is_linux) return error.SkipZigTest;
16681668
16691669 try testing.expectEqual(@as(usize, 120), @sizeOf(linux.io_uring_params));
16701670 try testing.expectEqual(@as(usize, 64), @sizeOf(linux.io_uring_sqe));
......@@ -1679,7 +1679,7 @@ test "structs/offsets/entries" {
16791679}
16801680
16811681test "nop" {
1682 if (builtin.os.tag != .linux) return error.SkipZigTest;
1682 if (!is_linux) return error.SkipZigTest;
16831683
16841684 var ring = IoUring.init(1, 0) catch |err| switch (err) {
16851685 error.SystemOutdated => return error.SkipZigTest,
......@@ -1688,7 +1688,7 @@ test "nop" {
16881688 };
16891689 defer {
16901690 ring.deinit();
1691 testing.expectEqual(@as(os.fd_t, -1), ring.fd) catch @panic("test failed");
1691 testing.expectEqual(@as(posix.fd_t, -1), ring.fd) catch @panic("test failed");
16921692 }
16931693
16941694 const sqe = try ring.nop(0xaaaaaaaa);
......@@ -1746,7 +1746,7 @@ test "nop" {
17461746}
17471747
17481748test "readv" {
1749 if (builtin.os.tag != .linux) return error.SkipZigTest;
1749 if (!is_linux) return error.SkipZigTest;
17501750
17511751 var ring = IoUring.init(1, 0) catch |err| switch (err) {
17521752 error.SystemOutdated => return error.SkipZigTest,
......@@ -1755,8 +1755,8 @@ test "readv" {
17551755 };
17561756 defer ring.deinit();
17571757
1758 const fd = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
1759 defer os.close(fd);
1758 const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
1759 defer posix.close(fd);
17601760
17611761 // Linux Kernel 5.4 supports IORING_REGISTER_FILES but not sparse fd sets (i.e. an fd of -1).
17621762 // Linux Kernel 5.5 adds support for sparse fd sets.
......@@ -1764,13 +1764,13 @@ test "readv" {
17641764 // https://github.com/torvalds/linux/blob/v5.4/fs/io_uring.c#L3119-L3124 vs
17651765 // https://github.com/torvalds/linux/blob/v5.8/fs/io_uring.c#L6687-L6691
17661766 // We therefore avoid stressing sparse fd sets here:
1767 var registered_fds = [_]os.fd_t{0} ** 1;
1767 var registered_fds = [_]posix.fd_t{0} ** 1;
17681768 const fd_index = 0;
17691769 registered_fds[fd_index] = fd;
17701770 try ring.register_files(registered_fds[0..]);
17711771
17721772 var buffer = [_]u8{42} ** 128;
1773 var iovecs = [_]os.iovec{os.iovec{ .iov_base = &buffer, .iov_len = buffer.len }};
1773 var iovecs = [_]posix.iovec{posix.iovec{ .iov_base = &buffer, .iov_len = buffer.len }};
17741774 const sqe = try ring.read(0xcccccccc, fd_index, .{ .iovecs = iovecs[0..] }, 0);
17751775 try testing.expectEqual(linux.IORING_OP.READV, sqe.opcode);
17761776 sqe.flags |= linux.IOSQE_FIXED_FILE;
......@@ -1788,7 +1788,7 @@ test "readv" {
17881788}
17891789
17901790test "writev/fsync/readv" {
1791 if (builtin.os.tag != .linux) return error.SkipZigTest;
1791 if (!is_linux) return error.SkipZigTest;
17921792
17931793 var ring = IoUring.init(4, 0) catch |err| switch (err) {
17941794 error.SystemOutdated => return error.SkipZigTest,
......@@ -1806,12 +1806,12 @@ test "writev/fsync/readv" {
18061806 const fd = file.handle;
18071807
18081808 const buffer_write = [_]u8{42} ** 128;
1809 const iovecs_write = [_]os.iovec_const{
1810 os.iovec_const{ .iov_base = &buffer_write, .iov_len = buffer_write.len },
1809 const iovecs_write = [_]posix.iovec_const{
1810 posix.iovec_const{ .iov_base = &buffer_write, .iov_len = buffer_write.len },
18111811 };
18121812 var buffer_read = [_]u8{0} ** 128;
1813 var iovecs_read = [_]os.iovec{
1814 os.iovec{ .iov_base = &buffer_read, .iov_len = buffer_read.len },
1813 var iovecs_read = [_]posix.iovec{
1814 posix.iovec{ .iov_base = &buffer_read, .iov_len = buffer_read.len },
18151815 };
18161816
18171817 const sqe_writev = try ring.writev(0xdddddddd, fd, iovecs_write[0..], 17);
......@@ -1858,7 +1858,7 @@ test "writev/fsync/readv" {
18581858}
18591859
18601860test "write/read" {
1861 if (builtin.os.tag != .linux) return error.SkipZigTest;
1861 if (!is_linux) return error.SkipZigTest;
18621862
18631863 var ring = IoUring.init(2, 0) catch |err| switch (err) {
18641864 error.SystemOutdated => return error.SkipZigTest,
......@@ -1905,7 +1905,7 @@ test "write/read" {
19051905}
19061906
19071907test "splice/read" {
1908 if (builtin.os.tag != .linux) return error.SkipZigTest;
1908 if (!is_linux) return error.SkipZigTest;
19091909
19101910 var ring = IoUring.init(4, 0) catch |err| switch (err) {
19111911 error.SystemOutdated => return error.SkipZigTest,
......@@ -1929,7 +1929,7 @@ test "splice/read" {
19291929 var buffer_read = [_]u8{98} ** 20;
19301930 _ = try file_src.write(&buffer_write);
19311931
1932 const fds = try os.pipe();
1932 const fds = try posix.pipe();
19331933 const pipe_offset: u64 = std.math.maxInt(u64);
19341934
19351935 const sqe_splice_to_pipe = try ring.splice(0x11111111, fd_src, 0, fds[1], pipe_offset, buffer_write.len);
......@@ -1976,7 +1976,7 @@ test "splice/read" {
19761976}
19771977
19781978test "write_fixed/read_fixed" {
1979 if (builtin.os.tag != .linux) return error.SkipZigTest;
1979 if (!is_linux) return error.SkipZigTest;
19801980
19811981 var ring = IoUring.init(2, 0) catch |err| switch (err) {
19821982 error.SystemOutdated => return error.SkipZigTest,
......@@ -1998,7 +1998,7 @@ test "write_fixed/read_fixed" {
19981998 @memset(&raw_buffers[0], 'z');
19991999 raw_buffers[0][0.."foobar".len].* = "foobar".*;
20002000
2001 var buffers = [2]os.iovec{
2001 var buffers = [2]posix.iovec{
20022002 .{ .iov_base = &raw_buffers[0], .iov_len = raw_buffers[0].len },
20032003 .{ .iov_base = &raw_buffers[1], .iov_len = raw_buffers[1].len },
20042004 };
......@@ -2041,7 +2041,7 @@ test "write_fixed/read_fixed" {
20412041}
20422042
20432043test "openat" {
2044 if (builtin.os.tag != .linux) return error.SkipZigTest;
2044 if (!is_linux) return error.SkipZigTest;
20452045
20462046 var ring = IoUring.init(1, 0) catch |err| switch (err) {
20472047 error.SystemOutdated => return error.SkipZigTest,
......@@ -2063,7 +2063,7 @@ test "openat" {
20632063 } else @intFromPtr(path);
20642064
20652065 const flags: linux.O = .{ .CLOEXEC = true, .ACCMODE = .RDWR, .CREAT = true };
2066 const mode: os.mode_t = 0o666;
2066 const mode: posix.mode_t = 0o666;
20672067 const sqe_openat = try ring.openat(0x33333333, tmp.dir.fd, path, flags, mode);
20682068 try testing.expectEqual(linux.io_uring_sqe{
20692069 .opcode = .OPENAT,
......@@ -2091,11 +2091,11 @@ test "openat" {
20912091 try testing.expect(cqe_openat.res > 0);
20922092 try testing.expectEqual(@as(u32, 0), cqe_openat.flags);
20932093
2094 os.close(cqe_openat.res);
2094 posix.close(cqe_openat.res);
20952095}
20962096
20972097test "close" {
2098 if (builtin.os.tag != .linux) return error.SkipZigTest;
2098 if (!is_linux) return error.SkipZigTest;
20992099
21002100 var ring = IoUring.init(1, 0) catch |err| switch (err) {
21012101 error.SystemOutdated => return error.SkipZigTest,
......@@ -2126,7 +2126,7 @@ test "close" {
21262126}
21272127
21282128test "accept/connect/send/recv" {
2129 if (builtin.os.tag != .linux) return error.SkipZigTest;
2129 if (!is_linux) return error.SkipZigTest;
21302130
21312131 var ring = IoUring.init(16, 0) catch |err| switch (err) {
21322132 error.SystemOutdated => return error.SkipZigTest,
......@@ -2167,7 +2167,7 @@ test "accept/connect/send/recv" {
21672167}
21682168
21692169test "sendmsg/recvmsg" {
2170 if (builtin.os.tag != .linux) return error.SkipZigTest;
2170 if (!is_linux) return error.SkipZigTest;
21712171
21722172 var ring = IoUring.init(2, 0) catch |err| switch (err) {
21732173 error.SystemOutdated => return error.SkipZigTest,
......@@ -2178,24 +2178,24 @@ test "sendmsg/recvmsg" {
21782178
21792179 var address_server = try net.Address.parseIp4("127.0.0.1", 0);
21802180
2181 const server = try os.socket(address_server.any.family, os.SOCK.DGRAM, 0);
2182 defer os.close(server);
2183 try os.setsockopt(server, os.SOL.SOCKET, os.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1)));
2184 try os.setsockopt(server, os.SOL.SOCKET, os.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
2185 try os.bind(server, &address_server.any, address_server.getOsSockLen());
2181 const server = try posix.socket(address_server.any.family, posix.SOCK.DGRAM, 0);
2182 defer posix.close(server);
2183 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1)));
2184 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
2185 try posix.bind(server, &address_server.any, address_server.getOsSockLen());
21862186
21872187 // set address_server to the OS-chosen IP/port.
2188 var slen: os.socklen_t = address_server.getOsSockLen();
2189 try os.getsockname(server, &address_server.any, &slen);
2188 var slen: posix.socklen_t = address_server.getOsSockLen();
2189 try posix.getsockname(server, &address_server.any, &slen);
21902190
2191 const client = try os.socket(address_server.any.family, os.SOCK.DGRAM, 0);
2192 defer os.close(client);
2191 const client = try posix.socket(address_server.any.family, posix.SOCK.DGRAM, 0);
2192 defer posix.close(client);
21932193
21942194 const buffer_send = [_]u8{42} ** 128;
2195 const iovecs_send = [_]os.iovec_const{
2196 os.iovec_const{ .iov_base = &buffer_send, .iov_len = buffer_send.len },
2195 const iovecs_send = [_]posix.iovec_const{
2196 posix.iovec_const{ .iov_base = &buffer_send, .iov_len = buffer_send.len },
21972197 };
2198 const msg_send = os.msghdr_const{
2198 const msg_send: posix.msghdr_const = .{
21992199 .name = &address_server.any,
22002200 .namelen = address_server.getOsSockLen(),
22012201 .iov = &iovecs_send,
......@@ -2210,12 +2210,12 @@ test "sendmsg/recvmsg" {
22102210 try testing.expectEqual(client, sqe_sendmsg.fd);
22112211
22122212 var buffer_recv = [_]u8{0} ** 128;
2213 var iovecs_recv = [_]os.iovec{
2214 os.iovec{ .iov_base = &buffer_recv, .iov_len = buffer_recv.len },
2213 var iovecs_recv = [_]posix.iovec{
2214 posix.iovec{ .iov_base = &buffer_recv, .iov_len = buffer_recv.len },
22152215 };
22162216 const addr = [_]u8{0} ** 4;
22172217 var address_recv = net.Address.initIp4(addr, 0);
2218 var msg_recv: os.msghdr = os.msghdr{
2218 var msg_recv: posix.msghdr = .{
22192219 .name = &address_recv.any,
22202220 .namelen = address_recv.getOsSockLen(),
22212221 .iov = &iovecs_recv,
......@@ -2254,7 +2254,7 @@ test "sendmsg/recvmsg" {
22542254}
22552255
22562256test "timeout (after a relative time)" {
2257 if (builtin.os.tag != .linux) return error.SkipZigTest;
2257 if (!is_linux) return error.SkipZigTest;
22582258
22592259 var ring = IoUring.init(1, 0) catch |err| switch (err) {
22602260 error.SystemOutdated => return error.SkipZigTest,
......@@ -2265,7 +2265,7 @@ test "timeout (after a relative time)" {
22652265
22662266 const ms = 10;
22672267 const margin = 5;
2268 const ts = os.linux.kernel_timespec{ .tv_sec = 0, .tv_nsec = ms * 1000000 };
2268 const ts: linux.kernel_timespec = .{ .tv_sec = 0, .tv_nsec = ms * 1000000 };
22692269
22702270 const started = std.time.milliTimestamp();
22712271 const sqe = try ring.timeout(0x55555555, &ts, 0, 0);
......@@ -2285,7 +2285,7 @@ test "timeout (after a relative time)" {
22852285}
22862286
22872287test "timeout (after a number of completions)" {
2288 if (builtin.os.tag != .linux) return error.SkipZigTest;
2288 if (!is_linux) return error.SkipZigTest;
22892289
22902290 var ring = IoUring.init(2, 0) catch |err| switch (err) {
22912291 error.SystemOutdated => return error.SkipZigTest,
......@@ -2294,7 +2294,7 @@ test "timeout (after a number of completions)" {
22942294 };
22952295 defer ring.deinit();
22962296
2297 const ts = os.linux.kernel_timespec{ .tv_sec = 3, .tv_nsec = 0 };
2297 const ts: linux.kernel_timespec = .{ .tv_sec = 3, .tv_nsec = 0 };
22982298 const count_completions: u64 = 1;
22992299 const sqe_timeout = try ring.timeout(0x66666666, &ts, count_completions, 0);
23002300 try testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe_timeout.opcode);
......@@ -2318,7 +2318,7 @@ test "timeout (after a number of completions)" {
23182318}
23192319
23202320test "timeout_remove" {
2321 if (builtin.os.tag != .linux) return error.SkipZigTest;
2321 if (!is_linux) return error.SkipZigTest;
23222322
23232323 var ring = IoUring.init(2, 0) catch |err| switch (err) {
23242324 error.SystemOutdated => return error.SkipZigTest,
......@@ -2327,7 +2327,7 @@ test "timeout_remove" {
23272327 };
23282328 defer ring.deinit();
23292329
2330 const ts = os.linux.kernel_timespec{ .tv_sec = 3, .tv_nsec = 0 };
2330 const ts: linux.kernel_timespec = .{ .tv_sec = 3, .tv_nsec = 0 };
23312331 const sqe_timeout = try ring.timeout(0x88888888, &ts, 0, 0);
23322332 try testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe_timeout.opcode);
23332333 try testing.expectEqual(@as(u64, 0x88888888), sqe_timeout.user_data);
......@@ -2343,7 +2343,7 @@ test "timeout_remove" {
23432343 // * kernel 5.10 gives user data 0x88888888 first, 0x99999999 second
23442344 // * kernel 5.18 gives user data 0x99999999 first, 0x88888888 second
23452345
2346 var cqes: [2]os.linux.io_uring_cqe = undefined;
2346 var cqes: [2]linux.io_uring_cqe = undefined;
23472347 cqes[0] = try ring.copy_cqe();
23482348 cqes[1] = try ring.copy_cqe();
23492349
......@@ -2378,7 +2378,7 @@ test "timeout_remove" {
23782378}
23792379
23802380test "accept/connect/recv/link_timeout" {
2381 if (builtin.os.tag != .linux) return error.SkipZigTest;
2381 if (!is_linux) return error.SkipZigTest;
23822382
23832383 var ring = IoUring.init(16, 0) catch |err| switch (err) {
23842384 error.SystemOutdated => return error.SkipZigTest,
......@@ -2395,7 +2395,7 @@ test "accept/connect/recv/link_timeout" {
23952395 const sqe_recv = try ring.recv(0xffffffff, socket_test_harness.server, .{ .buffer = buffer_recv[0..] }, 0);
23962396 sqe_recv.flags |= linux.IOSQE_IO_LINK;
23972397
2398 const ts = os.linux.kernel_timespec{ .tv_sec = 0, .tv_nsec = 1000000 };
2398 const ts = linux.kernel_timespec{ .tv_sec = 0, .tv_nsec = 1000000 };
23992399 _ = try ring.link_timeout(0x22222222, &ts, 0);
24002400
24012401 const nr_wait = try ring.submit();
......@@ -2427,7 +2427,7 @@ test "accept/connect/recv/link_timeout" {
24272427}
24282428
24292429test "fallocate" {
2430 if (builtin.os.tag != .linux) return error.SkipZigTest;
2430 if (!is_linux) return error.SkipZigTest;
24312431
24322432 var ring = IoUring.init(1, 0) catch |err| switch (err) {
24332433 error.SystemOutdated => return error.SkipZigTest,
......@@ -2473,7 +2473,7 @@ test "fallocate" {
24732473}
24742474
24752475test "statx" {
2476 if (builtin.os.tag != .linux) return error.SkipZigTest;
2476 if (!is_linux) return error.SkipZigTest;
24772477
24782478 var ring = IoUring.init(1, 0) catch |err| switch (err) {
24792479 error.SystemOutdated => return error.SkipZigTest,
......@@ -2525,12 +2525,12 @@ test "statx" {
25252525 .flags = 0,
25262526 }, cqe);
25272527
2528 try testing.expect(buf.mask & os.linux.STATX_SIZE == os.linux.STATX_SIZE);
2528 try testing.expect(buf.mask & linux.STATX_SIZE == linux.STATX_SIZE);
25292529 try testing.expectEqual(@as(u64, 6), buf.size);
25302530}
25312531
25322532test "accept/connect/recv/cancel" {
2533 if (builtin.os.tag != .linux) return error.SkipZigTest;
2533 if (!is_linux) return error.SkipZigTest;
25342534
25352535 var ring = IoUring.init(16, 0) catch |err| switch (err) {
25362536 error.SystemOutdated => return error.SkipZigTest,
......@@ -2580,7 +2580,7 @@ test "accept/connect/recv/cancel" {
25802580}
25812581
25822582test "register_files_update" {
2583 if (builtin.os.tag != .linux) return error.SkipZigTest;
2583 if (!is_linux) return error.SkipZigTest;
25842584
25852585 var ring = IoUring.init(1, 0) catch |err| switch (err) {
25862586 error.SystemOutdated => return error.SkipZigTest,
......@@ -2589,10 +2589,10 @@ test "register_files_update" {
25892589 };
25902590 defer ring.deinit();
25912591
2592 const fd = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
2593 defer os.close(fd);
2592 const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
2593 defer posix.close(fd);
25942594
2595 var registered_fds = [_]os.fd_t{0} ** 2;
2595 var registered_fds = [_]posix.fd_t{0} ** 2;
25962596 const fd_index = 0;
25972597 const fd_index2 = 1;
25982598 registered_fds[fd_index] = fd;
......@@ -2607,8 +2607,8 @@ test "register_files_update" {
26072607 // Test IORING_REGISTER_FILES_UPDATE
26082608 // Only available since Linux 5.5
26092609
2610 const fd2 = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
2611 defer os.close(fd2);
2610 const fd2 = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
2611 defer posix.close(fd2);
26122612
26132613 registered_fds[fd_index] = fd2;
26142614 registered_fds[fd_index2] = -1;
......@@ -2660,14 +2660,14 @@ test "register_files_update" {
26602660
26612661 try testing.expectEqual(@as(u32, 1), try ring.submit());
26622662 const cqe = try ring.copy_cqe();
2663 try testing.expectEqual(os.linux.E.BADF, cqe.err());
2663 try testing.expectEqual(linux.E.BADF, cqe.err());
26642664 }
26652665
26662666 try ring.unregister_files();
26672667}
26682668
26692669test "shutdown" {
2670 if (builtin.os.tag != .linux) return error.SkipZigTest;
2670 if (!is_linux) return error.SkipZigTest;
26712671
26722672 var ring = IoUring.init(16, 0) catch |err| switch (err) {
26732673 error.SystemOutdated => return error.SkipZigTest,
......@@ -2680,17 +2680,17 @@ test "shutdown" {
26802680
26812681 // Socket bound, expect shutdown to work
26822682 {
2683 const server = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);
2684 defer os.close(server);
2685 try os.setsockopt(server, os.SOL.SOCKET, os.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
2686 try os.bind(server, &address.any, address.getOsSockLen());
2687 try os.listen(server, 1);
2683 const server = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
2684 defer posix.close(server);
2685 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
2686 try posix.bind(server, &address.any, address.getOsSockLen());
2687 try posix.listen(server, 1);
26882688
26892689 // set address to the OS-chosen IP/port.
2690 var slen: os.socklen_t = address.getOsSockLen();
2691 try os.getsockname(server, &address.any, &slen);
2690 var slen: posix.socklen_t = address.getOsSockLen();
2691 try posix.getsockname(server, &address.any, &slen);
26922692
2693 const shutdown_sqe = try ring.shutdown(0x445445445, server, os.linux.SHUT.RD);
2693 const shutdown_sqe = try ring.shutdown(0x445445445, server, linux.SHUT.RD);
26942694 try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode);
26952695 try testing.expectEqual(@as(i32, server), shutdown_sqe.fd);
26962696
......@@ -2713,10 +2713,10 @@ test "shutdown" {
27132713
27142714 // Socket not bound, expect to fail with ENOTCONN
27152715 {
2716 const server = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);
2717 defer os.close(server);
2716 const server = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
2717 defer posix.close(server);
27182718
2719 const shutdown_sqe = ring.shutdown(0x445445445, server, os.linux.SHUT.RD) catch |err| switch (err) {
2719 const shutdown_sqe = ring.shutdown(0x445445445, server, linux.SHUT.RD) catch |err| switch (err) {
27202720 else => |errno| std.debug.panic("unhandled errno: {}", .{errno}),
27212721 };
27222722 try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode);
......@@ -2726,12 +2726,12 @@ test "shutdown" {
27262726
27272727 const cqe = try ring.copy_cqe();
27282728 try testing.expectEqual(@as(u64, 0x445445445), cqe.user_data);
2729 try testing.expectEqual(os.linux.E.NOTCONN, cqe.err());
2729 try testing.expectEqual(linux.E.NOTCONN, cqe.err());
27302730 }
27312731}
27322732
27332733test "renameat" {
2734 if (builtin.os.tag != .linux) return error.SkipZigTest;
2734 if (!is_linux) return error.SkipZigTest;
27352735
27362736 var ring = IoUring.init(1, 0) catch |err| switch (err) {
27372737 error.SystemOutdated => return error.SkipZigTest,
......@@ -2800,7 +2800,7 @@ test "renameat" {
28002800}
28012801
28022802test "unlinkat" {
2803 if (builtin.os.tag != .linux) return error.SkipZigTest;
2803 if (!is_linux) return error.SkipZigTest;
28042804
28052805 var ring = IoUring.init(1, 0) catch |err| switch (err) {
28062806 error.SystemOutdated => return error.SkipZigTest,
......@@ -2852,7 +2852,7 @@ test "unlinkat" {
28522852}
28532853
28542854test "mkdirat" {
2855 if (builtin.os.tag != .linux) return error.SkipZigTest;
2855 if (!is_linux) return error.SkipZigTest;
28562856
28572857 var ring = IoUring.init(1, 0) catch |err| switch (err) {
28582858 error.SystemOutdated => return error.SkipZigTest,
......@@ -2896,7 +2896,7 @@ test "mkdirat" {
28962896}
28972897
28982898test "symlinkat" {
2899 if (builtin.os.tag != .linux) return error.SkipZigTest;
2899 if (!is_linux) return error.SkipZigTest;
29002900
29012901 var ring = IoUring.init(1, 0) catch |err| switch (err) {
29022902 error.SystemOutdated => return error.SkipZigTest,
......@@ -2944,7 +2944,7 @@ test "symlinkat" {
29442944}
29452945
29462946test "linkat" {
2947 if (builtin.os.tag != .linux) return error.SkipZigTest;
2947 if (!is_linux) return error.SkipZigTest;
29482948
29492949 var ring = IoUring.init(1, 0) catch |err| switch (err) {
29502950 error.SystemOutdated => return error.SkipZigTest,
......@@ -3003,7 +3003,7 @@ test "linkat" {
30033003}
30043004
30053005test "provide_buffers: read" {
3006 if (builtin.os.tag != .linux) return error.SkipZigTest;
3006 if (!is_linux) return error.SkipZigTest;
30073007
30083008 var ring = IoUring.init(1, 0) catch |err| switch (err) {
30093009 error.SystemOutdated => return error.SkipZigTest,
......@@ -3012,8 +3012,8 @@ test "provide_buffers: read" {
30123012 };
30133013 defer ring.deinit();
30143014
3015 const fd = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
3016 defer os.close(fd);
3015 const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
3016 defer posix.close(fd);
30173017
30183018 const group_id = 1337;
30193019 const buffer_id = 0;
......@@ -3135,7 +3135,7 @@ test "provide_buffers: read" {
31353135}
31363136
31373137test "remove_buffers" {
3138 if (builtin.os.tag != .linux) return error.SkipZigTest;
3138 if (!is_linux) return error.SkipZigTest;
31393139
31403140 var ring = IoUring.init(1, 0) catch |err| switch (err) {
31413141 error.SystemOutdated => return error.SkipZigTest,
......@@ -3144,8 +3144,8 @@ test "remove_buffers" {
31443144 };
31453145 defer ring.deinit();
31463146
3147 const fd = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
3148 defer os.close(fd);
3147 const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
3148 defer posix.close(fd);
31493149
31503150 const group_id = 1337;
31513151 const buffer_id = 0;
......@@ -3224,7 +3224,7 @@ test "remove_buffers" {
32243224}
32253225
32263226test "provide_buffers: accept/connect/send/recv" {
3227 if (builtin.os.tag != .linux) return error.SkipZigTest;
3227 if (!is_linux) return error.SkipZigTest;
32283228
32293229 var ring = IoUring.init(16, 0) catch |err| switch (err) {
32303230 error.SystemOutdated => return error.SkipZigTest,
......@@ -3391,9 +3391,9 @@ test "provide_buffers: accept/connect/send/recv" {
33913391
33923392/// Used for testing server/client interactions.
33933393const SocketTestHarness = struct {
3394 listener: os.socket_t,
3395 server: os.socket_t,
3396 client: os.socket_t,
3394 listener: posix.socket_t,
3395 server: posix.socket_t,
3396 client: posix.socket_t,
33973397
33983398 fn close(self: SocketTestHarness) void {
33993399 posix.close(self.client);
......@@ -3408,12 +3408,12 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
34083408 errdefer posix.close(listener_socket);
34093409
34103410 // Submit 1 accept
3411 var accept_addr: os.sockaddr = undefined;
3412 var accept_addr_len: os.socklen_t = @sizeOf(@TypeOf(accept_addr));
3411 var accept_addr: posix.sockaddr = undefined;
3412 var accept_addr_len: posix.socklen_t = @sizeOf(@TypeOf(accept_addr));
34133413 _ = try ring.accept(0xaaaaaaaa, listener_socket, &accept_addr, &accept_addr_len, 0);
34143414
34153415 // Create a TCP client socket
3416 const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);
3416 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
34173417 errdefer posix.close(client);
34183418 _ = try ring.connect(0xcccccccc, client, &address.any, address.getOsSockLen());
34193419
......@@ -3451,24 +3451,24 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
34513451 };
34523452}
34533453
3454fn createListenerSocket(address: *net.Address) !os.socket_t {
3454fn createListenerSocket(address: *net.Address) !posix.socket_t {
34553455 const kernel_backlog = 1;
3456 const listener_socket = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);
3456 const listener_socket = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
34573457 errdefer posix.close(listener_socket);
34583458
3459 try os.setsockopt(listener_socket, os.SOL.SOCKET, os.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
3460 try os.bind(listener_socket, &address.any, address.getOsSockLen());
3461 try os.listen(listener_socket, kernel_backlog);
3459 try posix.setsockopt(listener_socket, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
3460 try posix.bind(listener_socket, &address.any, address.getOsSockLen());
3461 try posix.listen(listener_socket, kernel_backlog);
34623462
34633463 // set address to the OS-chosen IP/port.
3464 var slen: os.socklen_t = address.getOsSockLen();
3465 try os.getsockname(listener_socket, &address.any, &slen);
3464 var slen: posix.socklen_t = address.getOsSockLen();
3465 try posix.getsockname(listener_socket, &address.any, &slen);
34663466
34673467 return listener_socket;
34683468}
34693469
34703470test "accept multishot" {
3471 if (builtin.os.tag != .linux) return error.SkipZigTest;
3471 if (!is_linux) return error.SkipZigTest;
34723472
34733473 var ring = IoUring.init(16, 0) catch |err| switch (err) {
34743474 error.SystemOutdated => return error.SkipZigTest,
......@@ -3482,8 +3482,8 @@ test "accept multishot" {
34823482 defer posix.close(listener_socket);
34833483
34843484 // submit multishot accept operation
3485 var addr: os.sockaddr = undefined;
3486 var addr_len: os.socklen_t = @sizeOf(@TypeOf(addr));
3485 var addr: posix.sockaddr = undefined;
3486 var addr_len: posix.socklen_t = @sizeOf(@TypeOf(addr));
34873487 const userdata: u64 = 0xaaaaaaaa;
34883488 _ = try ring.accept_multishot(userdata, listener_socket, &addr, &addr_len, 0);
34893489 try testing.expectEqual(@as(u32, 1), try ring.submit());
......@@ -3491,9 +3491,9 @@ test "accept multishot" {
34913491 var nr: usize = 4; // number of clients to connect
34923492 while (nr > 0) : (nr -= 1) {
34933493 // connect client
3494 const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);
3494 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
34953495 errdefer posix.close(client);
3496 try os.connect(client, &address.any, address.getOsSockLen());
3496 try posix.connect(client, &address.any, address.getOsSockLen());
34973497
34983498 // test accept completion
34993499 var cqe = try ring.copy_cqe();
......@@ -3571,7 +3571,7 @@ test "accept_direct" {
35713571 var address = try net.Address.parseIp4("127.0.0.1", 0);
35723572
35733573 // register direct file descriptors
3574 var registered_fds = [_]os.fd_t{-1} ** 2;
3574 var registered_fds = [_]posix.fd_t{-1} ** 2;
35753575 try ring.register_files(registered_fds[0..]);
35763576
35773577 const listener_socket = try createListenerSocket(&address);
......@@ -3591,19 +3591,19 @@ test "accept_direct" {
35913591 try testing.expectEqual(@as(u32, 1), try ring.submit());
35923592
35933593 // connect
3594 const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);
3595 try os.connect(client, &address.any, address.getOsSockLen());
3594 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3595 try posix.connect(client, &address.any, address.getOsSockLen());
35963596 defer posix.close(client);
35973597
35983598 // accept completion
35993599 const cqe_accept = try ring.copy_cqe();
3600 try testing.expectEqual(os.E.SUCCESS, cqe_accept.err());
3600 try testing.expectEqual(posix.E.SUCCESS, cqe_accept.err());
36013601 const fd_index = cqe_accept.res;
36023602 try testing.expect(fd_index < registered_fds.len);
36033603 try testing.expect(cqe_accept.user_data == accept_userdata);
36043604
36053605 // send data
3606 _ = try os.send(client, buffer_send, 0);
3606 _ = try posix.send(client, buffer_send, 0);
36073607
36083608 // Example of how to use registered fd:
36093609 // Submit receive to fixed file returned by accept (fd_index).
......@@ -3625,13 +3625,13 @@ test "accept_direct" {
36253625 _ = try ring.accept_direct(accept_userdata, listener_socket, null, null, 0);
36263626 try testing.expectEqual(@as(u32, 1), try ring.submit());
36273627 // connect
3628 const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);
3629 try os.connect(client, &address.any, address.getOsSockLen());
3628 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3629 try posix.connect(client, &address.any, address.getOsSockLen());
36303630 defer posix.close(client);
36313631 // completion with error
36323632 const cqe_accept = try ring.copy_cqe();
36333633 try testing.expect(cqe_accept.user_data == accept_userdata);
3634 try testing.expectEqual(os.E.NFILE, cqe_accept.err());
3634 try testing.expectEqual(posix.E.NFILE, cqe_accept.err());
36353635 }
36363636 // return file descriptors to kernel
36373637 try ring.register_files_update(0, registered_fds[0..]);
......@@ -3651,7 +3651,7 @@ test "accept_multishot_direct" {
36513651
36523652 var address = try net.Address.parseIp4("127.0.0.1", 0);
36533653
3654 var registered_fds = [_]os.fd_t{-1} ** 2;
3654 var registered_fds = [_]posix.fd_t{-1} ** 2;
36553655 try ring.register_files(registered_fds[0..]);
36563656
36573657 const listener_socket = try createListenerSocket(&address);
......@@ -3667,8 +3667,8 @@ test "accept_multishot_direct" {
36673667
36683668 for (registered_fds) |_| {
36693669 // connect
3670 const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);
3671 try os.connect(client, &address.any, address.getOsSockLen());
3670 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3671 try posix.connect(client, &address.any, address.getOsSockLen());
36723672 defer posix.close(client);
36733673
36743674 // accept completion
......@@ -3682,13 +3682,13 @@ test "accept_multishot_direct" {
36823682 // Multishot is terminated (more flag is not set).
36833683 {
36843684 // connect
3685 const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);
3686 try os.connect(client, &address.any, address.getOsSockLen());
3685 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3686 try posix.connect(client, &address.any, address.getOsSockLen());
36873687 defer posix.close(client);
36883688 // completion with error
36893689 const cqe_accept = try ring.copy_cqe();
36903690 try testing.expect(cqe_accept.user_data == accept_userdata);
3691 try testing.expectEqual(os.E.NFILE, cqe_accept.err());
3691 try testing.expectEqual(posix.E.NFILE, cqe_accept.err());
36923692 try testing.expect(cqe_accept.flags & linux.IORING_CQE_F_MORE == 0); // has more is not set
36933693 }
36943694 // return file descriptors to kernel
......@@ -3708,16 +3708,16 @@ test "socket" {
37083708 defer ring.deinit();
37093709
37103710 // prepare, submit socket operation
3711 _ = try ring.socket(0, linux.AF.INET, os.SOCK.STREAM, 0, 0);
3711 _ = try ring.socket(0, linux.AF.INET, posix.SOCK.STREAM, 0, 0);
37123712 try testing.expectEqual(@as(u32, 1), try ring.submit());
37133713
37143714 // test completion
37153715 var cqe = try ring.copy_cqe();
3716 try testing.expectEqual(os.E.SUCCESS, cqe.err());
3717 const fd: os.fd_t = @intCast(cqe.res);
3716 try testing.expectEqual(posix.E.SUCCESS, cqe.err());
3717 const fd: posix.fd_t = @intCast(cqe.res);
37183718 try testing.expect(fd > 2);
37193719
3720 os.close(fd);
3720 posix.close(fd);
37213721}
37223722
37233723test "socket_direct/socket_direct_alloc/close_direct" {
......@@ -3730,29 +3730,29 @@ test "socket_direct/socket_direct_alloc/close_direct" {
37303730 };
37313731 defer ring.deinit();
37323732
3733 var registered_fds = [_]os.fd_t{-1} ** 3;
3733 var registered_fds = [_]posix.fd_t{-1} ** 3;
37343734 try ring.register_files(registered_fds[0..]);
37353735
37363736 // create socket in registered file descriptor at index 0 (last param)
3737 _ = try ring.socket_direct(0, linux.AF.INET, os.SOCK.STREAM, 0, 0, 0);
3737 _ = try ring.socket_direct(0, linux.AF.INET, posix.SOCK.STREAM, 0, 0, 0);
37383738 try testing.expectEqual(@as(u32, 1), try ring.submit());
37393739 var cqe_socket = try ring.copy_cqe();
3740 try testing.expectEqual(os.E.SUCCESS, cqe_socket.err());
3740 try testing.expectEqual(posix.E.SUCCESS, cqe_socket.err());
37413741 try testing.expect(cqe_socket.res == 0);
37423742
37433743 // create socket in registered file descriptor at index 1 (last param)
3744 _ = try ring.socket_direct(0, linux.AF.INET, os.SOCK.STREAM, 0, 0, 1);
3744 _ = try ring.socket_direct(0, linux.AF.INET, posix.SOCK.STREAM, 0, 0, 1);
37453745 try testing.expectEqual(@as(u32, 1), try ring.submit());
37463746 cqe_socket = try ring.copy_cqe();
3747 try testing.expectEqual(os.E.SUCCESS, cqe_socket.err());
3747 try testing.expectEqual(posix.E.SUCCESS, cqe_socket.err());
37483748 try testing.expect(cqe_socket.res == 0); // res is 0 when index is specified
37493749
37503750 // create socket in kernel chosen file descriptor index (_alloc version)
37513751 // completion res has index from registered files
3752 _ = try ring.socket_direct_alloc(0, linux.AF.INET, os.SOCK.STREAM, 0, 0);
3752 _ = try ring.socket_direct_alloc(0, linux.AF.INET, posix.SOCK.STREAM, 0, 0);
37533753 try testing.expectEqual(@as(u32, 1), try ring.submit());
37543754 cqe_socket = try ring.copy_cqe();
3755 try testing.expectEqual(os.E.SUCCESS, cqe_socket.err());
3755 try testing.expectEqual(posix.E.SUCCESS, cqe_socket.err());
37563756 try testing.expect(cqe_socket.res == 2); // returns registered file index
37573757
37583758 // use sockets from registered_fds in connect operation
......@@ -3782,17 +3782,17 @@ test "socket_direct/socket_direct_alloc/close_direct" {
37823782 }
37833783 // test connect completion
37843784 try testing.expect(cqe_connect.user_data == connect_userdata);
3785 try testing.expectEqual(os.E.SUCCESS, cqe_connect.err());
3785 try testing.expectEqual(posix.E.SUCCESS, cqe_connect.err());
37863786 // test accept completion
37873787 try testing.expect(cqe_accept.user_data == accept_userdata);
3788 try testing.expectEqual(os.E.SUCCESS, cqe_accept.err());
3788 try testing.expectEqual(posix.E.SUCCESS, cqe_accept.err());
37893789
37903790 // submit and test close_direct
37913791 _ = try ring.close_direct(close_userdata, @intCast(fd_index));
37923792 try testing.expectEqual(@as(u32, 1), try ring.submit());
37933793 var cqe_close = try ring.copy_cqe();
37943794 try testing.expect(cqe_close.user_data == close_userdata);
3795 try testing.expectEqual(os.E.SUCCESS, cqe_close.err());
3795 try testing.expectEqual(posix.E.SUCCESS, cqe_close.err());
37963796 }
37973797
37983798 try ring.unregister_files();
......@@ -3808,35 +3808,35 @@ test "openat_direct/close_direct" {
38083808 };
38093809 defer ring.deinit();
38103810
3811 var registered_fds = [_]os.fd_t{-1} ** 3;
3811 var registered_fds = [_]posix.fd_t{-1} ** 3;
38123812 try ring.register_files(registered_fds[0..]);
38133813
38143814 var tmp = std.testing.tmpDir(.{});
38153815 defer tmp.cleanup();
38163816 const path = "test_io_uring_close_direct";
38173817 const flags: linux.O = .{ .ACCMODE = .RDWR, .CREAT = true };
3818 const mode: os.mode_t = 0o666;
3818 const mode: posix.mode_t = 0o666;
38193819 const user_data: u64 = 0;
38203820
38213821 // use registered file at index 0 (last param)
38223822 _ = try ring.openat_direct(user_data, tmp.dir.fd, path, flags, mode, 0);
38233823 try testing.expectEqual(@as(u32, 1), try ring.submit());
38243824 var cqe = try ring.copy_cqe();
3825 try testing.expectEqual(os.E.SUCCESS, cqe.err());
3825 try testing.expectEqual(posix.E.SUCCESS, cqe.err());
38263826 try testing.expect(cqe.res == 0);
38273827
38283828 // use registered file at index 1
38293829 _ = try ring.openat_direct(user_data, tmp.dir.fd, path, flags, mode, 1);
38303830 try testing.expectEqual(@as(u32, 1), try ring.submit());
38313831 cqe = try ring.copy_cqe();
3832 try testing.expectEqual(os.E.SUCCESS, cqe.err());
3832 try testing.expectEqual(posix.E.SUCCESS, cqe.err());
38333833 try testing.expect(cqe.res == 0); // res is 0 when we specify index
38343834
38353835 // let kernel choose registered file index
38363836 _ = try ring.openat_direct(user_data, tmp.dir.fd, path, flags, mode, linux.IORING_FILE_INDEX_ALLOC);
38373837 try testing.expectEqual(@as(u32, 1), try ring.submit());
38383838 cqe = try ring.copy_cqe();
3839 try testing.expectEqual(os.E.SUCCESS, cqe.err());
3839 try testing.expectEqual(posix.E.SUCCESS, cqe.err());
38403840 try testing.expect(cqe.res == 2); // chosen index is in res
38413841
38423842 // close all open file descriptors
......@@ -3844,7 +3844,7 @@ test "openat_direct/close_direct" {
38443844 _ = try ring.close_direct(user_data, @intCast(fd_index));
38453845 try testing.expectEqual(@as(u32, 1), try ring.submit());
38463846 var cqe_close = try ring.copy_cqe();
3847 try testing.expectEqual(os.E.SUCCESS, cqe_close.err());
3847 try testing.expectEqual(posix.E.SUCCESS, cqe_close.err());
38483848 }
38493849 try ring.unregister_files();
38503850}
......@@ -3859,13 +3859,13 @@ test "waitid" {
38593859 };
38603860 defer ring.deinit();
38613861
3862 const pid = try os.fork();
3862 const pid = try posix.fork();
38633863 if (pid == 0) {
3864 os.exit(7);
3864 posix.exit(7);
38653865 }
38663866
3867 var siginfo: os.siginfo_t = undefined;
3868 _ = try ring.waitid(0, .PID, pid, &siginfo, os.W.EXITED, 0);
3867 var siginfo: posix.siginfo_t = undefined;
3868 _ = try ring.waitid(0, .PID, pid, &siginfo, posix.W.EXITED, 0);
38693869
38703870 try testing.expectEqual(1, try ring.submit());
38713871
......@@ -3877,13 +3877,13 @@ test "waitid" {
38773877
38783878/// For use in tests. Returns SkipZigTest if kernel version is less than required.
38793879inline fn skipKernelLessThan(required: std.SemanticVersion) !void {
3880 if (builtin.os.tag != .linux) return error.SkipZigTest;
3880 if (!is_linux) return error.SkipZigTest;
38813881
38823882 var uts: linux.utsname = undefined;
38833883 const res = linux.uname(&uts);
3884 switch (linux.getErrno(res)) {
3884 switch (linux.E.init(res)) {
38853885 .SUCCESS => {},
3886 else => |errno| return os.unexpectedErrno(errno),
3886 else => |errno| return posix.unexpectedErrno(errno),
38873887 }
38883888
38893889 const release = mem.sliceTo(&uts.release, 0);
......@@ -3893,7 +3893,7 @@ inline fn skipKernelLessThan(required: std.SemanticVersion) !void {
38933893}
38943894
38953895test BufferGroup {
3896 if (builtin.os.tag != .linux) return error.SkipZigTest;
3896 if (!is_linux) return error.SkipZigTest;
38973897
38983898 // Init IoUring
38993899 var ring = IoUring.init(16, 0) catch |err| switch (err) {
......@@ -3948,7 +3948,7 @@ test BufferGroup {
39483948 const cqe = try ring.copy_cqe();
39493949 try testing.expectEqual(2, cqe.user_data); // matches submitted user_data
39503950 try testing.expect(cqe.res >= 0); // success
3951 try testing.expectEqual(os.E.SUCCESS, cqe.err());
3951 try testing.expectEqual(posix.E.SUCCESS, cqe.err());
39523952 try testing.expectEqual(data.len, @as(usize, @intCast(cqe.res))); // cqe.res holds received data len
39533953
39543954 // Read buffer_id and used buffer len from cqe
......@@ -3963,7 +3963,7 @@ test BufferGroup {
39633963}
39643964
39653965test "ring mapped buffers recv" {
3966 if (builtin.os.tag != .linux) return error.SkipZigTest;
3966 if (!is_linux) return error.SkipZigTest;
39673967
39683968 var ring = IoUring.init(16, 0) catch |err| switch (err) {
39693969 error.SystemOutdated => return error.SkipZigTest,
......@@ -4029,7 +4029,7 @@ test "ring mapped buffers recv" {
40294029 const cqe = try ring.copy_cqe();
40304030 try testing.expectEqual(user_data, cqe.user_data);
40314031 try testing.expect(cqe.res < 0); // fail
4032 try testing.expectEqual(os.E.NOBUFS, cqe.err());
4032 try testing.expectEqual(posix.E.NOBUFS, cqe.err());
40334033 try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == 0); // IORING_CQE_F_BUFFER flags is set on success only
40344034 try testing.expectError(error.NoBufferSelected, cqe.buffer_id());
40354035 }
......@@ -4049,7 +4049,7 @@ test "ring mapped buffers recv" {
40494049}
40504050
40514051test "ring mapped buffers multishot recv" {
4052 if (builtin.os.tag != .linux) return error.SkipZigTest;
4052 if (!is_linux) return error.SkipZigTest;
40534053
40544054 var ring = IoUring.init(16, 0) catch |err| switch (err) {
40554055 error.SystemOutdated => return error.SkipZigTest,
......@@ -4120,7 +4120,7 @@ test "ring mapped buffers multishot recv" {
41204120 const cqe = try ring.copy_cqe();
41214121 try testing.expectEqual(recv_user_data, cqe.user_data);
41224122 try testing.expect(cqe.res < 0); // fail
4123 try testing.expectEqual(os.E.NOBUFS, cqe.err());
4123 try testing.expectEqual(posix.E.NOBUFS, cqe.err());
41244124 try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == 0); // IORING_CQE_F_BUFFER flags is set on success only
41254125 // has more is not set
41264126 // indicates that multishot is finished
......@@ -4194,7 +4194,7 @@ test "ring mapped buffers multishot recv" {
41944194fn expect_buf_grp_recv(
41954195 ring: *IoUring,
41964196 buf_grp: *BufferGroup,
4197 fd: os.fd_t,
4197 fd: posix.fd_t,
41984198 user_data: u64,
41994199 expected: []const u8,
42004200) !u16 {
......@@ -4220,7 +4220,7 @@ fn expect_buf_grp_cqe(
42204220 try testing.expect(cqe.res >= 0); // success
42214221 try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == linux.IORING_CQE_F_BUFFER); // IORING_CQE_F_BUFFER flag is set
42224222 try testing.expectEqual(expected.len, @as(usize, @intCast(cqe.res)));
4223 try testing.expectEqual(os.E.SUCCESS, cqe.err());
4223 try testing.expectEqual(posix.E.SUCCESS, cqe.err());
42244224
42254225 // get buffer from pool
42264226 const buffer_id = try cqe.buffer_id();
lib/std/os/linux/arm-eabi.zig+2-2
......@@ -2,8 +2,8 @@ const std = @import("../../std.zig");
22const maxInt = std.math.maxInt;
33const linux = std.os.linux;
44const SYS = linux.SYS;
5const iovec = std.os.iovec;
6const iovec_const = std.os.iovec_const;
5const iovec = std.posix.iovec;
6const iovec_const = std.posix.iovec_const;
77const socklen_t = linux.socklen_t;
88const stack_t = linux.stack_t;
99const sigset_t = linux.sigset_t;
lib/std/os/linux/arm64.zig+2-2
......@@ -4,8 +4,8 @@ const linux = std.os.linux;
44const SYS = linux.SYS;
55const socklen_t = linux.socklen_t;
66const sockaddr = linux.sockaddr;
7const iovec = std.os.iovec;
8const iovec_const = std.os.iovec_const;
7const iovec = std.posix.iovec;
8const iovec_const = std.posix.iovec_const;
99const uid_t = linux.uid_t;
1010const gid_t = linux.gid_t;
1111const pid_t = linux.pid_t;
lib/std/os/linux/bpf.zig+1-2
......@@ -1,5 +1,5 @@
11const std = @import("../../std.zig");
2const errno = getErrno;
2const errno = linux.E.init;
33const unexpectedErrno = std.os.unexpectedErrno;
44const expectEqual = std.testing.expectEqual;
55const expectError = std.testing.expectError;
......@@ -8,7 +8,6 @@ const expect = std.testing.expect;
88const linux = std.os.linux;
99const fd_t = linux.fd_t;
1010const pid_t = linux.pid_t;
11const getErrno = linux.getErrno;
1211
1312pub const btf = @import("bpf/btf.zig");
1413pub const kern = @import("bpf/kern.zig");
lib/std/os/linux/errno/generic.zig deleted-460
......@@ -1,460 +0,0 @@
1pub const E = enum(u16) {
2 /// No error occurred.
3 /// Same code used for `NSROK`.
4 SUCCESS = 0,
5
6 /// Operation not permitted
7 PERM = 1,
8
9 /// No such file or directory
10 NOENT = 2,
11
12 /// No such process
13 SRCH = 3,
14
15 /// Interrupted system call
16 INTR = 4,
17
18 /// I/O error
19 IO = 5,
20
21 /// No such device or address
22 NXIO = 6,
23
24 /// Arg list too long
25 @"2BIG" = 7,
26
27 /// Exec format error
28 NOEXEC = 8,
29
30 /// Bad file number
31 BADF = 9,
32
33 /// No child processes
34 CHILD = 10,
35
36 /// Try again
37 /// Also means: WOULDBLOCK: operation would block
38 AGAIN = 11,
39
40 /// Out of memory
41 NOMEM = 12,
42
43 /// Permission denied
44 ACCES = 13,
45
46 /// Bad address
47 FAULT = 14,
48
49 /// Block device required
50 NOTBLK = 15,
51
52 /// Device or resource busy
53 BUSY = 16,
54
55 /// File exists
56 EXIST = 17,
57
58 /// Cross-device link
59 XDEV = 18,
60
61 /// No such device
62 NODEV = 19,
63
64 /// Not a directory
65 NOTDIR = 20,
66
67 /// Is a directory
68 ISDIR = 21,
69
70 /// Invalid argument
71 INVAL = 22,
72
73 /// File table overflow
74 NFILE = 23,
75
76 /// Too many open files
77 MFILE = 24,
78
79 /// Not a typewriter
80 NOTTY = 25,
81
82 /// Text file busy
83 TXTBSY = 26,
84
85 /// File too large
86 FBIG = 27,
87
88 /// No space left on device
89 NOSPC = 28,
90
91 /// Illegal seek
92 SPIPE = 29,
93
94 /// Read-only file system
95 ROFS = 30,
96
97 /// Too many links
98 MLINK = 31,
99
100 /// Broken pipe
101 PIPE = 32,
102
103 /// Math argument out of domain of func
104 DOM = 33,
105
106 /// Math result not representable
107 RANGE = 34,
108
109 /// Resource deadlock would occur
110 DEADLK = 35,
111
112 /// File name too long
113 NAMETOOLONG = 36,
114
115 /// No record locks available
116 NOLCK = 37,
117
118 /// Function not implemented
119 NOSYS = 38,
120
121 /// Directory not empty
122 NOTEMPTY = 39,
123
124 /// Too many symbolic links encountered
125 LOOP = 40,
126
127 /// No message of desired type
128 NOMSG = 42,
129
130 /// Identifier removed
131 IDRM = 43,
132
133 /// Channel number out of range
134 CHRNG = 44,
135
136 /// Level 2 not synchronized
137 L2NSYNC = 45,
138
139 /// Level 3 halted
140 L3HLT = 46,
141
142 /// Level 3 reset
143 L3RST = 47,
144
145 /// Link number out of range
146 LNRNG = 48,
147
148 /// Protocol driver not attached
149 UNATCH = 49,
150
151 /// No CSI structure available
152 NOCSI = 50,
153
154 /// Level 2 halted
155 L2HLT = 51,
156
157 /// Invalid exchange
158 BADE = 52,
159
160 /// Invalid request descriptor
161 BADR = 53,
162
163 /// Exchange full
164 XFULL = 54,
165
166 /// No anode
167 NOANO = 55,
168
169 /// Invalid request code
170 BADRQC = 56,
171
172 /// Invalid slot
173 BADSLT = 57,
174
175 /// Bad font file format
176 BFONT = 59,
177
178 /// Device not a stream
179 NOSTR = 60,
180
181 /// No data available
182 NODATA = 61,
183
184 /// Timer expired
185 TIME = 62,
186
187 /// Out of streams resources
188 NOSR = 63,
189
190 /// Machine is not on the network
191 NONET = 64,
192
193 /// Package not installed
194 NOPKG = 65,
195
196 /// Object is remote
197 REMOTE = 66,
198
199 /// Link has been severed
200 NOLINK = 67,
201
202 /// Advertise error
203 ADV = 68,
204
205 /// Srmount error
206 SRMNT = 69,
207
208 /// Communication error on send
209 COMM = 70,
210
211 /// Protocol error
212 PROTO = 71,
213
214 /// Multihop attempted
215 MULTIHOP = 72,
216
217 /// RFS specific error
218 DOTDOT = 73,
219
220 /// Not a data message
221 BADMSG = 74,
222
223 /// Value too large for defined data type
224 OVERFLOW = 75,
225
226 /// Name not unique on network
227 NOTUNIQ = 76,
228
229 /// File descriptor in bad state
230 BADFD = 77,
231
232 /// Remote address changed
233 REMCHG = 78,
234
235 /// Can not access a needed shared library
236 LIBACC = 79,
237
238 /// Accessing a corrupted shared library
239 LIBBAD = 80,
240
241 /// .lib section in a.out corrupted
242 LIBSCN = 81,
243
244 /// Attempting to link in too many shared libraries
245 LIBMAX = 82,
246
247 /// Cannot exec a shared library directly
248 LIBEXEC = 83,
249
250 /// Illegal byte sequence
251 ILSEQ = 84,
252
253 /// Interrupted system call should be restarted
254 RESTART = 85,
255
256 /// Streams pipe error
257 STRPIPE = 86,
258
259 /// Too many users
260 USERS = 87,
261
262 /// Socket operation on non-socket
263 NOTSOCK = 88,
264
265 /// Destination address required
266 DESTADDRREQ = 89,
267
268 /// Message too long
269 MSGSIZE = 90,
270
271 /// Protocol wrong type for socket
272 PROTOTYPE = 91,
273
274 /// Protocol not available
275 NOPROTOOPT = 92,
276
277 /// Protocol not supported
278 PROTONOSUPPORT = 93,
279
280 /// Socket type not supported
281 SOCKTNOSUPPORT = 94,
282
283 /// Operation not supported on transport endpoint
284 /// This code also means `NOTSUP`.
285 OPNOTSUPP = 95,
286
287 /// Protocol family not supported
288 PFNOSUPPORT = 96,
289
290 /// Address family not supported by protocol
291 AFNOSUPPORT = 97,
292
293 /// Address already in use
294 ADDRINUSE = 98,
295
296 /// Cannot assign requested address
297 ADDRNOTAVAIL = 99,
298
299 /// Network is down
300 NETDOWN = 100,
301
302 /// Network is unreachable
303 NETUNREACH = 101,
304
305 /// Network dropped connection because of reset
306 NETRESET = 102,
307
308 /// Software caused connection abort
309 CONNABORTED = 103,
310
311 /// Connection reset by peer
312 CONNRESET = 104,
313
314 /// No buffer space available
315 NOBUFS = 105,
316
317 /// Transport endpoint is already connected
318 ISCONN = 106,
319
320 /// Transport endpoint is not connected
321 NOTCONN = 107,
322
323 /// Cannot send after transport endpoint shutdown
324 SHUTDOWN = 108,
325
326 /// Too many references: cannot splice
327 TOOMANYREFS = 109,
328
329 /// Connection timed out
330 TIMEDOUT = 110,
331
332 /// Connection refused
333 CONNREFUSED = 111,
334
335 /// Host is down
336 HOSTDOWN = 112,
337
338 /// No route to host
339 HOSTUNREACH = 113,
340
341 /// Operation already in progress
342 ALREADY = 114,
343
344 /// Operation now in progress
345 INPROGRESS = 115,
346
347 /// Stale NFS file handle
348 STALE = 116,
349
350 /// Structure needs cleaning
351 UCLEAN = 117,
352
353 /// Not a XENIX named type file
354 NOTNAM = 118,
355
356 /// No XENIX semaphores available
357 NAVAIL = 119,
358
359 /// Is a named type file
360 ISNAM = 120,
361
362 /// Remote I/O error
363 REMOTEIO = 121,
364
365 /// Quota exceeded
366 DQUOT = 122,
367
368 /// No medium found
369 NOMEDIUM = 123,
370
371 /// Wrong medium type
372 MEDIUMTYPE = 124,
373
374 /// Operation canceled
375 CANCELED = 125,
376
377 /// Required key not available
378 NOKEY = 126,
379
380 /// Key has expired
381 KEYEXPIRED = 127,
382
383 /// Key has been revoked
384 KEYREVOKED = 128,
385
386 /// Key was rejected by service
387 KEYREJECTED = 129,
388
389 // for robust mutexes
390
391 /// Owner died
392 OWNERDEAD = 130,
393
394 /// State not recoverable
395 NOTRECOVERABLE = 131,
396
397 /// Operation not possible due to RF-kill
398 RFKILL = 132,
399
400 /// Memory page has hardware error
401 HWPOISON = 133,
402
403 // nameserver query return codes
404
405 /// DNS server returned answer with no data
406 NSRNODATA = 160,
407
408 /// DNS server claims query was misformatted
409 NSRFORMERR = 161,
410
411 /// DNS server returned general failure
412 NSRSERVFAIL = 162,
413
414 /// Domain name not found
415 NSRNOTFOUND = 163,
416
417 /// DNS server does not implement requested operation
418 NSRNOTIMP = 164,
419
420 /// DNS server refused query
421 NSRREFUSED = 165,
422
423 /// Misformatted DNS query
424 NSRBADQUERY = 166,
425
426 /// Misformatted domain name
427 NSRBADNAME = 167,
428
429 /// Unsupported address family
430 NSRBADFAMILY = 168,
431
432 /// Misformatted DNS reply
433 NSRBADRESP = 169,
434
435 /// Could not contact DNS servers
436 NSRCONNREFUSED = 170,
437
438 /// Timeout while contacting DNS servers
439 NSRTIMEOUT = 171,
440
441 /// End of file
442 NSROF = 172,
443
444 /// Error reading file
445 NSRFILE = 173,
446
447 /// Out of memory
448 NSRNOMEM = 174,
449
450 /// Application terminated lookup
451 NSRDESTRUCTION = 175,
452
453 /// Domain name is too long
454 NSRQUERYDOMAINTOOLONG = 176,
455
456 /// Domain name is too long
457 NSRCNAMELOOP = 177,
458
459 _,
460};
lib/std/os/linux/errno/mips.zig deleted-141
......@@ -1,141 +0,0 @@
1//! These are MIPS ABI compatible.
2pub const E = enum(i32) {
3 /// No error occurred.
4 SUCCESS = 0,
5
6 PERM = 1,
7 NOENT = 2,
8 SRCH = 3,
9 INTR = 4,
10 IO = 5,
11 NXIO = 6,
12 @"2BIG" = 7,
13 NOEXEC = 8,
14 BADF = 9,
15 CHILD = 10,
16 /// Also used for WOULDBLOCK.
17 AGAIN = 11,
18 NOMEM = 12,
19 ACCES = 13,
20 FAULT = 14,
21 NOTBLK = 15,
22 BUSY = 16,
23 EXIST = 17,
24 XDEV = 18,
25 NODEV = 19,
26 NOTDIR = 20,
27 ISDIR = 21,
28 INVAL = 22,
29 NFILE = 23,
30 MFILE = 24,
31 NOTTY = 25,
32 TXTBSY = 26,
33 FBIG = 27,
34 NOSPC = 28,
35 SPIPE = 29,
36 ROFS = 30,
37 MLINK = 31,
38 PIPE = 32,
39 DOM = 33,
40 RANGE = 34,
41
42 NOMSG = 35,
43 IDRM = 36,
44 CHRNG = 37,
45 L2NSYNC = 38,
46 L3HLT = 39,
47 L3RST = 40,
48 LNRNG = 41,
49 UNATCH = 42,
50 NOCSI = 43,
51 L2HLT = 44,
52 DEADLK = 45,
53 NOLCK = 46,
54 BADE = 50,
55 BADR = 51,
56 XFULL = 52,
57 NOANO = 53,
58 BADRQC = 54,
59 BADSLT = 55,
60 DEADLOCK = 56,
61 BFONT = 59,
62 NOSTR = 60,
63 NODATA = 61,
64 TIME = 62,
65 NOSR = 63,
66 NONET = 64,
67 NOPKG = 65,
68 REMOTE = 66,
69 NOLINK = 67,
70 ADV = 68,
71 SRMNT = 69,
72 COMM = 70,
73 PROTO = 71,
74 DOTDOT = 73,
75 MULTIHOP = 74,
76 BADMSG = 77,
77 NAMETOOLONG = 78,
78 OVERFLOW = 79,
79 NOTUNIQ = 80,
80 BADFD = 81,
81 REMCHG = 82,
82 LIBACC = 83,
83 LIBBAD = 84,
84 LIBSCN = 85,
85 LIBMAX = 86,
86 LIBEXEC = 87,
87 ILSEQ = 88,
88 NOSYS = 89,
89 LOOP = 90,
90 RESTART = 91,
91 STRPIPE = 92,
92 NOTEMPTY = 93,
93 USERS = 94,
94 NOTSOCK = 95,
95 DESTADDRREQ = 96,
96 MSGSIZE = 97,
97 PROTOTYPE = 98,
98 NOPROTOOPT = 99,
99 PROTONOSUPPORT = 120,
100 SOCKTNOSUPPORT = 121,
101 OPNOTSUPP = 122,
102 PFNOSUPPORT = 123,
103 AFNOSUPPORT = 124,
104 ADDRINUSE = 125,
105 ADDRNOTAVAIL = 126,
106 NETDOWN = 127,
107 NETUNREACH = 128,
108 NETRESET = 129,
109 CONNABORTED = 130,
110 CONNRESET = 131,
111 NOBUFS = 132,
112 ISCONN = 133,
113 NOTCONN = 134,
114 UCLEAN = 135,
115 NOTNAM = 137,
116 NAVAIL = 138,
117 ISNAM = 139,
118 REMOTEIO = 140,
119 SHUTDOWN = 143,
120 TOOMANYREFS = 144,
121 TIMEDOUT = 145,
122 CONNREFUSED = 146,
123 HOSTDOWN = 147,
124 HOSTUNREACH = 148,
125 ALREADY = 149,
126 INPROGRESS = 150,
127 STALE = 151,
128 CANCELED = 158,
129 NOMEDIUM = 159,
130 MEDIUMTYPE = 160,
131 NOKEY = 161,
132 KEYEXPIRED = 162,
133 KEYREVOKED = 163,
134 KEYREJECTED = 164,
135 OWNERDEAD = 165,
136 NOTRECOVERABLE = 166,
137 RFKILL = 167,
138 HWPOISON = 168,
139 DQUOT = 1133,
140 _,
141};
lib/std/os/linux/errno/sparc.zig deleted-144
......@@ -1,144 +0,0 @@
1//! These match the SunOS error numbering scheme.
2pub const E = enum(i32) {
3 /// No error occurred.
4 SUCCESS = 0,
5
6 PERM = 1,
7 NOENT = 2,
8 SRCH = 3,
9 INTR = 4,
10 IO = 5,
11 NXIO = 6,
12 @"2BIG" = 7,
13 NOEXEC = 8,
14 BADF = 9,
15 CHILD = 10,
16 /// Also used for WOULDBLOCK
17 AGAIN = 11,
18 NOMEM = 12,
19 ACCES = 13,
20 FAULT = 14,
21 NOTBLK = 15,
22 BUSY = 16,
23 EXIST = 17,
24 XDEV = 18,
25 NODEV = 19,
26 NOTDIR = 20,
27 ISDIR = 21,
28 INVAL = 22,
29 NFILE = 23,
30 MFILE = 24,
31 NOTTY = 25,
32 TXTBSY = 26,
33 FBIG = 27,
34 NOSPC = 28,
35 SPIPE = 29,
36 ROFS = 30,
37 MLINK = 31,
38 PIPE = 32,
39 DOM = 33,
40 RANGE = 34,
41
42 INPROGRESS = 36,
43 ALREADY = 37,
44 NOTSOCK = 38,
45 DESTADDRREQ = 39,
46 MSGSIZE = 40,
47 PROTOTYPE = 41,
48 NOPROTOOPT = 42,
49 PROTONOSUPPORT = 43,
50 SOCKTNOSUPPORT = 44,
51 /// Also used for NOTSUP
52 OPNOTSUPP = 45,
53 PFNOSUPPORT = 46,
54 AFNOSUPPORT = 47,
55 ADDRINUSE = 48,
56 ADDRNOTAVAIL = 49,
57 NETDOWN = 50,
58 NETUNREACH = 51,
59 NETRESET = 52,
60 CONNABORTED = 53,
61 CONNRESET = 54,
62 NOBUFS = 55,
63 ISCONN = 56,
64 NOTCONN = 57,
65 SHUTDOWN = 58,
66 TOOMANYREFS = 59,
67 TIMEDOUT = 60,
68 CONNREFUSED = 61,
69 LOOP = 62,
70 NAMETOOLONG = 63,
71 HOSTDOWN = 64,
72 HOSTUNREACH = 65,
73 NOTEMPTY = 66,
74 PROCLIM = 67,
75 USERS = 68,
76 DQUOT = 69,
77 STALE = 70,
78 REMOTE = 71,
79 NOSTR = 72,
80 TIME = 73,
81 NOSR = 74,
82 NOMSG = 75,
83 BADMSG = 76,
84 IDRM = 77,
85 DEADLK = 78,
86 NOLCK = 79,
87 NONET = 80,
88 RREMOTE = 81,
89 NOLINK = 82,
90 ADV = 83,
91 SRMNT = 84,
92 COMM = 85,
93 PROTO = 86,
94 MULTIHOP = 87,
95 DOTDOT = 88,
96 REMCHG = 89,
97 NOSYS = 90,
98 STRPIPE = 91,
99 OVERFLOW = 92,
100 BADFD = 93,
101 CHRNG = 94,
102 L2NSYNC = 95,
103 L3HLT = 96,
104 L3RST = 97,
105 LNRNG = 98,
106 UNATCH = 99,
107 NOCSI = 100,
108 L2HLT = 101,
109 BADE = 102,
110 BADR = 103,
111 XFULL = 104,
112 NOANO = 105,
113 BADRQC = 106,
114 BADSLT = 107,
115 DEADLOCK = 108,
116 BFONT = 109,
117 LIBEXEC = 110,
118 NODATA = 111,
119 LIBBAD = 112,
120 NOPKG = 113,
121 LIBACC = 114,
122 NOTUNIQ = 115,
123 RESTART = 116,
124 UCLEAN = 117,
125 NOTNAM = 118,
126 NAVAIL = 119,
127 ISNAM = 120,
128 REMOTEIO = 121,
129 ILSEQ = 122,
130 LIBMAX = 123,
131 LIBSCN = 124,
132 NOMEDIUM = 125,
133 MEDIUMTYPE = 126,
134 CANCELED = 127,
135 NOKEY = 128,
136 KEYEXPIRED = 129,
137 KEYREVOKED = 130,
138 KEYREJECTED = 131,
139 OWNERDEAD = 132,
140 NOTRECOVERABLE = 133,
141 RFKILL = 134,
142 HWPOISON = 135,
143 _,
144};
lib/std/os/linux/io_uring_sqe.zig+61-62
......@@ -2,8 +2,7 @@
22//! Split into its own file to compartmentalize the initialization methods.
33
44const std = @import("../../std.zig");
5const os = std.os;
6const linux = os.linux;
5const linux = std.os.linux;
76
87pub const io_uring_sqe = extern struct {
98 opcode: linux.IORING_OP,
......@@ -40,7 +39,7 @@ pub const io_uring_sqe = extern struct {
4039 };
4140 }
4241
43 pub fn prep_fsync(sqe: *linux.io_uring_sqe, fd: os.fd_t, flags: u32) void {
42 pub fn prep_fsync(sqe: *linux.io_uring_sqe, fd: linux.fd_t, flags: u32) void {
4443 sqe.* = .{
4544 .opcode = .FSYNC,
4645 .flags = 0,
......@@ -62,7 +61,7 @@ pub const io_uring_sqe = extern struct {
6261 pub fn prep_rw(
6362 sqe: *linux.io_uring_sqe,
6463 op: linux.IORING_OP,
65 fd: os.fd_t,
64 fd: linux.fd_t,
6665 addr: u64,
6766 len: usize,
6867 offset: u64,
......@@ -85,15 +84,15 @@ pub const io_uring_sqe = extern struct {
8584 };
8685 }
8786
88 pub fn prep_read(sqe: *linux.io_uring_sqe, fd: os.fd_t, buffer: []u8, offset: u64) void {
87 pub fn prep_read(sqe: *linux.io_uring_sqe, fd: linux.fd_t, buffer: []u8, offset: u64) void {
8988 sqe.prep_rw(.READ, fd, @intFromPtr(buffer.ptr), buffer.len, offset);
9089 }
9190
92 pub fn prep_write(sqe: *linux.io_uring_sqe, fd: os.fd_t, buffer: []const u8, offset: u64) void {
91 pub fn prep_write(sqe: *linux.io_uring_sqe, fd: linux.fd_t, buffer: []const u8, offset: u64) void {
9392 sqe.prep_rw(.WRITE, fd, @intFromPtr(buffer.ptr), buffer.len, offset);
9493 }
9594
96 pub fn prep_splice(sqe: *linux.io_uring_sqe, fd_in: os.fd_t, off_in: u64, fd_out: os.fd_t, off_out: u64, len: usize) void {
95 pub fn prep_splice(sqe: *linux.io_uring_sqe, fd_in: linux.fd_t, off_in: u64, fd_out: linux.fd_t, off_out: u64, len: usize) void {
9796 sqe.prep_rw(.SPLICE, fd_out, undefined, len, off_out);
9897 sqe.addr = off_in;
9998 sqe.splice_fd_in = fd_in;
......@@ -101,8 +100,8 @@ pub const io_uring_sqe = extern struct {
101100
102101 pub fn prep_readv(
103102 sqe: *linux.io_uring_sqe,
104 fd: os.fd_t,
105 iovecs: []const os.iovec,
103 fd: linux.fd_t,
104 iovecs: []const std.posix.iovec,
106105 offset: u64,
107106 ) void {
108107 sqe.prep_rw(.READV, fd, @intFromPtr(iovecs.ptr), iovecs.len, offset);
......@@ -110,28 +109,28 @@ pub const io_uring_sqe = extern struct {
110109
111110 pub fn prep_writev(
112111 sqe: *linux.io_uring_sqe,
113 fd: os.fd_t,
114 iovecs: []const os.iovec_const,
112 fd: linux.fd_t,
113 iovecs: []const std.posix.iovec_const,
115114 offset: u64,
116115 ) void {
117116 sqe.prep_rw(.WRITEV, fd, @intFromPtr(iovecs.ptr), iovecs.len, offset);
118117 }
119118
120 pub fn prep_read_fixed(sqe: *linux.io_uring_sqe, fd: os.fd_t, buffer: *os.iovec, offset: u64, buffer_index: u16) void {
119 pub fn prep_read_fixed(sqe: *linux.io_uring_sqe, fd: linux.fd_t, buffer: *std.posix.iovec, offset: u64, buffer_index: u16) void {
121120 sqe.prep_rw(.READ_FIXED, fd, @intFromPtr(buffer.iov_base), buffer.iov_len, offset);
122121 sqe.buf_index = buffer_index;
123122 }
124123
125 pub fn prep_write_fixed(sqe: *linux.io_uring_sqe, fd: os.fd_t, buffer: *os.iovec, offset: u64, buffer_index: u16) void {
124 pub fn prep_write_fixed(sqe: *linux.io_uring_sqe, fd: linux.fd_t, buffer: *std.posix.iovec, offset: u64, buffer_index: u16) void {
126125 sqe.prep_rw(.WRITE_FIXED, fd, @intFromPtr(buffer.iov_base), buffer.iov_len, offset);
127126 sqe.buf_index = buffer_index;
128127 }
129128
130129 pub fn prep_accept(
131130 sqe: *linux.io_uring_sqe,
132 fd: os.fd_t,
133 addr: ?*os.sockaddr,
134 addrlen: ?*os.socklen_t,
131 fd: linux.fd_t,
132 addr: ?*linux.sockaddr,
133 addrlen: ?*linux.socklen_t,
135134 flags: u32,
136135 ) void {
137136 // `addr` holds a pointer to `sockaddr`, and `addr2` holds a pointer to socklen_t`.
......@@ -142,9 +141,9 @@ pub const io_uring_sqe = extern struct {
142141
143142 pub fn prep_accept_direct(
144143 sqe: *linux.io_uring_sqe,
145 fd: os.fd_t,
146 addr: ?*os.sockaddr,
147 addrlen: ?*os.socklen_t,
144 fd: linux.fd_t,
145 addr: ?*linux.sockaddr,
146 addrlen: ?*linux.socklen_t,
148147 flags: u32,
149148 file_index: u32,
150149 ) void {
......@@ -154,9 +153,9 @@ pub const io_uring_sqe = extern struct {
154153
155154 pub fn prep_multishot_accept_direct(
156155 sqe: *linux.io_uring_sqe,
157 fd: os.fd_t,
158 addr: ?*os.sockaddr,
159 addrlen: ?*os.socklen_t,
156 fd: linux.fd_t,
157 addr: ?*linux.sockaddr,
158 addrlen: ?*linux.socklen_t,
160159 flags: u32,
161160 ) void {
162161 prep_multishot_accept(sqe, fd, addr, addrlen, flags);
......@@ -177,9 +176,9 @@ pub const io_uring_sqe = extern struct {
177176
178177 pub fn prep_connect(
179178 sqe: *linux.io_uring_sqe,
180 fd: os.fd_t,
181 addr: *const os.sockaddr,
182 addrlen: os.socklen_t,
179 fd: linux.fd_t,
180 addr: *const linux.sockaddr,
181 addrlen: linux.socklen_t,
183182 ) void {
184183 // `addrlen` maps to `sqe.off` (u64) instead of `sqe.len` (which is only a u32).
185184 sqe.prep_rw(.CONNECT, fd, @intFromPtr(addr), 0, addrlen);
......@@ -187,22 +186,22 @@ pub const io_uring_sqe = extern struct {
187186
188187 pub fn prep_epoll_ctl(
189188 sqe: *linux.io_uring_sqe,
190 epfd: os.fd_t,
191 fd: os.fd_t,
189 epfd: linux.fd_t,
190 fd: linux.fd_t,
192191 op: u32,
193192 ev: ?*linux.epoll_event,
194193 ) void {
195194 sqe.prep_rw(.EPOLL_CTL, epfd, @intFromPtr(ev), op, @intCast(fd));
196195 }
197196
198 pub fn prep_recv(sqe: *linux.io_uring_sqe, fd: os.fd_t, buffer: []u8, flags: u32) void {
197 pub fn prep_recv(sqe: *linux.io_uring_sqe, fd: linux.fd_t, buffer: []u8, flags: u32) void {
199198 sqe.prep_rw(.RECV, fd, @intFromPtr(buffer.ptr), buffer.len, 0);
200199 sqe.rw_flags = flags;
201200 }
202201
203202 pub fn prep_recv_multishot(
204203 sqe: *linux.io_uring_sqe,
205 fd: os.fd_t,
204 fd: linux.fd_t,
206205 buffer: []u8,
207206 flags: u32,
208207 ) void {
......@@ -212,8 +211,8 @@ pub const io_uring_sqe = extern struct {
212211
213212 pub fn prep_recvmsg(
214213 sqe: *linux.io_uring_sqe,
215 fd: os.fd_t,
216 msg: *os.msghdr,
214 fd: linux.fd_t,
215 msg: *linux.msghdr,
217216 flags: u32,
218217 ) void {
219218 sqe.prep_rw(.RECVMSG, fd, @intFromPtr(msg), 1, 0);
......@@ -222,26 +221,26 @@ pub const io_uring_sqe = extern struct {
222221
223222 pub fn prep_recvmsg_multishot(
224223 sqe: *linux.io_uring_sqe,
225 fd: os.fd_t,
226 msg: *os.msghdr,
224 fd: linux.fd_t,
225 msg: *linux.msghdr,
227226 flags: u32,
228227 ) void {
229228 sqe.prep_recvmsg(fd, msg, flags);
230229 sqe.ioprio |= linux.IORING_RECV_MULTISHOT;
231230 }
232231
233 pub fn prep_send(sqe: *linux.io_uring_sqe, fd: os.fd_t, buffer: []const u8, flags: u32) void {
232 pub fn prep_send(sqe: *linux.io_uring_sqe, fd: linux.fd_t, buffer: []const u8, flags: u32) void {
234233 sqe.prep_rw(.SEND, fd, @intFromPtr(buffer.ptr), buffer.len, 0);
235234 sqe.rw_flags = flags;
236235 }
237236
238 pub fn prep_send_zc(sqe: *linux.io_uring_sqe, fd: os.fd_t, buffer: []const u8, flags: u32, zc_flags: u16) void {
237 pub fn prep_send_zc(sqe: *linux.io_uring_sqe, fd: linux.fd_t, buffer: []const u8, flags: u32, zc_flags: u16) void {
239238 sqe.prep_rw(.SEND_ZC, fd, @intFromPtr(buffer.ptr), buffer.len, 0);
240239 sqe.rw_flags = flags;
241240 sqe.ioprio = zc_flags;
242241 }
243242
244 pub fn prep_send_zc_fixed(sqe: *linux.io_uring_sqe, fd: os.fd_t, buffer: []const u8, flags: u32, zc_flags: u16, buf_index: u16) void {
243 pub fn prep_send_zc_fixed(sqe: *linux.io_uring_sqe, fd: linux.fd_t, buffer: []const u8, flags: u32, zc_flags: u16, buf_index: u16) void {
245244 prep_send_zc(sqe, fd, buffer, flags, zc_flags);
246245 sqe.ioprio |= linux.IORING_RECVSEND_FIXED_BUF;
247246 sqe.buf_index = buf_index;
......@@ -249,8 +248,8 @@ pub const io_uring_sqe = extern struct {
249248
250249 pub fn prep_sendmsg_zc(
251250 sqe: *linux.io_uring_sqe,
252 fd: os.fd_t,
253 msg: *const os.msghdr_const,
251 fd: linux.fd_t,
252 msg: *const linux.msghdr_const,
254253 flags: u32,
255254 ) void {
256255 prep_sendmsg(sqe, fd, msg, flags);
......@@ -259,8 +258,8 @@ pub const io_uring_sqe = extern struct {
259258
260259 pub fn prep_sendmsg(
261260 sqe: *linux.io_uring_sqe,
262 fd: os.fd_t,
263 msg: *const os.msghdr_const,
261 fd: linux.fd_t,
262 msg: *const linux.msghdr_const,
264263 flags: u32,
265264 ) void {
266265 sqe.prep_rw(.SENDMSG, fd, @intFromPtr(msg), 1, 0);
......@@ -269,10 +268,10 @@ pub const io_uring_sqe = extern struct {
269268
270269 pub fn prep_openat(
271270 sqe: *linux.io_uring_sqe,
272 fd: os.fd_t,
271 fd: linux.fd_t,
273272 path: [*:0]const u8,
274273 flags: linux.O,
275 mode: os.mode_t,
274 mode: linux.mode_t,
276275 ) void {
277276 sqe.prep_rw(.OPENAT, fd, @intFromPtr(path), mode, 0);
278277 sqe.rw_flags = @bitCast(flags);
......@@ -280,17 +279,17 @@ pub const io_uring_sqe = extern struct {
280279
281280 pub fn prep_openat_direct(
282281 sqe: *linux.io_uring_sqe,
283 fd: os.fd_t,
282 fd: linux.fd_t,
284283 path: [*:0]const u8,
285284 flags: linux.O,
286 mode: os.mode_t,
285 mode: linux.mode_t,
287286 file_index: u32,
288287 ) void {
289288 prep_openat(sqe, fd, path, flags, mode);
290289 __io_uring_set_target_fixed_file(sqe, file_index);
291290 }
292291
293 pub fn prep_close(sqe: *linux.io_uring_sqe, fd: os.fd_t) void {
292 pub fn prep_close(sqe: *linux.io_uring_sqe, fd: linux.fd_t) void {
294293 sqe.* = .{
295294 .opcode = .CLOSE,
296295 .flags = 0,
......@@ -316,7 +315,7 @@ pub const io_uring_sqe = extern struct {
316315
317316 pub fn prep_timeout(
318317 sqe: *linux.io_uring_sqe,
319 ts: *const os.linux.kernel_timespec,
318 ts: *const linux.kernel_timespec,
320319 count: u32,
321320 flags: u32,
322321 ) void {
......@@ -345,7 +344,7 @@ pub const io_uring_sqe = extern struct {
345344
346345 pub fn prep_link_timeout(
347346 sqe: *linux.io_uring_sqe,
348 ts: *const os.linux.kernel_timespec,
347 ts: *const linux.kernel_timespec,
349348 flags: u32,
350349 ) void {
351350 sqe.prep_rw(.LINK_TIMEOUT, -1, @intFromPtr(ts), 1, 0);
......@@ -354,7 +353,7 @@ pub const io_uring_sqe = extern struct {
354353
355354 pub fn prep_poll_add(
356355 sqe: *linux.io_uring_sqe,
357 fd: os.fd_t,
356 fd: linux.fd_t,
358357 poll_mask: u32,
359358 ) void {
360359 sqe.prep_rw(.POLL_ADD, fd, @intFromPtr(@as(?*anyopaque, null)), 0, 0);
......@@ -393,7 +392,7 @@ pub const io_uring_sqe = extern struct {
393392
394393 pub fn prep_fallocate(
395394 sqe: *linux.io_uring_sqe,
396 fd: os.fd_t,
395 fd: linux.fd_t,
397396 mode: i32,
398397 offset: u64,
399398 len: u64,
......@@ -418,7 +417,7 @@ pub const io_uring_sqe = extern struct {
418417
419418 pub fn prep_statx(
420419 sqe: *linux.io_uring_sqe,
421 fd: os.fd_t,
420 fd: linux.fd_t,
422421 path: [*:0]const u8,
423422 flags: u32,
424423 mask: u32,
......@@ -439,7 +438,7 @@ pub const io_uring_sqe = extern struct {
439438
440439 pub fn prep_shutdown(
441440 sqe: *linux.io_uring_sqe,
442 sockfd: os.socket_t,
441 sockfd: linux.socket_t,
443442 how: u32,
444443 ) void {
445444 sqe.prep_rw(.SHUTDOWN, sockfd, 0, how, 0);
......@@ -447,9 +446,9 @@ pub const io_uring_sqe = extern struct {
447446
448447 pub fn prep_renameat(
449448 sqe: *linux.io_uring_sqe,
450 old_dir_fd: os.fd_t,
449 old_dir_fd: linux.fd_t,
451450 old_path: [*:0]const u8,
452 new_dir_fd: os.fd_t,
451 new_dir_fd: linux.fd_t,
453452 new_path: [*:0]const u8,
454453 flags: u32,
455454 ) void {
......@@ -466,7 +465,7 @@ pub const io_uring_sqe = extern struct {
466465
467466 pub fn prep_unlinkat(
468467 sqe: *linux.io_uring_sqe,
469 dir_fd: os.fd_t,
468 dir_fd: linux.fd_t,
470469 path: [*:0]const u8,
471470 flags: u32,
472471 ) void {
......@@ -476,9 +475,9 @@ pub const io_uring_sqe = extern struct {
476475
477476 pub fn prep_mkdirat(
478477 sqe: *linux.io_uring_sqe,
479 dir_fd: os.fd_t,
478 dir_fd: linux.fd_t,
480479 path: [*:0]const u8,
481 mode: os.mode_t,
480 mode: linux.mode_t,
482481 ) void {
483482 sqe.prep_rw(.MKDIRAT, dir_fd, @intFromPtr(path), mode, 0);
484483 }
......@@ -486,7 +485,7 @@ pub const io_uring_sqe = extern struct {
486485 pub fn prep_symlinkat(
487486 sqe: *linux.io_uring_sqe,
488487 target: [*:0]const u8,
489 new_dir_fd: os.fd_t,
488 new_dir_fd: linux.fd_t,
490489 link_path: [*:0]const u8,
491490 ) void {
492491 sqe.prep_rw(
......@@ -500,9 +499,9 @@ pub const io_uring_sqe = extern struct {
500499
501500 pub fn prep_linkat(
502501 sqe: *linux.io_uring_sqe,
503 old_dir_fd: os.fd_t,
502 old_dir_fd: linux.fd_t,
504503 old_path: [*:0]const u8,
505 new_dir_fd: os.fd_t,
504 new_dir_fd: linux.fd_t,
506505 new_path: [*:0]const u8,
507506 flags: u32,
508507 ) void {
......@@ -541,9 +540,9 @@ pub const io_uring_sqe = extern struct {
541540
542541 pub fn prep_multishot_accept(
543542 sqe: *linux.io_uring_sqe,
544 fd: os.fd_t,
545 addr: ?*os.sockaddr,
546 addrlen: ?*os.socklen_t,
543 fd: linux.fd_t,
544 addr: ?*linux.sockaddr,
545 addrlen: ?*linux.socklen_t,
547546 flags: u32,
548547 ) void {
549548 prep_accept(sqe, fd, addr, addrlen, flags);
lib/std/os/linux/mips.zig+2-2
......@@ -3,8 +3,8 @@ const maxInt = std.math.maxInt;
33const linux = std.os.linux;
44const SYS = linux.SYS;
55const socklen_t = linux.socklen_t;
6const iovec = std.os.iovec;
7const iovec_const = std.os.iovec_const;
6const iovec = std.posix.iovec;
7const iovec_const = std.posix.iovec_const;
88const uid_t = linux.uid_t;
99const gid_t = linux.gid_t;
1010const pid_t = linux.pid_t;
lib/std/os/linux/mips64.zig+2-2
......@@ -3,8 +3,8 @@ const maxInt = std.math.maxInt;
33const linux = std.os.linux;
44const SYS = linux.SYS;
55const socklen_t = linux.socklen_t;
6const iovec = std.os.iovec;
7const iovec_const = std.os.iovec_const;
6const iovec = std.posix.iovec;
7const iovec_const = std.posix.iovec_const;
88const uid_t = linux.uid_t;
99const gid_t = linux.gid_t;
1010const pid_t = linux.pid_t;
lib/std/os/linux/powerpc.zig+2-2
......@@ -3,8 +3,8 @@ const maxInt = std.math.maxInt;
33const linux = std.os.linux;
44const SYS = linux.SYS;
55const socklen_t = linux.socklen_t;
6const iovec = std.os.iovec;
7const iovec_const = std.os.iovec_const;
6const iovec = std.posix.iovec;
7const iovec_const = std.posix.iovec_const;
88const uid_t = linux.uid_t;
99const gid_t = linux.gid_t;
1010const pid_t = linux.pid_t;
lib/std/os/linux/powerpc64.zig+2-2
......@@ -3,8 +3,8 @@ const maxInt = std.math.maxInt;
33const linux = std.os.linux;
44const SYS = linux.SYS;
55const socklen_t = linux.socklen_t;
6const iovec = std.os.iovec;
7const iovec_const = std.os.iovec_const;
6const iovec = std.posix.iovec;
7const iovec_const = std.posix.iovec_const;
88const uid_t = linux.uid_t;
99const gid_t = linux.gid_t;
1010const pid_t = linux.pid_t;
lib/std/os/linux/riscv64.zig+2-2
......@@ -1,6 +1,6 @@
11const std = @import("../../std.zig");
2const iovec = std.os.iovec;
3const iovec_const = std.os.iovec_const;
2const iovec = std.posix.iovec;
3const iovec_const = std.posix.iovec_const;
44const linux = std.os.linux;
55const SYS = linux.SYS;
66const uid_t = std.os.linux.uid_t;
lib/std/os/linux/sparc64.zig+2-2
......@@ -10,8 +10,8 @@ const linux = std.os.linux;
1010const SYS = linux.SYS;
1111const sockaddr = linux.sockaddr;
1212const socklen_t = linux.socklen_t;
13const iovec = std.os.iovec;
14const iovec_const = std.os.iovec_const;
13const iovec = std.posix.iovec;
14const iovec_const = std.posix.iovec_const;
1515const timespec = linux.timespec;
1616
1717pub fn syscall_pipe(fd: *[2]i32) usize {
lib/std/os/linux/test.zig+8-8
......@@ -18,7 +18,7 @@ test "fallocate" {
1818 try expect((try file.stat()).size == 0);
1919
2020 const len: i64 = 65536;
21 switch (linux.getErrno(linux.fallocate(file.handle, 0, 0, len))) {
21 switch (linux.E.init(linux.fallocate(file.handle, 0, 0, len))) {
2222 .SUCCESS => {},
2323 .NOSYS => return error.SkipZigTest,
2424 .OPNOTSUPP => return error.SkipZigTest,
......@@ -34,11 +34,11 @@ test "getpid" {
3434
3535test "timer" {
3636 const epoll_fd = linux.epoll_create();
37 var err: linux.E = linux.getErrno(epoll_fd);
37 var err: linux.E = linux.E.init(epoll_fd);
3838 try expect(err == .SUCCESS);
3939
4040 const timer_fd = linux.timerfd_create(linux.CLOCK.MONOTONIC, .{});
41 try expect(linux.getErrno(timer_fd) == .SUCCESS);
41 try expect(linux.E.init(timer_fd) == .SUCCESS);
4242
4343 const time_interval = linux.timespec{
4444 .tv_sec = 0,
......@@ -50,7 +50,7 @@ test "timer" {
5050 .it_value = time_interval,
5151 };
5252
53 err = linux.getErrno(linux.timerfd_settime(@as(i32, @intCast(timer_fd)), .{}, &new_time, null));
53 err = linux.E.init(linux.timerfd_settime(@as(i32, @intCast(timer_fd)), .{}, &new_time, null));
5454 try expect(err == .SUCCESS);
5555
5656 var event = linux.epoll_event{
......@@ -58,13 +58,13 @@ test "timer" {
5858 .data = linux.epoll_data{ .ptr = 0 },
5959 };
6060
61 err = linux.getErrno(linux.epoll_ctl(@as(i32, @intCast(epoll_fd)), linux.EPOLL.CTL_ADD, @as(i32, @intCast(timer_fd)), &event));
61 err = linux.E.init(linux.epoll_ctl(@as(i32, @intCast(epoll_fd)), linux.EPOLL.CTL_ADD, @as(i32, @intCast(timer_fd)), &event));
6262 try expect(err == .SUCCESS);
6363
6464 const events_one: linux.epoll_event = undefined;
6565 var events = [_]linux.epoll_event{events_one} ** 8;
6666
67 err = linux.getErrno(linux.epoll_wait(@as(i32, @intCast(epoll_fd)), &events, 8, -1));
67 err = linux.E.init(linux.epoll_wait(@as(i32, @intCast(epoll_fd)), &events, 8, -1));
6868 try expect(err == .SUCCESS);
6969}
7070
......@@ -77,7 +77,7 @@ test "statx" {
7777 defer file.close();
7878
7979 var statx_buf: linux.Statx = undefined;
80 switch (linux.getErrno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_buf))) {
80 switch (linux.E.init(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_buf))) {
8181 .SUCCESS => {},
8282 // The statx syscall was only introduced in linux 4.11
8383 .NOSYS => return error.SkipZigTest,
......@@ -85,7 +85,7 @@ test "statx" {
8585 }
8686
8787 var stat_buf: linux.Stat = undefined;
88 switch (linux.getErrno(linux.fstatat(file.handle, "", &stat_buf, linux.AT.EMPTY_PATH))) {
88 switch (linux.E.init(linux.fstatat(file.handle, "", &stat_buf, linux.AT.EMPTY_PATH))) {
8989 .SUCCESS => {},
9090 else => unreachable,
9191 }
lib/std/os/linux/tls.zig+10-9
......@@ -1,10 +1,11 @@
11const std = @import("std");
2const os = std.os;
32const mem = std.mem;
43const elf = std.elf;
54const math = std.math;
65const assert = std.debug.assert;
76const native_arch = @import("builtin").cpu.arch;
7const linux = std.os.linux;
8const posix = std.posix;
89
910// This file implements the two TLS variants [1] used by ELF-based systems.
1011//
......@@ -111,7 +112,7 @@ pub var tls_image: TLSImage = undefined;
111112pub fn setThreadPointer(addr: usize) void {
112113 switch (native_arch) {
113114 .x86 => {
114 var user_desc = std.os.linux.user_desc{
115 var user_desc: linux.user_desc = .{
115116 .entry_number = tls_image.gdt_entry_number,
116117 .base_addr = addr,
117118 .limit = 0xfffff,
......@@ -124,7 +125,7 @@ pub fn setThreadPointer(addr: usize) void {
124125 .useable = 1,
125126 },
126127 };
127 const rc = std.os.linux.syscall1(.set_thread_area, @intFromPtr(&user_desc));
128 const rc = linux.syscall1(.set_thread_area, @intFromPtr(&user_desc));
128129 assert(rc == 0);
129130
130131 const gdt_entry_number = user_desc.entry_number;
......@@ -137,7 +138,7 @@ pub fn setThreadPointer(addr: usize) void {
137138 );
138139 },
139140 .x86_64 => {
140 const rc = std.os.linux.syscall2(.arch_prctl, std.os.linux.ARCH.SET_FS, addr);
141 const rc = linux.syscall2(.arch_prctl, linux.ARCH.SET_FS, addr);
141142 assert(rc == 0);
142143 },
143144 .aarch64, .aarch64_be => {
......@@ -148,7 +149,7 @@ pub fn setThreadPointer(addr: usize) void {
148149 );
149150 },
150151 .arm, .thumb => {
151 const rc = std.os.linux.syscall1(.set_tls, addr);
152 const rc = linux.syscall1(.set_tls, addr);
152153 assert(rc == 0);
153154 },
154155 .riscv64 => {
......@@ -159,7 +160,7 @@ pub fn setThreadPointer(addr: usize) void {
159160 );
160161 },
161162 .mips, .mipsel, .mips64, .mips64el => {
162 const rc = std.os.linux.syscall1(.set_thread_area, addr);
163 const rc = linux.syscall1(.set_thread_area, addr);
163164 assert(rc == 0);
164165 },
165166 .powerpc, .powerpcle => {
......@@ -320,14 +321,14 @@ pub fn initStaticTLS(phdrs: []elf.Phdr) void {
320321 break :blk main_thread_tls_buffer[0..tls_image.alloc_size];
321322 }
322323
323 const alloc_tls_area = os.mmap(
324 const alloc_tls_area = posix.mmap(
324325 null,
325326 tls_image.alloc_size + tls_image.alloc_align - 1,
326 os.PROT.READ | os.PROT.WRITE,
327 posix.PROT.READ | posix.PROT.WRITE,
327328 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
328329 -1,
329330 0,
330 ) catch os.abort();
331 ) catch posix.abort();
331332
332333 // Make sure the slice is correctly aligned.
333334 const begin_addr = @intFromPtr(alloc_tls_area.ptr);
lib/std/os/linux/vdso.zig+1-1
......@@ -5,7 +5,7 @@ const mem = std.mem;
55const maxInt = std.math.maxInt;
66
77pub fn lookup(vername: []const u8, name: []const u8) usize {
8 const vdso_addr = std.os.system.getauxval(std.elf.AT_SYSINFO_EHDR);
8 const vdso_addr = linux.getauxval(std.elf.AT_SYSINFO_EHDR);
99 if (vdso_addr == 0) return 0;
1010
1111 const eh = @as(*elf.Ehdr, @ptrFromInt(vdso_addr));
lib/std/os/linux/x86.zig+2-2
......@@ -3,8 +3,8 @@ const maxInt = std.math.maxInt;
33const linux = std.os.linux;
44const SYS = linux.SYS;
55const socklen_t = linux.socklen_t;
6const iovec = std.os.iovec;
7const iovec_const = std.os.iovec_const;
6const iovec = std.posix.iovec;
7const iovec_const = std.posix.iovec_const;
88const uid_t = linux.uid_t;
99const gid_t = linux.gid_t;
1010const pid_t = linux.pid_t;
lib/std/os/linux/x86_64.zig+2-2
......@@ -2,8 +2,8 @@ const std = @import("../../std.zig");
22const maxInt = std.math.maxInt;
33const linux = std.os.linux;
44const SYS = linux.SYS;
5const iovec = std.os.iovec;
6const iovec_const = std.os.iovec_const;
5const iovec = std.posix.iovec;
6const iovec_const = std.posix.iovec_const;
77
88const pid_t = linux.pid_t;
99const uid_t = linux.uid_t;
lib/std/os/plan9.zig+90-7
......@@ -11,13 +11,96 @@ pub const syscall_bits = switch (builtin.cpu.arch) {
1111 .x86_64 => @import("plan9/x86_64.zig"),
1212 else => @compileError("more plan9 syscall implementations (needs more inline asm in stage2"),
1313};
14pub const E = @import("plan9/errno.zig").E;
15/// Get the errno from a syscall return value, or 0 for no error.
16pub fn getErrno(r: usize) E {
17 const signed_r = @as(isize, @bitCast(r));
18 const int = if (signed_r > -4096 and signed_r < 0) -signed_r else 0;
19 return @as(E, @enumFromInt(int));
20}
14/// Ported from /sys/include/ape/errno.h
15pub const E = enum(u16) {
16 SUCCESS = 0,
17 DOM = 1000,
18 RANGE = 1001,
19 PLAN9 = 1002,
20
21 @"2BIG" = 1,
22 ACCES = 2,
23 AGAIN = 3,
24 // WOULDBLOCK = 3, // TODO errno.h has 2 names for 3
25 BADF = 4,
26 BUSY = 5,
27 CHILD = 6,
28 DEADLK = 7,
29 EXIST = 8,
30 FAULT = 9,
31 FBIG = 10,
32 INTR = 11,
33 INVAL = 12,
34 IO = 13,
35 ISDIR = 14,
36 MFILE = 15,
37 MLINK = 16,
38 NAMETOOLONG = 17,
39 NFILE = 18,
40 NODEV = 19,
41 NOENT = 20,
42 NOEXEC = 21,
43 NOLCK = 22,
44 NOMEM = 23,
45 NOSPC = 24,
46 NOSYS = 25,
47 NOTDIR = 26,
48 NOTEMPTY = 27,
49 NOTTY = 28,
50 NXIO = 29,
51 PERM = 30,
52 PIPE = 31,
53 ROFS = 32,
54 SPIPE = 33,
55 SRCH = 34,
56 XDEV = 35,
57
58 // bsd networking software
59 NOTSOCK = 36,
60 PROTONOSUPPORT = 37,
61 // PROTOTYPE = 37, // TODO errno.h has two names for 37
62 CONNREFUSED = 38,
63 AFNOSUPPORT = 39,
64 NOBUFS = 40,
65 OPNOTSUPP = 41,
66 ADDRINUSE = 42,
67 DESTADDRREQ = 43,
68 MSGSIZE = 44,
69 NOPROTOOPT = 45,
70 SOCKTNOSUPPORT = 46,
71 PFNOSUPPORT = 47,
72 ADDRNOTAVAIL = 48,
73 NETDOWN = 49,
74 NETUNREACH = 50,
75 NETRESET = 51,
76 CONNABORTED = 52,
77 ISCONN = 53,
78 NOTCONN = 54,
79 SHUTDOWN = 55,
80 TOOMANYREFS = 56,
81 TIMEDOUT = 57,
82 HOSTDOWN = 58,
83 HOSTUNREACH = 59,
84 GREG = 60,
85
86 // These added in 1003.1b-1993
87 CANCELED = 61,
88 INPROGRESS = 62,
89
90 // We just add these to be compatible with std.os, which uses them,
91 // They should never get used.
92 DQUOT,
93 CONNRESET,
94 OVERFLOW,
95 LOOP,
96 TXTBSY,
97
98 pub fn init(r: usize) E {
99 const signed_r: isize = @bitCast(r);
100 const int = if (signed_r > -4096 and signed_r < 0) -signed_r else 0;
101 return @enumFromInt(int);
102 }
103};
21104// The max bytes that can be in the errstr buff
22105pub const ERRMAX = 128;
23106var errstr_buf: [ERRMAX]u8 = undefined;
lib/std/os/plan9/errno.zig deleted-84
......@@ -1,84 +0,0 @@
1//! Ported from /sys/include/ape/errno.h
2pub const E = enum(u16) {
3 SUCCESS = 0,
4 DOM = 1000,
5 RANGE = 1001,
6 PLAN9 = 1002,
7
8 @"2BIG" = 1,
9 ACCES = 2,
10 AGAIN = 3,
11 // WOULDBLOCK = 3, // TODO errno.h has 2 names for 3
12 BADF = 4,
13 BUSY = 5,
14 CHILD = 6,
15 DEADLK = 7,
16 EXIST = 8,
17 FAULT = 9,
18 FBIG = 10,
19 INTR = 11,
20 INVAL = 12,
21 IO = 13,
22 ISDIR = 14,
23 MFILE = 15,
24 MLINK = 16,
25 NAMETOOLONG = 17,
26 NFILE = 18,
27 NODEV = 19,
28 NOENT = 20,
29 NOEXEC = 21,
30 NOLCK = 22,
31 NOMEM = 23,
32 NOSPC = 24,
33 NOSYS = 25,
34 NOTDIR = 26,
35 NOTEMPTY = 27,
36 NOTTY = 28,
37 NXIO = 29,
38 PERM = 30,
39 PIPE = 31,
40 ROFS = 32,
41 SPIPE = 33,
42 SRCH = 34,
43 XDEV = 35,
44
45 // bsd networking software
46 NOTSOCK = 36,
47 PROTONOSUPPORT = 37,
48 // PROTOTYPE = 37, // TODO errno.h has two names for 37
49 CONNREFUSED = 38,
50 AFNOSUPPORT = 39,
51 NOBUFS = 40,
52 OPNOTSUPP = 41,
53 ADDRINUSE = 42,
54 DESTADDRREQ = 43,
55 MSGSIZE = 44,
56 NOPROTOOPT = 45,
57 SOCKTNOSUPPORT = 46,
58 PFNOSUPPORT = 47,
59 ADDRNOTAVAIL = 48,
60 NETDOWN = 49,
61 NETUNREACH = 50,
62 NETRESET = 51,
63 CONNABORTED = 52,
64 ISCONN = 53,
65 NOTCONN = 54,
66 SHUTDOWN = 55,
67 TOOMANYREFS = 56,
68 TIMEDOUT = 57,
69 HOSTDOWN = 58,
70 HOSTUNREACH = 59,
71 GREG = 60,
72
73 // These added in 1003.1b-1993
74 CANCELED = 61,
75 INPROGRESS = 62,
76
77 // We just add these to be compatible with std.os, which uses them,
78 // They should never get used.
79 DQUOT,
80 CONNRESET,
81 OVERFLOW,
82 LOOP,
83 TXTBSY,
84};
lib/std/os/test.zig deleted-1286
......@@ -1,1286 +0,0 @@
1const std = @import("../std.zig");
2const os = std.os;
3const testing = std.testing;
4const expect = testing.expect;
5const expectEqual = testing.expectEqual;
6const expectError = testing.expectError;
7const io = std.io;
8const fs = std.fs;
9const mem = std.mem;
10const elf = std.elf;
11const File = std.fs.File;
12const Thread = std.Thread;
13
14const a = std.testing.allocator;
15
16const builtin = @import("builtin");
17const AtomicRmwOp = std.builtin.AtomicRmwOp;
18const AtomicOrder = std.builtin.AtomicOrder;
19const native_os = builtin.target.os.tag;
20const tmpDir = std.testing.tmpDir;
21const Dir = std.fs.Dir;
22const ArenaAllocator = std.heap.ArenaAllocator;
23
24test "chdir smoke test" {
25 if (native_os == .wasi) return error.SkipZigTest;
26
27 if (true) {
28 // https://github.com/ziglang/zig/issues/14968
29 return error.SkipZigTest;
30 }
31
32 // Get current working directory path
33 var old_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
34 const old_cwd = try os.getcwd(old_cwd_buf[0..]);
35
36 {
37 // Firstly, changing to itself should have no effect
38 try os.chdir(old_cwd);
39 var new_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
40 const new_cwd = try os.getcwd(new_cwd_buf[0..]);
41 try expect(mem.eql(u8, old_cwd, new_cwd));
42 }
43
44 // Next, change current working directory to one level above
45 if (native_os != .wasi) { // WASI does not support navigating outside of Preopens
46 const parent = fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute
47 try os.chdir(parent);
48
49 // Restore cwd because process may have other tests that do not tolerate chdir.
50 defer os.chdir(old_cwd) catch unreachable;
51
52 var new_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
53 const new_cwd = try os.getcwd(new_cwd_buf[0..]);
54 try expect(mem.eql(u8, parent, new_cwd));
55 }
56
57 // Next, change current working directory to a temp directory one level below
58 {
59 // Create a tmp directory
60 var tmp_dir_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
61 const tmp_dir_path = path: {
62 var allocator = std.heap.FixedBufferAllocator.init(&tmp_dir_buf);
63 break :path try fs.path.resolve(allocator.allocator(), &[_][]const u8{ old_cwd, "zig-test-tmp" });
64 };
65 var tmp_dir = try fs.cwd().makeOpenPath("zig-test-tmp", .{});
66
67 // Change current working directory to tmp directory
68 try os.chdir("zig-test-tmp");
69
70 var new_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
71 const new_cwd = try os.getcwd(new_cwd_buf[0..]);
72
73 // On Windows, fs.path.resolve returns an uppercase drive letter, but the drive letter returned by getcwd may be lowercase
74 var resolved_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
75 const resolved_cwd = path: {
76 var allocator = std.heap.FixedBufferAllocator.init(&resolved_cwd_buf);
77 break :path try fs.path.resolve(allocator.allocator(), &[_][]const u8{new_cwd});
78 };
79 try expect(mem.eql(u8, tmp_dir_path, resolved_cwd));
80
81 // Restore cwd because process may have other tests that do not tolerate chdir.
82 tmp_dir.close();
83 os.chdir(old_cwd) catch unreachable;
84 try fs.cwd().deleteDir("zig-test-tmp");
85 }
86}
87
88test "open smoke test" {
89 if (native_os == .wasi) return error.SkipZigTest;
90 if (native_os == .windows) return error.SkipZigTest;
91
92 // TODO verify file attributes using `fstat`
93
94 var tmp = tmpDir(.{});
95 defer tmp.cleanup();
96
97 // Get base abs path
98 var arena = ArenaAllocator.init(testing.allocator);
99 defer arena.deinit();
100 const allocator = arena.allocator();
101
102 const base_path = blk: {
103 const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
104 break :blk try fs.realpathAlloc(allocator, relative_path);
105 };
106
107 var file_path: []u8 = undefined;
108 var fd: os.fd_t = undefined;
109 const mode: os.mode_t = if (native_os == .windows) 0 else 0o666;
110
111 // Create some file using `open`.
112 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
113 fd = try os.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode);
114 os.close(fd);
115
116 // Try this again with the same flags. This op should fail with error.PathAlreadyExists.
117 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
118 try expectError(error.PathAlreadyExists, os.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode));
119
120 // Try opening without `EXCL` flag.
121 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
122 fd = try os.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true }, mode);
123 os.close(fd);
124
125 // Try opening as a directory which should fail.
126 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
127 try expectError(error.NotDir, os.open(file_path, .{ .ACCMODE = .RDWR, .DIRECTORY = true }, mode));
128
129 // Create some directory
130 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
131 try os.mkdir(file_path, mode);
132
133 // Open dir using `open`
134 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
135 fd = try os.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode);
136 os.close(fd);
137
138 // Try opening as file which should fail.
139 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
140 try expectError(error.IsDir, os.open(file_path, .{ .ACCMODE = .RDWR }, mode));
141}
142
143test "openat smoke test" {
144 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
145 if (native_os == .windows) return error.SkipZigTest;
146
147 // TODO verify file attributes using `fstatat`
148
149 var tmp = tmpDir(.{});
150 defer tmp.cleanup();
151
152 var fd: os.fd_t = undefined;
153 const mode: os.mode_t = if (native_os == .windows) 0 else 0o666;
154
155 // Create some file using `openat`.
156 fd = try os.openat(tmp.dir.fd, "some_file", os.CommonOpenFlags.lower(.{
157 .ACCMODE = .RDWR,
158 .CREAT = true,
159 .EXCL = true,
160 }), mode);
161 os.close(fd);
162
163 // Try this again with the same flags. This op should fail with error.PathAlreadyExists.
164 try expectError(error.PathAlreadyExists, os.openat(tmp.dir.fd, "some_file", os.CommonOpenFlags.lower(.{
165 .ACCMODE = .RDWR,
166 .CREAT = true,
167 .EXCL = true,
168 }), mode));
169
170 // Try opening without `EXCL` flag.
171 fd = try os.openat(tmp.dir.fd, "some_file", os.CommonOpenFlags.lower(.{
172 .ACCMODE = .RDWR,
173 .CREAT = true,
174 }), mode);
175 os.close(fd);
176
177 // Try opening as a directory which should fail.
178 try expectError(error.NotDir, os.openat(tmp.dir.fd, "some_file", os.CommonOpenFlags.lower(.{
179 .ACCMODE = .RDWR,
180 .DIRECTORY = true,
181 }), mode));
182
183 // Create some directory
184 try os.mkdirat(tmp.dir.fd, "some_dir", mode);
185
186 // Open dir using `open`
187 fd = try os.openat(tmp.dir.fd, "some_dir", os.CommonOpenFlags.lower(.{
188 .ACCMODE = .RDONLY,
189 .DIRECTORY = true,
190 }), mode);
191 os.close(fd);
192
193 // Try opening as file which should fail.
194 try expectError(error.IsDir, os.openat(tmp.dir.fd, "some_dir", os.CommonOpenFlags.lower(.{
195 .ACCMODE = .RDWR,
196 }), mode));
197}
198
199test "symlink with relative paths" {
200 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
201
202 if (true) {
203 // https://github.com/ziglang/zig/issues/14968
204 return error.SkipZigTest;
205 }
206 const cwd = fs.cwd();
207 cwd.deleteFile("file.txt") catch {};
208 cwd.deleteFile("symlinked") catch {};
209
210 // First, try relative paths in cwd
211 try cwd.writeFile("file.txt", "nonsense");
212
213 if (native_os == .windows) {
214 os.windows.CreateSymbolicLink(
215 cwd.fd,
216 &[_]u16{ 's', 'y', 'm', 'l', 'i', 'n', 'k', 'e', 'd' },
217 &[_:0]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' },
218 false,
219 ) catch |err| switch (err) {
220 // Symlink requires admin privileges on windows, so this test can legitimately fail.
221 error.AccessDenied => {
222 try cwd.deleteFile("file.txt");
223 try cwd.deleteFile("symlinked");
224 return error.SkipZigTest;
225 },
226 else => return err,
227 };
228 } else {
229 try os.symlink("file.txt", "symlinked");
230 }
231
232 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
233 const given = try os.readlink("symlinked", buffer[0..]);
234 try expect(mem.eql(u8, "file.txt", given));
235
236 try cwd.deleteFile("file.txt");
237 try cwd.deleteFile("symlinked");
238}
239
240test "readlink on Windows" {
241 if (native_os != .windows) return error.SkipZigTest;
242
243 try testReadlink("C:\\ProgramData", "C:\\Users\\All Users");
244 try testReadlink("C:\\Users\\Default", "C:\\Users\\Default User");
245 try testReadlink("C:\\Users", "C:\\Documents and Settings");
246}
247
248fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void {
249 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
250 const given = try os.readlink(symlink_path, buffer[0..]);
251 try expect(mem.eql(u8, target_path, given));
252}
253
254test "link with relative paths" {
255 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
256
257 switch (native_os) {
258 .wasi, .linux, .solaris, .illumos => {},
259 else => return error.SkipZigTest,
260 }
261 if (true) {
262 // https://github.com/ziglang/zig/issues/14968
263 return error.SkipZigTest;
264 }
265 var cwd = fs.cwd();
266
267 cwd.deleteFile("example.txt") catch {};
268 cwd.deleteFile("new.txt") catch {};
269
270 try cwd.writeFile("example.txt", "example");
271 try os.link("example.txt", "new.txt", 0);
272
273 const efd = try cwd.openFile("example.txt", .{});
274 defer efd.close();
275
276 const nfd = try cwd.openFile("new.txt", .{});
277 defer nfd.close();
278
279 {
280 const estat = try os.fstat(efd.handle);
281 const nstat = try os.fstat(nfd.handle);
282
283 try testing.expectEqual(estat.ino, nstat.ino);
284 try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink);
285 }
286
287 try os.unlink("new.txt");
288
289 {
290 const estat = try os.fstat(efd.handle);
291 try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink);
292 }
293
294 try cwd.deleteFile("example.txt");
295}
296
297test "linkat with different directories" {
298 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
299
300 switch (native_os) {
301 .wasi, .linux, .solaris, .illumos => {},
302 else => return error.SkipZigTest,
303 }
304 if (true) {
305 // https://github.com/ziglang/zig/issues/14968
306 return error.SkipZigTest;
307 }
308 var cwd = fs.cwd();
309 var tmp = tmpDir(.{});
310
311 cwd.deleteFile("example.txt") catch {};
312 tmp.dir.deleteFile("new.txt") catch {};
313
314 try cwd.writeFile("example.txt", "example");
315 try os.linkat(cwd.fd, "example.txt", tmp.dir.fd, "new.txt", 0);
316
317 const efd = try cwd.openFile("example.txt", .{});
318 defer efd.close();
319
320 const nfd = try tmp.dir.openFile("new.txt", .{});
321
322 {
323 defer nfd.close();
324 const estat = try os.fstat(efd.handle);
325 const nstat = try os.fstat(nfd.handle);
326
327 try testing.expectEqual(estat.ino, nstat.ino);
328 try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink);
329 }
330
331 try os.unlinkat(tmp.dir.fd, "new.txt", 0);
332
333 {
334 const estat = try os.fstat(efd.handle);
335 try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink);
336 }
337
338 try cwd.deleteFile("example.txt");
339}
340
341test "fstatat" {
342 // enable when `fstat` and `fstatat` are implemented on Windows
343 if (native_os == .windows) return error.SkipZigTest;
344
345 var tmp = tmpDir(.{});
346 defer tmp.cleanup();
347
348 // create dummy file
349 const contents = "nonsense";
350 try tmp.dir.writeFile("file.txt", contents);
351
352 // fetch file's info on the opened fd directly
353 const file = try tmp.dir.openFile("file.txt", .{});
354 const stat = try os.fstat(file.handle);
355 defer file.close();
356
357 // now repeat but using `fstatat` instead
358 const flags = if (native_os == .wasi) 0x0 else os.AT.SYMLINK_NOFOLLOW;
359 const statat = try os.fstatat(tmp.dir.fd, "file.txt", flags);
360 try expectEqual(stat, statat);
361}
362
363test "readlinkat" {
364 var tmp = tmpDir(.{});
365 defer tmp.cleanup();
366
367 // create file
368 try tmp.dir.writeFile("file.txt", "nonsense");
369
370 // create a symbolic link
371 if (native_os == .windows) {
372 os.windows.CreateSymbolicLink(
373 tmp.dir.fd,
374 &[_]u16{ 'l', 'i', 'n', 'k' },
375 &[_:0]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' },
376 false,
377 ) catch |err| switch (err) {
378 // Symlink requires admin privileges on windows, so this test can legitimately fail.
379 error.AccessDenied => return error.SkipZigTest,
380 else => return err,
381 };
382 } else {
383 try os.symlinkat("file.txt", tmp.dir.fd, "link");
384 }
385
386 // read the link
387 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
388 const read_link = try os.readlinkat(tmp.dir.fd, "link", buffer[0..]);
389 try expect(mem.eql(u8, "file.txt", read_link));
390}
391
392fn testThreadIdFn(thread_id: *Thread.Id) void {
393 thread_id.* = Thread.getCurrentId();
394}
395
396test "Thread.getCurrentId" {
397 if (builtin.single_threaded) return error.SkipZigTest;
398
399 var thread_current_id: Thread.Id = undefined;
400 const thread = try Thread.spawn(.{}, testThreadIdFn, .{&thread_current_id});
401 thread.join();
402 try expect(Thread.getCurrentId() != thread_current_id);
403}
404
405test "spawn threads" {
406 if (builtin.single_threaded) return error.SkipZigTest;
407
408 var shared_ctx: i32 = 1;
409
410 const thread1 = try Thread.spawn(.{}, start1, .{});
411 const thread2 = try Thread.spawn(.{}, start2, .{&shared_ctx});
412 const thread3 = try Thread.spawn(.{}, start2, .{&shared_ctx});
413 const thread4 = try Thread.spawn(.{}, start2, .{&shared_ctx});
414
415 thread1.join();
416 thread2.join();
417 thread3.join();
418 thread4.join();
419
420 try expect(shared_ctx == 4);
421}
422
423fn start1() u8 {
424 return 0;
425}
426
427fn start2(ctx: *i32) u8 {
428 _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.seq_cst);
429 return 0;
430}
431
432test "cpu count" {
433 if (native_os == .wasi) return error.SkipZigTest;
434
435 const cpu_count = try Thread.getCpuCount();
436 try expect(cpu_count >= 1);
437}
438
439test "thread local storage" {
440 if (builtin.single_threaded) return error.SkipZigTest;
441
442 const thread1 = try Thread.spawn(.{}, testTls, .{});
443 const thread2 = try Thread.spawn(.{}, testTls, .{});
444 try testTls();
445 thread1.join();
446 thread2.join();
447}
448
449threadlocal var x: i32 = 1234;
450fn testTls() !void {
451 if (x != 1234) return error.TlsBadStartValue;
452 x += 1;
453 if (x != 1235) return error.TlsBadEndValue;
454}
455
456test "getrandom" {
457 var buf_a: [50]u8 = undefined;
458 var buf_b: [50]u8 = undefined;
459 try os.getrandom(&buf_a);
460 try os.getrandom(&buf_b);
461 // If this test fails the chance is significantly higher that there is a bug than
462 // that two sets of 50 bytes were equal.
463 try expect(!mem.eql(u8, &buf_a, &buf_b));
464}
465
466test "getcwd" {
467 // at least call it so it gets compiled
468 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
469 _ = os.getcwd(&buf) catch undefined;
470}
471
472test "sigaltstack" {
473 if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;
474
475 var st: os.stack_t = undefined;
476 try os.sigaltstack(null, &st);
477 // Setting a stack size less than MINSIGSTKSZ returns ENOMEM
478 st.flags = 0;
479 st.size = 1;
480 try testing.expectError(error.SizeTooSmall, os.sigaltstack(&st, null));
481}
482
483// If the type is not available use void to avoid erroring out when `iter_fn` is
484// analyzed
485const dl_phdr_info = if (@hasDecl(os.system, "dl_phdr_info")) os.dl_phdr_info else anyopaque;
486
487const IterFnError = error{
488 MissingPtLoadSegment,
489 MissingLoad,
490 BadElfMagic,
491 FailedConsistencyCheck,
492};
493
494fn iter_fn(info: *dl_phdr_info, size: usize, counter: *usize) IterFnError!void {
495 _ = size;
496 // Count how many libraries are loaded
497 counter.* += @as(usize, 1);
498
499 // The image should contain at least a PT_LOAD segment
500 if (info.dlpi_phnum < 1) return error.MissingPtLoadSegment;
501
502 // Quick & dirty validation of the phdr pointers, make sure we're not
503 // pointing to some random gibberish
504 var i: usize = 0;
505 var found_load = false;
506 while (i < info.dlpi_phnum) : (i += 1) {
507 const phdr = info.dlpi_phdr[i];
508
509 if (phdr.p_type != elf.PT_LOAD) continue;
510
511 const reloc_addr = info.dlpi_addr + phdr.p_vaddr;
512 // Find the ELF header
513 const elf_header = @as(*elf.Ehdr, @ptrFromInt(reloc_addr - phdr.p_offset));
514 // Validate the magic
515 if (!mem.eql(u8, elf_header.e_ident[0..4], elf.MAGIC)) return error.BadElfMagic;
516 // Consistency check
517 if (elf_header.e_phnum != info.dlpi_phnum) return error.FailedConsistencyCheck;
518
519 found_load = true;
520 break;
521 }
522
523 if (!found_load) return error.MissingLoad;
524}
525
526test "dl_iterate_phdr" {
527 if (builtin.object_format != .elf) return error.SkipZigTest;
528
529 var counter: usize = 0;
530 try os.dl_iterate_phdr(&counter, IterFnError, iter_fn);
531 try expect(counter != 0);
532}
533
534test "gethostname" {
535 if (native_os == .windows or native_os == .wasi)
536 return error.SkipZigTest;
537
538 var buf: [os.HOST_NAME_MAX]u8 = undefined;
539 const hostname = try os.gethostname(&buf);
540 try expect(hostname.len != 0);
541}
542
543test "pipe" {
544 if (native_os == .windows or native_os == .wasi)
545 return error.SkipZigTest;
546
547 const fds = try os.pipe();
548 try expect((try os.write(fds[1], "hello")) == 5);
549 var buf: [16]u8 = undefined;
550 try expect((try os.read(fds[0], buf[0..])) == 5);
551 try testing.expectEqualSlices(u8, buf[0..5], "hello");
552 os.close(fds[1]);
553 os.close(fds[0]);
554}
555
556test "argsAlloc" {
557 const args = try std.process.argsAlloc(std.testing.allocator);
558 std.process.argsFree(std.testing.allocator, args);
559}
560
561test "memfd_create" {
562 // memfd_create is only supported by linux and freebsd.
563 switch (native_os) {
564 .linux => {},
565 .freebsd => {
566 if (comptime builtin.os.version_range.semver.max.order(.{ .major = 13, .minor = 0, .patch = 0 }) == .lt)
567 return error.SkipZigTest;
568 },
569 else => return error.SkipZigTest,
570 }
571
572 const fd = os.memfd_create("test", 0) catch |err| switch (err) {
573 // Related: https://github.com/ziglang/zig/issues/4019
574 error.SystemOutdated => return error.SkipZigTest,
575 else => |e| return e,
576 };
577 defer os.close(fd);
578 try expect((try os.write(fd, "test")) == 4);
579 try os.lseek_SET(fd, 0);
580
581 var buf: [10]u8 = undefined;
582 const bytes_read = try os.read(fd, &buf);
583 try expect(bytes_read == 4);
584 try expect(mem.eql(u8, buf[0..4], "test"));
585}
586
587test "mmap" {
588 if (native_os == .windows or native_os == .wasi)
589 return error.SkipZigTest;
590
591 var tmp = tmpDir(.{});
592 defer tmp.cleanup();
593
594 // Simple mmap() call with non page-aligned size
595 {
596 const data = try os.mmap(
597 null,
598 1234,
599 os.PROT.READ | os.PROT.WRITE,
600 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
601 -1,
602 0,
603 );
604 defer os.munmap(data);
605
606 try testing.expectEqual(@as(usize, 1234), data.len);
607
608 // By definition the data returned by mmap is zero-filled
609 try testing.expect(mem.eql(u8, data, &[_]u8{0x00} ** 1234));
610
611 // Make sure the memory is writeable as requested
612 @memset(data, 0x55);
613 try testing.expect(mem.eql(u8, data, &[_]u8{0x55} ** 1234));
614 }
615
616 const test_out_file = "os_tmp_test";
617 // Must be a multiple of 4096 so that the test works with mmap2
618 const alloc_size = 8 * 4096;
619
620 // Create a file used for testing mmap() calls with a file descriptor
621 {
622 const file = try tmp.dir.createFile(test_out_file, .{});
623 defer file.close();
624
625 const stream = file.writer();
626
627 var i: u32 = 0;
628 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
629 try stream.writeInt(u32, i, .little);
630 }
631 }
632
633 // Map the whole file
634 {
635 const file = try tmp.dir.openFile(test_out_file, .{});
636 defer file.close();
637
638 const data = try os.mmap(
639 null,
640 alloc_size,
641 os.PROT.READ,
642 .{ .TYPE = .PRIVATE },
643 file.handle,
644 0,
645 );
646 defer os.munmap(data);
647
648 var mem_stream = io.fixedBufferStream(data);
649 const stream = mem_stream.reader();
650
651 var i: u32 = 0;
652 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
653 try testing.expectEqual(i, try stream.readInt(u32, .little));
654 }
655 }
656
657 // Map the upper half of the file
658 {
659 const file = try tmp.dir.openFile(test_out_file, .{});
660 defer file.close();
661
662 const data = try os.mmap(
663 null,
664 alloc_size / 2,
665 os.PROT.READ,
666 .{ .TYPE = .PRIVATE },
667 file.handle,
668 alloc_size / 2,
669 );
670 defer os.munmap(data);
671
672 var mem_stream = io.fixedBufferStream(data);
673 const stream = mem_stream.reader();
674
675 var i: u32 = alloc_size / 2 / @sizeOf(u32);
676 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
677 try testing.expectEqual(i, try stream.readInt(u32, .little));
678 }
679 }
680
681 try tmp.dir.deleteFile(test_out_file);
682}
683
684test "getenv" {
685 if (native_os == .wasi and !builtin.link_libc) {
686 // std.os.getenv is not supported on WASI due to the need of allocation
687 return error.SkipZigTest;
688 }
689
690 if (native_os == .windows) {
691 try expect(os.getenvW(&[_:0]u16{ 'B', 'O', 'G', 'U', 'S', 0x11, 0x22, 0x33, 0x44, 0x55 }) == null);
692 } else {
693 try expect(os.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null);
694 }
695}
696
697test "fcntl" {
698 if (native_os == .windows or native_os == .wasi)
699 return error.SkipZigTest;
700
701 var tmp = tmpDir(.{});
702 defer tmp.cleanup();
703
704 const test_out_file = "os_tmp_test";
705
706 const file = try tmp.dir.createFile(test_out_file, .{});
707 defer {
708 file.close();
709 tmp.dir.deleteFile(test_out_file) catch {};
710 }
711
712 // Note: The test assumes createFile opens the file with CLOEXEC
713 {
714 const flags = try os.fcntl(file.handle, os.F.GETFD, 0);
715 try expect((flags & os.FD_CLOEXEC) != 0);
716 }
717 {
718 _ = try os.fcntl(file.handle, os.F.SETFD, 0);
719 const flags = try os.fcntl(file.handle, os.F.GETFD, 0);
720 try expect((flags & os.FD_CLOEXEC) == 0);
721 }
722 {
723 _ = try os.fcntl(file.handle, os.F.SETFD, os.FD_CLOEXEC);
724 const flags = try os.fcntl(file.handle, os.F.GETFD, 0);
725 try expect((flags & os.FD_CLOEXEC) != 0);
726 }
727}
728
729test "signalfd" {
730 switch (native_os) {
731 .linux, .solaris, .illumos => {},
732 else => return error.SkipZigTest,
733 }
734 _ = &os.signalfd;
735}
736
737test "sync" {
738 if (native_os != .linux)
739 return error.SkipZigTest;
740
741 var tmp = tmpDir(.{});
742 defer tmp.cleanup();
743
744 const test_out_file = "os_tmp_test";
745 const file = try tmp.dir.createFile(test_out_file, .{});
746 defer {
747 file.close();
748 tmp.dir.deleteFile(test_out_file) catch {};
749 }
750
751 os.sync();
752 try os.syncfs(file.handle);
753}
754
755test "fsync" {
756 switch (native_os) {
757 .linux, .windows, .solaris, .illumos => {},
758 else => return error.SkipZigTest,
759 }
760
761 var tmp = tmpDir(.{});
762 defer tmp.cleanup();
763
764 const test_out_file = "os_tmp_test";
765 const file = try tmp.dir.createFile(test_out_file, .{});
766 defer {
767 file.close();
768 tmp.dir.deleteFile(test_out_file) catch {};
769 }
770
771 try os.fsync(file.handle);
772 try os.fdatasync(file.handle);
773}
774
775test "getrlimit and setrlimit" {
776 if (!@hasDecl(os.system, "rlimit")) {
777 return error.SkipZigTest;
778 }
779
780 inline for (std.meta.fields(os.rlimit_resource)) |field| {
781 const resource = @as(os.rlimit_resource, @enumFromInt(field.value));
782 const limit = try os.getrlimit(resource);
783
784 // XNU kernel does not support RLIMIT_STACK if a custom stack is active,
785 // which looks to always be the case. EINVAL is returned.
786 // See https://github.com/apple-oss-distributions/xnu/blob/5e3eaea39dcf651e66cb99ba7d70e32cc4a99587/bsd/kern/kern_resource.c#L1173
787 if (builtin.os.tag.isDarwin() and resource == .STACK) {
788 continue;
789 }
790
791 // On 32 bit MIPS musl includes a fix which changes limits greater than -1UL/2 to RLIM_INFINITY.
792 // See http://git.musl-libc.org/cgit/musl/commit/src/misc/getrlimit.c?id=8258014fd1e34e942a549c88c7e022a00445c352
793 //
794 // This happens for example if RLIMIT_MEMLOCK is bigger than ~2GiB.
795 // In that case the following the limit would be RLIM_INFINITY and the following setrlimit fails with EPERM.
796 if (comptime builtin.cpu.arch.isMIPS() and builtin.link_libc) {
797 if (limit.cur != os.linux.RLIM.INFINITY) {
798 try os.setrlimit(resource, limit);
799 }
800 } else {
801 try os.setrlimit(resource, limit);
802 }
803 }
804}
805
806test "shutdown socket" {
807 if (native_os == .wasi)
808 return error.SkipZigTest;
809 if (native_os == .windows) {
810 _ = try os.windows.WSAStartup(2, 2);
811 }
812 defer {
813 if (native_os == .windows) {
814 os.windows.WSACleanup() catch unreachable;
815 }
816 }
817 const sock = try os.socket(os.AF.INET, os.SOCK.STREAM, 0);
818 os.shutdown(sock, .both) catch |err| switch (err) {
819 error.SocketNotConnected => {},
820 else => |e| return e,
821 };
822 std.net.Stream.close(.{ .handle = sock });
823}
824
825test "sigaction" {
826 if (native_os == .wasi or native_os == .windows)
827 return error.SkipZigTest;
828
829 // https://github.com/ziglang/zig/issues/7427
830 if (native_os == .linux and builtin.target.cpu.arch == .x86)
831 return error.SkipZigTest;
832
833 // https://github.com/ziglang/zig/issues/15381
834 if (native_os == .macos and builtin.target.cpu.arch == .x86_64) {
835 return error.SkipZigTest;
836 }
837
838 const S = struct {
839 var handler_called_count: u32 = 0;
840
841 fn handler(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const anyopaque) callconv(.C) void {
842 _ = ctx_ptr;
843 // Check that we received the correct signal.
844 switch (native_os) {
845 .netbsd => {
846 if (sig == os.SIG.USR1 and sig == info.info.signo)
847 handler_called_count += 1;
848 },
849 else => {
850 if (sig == os.SIG.USR1 and sig == info.signo)
851 handler_called_count += 1;
852 },
853 }
854 }
855 };
856
857 var sa = os.Sigaction{
858 .handler = .{ .sigaction = &S.handler },
859 .mask = os.empty_sigset,
860 .flags = os.SA.SIGINFO | os.SA.RESETHAND,
861 };
862 var old_sa: os.Sigaction = undefined;
863
864 // Install the new signal handler.
865 try os.sigaction(os.SIG.USR1, &sa, null);
866
867 // Check that we can read it back correctly.
868 try os.sigaction(os.SIG.USR1, null, &old_sa);
869 try testing.expectEqual(&S.handler, old_sa.handler.sigaction.?);
870 try testing.expect((old_sa.flags & os.SA.SIGINFO) != 0);
871
872 // Invoke the handler.
873 try os.raise(os.SIG.USR1);
874 try testing.expect(S.handler_called_count == 1);
875
876 // Check if passing RESETHAND correctly reset the handler to SIG_DFL
877 try os.sigaction(os.SIG.USR1, null, &old_sa);
878 try testing.expectEqual(os.SIG.DFL, old_sa.handler.handler);
879
880 // Reinstall the signal w/o RESETHAND and re-raise
881 sa.flags = os.SA.SIGINFO;
882 try os.sigaction(os.SIG.USR1, &sa, null);
883 try os.raise(os.SIG.USR1);
884 try testing.expect(S.handler_called_count == 2);
885
886 // Now set the signal to ignored
887 sa.handler = .{ .handler = os.SIG.IGN };
888 sa.flags = 0;
889 try os.sigaction(os.SIG.USR1, &sa, null);
890
891 // Re-raise to ensure handler is actually ignored
892 try os.raise(os.SIG.USR1);
893 try testing.expect(S.handler_called_count == 2);
894
895 // Ensure that ignored state is returned when querying
896 try os.sigaction(os.SIG.USR1, null, &old_sa);
897 try testing.expectEqual(os.SIG.IGN, old_sa.handler.handler.?);
898}
899
900test "dup & dup2" {
901 switch (native_os) {
902 .linux, .solaris, .illumos => {},
903 else => return error.SkipZigTest,
904 }
905
906 var tmp = tmpDir(.{});
907 defer tmp.cleanup();
908
909 {
910 var file = try tmp.dir.createFile("os_dup_test", .{});
911 defer file.close();
912
913 var duped = std.fs.File{ .handle = try os.dup(file.handle) };
914 defer duped.close();
915 try duped.writeAll("dup");
916
917 // Tests aren't run in parallel so using the next fd shouldn't be an issue.
918 const new_fd = duped.handle + 1;
919 try os.dup2(file.handle, new_fd);
920 var dup2ed = std.fs.File{ .handle = new_fd };
921 defer dup2ed.close();
922 try dup2ed.writeAll("dup2");
923 }
924
925 var file = try tmp.dir.openFile("os_dup_test", .{});
926 defer file.close();
927
928 var buf: [7]u8 = undefined;
929 try testing.expectEqualStrings("dupdup2", buf[0..try file.readAll(&buf)]);
930}
931
932test "writev longer than IOV_MAX" {
933 if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;
934
935 var tmp = tmpDir(.{});
936 defer tmp.cleanup();
937
938 var file = try tmp.dir.createFile("pwritev", .{});
939 defer file.close();
940
941 const iovecs = [_]os.iovec_const{.{ .iov_base = "a", .iov_len = 1 }} ** (os.IOV_MAX + 1);
942 const amt = try file.writev(&iovecs);
943 try testing.expectEqual(@as(usize, os.IOV_MAX), amt);
944}
945
946test "POSIX file locking with fcntl" {
947 if (native_os == .windows or native_os == .wasi) {
948 // Not POSIX.
949 return error.SkipZigTest;
950 }
951
952 if (true) {
953 // https://github.com/ziglang/zig/issues/11074
954 return error.SkipZigTest;
955 }
956
957 var tmp = std.testing.tmpDir(.{});
958 defer tmp.cleanup();
959
960 // Create a temporary lock file
961 var file = try tmp.dir.createFile("lock", .{ .read = true });
962 defer file.close();
963 try file.setEndPos(2);
964 const fd = file.handle;
965
966 // Place an exclusive lock on the first byte, and a shared lock on the second byte:
967 var struct_flock = std.mem.zeroInit(os.Flock, .{ .type = os.F.WRLCK });
968 _ = try os.fcntl(fd, os.F.SETLK, @intFromPtr(&struct_flock));
969 struct_flock.start = 1;
970 struct_flock.type = os.F.RDLCK;
971 _ = try os.fcntl(fd, os.F.SETLK, @intFromPtr(&struct_flock));
972
973 // Check the locks in a child process:
974 const pid = try os.fork();
975 if (pid == 0) {
976 // child expects be denied the exclusive lock:
977 struct_flock.start = 0;
978 struct_flock.type = os.F.WRLCK;
979 try expectError(error.Locked, os.fcntl(fd, os.F.SETLK, @intFromPtr(&struct_flock)));
980 // child expects to get the shared lock:
981 struct_flock.start = 1;
982 struct_flock.type = os.F.RDLCK;
983 _ = try os.fcntl(fd, os.F.SETLK, @intFromPtr(&struct_flock));
984 // child waits for the exclusive lock in order to test deadlock:
985 struct_flock.start = 0;
986 struct_flock.type = os.F.WRLCK;
987 _ = try os.fcntl(fd, os.F.SETLKW, @intFromPtr(&struct_flock));
988 // child exits without continuing:
989 os.exit(0);
990 } else {
991 // parent waits for child to get shared lock:
992 std.time.sleep(1 * std.time.ns_per_ms);
993 // parent expects deadlock when attempting to upgrade the shared lock to exclusive:
994 struct_flock.start = 1;
995 struct_flock.type = os.F.WRLCK;
996 try expectError(error.DeadLock, os.fcntl(fd, os.F.SETLKW, @intFromPtr(&struct_flock)));
997 // parent releases exclusive lock:
998 struct_flock.start = 0;
999 struct_flock.type = os.F.UNLCK;
1000 _ = try os.fcntl(fd, os.F.SETLK, @intFromPtr(&struct_flock));
1001 // parent releases shared lock:
1002 struct_flock.start = 1;
1003 struct_flock.type = os.F.UNLCK;
1004 _ = try os.fcntl(fd, os.F.SETLK, @intFromPtr(&struct_flock));
1005 // parent waits for child:
1006 const result = os.waitpid(pid, 0);
1007 try expect(result.status == 0 * 256);
1008 }
1009}
1010
1011test "rename smoke test" {
1012 if (native_os == .wasi) return error.SkipZigTest;
1013 if (native_os == .windows) return error.SkipZigTest;
1014
1015 var tmp = tmpDir(.{});
1016 defer tmp.cleanup();
1017
1018 // Get base abs path
1019 var arena = ArenaAllocator.init(testing.allocator);
1020 defer arena.deinit();
1021 const allocator = arena.allocator();
1022
1023 const base_path = blk: {
1024 const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
1025 break :blk try fs.realpathAlloc(allocator, relative_path);
1026 };
1027
1028 var file_path: []u8 = undefined;
1029 var fd: os.fd_t = undefined;
1030 const mode: os.mode_t = if (native_os == .windows) 0 else 0o666;
1031
1032 // Create some file using `open`.
1033 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1034 fd = try os.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode);
1035 os.close(fd);
1036
1037 // Rename the file
1038 var new_file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_file" });
1039 try os.rename(file_path, new_file_path);
1040
1041 // Try opening renamed file
1042 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_file" });
1043 fd = try os.open(file_path, .{ .ACCMODE = .RDWR }, mode);
1044 os.close(fd);
1045
1046 // Try opening original file - should fail with error.FileNotFound
1047 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1048 try expectError(error.FileNotFound, os.open(file_path, .{ .ACCMODE = .RDWR }, mode));
1049
1050 // Create some directory
1051 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
1052 try os.mkdir(file_path, mode);
1053
1054 // Rename the directory
1055 new_file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_dir" });
1056 try os.rename(file_path, new_file_path);
1057
1058 // Try opening renamed directory
1059 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_dir" });
1060 fd = try os.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode);
1061 os.close(fd);
1062
1063 // Try opening original directory - should fail with error.FileNotFound
1064 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
1065 try expectError(error.FileNotFound, os.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode));
1066}
1067
1068test "access smoke test" {
1069 if (native_os == .wasi) return error.SkipZigTest;
1070 if (native_os == .windows) return error.SkipZigTest;
1071
1072 var tmp = tmpDir(.{});
1073 defer tmp.cleanup();
1074
1075 // Get base abs path
1076 var arena = ArenaAllocator.init(testing.allocator);
1077 defer arena.deinit();
1078 const allocator = arena.allocator();
1079
1080 const base_path = blk: {
1081 const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
1082 break :blk try fs.realpathAlloc(allocator, relative_path);
1083 };
1084
1085 var file_path: []u8 = undefined;
1086 var fd: os.fd_t = undefined;
1087 const mode: os.mode_t = if (native_os == .windows) 0 else 0o666;
1088
1089 // Create some file using `open`.
1090 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1091 fd = try os.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode);
1092 os.close(fd);
1093
1094 // Try to access() the file
1095 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1096 if (builtin.os.tag == .windows) {
1097 try os.access(file_path, os.F_OK);
1098 } else {
1099 try os.access(file_path, os.F_OK | os.W_OK | os.R_OK);
1100 }
1101
1102 // Try to access() a non-existent file - should fail with error.FileNotFound
1103 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_file" });
1104 try expectError(error.FileNotFound, os.access(file_path, os.F_OK));
1105
1106 // Create some directory
1107 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
1108 try os.mkdir(file_path, mode);
1109
1110 // Try to access() the directory
1111 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
1112 try os.access(file_path, os.F_OK);
1113}
1114
1115test "timerfd" {
1116 if (native_os != .linux) return error.SkipZigTest;
1117
1118 const linux = os.linux;
1119 const tfd = try os.timerfd_create(linux.CLOCK.MONOTONIC, .{ .CLOEXEC = true });
1120 defer os.close(tfd);
1121
1122 // Fire event 10_000_000ns = 10ms after the os.timerfd_settime call.
1123 var sit: linux.itimerspec = .{ .it_interval = .{ .tv_sec = 0, .tv_nsec = 0 }, .it_value = .{ .tv_sec = 0, .tv_nsec = 10 * (1000 * 1000) } };
1124 try os.timerfd_settime(tfd, .{}, &sit, null);
1125
1126 var fds: [1]os.pollfd = .{.{ .fd = tfd, .events = os.linux.POLL.IN, .revents = 0 }};
1127 try expectEqual(@as(usize, 1), try os.poll(&fds, -1)); // -1 => infinite waiting
1128
1129 const git = try os.timerfd_gettime(tfd);
1130 const expect_disarmed_timer: linux.itimerspec = .{ .it_interval = .{ .tv_sec = 0, .tv_nsec = 0 }, .it_value = .{ .tv_sec = 0, .tv_nsec = 0 } };
1131 try expectEqual(expect_disarmed_timer, git);
1132}
1133
1134test "isatty" {
1135 var tmp = tmpDir(.{});
1136 defer tmp.cleanup();
1137
1138 var file = try tmp.dir.createFile("foo", .{});
1139 defer file.close();
1140
1141 try expectEqual(os.isatty(file.handle), false);
1142}
1143
1144test "read with empty buffer" {
1145 if (native_os == .wasi) return error.SkipZigTest;
1146
1147 var tmp = tmpDir(.{});
1148 defer tmp.cleanup();
1149
1150 var arena = ArenaAllocator.init(testing.allocator);
1151 defer arena.deinit();
1152 const allocator = arena.allocator();
1153
1154 // Get base abs path
1155 const base_path = blk: {
1156 const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
1157 break :blk try fs.realpathAlloc(allocator, relative_path);
1158 };
1159
1160 const file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1161 var file = try fs.cwd().createFile(file_path, .{ .read = true });
1162 defer file.close();
1163
1164 const bytes = try allocator.alloc(u8, 0);
1165
1166 _ = try os.read(file.handle, bytes);
1167}
1168
1169test "pread with empty buffer" {
1170 if (native_os == .wasi) return error.SkipZigTest;
1171
1172 var tmp = tmpDir(.{});
1173 defer tmp.cleanup();
1174
1175 var arena = ArenaAllocator.init(testing.allocator);
1176 defer arena.deinit();
1177 const allocator = arena.allocator();
1178
1179 // Get base abs path
1180 const base_path = blk: {
1181 const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
1182 break :blk try fs.realpathAlloc(allocator, relative_path);
1183 };
1184
1185 const file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1186 var file = try fs.cwd().createFile(file_path, .{ .read = true });
1187 defer file.close();
1188
1189 const bytes = try allocator.alloc(u8, 0);
1190
1191 _ = try os.pread(file.handle, bytes, 0);
1192}
1193
1194test "write with empty buffer" {
1195 if (native_os == .wasi) return error.SkipZigTest;
1196
1197 var tmp = tmpDir(.{});
1198 defer tmp.cleanup();
1199
1200 var arena = ArenaAllocator.init(testing.allocator);
1201 defer arena.deinit();
1202 const allocator = arena.allocator();
1203
1204 // Get base abs path
1205 const base_path = blk: {
1206 const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
1207 break :blk try fs.realpathAlloc(allocator, relative_path);
1208 };
1209
1210 const file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1211 var file = try fs.cwd().createFile(file_path, .{});
1212 defer file.close();
1213
1214 const bytes = try allocator.alloc(u8, 0);
1215
1216 _ = try os.write(file.handle, bytes);
1217}
1218
1219test "pwrite with empty buffer" {
1220 if (native_os == .wasi) return error.SkipZigTest;
1221
1222 var tmp = tmpDir(.{});
1223 defer tmp.cleanup();
1224
1225 var arena = ArenaAllocator.init(testing.allocator);
1226 defer arena.deinit();
1227 const allocator = arena.allocator();
1228
1229 // Get base abs path
1230 const base_path = blk: {
1231 const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
1232 break :blk try fs.realpathAlloc(allocator, relative_path);
1233 };
1234
1235 const file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1236 var file = try fs.cwd().createFile(file_path, .{});
1237 defer file.close();
1238
1239 const bytes = try allocator.alloc(u8, 0);
1240
1241 _ = try os.pwrite(file.handle, bytes, 0);
1242}
1243
1244fn expectMode(dir: os.fd_t, file: []const u8, mode: os.mode_t) !void {
1245 const st = try os.fstatat(dir, file, os.AT.SYMLINK_NOFOLLOW);
1246 try expectEqual(mode, st.mode & 0b111_111_111);
1247}
1248
1249test "fchmodat smoke test" {
1250 if (!std.fs.has_executable_bit) return error.SkipZigTest;
1251
1252 var tmp = tmpDir(.{});
1253 defer tmp.cleanup();
1254
1255 try expectError(error.FileNotFound, os.fchmodat(tmp.dir.fd, "regfile", 0o666, 0));
1256 const fd = try os.openat(
1257 tmp.dir.fd,
1258 "regfile",
1259 .{ .ACCMODE = .WRONLY, .CREAT = true, .EXCL = true, .TRUNC = true },
1260 0o644,
1261 );
1262 os.close(fd);
1263 try os.symlinkat("regfile", tmp.dir.fd, "symlink");
1264 const sym_mode = blk: {
1265 const st = try os.fstatat(tmp.dir.fd, "symlink", os.AT.SYMLINK_NOFOLLOW);
1266 break :blk st.mode & 0b111_111_111;
1267 };
1268
1269 try os.fchmodat(tmp.dir.fd, "regfile", 0o640, 0);
1270 try expectMode(tmp.dir.fd, "regfile", 0o640);
1271 try os.fchmodat(tmp.dir.fd, "regfile", 0o600, os.AT.SYMLINK_NOFOLLOW);
1272 try expectMode(tmp.dir.fd, "regfile", 0o600);
1273
1274 try os.fchmodat(tmp.dir.fd, "symlink", 0o640, 0);
1275 try expectMode(tmp.dir.fd, "regfile", 0o640);
1276 try expectMode(tmp.dir.fd, "symlink", sym_mode);
1277
1278 var test_link = true;
1279 os.fchmodat(tmp.dir.fd, "symlink", 0o600, os.AT.SYMLINK_NOFOLLOW) catch |err| switch (err) {
1280 error.OperationNotSupported => test_link = false,
1281 else => |e| return e,
1282 };
1283 if (test_link)
1284 try expectMode(tmp.dir.fd, "symlink", 0o600);
1285 try expectMode(tmp.dir.fd, "regfile", 0o640);
1286}
lib/std/os/wasi.zig+2-2
......@@ -17,8 +17,8 @@ comptime {
1717 // assert(@alignOf(u64) == 8);
1818}
1919
20pub const iovec_t = std.os.iovec;
21pub const ciovec_t = std.os.iovec_const;
20pub const iovec_t = std.posix.iovec;
21pub const ciovec_t = std.posix.iovec_const;
2222
2323pub extern "wasi_snapshot_preview1" fn args_get(argv: [*][*:0]u8, argv_buf: [*]u8) errno_t;
2424pub extern "wasi_snapshot_preview1" fn args_sizes_get(argc: *usize, argv_buf_size: *usize) errno_t;
lib/std/os/windows.zig+11-10
......@@ -11,6 +11,7 @@ const assert = std.debug.assert;
1111const math = std.math;
1212const maxInt = std.math.maxInt;
1313const native_arch = builtin.cpu.arch;
14const UnexpectedError = std.posix.UnexpectedError;
1415
1516test {
1617 if (builtin.os.tag == .windows) {
......@@ -547,7 +548,7 @@ pub const GetQueuedCompletionStatusError = error{
547548 Cancelled,
548549 EOF,
549550 Timeout,
550} || std.os.UnexpectedError;
551} || UnexpectedError;
551552
552553pub fn GetQueuedCompletionStatusEx(
553554 completion_port: HANDLE,
......@@ -1701,7 +1702,7 @@ pub fn VirtualProtectEx(handle: HANDLE, addr: ?LPVOID, size: SIZE_T, new_prot: D
17011702 .SUCCESS => return old_prot,
17021703 .INVALID_ADDRESS => return error.InvalidAddress,
17031704 // TODO: map errors
1704 else => |rc| return std.os.windows.unexpectedStatus(rc),
1705 else => |rc| return unexpectedStatus(rc),
17051706 }
17061707}
17071708
......@@ -1946,7 +1947,7 @@ pub fn SetFileTime(
19461947pub const LockFileError = error{
19471948 SystemResources,
19481949 WouldBlock,
1949} || std.os.UnexpectedError;
1950} || UnexpectedError;
19501951
19511952pub fn LockFile(
19521953 FileHandle: HANDLE,
......@@ -1983,7 +1984,7 @@ pub fn LockFile(
19831984
19841985pub const UnlockFileError = error{
19851986 RangeNotLocked,
1986} || std.os.UnexpectedError;
1987} || UnexpectedError;
19871988
19881989pub fn UnlockFile(
19891990 FileHandle: HANDLE,
......@@ -2672,8 +2673,8 @@ pub fn loadWinsockExtensionFunction(comptime T: type, sock: ws2_32.SOCKET, guid:
26722673
26732674/// Call this when you made a windows DLL call or something that does SetLastError
26742675/// and you get an unexpected error.
2675pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError {
2676 if (std.os.unexpected_error_tracing) {
2676pub fn unexpectedError(err: Win32Error) UnexpectedError {
2677 if (std.posix.unexpected_error_tracing) {
26772678 // 614 is the length of the longest windows error description
26782679 var buf_wstr: [614]WCHAR = undefined;
26792680 const len = kernel32.FormatMessageW(
......@@ -2694,14 +2695,14 @@ pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError {
26942695 return error.Unexpected;
26952696}
26962697
2697pub fn unexpectedWSAError(err: ws2_32.WinsockError) std.os.UnexpectedError {
2698pub fn unexpectedWSAError(err: ws2_32.WinsockError) UnexpectedError {
26982699 return unexpectedError(@as(Win32Error, @enumFromInt(@intFromEnum(err))));
26992700}
27002701
27012702/// Call this when you made a windows NtDll call
27022703/// and you get an unexpected status.
2703pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError {
2704 if (std.os.unexpected_error_tracing) {
2704pub fn unexpectedStatus(status: NTSTATUS) UnexpectedError {
2705 if (std.posix.unexpected_error_tracing) {
27052706 std.debug.print("error.Unexpected NTSTATUS=0x{x}\n", .{@intFromEnum(status)});
27062707 std.debug.dumpCurrentStackTrace(@returnAddress());
27072708 }
......@@ -4246,7 +4247,7 @@ pub const KNONVOLATILE_CONTEXT_POINTERS = switch (native_arch) {
42464247
42474248pub const EXCEPTION_POINTERS = extern struct {
42484249 ExceptionRecord: *EXCEPTION_RECORD,
4249 ContextRecord: *std.os.windows.CONTEXT,
4250 ContextRecord: *CONTEXT,
42504251};
42514252
42524253pub const VECTORED_EXCEPTION_HANDLER = *const fn (ExceptionInfo: *EXCEPTION_POINTERS) callconv(WINAPI) c_long;
lib/std/os/windows/test.zig+2-2
......@@ -245,7 +245,7 @@ test "loadWinsockExtensionFunction" {
245245 const LPFN_CONNECTEX = *const fn (
246246 Socket: windows.ws2_32.SOCKET,
247247 SockAddr: *const windows.ws2_32.sockaddr,
248 SockLen: std.os.socklen_t,
248 SockLen: std.posix.socklen_t,
249249 SendBuf: ?*const anyopaque,
250250 SendBufLen: windows.DWORD,
251251 BytesSent: *windows.DWORD,
......@@ -254,7 +254,7 @@ test "loadWinsockExtensionFunction" {
254254
255255 _ = windows.loadWinsockExtensionFunction(
256256 LPFN_CONNECTEX,
257 try std.os.socket(std.os.AF.INET, std.os.SOCK.DGRAM, 0),
257 try std.posix.socket(std.posix.AF.INET, std.posix.SOCK.DGRAM, 0),
258258 windows.ws2_32.WSAID_CONNECTEX,
259259 ) catch |err| switch (err) {
260260 error.OperationNotSupported => unreachable,
lib/std/pdb.zig-1
......@@ -2,7 +2,6 @@ const std = @import("std.zig");
22const io = std.io;
33const math = std.math;
44const mem = std.mem;
5const os = std.os;
65const coff = std.coff;
76const fs = std.fs;
87const File = std.fs.File;
lib/std/posix.zig created+7364
......@@ -0,0 +1,7364 @@
1//! POSIX API layer.
2//!
3//! This is more cross platform than using OS-specific APIs, however, it is
4//! lower-level and less portable than other namespaces such as `std.fs` and
5//! `std.process`.
6//!
7//! These APIs are generally lowered to libc function calls if and only if libc
8//! is linked. Most operating systems other than Windows, Linux, and WASI
9//! require always linking libc because they use it as the stable syscall ABI.
10//!
11//! Operating systems that are not POSIX-compliant are sometimes supported by
12//! this API layer; sometimes not. Generally, an implementation will be
13//! provided only if such implementation is straightforward on that operating
14//! system. Otherwise, programmers are expected to use OS-specific logic to
15//! deal with the exception.
16
17const builtin = @import("builtin");
18const root = @import("root");
19const std = @import("std.zig");
20const mem = std.mem;
21const fs = std.fs;
22const max_path_bytes = fs.MAX_PATH_BYTES;
23const maxInt = std.math.maxInt;
24const cast = std.math.cast;
25const assert = std.debug.assert;
26const native_os = builtin.os.tag;
27
28test {
29 _ = @import("posix/test.zig");
30}
31
32/// Whether to use libc for the POSIX API layer.
33const use_libc = builtin.link_libc or switch (native_os) {
34 .windows, .wasi => true,
35 else => false,
36};
37
38const linux = std.os.linux;
39const windows = std.os.windows;
40const wasi = std.os.wasi;
41
42/// Applications can override the `system` API layer in their root source file.
43/// Otherwise, when linking libc, this is the C API.
44/// When not linking libc, it is the OS-specific system interface.
45pub const system = if (@hasDecl(root, "os") and @hasDecl(root.os, "system") and root.os != @This())
46 root.os.system
47else if (use_libc)
48 std.c
49else switch (native_os) {
50 .linux => linux,
51 .plan9 => std.os.plan9,
52 else => struct {},
53};
54
55pub const AF = system.AF;
56pub const AF_SUN = system.AF_SUN;
57pub const ARCH = system.ARCH;
58pub const AT = system.AT;
59pub const AT_SUN = system.AT_SUN;
60pub const CLOCK = system.CLOCK;
61pub const CPU_COUNT = system.CPU_COUNT;
62pub const CTL = system.CTL;
63pub const DT = system.DT;
64pub const E = system.E;
65pub const Elf_Symndx = system.Elf_Symndx;
66pub const F = system.F;
67pub const FD_CLOEXEC = system.FD_CLOEXEC;
68pub const Flock = system.Flock;
69pub const HOST_NAME_MAX = system.HOST_NAME_MAX;
70pub const HW = system.HW;
71pub const IFNAMESIZE = system.IFNAMESIZE;
72pub const IOV_MAX = system.IOV_MAX;
73pub const IPPROTO = system.IPPROTO;
74pub const KERN = system.KERN;
75pub const Kevent = system.Kevent;
76pub const LOCK = system.LOCK;
77pub const MADV = system.MADV;
78pub const MAP = system.MAP;
79pub const MSF = system.MSF;
80pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN;
81pub const MFD = system.MFD;
82pub const MMAP2_UNIT = system.MMAP2_UNIT;
83pub const MSG = system.MSG;
84pub const NAME_MAX = system.NAME_MAX;
85pub const O = system.O;
86pub const PATH_MAX = system.PATH_MAX;
87pub const POLL = system.POLL;
88pub const POSIX_FADV = system.POSIX_FADV;
89pub const PR = system.PR;
90pub const PROT = system.PROT;
91pub const REG = system.REG;
92pub const RLIM = system.RLIM;
93pub const RR = system.RR;
94pub const S = system.S;
95pub const SA = system.SA;
96pub const SC = system.SC;
97pub const _SC = system._SC;
98pub const SEEK = system.SEEK;
99pub const SHUT = system.SHUT;
100pub const SIG = system.SIG;
101pub const SIOCGIFINDEX = system.SIOCGIFINDEX;
102pub const SO = system.SO;
103pub const SOCK = system.SOCK;
104pub const SOL = system.SOL;
105pub const STDERR_FILENO = system.STDERR_FILENO;
106pub const STDIN_FILENO = system.STDIN_FILENO;
107pub const STDOUT_FILENO = system.STDOUT_FILENO;
108pub const SYS = system.SYS;
109pub const Sigaction = system.Sigaction;
110pub const Stat = system.Stat;
111pub const T = system.T;
112pub const TCSA = system.TCSA;
113pub const TCP = system.TCP;
114pub const VDSO = system.VDSO;
115pub const W = system.W;
116pub const addrinfo = system.addrinfo;
117pub const blkcnt_t = system.blkcnt_t;
118pub const blksize_t = system.blksize_t;
119pub const clock_t = system.clock_t;
120pub const cpu_set_t = system.cpu_set_t;
121pub const dev_t = system.dev_t;
122pub const dl_phdr_info = system.dl_phdr_info;
123pub const empty_sigset = system.empty_sigset;
124pub const filled_sigset = system.filled_sigset;
125pub const fd_t = system.fd_t;
126pub const gid_t = system.gid_t;
127pub const ifreq = system.ifreq;
128pub const ino_t = system.ino_t;
129pub const mcontext_t = system.mcontext_t;
130pub const mode_t = system.mode_t;
131pub const msghdr = system.msghdr;
132pub const msghdr_const = system.msghdr_const;
133pub const nfds_t = system.nfds_t;
134pub const nlink_t = system.nlink_t;
135pub const off_t = system.off_t;
136pub const pid_t = system.pid_t;
137pub const pollfd = system.pollfd;
138pub const port_t = system.port_t;
139pub const port_event = system.port_event;
140pub const port_notify = system.port_notify;
141pub const file_obj = system.file_obj;
142pub const rlim_t = system.rlim_t;
143pub const rlimit = system.rlimit;
144pub const rlimit_resource = system.rlimit_resource;
145pub const rusage = system.rusage;
146pub const sa_family_t = system.sa_family_t;
147pub const siginfo_t = system.siginfo_t;
148pub const sigset_t = system.sigset_t;
149pub const sockaddr = system.sockaddr;
150pub const socklen_t = system.socklen_t;
151pub const stack_t = system.stack_t;
152pub const time_t = system.time_t;
153pub const timespec = system.timespec;
154pub const timestamp_t = system.timestamp_t;
155pub const timeval = system.timeval;
156pub const timezone = system.timezone;
157pub const ucontext_t = system.ucontext_t;
158pub const uid_t = system.uid_t;
159pub const user_desc = system.user_desc;
160pub const utsname = system.utsname;
161pub const winsize = system.winsize;
162
163pub const termios = system.termios;
164pub const CSIZE = system.CSIZE;
165pub const NCCS = system.NCCS;
166pub const cc_t = system.cc_t;
167pub const V = system.V;
168pub const speed_t = system.speed_t;
169pub const tc_iflag_t = system.tc_iflag_t;
170pub const tc_oflag_t = system.tc_oflag_t;
171pub const tc_cflag_t = system.tc_cflag_t;
172pub const tc_lflag_t = system.tc_lflag_t;
173
174pub const F_OK = system.F_OK;
175pub const R_OK = system.R_OK;
176pub const W_OK = system.W_OK;
177pub const X_OK = system.X_OK;
178
179pub const iovec = extern struct {
180 iov_base: [*]u8,
181 iov_len: usize,
182};
183
184pub const iovec_const = extern struct {
185 iov_base: [*]const u8,
186 iov_len: usize,
187};
188
189pub const ACCMODE = enum(u2) {
190 RDONLY = 0,
191 WRONLY = 1,
192 RDWR = 2,
193};
194
195pub const LOG = struct {
196 /// system is unusable
197 pub const EMERG = 0;
198 /// action must be taken immediately
199 pub const ALERT = 1;
200 /// critical conditions
201 pub const CRIT = 2;
202 /// error conditions
203 pub const ERR = 3;
204 /// warning conditions
205 pub const WARNING = 4;
206 /// normal but significant condition
207 pub const NOTICE = 5;
208 /// informational
209 pub const INFO = 6;
210 /// debug-level messages
211 pub const DEBUG = 7;
212};
213
214pub const socket_t = if (native_os == .windows) windows.ws2_32.SOCKET else fd_t;
215
216/// Obtains errno from the return value of a system function call.
217///
218/// For some systems this will obtain the value directly from the syscall return value;
219/// for others it will use a thread-local errno variable. Therefore, this
220/// function only returns a well-defined value when it is called directly after
221/// the system function call whose errno value is intended to be observed.
222pub fn errno(rc: anytype) E {
223 if (use_libc) {
224 return if (rc == -1) @enumFromInt(std.c._errno().*) else .SUCCESS;
225 }
226 const signed: isize = @bitCast(rc);
227 const int = if (signed > -4096 and signed < 0) -signed else 0;
228 return @enumFromInt(int);
229}
230
231/// Closes the file descriptor.
232///
233/// This function is not capable of returning any indication of failure. An
234/// application which wants to ensure writes have succeeded before closing must
235/// call `fsync` before `close`.
236///
237/// The Zig standard library does not support POSIX thread cancellation.
238pub fn close(fd: fd_t) void {
239 if (native_os == .windows) {
240 return windows.CloseHandle(fd);
241 }
242 if (native_os == .wasi and !builtin.link_libc) {
243 _ = std.os.wasi.fd_close(fd);
244 return;
245 }
246 if (builtin.target.isDarwin()) {
247 // This avoids the EINTR problem.
248 switch (errno(std.c.@"close$NOCANCEL"(fd))) {
249 .BADF => unreachable, // Always a race condition.
250 else => return,
251 }
252 }
253 switch (errno(system.close(fd))) {
254 .BADF => unreachable, // Always a race condition.
255 .INTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425
256 else => return,
257 }
258}
259
260pub const FChmodError = error{
261 AccessDenied,
262 InputOutput,
263 SymLinkLoop,
264 FileNotFound,
265 SystemResources,
266 ReadOnlyFileSystem,
267} || UnexpectedError;
268
269/// Changes the mode of the file referred to by the file descriptor.
270///
271/// The process must have the correct privileges in order to do this
272/// successfully, or must have the effective user ID matching the owner
273/// of the file.
274pub fn fchmod(fd: fd_t, mode: mode_t) FChmodError!void {
275 if (!fs.has_executable_bit) @compileError("fchmod unsupported by target OS");
276
277 while (true) {
278 const res = system.fchmod(fd, mode);
279 switch (errno(res)) {
280 .SUCCESS => return,
281 .INTR => continue,
282 .BADF => unreachable,
283 .FAULT => unreachable,
284 .INVAL => unreachable,
285 .ACCES => return error.AccessDenied,
286 .IO => return error.InputOutput,
287 .LOOP => return error.SymLinkLoop,
288 .NOENT => return error.FileNotFound,
289 .NOMEM => return error.SystemResources,
290 .NOTDIR => return error.FileNotFound,
291 .PERM => return error.AccessDenied,
292 .ROFS => return error.ReadOnlyFileSystem,
293 else => |err| return unexpectedErrno(err),
294 }
295 }
296}
297
298pub const FChmodAtError = FChmodError || error{
299 /// A component of `path` exceeded `NAME_MAX`, or the entire path exceeded
300 /// `PATH_MAX`.
301 NameTooLong,
302 /// `path` resolves to a symbolic link, and `AT.SYMLINK_NOFOLLOW` was set
303 /// in `flags`. This error only occurs on Linux, where changing the mode of
304 /// a symbolic link has no meaning and can cause undefined behaviour on
305 /// certain filesystems.
306 ///
307 /// The procfs fallback was used but procfs was not mounted.
308 OperationNotSupported,
309 /// The procfs fallback was used but the process exceeded its open file
310 /// limit.
311 ProcessFdQuotaExceeded,
312 /// The procfs fallback was used but the system exceeded it open file limit.
313 SystemFdQuotaExceeded,
314};
315
316/// Changes the `mode` of `path` relative to the directory referred to by
317/// `dirfd`. The process must have the correct privileges in order to do this
318/// successfully, or must have the effective user ID matching the owner of the
319/// file.
320///
321/// On Linux the `fchmodat2` syscall will be used if available, otherwise a
322/// workaround using procfs will be employed. Changing the mode of a symbolic
323/// link with `AT.SYMLINK_NOFOLLOW` set will also return
324/// `OperationNotSupported`, as:
325///
326/// 1. Permissions on the link are ignored when resolving its target.
327/// 2. This operation has been known to invoke undefined behaviour across
328/// different filesystems[1].
329///
330/// [1]: https://sourceware.org/legacy-ml/libc-alpha/2020-02/msg00467.html.
331pub inline fn fchmodat(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void {
332 if (!fs.has_executable_bit) @compileError("fchmodat unsupported by target OS");
333
334 // No special handling for linux is needed if we can use the libc fallback
335 // or `flags` is empty. Glibc only added the fallback in 2.32.
336 const skip_fchmodat_fallback = native_os != .linux or
337 std.c.versionCheck(.{ .major = 2, .minor = 32, .patch = 0 }) or
338 flags == 0;
339
340 // This function is marked inline so that when flags is comptime-known,
341 // skip_fchmodat_fallback will be comptime-known true.
342 if (skip_fchmodat_fallback)
343 return fchmodat1(dirfd, path, mode, flags);
344
345 return fchmodat2(dirfd, path, mode, flags);
346}
347
348fn fchmodat1(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void {
349 const path_c = try toPosixPath(path);
350 while (true) {
351 const res = system.fchmodat(dirfd, &path_c, mode, flags);
352 switch (errno(res)) {
353 .SUCCESS => return,
354 .INTR => continue,
355 .BADF => unreachable,
356 .FAULT => unreachable,
357 .INVAL => unreachable,
358 .ACCES => return error.AccessDenied,
359 .IO => return error.InputOutput,
360 .LOOP => return error.SymLinkLoop,
361 .MFILE => return error.ProcessFdQuotaExceeded,
362 .NAMETOOLONG => return error.NameTooLong,
363 .NFILE => return error.SystemFdQuotaExceeded,
364 .NOENT => return error.FileNotFound,
365 .NOTDIR => return error.FileNotFound,
366 .NOMEM => return error.SystemResources,
367 .OPNOTSUPP => return error.OperationNotSupported,
368 .PERM => return error.AccessDenied,
369 .ROFS => return error.ReadOnlyFileSystem,
370 else => |err| return unexpectedErrno(err),
371 }
372 }
373}
374
375fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void {
376 const global = struct {
377 var has_fchmodat2: bool = true;
378 };
379 const path_c = try toPosixPath(path);
380 const use_fchmodat2 = (builtin.os.isAtLeast(.linux, .{ .major = 6, .minor = 6, .patch = 0 }) orelse false) and
381 @atomicLoad(bool, &global.has_fchmodat2, .monotonic);
382 while (use_fchmodat2) {
383 // Later on this should be changed to `system.fchmodat2`
384 // when the musl/glibc add a wrapper.
385 const res = linux.fchmodat2(dirfd, &path_c, mode, flags);
386 switch (E.init(res)) {
387 .SUCCESS => return,
388 .INTR => continue,
389 .BADF => unreachable,
390 .FAULT => unreachable,
391 .INVAL => unreachable,
392 .ACCES => return error.AccessDenied,
393 .IO => return error.InputOutput,
394 .LOOP => return error.SymLinkLoop,
395 .NOENT => return error.FileNotFound,
396 .NOMEM => return error.SystemResources,
397 .NOTDIR => return error.FileNotFound,
398 .OPNOTSUPP => return error.OperationNotSupported,
399 .PERM => return error.AccessDenied,
400 .ROFS => return error.ReadOnlyFileSystem,
401
402 .NOSYS => {
403 @atomicStore(bool, &global.has_fchmodat2, false, .monotonic);
404 break;
405 },
406 else => |err| return unexpectedErrno(err),
407 }
408 }
409
410 // Fallback to changing permissions using procfs:
411 //
412 // 1. Open `path` as a `PATH` descriptor.
413 // 2. Stat the fd and check if it isn't a symbolic link.
414 // 3. Generate the procfs reference to the fd via `/proc/self/fd/{fd}`.
415 // 4. Pass the procfs path to `chmod` with the `mode`.
416 var pathfd: fd_t = undefined;
417 while (true) {
418 const rc = system.openat(dirfd, &path_c, .{ .PATH = true, .NOFOLLOW = true, .CLOEXEC = true }, @as(mode_t, 0));
419 switch (errno(rc)) {
420 .SUCCESS => {
421 pathfd = @intCast(rc);
422 break;
423 },
424 .INTR => continue,
425 .FAULT => unreachable,
426 .INVAL => unreachable,
427 .ACCES => return error.AccessDenied,
428 .PERM => return error.AccessDenied,
429 .LOOP => return error.SymLinkLoop,
430 .MFILE => return error.ProcessFdQuotaExceeded,
431 .NAMETOOLONG => return error.NameTooLong,
432 .NFILE => return error.SystemFdQuotaExceeded,
433 .NOENT => return error.FileNotFound,
434 .NOMEM => return error.SystemResources,
435 else => |err| return unexpectedErrno(err),
436 }
437 }
438 defer close(pathfd);
439
440 const stat = fstatatZ(pathfd, "", AT.EMPTY_PATH) catch |err| switch (err) {
441 error.NameTooLong => unreachable,
442 error.FileNotFound => unreachable,
443 error.InvalidUtf8 => unreachable,
444 else => |e| return e,
445 };
446 if ((stat.mode & S.IFMT) == S.IFLNK)
447 return error.OperationNotSupported;
448
449 var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
450 const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/fd/{d}", .{pathfd}) catch unreachable;
451 while (true) {
452 const res = system.chmod(proc_path, mode);
453 switch (errno(res)) {
454 // Getting NOENT here means that procfs isn't mounted.
455 .NOENT => return error.OperationNotSupported,
456
457 .SUCCESS => return,
458 .INTR => continue,
459 .BADF => unreachable,
460 .FAULT => unreachable,
461 .INVAL => unreachable,
462 .ACCES => return error.AccessDenied,
463 .IO => return error.InputOutput,
464 .LOOP => return error.SymLinkLoop,
465 .NOMEM => return error.SystemResources,
466 .NOTDIR => return error.FileNotFound,
467 .PERM => return error.AccessDenied,
468 .ROFS => return error.ReadOnlyFileSystem,
469 else => |err| return unexpectedErrno(err),
470 }
471 }
472}
473
474pub const FChownError = error{
475 AccessDenied,
476 InputOutput,
477 SymLinkLoop,
478 FileNotFound,
479 SystemResources,
480 ReadOnlyFileSystem,
481} || UnexpectedError;
482
483/// Changes the owner and group of the file referred to by the file descriptor.
484/// The process must have the correct privileges in order to do this
485/// successfully. The group may be changed by the owner of the directory to
486/// any group of which the owner is a member. If the owner or group is
487/// specified as `null`, the ID is not changed.
488pub fn fchown(fd: fd_t, owner: ?uid_t, group: ?gid_t) FChownError!void {
489 switch (native_os) {
490 .windows, .wasi => @compileError("Unsupported OS"),
491 else => {},
492 }
493
494 while (true) {
495 const res = system.fchown(fd, owner orelse ~@as(uid_t, 0), group orelse ~@as(gid_t, 0));
496
497 switch (errno(res)) {
498 .SUCCESS => return,
499 .INTR => continue,
500 .BADF => unreachable, // Can be reached if the fd refers to a directory opened without `OpenDirOptions{ .iterate = true }`
501
502 .FAULT => unreachable,
503 .INVAL => unreachable,
504 .ACCES => return error.AccessDenied,
505 .IO => return error.InputOutput,
506 .LOOP => return error.SymLinkLoop,
507 .NOENT => return error.FileNotFound,
508 .NOMEM => return error.SystemResources,
509 .NOTDIR => return error.FileNotFound,
510 .PERM => return error.AccessDenied,
511 .ROFS => return error.ReadOnlyFileSystem,
512 else => |err| return unexpectedErrno(err),
513 }
514 }
515}
516
517pub const RebootError = error{
518 PermissionDenied,
519} || UnexpectedError;
520
521pub const RebootCommand = switch (native_os) {
522 .linux => union(linux.LINUX_REBOOT.CMD) {
523 RESTART: void,
524 HALT: void,
525 CAD_ON: void,
526 CAD_OFF: void,
527 POWER_OFF: void,
528 RESTART2: [*:0]const u8,
529 SW_SUSPEND: void,
530 KEXEC: void,
531 },
532 else => @compileError("Unsupported OS"),
533};
534
535pub fn reboot(cmd: RebootCommand) RebootError!void {
536 switch (native_os) {
537 .linux => {
538 switch (linux.E.init(linux.reboot(
539 .MAGIC1,
540 .MAGIC2,
541 cmd,
542 switch (cmd) {
543 .RESTART2 => |s| s,
544 else => null,
545 },
546 ))) {
547 .SUCCESS => {},
548 .PERM => return error.PermissionDenied,
549 else => |err| return std.os.unexpectedErrno(err),
550 }
551 switch (cmd) {
552 .CAD_OFF => {},
553 .CAD_ON => {},
554 .SW_SUSPEND => {},
555
556 .HALT => unreachable,
557 .KEXEC => unreachable,
558 .POWER_OFF => unreachable,
559 .RESTART => unreachable,
560 .RESTART2 => unreachable,
561 }
562 },
563 else => @compileError("Unsupported OS"),
564 }
565}
566
567pub const GetRandomError = OpenError;
568
569/// Obtain a series of random bytes. These bytes can be used to seed user-space
570/// random number generators or for cryptographic purposes.
571/// When linking against libc, this calls the
572/// appropriate OS-specific library call. Otherwise it uses the zig standard
573/// library implementation.
574pub fn getrandom(buffer: []u8) GetRandomError!void {
575 if (native_os == .windows) {
576 return windows.RtlGenRandom(buffer);
577 }
578 if (native_os == .linux or native_os == .freebsd) {
579 var buf = buffer;
580 const use_c = native_os != .linux or
581 std.c.versionCheck(std.SemanticVersion{ .major = 2, .minor = 25, .patch = 0 });
582
583 while (buf.len != 0) {
584 const num_read: usize, const err = if (use_c) res: {
585 const rc = std.c.getrandom(buf.ptr, buf.len, 0);
586 break :res .{ @bitCast(rc), errno(rc) };
587 } else res: {
588 const rc = linux.getrandom(buf.ptr, buf.len, 0);
589 break :res .{ rc, linux.E.init(rc) };
590 };
591
592 switch (err) {
593 .SUCCESS => buf = buf[num_read..],
594 .INVAL => unreachable,
595 .FAULT => unreachable,
596 .INTR => continue,
597 .NOSYS => return getRandomBytesDevURandom(buf),
598 else => return unexpectedErrno(err),
599 }
600 }
601 return;
602 }
603 if (native_os == .emscripten) {
604 const err = errno(std.c.getentropy(buffer.ptr, buffer.len));
605 switch (err) {
606 .SUCCESS => return,
607 else => return unexpectedErrno(err),
608 }
609 }
610 switch (native_os) {
611 .netbsd, .openbsd, .macos, .ios, .tvos, .watchos => {
612 system.arc4random_buf(buffer.ptr, buffer.len);
613 return;
614 },
615 .wasi => switch (wasi.random_get(buffer.ptr, buffer.len)) {
616 .SUCCESS => return,
617 else => |err| return unexpectedErrno(err),
618 },
619 else => return getRandomBytesDevURandom(buffer),
620 }
621}
622
623fn getRandomBytesDevURandom(buf: []u8) !void {
624 const fd = try openZ("/dev/urandom", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
625 defer close(fd);
626
627 const st = try fstat(fd);
628 if (!S.ISCHR(st.mode)) {
629 return error.NoDevice;
630 }
631
632 const file: fs.File = .{ .handle = fd };
633 const stream = file.reader();
634 stream.readNoEof(buf) catch return error.Unexpected;
635}
636
637/// Causes abnormal process termination.
638/// If linking against libc, this calls the abort() libc function. Otherwise
639/// it raises SIGABRT followed by SIGKILL and finally lo
640/// Invokes the current signal handler for SIGABRT, if any.
641pub fn abort() noreturn {
642 @setCold(true);
643 // MSVCRT abort() sometimes opens a popup window which is undesirable, so
644 // even when linking libc on Windows we use our own abort implementation.
645 // See https://github.com/ziglang/zig/issues/2071 for more details.
646 if (native_os == .windows) {
647 if (builtin.mode == .Debug) {
648 @breakpoint();
649 }
650 windows.kernel32.ExitProcess(3);
651 }
652 if (!builtin.link_libc and native_os == .linux) {
653 // The Linux man page says that the libc abort() function
654 // "first unblocks the SIGABRT signal", but this is a footgun
655 // for user-defined signal handlers that want to restore some state in
656 // some program sections and crash in others.
657 // So, the user-installed SIGABRT handler is run, if present.
658 raise(SIG.ABRT) catch {};
659
660 // Disable all signal handlers.
661 sigprocmask(SIG.BLOCK, &linux.all_mask, null);
662
663 // Only one thread may proceed to the rest of abort().
664 if (!builtin.single_threaded) {
665 const global = struct {
666 var abort_entered: bool = false;
667 };
668 while (@cmpxchgWeak(bool, &global.abort_entered, false, true, .seq_cst, .seq_cst)) |_| {}
669 }
670
671 // Install default handler so that the tkill below will terminate.
672 const sigact = Sigaction{
673 .handler = .{ .handler = SIG.DFL },
674 .mask = empty_sigset,
675 .flags = 0,
676 };
677 sigaction(SIG.ABRT, &sigact, null) catch |err| switch (err) {
678 error.OperationNotSupported => unreachable,
679 };
680
681 _ = linux.tkill(linux.gettid(), SIG.ABRT);
682
683 const sigabrtmask: linux.sigset_t = [_]u32{0} ** 31 ++ [_]u32{1 << (SIG.ABRT - 1)};
684 sigprocmask(SIG.UNBLOCK, &sigabrtmask, null);
685
686 // Beyond this point should be unreachable.
687 @as(*allowzero volatile u8, @ptrFromInt(0)).* = 0;
688 raise(SIG.KILL) catch {};
689 exit(127); // Pid 1 might not be signalled in some containers.
690 }
691 switch (native_os) {
692 .uefi, .wasi, .emscripten, .cuda, .amdhsa => @trap(),
693 else => system.abort(),
694 }
695}
696
697pub const RaiseError = UnexpectedError;
698
699pub fn raise(sig: u8) RaiseError!void {
700 if (builtin.link_libc) {
701 switch (errno(system.raise(sig))) {
702 .SUCCESS => return,
703 else => |err| return unexpectedErrno(err),
704 }
705 }
706
707 if (native_os == .linux) {
708 var set: sigset_t = undefined;
709 // block application signals
710 sigprocmask(SIG.BLOCK, &linux.app_mask, &set);
711
712 const tid = linux.gettid();
713 const rc = linux.tkill(tid, sig);
714
715 // restore signal mask
716 sigprocmask(SIG.SETMASK, &set, null);
717
718 switch (errno(rc)) {
719 .SUCCESS => return,
720 else => |err| return unexpectedErrno(err),
721 }
722 }
723
724 @compileError("std.os.raise unimplemented for this target");
725}
726
727pub const KillError = error{ ProcessNotFound, PermissionDenied } || UnexpectedError;
728
729pub fn kill(pid: pid_t, sig: u8) KillError!void {
730 switch (errno(system.kill(pid, sig))) {
731 .SUCCESS => return,
732 .INVAL => unreachable, // invalid signal
733 .PERM => return error.PermissionDenied,
734 .SRCH => return error.ProcessNotFound,
735 else => |err| return unexpectedErrno(err),
736 }
737}
738
739/// Exits all threads of the program with the specified status code.
740pub fn exit(status: u8) noreturn {
741 if (builtin.link_libc) {
742 std.c.exit(status);
743 }
744 if (native_os == .windows) {
745 windows.kernel32.ExitProcess(status);
746 }
747 if (native_os == .wasi) {
748 wasi.proc_exit(status);
749 }
750 if (native_os == .linux and !builtin.single_threaded) {
751 linux.exit_group(status);
752 }
753 if (native_os == .uefi) {
754 const uefi = std.os.uefi;
755 // exit() is only available if exitBootServices() has not been called yet.
756 // This call to exit should not fail, so we don't care about its return value.
757 if (uefi.system_table.boot_services) |bs| {
758 _ = bs.exit(uefi.handle, @enumFromInt(status), 0, null);
759 }
760 // If we can't exit, reboot the system instead.
761 uefi.system_table.runtime_services.resetSystem(.ResetCold, @enumFromInt(status), 0, null);
762 }
763 system.exit(status);
764}
765
766pub const ReadError = error{
767 InputOutput,
768 SystemResources,
769 IsDir,
770 OperationAborted,
771 BrokenPipe,
772 ConnectionResetByPeer,
773 ConnectionTimedOut,
774 NotOpenForReading,
775 SocketNotConnected,
776
777 /// This error occurs when no global event loop is configured,
778 /// and reading from the file descriptor would block.
779 WouldBlock,
780
781 /// In WASI, this error occurs when the file descriptor does
782 /// not hold the required rights to read from it.
783 AccessDenied,
784} || UnexpectedError;
785
786/// Returns the number of bytes that were read, which can be less than
787/// buf.len. If 0 bytes were read, that means EOF.
788/// If `fd` is opened in non blocking mode, the function will return error.WouldBlock
789/// when EAGAIN is received.
790///
791/// Linux has a limit on how many bytes may be transferred in one `read` call, which is `0x7ffff000`
792/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
793/// well as stuffing the errno codes into the last `4096` values. This is noted on the `read` man page.
794/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
795/// The corresponding POSIX limit is `maxInt(isize)`.
796pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
797 if (buf.len == 0) return 0;
798 if (native_os == .windows) {
799 return windows.ReadFile(fd, buf, null);
800 }
801 if (native_os == .wasi and !builtin.link_libc) {
802 const iovs = [1]iovec{iovec{
803 .iov_base = buf.ptr,
804 .iov_len = buf.len,
805 }};
806
807 var nread: usize = undefined;
808 switch (wasi.fd_read(fd, &iovs, iovs.len, &nread)) {
809 .SUCCESS => return nread,
810 .INTR => unreachable,
811 .INVAL => unreachable,
812 .FAULT => unreachable,
813 .AGAIN => unreachable,
814 .BADF => return error.NotOpenForReading, // Can be a race condition.
815 .IO => return error.InputOutput,
816 .ISDIR => return error.IsDir,
817 .NOBUFS => return error.SystemResources,
818 .NOMEM => return error.SystemResources,
819 .NOTCONN => return error.SocketNotConnected,
820 .CONNRESET => return error.ConnectionResetByPeer,
821 .TIMEDOUT => return error.ConnectionTimedOut,
822 .NOTCAPABLE => return error.AccessDenied,
823 else => |err| return unexpectedErrno(err),
824 }
825 }
826
827 // Prevents EINVAL.
828 const max_count = switch (native_os) {
829 .linux => 0x7ffff000,
830 .macos, .ios, .watchos, .tvos => maxInt(i32),
831 else => maxInt(isize),
832 };
833 while (true) {
834 const rc = system.read(fd, buf.ptr, @min(buf.len, max_count));
835 switch (errno(rc)) {
836 .SUCCESS => return @intCast(rc),
837 .INTR => continue,
838 .INVAL => unreachable,
839 .FAULT => unreachable,
840 .AGAIN => return error.WouldBlock,
841 .BADF => return error.NotOpenForReading, // Can be a race condition.
842 .IO => return error.InputOutput,
843 .ISDIR => return error.IsDir,
844 .NOBUFS => return error.SystemResources,
845 .NOMEM => return error.SystemResources,
846 .NOTCONN => return error.SocketNotConnected,
847 .CONNRESET => return error.ConnectionResetByPeer,
848 .TIMEDOUT => return error.ConnectionTimedOut,
849 else => |err| return unexpectedErrno(err),
850 }
851 }
852}
853
854/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
855///
856/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
857/// return error.WouldBlock when EAGAIN is received.
858/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
859/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
860///
861/// This operation is non-atomic on the following systems:
862/// * Windows
863/// On these systems, the read races with concurrent writes to the same file descriptor.
864///
865/// This function assumes that all vectors, including zero-length vectors, have
866/// a pointer within the address space of the application.
867pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
868 if (native_os == .windows) {
869 // TODO improve this to use ReadFileScatter
870 if (iov.len == 0) return 0;
871 const first = iov[0];
872 return read(fd, first.iov_base[0..first.iov_len]);
873 }
874 if (native_os == .wasi and !builtin.link_libc) {
875 var nread: usize = undefined;
876 switch (wasi.fd_read(fd, iov.ptr, iov.len, &nread)) {
877 .SUCCESS => return nread,
878 .INTR => unreachable,
879 .INVAL => unreachable,
880 .FAULT => unreachable,
881 .AGAIN => unreachable, // currently not support in WASI
882 .BADF => return error.NotOpenForReading, // can be a race condition
883 .IO => return error.InputOutput,
884 .ISDIR => return error.IsDir,
885 .NOBUFS => return error.SystemResources,
886 .NOMEM => return error.SystemResources,
887 .NOTCONN => return error.SocketNotConnected,
888 .CONNRESET => return error.ConnectionResetByPeer,
889 .TIMEDOUT => return error.ConnectionTimedOut,
890 .NOTCAPABLE => return error.AccessDenied,
891 else => |err| return unexpectedErrno(err),
892 }
893 }
894
895 while (true) {
896 const rc = system.readv(fd, iov.ptr, @min(iov.len, IOV_MAX));
897 switch (errno(rc)) {
898 .SUCCESS => return @intCast(rc),
899 .INTR => continue,
900 .INVAL => unreachable,
901 .FAULT => unreachable,
902 .AGAIN => return error.WouldBlock,
903 .BADF => return error.NotOpenForReading, // can be a race condition
904 .IO => return error.InputOutput,
905 .ISDIR => return error.IsDir,
906 .NOBUFS => return error.SystemResources,
907 .NOMEM => return error.SystemResources,
908 .NOTCONN => return error.SocketNotConnected,
909 .CONNRESET => return error.ConnectionResetByPeer,
910 .TIMEDOUT => return error.ConnectionTimedOut,
911 else => |err| return unexpectedErrno(err),
912 }
913 }
914}
915
916pub const PReadError = ReadError || error{Unseekable};
917
918/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
919///
920/// Retries when interrupted by a signal.
921///
922/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
923/// return error.WouldBlock when EAGAIN is received.
924/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
925/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
926///
927/// Linux has a limit on how many bytes may be transferred in one `pread` call, which is `0x7ffff000`
928/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
929/// well as stuffing the errno codes into the last `4096` values. This is noted on the `read` man page.
930/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
931/// The corresponding POSIX limit is `maxInt(isize)`.
932pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
933 if (buf.len == 0) return 0;
934 if (native_os == .windows) {
935 return windows.ReadFile(fd, buf, offset);
936 }
937 if (native_os == .wasi and !builtin.link_libc) {
938 const iovs = [1]iovec{iovec{
939 .iov_base = buf.ptr,
940 .iov_len = buf.len,
941 }};
942
943 var nread: usize = undefined;
944 switch (wasi.fd_pread(fd, &iovs, iovs.len, offset, &nread)) {
945 .SUCCESS => return nread,
946 .INTR => unreachable,
947 .INVAL => unreachable,
948 .FAULT => unreachable,
949 .AGAIN => unreachable,
950 .BADF => return error.NotOpenForReading, // Can be a race condition.
951 .IO => return error.InputOutput,
952 .ISDIR => return error.IsDir,
953 .NOBUFS => return error.SystemResources,
954 .NOMEM => return error.SystemResources,
955 .NOTCONN => return error.SocketNotConnected,
956 .CONNRESET => return error.ConnectionResetByPeer,
957 .TIMEDOUT => return error.ConnectionTimedOut,
958 .NXIO => return error.Unseekable,
959 .SPIPE => return error.Unseekable,
960 .OVERFLOW => return error.Unseekable,
961 .NOTCAPABLE => return error.AccessDenied,
962 else => |err| return unexpectedErrno(err),
963 }
964 }
965
966 // Prevent EINVAL.
967 const max_count = switch (native_os) {
968 .linux => 0x7ffff000,
969 .macos, .ios, .watchos, .tvos => maxInt(i32),
970 else => maxInt(isize),
971 };
972
973 const pread_sym = if (lfs64_abi) system.pread64 else system.pread;
974 while (true) {
975 const rc = pread_sym(fd, buf.ptr, @min(buf.len, max_count), @bitCast(offset));
976 switch (errno(rc)) {
977 .SUCCESS => return @intCast(rc),
978 .INTR => continue,
979 .INVAL => unreachable,
980 .FAULT => unreachable,
981 .AGAIN => return error.WouldBlock,
982 .BADF => return error.NotOpenForReading, // Can be a race condition.
983 .IO => return error.InputOutput,
984 .ISDIR => return error.IsDir,
985 .NOBUFS => return error.SystemResources,
986 .NOMEM => return error.SystemResources,
987 .NOTCONN => return error.SocketNotConnected,
988 .CONNRESET => return error.ConnectionResetByPeer,
989 .TIMEDOUT => return error.ConnectionTimedOut,
990 .NXIO => return error.Unseekable,
991 .SPIPE => return error.Unseekable,
992 .OVERFLOW => return error.Unseekable,
993 else => |err| return unexpectedErrno(err),
994 }
995 }
996}
997
998pub const TruncateError = error{
999 FileTooBig,
1000 InputOutput,
1001 FileBusy,
1002
1003 /// In WASI, this error occurs when the file descriptor does
1004 /// not hold the required rights to call `ftruncate` on it.
1005 AccessDenied,
1006} || UnexpectedError;
1007
1008pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
1009 if (native_os == .windows) {
1010 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
1011 var eof_info = windows.FILE_END_OF_FILE_INFORMATION{
1012 .EndOfFile = @bitCast(length),
1013 };
1014
1015 const rc = windows.ntdll.NtSetInformationFile(
1016 fd,
1017 &io_status_block,
1018 &eof_info,
1019 @sizeOf(windows.FILE_END_OF_FILE_INFORMATION),
1020 .FileEndOfFileInformation,
1021 );
1022
1023 switch (rc) {
1024 .SUCCESS => return,
1025 .INVALID_HANDLE => unreachable, // Handle not open for writing
1026 .ACCESS_DENIED => return error.AccessDenied,
1027 else => return windows.unexpectedStatus(rc),
1028 }
1029 }
1030 if (native_os == .wasi and !builtin.link_libc) {
1031 switch (wasi.fd_filestat_set_size(fd, length)) {
1032 .SUCCESS => return,
1033 .INTR => unreachable,
1034 .FBIG => return error.FileTooBig,
1035 .IO => return error.InputOutput,
1036 .PERM => return error.AccessDenied,
1037 .TXTBSY => return error.FileBusy,
1038 .BADF => unreachable, // Handle not open for writing
1039 .INVAL => unreachable, // Handle not open for writing
1040 .NOTCAPABLE => return error.AccessDenied,
1041 else => |err| return unexpectedErrno(err),
1042 }
1043 }
1044
1045 const ftruncate_sym = if (lfs64_abi) system.ftruncate64 else system.ftruncate;
1046 while (true) {
1047 switch (errno(ftruncate_sym(fd, @bitCast(length)))) {
1048 .SUCCESS => return,
1049 .INTR => continue,
1050 .FBIG => return error.FileTooBig,
1051 .IO => return error.InputOutput,
1052 .PERM => return error.AccessDenied,
1053 .TXTBSY => return error.FileBusy,
1054 .BADF => unreachable, // Handle not open for writing
1055 .INVAL => unreachable, // Handle not open for writing
1056 else => |err| return unexpectedErrno(err),
1057 }
1058 }
1059}
1060
1061/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
1062///
1063/// Retries when interrupted by a signal.
1064///
1065/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
1066/// return error.WouldBlock when EAGAIN is received.
1067/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
1068/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
1069///
1070/// This operation is non-atomic on the following systems:
1071/// * Darwin
1072/// * Windows
1073/// On these systems, the read races with concurrent writes to the same file descriptor.
1074pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {
1075 const have_pread_but_not_preadv = switch (native_os) {
1076 .windows, .macos, .ios, .watchos, .tvos, .haiku => true,
1077 else => false,
1078 };
1079 if (have_pread_but_not_preadv) {
1080 // We could loop here; but proper usage of `preadv` must handle partial reads anyway.
1081 // So we simply read into the first vector only.
1082 if (iov.len == 0) return 0;
1083 const first = iov[0];
1084 return pread(fd, first.iov_base[0..first.iov_len], offset);
1085 }
1086 if (native_os == .wasi and !builtin.link_libc) {
1087 var nread: usize = undefined;
1088 switch (wasi.fd_pread(fd, iov.ptr, iov.len, offset, &nread)) {
1089 .SUCCESS => return nread,
1090 .INTR => unreachable,
1091 .INVAL => unreachable,
1092 .FAULT => unreachable,
1093 .AGAIN => unreachable,
1094 .BADF => return error.NotOpenForReading, // can be a race condition
1095 .IO => return error.InputOutput,
1096 .ISDIR => return error.IsDir,
1097 .NOBUFS => return error.SystemResources,
1098 .NOMEM => return error.SystemResources,
1099 .NOTCONN => return error.SocketNotConnected,
1100 .CONNRESET => return error.ConnectionResetByPeer,
1101 .TIMEDOUT => return error.ConnectionTimedOut,
1102 .NXIO => return error.Unseekable,
1103 .SPIPE => return error.Unseekable,
1104 .OVERFLOW => return error.Unseekable,
1105 .NOTCAPABLE => return error.AccessDenied,
1106 else => |err| return unexpectedErrno(err),
1107 }
1108 }
1109
1110 const preadv_sym = if (lfs64_abi) system.preadv64 else system.preadv;
1111 while (true) {
1112 const rc = preadv_sym(fd, iov.ptr, @min(iov.len, IOV_MAX), @bitCast(offset));
1113 switch (errno(rc)) {
1114 .SUCCESS => return @bitCast(rc),
1115 .INTR => continue,
1116 .INVAL => unreachable,
1117 .FAULT => unreachable,
1118 .AGAIN => return error.WouldBlock,
1119 .BADF => return error.NotOpenForReading, // can be a race condition
1120 .IO => return error.InputOutput,
1121 .ISDIR => return error.IsDir,
1122 .NOBUFS => return error.SystemResources,
1123 .NOMEM => return error.SystemResources,
1124 .NOTCONN => return error.SocketNotConnected,
1125 .CONNRESET => return error.ConnectionResetByPeer,
1126 .TIMEDOUT => return error.ConnectionTimedOut,
1127 .NXIO => return error.Unseekable,
1128 .SPIPE => return error.Unseekable,
1129 .OVERFLOW => return error.Unseekable,
1130 else => |err| return unexpectedErrno(err),
1131 }
1132 }
1133}
1134
1135pub const WriteError = error{
1136 DiskQuota,
1137 FileTooBig,
1138 InputOutput,
1139 NoSpaceLeft,
1140 DeviceBusy,
1141 InvalidArgument,
1142
1143 /// In WASI, this error may occur when the file descriptor does
1144 /// not hold the required rights to write to it.
1145 AccessDenied,
1146 BrokenPipe,
1147 SystemResources,
1148 OperationAborted,
1149 NotOpenForWriting,
1150
1151 /// The process cannot access the file because another process has locked
1152 /// a portion of the file. Windows-only.
1153 LockViolation,
1154
1155 /// This error occurs when no global event loop is configured,
1156 /// and reading from the file descriptor would block.
1157 WouldBlock,
1158
1159 /// Connection reset by peer.
1160 ConnectionResetByPeer,
1161} || UnexpectedError;
1162
1163/// Write to a file descriptor.
1164/// Retries when interrupted by a signal.
1165/// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
1166///
1167/// Note that a successful write() may transfer fewer than count bytes. Such partial writes can
1168/// occur for various reasons; for example, because there was insufficient space on the disk
1169/// device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or
1170/// similar was interrupted by a signal handler after it had transferred some, but before it had
1171/// transferred all of the requested bytes. In the event of a partial write, the caller can make
1172/// another write() call to transfer the remaining bytes. The subsequent call will either
1173/// transfer further bytes or may result in an error (e.g., if the disk is now full).
1174///
1175/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
1176/// return error.WouldBlock when EAGAIN is received.
1177/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
1178/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
1179///
1180/// Linux has a limit on how many bytes may be transferred in one `write` call, which is `0x7ffff000`
1181/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
1182/// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page.
1183/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
1184/// The corresponding POSIX limit is `maxInt(isize)`.
1185pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
1186 if (bytes.len == 0) return 0;
1187 if (native_os == .windows) {
1188 return windows.WriteFile(fd, bytes, null);
1189 }
1190
1191 if (native_os == .wasi and !builtin.link_libc) {
1192 const ciovs = [_]iovec_const{iovec_const{
1193 .iov_base = bytes.ptr,
1194 .iov_len = bytes.len,
1195 }};
1196 var nwritten: usize = undefined;
1197 switch (wasi.fd_write(fd, &ciovs, ciovs.len, &nwritten)) {
1198 .SUCCESS => return nwritten,
1199 .INTR => unreachable,
1200 .INVAL => unreachable,
1201 .FAULT => unreachable,
1202 .AGAIN => unreachable,
1203 .BADF => return error.NotOpenForWriting, // can be a race condition.
1204 .DESTADDRREQ => unreachable, // `connect` was never called.
1205 .DQUOT => return error.DiskQuota,
1206 .FBIG => return error.FileTooBig,
1207 .IO => return error.InputOutput,
1208 .NOSPC => return error.NoSpaceLeft,
1209 .PERM => return error.AccessDenied,
1210 .PIPE => return error.BrokenPipe,
1211 .NOTCAPABLE => return error.AccessDenied,
1212 else => |err| return unexpectedErrno(err),
1213 }
1214 }
1215
1216 const max_count = switch (native_os) {
1217 .linux => 0x7ffff000,
1218 .macos, .ios, .watchos, .tvos => maxInt(i32),
1219 else => maxInt(isize),
1220 };
1221 while (true) {
1222 const rc = system.write(fd, bytes.ptr, @min(bytes.len, max_count));
1223 switch (errno(rc)) {
1224 .SUCCESS => return @intCast(rc),
1225 .INTR => continue,
1226 .INVAL => return error.InvalidArgument,
1227 .FAULT => unreachable,
1228 .AGAIN => return error.WouldBlock,
1229 .BADF => return error.NotOpenForWriting, // can be a race condition.
1230 .DESTADDRREQ => unreachable, // `connect` was never called.
1231 .DQUOT => return error.DiskQuota,
1232 .FBIG => return error.FileTooBig,
1233 .IO => return error.InputOutput,
1234 .NOSPC => return error.NoSpaceLeft,
1235 .PERM => return error.AccessDenied,
1236 .PIPE => return error.BrokenPipe,
1237 .CONNRESET => return error.ConnectionResetByPeer,
1238 .BUSY => return error.DeviceBusy,
1239 else => |err| return unexpectedErrno(err),
1240 }
1241 }
1242}
1243
1244/// Write multiple buffers to a file descriptor.
1245/// Retries when interrupted by a signal.
1246/// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
1247///
1248/// Note that a successful write() may transfer fewer bytes than supplied. Such partial writes can
1249/// occur for various reasons; for example, because there was insufficient space on the disk
1250/// device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or
1251/// similar was interrupted by a signal handler after it had transferred some, but before it had
1252/// transferred all of the requested bytes. In the event of a partial write, the caller can make
1253/// another write() call to transfer the remaining bytes. The subsequent call will either
1254/// transfer further bytes or may result in an error (e.g., if the disk is now full).
1255///
1256/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
1257/// return error.WouldBlock when EAGAIN is received.
1258/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
1259/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
1260///
1261/// If `iov.len` is larger than `IOV_MAX`, a partial write will occur.
1262///
1263/// This function assumes that all vectors, including zero-length vectors, have
1264/// a pointer within the address space of the application.
1265pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
1266 if (native_os == .windows) {
1267 // TODO improve this to use WriteFileScatter
1268 if (iov.len == 0) return 0;
1269 const first = iov[0];
1270 return write(fd, first.iov_base[0..first.iov_len]);
1271 }
1272 if (native_os == .wasi and !builtin.link_libc) {
1273 var nwritten: usize = undefined;
1274 switch (wasi.fd_write(fd, iov.ptr, iov.len, &nwritten)) {
1275 .SUCCESS => return nwritten,
1276 .INTR => unreachable,
1277 .INVAL => unreachable,
1278 .FAULT => unreachable,
1279 .AGAIN => unreachable,
1280 .BADF => return error.NotOpenForWriting, // can be a race condition.
1281 .DESTADDRREQ => unreachable, // `connect` was never called.
1282 .DQUOT => return error.DiskQuota,
1283 .FBIG => return error.FileTooBig,
1284 .IO => return error.InputOutput,
1285 .NOSPC => return error.NoSpaceLeft,
1286 .PERM => return error.AccessDenied,
1287 .PIPE => return error.BrokenPipe,
1288 .NOTCAPABLE => return error.AccessDenied,
1289 else => |err| return unexpectedErrno(err),
1290 }
1291 }
1292
1293 while (true) {
1294 const rc = system.writev(fd, iov.ptr, @min(iov.len, IOV_MAX));
1295 switch (errno(rc)) {
1296 .SUCCESS => return @intCast(rc),
1297 .INTR => continue,
1298 .INVAL => return error.InvalidArgument,
1299 .FAULT => unreachable,
1300 .AGAIN => return error.WouldBlock,
1301 .BADF => return error.NotOpenForWriting, // Can be a race condition.
1302 .DESTADDRREQ => unreachable, // `connect` was never called.
1303 .DQUOT => return error.DiskQuota,
1304 .FBIG => return error.FileTooBig,
1305 .IO => return error.InputOutput,
1306 .NOSPC => return error.NoSpaceLeft,
1307 .PERM => return error.AccessDenied,
1308 .PIPE => return error.BrokenPipe,
1309 .CONNRESET => return error.ConnectionResetByPeer,
1310 .BUSY => return error.DeviceBusy,
1311 else => |err| return unexpectedErrno(err),
1312 }
1313 }
1314}
1315
1316pub const PWriteError = WriteError || error{Unseekable};
1317
1318/// Write to a file descriptor, with a position offset.
1319/// Retries when interrupted by a signal.
1320/// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
1321///
1322/// Note that a successful write() may transfer fewer bytes than supplied. Such partial writes can
1323/// occur for various reasons; for example, because there was insufficient space on the disk
1324/// device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or
1325/// similar was interrupted by a signal handler after it had transferred some, but before it had
1326/// transferred all of the requested bytes. In the event of a partial write, the caller can make
1327/// another write() call to transfer the remaining bytes. The subsequent call will either
1328/// transfer further bytes or may result in an error (e.g., if the disk is now full).
1329///
1330/// For POSIX systems, if `fd` is opened in non blocking mode, the function will
1331/// return error.WouldBlock when EAGAIN is received.
1332/// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
1333/// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
1334///
1335/// Linux has a limit on how many bytes may be transferred in one `pwrite` call, which is `0x7ffff000`
1336/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
1337/// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page.
1338/// The limit on Darwin is `0x7fffffff`, trying to write more than that returns EINVAL.
1339/// The corresponding POSIX limit is `maxInt(isize)`.
1340pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
1341 if (bytes.len == 0) return 0;
1342 if (native_os == .windows) {
1343 return windows.WriteFile(fd, bytes, offset);
1344 }
1345 if (native_os == .wasi and !builtin.link_libc) {
1346 const ciovs = [1]iovec_const{iovec_const{
1347 .iov_base = bytes.ptr,
1348 .iov_len = bytes.len,
1349 }};
1350
1351 var nwritten: usize = undefined;
1352 switch (wasi.fd_pwrite(fd, &ciovs, ciovs.len, offset, &nwritten)) {
1353 .SUCCESS => return nwritten,
1354 .INTR => unreachable,
1355 .INVAL => unreachable,
1356 .FAULT => unreachable,
1357 .AGAIN => unreachable,
1358 .BADF => return error.NotOpenForWriting, // can be a race condition.
1359 .DESTADDRREQ => unreachable, // `connect` was never called.
1360 .DQUOT => return error.DiskQuota,
1361 .FBIG => return error.FileTooBig,
1362 .IO => return error.InputOutput,
1363 .NOSPC => return error.NoSpaceLeft,
1364 .PERM => return error.AccessDenied,
1365 .PIPE => return error.BrokenPipe,
1366 .NXIO => return error.Unseekable,
1367 .SPIPE => return error.Unseekable,
1368 .OVERFLOW => return error.Unseekable,
1369 .NOTCAPABLE => return error.AccessDenied,
1370 else => |err| return unexpectedErrno(err),
1371 }
1372 }
1373
1374 // Prevent EINVAL.
1375 const max_count = switch (native_os) {
1376 .linux => 0x7ffff000,
1377 .macos, .ios, .watchos, .tvos => maxInt(i32),
1378 else => maxInt(isize),
1379 };
1380
1381 const pwrite_sym = if (lfs64_abi) system.pwrite64 else system.pwrite;
1382 while (true) {
1383 const rc = pwrite_sym(fd, bytes.ptr, @min(bytes.len, max_count), @bitCast(offset));
1384 switch (errno(rc)) {
1385 .SUCCESS => return @intCast(rc),
1386 .INTR => continue,
1387 .INVAL => return error.InvalidArgument,
1388 .FAULT => unreachable,
1389 .AGAIN => return error.WouldBlock,
1390 .BADF => return error.NotOpenForWriting, // Can be a race condition.
1391 .DESTADDRREQ => unreachable, // `connect` was never called.
1392 .DQUOT => return error.DiskQuota,
1393 .FBIG => return error.FileTooBig,
1394 .IO => return error.InputOutput,
1395 .NOSPC => return error.NoSpaceLeft,
1396 .PERM => return error.AccessDenied,
1397 .PIPE => return error.BrokenPipe,
1398 .NXIO => return error.Unseekable,
1399 .SPIPE => return error.Unseekable,
1400 .OVERFLOW => return error.Unseekable,
1401 .BUSY => return error.DeviceBusy,
1402 else => |err| return unexpectedErrno(err),
1403 }
1404 }
1405}
1406
1407/// Write multiple buffers to a file descriptor, with a position offset.
1408/// Retries when interrupted by a signal.
1409/// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
1410///
1411/// Note that a successful write() may transfer fewer than count bytes. Such partial writes can
1412/// occur for various reasons; for example, because there was insufficient space on the disk
1413/// device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or
1414/// similar was interrupted by a signal handler after it had transferred some, but before it had
1415/// transferred all of the requested bytes. In the event of a partial write, the caller can make
1416/// another write() call to transfer the remaining bytes. The subsequent call will either
1417/// transfer further bytes or may result in an error (e.g., if the disk is now full).
1418///
1419/// If `fd` is opened in non blocking mode, the function will
1420/// return error.WouldBlock when EAGAIN is received.
1421///
1422/// The following systems do not have this syscall, and will return partial writes if more than one
1423/// vector is provided:
1424/// * Darwin
1425/// * Windows
1426///
1427/// If `iov.len` is larger than `IOV_MAX`, a partial write will occur.
1428pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usize {
1429 const have_pwrite_but_not_pwritev = switch (native_os) {
1430 .windows, .macos, .ios, .watchos, .tvos, .haiku => true,
1431 else => false,
1432 };
1433
1434 if (have_pwrite_but_not_pwritev) {
1435 // We could loop here; but proper usage of `pwritev` must handle partial writes anyway.
1436 // So we simply write the first vector only.
1437 if (iov.len == 0) return 0;
1438 const first = iov[0];
1439 return pwrite(fd, first.iov_base[0..first.iov_len], offset);
1440 }
1441 if (native_os == .wasi and !builtin.link_libc) {
1442 var nwritten: usize = undefined;
1443 switch (wasi.fd_pwrite(fd, iov.ptr, iov.len, offset, &nwritten)) {
1444 .SUCCESS => return nwritten,
1445 .INTR => unreachable,
1446 .INVAL => unreachable,
1447 .FAULT => unreachable,
1448 .AGAIN => unreachable,
1449 .BADF => return error.NotOpenForWriting, // Can be a race condition.
1450 .DESTADDRREQ => unreachable, // `connect` was never called.
1451 .DQUOT => return error.DiskQuota,
1452 .FBIG => return error.FileTooBig,
1453 .IO => return error.InputOutput,
1454 .NOSPC => return error.NoSpaceLeft,
1455 .PERM => return error.AccessDenied,
1456 .PIPE => return error.BrokenPipe,
1457 .NXIO => return error.Unseekable,
1458 .SPIPE => return error.Unseekable,
1459 .OVERFLOW => return error.Unseekable,
1460 .NOTCAPABLE => return error.AccessDenied,
1461 else => |err| return unexpectedErrno(err),
1462 }
1463 }
1464
1465 const pwritev_sym = if (lfs64_abi) system.pwritev64 else system.pwritev;
1466 while (true) {
1467 const rc = pwritev_sym(fd, iov.ptr, @min(iov.len, IOV_MAX), @bitCast(offset));
1468 switch (errno(rc)) {
1469 .SUCCESS => return @intCast(rc),
1470 .INTR => continue,
1471 .INVAL => return error.InvalidArgument,
1472 .FAULT => unreachable,
1473 .AGAIN => return error.WouldBlock,
1474 .BADF => return error.NotOpenForWriting, // Can be a race condition.
1475 .DESTADDRREQ => unreachable, // `connect` was never called.
1476 .DQUOT => return error.DiskQuota,
1477 .FBIG => return error.FileTooBig,
1478 .IO => return error.InputOutput,
1479 .NOSPC => return error.NoSpaceLeft,
1480 .PERM => return error.AccessDenied,
1481 .PIPE => return error.BrokenPipe,
1482 .NXIO => return error.Unseekable,
1483 .SPIPE => return error.Unseekable,
1484 .OVERFLOW => return error.Unseekable,
1485 .BUSY => return error.DeviceBusy,
1486 else => |err| return unexpectedErrno(err),
1487 }
1488 }
1489}
1490
1491pub const OpenError = error{
1492 /// In WASI, this error may occur when the file descriptor does
1493 /// not hold the required rights to open a new resource relative to it.
1494 AccessDenied,
1495 SymLinkLoop,
1496 ProcessFdQuotaExceeded,
1497 SystemFdQuotaExceeded,
1498 NoDevice,
1499 FileNotFound,
1500
1501 /// The path exceeded `max_path_bytes` bytes.
1502 NameTooLong,
1503
1504 /// Insufficient kernel memory was available, or
1505 /// the named file is a FIFO and per-user hard limit on
1506 /// memory allocation for pipes has been reached.
1507 SystemResources,
1508
1509 /// The file is too large to be opened. This error is unreachable
1510 /// for 64-bit targets, as well as when opening directories.
1511 FileTooBig,
1512
1513 /// The path refers to directory but the `DIRECTORY` flag was not provided.
1514 IsDir,
1515
1516 /// A new path cannot be created because the device has no room for the new file.
1517 /// This error is only reachable when the `CREAT` flag is provided.
1518 NoSpaceLeft,
1519
1520 /// A component used as a directory in the path was not, in fact, a directory, or
1521 /// `DIRECTORY` was specified and the path was not a directory.
1522 NotDir,
1523
1524 /// The path already exists and the `CREAT` and `EXCL` flags were provided.
1525 PathAlreadyExists,
1526 DeviceBusy,
1527
1528 /// The underlying filesystem does not support file locks
1529 FileLocksNotSupported,
1530
1531 /// Path contains characters that are disallowed by the underlying filesystem.
1532 BadPathName,
1533
1534 /// WASI-only; file paths must be valid UTF-8.
1535 InvalidUtf8,
1536
1537 /// Windows-only; file paths provided by the user must be valid WTF-8.
1538 /// https://simonsapin.github.io/wtf-8/
1539 InvalidWtf8,
1540
1541 /// On Windows, `\\server` or `\\server\share` was not found.
1542 NetworkNotFound,
1543
1544 /// One of these three things:
1545 /// * pathname refers to an executable image which is currently being
1546 /// executed and write access was requested.
1547 /// * pathname refers to a file that is currently in use as a swap
1548 /// file, and the O_TRUNC flag was specified.
1549 /// * pathname refers to a file that is currently being read by the
1550 /// kernel (e.g., for module/firmware loading), and write access was
1551 /// requested.
1552 FileBusy,
1553
1554 WouldBlock,
1555} || UnexpectedError;
1556
1557/// Open and possibly create a file. Keeps trying if it gets interrupted.
1558/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
1559/// On WASI, `file_path` should be encoded as valid UTF-8.
1560/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
1561/// See also `openZ`.
1562pub fn open(file_path: []const u8, flags: O, perm: mode_t) OpenError!fd_t {
1563 if (native_os == .windows) {
1564 @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
1565 } else if (native_os == .wasi and !builtin.link_libc) {
1566 return openat(AT.FDCWD, file_path, flags, perm);
1567 }
1568 const file_path_c = try toPosixPath(file_path);
1569 return openZ(&file_path_c, flags, perm);
1570}
1571
1572/// Open and possibly create a file. Keeps trying if it gets interrupted.
1573/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
1574/// On WASI, `file_path` should be encoded as valid UTF-8.
1575/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
1576/// See also `open`.
1577pub fn openZ(file_path: [*:0]const u8, flags: O, perm: mode_t) OpenError!fd_t {
1578 if (native_os == .windows) {
1579 @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
1580 } else if (native_os == .wasi and !builtin.link_libc) {
1581 return open(mem.sliceTo(file_path, 0), flags, perm);
1582 }
1583
1584 const open_sym = if (lfs64_abi) system.open64 else system.open;
1585 while (true) {
1586 const rc = open_sym(file_path, flags, perm);
1587 switch (errno(rc)) {
1588 .SUCCESS => return @intCast(rc),
1589 .INTR => continue,
1590
1591 .FAULT => unreachable,
1592 .INVAL => unreachable,
1593 .ACCES => return error.AccessDenied,
1594 .FBIG => return error.FileTooBig,
1595 .OVERFLOW => return error.FileTooBig,
1596 .ISDIR => return error.IsDir,
1597 .LOOP => return error.SymLinkLoop,
1598 .MFILE => return error.ProcessFdQuotaExceeded,
1599 .NAMETOOLONG => return error.NameTooLong,
1600 .NFILE => return error.SystemFdQuotaExceeded,
1601 .NODEV => return error.NoDevice,
1602 .NOENT => return error.FileNotFound,
1603 .NOMEM => return error.SystemResources,
1604 .NOSPC => return error.NoSpaceLeft,
1605 .NOTDIR => return error.NotDir,
1606 .PERM => return error.AccessDenied,
1607 .EXIST => return error.PathAlreadyExists,
1608 .BUSY => return error.DeviceBusy,
1609 else => |err| return unexpectedErrno(err),
1610 }
1611 }
1612}
1613
1614/// Open and possibly create a file. Keeps trying if it gets interrupted.
1615/// `file_path` is relative to the open directory handle `dir_fd`.
1616/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
1617/// On WASI, `file_path` should be encoded as valid UTF-8.
1618/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
1619/// See also `openatZ`.
1620pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: O, mode: mode_t) OpenError!fd_t {
1621 if (native_os == .windows) {
1622 @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
1623 } else if (native_os == .wasi and !builtin.link_libc) {
1624 // `mode` is ignored on WASI, which does not support unix-style file permissions
1625 const opts = try openOptionsFromFlagsWasi(flags);
1626 const fd = try openatWasi(
1627 dir_fd,
1628 file_path,
1629 opts.lookup_flags,
1630 opts.oflags,
1631 opts.fs_flags,
1632 opts.fs_rights_base,
1633 opts.fs_rights_inheriting,
1634 );
1635 errdefer close(fd);
1636
1637 if (flags.write) {
1638 const info = try fstat_wasi(fd);
1639 if (info.filetype == .DIRECTORY)
1640 return error.IsDir;
1641 }
1642
1643 return fd;
1644 }
1645 const file_path_c = try toPosixPath(file_path);
1646 return openatZ(dir_fd, &file_path_c, flags, mode);
1647}
1648
1649/// Open and possibly create a file in WASI.
1650pub fn openatWasi(
1651 dir_fd: fd_t,
1652 file_path: []const u8,
1653 lookup_flags: wasi.lookupflags_t,
1654 oflags: wasi.oflags_t,
1655 fdflags: wasi.fdflags_t,
1656 base: wasi.rights_t,
1657 inheriting: wasi.rights_t,
1658) OpenError!fd_t {
1659 while (true) {
1660 var fd: fd_t = undefined;
1661 switch (wasi.path_open(dir_fd, lookup_flags, file_path.ptr, file_path.len, oflags, base, inheriting, fdflags, &fd)) {
1662 .SUCCESS => return fd,
1663 .INTR => continue,
1664
1665 .FAULT => unreachable,
1666 .INVAL => unreachable,
1667 .BADF => unreachable,
1668 .ACCES => return error.AccessDenied,
1669 .FBIG => return error.FileTooBig,
1670 .OVERFLOW => return error.FileTooBig,
1671 .ISDIR => return error.IsDir,
1672 .LOOP => return error.SymLinkLoop,
1673 .MFILE => return error.ProcessFdQuotaExceeded,
1674 .NAMETOOLONG => return error.NameTooLong,
1675 .NFILE => return error.SystemFdQuotaExceeded,
1676 .NODEV => return error.NoDevice,
1677 .NOENT => return error.FileNotFound,
1678 .NOMEM => return error.SystemResources,
1679 .NOSPC => return error.NoSpaceLeft,
1680 .NOTDIR => return error.NotDir,
1681 .PERM => return error.AccessDenied,
1682 .EXIST => return error.PathAlreadyExists,
1683 .BUSY => return error.DeviceBusy,
1684 .NOTCAPABLE => return error.AccessDenied,
1685 .ILSEQ => return error.InvalidUtf8,
1686 else => |err| return unexpectedErrno(err),
1687 }
1688 }
1689}
1690
1691/// A struct to contain all lookup/rights flags accepted by `wasi.path_open`
1692const WasiOpenOptions = struct {
1693 oflags: wasi.oflags_t,
1694 lookup_flags: wasi.lookupflags_t,
1695 fs_rights_base: wasi.rights_t,
1696 fs_rights_inheriting: wasi.rights_t,
1697 fs_flags: wasi.fdflags_t,
1698};
1699
1700/// Compute rights + flags corresponding to the provided POSIX access mode.
1701fn openOptionsFromFlagsWasi(oflag: O) OpenError!WasiOpenOptions {
1702 const w = std.os.wasi;
1703
1704 // Next, calculate the read/write rights to request, depending on the
1705 // provided POSIX access mode
1706 var rights: w.rights_t = .{};
1707 if (oflag.read) {
1708 rights.FD_READ = true;
1709 rights.FD_READDIR = true;
1710 }
1711 if (oflag.write) {
1712 rights.FD_DATASYNC = true;
1713 rights.FD_WRITE = true;
1714 rights.FD_ALLOCATE = true;
1715 rights.FD_FILESTAT_SET_SIZE = true;
1716 }
1717
1718 // https://github.com/ziglang/zig/issues/18882
1719 const flag_bits: u32 = @bitCast(oflag);
1720 const oflags_int: u16 = @as(u12, @truncate(flag_bits >> 12));
1721 const fs_flags_int: u16 = @as(u12, @truncate(flag_bits));
1722
1723 return .{
1724 // https://github.com/ziglang/zig/issues/18882
1725 .oflags = @bitCast(oflags_int),
1726 .lookup_flags = .{
1727 .SYMLINK_FOLLOW = !oflag.NOFOLLOW,
1728 },
1729 .fs_rights_base = rights,
1730 .fs_rights_inheriting = rights,
1731 // https://github.com/ziglang/zig/issues/18882
1732 .fs_flags = @bitCast(fs_flags_int),
1733 };
1734}
1735
1736/// Open and possibly create a file. Keeps trying if it gets interrupted.
1737/// `file_path` is relative to the open directory handle `dir_fd`.
1738/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
1739/// On WASI, `file_path` should be encoded as valid UTF-8.
1740/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
1741/// See also `openat`.
1742pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: O, mode: mode_t) OpenError!fd_t {
1743 if (native_os == .windows) {
1744 @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
1745 } else if (native_os == .wasi and !builtin.link_libc) {
1746 return openat(dir_fd, mem.sliceTo(file_path, 0), flags, mode);
1747 }
1748
1749 const openat_sym = if (lfs64_abi) system.openat64 else system.openat;
1750 while (true) {
1751 const rc = openat_sym(dir_fd, file_path, flags, mode);
1752 switch (errno(rc)) {
1753 .SUCCESS => return @intCast(rc),
1754 .INTR => continue,
1755
1756 .FAULT => unreachable,
1757 .INVAL => unreachable,
1758 .BADF => unreachable,
1759 .ACCES => return error.AccessDenied,
1760 .FBIG => return error.FileTooBig,
1761 .OVERFLOW => return error.FileTooBig,
1762 .ISDIR => return error.IsDir,
1763 .LOOP => return error.SymLinkLoop,
1764 .MFILE => return error.ProcessFdQuotaExceeded,
1765 .NAMETOOLONG => return error.NameTooLong,
1766 .NFILE => return error.SystemFdQuotaExceeded,
1767 .NODEV => return error.NoDevice,
1768 .NOENT => return error.FileNotFound,
1769 .NOMEM => return error.SystemResources,
1770 .NOSPC => return error.NoSpaceLeft,
1771 .NOTDIR => return error.NotDir,
1772 .PERM => return error.AccessDenied,
1773 .EXIST => return error.PathAlreadyExists,
1774 .BUSY => return error.DeviceBusy,
1775 .OPNOTSUPP => return error.FileLocksNotSupported,
1776 .AGAIN => return error.WouldBlock,
1777 .TXTBSY => return error.FileBusy,
1778 else => |err| return unexpectedErrno(err),
1779 }
1780 }
1781}
1782
1783pub fn dup(old_fd: fd_t) !fd_t {
1784 const rc = system.dup(old_fd);
1785 return switch (errno(rc)) {
1786 .SUCCESS => return @intCast(rc),
1787 .MFILE => error.ProcessFdQuotaExceeded,
1788 .BADF => unreachable, // invalid file descriptor
1789 else => |err| return unexpectedErrno(err),
1790 };
1791}
1792
1793pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
1794 while (true) {
1795 switch (errno(system.dup2(old_fd, new_fd))) {
1796 .SUCCESS => return,
1797 .BUSY, .INTR => continue,
1798 .MFILE => return error.ProcessFdQuotaExceeded,
1799 .INVAL => unreachable, // invalid parameters passed to dup2
1800 .BADF => unreachable, // invalid file descriptor
1801 else => |err| return unexpectedErrno(err),
1802 }
1803 }
1804}
1805
1806pub const ExecveError = error{
1807 SystemResources,
1808 AccessDenied,
1809 InvalidExe,
1810 FileSystem,
1811 IsDir,
1812 FileNotFound,
1813 NotDir,
1814 FileBusy,
1815 ProcessFdQuotaExceeded,
1816 SystemFdQuotaExceeded,
1817 NameTooLong,
1818} || UnexpectedError;
1819
1820/// This function ignores PATH environment variable. See `execvpeZ` for that.
1821pub fn execveZ(
1822 path: [*:0]const u8,
1823 child_argv: [*:null]const ?[*:0]const u8,
1824 envp: [*:null]const ?[*:0]const u8,
1825) ExecveError {
1826 switch (errno(system.execve(path, child_argv, envp))) {
1827 .SUCCESS => unreachable,
1828 .FAULT => unreachable,
1829 .@"2BIG" => return error.SystemResources,
1830 .MFILE => return error.ProcessFdQuotaExceeded,
1831 .NAMETOOLONG => return error.NameTooLong,
1832 .NFILE => return error.SystemFdQuotaExceeded,
1833 .NOMEM => return error.SystemResources,
1834 .ACCES => return error.AccessDenied,
1835 .PERM => return error.AccessDenied,
1836 .INVAL => return error.InvalidExe,
1837 .NOEXEC => return error.InvalidExe,
1838 .IO => return error.FileSystem,
1839 .LOOP => return error.FileSystem,
1840 .ISDIR => return error.IsDir,
1841 .NOENT => return error.FileNotFound,
1842 .NOTDIR => return error.NotDir,
1843 .TXTBSY => return error.FileBusy,
1844 else => |err| switch (native_os) {
1845 .macos, .ios, .tvos, .watchos => switch (err) {
1846 .BADEXEC => return error.InvalidExe,
1847 .BADARCH => return error.InvalidExe,
1848 else => return unexpectedErrno(err),
1849 },
1850 .linux => switch (err) {
1851 .LIBBAD => return error.InvalidExe,
1852 else => return unexpectedErrno(err),
1853 },
1854 else => return unexpectedErrno(err),
1855 },
1856 }
1857}
1858
1859pub const Arg0Expand = enum {
1860 expand,
1861 no_expand,
1862};
1863
1864/// Like `execvpeZ` except if `arg0_expand` is `.expand`, then `argv` is mutable,
1865/// and `argv[0]` is expanded to be the same absolute path that is passed to the execve syscall.
1866/// If this function returns with an error, `argv[0]` will be restored to the value it was when it was passed in.
1867pub fn execvpeZ_expandArg0(
1868 comptime arg0_expand: Arg0Expand,
1869 file: [*:0]const u8,
1870 child_argv: switch (arg0_expand) {
1871 .expand => [*:null]?[*:0]const u8,
1872 .no_expand => [*:null]const ?[*:0]const u8,
1873 },
1874 envp: [*:null]const ?[*:0]const u8,
1875) ExecveError {
1876 const file_slice = mem.sliceTo(file, 0);
1877 if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveZ(file, child_argv, envp);
1878
1879 const PATH = getenvZ("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
1880 // Use of PATH_MAX here is valid as the path_buf will be passed
1881 // directly to the operating system in execveZ.
1882 var path_buf: [PATH_MAX]u8 = undefined;
1883 var it = mem.tokenizeScalar(u8, PATH, ':');
1884 var seen_eacces = false;
1885 var err: ExecveError = error.FileNotFound;
1886
1887 // In case of expanding arg0 we must put it back if we return with an error.
1888 const prev_arg0 = child_argv[0];
1889 defer switch (arg0_expand) {
1890 .expand => child_argv[0] = prev_arg0,
1891 .no_expand => {},
1892 };
1893
1894 while (it.next()) |search_path| {
1895 const path_len = search_path.len + file_slice.len + 1;
1896 if (path_buf.len < path_len + 1) return error.NameTooLong;
1897 @memcpy(path_buf[0..search_path.len], search_path);
1898 path_buf[search_path.len] = '/';
1899 @memcpy(path_buf[search_path.len + 1 ..][0..file_slice.len], file_slice);
1900 path_buf[path_len] = 0;
1901 const full_path = path_buf[0..path_len :0].ptr;
1902 switch (arg0_expand) {
1903 .expand => child_argv[0] = full_path,
1904 .no_expand => {},
1905 }
1906 err = execveZ(full_path, child_argv, envp);
1907 switch (err) {
1908 error.AccessDenied => seen_eacces = true,
1909 error.FileNotFound, error.NotDir => {},
1910 else => |e| return e,
1911 }
1912 }
1913 if (seen_eacces) return error.AccessDenied;
1914 return err;
1915}
1916
1917/// This function also uses the PATH environment variable to get the full path to the executable.
1918/// If `file` is an absolute path, this is the same as `execveZ`.
1919pub fn execvpeZ(
1920 file: [*:0]const u8,
1921 argv_ptr: [*:null]const ?[*:0]const u8,
1922 envp: [*:null]const ?[*:0]const u8,
1923) ExecveError {
1924 return execvpeZ_expandArg0(.no_expand, file, argv_ptr, envp);
1925}
1926
1927/// Get an environment variable.
1928/// See also `getenvZ`.
1929pub fn getenv(key: []const u8) ?[:0]const u8 {
1930 if (native_os == .windows) {
1931 @compileError("std.os.getenv is unavailable for Windows because environment strings are in WTF-16 format. See std.process.getEnvVarOwned for a cross-platform API or std.process.getenvW for a Windows-specific API.");
1932 }
1933 if (builtin.link_libc) {
1934 var ptr = std.c.environ;
1935 while (ptr[0]) |line| : (ptr += 1) {
1936 var line_i: usize = 0;
1937 while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
1938 const this_key = line[0..line_i];
1939
1940 if (!mem.eql(u8, this_key, key)) continue;
1941
1942 return mem.sliceTo(line + line_i + 1, 0);
1943 }
1944 return null;
1945 }
1946 if (native_os == .wasi) {
1947 @compileError("std.os.getenv is unavailable for WASI. See std.process.getEnvMap or std.process.getEnvVarOwned for a cross-platform API.");
1948 }
1949 // The simplified start logic doesn't populate environ.
1950 if (std.start.simplified_logic) return null;
1951 // TODO see https://github.com/ziglang/zig/issues/4524
1952 for (std.os.environ) |ptr| {
1953 var line_i: usize = 0;
1954 while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
1955 const this_key = ptr[0..line_i];
1956 if (!mem.eql(u8, key, this_key)) continue;
1957
1958 return mem.sliceTo(ptr + line_i + 1, 0);
1959 }
1960 return null;
1961}
1962
1963/// Get an environment variable with a null-terminated name.
1964/// See also `getenv`.
1965pub fn getenvZ(key: [*:0]const u8) ?[:0]const u8 {
1966 if (builtin.link_libc) {
1967 const value = system.getenv(key) orelse return null;
1968 return mem.sliceTo(value, 0);
1969 }
1970 if (native_os == .windows) {
1971 @compileError("std.os.getenvZ is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.process.getenvW for Windows-specific API.");
1972 }
1973 return getenv(mem.sliceTo(key, 0));
1974}
1975
1976pub const GetCwdError = error{
1977 NameTooLong,
1978 CurrentWorkingDirectoryUnlinked,
1979} || UnexpectedError;
1980
1981/// The result is a slice of out_buffer, indexed from 0.
1982pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
1983 if (native_os == .windows) {
1984 return windows.GetCurrentDirectory(out_buffer);
1985 } else if (native_os == .wasi and !builtin.link_libc) {
1986 const path = ".";
1987 if (out_buffer.len < path.len) return error.NameTooLong;
1988 const result = out_buffer[0..path.len];
1989 @memcpy(result, path);
1990 return result;
1991 }
1992
1993 const err: E = if (builtin.link_libc) err: {
1994 const c_err = if (std.c.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else std.c._errno().*;
1995 break :err @enumFromInt(c_err);
1996 } else err: {
1997 break :err errno(system.getcwd(out_buffer.ptr, out_buffer.len));
1998 };
1999 switch (err) {
2000 .SUCCESS => return mem.sliceTo(out_buffer, 0),
2001 .FAULT => unreachable,
2002 .INVAL => unreachable,
2003 .NOENT => return error.CurrentWorkingDirectoryUnlinked,
2004 .RANGE => return error.NameTooLong,
2005 else => return unexpectedErrno(err),
2006 }
2007}
2008
2009pub const SymLinkError = error{
2010 /// In WASI, this error may occur when the file descriptor does
2011 /// not hold the required rights to create a new symbolic link relative to it.
2012 AccessDenied,
2013 DiskQuota,
2014 PathAlreadyExists,
2015 FileSystem,
2016 SymLinkLoop,
2017 FileNotFound,
2018 SystemResources,
2019 NoSpaceLeft,
2020 ReadOnlyFileSystem,
2021 NotDir,
2022 NameTooLong,
2023
2024 /// WASI-only; file paths must be valid UTF-8.
2025 InvalidUtf8,
2026
2027 /// Windows-only; file paths provided by the user must be valid WTF-8.
2028 /// https://simonsapin.github.io/wtf-8/
2029 InvalidWtf8,
2030
2031 BadPathName,
2032} || UnexpectedError;
2033
2034/// Creates a symbolic link named `sym_link_path` which contains the string `target_path`.
2035/// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
2036/// one; the latter case is known as a dangling link.
2037/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2038/// On WASI, both paths should be encoded as valid UTF-8.
2039/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
2040/// If `sym_link_path` exists, it will not be overwritten.
2041/// See also `symlinkZ.
2042pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {
2043 if (native_os == .windows) {
2044 @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
2045 } else if (native_os == .wasi and !builtin.link_libc) {
2046 return symlinkat(target_path, wasi.AT.FDCWD, sym_link_path);
2047 }
2048 const target_path_c = try toPosixPath(target_path);
2049 const sym_link_path_c = try toPosixPath(sym_link_path);
2050 return symlinkZ(&target_path_c, &sym_link_path_c);
2051}
2052
2053/// This is the same as `symlink` except the parameters are null-terminated pointers.
2054/// See also `symlink`.
2055pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLinkError!void {
2056 if (native_os == .windows) {
2057 @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
2058 } else if (native_os == .wasi and !builtin.link_libc) {
2059 return symlinkatZ(target_path, fs.cwd().fd, sym_link_path);
2060 }
2061 switch (errno(system.symlink(target_path, sym_link_path))) {
2062 .SUCCESS => return,
2063 .FAULT => unreachable,
2064 .INVAL => unreachable,
2065 .ACCES => return error.AccessDenied,
2066 .PERM => return error.AccessDenied,
2067 .DQUOT => return error.DiskQuota,
2068 .EXIST => return error.PathAlreadyExists,
2069 .IO => return error.FileSystem,
2070 .LOOP => return error.SymLinkLoop,
2071 .NAMETOOLONG => return error.NameTooLong,
2072 .NOENT => return error.FileNotFound,
2073 .NOTDIR => return error.NotDir,
2074 .NOMEM => return error.SystemResources,
2075 .NOSPC => return error.NoSpaceLeft,
2076 .ROFS => return error.ReadOnlyFileSystem,
2077 .ILSEQ => |err| if (native_os == .wasi)
2078 return error.InvalidUtf8
2079 else
2080 return unexpectedErrno(err),
2081 else => |err| return unexpectedErrno(err),
2082 }
2083}
2084
2085/// Similar to `symlink`, however, creates a symbolic link named `sym_link_path` which contains the string
2086/// `target_path` **relative** to `newdirfd` directory handle.
2087/// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
2088/// one; the latter case is known as a dangling link.
2089/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2090/// On WASI, both paths should be encoded as valid UTF-8.
2091/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
2092/// If `sym_link_path` exists, it will not be overwritten.
2093/// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`.
2094pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
2095 if (native_os == .windows) {
2096 @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
2097 } else if (native_os == .wasi and !builtin.link_libc) {
2098 return symlinkatWasi(target_path, newdirfd, sym_link_path);
2099 }
2100 const target_path_c = try toPosixPath(target_path);
2101 const sym_link_path_c = try toPosixPath(sym_link_path);
2102 return symlinkatZ(&target_path_c, newdirfd, &sym_link_path_c);
2103}
2104
2105/// WASI-only. The same as `symlinkat` but targeting WASI.
2106/// See also `symlinkat`.
2107pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
2108 switch (wasi.path_symlink(target_path.ptr, target_path.len, newdirfd, sym_link_path.ptr, sym_link_path.len)) {
2109 .SUCCESS => {},
2110 .FAULT => unreachable,
2111 .INVAL => unreachable,
2112 .BADF => unreachable,
2113 .ACCES => return error.AccessDenied,
2114 .PERM => return error.AccessDenied,
2115 .DQUOT => return error.DiskQuota,
2116 .EXIST => return error.PathAlreadyExists,
2117 .IO => return error.FileSystem,
2118 .LOOP => return error.SymLinkLoop,
2119 .NAMETOOLONG => return error.NameTooLong,
2120 .NOENT => return error.FileNotFound,
2121 .NOTDIR => return error.NotDir,
2122 .NOMEM => return error.SystemResources,
2123 .NOSPC => return error.NoSpaceLeft,
2124 .ROFS => return error.ReadOnlyFileSystem,
2125 .NOTCAPABLE => return error.AccessDenied,
2126 .ILSEQ => return error.InvalidUtf8,
2127 else => |err| return unexpectedErrno(err),
2128 }
2129}
2130
2131/// The same as `symlinkat` except the parameters are null-terminated pointers.
2132/// See also `symlinkat`.
2133pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void {
2134 if (native_os == .windows) {
2135 @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
2136 } else if (native_os == .wasi and !builtin.link_libc) {
2137 return symlinkat(mem.sliceTo(target_path, 0), newdirfd, mem.sliceTo(sym_link_path, 0));
2138 }
2139 switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) {
2140 .SUCCESS => return,
2141 .FAULT => unreachable,
2142 .INVAL => unreachable,
2143 .ACCES => return error.AccessDenied,
2144 .PERM => return error.AccessDenied,
2145 .DQUOT => return error.DiskQuota,
2146 .EXIST => return error.PathAlreadyExists,
2147 .IO => return error.FileSystem,
2148 .LOOP => return error.SymLinkLoop,
2149 .NAMETOOLONG => return error.NameTooLong,
2150 .NOENT => return error.FileNotFound,
2151 .NOTDIR => return error.NotDir,
2152 .NOMEM => return error.SystemResources,
2153 .NOSPC => return error.NoSpaceLeft,
2154 .ROFS => return error.ReadOnlyFileSystem,
2155 .ILSEQ => |err| if (native_os == .wasi)
2156 return error.InvalidUtf8
2157 else
2158 return unexpectedErrno(err),
2159 else => |err| return unexpectedErrno(err),
2160 }
2161}
2162
2163pub const LinkError = UnexpectedError || error{
2164 AccessDenied,
2165 DiskQuota,
2166 PathAlreadyExists,
2167 FileSystem,
2168 SymLinkLoop,
2169 LinkQuotaExceeded,
2170 NameTooLong,
2171 FileNotFound,
2172 SystemResources,
2173 NoSpaceLeft,
2174 ReadOnlyFileSystem,
2175 NotSameFileSystem,
2176
2177 /// WASI-only; file paths must be valid UTF-8.
2178 InvalidUtf8,
2179};
2180
2181/// On WASI, both paths should be encoded as valid UTF-8.
2182/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
2183pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) LinkError!void {
2184 if (native_os == .wasi and !builtin.link_libc) {
2185 return link(mem.sliceTo(oldpath, 0), mem.sliceTo(newpath, 0), flags);
2186 }
2187 switch (errno(system.link(oldpath, newpath, flags))) {
2188 .SUCCESS => return,
2189 .ACCES => return error.AccessDenied,
2190 .DQUOT => return error.DiskQuota,
2191 .EXIST => return error.PathAlreadyExists,
2192 .FAULT => unreachable,
2193 .IO => return error.FileSystem,
2194 .LOOP => return error.SymLinkLoop,
2195 .MLINK => return error.LinkQuotaExceeded,
2196 .NAMETOOLONG => return error.NameTooLong,
2197 .NOENT => return error.FileNotFound,
2198 .NOMEM => return error.SystemResources,
2199 .NOSPC => return error.NoSpaceLeft,
2200 .PERM => return error.AccessDenied,
2201 .ROFS => return error.ReadOnlyFileSystem,
2202 .XDEV => return error.NotSameFileSystem,
2203 .INVAL => unreachable,
2204 .ILSEQ => |err| if (native_os == .wasi)
2205 return error.InvalidUtf8
2206 else
2207 return unexpectedErrno(err),
2208 else => |err| return unexpectedErrno(err),
2209 }
2210}
2211
2212/// On WASI, both paths should be encoded as valid UTF-8.
2213/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
2214pub fn link(oldpath: []const u8, newpath: []const u8, flags: i32) LinkError!void {
2215 if (native_os == .wasi and !builtin.link_libc) {
2216 return linkat(wasi.AT.FDCWD, oldpath, wasi.AT.FDCWD, newpath, flags) catch |err| switch (err) {
2217 error.NotDir => unreachable, // link() does not support directories
2218 else => |e| return e,
2219 };
2220 }
2221 const old = try toPosixPath(oldpath);
2222 const new = try toPosixPath(newpath);
2223 return try linkZ(&old, &new, flags);
2224}
2225
2226pub const LinkatError = LinkError || error{NotDir};
2227
2228/// On WASI, both paths should be encoded as valid UTF-8.
2229/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
2230pub fn linkatZ(
2231 olddir: fd_t,
2232 oldpath: [*:0]const u8,
2233 newdir: fd_t,
2234 newpath: [*:0]const u8,
2235 flags: i32,
2236) LinkatError!void {
2237 if (native_os == .wasi and !builtin.link_libc) {
2238 return linkat(olddir, mem.sliceTo(oldpath, 0), newdir, mem.sliceTo(newpath, 0), flags);
2239 }
2240 switch (errno(system.linkat(olddir, oldpath, newdir, newpath, flags))) {
2241 .SUCCESS => return,
2242 .ACCES => return error.AccessDenied,
2243 .DQUOT => return error.DiskQuota,
2244 .EXIST => return error.PathAlreadyExists,
2245 .FAULT => unreachable,
2246 .IO => return error.FileSystem,
2247 .LOOP => return error.SymLinkLoop,
2248 .MLINK => return error.LinkQuotaExceeded,
2249 .NAMETOOLONG => return error.NameTooLong,
2250 .NOENT => return error.FileNotFound,
2251 .NOMEM => return error.SystemResources,
2252 .NOSPC => return error.NoSpaceLeft,
2253 .NOTDIR => return error.NotDir,
2254 .PERM => return error.AccessDenied,
2255 .ROFS => return error.ReadOnlyFileSystem,
2256 .XDEV => return error.NotSameFileSystem,
2257 .INVAL => unreachable,
2258 .ILSEQ => |err| if (native_os == .wasi)
2259 return error.InvalidUtf8
2260 else
2261 return unexpectedErrno(err),
2262 else => |err| return unexpectedErrno(err),
2263 }
2264}
2265
2266/// On WASI, both paths should be encoded as valid UTF-8.
2267/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
2268pub fn linkat(
2269 olddir: fd_t,
2270 oldpath: []const u8,
2271 newdir: fd_t,
2272 newpath: []const u8,
2273 flags: i32,
2274) LinkatError!void {
2275 if (native_os == .wasi and !builtin.link_libc) {
2276 const old: RelativePathWasi = .{ .dir_fd = olddir, .relative_path = oldpath };
2277 const new: RelativePathWasi = .{ .dir_fd = newdir, .relative_path = newpath };
2278 const old_flags: wasi.lookupflags_t = .{
2279 .SYMLINK_FOLLOW = (flags & AT.SYMLINK_FOLLOW) != 0,
2280 };
2281 switch (wasi.path_link(
2282 old.dir_fd,
2283 old_flags,
2284 old.relative_path.ptr,
2285 old.relative_path.len,
2286 new.dir_fd,
2287 new.relative_path.ptr,
2288 new.relative_path.len,
2289 )) {
2290 .SUCCESS => return,
2291 .ACCES => return error.AccessDenied,
2292 .DQUOT => return error.DiskQuota,
2293 .EXIST => return error.PathAlreadyExists,
2294 .FAULT => unreachable,
2295 .IO => return error.FileSystem,
2296 .LOOP => return error.SymLinkLoop,
2297 .MLINK => return error.LinkQuotaExceeded,
2298 .NAMETOOLONG => return error.NameTooLong,
2299 .NOENT => return error.FileNotFound,
2300 .NOMEM => return error.SystemResources,
2301 .NOSPC => return error.NoSpaceLeft,
2302 .NOTDIR => return error.NotDir,
2303 .PERM => return error.AccessDenied,
2304 .ROFS => return error.ReadOnlyFileSystem,
2305 .XDEV => return error.NotSameFileSystem,
2306 .INVAL => unreachable,
2307 .ILSEQ => return error.InvalidUtf8,
2308 else => |err| return unexpectedErrno(err),
2309 }
2310 }
2311 const old = try toPosixPath(oldpath);
2312 const new = try toPosixPath(newpath);
2313 return try linkatZ(olddir, &old, newdir, &new, flags);
2314}
2315
2316pub const UnlinkError = error{
2317 FileNotFound,
2318
2319 /// In WASI, this error may occur when the file descriptor does
2320 /// not hold the required rights to unlink a resource by path relative to it.
2321 AccessDenied,
2322 FileBusy,
2323 FileSystem,
2324 IsDir,
2325 SymLinkLoop,
2326 NameTooLong,
2327 NotDir,
2328 SystemResources,
2329 ReadOnlyFileSystem,
2330
2331 /// WASI-only; file paths must be valid UTF-8.
2332 InvalidUtf8,
2333
2334 /// Windows-only; file paths provided by the user must be valid WTF-8.
2335 /// https://simonsapin.github.io/wtf-8/
2336 InvalidWtf8,
2337
2338 /// On Windows, file paths cannot contain these characters:
2339 /// '/', '*', '?', '"', '<', '>', '|'
2340 BadPathName,
2341
2342 /// On Windows, `\\server` or `\\server\share` was not found.
2343 NetworkNotFound,
2344} || UnexpectedError;
2345
2346/// Delete a name and possibly the file it refers to.
2347/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2348/// On WASI, `file_path` should be encoded as valid UTF-8.
2349/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
2350/// See also `unlinkZ`.
2351pub fn unlink(file_path: []const u8) UnlinkError!void {
2352 if (native_os == .wasi and !builtin.link_libc) {
2353 return unlinkat(wasi.AT.FDCWD, file_path, 0) catch |err| switch (err) {
2354 error.DirNotEmpty => unreachable, // only occurs when targeting directories
2355 else => |e| return e,
2356 };
2357 } else if (native_os == .windows) {
2358 const file_path_w = try windows.sliceToPrefixedFileW(null, file_path);
2359 return unlinkW(file_path_w.span());
2360 } else {
2361 const file_path_c = try toPosixPath(file_path);
2362 return unlinkZ(&file_path_c);
2363 }
2364}
2365
2366/// Same as `unlink` except the parameter is null terminated.
2367pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {
2368 if (native_os == .windows) {
2369 const file_path_w = try windows.cStrToPrefixedFileW(null, file_path);
2370 return unlinkW(file_path_w.span());
2371 } else if (native_os == .wasi and !builtin.link_libc) {
2372 return unlink(mem.sliceTo(file_path, 0));
2373 }
2374 switch (errno(system.unlink(file_path))) {
2375 .SUCCESS => return,
2376 .ACCES => return error.AccessDenied,
2377 .PERM => return error.AccessDenied,
2378 .BUSY => return error.FileBusy,
2379 .FAULT => unreachable,
2380 .INVAL => unreachable,
2381 .IO => return error.FileSystem,
2382 .ISDIR => return error.IsDir,
2383 .LOOP => return error.SymLinkLoop,
2384 .NAMETOOLONG => return error.NameTooLong,
2385 .NOENT => return error.FileNotFound,
2386 .NOTDIR => return error.NotDir,
2387 .NOMEM => return error.SystemResources,
2388 .ROFS => return error.ReadOnlyFileSystem,
2389 .ILSEQ => |err| if (native_os == .wasi)
2390 return error.InvalidUtf8
2391 else
2392 return unexpectedErrno(err),
2393 else => |err| return unexpectedErrno(err),
2394 }
2395}
2396
2397/// Windows-only. Same as `unlink` except the parameter is null-terminated, WTF16 LE encoded.
2398pub fn unlinkW(file_path_w: []const u16) UnlinkError!void {
2399 windows.DeleteFile(file_path_w, .{ .dir = fs.cwd().fd }) catch |err| switch (err) {
2400 error.DirNotEmpty => unreachable, // we're not passing .remove_dir = true
2401 else => |e| return e,
2402 };
2403}
2404
2405pub const UnlinkatError = UnlinkError || error{
2406 /// When passing `AT.REMOVEDIR`, this error occurs when the named directory is not empty.
2407 DirNotEmpty,
2408};
2409
2410/// Delete a file name and possibly the file it refers to, based on an open directory handle.
2411/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2412/// On WASI, `file_path` should be encoded as valid UTF-8.
2413/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
2414/// Asserts that the path parameter has no null bytes.
2415pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
2416 if (native_os == .windows) {
2417 const file_path_w = try windows.sliceToPrefixedFileW(dirfd, file_path);
2418 return unlinkatW(dirfd, file_path_w.span(), flags);
2419 } else if (native_os == .wasi and !builtin.link_libc) {
2420 return unlinkatWasi(dirfd, file_path, flags);
2421 } else {
2422 const file_path_c = try toPosixPath(file_path);
2423 return unlinkatZ(dirfd, &file_path_c, flags);
2424 }
2425}
2426
2427/// WASI-only. Same as `unlinkat` but targeting WASI.
2428/// See also `unlinkat`.
2429pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
2430 const remove_dir = (flags & AT.REMOVEDIR) != 0;
2431 const res = if (remove_dir)
2432 wasi.path_remove_directory(dirfd, file_path.ptr, file_path.len)
2433 else
2434 wasi.path_unlink_file(dirfd, file_path.ptr, file_path.len);
2435 switch (res) {
2436 .SUCCESS => return,
2437 .ACCES => return error.AccessDenied,
2438 .PERM => return error.AccessDenied,
2439 .BUSY => return error.FileBusy,
2440 .FAULT => unreachable,
2441 .IO => return error.FileSystem,
2442 .ISDIR => return error.IsDir,
2443 .LOOP => return error.SymLinkLoop,
2444 .NAMETOOLONG => return error.NameTooLong,
2445 .NOENT => return error.FileNotFound,
2446 .NOTDIR => return error.NotDir,
2447 .NOMEM => return error.SystemResources,
2448 .ROFS => return error.ReadOnlyFileSystem,
2449 .NOTEMPTY => return error.DirNotEmpty,
2450 .NOTCAPABLE => return error.AccessDenied,
2451 .ILSEQ => return error.InvalidUtf8,
2452
2453 .INVAL => unreachable, // invalid flags, or pathname has . as last component
2454 .BADF => unreachable, // always a race condition
2455
2456 else => |err| return unexpectedErrno(err),
2457 }
2458}
2459
2460/// Same as `unlinkat` but `file_path` is a null-terminated string.
2461pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void {
2462 if (native_os == .windows) {
2463 const file_path_w = try windows.cStrToPrefixedFileW(dirfd, file_path_c);
2464 return unlinkatW(dirfd, file_path_w.span(), flags);
2465 } else if (native_os == .wasi and !builtin.link_libc) {
2466 return unlinkat(dirfd, mem.sliceTo(file_path_c, 0), flags);
2467 }
2468 switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {
2469 .SUCCESS => return,
2470 .ACCES => return error.AccessDenied,
2471 .PERM => return error.AccessDenied,
2472 .BUSY => return error.FileBusy,
2473 .FAULT => unreachable,
2474 .IO => return error.FileSystem,
2475 .ISDIR => return error.IsDir,
2476 .LOOP => return error.SymLinkLoop,
2477 .NAMETOOLONG => return error.NameTooLong,
2478 .NOENT => return error.FileNotFound,
2479 .NOTDIR => return error.NotDir,
2480 .NOMEM => return error.SystemResources,
2481 .ROFS => return error.ReadOnlyFileSystem,
2482 .EXIST => return error.DirNotEmpty,
2483 .NOTEMPTY => return error.DirNotEmpty,
2484 .ILSEQ => |err| if (native_os == .wasi)
2485 return error.InvalidUtf8
2486 else
2487 return unexpectedErrno(err),
2488
2489 .INVAL => unreachable, // invalid flags, or pathname has . as last component
2490 .BADF => unreachable, // always a race condition
2491
2492 else => |err| return unexpectedErrno(err),
2493 }
2494}
2495
2496/// Same as `unlinkat` but `sub_path_w` is WTF16LE, NT prefixed. Windows only.
2497pub fn unlinkatW(dirfd: fd_t, sub_path_w: []const u16, flags: u32) UnlinkatError!void {
2498 const remove_dir = (flags & AT.REMOVEDIR) != 0;
2499 return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir });
2500}
2501
2502pub const RenameError = error{
2503 /// In WASI, this error may occur when the file descriptor does
2504 /// not hold the required rights to rename a resource by path relative to it.
2505 ///
2506 /// On Windows, this error may be returned instead of PathAlreadyExists when
2507 /// renaming a directory over an existing directory.
2508 AccessDenied,
2509 FileBusy,
2510 DiskQuota,
2511 IsDir,
2512 SymLinkLoop,
2513 LinkQuotaExceeded,
2514 NameTooLong,
2515 FileNotFound,
2516 NotDir,
2517 SystemResources,
2518 NoSpaceLeft,
2519 PathAlreadyExists,
2520 ReadOnlyFileSystem,
2521 RenameAcrossMountPoints,
2522 /// WASI-only; file paths must be valid UTF-8.
2523 InvalidUtf8,
2524 /// Windows-only; file paths provided by the user must be valid WTF-8.
2525 /// https://simonsapin.github.io/wtf-8/
2526 InvalidWtf8,
2527 BadPathName,
2528 NoDevice,
2529 SharingViolation,
2530 PipeBusy,
2531 /// On Windows, `\\server` or `\\server\share` was not found.
2532 NetworkNotFound,
2533 /// On Windows, antivirus software is enabled by default. It can be
2534 /// disabled, but Windows Update sometimes ignores the user's preference
2535 /// and re-enables it. When enabled, antivirus software on Windows
2536 /// intercepts file system operations and makes them significantly slower
2537 /// in addition to possibly failing with this error code.
2538 AntivirusInterference,
2539} || UnexpectedError;
2540
2541/// Change the name or location of a file.
2542/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2543/// On WASI, both paths should be encoded as valid UTF-8.
2544/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
2545pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
2546 if (native_os == .wasi and !builtin.link_libc) {
2547 return renameat(wasi.AT.FDCWD, old_path, wasi.AT.FDCWD, new_path);
2548 } else if (native_os == .windows) {
2549 const old_path_w = try windows.sliceToPrefixedFileW(null, old_path);
2550 const new_path_w = try windows.sliceToPrefixedFileW(null, new_path);
2551 return renameW(old_path_w.span().ptr, new_path_w.span().ptr);
2552 } else {
2553 const old_path_c = try toPosixPath(old_path);
2554 const new_path_c = try toPosixPath(new_path);
2555 return renameZ(&old_path_c, &new_path_c);
2556 }
2557}
2558
2559/// Same as `rename` except the parameters are null-terminated.
2560pub fn renameZ(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!void {
2561 if (native_os == .windows) {
2562 const old_path_w = try windows.cStrToPrefixedFileW(null, old_path);
2563 const new_path_w = try windows.cStrToPrefixedFileW(null, new_path);
2564 return renameW(old_path_w.span().ptr, new_path_w.span().ptr);
2565 } else if (native_os == .wasi and !builtin.link_libc) {
2566 return rename(mem.sliceTo(old_path, 0), mem.sliceTo(new_path, 0));
2567 }
2568 switch (errno(system.rename(old_path, new_path))) {
2569 .SUCCESS => return,
2570 .ACCES => return error.AccessDenied,
2571 .PERM => return error.AccessDenied,
2572 .BUSY => return error.FileBusy,
2573 .DQUOT => return error.DiskQuota,
2574 .FAULT => unreachable,
2575 .INVAL => unreachable,
2576 .ISDIR => return error.IsDir,
2577 .LOOP => return error.SymLinkLoop,
2578 .MLINK => return error.LinkQuotaExceeded,
2579 .NAMETOOLONG => return error.NameTooLong,
2580 .NOENT => return error.FileNotFound,
2581 .NOTDIR => return error.NotDir,
2582 .NOMEM => return error.SystemResources,
2583 .NOSPC => return error.NoSpaceLeft,
2584 .EXIST => return error.PathAlreadyExists,
2585 .NOTEMPTY => return error.PathAlreadyExists,
2586 .ROFS => return error.ReadOnlyFileSystem,
2587 .XDEV => return error.RenameAcrossMountPoints,
2588 .ILSEQ => |err| if (native_os == .wasi)
2589 return error.InvalidUtf8
2590 else
2591 return unexpectedErrno(err),
2592 else => |err| return unexpectedErrno(err),
2593 }
2594}
2595
2596/// Same as `rename` except the parameters are null-terminated and WTF16LE encoded.
2597/// Assumes target is Windows.
2598pub fn renameW(old_path: [*:0]const u16, new_path: [*:0]const u16) RenameError!void {
2599 const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
2600 return windows.MoveFileExW(old_path, new_path, flags);
2601}
2602
2603/// Change the name or location of a file based on an open directory handle.
2604/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2605/// On WASI, both paths should be encoded as valid UTF-8.
2606/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
2607pub fn renameat(
2608 old_dir_fd: fd_t,
2609 old_path: []const u8,
2610 new_dir_fd: fd_t,
2611 new_path: []const u8,
2612) RenameError!void {
2613 if (native_os == .windows) {
2614 const old_path_w = try windows.sliceToPrefixedFileW(old_dir_fd, old_path);
2615 const new_path_w = try windows.sliceToPrefixedFileW(new_dir_fd, new_path);
2616 return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
2617 } else if (native_os == .wasi and !builtin.link_libc) {
2618 const old: RelativePathWasi = .{ .dir_fd = old_dir_fd, .relative_path = old_path };
2619 const new: RelativePathWasi = .{ .dir_fd = new_dir_fd, .relative_path = new_path };
2620 return renameatWasi(old, new);
2621 } else {
2622 const old_path_c = try toPosixPath(old_path);
2623 const new_path_c = try toPosixPath(new_path);
2624 return renameatZ(old_dir_fd, &old_path_c, new_dir_fd, &new_path_c);
2625 }
2626}
2627
2628/// WASI-only. Same as `renameat` expect targeting WASI.
2629/// See also `renameat`.
2630fn renameatWasi(old: RelativePathWasi, new: RelativePathWasi) RenameError!void {
2631 switch (wasi.path_rename(old.dir_fd, old.relative_path.ptr, old.relative_path.len, new.dir_fd, new.relative_path.ptr, new.relative_path.len)) {
2632 .SUCCESS => return,
2633 .ACCES => return error.AccessDenied,
2634 .PERM => return error.AccessDenied,
2635 .BUSY => return error.FileBusy,
2636 .DQUOT => return error.DiskQuota,
2637 .FAULT => unreachable,
2638 .INVAL => unreachable,
2639 .ISDIR => return error.IsDir,
2640 .LOOP => return error.SymLinkLoop,
2641 .MLINK => return error.LinkQuotaExceeded,
2642 .NAMETOOLONG => return error.NameTooLong,
2643 .NOENT => return error.FileNotFound,
2644 .NOTDIR => return error.NotDir,
2645 .NOMEM => return error.SystemResources,
2646 .NOSPC => return error.NoSpaceLeft,
2647 .EXIST => return error.PathAlreadyExists,
2648 .NOTEMPTY => return error.PathAlreadyExists,
2649 .ROFS => return error.ReadOnlyFileSystem,
2650 .XDEV => return error.RenameAcrossMountPoints,
2651 .NOTCAPABLE => return error.AccessDenied,
2652 .ILSEQ => return error.InvalidUtf8,
2653 else => |err| return unexpectedErrno(err),
2654 }
2655}
2656
2657/// An fd-relative file path
2658///
2659/// This is currently only used for WASI-specific functionality, but the concept
2660/// is the same as the dirfd/pathname pairs in the `*at(...)` POSIX functions.
2661const RelativePathWasi = struct {
2662 /// Handle to directory
2663 dir_fd: fd_t,
2664 /// Path to resource within `dir_fd`.
2665 relative_path: []const u8,
2666};
2667
2668/// Same as `renameat` except the parameters are null-terminated.
2669pub fn renameatZ(
2670 old_dir_fd: fd_t,
2671 old_path: [*:0]const u8,
2672 new_dir_fd: fd_t,
2673 new_path: [*:0]const u8,
2674) RenameError!void {
2675 if (native_os == .windows) {
2676 const old_path_w = try windows.cStrToPrefixedFileW(old_dir_fd, old_path);
2677 const new_path_w = try windows.cStrToPrefixedFileW(new_dir_fd, new_path);
2678 return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
2679 } else if (native_os == .wasi and !builtin.link_libc) {
2680 return renameat(old_dir_fd, mem.sliceTo(old_path, 0), new_dir_fd, mem.sliceTo(new_path, 0));
2681 }
2682
2683 switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) {
2684 .SUCCESS => return,
2685 .ACCES => return error.AccessDenied,
2686 .PERM => return error.AccessDenied,
2687 .BUSY => return error.FileBusy,
2688 .DQUOT => return error.DiskQuota,
2689 .FAULT => unreachable,
2690 .INVAL => unreachable,
2691 .ISDIR => return error.IsDir,
2692 .LOOP => return error.SymLinkLoop,
2693 .MLINK => return error.LinkQuotaExceeded,
2694 .NAMETOOLONG => return error.NameTooLong,
2695 .NOENT => return error.FileNotFound,
2696 .NOTDIR => return error.NotDir,
2697 .NOMEM => return error.SystemResources,
2698 .NOSPC => return error.NoSpaceLeft,
2699 .EXIST => return error.PathAlreadyExists,
2700 .NOTEMPTY => return error.PathAlreadyExists,
2701 .ROFS => return error.ReadOnlyFileSystem,
2702 .XDEV => return error.RenameAcrossMountPoints,
2703 .ILSEQ => |err| if (native_os == .wasi)
2704 return error.InvalidUtf8
2705 else
2706 return unexpectedErrno(err),
2707 else => |err| return unexpectedErrno(err),
2708 }
2709}
2710
2711/// Same as `renameat` but Windows-only and the path parameters are
2712/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
2713pub fn renameatW(
2714 old_dir_fd: fd_t,
2715 old_path_w: []const u16,
2716 new_dir_fd: fd_t,
2717 new_path_w: []const u16,
2718 ReplaceIfExists: windows.BOOLEAN,
2719) RenameError!void {
2720 const src_fd = windows.OpenFile(old_path_w, .{
2721 .dir = old_dir_fd,
2722 .access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE,
2723 .creation = windows.FILE_OPEN,
2724 .filter = .any, // This function is supposed to rename both files and directories.
2725 .follow_symlinks = false,
2726 }) catch |err| switch (err) {
2727 error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`.
2728 else => |e| return e,
2729 };
2730 defer windows.CloseHandle(src_fd);
2731
2732 var need_fallback = true;
2733 var rc: windows.NTSTATUS = undefined;
2734 // FILE_RENAME_INFORMATION_EX and FILE_RENAME_POSIX_SEMANTICS require >= win10_rs1,
2735 // but FILE_RENAME_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5. We check >= rs5 here
2736 // so that we only use POSIX_SEMANTICS when we know IGNORE_READONLY_ATTRIBUTE will also be
2737 // supported in order to avoid either (1) using a redundant call that we can know in advance will return
2738 // STATUS_NOT_SUPPORTED or (2) only setting IGNORE_READONLY_ATTRIBUTE when >= rs5
2739 // and therefore having different behavior when the Windows version is >= rs1 but < rs5.
2740 if (builtin.target.os.isAtLeast(.windows, .win10_rs5) orelse false) {
2741 const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION_EX) + (max_path_bytes - 1);
2742 var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION_EX)) = undefined;
2743 const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION_EX) - 1 + new_path_w.len * 2;
2744 if (struct_len > struct_buf_len) return error.NameTooLong;
2745
2746 const rename_info: *windows.FILE_RENAME_INFORMATION_EX = @ptrCast(&rename_info_buf);
2747 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
2748
2749 var flags: windows.ULONG = windows.FILE_RENAME_POSIX_SEMANTICS | windows.FILE_RENAME_IGNORE_READONLY_ATTRIBUTE;
2750 if (ReplaceIfExists == windows.TRUE) flags |= windows.FILE_RENAME_REPLACE_IF_EXISTS;
2751 rename_info.* = .{
2752 .Flags = flags,
2753 .RootDirectory = if (fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd,
2754 .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong
2755 .FileName = undefined,
2756 };
2757 @memcpy((&rename_info.FileName).ptr, new_path_w);
2758 rc = windows.ntdll.NtSetInformationFile(
2759 src_fd,
2760 &io_status_block,
2761 rename_info,
2762 @intCast(struct_len), // already checked for error.NameTooLong
2763 .FileRenameInformationEx,
2764 );
2765 switch (rc) {
2766 .SUCCESS => return,
2767 // INVALID_PARAMETER here means that the filesystem does not support FileRenameInformationEx
2768 .INVALID_PARAMETER => {},
2769 .DIRECTORY_NOT_EMPTY => return error.PathAlreadyExists,
2770 .FILE_IS_A_DIRECTORY => return error.IsDir,
2771 .NOT_A_DIRECTORY => return error.NotDir,
2772 // For all other statuses, fall down to the switch below to handle them.
2773 else => need_fallback = false,
2774 }
2775 }
2776
2777 if (need_fallback) {
2778 const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (max_path_bytes - 1);
2779 var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION)) = undefined;
2780 const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path_w.len * 2;
2781 if (struct_len > struct_buf_len) return error.NameTooLong;
2782
2783 const rename_info: *windows.FILE_RENAME_INFORMATION = @ptrCast(&rename_info_buf);
2784 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
2785
2786 rename_info.* = .{
2787 .Flags = ReplaceIfExists,
2788 .RootDirectory = if (fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd,
2789 .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong
2790 .FileName = undefined,
2791 };
2792 @memcpy((&rename_info.FileName).ptr, new_path_w);
2793
2794 rc =
2795 windows.ntdll.NtSetInformationFile(
2796 src_fd,
2797 &io_status_block,
2798 rename_info,
2799 @intCast(struct_len), // already checked for error.NameTooLong
2800 .FileRenameInformation,
2801 );
2802 }
2803
2804 switch (rc) {
2805 .SUCCESS => {},
2806 .INVALID_HANDLE => unreachable,
2807 .INVALID_PARAMETER => unreachable,
2808 .OBJECT_PATH_SYNTAX_BAD => unreachable,
2809 .ACCESS_DENIED => return error.AccessDenied,
2810 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
2811 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
2812 .NOT_SAME_DEVICE => return error.RenameAcrossMountPoints,
2813 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
2814 else => return windows.unexpectedStatus(rc),
2815 }
2816}
2817
2818/// On Windows, `sub_dir_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2819/// On WASI, `sub_dir_path` should be encoded as valid UTF-8.
2820/// On other platforms, `sub_dir_path` is an opaque sequence of bytes with no particular encoding.
2821pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
2822 if (native_os == .windows) {
2823 const sub_dir_path_w = try windows.sliceToPrefixedFileW(dir_fd, sub_dir_path);
2824 return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
2825 } else if (native_os == .wasi and !builtin.link_libc) {
2826 return mkdiratWasi(dir_fd, sub_dir_path, mode);
2827 } else {
2828 const sub_dir_path_c = try toPosixPath(sub_dir_path);
2829 return mkdiratZ(dir_fd, &sub_dir_path_c, mode);
2830 }
2831}
2832
2833pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
2834 _ = mode;
2835 switch (wasi.path_create_directory(dir_fd, sub_dir_path.ptr, sub_dir_path.len)) {
2836 .SUCCESS => return,
2837 .ACCES => return error.AccessDenied,
2838 .BADF => unreachable,
2839 .PERM => return error.AccessDenied,
2840 .DQUOT => return error.DiskQuota,
2841 .EXIST => return error.PathAlreadyExists,
2842 .FAULT => unreachable,
2843 .LOOP => return error.SymLinkLoop,
2844 .MLINK => return error.LinkQuotaExceeded,
2845 .NAMETOOLONG => return error.NameTooLong,
2846 .NOENT => return error.FileNotFound,
2847 .NOMEM => return error.SystemResources,
2848 .NOSPC => return error.NoSpaceLeft,
2849 .NOTDIR => return error.NotDir,
2850 .ROFS => return error.ReadOnlyFileSystem,
2851 .NOTCAPABLE => return error.AccessDenied,
2852 .ILSEQ => return error.InvalidUtf8,
2853 else => |err| return unexpectedErrno(err),
2854 }
2855}
2856
2857/// Same as `mkdirat` except the parameters are null-terminated.
2858pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
2859 if (native_os == .windows) {
2860 const sub_dir_path_w = try windows.cStrToPrefixedFileW(dir_fd, sub_dir_path);
2861 return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
2862 } else if (native_os == .wasi and !builtin.link_libc) {
2863 return mkdirat(dir_fd, mem.sliceTo(sub_dir_path, 0), mode);
2864 }
2865 switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) {
2866 .SUCCESS => return,
2867 .ACCES => return error.AccessDenied,
2868 .BADF => unreachable,
2869 .PERM => return error.AccessDenied,
2870 .DQUOT => return error.DiskQuota,
2871 .EXIST => return error.PathAlreadyExists,
2872 .FAULT => unreachable,
2873 .LOOP => return error.SymLinkLoop,
2874 .MLINK => return error.LinkQuotaExceeded,
2875 .NAMETOOLONG => return error.NameTooLong,
2876 .NOENT => return error.FileNotFound,
2877 .NOMEM => return error.SystemResources,
2878 .NOSPC => return error.NoSpaceLeft,
2879 .NOTDIR => return error.NotDir,
2880 .ROFS => return error.ReadOnlyFileSystem,
2881 // dragonfly: when dir_fd is unlinked from filesystem
2882 .NOTCONN => return error.FileNotFound,
2883 .ILSEQ => |err| if (native_os == .wasi)
2884 return error.InvalidUtf8
2885 else
2886 return unexpectedErrno(err),
2887 else => |err| return unexpectedErrno(err),
2888 }
2889}
2890
2891/// Windows-only. Same as `mkdirat` except the parameter WTF16 LE encoded.
2892pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!void {
2893 _ = mode;
2894 const sub_dir_handle = windows.OpenFile(sub_path_w, .{
2895 .dir = dir_fd,
2896 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
2897 .creation = windows.FILE_CREATE,
2898 .filter = .dir_only,
2899 }) catch |err| switch (err) {
2900 error.IsDir => return error.Unexpected,
2901 error.PipeBusy => return error.Unexpected,
2902 error.WouldBlock => return error.Unexpected,
2903 error.AntivirusInterference => return error.Unexpected,
2904 else => |e| return e,
2905 };
2906 windows.CloseHandle(sub_dir_handle);
2907}
2908
2909pub const MakeDirError = error{
2910 /// In WASI, this error may occur when the file descriptor does
2911 /// not hold the required rights to create a new directory relative to it.
2912 AccessDenied,
2913 DiskQuota,
2914 PathAlreadyExists,
2915 SymLinkLoop,
2916 LinkQuotaExceeded,
2917 NameTooLong,
2918 FileNotFound,
2919 SystemResources,
2920 NoSpaceLeft,
2921 NotDir,
2922 ReadOnlyFileSystem,
2923 /// WASI-only; file paths must be valid UTF-8.
2924 InvalidUtf8,
2925 /// Windows-only; file paths provided by the user must be valid WTF-8.
2926 /// https://simonsapin.github.io/wtf-8/
2927 InvalidWtf8,
2928 BadPathName,
2929 NoDevice,
2930 /// On Windows, `\\server` or `\\server\share` was not found.
2931 NetworkNotFound,
2932} || UnexpectedError;
2933
2934/// Create a directory.
2935/// `mode` is ignored on Windows and WASI.
2936/// On Windows, `dir_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2937/// On WASI, `dir_path` should be encoded as valid UTF-8.
2938/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
2939pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
2940 if (native_os == .wasi and !builtin.link_libc) {
2941 return mkdirat(wasi.AT.FDCWD, dir_path, mode);
2942 } else if (native_os == .windows) {
2943 const dir_path_w = try windows.sliceToPrefixedFileW(null, dir_path);
2944 return mkdirW(dir_path_w.span(), mode);
2945 } else {
2946 const dir_path_c = try toPosixPath(dir_path);
2947 return mkdirZ(&dir_path_c, mode);
2948 }
2949}
2950
2951/// Same as `mkdir` but the parameter is null-terminated.
2952/// On Windows, `dir_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2953/// On WASI, `dir_path` should be encoded as valid UTF-8.
2954/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
2955pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
2956 if (native_os == .windows) {
2957 const dir_path_w = try windows.cStrToPrefixedFileW(null, dir_path);
2958 return mkdirW(dir_path_w.span(), mode);
2959 } else if (native_os == .wasi and !builtin.link_libc) {
2960 return mkdir(mem.sliceTo(dir_path, 0), mode);
2961 }
2962 switch (errno(system.mkdir(dir_path, mode))) {
2963 .SUCCESS => return,
2964 .ACCES => return error.AccessDenied,
2965 .PERM => return error.AccessDenied,
2966 .DQUOT => return error.DiskQuota,
2967 .EXIST => return error.PathAlreadyExists,
2968 .FAULT => unreachable,
2969 .LOOP => return error.SymLinkLoop,
2970 .MLINK => return error.LinkQuotaExceeded,
2971 .NAMETOOLONG => return error.NameTooLong,
2972 .NOENT => return error.FileNotFound,
2973 .NOMEM => return error.SystemResources,
2974 .NOSPC => return error.NoSpaceLeft,
2975 .NOTDIR => return error.NotDir,
2976 .ROFS => return error.ReadOnlyFileSystem,
2977 .ILSEQ => |err| if (native_os == .wasi)
2978 return error.InvalidUtf8
2979 else
2980 return unexpectedErrno(err),
2981 else => |err| return unexpectedErrno(err),
2982 }
2983}
2984
2985/// Windows-only. Same as `mkdir` but the parameters is WTF16LE encoded.
2986pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void {
2987 _ = mode;
2988 const sub_dir_handle = windows.OpenFile(dir_path_w, .{
2989 .dir = fs.cwd().fd,
2990 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
2991 .creation = windows.FILE_CREATE,
2992 .filter = .dir_only,
2993 }) catch |err| switch (err) {
2994 error.IsDir => return error.Unexpected,
2995 error.PipeBusy => return error.Unexpected,
2996 error.WouldBlock => return error.Unexpected,
2997 error.AntivirusInterference => return error.Unexpected,
2998 else => |e| return e,
2999 };
3000 windows.CloseHandle(sub_dir_handle);
3001}
3002
3003pub const DeleteDirError = error{
3004 AccessDenied,
3005 FileBusy,
3006 SymLinkLoop,
3007 NameTooLong,
3008 FileNotFound,
3009 SystemResources,
3010 NotDir,
3011 DirNotEmpty,
3012 ReadOnlyFileSystem,
3013 /// WASI-only; file paths must be valid UTF-8.
3014 InvalidUtf8,
3015 /// Windows-only; file paths provided by the user must be valid WTF-8.
3016 /// https://simonsapin.github.io/wtf-8/
3017 InvalidWtf8,
3018 BadPathName,
3019 /// On Windows, `\\server` or `\\server\share` was not found.
3020 NetworkNotFound,
3021} || UnexpectedError;
3022
3023/// Deletes an empty directory.
3024/// On Windows, `dir_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3025/// On WASI, `dir_path` should be encoded as valid UTF-8.
3026/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
3027pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
3028 if (native_os == .wasi and !builtin.link_libc) {
3029 return unlinkat(wasi.AT.FDCWD, dir_path, AT.REMOVEDIR) catch |err| switch (err) {
3030 error.FileSystem => unreachable, // only occurs when targeting files
3031 error.IsDir => unreachable, // only occurs when targeting files
3032 else => |e| return e,
3033 };
3034 } else if (native_os == .windows) {
3035 const dir_path_w = try windows.sliceToPrefixedFileW(null, dir_path);
3036 return rmdirW(dir_path_w.span());
3037 } else {
3038 const dir_path_c = try toPosixPath(dir_path);
3039 return rmdirZ(&dir_path_c);
3040 }
3041}
3042
3043/// Same as `rmdir` except the parameter is null-terminated.
3044/// On Windows, `dir_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3045/// On WASI, `dir_path` should be encoded as valid UTF-8.
3046/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
3047pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {
3048 if (native_os == .windows) {
3049 const dir_path_w = try windows.cStrToPrefixedFileW(null, dir_path);
3050 return rmdirW(dir_path_w.span());
3051 } else if (native_os == .wasi and !builtin.link_libc) {
3052 return rmdir(mem.sliceTo(dir_path, 0));
3053 }
3054 switch (errno(system.rmdir(dir_path))) {
3055 .SUCCESS => return,
3056 .ACCES => return error.AccessDenied,
3057 .PERM => return error.AccessDenied,
3058 .BUSY => return error.FileBusy,
3059 .FAULT => unreachable,
3060 .INVAL => return error.BadPathName,
3061 .LOOP => return error.SymLinkLoop,
3062 .NAMETOOLONG => return error.NameTooLong,
3063 .NOENT => return error.FileNotFound,
3064 .NOMEM => return error.SystemResources,
3065 .NOTDIR => return error.NotDir,
3066 .EXIST => return error.DirNotEmpty,
3067 .NOTEMPTY => return error.DirNotEmpty,
3068 .ROFS => return error.ReadOnlyFileSystem,
3069 .ILSEQ => |err| if (native_os == .wasi)
3070 return error.InvalidUtf8
3071 else
3072 return unexpectedErrno(err),
3073 else => |err| return unexpectedErrno(err),
3074 }
3075}
3076
3077/// Windows-only. Same as `rmdir` except the parameter is WTF-16 LE encoded.
3078pub fn rmdirW(dir_path_w: []const u16) DeleteDirError!void {
3079 return windows.DeleteFile(dir_path_w, .{ .dir = fs.cwd().fd, .remove_dir = true }) catch |err| switch (err) {
3080 error.IsDir => unreachable,
3081 else => |e| return e,
3082 };
3083}
3084
3085pub const ChangeCurDirError = error{
3086 AccessDenied,
3087 FileSystem,
3088 SymLinkLoop,
3089 NameTooLong,
3090 FileNotFound,
3091 SystemResources,
3092 NotDir,
3093 BadPathName,
3094 /// WASI-only; file paths must be valid UTF-8.
3095 InvalidUtf8,
3096 /// Windows-only; file paths provided by the user must be valid WTF-8.
3097 /// https://simonsapin.github.io/wtf-8/
3098 InvalidWtf8,
3099} || UnexpectedError;
3100
3101/// Changes the current working directory of the calling process.
3102/// On Windows, `dir_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3103/// On WASI, `dir_path` should be encoded as valid UTF-8.
3104/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
3105pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
3106 if (native_os == .wasi and !builtin.link_libc) {
3107 @compileError("WASI does not support os.chdir");
3108 } else if (native_os == .windows) {
3109 var wtf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;
3110 const len = try std.unicode.wtf8ToWtf16Le(wtf16_dir_path[0..], dir_path);
3111 if (len > wtf16_dir_path.len) return error.NameTooLong;
3112 return chdirW(wtf16_dir_path[0..len]);
3113 } else {
3114 const dir_path_c = try toPosixPath(dir_path);
3115 return chdirZ(&dir_path_c);
3116 }
3117}
3118
3119/// Same as `chdir` except the parameter is null-terminated.
3120/// On Windows, `dir_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3121/// On WASI, `dir_path` should be encoded as valid UTF-8.
3122/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
3123pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void {
3124 if (native_os == .windows) {
3125 var wtf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;
3126 const len = try std.unicode.wtf8ToWtf16Le(wtf16_dir_path[0..], mem.span(dir_path));
3127 if (len > wtf16_dir_path.len) return error.NameTooLong;
3128 return chdirW(wtf16_dir_path[0..len]);
3129 } else if (native_os == .wasi and !builtin.link_libc) {
3130 return chdir(mem.span(dir_path));
3131 }
3132 switch (errno(system.chdir(dir_path))) {
3133 .SUCCESS => return,
3134 .ACCES => return error.AccessDenied,
3135 .FAULT => unreachable,
3136 .IO => return error.FileSystem,
3137 .LOOP => return error.SymLinkLoop,
3138 .NAMETOOLONG => return error.NameTooLong,
3139 .NOENT => return error.FileNotFound,
3140 .NOMEM => return error.SystemResources,
3141 .NOTDIR => return error.NotDir,
3142 .ILSEQ => |err| if (native_os == .wasi)
3143 return error.InvalidUtf8
3144 else
3145 return unexpectedErrno(err),
3146 else => |err| return unexpectedErrno(err),
3147 }
3148}
3149
3150/// Windows-only. Same as `chdir` except the parameter is WTF16 LE encoded.
3151pub fn chdirW(dir_path: []const u16) ChangeCurDirError!void {
3152 windows.SetCurrentDirectory(dir_path) catch |err| switch (err) {
3153 error.NoDevice => return error.FileSystem,
3154 else => |e| return e,
3155 };
3156}
3157
3158pub const FchdirError = error{
3159 AccessDenied,
3160 NotDir,
3161 FileSystem,
3162} || UnexpectedError;
3163
3164pub fn fchdir(dirfd: fd_t) FchdirError!void {
3165 if (dirfd == AT.FDCWD) return;
3166 while (true) {
3167 switch (errno(system.fchdir(dirfd))) {
3168 .SUCCESS => return,
3169 .ACCES => return error.AccessDenied,
3170 .BADF => unreachable,
3171 .NOTDIR => return error.NotDir,
3172 .INTR => continue,
3173 .IO => return error.FileSystem,
3174 else => |err| return unexpectedErrno(err),
3175 }
3176 }
3177}
3178
3179pub const ReadLinkError = error{
3180 /// In WASI, this error may occur when the file descriptor does
3181 /// not hold the required rights to read value of a symbolic link relative to it.
3182 AccessDenied,
3183 FileSystem,
3184 SymLinkLoop,
3185 NameTooLong,
3186 FileNotFound,
3187 SystemResources,
3188 NotLink,
3189 NotDir,
3190 /// WASI-only; file paths must be valid UTF-8.
3191 InvalidUtf8,
3192 /// Windows-only; file paths provided by the user must be valid WTF-8.
3193 /// https://simonsapin.github.io/wtf-8/
3194 InvalidWtf8,
3195 BadPathName,
3196 /// Windows-only. This error may occur if the opened reparse point is
3197 /// of unsupported type.
3198 UnsupportedReparsePointType,
3199 /// On Windows, `\\server` or `\\server\share` was not found.
3200 NetworkNotFound,
3201} || UnexpectedError;
3202
3203/// Read value of a symbolic link.
3204/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3205/// On WASI, `file_path` should be encoded as valid UTF-8.
3206/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
3207/// The return value is a slice of `out_buffer` from index 0.
3208/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3209/// On WASI, the result is encoded as UTF-8.
3210/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
3211pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
3212 if (native_os == .wasi and !builtin.link_libc) {
3213 return readlinkat(wasi.AT.FDCWD, file_path, out_buffer);
3214 } else if (native_os == .windows) {
3215 const file_path_w = try windows.sliceToPrefixedFileW(null, file_path);
3216 return readlinkW(file_path_w.span(), out_buffer);
3217 } else {
3218 const file_path_c = try toPosixPath(file_path);
3219 return readlinkZ(&file_path_c, out_buffer);
3220 }
3221}
3222
3223/// Windows-only. Same as `readlink` except `file_path` is WTF16 LE encoded.
3224/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3225/// See also `readlinkZ`.
3226pub fn readlinkW(file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
3227 return windows.ReadLink(fs.cwd().fd, file_path, out_buffer);
3228}
3229
3230/// Same as `readlink` except `file_path` is null-terminated.
3231pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
3232 if (native_os == .windows) {
3233 const file_path_w = try windows.cStrToPrefixedFileW(null, file_path);
3234 return readlinkW(file_path_w.span(), out_buffer);
3235 } else if (native_os == .wasi and !builtin.link_libc) {
3236 return readlink(mem.sliceTo(file_path, 0), out_buffer);
3237 }
3238 const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
3239 switch (errno(rc)) {
3240 .SUCCESS => return out_buffer[0..@bitCast(rc)],
3241 .ACCES => return error.AccessDenied,
3242 .FAULT => unreachable,
3243 .INVAL => return error.NotLink,
3244 .IO => return error.FileSystem,
3245 .LOOP => return error.SymLinkLoop,
3246 .NAMETOOLONG => return error.NameTooLong,
3247 .NOENT => return error.FileNotFound,
3248 .NOMEM => return error.SystemResources,
3249 .NOTDIR => return error.NotDir,
3250 .ILSEQ => |err| if (native_os == .wasi)
3251 return error.InvalidUtf8
3252 else
3253 return unexpectedErrno(err),
3254 else => |err| return unexpectedErrno(err),
3255 }
3256}
3257
3258/// Similar to `readlink` except reads value of a symbolink link **relative** to `dirfd` directory handle.
3259/// On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3260/// On WASI, `file_path` should be encoded as valid UTF-8.
3261/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
3262/// The return value is a slice of `out_buffer` from index 0.
3263/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3264/// On WASI, the result is encoded as UTF-8.
3265/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
3266/// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`.
3267pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
3268 if (native_os == .wasi and !builtin.link_libc) {
3269 return readlinkatWasi(dirfd, file_path, out_buffer);
3270 }
3271 if (native_os == .windows) {
3272 const file_path_w = try windows.sliceToPrefixedFileW(dirfd, file_path);
3273 return readlinkatW(dirfd, file_path_w.span(), out_buffer);
3274 }
3275 const file_path_c = try toPosixPath(file_path);
3276 return readlinkatZ(dirfd, &file_path_c, out_buffer);
3277}
3278
3279/// WASI-only. Same as `readlinkat` but targets WASI.
3280/// See also `readlinkat`.
3281pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
3282 var bufused: usize = undefined;
3283 switch (wasi.path_readlink(dirfd, file_path.ptr, file_path.len, out_buffer.ptr, out_buffer.len, &bufused)) {
3284 .SUCCESS => return out_buffer[0..bufused],
3285 .ACCES => return error.AccessDenied,
3286 .FAULT => unreachable,
3287 .INVAL => return error.NotLink,
3288 .IO => return error.FileSystem,
3289 .LOOP => return error.SymLinkLoop,
3290 .NAMETOOLONG => return error.NameTooLong,
3291 .NOENT => return error.FileNotFound,
3292 .NOMEM => return error.SystemResources,
3293 .NOTDIR => return error.NotDir,
3294 .NOTCAPABLE => return error.AccessDenied,
3295 .ILSEQ => return error.InvalidUtf8,
3296 else => |err| return unexpectedErrno(err),
3297 }
3298}
3299
3300/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 LE encoded.
3301/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
3302/// See also `readlinkat`.
3303pub fn readlinkatW(dirfd: fd_t, file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
3304 return windows.ReadLink(dirfd, file_path, out_buffer);
3305}
3306
3307/// Same as `readlinkat` except `file_path` is null-terminated.
3308/// See also `readlinkat`.
3309pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
3310 if (native_os == .windows) {
3311 const file_path_w = try windows.cStrToPrefixedFileW(dirfd, file_path);
3312 return readlinkatW(dirfd, file_path_w.span(), out_buffer);
3313 } else if (native_os == .wasi and !builtin.link_libc) {
3314 return readlinkat(dirfd, mem.sliceTo(file_path, 0), out_buffer);
3315 }
3316 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
3317 switch (errno(rc)) {
3318 .SUCCESS => return out_buffer[0..@bitCast(rc)],
3319 .ACCES => return error.AccessDenied,
3320 .FAULT => unreachable,
3321 .INVAL => return error.NotLink,
3322 .IO => return error.FileSystem,
3323 .LOOP => return error.SymLinkLoop,
3324 .NAMETOOLONG => return error.NameTooLong,
3325 .NOENT => return error.FileNotFound,
3326 .NOMEM => return error.SystemResources,
3327 .NOTDIR => return error.NotDir,
3328 .ILSEQ => |err| if (native_os == .wasi)
3329 return error.InvalidUtf8
3330 else
3331 return unexpectedErrno(err),
3332 else => |err| return unexpectedErrno(err),
3333 }
3334}
3335
3336pub const SetEidError = error{
3337 InvalidUserId,
3338 PermissionDenied,
3339} || UnexpectedError;
3340
3341pub const SetIdError = error{ResourceLimitReached} || SetEidError;
3342
3343pub fn setuid(uid: uid_t) SetIdError!void {
3344 switch (errno(system.setuid(uid))) {
3345 .SUCCESS => return,
3346 .AGAIN => return error.ResourceLimitReached,
3347 .INVAL => return error.InvalidUserId,
3348 .PERM => return error.PermissionDenied,
3349 else => |err| return unexpectedErrno(err),
3350 }
3351}
3352
3353pub fn seteuid(uid: uid_t) SetEidError!void {
3354 switch (errno(system.seteuid(uid))) {
3355 .SUCCESS => return,
3356 .INVAL => return error.InvalidUserId,
3357 .PERM => return error.PermissionDenied,
3358 else => |err| return unexpectedErrno(err),
3359 }
3360}
3361
3362pub fn setreuid(ruid: uid_t, euid: uid_t) SetIdError!void {
3363 switch (errno(system.setreuid(ruid, euid))) {
3364 .SUCCESS => return,
3365 .AGAIN => return error.ResourceLimitReached,
3366 .INVAL => return error.InvalidUserId,
3367 .PERM => return error.PermissionDenied,
3368 else => |err| return unexpectedErrno(err),
3369 }
3370}
3371
3372pub fn setgid(gid: gid_t) SetIdError!void {
3373 switch (errno(system.setgid(gid))) {
3374 .SUCCESS => return,
3375 .AGAIN => return error.ResourceLimitReached,
3376 .INVAL => return error.InvalidUserId,
3377 .PERM => return error.PermissionDenied,
3378 else => |err| return unexpectedErrno(err),
3379 }
3380}
3381
3382pub fn setegid(uid: uid_t) SetEidError!void {
3383 switch (errno(system.setegid(uid))) {
3384 .SUCCESS => return,
3385 .INVAL => return error.InvalidUserId,
3386 .PERM => return error.PermissionDenied,
3387 else => |err| return unexpectedErrno(err),
3388 }
3389}
3390
3391pub fn setregid(rgid: gid_t, egid: gid_t) SetIdError!void {
3392 switch (errno(system.setregid(rgid, egid))) {
3393 .SUCCESS => return,
3394 .AGAIN => return error.ResourceLimitReached,
3395 .INVAL => return error.InvalidUserId,
3396 .PERM => return error.PermissionDenied,
3397 else => |err| return unexpectedErrno(err),
3398 }
3399}
3400
3401/// Test whether a file descriptor refers to a terminal.
3402pub fn isatty(handle: fd_t) bool {
3403 if (native_os == .windows) {
3404 if (fs.File.isCygwinPty(.{ .handle = handle }))
3405 return true;
3406
3407 var out: windows.DWORD = undefined;
3408 return windows.kernel32.GetConsoleMode(handle, &out) != 0;
3409 }
3410 if (builtin.link_libc) {
3411 return system.isatty(handle) != 0;
3412 }
3413 if (native_os == .wasi) {
3414 var statbuf: wasi.fdstat_t = undefined;
3415 const err = wasi.fd_fdstat_get(handle, &statbuf);
3416 if (err != .SUCCESS)
3417 return false;
3418
3419 // A tty is a character device that we can't seek or tell on.
3420 if (statbuf.fs_filetype != .CHARACTER_DEVICE)
3421 return false;
3422 if (statbuf.fs_rights_base.FD_SEEK or statbuf.fs_rights_base.FD_TELL)
3423 return false;
3424
3425 return true;
3426 }
3427 if (native_os == .linux) {
3428 while (true) {
3429 var wsz: linux.winsize = undefined;
3430 const fd: usize = @bitCast(@as(isize, handle));
3431 const rc = linux.syscall3(.ioctl, fd, linux.T.IOCGWINSZ, @intFromPtr(&wsz));
3432 switch (linux.E.init(rc)) {
3433 .SUCCESS => return true,
3434 .INTR => continue,
3435 else => return false,
3436 }
3437 }
3438 }
3439 return system.isatty(handle) != 0;
3440}
3441
3442pub const SocketError = error{
3443 /// Permission to create a socket of the specified type and/or
3444 /// pro‐tocol is denied.
3445 PermissionDenied,
3446
3447 /// The implementation does not support the specified address family.
3448 AddressFamilyNotSupported,
3449
3450 /// Unknown protocol, or protocol family not available.
3451 ProtocolFamilyNotAvailable,
3452
3453 /// The per-process limit on the number of open file descriptors has been reached.
3454 ProcessFdQuotaExceeded,
3455
3456 /// The system-wide limit on the total number of open files has been reached.
3457 SystemFdQuotaExceeded,
3458
3459 /// Insufficient memory is available. The socket cannot be created until sufficient
3460 /// resources are freed.
3461 SystemResources,
3462
3463 /// The protocol type or the specified protocol is not supported within this domain.
3464 ProtocolNotSupported,
3465
3466 /// The socket type is not supported by the protocol.
3467 SocketTypeNotSupported,
3468} || UnexpectedError;
3469
3470pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t {
3471 if (native_os == .windows) {
3472 // NOTE: windows translates the SOCK.NONBLOCK/SOCK.CLOEXEC flags into
3473 // windows-analagous operations
3474 const filtered_sock_type = socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC);
3475 const flags: u32 = if ((socket_type & SOCK.CLOEXEC) != 0)
3476 windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT
3477 else
3478 0;
3479 const rc = try windows.WSASocketW(
3480 @bitCast(domain),
3481 @bitCast(filtered_sock_type),
3482 @bitCast(protocol),
3483 null,
3484 0,
3485 flags,
3486 );
3487 errdefer windows.closesocket(rc) catch unreachable;
3488 if ((socket_type & SOCK.NONBLOCK) != 0) {
3489 var mode: c_ulong = 1; // nonblocking
3490 if (windows.ws2_32.SOCKET_ERROR == windows.ws2_32.ioctlsocket(rc, windows.ws2_32.FIONBIO, &mode)) {
3491 switch (windows.ws2_32.WSAGetLastError()) {
3492 // have not identified any error codes that should be handled yet
3493 else => unreachable,
3494 }
3495 }
3496 }
3497 return rc;
3498 }
3499
3500 const have_sock_flags = !builtin.target.isDarwin();
3501 const filtered_sock_type = if (!have_sock_flags)
3502 socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC)
3503 else
3504 socket_type;
3505 const rc = system.socket(domain, filtered_sock_type, protocol);
3506 switch (errno(rc)) {
3507 .SUCCESS => {
3508 const fd: fd_t = @intCast(rc);
3509 errdefer close(fd);
3510 if (!have_sock_flags) {
3511 try setSockFlags(fd, socket_type);
3512 }
3513 return fd;
3514 },
3515 .ACCES => return error.PermissionDenied,
3516 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
3517 .INVAL => return error.ProtocolFamilyNotAvailable,
3518 .MFILE => return error.ProcessFdQuotaExceeded,
3519 .NFILE => return error.SystemFdQuotaExceeded,
3520 .NOBUFS => return error.SystemResources,
3521 .NOMEM => return error.SystemResources,
3522 .PROTONOSUPPORT => return error.ProtocolNotSupported,
3523 .PROTOTYPE => return error.SocketTypeNotSupported,
3524 else => |err| return unexpectedErrno(err),
3525 }
3526}
3527
3528pub const ShutdownError = error{
3529 ConnectionAborted,
3530
3531 /// Connection was reset by peer, application should close socket as it is no longer usable.
3532 ConnectionResetByPeer,
3533 BlockingOperationInProgress,
3534
3535 /// The network subsystem has failed.
3536 NetworkSubsystemFailed,
3537
3538 /// The socket is not connected (connection-oriented sockets only).
3539 SocketNotConnected,
3540 SystemResources,
3541} || UnexpectedError;
3542
3543pub const ShutdownHow = enum { recv, send, both };
3544
3545/// Shutdown socket send/receive operations
3546pub fn shutdown(sock: socket_t, how: ShutdownHow) ShutdownError!void {
3547 if (native_os == .windows) {
3548 const result = windows.ws2_32.shutdown(sock, switch (how) {
3549 .recv => windows.ws2_32.SD_RECEIVE,
3550 .send => windows.ws2_32.SD_SEND,
3551 .both => windows.ws2_32.SD_BOTH,
3552 });
3553 if (0 != result) switch (windows.ws2_32.WSAGetLastError()) {
3554 .WSAECONNABORTED => return error.ConnectionAborted,
3555 .WSAECONNRESET => return error.ConnectionResetByPeer,
3556 .WSAEINPROGRESS => return error.BlockingOperationInProgress,
3557 .WSAEINVAL => unreachable,
3558 .WSAENETDOWN => return error.NetworkSubsystemFailed,
3559 .WSAENOTCONN => return error.SocketNotConnected,
3560 .WSAENOTSOCK => unreachable,
3561 .WSANOTINITIALISED => unreachable,
3562 else => |err| return windows.unexpectedWSAError(err),
3563 };
3564 } else {
3565 const rc = system.shutdown(sock, switch (how) {
3566 .recv => SHUT.RD,
3567 .send => SHUT.WR,
3568 .both => SHUT.RDWR,
3569 });
3570 switch (errno(rc)) {
3571 .SUCCESS => return,
3572 .BADF => unreachable,
3573 .INVAL => unreachable,
3574 .NOTCONN => return error.SocketNotConnected,
3575 .NOTSOCK => unreachable,
3576 .NOBUFS => return error.SystemResources,
3577 else => |err| return unexpectedErrno(err),
3578 }
3579 }
3580}
3581
3582pub const BindError = error{
3583 /// The address is protected, and the user is not the superuser.
3584 /// For UNIX domain sockets: Search permission is denied on a component
3585 /// of the path prefix.
3586 AccessDenied,
3587
3588 /// The given address is already in use, or in the case of Internet domain sockets,
3589 /// The port number was specified as zero in the socket
3590 /// address structure, but, upon attempting to bind to an ephemeral port, it was
3591 /// determined that all port numbers in the ephemeral port range are currently in
3592 /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7).
3593 AddressInUse,
3594
3595 /// A nonexistent interface was requested or the requested address was not local.
3596 AddressNotAvailable,
3597
3598 /// The address is not valid for the address family of socket.
3599 AddressFamilyNotSupported,
3600
3601 /// Too many symbolic links were encountered in resolving addr.
3602 SymLinkLoop,
3603
3604 /// addr is too long.
3605 NameTooLong,
3606
3607 /// A component in the directory prefix of the socket pathname does not exist.
3608 FileNotFound,
3609
3610 /// Insufficient kernel memory was available.
3611 SystemResources,
3612
3613 /// A component of the path prefix is not a directory.
3614 NotDir,
3615
3616 /// The socket inode would reside on a read-only filesystem.
3617 ReadOnlyFileSystem,
3618
3619 /// The network subsystem has failed.
3620 NetworkSubsystemFailed,
3621
3622 FileDescriptorNotASocket,
3623
3624 AlreadyBound,
3625} || UnexpectedError;
3626
3627/// addr is `*const T` where T is one of the sockaddr
3628pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!void {
3629 if (native_os == .windows) {
3630 const rc = windows.bind(sock, addr, len);
3631 if (rc == windows.ws2_32.SOCKET_ERROR) {
3632 switch (windows.ws2_32.WSAGetLastError()) {
3633 .WSANOTINITIALISED => unreachable, // not initialized WSA
3634 .WSAEACCES => return error.AccessDenied,
3635 .WSAEADDRINUSE => return error.AddressInUse,
3636 .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
3637 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
3638 .WSAEFAULT => unreachable, // invalid pointers
3639 .WSAEINVAL => return error.AlreadyBound,
3640 .WSAENOBUFS => return error.SystemResources,
3641 .WSAENETDOWN => return error.NetworkSubsystemFailed,
3642 else => |err| return windows.unexpectedWSAError(err),
3643 }
3644 unreachable;
3645 }
3646 return;
3647 } else {
3648 const rc = system.bind(sock, addr, len);
3649 switch (errno(rc)) {
3650 .SUCCESS => return,
3651 .ACCES, .PERM => return error.AccessDenied,
3652 .ADDRINUSE => return error.AddressInUse,
3653 .BADF => unreachable, // always a race condition if this error is returned
3654 .INVAL => unreachable, // invalid parameters
3655 .NOTSOCK => unreachable, // invalid `sockfd`
3656 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
3657 .ADDRNOTAVAIL => return error.AddressNotAvailable,
3658 .FAULT => unreachable, // invalid `addr` pointer
3659 .LOOP => return error.SymLinkLoop,
3660 .NAMETOOLONG => return error.NameTooLong,
3661 .NOENT => return error.FileNotFound,
3662 .NOMEM => return error.SystemResources,
3663 .NOTDIR => return error.NotDir,
3664 .ROFS => return error.ReadOnlyFileSystem,
3665 else => |err| return unexpectedErrno(err),
3666 }
3667 }
3668 unreachable;
3669}
3670
3671pub const ListenError = error{
3672 /// Another socket is already listening on the same port.
3673 /// For Internet domain sockets, the socket referred to by sockfd had not previously
3674 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it
3675 /// was determined that all port numbers in the ephemeral port range are currently in
3676 /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
3677 AddressInUse,
3678
3679 /// The file descriptor sockfd does not refer to a socket.
3680 FileDescriptorNotASocket,
3681
3682 /// The socket is not of a type that supports the listen() operation.
3683 OperationNotSupported,
3684
3685 /// The network subsystem has failed.
3686 NetworkSubsystemFailed,
3687
3688 /// Ran out of system resources
3689 /// On Windows it can either run out of socket descriptors or buffer space
3690 SystemResources,
3691
3692 /// Already connected
3693 AlreadyConnected,
3694
3695 /// Socket has not been bound yet
3696 SocketNotBound,
3697} || UnexpectedError;
3698
3699pub fn listen(sock: socket_t, backlog: u31) ListenError!void {
3700 if (native_os == .windows) {
3701 const rc = windows.listen(sock, backlog);
3702 if (rc == windows.ws2_32.SOCKET_ERROR) {
3703 switch (windows.ws2_32.WSAGetLastError()) {
3704 .WSANOTINITIALISED => unreachable, // not initialized WSA
3705 .WSAENETDOWN => return error.NetworkSubsystemFailed,
3706 .WSAEADDRINUSE => return error.AddressInUse,
3707 .WSAEISCONN => return error.AlreadyConnected,
3708 .WSAEINVAL => return error.SocketNotBound,
3709 .WSAEMFILE, .WSAENOBUFS => return error.SystemResources,
3710 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
3711 .WSAEOPNOTSUPP => return error.OperationNotSupported,
3712 .WSAEINPROGRESS => unreachable,
3713 else => |err| return windows.unexpectedWSAError(err),
3714 }
3715 }
3716 return;
3717 } else {
3718 const rc = system.listen(sock, backlog);
3719 switch (errno(rc)) {
3720 .SUCCESS => return,
3721 .ADDRINUSE => return error.AddressInUse,
3722 .BADF => unreachable,
3723 .NOTSOCK => return error.FileDescriptorNotASocket,
3724 .OPNOTSUPP => return error.OperationNotSupported,
3725 else => |err| return unexpectedErrno(err),
3726 }
3727 }
3728}
3729
3730pub const AcceptError = error{
3731 ConnectionAborted,
3732
3733 /// The file descriptor sockfd does not refer to a socket.
3734 FileDescriptorNotASocket,
3735
3736 /// The per-process limit on the number of open file descriptors has been reached.
3737 ProcessFdQuotaExceeded,
3738
3739 /// The system-wide limit on the total number of open files has been reached.
3740 SystemFdQuotaExceeded,
3741
3742 /// Not enough free memory. This often means that the memory allocation is limited
3743 /// by the socket buffer limits, not by the system memory.
3744 SystemResources,
3745
3746 /// Socket is not listening for new connections.
3747 SocketNotListening,
3748
3749 ProtocolFailure,
3750
3751 /// Firewall rules forbid connection.
3752 BlockedByFirewall,
3753
3754 /// This error occurs when no global event loop is configured,
3755 /// and accepting from the socket would block.
3756 WouldBlock,
3757
3758 /// An incoming connection was indicated, but was subsequently terminated by the
3759 /// remote peer prior to accepting the call.
3760 ConnectionResetByPeer,
3761
3762 /// The network subsystem has failed.
3763 NetworkSubsystemFailed,
3764
3765 /// The referenced socket is not a type that supports connection-oriented service.
3766 OperationNotSupported,
3767} || UnexpectedError;
3768
3769/// Accept a connection on a socket.
3770/// If `sockfd` is opened in non blocking mode, the function will
3771/// return error.WouldBlock when EAGAIN is received.
3772pub fn accept(
3773 /// This argument is a socket that has been created with `socket`, bound to a local address
3774 /// with `bind`, and is listening for connections after a `listen`.
3775 sock: socket_t,
3776 /// This argument is a pointer to a sockaddr structure. This structure is filled in with the
3777 /// address of the peer socket, as known to the communications layer. The exact format of the
3778 /// address returned addr is determined by the socket's address family (see `socket` and the
3779 /// respective protocol man pages).
3780 addr: ?*sockaddr,
3781 /// This argument is a value-result argument: the caller must initialize it to contain the
3782 /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size
3783 /// of the peer address.
3784 ///
3785 /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size`
3786 /// will return a value greater than was supplied to the call.
3787 addr_size: ?*socklen_t,
3788 /// The following values can be bitwise ORed in flags to obtain different behavior:
3789 /// * `SOCK.NONBLOCK` - Set the `NONBLOCK` file status flag on the open file description (see `open`)
3790 /// referred to by the new file descriptor. Using this flag saves extra calls to `fcntl` to achieve
3791 /// the same result.
3792 /// * `SOCK.CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the
3793 /// description of the `CLOEXEC` flag in `open` for reasons why this may be useful.
3794 flags: u32,
3795) AcceptError!socket_t {
3796 const have_accept4 = !(builtin.target.isDarwin() or native_os == .windows);
3797 assert(0 == (flags & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC))); // Unsupported flag(s)
3798
3799 const accepted_sock: socket_t = while (true) {
3800 const rc = if (have_accept4)
3801 system.accept4(sock, addr, addr_size, flags)
3802 else if (native_os == .windows)
3803 windows.accept(sock, addr, addr_size)
3804 else
3805 system.accept(sock, addr, addr_size);
3806
3807 if (native_os == .windows) {
3808 if (rc == windows.ws2_32.INVALID_SOCKET) {
3809 switch (windows.ws2_32.WSAGetLastError()) {
3810 .WSANOTINITIALISED => unreachable, // not initialized WSA
3811 .WSAECONNRESET => return error.ConnectionResetByPeer,
3812 .WSAEFAULT => unreachable,
3813 .WSAEINVAL => return error.SocketNotListening,
3814 .WSAEMFILE => return error.ProcessFdQuotaExceeded,
3815 .WSAENETDOWN => return error.NetworkSubsystemFailed,
3816 .WSAENOBUFS => return error.FileDescriptorNotASocket,
3817 .WSAEOPNOTSUPP => return error.OperationNotSupported,
3818 .WSAEWOULDBLOCK => return error.WouldBlock,
3819 else => |err| return windows.unexpectedWSAError(err),
3820 }
3821 } else {
3822 break rc;
3823 }
3824 } else {
3825 switch (errno(rc)) {
3826 .SUCCESS => break @intCast(rc),
3827 .INTR => continue,
3828 .AGAIN => return error.WouldBlock,
3829 .BADF => unreachable, // always a race condition
3830 .CONNABORTED => return error.ConnectionAborted,
3831 .FAULT => unreachable,
3832 .INVAL => return error.SocketNotListening,
3833 .NOTSOCK => unreachable,
3834 .MFILE => return error.ProcessFdQuotaExceeded,
3835 .NFILE => return error.SystemFdQuotaExceeded,
3836 .NOBUFS => return error.SystemResources,
3837 .NOMEM => return error.SystemResources,
3838 .OPNOTSUPP => unreachable,
3839 .PROTO => return error.ProtocolFailure,
3840 .PERM => return error.BlockedByFirewall,
3841 else => |err| return unexpectedErrno(err),
3842 }
3843 }
3844 };
3845
3846 errdefer switch (native_os) {
3847 .windows => windows.closesocket(accepted_sock) catch unreachable,
3848 else => close(accepted_sock),
3849 };
3850 if (!have_accept4) {
3851 try setSockFlags(accepted_sock, flags);
3852 }
3853 return accepted_sock;
3854}
3855
3856fn setSockFlags(sock: socket_t, flags: u32) !void {
3857 if ((flags & SOCK.CLOEXEC) != 0) {
3858 if (native_os == .windows) {
3859 // TODO: Find out if this is supported for sockets
3860 } else {
3861 var fd_flags = fcntl(sock, F.GETFD, 0) catch |err| switch (err) {
3862 error.FileBusy => unreachable,
3863 error.Locked => unreachable,
3864 error.PermissionDenied => unreachable,
3865 error.DeadLock => unreachable,
3866 error.LockedRegionLimitExceeded => unreachable,
3867 else => |e| return e,
3868 };
3869 fd_flags |= FD_CLOEXEC;
3870 _ = fcntl(sock, F.SETFD, fd_flags) catch |err| switch (err) {
3871 error.FileBusy => unreachable,
3872 error.Locked => unreachable,
3873 error.PermissionDenied => unreachable,
3874 error.DeadLock => unreachable,
3875 error.LockedRegionLimitExceeded => unreachable,
3876 else => |e| return e,
3877 };
3878 }
3879 }
3880 if ((flags & SOCK.NONBLOCK) != 0) {
3881 if (native_os == .windows) {
3882 var mode: c_ulong = 1;
3883 if (windows.ws2_32.ioctlsocket(sock, windows.ws2_32.FIONBIO, &mode) == windows.ws2_32.SOCKET_ERROR) {
3884 switch (windows.ws2_32.WSAGetLastError()) {
3885 .WSANOTINITIALISED => unreachable,
3886 .WSAENETDOWN => return error.NetworkSubsystemFailed,
3887 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
3888 // TODO: handle more errors
3889 else => |err| return windows.unexpectedWSAError(err),
3890 }
3891 }
3892 } else {
3893 var fl_flags = fcntl(sock, F.GETFL, 0) catch |err| switch (err) {
3894 error.FileBusy => unreachable,
3895 error.Locked => unreachable,
3896 error.PermissionDenied => unreachable,
3897 error.DeadLock => unreachable,
3898 error.LockedRegionLimitExceeded => unreachable,
3899 else => |e| return e,
3900 };
3901 fl_flags |= 1 << @bitOffsetOf(O, "NONBLOCK");
3902 _ = fcntl(sock, F.SETFL, fl_flags) catch |err| switch (err) {
3903 error.FileBusy => unreachable,
3904 error.Locked => unreachable,
3905 error.PermissionDenied => unreachable,
3906 error.DeadLock => unreachable,
3907 error.LockedRegionLimitExceeded => unreachable,
3908 else => |e| return e,
3909 };
3910 }
3911 }
3912}
3913
3914pub const EpollCreateError = error{
3915 /// The per-user limit on the number of epoll instances imposed by
3916 /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further
3917 /// details.
3918 /// Or, The per-process limit on the number of open file descriptors has been reached.
3919 ProcessFdQuotaExceeded,
3920
3921 /// The system-wide limit on the total number of open files has been reached.
3922 SystemFdQuotaExceeded,
3923
3924 /// There was insufficient memory to create the kernel object.
3925 SystemResources,
3926} || UnexpectedError;
3927
3928pub fn epoll_create1(flags: u32) EpollCreateError!i32 {
3929 const rc = system.epoll_create1(flags);
3930 switch (errno(rc)) {
3931 .SUCCESS => return @intCast(rc),
3932 else => |err| return unexpectedErrno(err),
3933
3934 .INVAL => unreachable,
3935 .MFILE => return error.ProcessFdQuotaExceeded,
3936 .NFILE => return error.SystemFdQuotaExceeded,
3937 .NOMEM => return error.SystemResources,
3938 }
3939}
3940
3941pub const EpollCtlError = error{
3942 /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered
3943 /// with this epoll instance.
3944 FileDescriptorAlreadyPresentInSet,
3945
3946 /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a
3947 /// circular loop of epoll instances monitoring one another.
3948 OperationCausesCircularLoop,
3949
3950 /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll
3951 /// instance.
3952 FileDescriptorNotRegistered,
3953
3954 /// There was insufficient memory to handle the requested op control operation.
3955 SystemResources,
3956
3957 /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while
3958 /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance.
3959 /// See epoll(7) for further details.
3960 UserResourceLimitReached,
3961
3962 /// The target file fd does not support epoll. This error can occur if fd refers to,
3963 /// for example, a regular file or a directory.
3964 FileDescriptorIncompatibleWithEpoll,
3965} || UnexpectedError;
3966
3967pub fn epoll_ctl(epfd: i32, op: u32, fd: i32, event: ?*linux.epoll_event) EpollCtlError!void {
3968 const rc = system.epoll_ctl(epfd, op, fd, event);
3969 switch (errno(rc)) {
3970 .SUCCESS => return,
3971 else => |err| return unexpectedErrno(err),
3972
3973 .BADF => unreachable, // always a race condition if this happens
3974 .EXIST => return error.FileDescriptorAlreadyPresentInSet,
3975 .INVAL => unreachable,
3976 .LOOP => return error.OperationCausesCircularLoop,
3977 .NOENT => return error.FileDescriptorNotRegistered,
3978 .NOMEM => return error.SystemResources,
3979 .NOSPC => return error.UserResourceLimitReached,
3980 .PERM => return error.FileDescriptorIncompatibleWithEpoll,
3981 }
3982}
3983
3984/// Waits for an I/O event on an epoll file descriptor.
3985/// Returns the number of file descriptors ready for the requested I/O,
3986/// or zero if no file descriptor became ready during the requested timeout milliseconds.
3987pub fn epoll_wait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize {
3988 while (true) {
3989 // TODO get rid of the @intCast
3990 const rc = system.epoll_wait(epfd, events.ptr, @intCast(events.len), timeout);
3991 switch (errno(rc)) {
3992 .SUCCESS => return @intCast(rc),
3993 .INTR => continue,
3994 .BADF => unreachable,
3995 .FAULT => unreachable,
3996 .INVAL => unreachable,
3997 else => unreachable,
3998 }
3999 }
4000}
4001
4002pub const EventFdError = error{
4003 SystemResources,
4004 ProcessFdQuotaExceeded,
4005 SystemFdQuotaExceeded,
4006} || UnexpectedError;
4007
4008pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 {
4009 const rc = system.eventfd(initval, flags);
4010 switch (errno(rc)) {
4011 .SUCCESS => return @intCast(rc),
4012 else => |err| return unexpectedErrno(err),
4013
4014 .INVAL => unreachable, // invalid parameters
4015 .MFILE => return error.ProcessFdQuotaExceeded,
4016 .NFILE => return error.SystemFdQuotaExceeded,
4017 .NODEV => return error.SystemResources,
4018 .NOMEM => return error.SystemResources,
4019 }
4020}
4021
4022pub const GetSockNameError = error{
4023 /// Insufficient resources were available in the system to perform the operation.
4024 SystemResources,
4025
4026 /// The network subsystem has failed.
4027 NetworkSubsystemFailed,
4028
4029 /// Socket hasn't been bound yet
4030 SocketNotBound,
4031
4032 FileDescriptorNotASocket,
4033} || UnexpectedError;
4034
4035pub fn getsockname(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
4036 if (native_os == .windows) {
4037 const rc = windows.getsockname(sock, addr, addrlen);
4038 if (rc == windows.ws2_32.SOCKET_ERROR) {
4039 switch (windows.ws2_32.WSAGetLastError()) {
4040 .WSANOTINITIALISED => unreachable,
4041 .WSAENETDOWN => return error.NetworkSubsystemFailed,
4042 .WSAEFAULT => unreachable, // addr or addrlen have invalid pointers or addrlen points to an incorrect value
4043 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
4044 .WSAEINVAL => return error.SocketNotBound,
4045 else => |err| return windows.unexpectedWSAError(err),
4046 }
4047 }
4048 return;
4049 } else {
4050 const rc = system.getsockname(sock, addr, addrlen);
4051 switch (errno(rc)) {
4052 .SUCCESS => return,
4053 else => |err| return unexpectedErrno(err),
4054
4055 .BADF => unreachable, // always a race condition
4056 .FAULT => unreachable,
4057 .INVAL => unreachable, // invalid parameters
4058 .NOTSOCK => return error.FileDescriptorNotASocket,
4059 .NOBUFS => return error.SystemResources,
4060 }
4061 }
4062}
4063
4064pub fn getpeername(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
4065 if (native_os == .windows) {
4066 const rc = windows.getpeername(sock, addr, addrlen);
4067 if (rc == windows.ws2_32.SOCKET_ERROR) {
4068 switch (windows.ws2_32.WSAGetLastError()) {
4069 .WSANOTINITIALISED => unreachable,
4070 .WSAENETDOWN => return error.NetworkSubsystemFailed,
4071 .WSAEFAULT => unreachable, // addr or addrlen have invalid pointers or addrlen points to an incorrect value
4072 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
4073 .WSAEINVAL => return error.SocketNotBound,
4074 else => |err| return windows.unexpectedWSAError(err),
4075 }
4076 }
4077 return;
4078 } else {
4079 const rc = system.getpeername(sock, addr, addrlen);
4080 switch (errno(rc)) {
4081 .SUCCESS => return,
4082 else => |err| return unexpectedErrno(err),
4083
4084 .BADF => unreachable, // always a race condition
4085 .FAULT => unreachable,
4086 .INVAL => unreachable, // invalid parameters
4087 .NOTSOCK => return error.FileDescriptorNotASocket,
4088 .NOBUFS => return error.SystemResources,
4089 }
4090 }
4091}
4092
4093pub const ConnectError = error{
4094 /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket
4095 /// file, or search permission is denied for one of the directories in the path prefix.
4096 /// or
4097 /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or
4098 /// the connection request failed because of a local firewall rule.
4099 PermissionDenied,
4100
4101 /// Local address is already in use.
4102 AddressInUse,
4103
4104 /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an
4105 /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers
4106 /// in the ephemeral port range are currently in use. See the discussion of
4107 /// /proc/sys/net/ipv4/ip_local_port_range in ip(7).
4108 AddressNotAvailable,
4109
4110 /// The passed address didn't have the correct address family in its sa_family field.
4111 AddressFamilyNotSupported,
4112
4113 /// Insufficient entries in the routing cache.
4114 SystemResources,
4115
4116 /// A connect() on a stream socket found no one listening on the remote address.
4117 ConnectionRefused,
4118
4119 /// Network is unreachable.
4120 NetworkUnreachable,
4121
4122 /// Timeout while attempting connection. The server may be too busy to accept new connections. Note
4123 /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
4124 ConnectionTimedOut,
4125
4126 /// This error occurs when no global event loop is configured,
4127 /// and connecting to the socket would block.
4128 WouldBlock,
4129
4130 /// The given path for the unix socket does not exist.
4131 FileNotFound,
4132
4133 /// Connection was reset by peer before connect could complete.
4134 ConnectionResetByPeer,
4135
4136 /// Socket is non-blocking and already has a pending connection in progress.
4137 ConnectionPending,
4138} || UnexpectedError;
4139
4140/// Initiate a connection on a socket.
4141/// If `sockfd` is opened in non blocking mode, the function will
4142/// return error.WouldBlock when EAGAIN or EINPROGRESS is received.
4143pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
4144 if (native_os == .windows) {
4145 const rc = windows.ws2_32.connect(sock, sock_addr, @intCast(len));
4146 if (rc == 0) return;
4147 switch (windows.ws2_32.WSAGetLastError()) {
4148 .WSAEADDRINUSE => return error.AddressInUse,
4149 .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
4150 .WSAECONNREFUSED => return error.ConnectionRefused,
4151 .WSAECONNRESET => return error.ConnectionResetByPeer,
4152 .WSAETIMEDOUT => return error.ConnectionTimedOut,
4153 .WSAEHOSTUNREACH, // TODO: should we return NetworkUnreachable in this case as well?
4154 .WSAENETUNREACH,
4155 => return error.NetworkUnreachable,
4156 .WSAEFAULT => unreachable,
4157 .WSAEINVAL => unreachable,
4158 .WSAEISCONN => unreachable,
4159 .WSAENOTSOCK => unreachable,
4160 .WSAEWOULDBLOCK => return error.WouldBlock,
4161 .WSAEACCES => unreachable,
4162 .WSAENOBUFS => return error.SystemResources,
4163 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
4164 else => |err| return windows.unexpectedWSAError(err),
4165 }
4166 return;
4167 }
4168
4169 while (true) {
4170 switch (errno(system.connect(sock, sock_addr, len))) {
4171 .SUCCESS => return,
4172 .ACCES => return error.PermissionDenied,
4173 .PERM => return error.PermissionDenied,
4174 .ADDRINUSE => return error.AddressInUse,
4175 .ADDRNOTAVAIL => return error.AddressNotAvailable,
4176 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
4177 .AGAIN, .INPROGRESS => return error.WouldBlock,
4178 .ALREADY => return error.ConnectionPending,
4179 .BADF => unreachable, // sockfd is not a valid open file descriptor.
4180 .CONNREFUSED => return error.ConnectionRefused,
4181 .CONNRESET => return error.ConnectionResetByPeer,
4182 .FAULT => unreachable, // The socket structure address is outside the user's address space.
4183 .INTR => continue,
4184 .ISCONN => unreachable, // The socket is already connected.
4185 .HOSTUNREACH => return error.NetworkUnreachable,
4186 .NETUNREACH => return error.NetworkUnreachable,
4187 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
4188 .PROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
4189 .TIMEDOUT => return error.ConnectionTimedOut,
4190 .NOENT => return error.FileNotFound, // Returned when socket is AF.UNIX and the given path does not exist.
4191 .CONNABORTED => unreachable, // Tried to reuse socket that previously received error.ConnectionRefused.
4192 else => |err| return unexpectedErrno(err),
4193 }
4194 }
4195}
4196
4197pub fn getsockoptError(sockfd: fd_t) ConnectError!void {
4198 var err_code: i32 = undefined;
4199 var size: u32 = @sizeOf(u32);
4200 const rc = system.getsockopt(sockfd, SOL.SOCKET, SO.ERROR, @ptrCast(&err_code), &size);
4201 assert(size == 4);
4202 switch (errno(rc)) {
4203 .SUCCESS => switch (@as(E, @enumFromInt(err_code))) {
4204 .SUCCESS => return,
4205 .ACCES => return error.PermissionDenied,
4206 .PERM => return error.PermissionDenied,
4207 .ADDRINUSE => return error.AddressInUse,
4208 .ADDRNOTAVAIL => return error.AddressNotAvailable,
4209 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
4210 .AGAIN => return error.SystemResources,
4211 .ALREADY => return error.ConnectionPending,
4212 .BADF => unreachable, // sockfd is not a valid open file descriptor.
4213 .CONNREFUSED => return error.ConnectionRefused,
4214 .FAULT => unreachable, // The socket structure address is outside the user's address space.
4215 .ISCONN => unreachable, // The socket is already connected.
4216 .HOSTUNREACH => return error.NetworkUnreachable,
4217 .NETUNREACH => return error.NetworkUnreachable,
4218 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
4219 .PROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
4220 .TIMEDOUT => return error.ConnectionTimedOut,
4221 .CONNRESET => return error.ConnectionResetByPeer,
4222 else => |err| return unexpectedErrno(err),
4223 },
4224 .BADF => unreachable, // The argument sockfd is not a valid file descriptor.
4225 .FAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space.
4226 .INVAL => unreachable,
4227 .NOPROTOOPT => unreachable, // The option is unknown at the level indicated.
4228 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
4229 else => |err| return unexpectedErrno(err),
4230 }
4231}
4232
4233pub const WaitPidResult = struct {
4234 pid: pid_t,
4235 status: u32,
4236};
4237
4238/// Use this version of the `waitpid` wrapper if you spawned your child process using explicit
4239/// `fork` and `execve` method.
4240pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult {
4241 var status: if (builtin.link_libc) c_int else u32 = undefined;
4242 while (true) {
4243 const rc = system.waitpid(pid, &status, @intCast(flags));
4244 switch (errno(rc)) {
4245 .SUCCESS => return .{
4246 .pid = @intCast(rc),
4247 .status = @bitCast(status),
4248 },
4249 .INTR => continue,
4250 .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
4251 .INVAL => unreachable, // Invalid flags.
4252 else => unreachable,
4253 }
4254 }
4255}
4256
4257pub fn wait4(pid: pid_t, flags: u32, ru: ?*rusage) WaitPidResult {
4258 var status: if (builtin.link_libc) c_int else u32 = undefined;
4259 while (true) {
4260 const rc = system.wait4(pid, &status, @intCast(flags), ru);
4261 switch (errno(rc)) {
4262 .SUCCESS => return .{
4263 .pid = @intCast(rc),
4264 .status = @bitCast(status),
4265 },
4266 .INTR => continue,
4267 .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
4268 .INVAL => unreachable, // Invalid flags.
4269 else => unreachable,
4270 }
4271 }
4272}
4273
4274pub const FStatError = error{
4275 SystemResources,
4276
4277 /// In WASI, this error may occur when the file descriptor does
4278 /// not hold the required rights to get its filestat information.
4279 AccessDenied,
4280} || UnexpectedError;
4281
4282/// Return information about a file descriptor.
4283pub fn fstat(fd: fd_t) FStatError!Stat {
4284 if (native_os == .wasi and !builtin.link_libc) {
4285 return Stat.fromFilestat(try fstat_wasi(fd));
4286 }
4287 if (native_os == .windows) {
4288 @compileError("fstat is not yet implemented on Windows");
4289 }
4290
4291 const fstat_sym = if (lfs64_abi) system.fstat64 else system.fstat;
4292 var stat = mem.zeroes(Stat);
4293 switch (errno(fstat_sym(fd, &stat))) {
4294 .SUCCESS => return stat,
4295 .INVAL => unreachable,
4296 .BADF => unreachable, // Always a race condition.
4297 .NOMEM => return error.SystemResources,
4298 .ACCES => return error.AccessDenied,
4299 else => |err| return unexpectedErrno(err),
4300 }
4301}
4302
4303fn fstat_wasi(fd: fd_t) FStatError!wasi.filestat_t {
4304 var stat: wasi.filestat_t = undefined;
4305 switch (wasi.fd_filestat_get(fd, &stat)) {
4306 .SUCCESS => return stat,
4307 .INVAL => unreachable,
4308 .BADF => unreachable, // Always a race condition.
4309 .NOMEM => return error.SystemResources,
4310 .ACCES => return error.AccessDenied,
4311 .NOTCAPABLE => return error.AccessDenied,
4312 else => |err| return unexpectedErrno(err),
4313 }
4314}
4315
4316pub const FStatAtError = FStatError || error{
4317 NameTooLong,
4318 FileNotFound,
4319 SymLinkLoop,
4320 /// WASI-only; file paths must be valid UTF-8.
4321 InvalidUtf8,
4322};
4323
4324/// Similar to `fstat`, but returns stat of a resource pointed to by `pathname`
4325/// which is relative to `dirfd` handle.
4326/// On WASI, `pathname` should be encoded as valid UTF-8.
4327/// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding.
4328/// See also `fstatatZ` and `fstatat_wasi`.
4329pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
4330 if (native_os == .wasi and !builtin.link_libc) {
4331 const filestat = try fstatat_wasi(dirfd, pathname, .{
4332 .SYMLINK_FOLLOW = (flags & AT.SYMLINK_NOFOLLOW) == 0,
4333 });
4334 return Stat.fromFilestat(filestat);
4335 } else if (native_os == .windows) {
4336 @compileError("fstatat is not yet implemented on Windows");
4337 } else {
4338 const pathname_c = try toPosixPath(pathname);
4339 return fstatatZ(dirfd, &pathname_c, flags);
4340 }
4341}
4342
4343/// Same as `fstatat` but `pathname` is null-terminated.
4344/// See also `fstatat`.
4345pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat {
4346 if (native_os == .wasi and !builtin.link_libc) {
4347 const filestat = try fstatat_wasi(dirfd, mem.sliceTo(pathname, 0), .{
4348 .SYMLINK_FOLLOW = (flags & AT.SYMLINK_NOFOLLOW) == 0,
4349 });
4350 return Stat.fromFilestat(filestat);
4351 }
4352
4353 const fstatat_sym = if (lfs64_abi) system.fstatat64 else system.fstatat;
4354 var stat = mem.zeroes(Stat);
4355 switch (errno(fstatat_sym(dirfd, pathname, &stat, flags))) {
4356 .SUCCESS => return stat,
4357 .INVAL => unreachable,
4358 .BADF => unreachable, // Always a race condition.
4359 .NOMEM => return error.SystemResources,
4360 .ACCES => return error.AccessDenied,
4361 .PERM => return error.AccessDenied,
4362 .FAULT => unreachable,
4363 .NAMETOOLONG => return error.NameTooLong,
4364 .LOOP => return error.SymLinkLoop,
4365 .NOENT => return error.FileNotFound,
4366 .NOTDIR => return error.FileNotFound,
4367 .ILSEQ => |err| if (native_os == .wasi)
4368 return error.InvalidUtf8
4369 else
4370 return unexpectedErrno(err),
4371 else => |err| return unexpectedErrno(err),
4372 }
4373}
4374
4375/// WASI-only. Same as `fstatat` but targeting WASI.
4376/// `pathname` should be encoded as valid UTF-8.
4377/// See also `fstatat`.
4378fn fstatat_wasi(dirfd: fd_t, pathname: []const u8, flags: wasi.lookupflags_t) FStatAtError!wasi.filestat_t {
4379 var stat: wasi.filestat_t = undefined;
4380 switch (wasi.path_filestat_get(dirfd, flags, pathname.ptr, pathname.len, &stat)) {
4381 .SUCCESS => return stat,
4382 .INVAL => unreachable,
4383 .BADF => unreachable, // Always a race condition.
4384 .NOMEM => return error.SystemResources,
4385 .ACCES => return error.AccessDenied,
4386 .FAULT => unreachable,
4387 .NAMETOOLONG => return error.NameTooLong,
4388 .NOENT => return error.FileNotFound,
4389 .NOTDIR => return error.FileNotFound,
4390 .NOTCAPABLE => return error.AccessDenied,
4391 .ILSEQ => return error.InvalidUtf8,
4392 else => |err| return unexpectedErrno(err),
4393 }
4394}
4395
4396pub const KQueueError = error{
4397 /// The per-process limit on the number of open file descriptors has been reached.
4398 ProcessFdQuotaExceeded,
4399
4400 /// The system-wide limit on the total number of open files has been reached.
4401 SystemFdQuotaExceeded,
4402} || UnexpectedError;
4403
4404pub fn kqueue() KQueueError!i32 {
4405 const rc = system.kqueue();
4406 switch (errno(rc)) {
4407 .SUCCESS => return @intCast(rc),
4408 .MFILE => return error.ProcessFdQuotaExceeded,
4409 .NFILE => return error.SystemFdQuotaExceeded,
4410 else => |err| return unexpectedErrno(err),
4411 }
4412}
4413
4414pub const KEventError = error{
4415 /// The process does not have permission to register a filter.
4416 AccessDenied,
4417
4418 /// The event could not be found to be modified or deleted.
4419 EventNotFound,
4420
4421 /// No memory was available to register the event.
4422 SystemResources,
4423
4424 /// The specified process to attach to does not exist.
4425 ProcessNotFound,
4426
4427 /// changelist or eventlist had too many items on it.
4428 /// TODO remove this possibility
4429 Overflow,
4430};
4431
4432pub fn kevent(
4433 kq: i32,
4434 changelist: []const Kevent,
4435 eventlist: []Kevent,
4436 timeout: ?*const timespec,
4437) KEventError!usize {
4438 while (true) {
4439 const rc = system.kevent(
4440 kq,
4441 changelist.ptr,
4442 cast(c_int, changelist.len) orelse return error.Overflow,
4443 eventlist.ptr,
4444 cast(c_int, eventlist.len) orelse return error.Overflow,
4445 timeout,
4446 );
4447 switch (errno(rc)) {
4448 .SUCCESS => return @intCast(rc),
4449 .ACCES => return error.AccessDenied,
4450 .FAULT => unreachable,
4451 .BADF => unreachable, // Always a race condition.
4452 .INTR => continue,
4453 .INVAL => unreachable,
4454 .NOENT => return error.EventNotFound,
4455 .NOMEM => return error.SystemResources,
4456 .SRCH => return error.ProcessNotFound,
4457 else => unreachable,
4458 }
4459 }
4460}
4461
4462pub const INotifyInitError = error{
4463 ProcessFdQuotaExceeded,
4464 SystemFdQuotaExceeded,
4465 SystemResources,
4466} || UnexpectedError;
4467
4468/// initialize an inotify instance
4469pub fn inotify_init1(flags: u32) INotifyInitError!i32 {
4470 const rc = system.inotify_init1(flags);
4471 switch (errno(rc)) {
4472 .SUCCESS => return @intCast(rc),
4473 .INVAL => unreachable,
4474 .MFILE => return error.ProcessFdQuotaExceeded,
4475 .NFILE => return error.SystemFdQuotaExceeded,
4476 .NOMEM => return error.SystemResources,
4477 else => |err| return unexpectedErrno(err),
4478 }
4479}
4480
4481pub const INotifyAddWatchError = error{
4482 AccessDenied,
4483 NameTooLong,
4484 FileNotFound,
4485 SystemResources,
4486 UserResourceLimitReached,
4487 NotDir,
4488 WatchAlreadyExists,
4489} || UnexpectedError;
4490
4491/// add a watch to an initialized inotify instance
4492pub fn inotify_add_watch(inotify_fd: i32, pathname: []const u8, mask: u32) INotifyAddWatchError!i32 {
4493 const pathname_c = try toPosixPath(pathname);
4494 return inotify_add_watchZ(inotify_fd, &pathname_c, mask);
4495}
4496
4497/// Same as `inotify_add_watch` except pathname is null-terminated.
4498pub fn inotify_add_watchZ(inotify_fd: i32, pathname: [*:0]const u8, mask: u32) INotifyAddWatchError!i32 {
4499 const rc = system.inotify_add_watch(inotify_fd, pathname, mask);
4500 switch (errno(rc)) {
4501 .SUCCESS => return @intCast(rc),
4502 .ACCES => return error.AccessDenied,
4503 .BADF => unreachable,
4504 .FAULT => unreachable,
4505 .INVAL => unreachable,
4506 .NAMETOOLONG => return error.NameTooLong,
4507 .NOENT => return error.FileNotFound,
4508 .NOMEM => return error.SystemResources,
4509 .NOSPC => return error.UserResourceLimitReached,
4510 .NOTDIR => return error.NotDir,
4511 .EXIST => return error.WatchAlreadyExists,
4512 else => |err| return unexpectedErrno(err),
4513 }
4514}
4515
4516/// remove an existing watch from an inotify instance
4517pub fn inotify_rm_watch(inotify_fd: i32, wd: i32) void {
4518 switch (errno(system.inotify_rm_watch(inotify_fd, wd))) {
4519 .SUCCESS => return,
4520 .BADF => unreachable,
4521 .INVAL => unreachable,
4522 else => unreachable,
4523 }
4524}
4525
4526pub const FanotifyInitError = error{
4527 ProcessFdQuotaExceeded,
4528 SystemFdQuotaExceeded,
4529 SystemResources,
4530 OperationNotSupported,
4531 PermissionDenied,
4532} || UnexpectedError;
4533
4534pub fn fanotify_init(flags: u32, event_f_flags: u32) FanotifyInitError!i32 {
4535 const rc = system.fanotify_init(flags, event_f_flags);
4536 switch (errno(rc)) {
4537 .SUCCESS => return @intCast(rc),
4538 .INVAL => unreachable,
4539 .MFILE => return error.ProcessFdQuotaExceeded,
4540 .NFILE => return error.SystemFdQuotaExceeded,
4541 .NOMEM => return error.SystemResources,
4542 .NOSYS => return error.OperationNotSupported,
4543 .PERM => return error.PermissionDenied,
4544 else => |err| return unexpectedErrno(err),
4545 }
4546}
4547
4548pub const FanotifyMarkError = error{
4549 MarkAlreadyExists,
4550 IsDir,
4551 NotAssociatedWithFileSystem,
4552 FileNotFound,
4553 SystemResources,
4554 UserMarkQuotaExceeded,
4555 NotImplemented,
4556 NotDir,
4557 OperationNotSupported,
4558 PermissionDenied,
4559 NotSameFileSystem,
4560 NameTooLong,
4561} || UnexpectedError;
4562
4563pub fn fanotify_mark(fanotify_fd: i32, flags: u32, mask: u64, dirfd: i32, pathname: ?[]const u8) FanotifyMarkError!void {
4564 if (pathname) |path| {
4565 const path_c = try toPosixPath(path);
4566 return fanotify_markZ(fanotify_fd, flags, mask, dirfd, &path_c);
4567 }
4568
4569 return fanotify_markZ(fanotify_fd, flags, mask, dirfd, null);
4570}
4571
4572pub fn fanotify_markZ(fanotify_fd: i32, flags: u32, mask: u64, dirfd: i32, pathname: ?[*:0]const u8) FanotifyMarkError!void {
4573 const rc = system.fanotify_mark(fanotify_fd, flags, mask, dirfd, pathname);
4574 switch (errno(rc)) {
4575 .SUCCESS => return,
4576 .BADF => unreachable,
4577 .EXIST => return error.MarkAlreadyExists,
4578 .INVAL => unreachable,
4579 .ISDIR => return error.IsDir,
4580 .NODEV => return error.NotAssociatedWithFileSystem,
4581 .NOENT => return error.FileNotFound,
4582 .NOMEM => return error.SystemResources,
4583 .NOSPC => return error.UserMarkQuotaExceeded,
4584 .NOSYS => return error.NotImplemented,
4585 .NOTDIR => return error.NotDir,
4586 .OPNOTSUPP => return error.OperationNotSupported,
4587 .PERM => return error.PermissionDenied,
4588 .XDEV => return error.NotSameFileSystem,
4589 else => |err| return unexpectedErrno(err),
4590 }
4591}
4592
4593pub const MProtectError = error{
4594 /// The memory cannot be given the specified access. This can happen, for example, if you
4595 /// mmap(2) a file to which you have read-only access, then ask mprotect() to mark it
4596 /// PROT_WRITE.
4597 AccessDenied,
4598
4599 /// Changing the protection of a memory region would result in the total number of map‐
4600 /// pings with distinct attributes (e.g., read versus read/write protection) exceeding the
4601 /// allowed maximum. (For example, making the protection of a range PROT_READ in the mid‐
4602 /// dle of a region currently protected as PROT_READ|PROT_WRITE would result in three map‐
4603 /// pings: two read/write mappings at each end and a read-only mapping in the middle.)
4604 OutOfMemory,
4605} || UnexpectedError;
4606
4607/// `memory.len` must be page-aligned.
4608pub fn mprotect(memory: []align(mem.page_size) u8, protection: u32) MProtectError!void {
4609 assert(mem.isAligned(memory.len, mem.page_size));
4610 if (native_os == .windows) {
4611 const win_prot: windows.DWORD = switch (@as(u3, @truncate(protection))) {
4612 0b000 => windows.PAGE_NOACCESS,
4613 0b001 => windows.PAGE_READONLY,
4614 0b010 => unreachable, // +w -r not allowed
4615 0b011 => windows.PAGE_READWRITE,
4616 0b100 => windows.PAGE_EXECUTE,
4617 0b101 => windows.PAGE_EXECUTE_READ,
4618 0b110 => unreachable, // +w -r not allowed
4619 0b111 => windows.PAGE_EXECUTE_READWRITE,
4620 };
4621 var old: windows.DWORD = undefined;
4622 windows.VirtualProtect(memory.ptr, memory.len, win_prot, &old) catch |err| switch (err) {
4623 error.InvalidAddress => return error.AccessDenied,
4624 error.Unexpected => return error.Unexpected,
4625 };
4626 } else {
4627 switch (errno(system.mprotect(memory.ptr, memory.len, protection))) {
4628 .SUCCESS => return,
4629 .INVAL => unreachable,
4630 .ACCES => return error.AccessDenied,
4631 .NOMEM => return error.OutOfMemory,
4632 else => |err| return unexpectedErrno(err),
4633 }
4634 }
4635}
4636
4637pub const ForkError = error{SystemResources} || UnexpectedError;
4638
4639pub fn fork() ForkError!pid_t {
4640 const rc = system.fork();
4641 switch (errno(rc)) {
4642 .SUCCESS => return @intCast(rc),
4643 .AGAIN => return error.SystemResources,
4644 .NOMEM => return error.SystemResources,
4645 else => |err| return unexpectedErrno(err),
4646 }
4647}
4648
4649pub const MMapError = error{
4650 /// The underlying filesystem of the specified file does not support memory mapping.
4651 MemoryMappingNotSupported,
4652
4653 /// A file descriptor refers to a non-regular file. Or a file mapping was requested,
4654 /// but the file descriptor is not open for reading. Or `MAP.SHARED` was requested
4655 /// and `PROT_WRITE` is set, but the file descriptor is not open in `RDWR` mode.
4656 /// Or `PROT_WRITE` is set, but the file is append-only.
4657 AccessDenied,
4658
4659 /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on
4660 /// a filesystem that was mounted no-exec.
4661 PermissionDenied,
4662 LockedMemoryLimitExceeded,
4663 ProcessFdQuotaExceeded,
4664 SystemFdQuotaExceeded,
4665 OutOfMemory,
4666} || UnexpectedError;
4667
4668/// Map files or devices into memory.
4669/// `length` does not need to be aligned.
4670/// Use of a mapped region can result in these signals:
4671/// * SIGSEGV - Attempted write into a region mapped as read-only.
4672/// * SIGBUS - Attempted access to a portion of the buffer that does not correspond to the file
4673pub fn mmap(
4674 ptr: ?[*]align(mem.page_size) u8,
4675 length: usize,
4676 prot: u32,
4677 flags: system.MAP,
4678 fd: fd_t,
4679 offset: u64,
4680) MMapError![]align(mem.page_size) u8 {
4681 const mmap_sym = if (lfs64_abi) system.mmap64 else system.mmap;
4682 const rc = mmap_sym(ptr, length, prot, @bitCast(flags), fd, @bitCast(offset));
4683 const err: E = if (builtin.link_libc) blk: {
4684 if (rc != std.c.MAP_FAILED) return @as([*]align(mem.page_size) u8, @ptrCast(@alignCast(rc)))[0..length];
4685 break :blk @enumFromInt(system._errno().*);
4686 } else blk: {
4687 const err = errno(rc);
4688 if (err == .SUCCESS) return @as([*]align(mem.page_size) u8, @ptrFromInt(rc))[0..length];
4689 break :blk err;
4690 };
4691 switch (err) {
4692 .SUCCESS => unreachable,
4693 .TXTBSY => return error.AccessDenied,
4694 .ACCES => return error.AccessDenied,
4695 .PERM => return error.PermissionDenied,
4696 .AGAIN => return error.LockedMemoryLimitExceeded,
4697 .BADF => unreachable, // Always a race condition.
4698 .OVERFLOW => unreachable, // The number of pages used for length + offset would overflow.
4699 .NODEV => return error.MemoryMappingNotSupported,
4700 .INVAL => unreachable, // Invalid parameters to mmap()
4701 .MFILE => return error.ProcessFdQuotaExceeded,
4702 .NFILE => return error.SystemFdQuotaExceeded,
4703 .NOMEM => return error.OutOfMemory,
4704 else => return unexpectedErrno(err),
4705 }
4706}
4707
4708/// Deletes the mappings for the specified address range, causing
4709/// further references to addresses within the range to generate invalid memory references.
4710/// Note that while POSIX allows unmapping a region in the middle of an existing mapping,
4711/// Zig's munmap function does not, for two reasons:
4712/// * It violates the Zig principle that resource deallocation must succeed.
4713/// * The Windows function, VirtualFree, has this restriction.
4714pub fn munmap(memory: []align(mem.page_size) const u8) void {
4715 switch (errno(system.munmap(memory.ptr, memory.len))) {
4716 .SUCCESS => return,
4717 .INVAL => unreachable, // Invalid parameters.
4718 .NOMEM => unreachable, // Attempted to unmap a region in the middle of an existing mapping.
4719 else => unreachable,
4720 }
4721}
4722
4723pub const MSyncError = error{
4724 UnmappedMemory,
4725} || UnexpectedError;
4726
4727pub fn msync(memory: []align(mem.page_size) u8, flags: i32) MSyncError!void {
4728 switch (errno(system.msync(memory.ptr, memory.len, flags))) {
4729 .SUCCESS => return,
4730 .NOMEM => return error.UnmappedMemory, // Unsuccessful, provided pointer does not point mapped memory
4731 .INVAL => unreachable, // Invalid parameters.
4732 else => unreachable,
4733 }
4734}
4735
4736pub const AccessError = error{
4737 PermissionDenied,
4738 FileNotFound,
4739 NameTooLong,
4740 InputOutput,
4741 SystemResources,
4742 BadPathName,
4743 FileBusy,
4744 SymLinkLoop,
4745 ReadOnlyFileSystem,
4746 /// WASI-only; file paths must be valid UTF-8.
4747 InvalidUtf8,
4748 /// Windows-only; file paths provided by the user must be valid WTF-8.
4749 /// https://simonsapin.github.io/wtf-8/
4750 InvalidWtf8,
4751} || UnexpectedError;
4752
4753/// check user's permissions for a file
4754///
4755/// * On Windows, asserts `path` is valid [WTF-8](https://simonsapin.github.io/wtf-8/).
4756/// * On WASI, invalid UTF-8 passed to `path` causes `error.InvalidUtf8`.
4757/// * On other platforms, `path` is an opaque sequence of bytes with no particular encoding.
4758///
4759/// On Windows, `mode` is ignored. This is a POSIX API that is only partially supported by
4760/// Windows. See `fs` for the cross-platform file system API.
4761pub fn access(path: []const u8, mode: u32) AccessError!void {
4762 if (native_os == .windows) {
4763 const path_w = windows.sliceToPrefixedFileW(null, path) catch |err| switch (err) {
4764 error.AccessDenied => return error.PermissionDenied,
4765 else => |e| return e,
4766 };
4767 _ = try windows.GetFileAttributesW(path_w.span().ptr);
4768 return;
4769 } else if (native_os == .wasi and !builtin.link_libc) {
4770 return faccessat(wasi.AT.FDCWD, path, mode, 0);
4771 }
4772 const path_c = try toPosixPath(path);
4773 return accessZ(&path_c, mode);
4774}
4775
4776/// Same as `access` except `path` is null-terminated.
4777pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void {
4778 if (native_os == .windows) {
4779 const path_w = windows.cStrToPrefixedFileW(null, path) catch |err| switch (err) {
4780 error.AccessDenied => return error.PermissionDenied,
4781 else => |e| return e,
4782 };
4783 _ = try windows.GetFileAttributesW(path_w.span().ptr);
4784 return;
4785 } else if (native_os == .wasi and !builtin.link_libc) {
4786 return access(mem.sliceTo(path, 0), mode);
4787 }
4788 switch (errno(system.access(path, mode))) {
4789 .SUCCESS => return,
4790 .ACCES => return error.PermissionDenied,
4791 .ROFS => return error.ReadOnlyFileSystem,
4792 .LOOP => return error.SymLinkLoop,
4793 .TXTBSY => return error.FileBusy,
4794 .NOTDIR => return error.FileNotFound,
4795 .NOENT => return error.FileNotFound,
4796 .NAMETOOLONG => return error.NameTooLong,
4797 .INVAL => unreachable,
4798 .FAULT => unreachable,
4799 .IO => return error.InputOutput,
4800 .NOMEM => return error.SystemResources,
4801 .ILSEQ => |err| if (native_os == .wasi)
4802 return error.InvalidUtf8
4803 else
4804 return unexpectedErrno(err),
4805 else => |err| return unexpectedErrno(err),
4806 }
4807}
4808
4809/// Check user's permissions for a file, based on an open directory handle.
4810///
4811/// * On Windows, asserts `path` is valid [WTF-8](https://simonsapin.github.io/wtf-8/).
4812/// * On WASI, invalid UTF-8 passed to `path` causes `error.InvalidUtf8`.
4813/// * On other platforms, `path` is an opaque sequence of bytes with no particular encoding.
4814///
4815/// On Windows, `mode` is ignored. This is a POSIX API that is only partially supported by
4816/// Windows. See `fs` for the cross-platform file system API.
4817pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void {
4818 if (native_os == .windows) {
4819 const path_w = try windows.sliceToPrefixedFileW(dirfd, path);
4820 return faccessatW(dirfd, path_w.span().ptr);
4821 } else if (native_os == .wasi and !builtin.link_libc) {
4822 const resolved: RelativePathWasi = .{ .dir_fd = dirfd, .relative_path = path };
4823
4824 const st = blk: {
4825 break :blk fstatat_wasi(dirfd, path, .{
4826 .SYMLINK_FOLLOW = (flags & AT.SYMLINK_NOFOLLOW) == 0,
4827 });
4828 } catch |err| switch (err) {
4829 error.AccessDenied => return error.PermissionDenied,
4830 else => |e| return e,
4831 };
4832
4833 if (mode != F_OK) {
4834 var directory: wasi.fdstat_t = undefined;
4835 if (wasi.fd_fdstat_get(resolved.dir_fd, &directory) != .SUCCESS) {
4836 return error.PermissionDenied;
4837 }
4838
4839 var rights: wasi.rights_t = .{};
4840 if (mode & R_OK != 0) {
4841 if (st.filetype == .DIRECTORY) {
4842 rights.FD_READDIR = true;
4843 } else {
4844 rights.FD_READ = true;
4845 }
4846 }
4847 if (mode & W_OK != 0) {
4848 rights.FD_WRITE = true;
4849 }
4850 // No validation for X_OK
4851
4852 // https://github.com/ziglang/zig/issues/18882
4853 const rights_int: u64 = @bitCast(rights);
4854 const inheriting_int: u64 = @bitCast(directory.fs_rights_inheriting);
4855 if ((rights_int & inheriting_int) != rights_int) {
4856 return error.PermissionDenied;
4857 }
4858 }
4859 return;
4860 }
4861 const path_c = try toPosixPath(path);
4862 return faccessatZ(dirfd, &path_c, mode, flags);
4863}
4864
4865/// Same as `faccessat` except the path parameter is null-terminated.
4866pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void {
4867 if (native_os == .windows) {
4868 const path_w = try windows.cStrToPrefixedFileW(dirfd, path);
4869 return faccessatW(dirfd, path_w.span().ptr);
4870 } else if (native_os == .wasi and !builtin.link_libc) {
4871 return faccessat(dirfd, mem.sliceTo(path, 0), mode, flags);
4872 }
4873 switch (errno(system.faccessat(dirfd, path, mode, flags))) {
4874 .SUCCESS => return,
4875 .ACCES => return error.PermissionDenied,
4876 .ROFS => return error.ReadOnlyFileSystem,
4877 .LOOP => return error.SymLinkLoop,
4878 .TXTBSY => return error.FileBusy,
4879 .NOTDIR => return error.FileNotFound,
4880 .NOENT => return error.FileNotFound,
4881 .NAMETOOLONG => return error.NameTooLong,
4882 .INVAL => unreachable,
4883 .FAULT => unreachable,
4884 .IO => return error.InputOutput,
4885 .NOMEM => return error.SystemResources,
4886 .ILSEQ => |err| if (native_os == .wasi)
4887 return error.InvalidUtf8
4888 else
4889 return unexpectedErrno(err),
4890 else => |err| return unexpectedErrno(err),
4891 }
4892}
4893
4894/// Same as `faccessat` except asserts the target is Windows and the path parameter
4895/// is NtDll-prefixed, null-terminated, WTF-16 encoded.
4896pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16) AccessError!void {
4897 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
4898 return;
4899 }
4900 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
4901 return;
4902 }
4903
4904 const path_len_bytes = cast(u16, mem.sliceTo(sub_path_w, 0).len * 2) orelse return error.NameTooLong;
4905 var nt_name = windows.UNICODE_STRING{
4906 .Length = path_len_bytes,
4907 .MaximumLength = path_len_bytes,
4908 .Buffer = @constCast(sub_path_w),
4909 };
4910 var attr = windows.OBJECT_ATTRIBUTES{
4911 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
4912 .RootDirectory = if (fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd,
4913 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
4914 .ObjectName = &nt_name,
4915 .SecurityDescriptor = null,
4916 .SecurityQualityOfService = null,
4917 };
4918 var basic_info: windows.FILE_BASIC_INFORMATION = undefined;
4919 switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) {
4920 .SUCCESS => return,
4921 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
4922 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
4923 .OBJECT_NAME_INVALID => unreachable,
4924 .INVALID_PARAMETER => unreachable,
4925 .ACCESS_DENIED => return error.PermissionDenied,
4926 .OBJECT_PATH_SYNTAX_BAD => unreachable,
4927 else => |rc| return windows.unexpectedStatus(rc),
4928 }
4929}
4930
4931pub const PipeError = error{
4932 SystemFdQuotaExceeded,
4933 ProcessFdQuotaExceeded,
4934} || UnexpectedError;
4935
4936/// Creates a unidirectional data channel that can be used for interprocess communication.
4937pub fn pipe() PipeError![2]fd_t {
4938 var fds: [2]fd_t = undefined;
4939 switch (errno(system.pipe(&fds))) {
4940 .SUCCESS => return fds,
4941 .INVAL => unreachable, // Invalid parameters to pipe()
4942 .FAULT => unreachable, // Invalid fds pointer
4943 .NFILE => return error.SystemFdQuotaExceeded,
4944 .MFILE => return error.ProcessFdQuotaExceeded,
4945 else => |err| return unexpectedErrno(err),
4946 }
4947}
4948
4949pub fn pipe2(flags: O) PipeError![2]fd_t {
4950 // https://github.com/ziglang/zig/issues/19352
4951 if (@hasDecl(system, "pipe2")) {
4952 var fds: [2]fd_t = undefined;
4953 switch (errno(system.pipe2(&fds, flags))) {
4954 .SUCCESS => return fds,
4955 .INVAL => unreachable, // Invalid flags
4956 .FAULT => unreachable, // Invalid fds pointer
4957 .NFILE => return error.SystemFdQuotaExceeded,
4958 .MFILE => return error.ProcessFdQuotaExceeded,
4959 else => |err| return unexpectedErrno(err),
4960 }
4961 }
4962
4963 const fds: [2]fd_t = try pipe();
4964 errdefer {
4965 close(fds[0]);
4966 close(fds[1]);
4967 }
4968
4969 // https://github.com/ziglang/zig/issues/18882
4970 if (@as(u32, @bitCast(flags)) == 0)
4971 return fds;
4972
4973 // CLOEXEC is special, it's a file descriptor flag and must be set using
4974 // F.SETFD.
4975 if (flags.CLOEXEC) {
4976 for (fds) |fd| {
4977 switch (errno(system.fcntl(fd, F.SETFD, @as(u32, FD_CLOEXEC)))) {
4978 .SUCCESS => {},
4979 .INVAL => unreachable, // Invalid flags
4980 .BADF => unreachable, // Always a race condition
4981 else => |err| return unexpectedErrno(err),
4982 }
4983 }
4984 }
4985
4986 const new_flags: u32 = f: {
4987 var new_flags = flags;
4988 new_flags.CLOEXEC = false;
4989 break :f @bitCast(new_flags);
4990 };
4991 // Set every other flag affecting the file status using F.SETFL.
4992 if (new_flags != 0) {
4993 for (fds) |fd| {
4994 switch (errno(system.fcntl(fd, F.SETFL, new_flags))) {
4995 .SUCCESS => {},
4996 .INVAL => unreachable, // Invalid flags
4997 .BADF => unreachable, // Always a race condition
4998 else => |err| return unexpectedErrno(err),
4999 }
5000 }
5001 }
5002
5003 return fds;
5004}
5005
5006pub const SysCtlError = error{
5007 PermissionDenied,
5008 SystemResources,
5009 NameTooLong,
5010 UnknownName,
5011} || UnexpectedError;
5012
5013pub fn sysctl(
5014 name: []const c_int,
5015 oldp: ?*anyopaque,
5016 oldlenp: ?*usize,
5017 newp: ?*anyopaque,
5018 newlen: usize,
5019) SysCtlError!void {
5020 if (native_os == .wasi) {
5021 @panic("unsupported"); // TODO should be compile error, not panic
5022 }
5023 if (native_os == .haiku) {
5024 @panic("unsupported"); // TODO should be compile error, not panic
5025 }
5026
5027 const name_len = cast(c_uint, name.len) orelse return error.NameTooLong;
5028 switch (errno(system.sysctl(name.ptr, name_len, oldp, oldlenp, newp, newlen))) {
5029 .SUCCESS => return,
5030 .FAULT => unreachable,
5031 .PERM => return error.PermissionDenied,
5032 .NOMEM => return error.SystemResources,
5033 .NOENT => return error.UnknownName,
5034 else => |err| return unexpectedErrno(err),
5035 }
5036}
5037
5038pub fn sysctlbynameZ(
5039 name: [*:0]const u8,
5040 oldp: ?*anyopaque,
5041 oldlenp: ?*usize,
5042 newp: ?*anyopaque,
5043 newlen: usize,
5044) SysCtlError!void {
5045 if (native_os == .wasi) {
5046 @panic("unsupported"); // TODO should be compile error, not panic
5047 }
5048 if (native_os == .haiku) {
5049 @panic("unsupported"); // TODO should be compile error, not panic
5050 }
5051
5052 switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) {
5053 .SUCCESS => return,
5054 .FAULT => unreachable,
5055 .PERM => return error.PermissionDenied,
5056 .NOMEM => return error.SystemResources,
5057 .NOENT => return error.UnknownName,
5058 else => |err| return unexpectedErrno(err),
5059 }
5060}
5061
5062pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void {
5063 switch (errno(system.gettimeofday(tv, tz))) {
5064 .SUCCESS => return,
5065 .INVAL => unreachable,
5066 else => unreachable,
5067 }
5068}
5069
5070pub const SeekError = error{
5071 Unseekable,
5072
5073 /// In WASI, this error may occur when the file descriptor does
5074 /// not hold the required rights to seek on it.
5075 AccessDenied,
5076} || UnexpectedError;
5077
5078/// Repositions read/write file offset relative to the beginning.
5079pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
5080 if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
5081 var result: u64 = undefined;
5082 switch (errno(system.llseek(fd, offset, &result, SEEK.SET))) {
5083 .SUCCESS => return,
5084 .BADF => unreachable, // always a race condition
5085 .INVAL => return error.Unseekable,
5086 .OVERFLOW => return error.Unseekable,
5087 .SPIPE => return error.Unseekable,
5088 .NXIO => return error.Unseekable,
5089 else => |err| return unexpectedErrno(err),
5090 }
5091 }
5092 if (native_os == .windows) {
5093 return windows.SetFilePointerEx_BEGIN(fd, offset);
5094 }
5095 if (native_os == .wasi and !builtin.link_libc) {
5096 var new_offset: wasi.filesize_t = undefined;
5097 switch (wasi.fd_seek(fd, @bitCast(offset), .SET, &new_offset)) {
5098 .SUCCESS => return,
5099 .BADF => unreachable, // always a race condition
5100 .INVAL => return error.Unseekable,
5101 .OVERFLOW => return error.Unseekable,
5102 .SPIPE => return error.Unseekable,
5103 .NXIO => return error.Unseekable,
5104 .NOTCAPABLE => return error.AccessDenied,
5105 else => |err| return unexpectedErrno(err),
5106 }
5107 }
5108
5109 const lseek_sym = if (lfs64_abi) system.lseek64 else system.lseek;
5110 switch (errno(lseek_sym(fd, @bitCast(offset), SEEK.SET))) {
5111 .SUCCESS => return,
5112 .BADF => unreachable, // always a race condition
5113 .INVAL => return error.Unseekable,
5114 .OVERFLOW => return error.Unseekable,
5115 .SPIPE => return error.Unseekable,
5116 .NXIO => return error.Unseekable,
5117 else => |err| return unexpectedErrno(err),
5118 }
5119}
5120
5121/// Repositions read/write file offset relative to the current offset.
5122pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
5123 if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
5124 var result: u64 = undefined;
5125 switch (errno(system.llseek(fd, @bitCast(offset), &result, SEEK.CUR))) {
5126 .SUCCESS => return,
5127 .BADF => unreachable, // always a race condition
5128 .INVAL => return error.Unseekable,
5129 .OVERFLOW => return error.Unseekable,
5130 .SPIPE => return error.Unseekable,
5131 .NXIO => return error.Unseekable,
5132 else => |err| return unexpectedErrno(err),
5133 }
5134 }
5135 if (native_os == .windows) {
5136 return windows.SetFilePointerEx_CURRENT(fd, offset);
5137 }
5138 if (native_os == .wasi and !builtin.link_libc) {
5139 var new_offset: wasi.filesize_t = undefined;
5140 switch (wasi.fd_seek(fd, offset, .CUR, &new_offset)) {
5141 .SUCCESS => return,
5142 .BADF => unreachable, // always a race condition
5143 .INVAL => return error.Unseekable,
5144 .OVERFLOW => return error.Unseekable,
5145 .SPIPE => return error.Unseekable,
5146 .NXIO => return error.Unseekable,
5147 .NOTCAPABLE => return error.AccessDenied,
5148 else => |err| return unexpectedErrno(err),
5149 }
5150 }
5151 const lseek_sym = if (lfs64_abi) system.lseek64 else system.lseek;
5152 switch (errno(lseek_sym(fd, @bitCast(offset), SEEK.CUR))) {
5153 .SUCCESS => return,
5154 .BADF => unreachable, // always a race condition
5155 .INVAL => return error.Unseekable,
5156 .OVERFLOW => return error.Unseekable,
5157 .SPIPE => return error.Unseekable,
5158 .NXIO => return error.Unseekable,
5159 else => |err| return unexpectedErrno(err),
5160 }
5161}
5162
5163/// Repositions read/write file offset relative to the end.
5164pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
5165 if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
5166 var result: u64 = undefined;
5167 switch (errno(system.llseek(fd, @bitCast(offset), &result, SEEK.END))) {
5168 .SUCCESS => return,
5169 .BADF => unreachable, // always a race condition
5170 .INVAL => return error.Unseekable,
5171 .OVERFLOW => return error.Unseekable,
5172 .SPIPE => return error.Unseekable,
5173 .NXIO => return error.Unseekable,
5174 else => |err| return unexpectedErrno(err),
5175 }
5176 }
5177 if (native_os == .windows) {
5178 return windows.SetFilePointerEx_END(fd, offset);
5179 }
5180 if (native_os == .wasi and !builtin.link_libc) {
5181 var new_offset: wasi.filesize_t = undefined;
5182 switch (wasi.fd_seek(fd, offset, .END, &new_offset)) {
5183 .SUCCESS => return,
5184 .BADF => unreachable, // always a race condition
5185 .INVAL => return error.Unseekable,
5186 .OVERFLOW => return error.Unseekable,
5187 .SPIPE => return error.Unseekable,
5188 .NXIO => return error.Unseekable,
5189 .NOTCAPABLE => return error.AccessDenied,
5190 else => |err| return unexpectedErrno(err),
5191 }
5192 }
5193 const lseek_sym = if (lfs64_abi) system.lseek64 else system.lseek;
5194 switch (errno(lseek_sym(fd, @bitCast(offset), SEEK.END))) {
5195 .SUCCESS => return,
5196 .BADF => unreachable, // always a race condition
5197 .INVAL => return error.Unseekable,
5198 .OVERFLOW => return error.Unseekable,
5199 .SPIPE => return error.Unseekable,
5200 .NXIO => return error.Unseekable,
5201 else => |err| return unexpectedErrno(err),
5202 }
5203}
5204
5205/// Returns the read/write file offset relative to the beginning.
5206pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
5207 if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
5208 var result: u64 = undefined;
5209 switch (errno(system.llseek(fd, 0, &result, SEEK.CUR))) {
5210 .SUCCESS => return result,
5211 .BADF => unreachable, // always a race condition
5212 .INVAL => return error.Unseekable,
5213 .OVERFLOW => return error.Unseekable,
5214 .SPIPE => return error.Unseekable,
5215 .NXIO => return error.Unseekable,
5216 else => |err| return unexpectedErrno(err),
5217 }
5218 }
5219 if (native_os == .windows) {
5220 return windows.SetFilePointerEx_CURRENT_get(fd);
5221 }
5222 if (native_os == .wasi and !builtin.link_libc) {
5223 var new_offset: wasi.filesize_t = undefined;
5224 switch (wasi.fd_seek(fd, 0, .CUR, &new_offset)) {
5225 .SUCCESS => return new_offset,
5226 .BADF => unreachable, // always a race condition
5227 .INVAL => return error.Unseekable,
5228 .OVERFLOW => return error.Unseekable,
5229 .SPIPE => return error.Unseekable,
5230 .NXIO => return error.Unseekable,
5231 .NOTCAPABLE => return error.AccessDenied,
5232 else => |err| return unexpectedErrno(err),
5233 }
5234 }
5235 const lseek_sym = if (lfs64_abi) system.lseek64 else system.lseek;
5236 const rc = lseek_sym(fd, 0, SEEK.CUR);
5237 switch (errno(rc)) {
5238 .SUCCESS => return @bitCast(rc),
5239 .BADF => unreachable, // always a race condition
5240 .INVAL => return error.Unseekable,
5241 .OVERFLOW => return error.Unseekable,
5242 .SPIPE => return error.Unseekable,
5243 .NXIO => return error.Unseekable,
5244 else => |err| return unexpectedErrno(err),
5245 }
5246}
5247
5248pub const FcntlError = error{
5249 PermissionDenied,
5250 FileBusy,
5251 ProcessFdQuotaExceeded,
5252 Locked,
5253 DeadLock,
5254 LockedRegionLimitExceeded,
5255} || UnexpectedError;
5256
5257pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
5258 while (true) {
5259 const rc = system.fcntl(fd, cmd, arg);
5260 switch (errno(rc)) {
5261 .SUCCESS => return @intCast(rc),
5262 .INTR => continue,
5263 .AGAIN, .ACCES => return error.Locked,
5264 .BADF => unreachable,
5265 .BUSY => return error.FileBusy,
5266 .INVAL => unreachable, // invalid parameters
5267 .PERM => return error.PermissionDenied,
5268 .MFILE => return error.ProcessFdQuotaExceeded,
5269 .NOTDIR => unreachable, // invalid parameter
5270 .DEADLK => return error.DeadLock,
5271 .NOLCK => return error.LockedRegionLimitExceeded,
5272 else => |err| return unexpectedErrno(err),
5273 }
5274 }
5275}
5276
5277pub const FlockError = error{
5278 WouldBlock,
5279
5280 /// The kernel ran out of memory for allocating file locks
5281 SystemResources,
5282
5283 /// The underlying filesystem does not support file locks
5284 FileLocksNotSupported,
5285} || UnexpectedError;
5286
5287/// Depending on the operating system `flock` may or may not interact with
5288/// `fcntl` locks made by other processes.
5289pub fn flock(fd: fd_t, operation: i32) FlockError!void {
5290 while (true) {
5291 const rc = system.flock(fd, operation);
5292 switch (errno(rc)) {
5293 .SUCCESS => return,
5294 .BADF => unreachable,
5295 .INTR => continue,
5296 .INVAL => unreachable, // invalid parameters
5297 .NOLCK => return error.SystemResources,
5298 .AGAIN => return error.WouldBlock, // TODO: integrate with async instead of just returning an error
5299 .OPNOTSUPP => return error.FileLocksNotSupported,
5300 else => |err| return unexpectedErrno(err),
5301 }
5302 }
5303}
5304
5305pub const RealPathError = error{
5306 FileNotFound,
5307 AccessDenied,
5308 NameTooLong,
5309 NotSupported,
5310 NotDir,
5311 SymLinkLoop,
5312 InputOutput,
5313 FileTooBig,
5314 IsDir,
5315 ProcessFdQuotaExceeded,
5316 SystemFdQuotaExceeded,
5317 NoDevice,
5318 SystemResources,
5319 NoSpaceLeft,
5320 FileSystem,
5321 BadPathName,
5322 DeviceBusy,
5323
5324 SharingViolation,
5325 PipeBusy,
5326
5327 /// Windows-only; file paths provided by the user must be valid WTF-8.
5328 /// https://simonsapin.github.io/wtf-8/
5329 InvalidWtf8,
5330
5331 /// On Windows, `\\server` or `\\server\share` was not found.
5332 NetworkNotFound,
5333
5334 PathAlreadyExists,
5335
5336 /// On Windows, antivirus software is enabled by default. It can be
5337 /// disabled, but Windows Update sometimes ignores the user's preference
5338 /// and re-enables it. When enabled, antivirus software on Windows
5339 /// intercepts file system operations and makes them significantly slower
5340 /// in addition to possibly failing with this error code.
5341 AntivirusInterference,
5342
5343 /// On Windows, the volume does not contain a recognized file system. File
5344 /// system drivers might not be loaded, or the volume may be corrupt.
5345 UnrecognizedVolume,
5346} || UnexpectedError;
5347
5348/// Return the canonicalized absolute pathname.
5349///
5350/// Expands all symbolic links and resolves references to `.`, `..`, and
5351/// extra `/` characters in `pathname`.
5352///
5353/// On Windows, `pathname` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
5354///
5355/// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding.
5356///
5357/// The return value is a slice of `out_buffer`, but not necessarily from the beginning.
5358///
5359/// See also `realpathZ` and `realpathW`.
5360///
5361/// * On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
5362/// * On other platforms, the result is an opaque sequence of bytes with no particular encoding.
5363///
5364/// Calling this function is usually a bug.
5365pub fn realpath(pathname: []const u8, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
5366 if (native_os == .windows) {
5367 const pathname_w = try windows.sliceToPrefixedFileW(null, pathname);
5368 return realpathW(pathname_w.span(), out_buffer);
5369 } else if (native_os == .wasi and !builtin.link_libc) {
5370 @compileError("WASI does not support os.realpath");
5371 }
5372 const pathname_c = try toPosixPath(pathname);
5373 return realpathZ(&pathname_c, out_buffer);
5374}
5375
5376/// Same as `realpath` except `pathname` is null-terminated.
5377///
5378/// Calling this function is usually a bug.
5379pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
5380 if (native_os == .windows) {
5381 const pathname_w = try windows.cStrToPrefixedFileW(null, pathname);
5382 return realpathW(pathname_w.span(), out_buffer);
5383 } else if (native_os == .wasi and !builtin.link_libc) {
5384 return realpath(mem.sliceTo(pathname, 0), out_buffer);
5385 }
5386 if (!builtin.link_libc) {
5387 const flags: O = switch (native_os) {
5388 .linux => .{
5389 .NONBLOCK = true,
5390 .CLOEXEC = true,
5391 .PATH = true,
5392 },
5393 else => .{
5394 .NONBLOCK = true,
5395 .CLOEXEC = true,
5396 },
5397 };
5398 const fd = openZ(pathname, flags, 0) catch |err| switch (err) {
5399 error.FileLocksNotSupported => unreachable,
5400 error.WouldBlock => unreachable,
5401 error.FileBusy => unreachable, // not asking for write permissions
5402 error.InvalidUtf8 => unreachable, // WASI-only
5403 else => |e| return e,
5404 };
5405 defer close(fd);
5406
5407 return std.os.getFdPath(fd, out_buffer);
5408 }
5409 const result_path = std.c.realpath(pathname, out_buffer) orelse switch (@as(E, @enumFromInt(std.c._errno().*))) {
5410 .SUCCESS => unreachable,
5411 .INVAL => unreachable,
5412 .BADF => unreachable,
5413 .FAULT => unreachable,
5414 .ACCES => return error.AccessDenied,
5415 .NOENT => return error.FileNotFound,
5416 .OPNOTSUPP => return error.NotSupported,
5417 .NOTDIR => return error.NotDir,
5418 .NAMETOOLONG => return error.NameTooLong,
5419 .LOOP => return error.SymLinkLoop,
5420 .IO => return error.InputOutput,
5421 else => |err| return unexpectedErrno(err),
5422 };
5423 return mem.sliceTo(result_path, 0);
5424}
5425
5426/// Same as `realpath` except `pathname` is WTF16LE-encoded.
5427///
5428/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
5429///
5430/// Calling this function is usually a bug.
5431pub fn realpathW(pathname: []const u16, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
5432 const w = windows;
5433
5434 const dir = fs.cwd().fd;
5435 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
5436 const share_access = w.FILE_SHARE_READ;
5437 const creation = w.FILE_OPEN;
5438 const h_file = blk: {
5439 const res = w.OpenFile(pathname, .{
5440 .dir = dir,
5441 .access_mask = access_mask,
5442 .share_access = share_access,
5443 .creation = creation,
5444 .filter = .any,
5445 }) catch |err| switch (err) {
5446 error.WouldBlock => unreachable,
5447 else => |e| return e,
5448 };
5449 break :blk res;
5450 };
5451 defer w.CloseHandle(h_file);
5452
5453 return std.os.getFdPath(h_file, out_buffer);
5454}
5455
5456/// Spurious wakeups are possible and no precision of timing is guaranteed.
5457pub fn nanosleep(seconds: u64, nanoseconds: u64) void {
5458 var req = timespec{
5459 .tv_sec = cast(isize, seconds) orelse maxInt(isize),
5460 .tv_nsec = cast(isize, nanoseconds) orelse maxInt(isize),
5461 };
5462 var rem: timespec = undefined;
5463 while (true) {
5464 switch (errno(system.nanosleep(&req, &rem))) {
5465 .FAULT => unreachable,
5466 .INVAL => {
5467 // Sometimes Darwin returns EINVAL for no reason.
5468 // We treat it as a spurious wakeup.
5469 return;
5470 },
5471 .INTR => {
5472 req = rem;
5473 continue;
5474 },
5475 // This prong handles success as well as unexpected errors.
5476 else => return,
5477 }
5478 }
5479}
5480
5481pub fn dl_iterate_phdr(
5482 context: anytype,
5483 comptime Error: type,
5484 comptime callback: fn (info: *dl_phdr_info, size: usize, context: @TypeOf(context)) Error!void,
5485) Error!void {
5486 const Context = @TypeOf(context);
5487 const elf = std.elf;
5488 const dl = @import("dynamic_library.zig");
5489
5490 switch (builtin.object_format) {
5491 .elf, .c => {},
5492 else => @compileError("dl_iterate_phdr is not available for this target"),
5493 }
5494
5495 if (builtin.link_libc) {
5496 switch (system.dl_iterate_phdr(struct {
5497 fn callbackC(info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.C) c_int {
5498 const context_ptr: *const Context = @ptrCast(@alignCast(data));
5499 callback(info, size, context_ptr.*) catch |err| return @intFromError(err);
5500 return 0;
5501 }
5502 }.callbackC, @ptrCast(@constCast(&context)))) {
5503 0 => return,
5504 else => |err| return @as(Error, @errorCast(@errorFromInt(@as(std.meta.Int(.unsigned, @bitSizeOf(anyerror)), @intCast(err))))),
5505 }
5506 }
5507
5508 const elf_base = std.process.getBaseAddress();
5509 const ehdr: *elf.Ehdr = @ptrFromInt(elf_base);
5510 // Make sure the base address points to an ELF image.
5511 assert(mem.eql(u8, ehdr.e_ident[0..4], elf.MAGIC));
5512 const n_phdr = ehdr.e_phnum;
5513 const phdrs = (@as([*]elf.Phdr, @ptrFromInt(elf_base + ehdr.e_phoff)))[0..n_phdr];
5514
5515 var it = dl.linkmap_iterator(phdrs) catch unreachable;
5516
5517 // The executable has no dynamic link segment, create a single entry for
5518 // the whole ELF image.
5519 if (it.end()) {
5520 // Find the base address for the ELF image, if this is a PIE the value
5521 // is non-zero.
5522 const base_address = for (phdrs) |*phdr| {
5523 if (phdr.p_type == elf.PT_PHDR) {
5524 break @intFromPtr(phdrs.ptr) - phdr.p_vaddr;
5525 // We could try computing the difference between _DYNAMIC and
5526 // the p_vaddr of the PT_DYNAMIC section, but using the phdr is
5527 // good enough (Is it?).
5528 }
5529 } else unreachable;
5530
5531 var info = dl_phdr_info{
5532 .dlpi_addr = base_address,
5533 .dlpi_name = "/proc/self/exe",
5534 .dlpi_phdr = phdrs.ptr,
5535 .dlpi_phnum = ehdr.e_phnum,
5536 };
5537
5538 return callback(&info, @sizeOf(dl_phdr_info), context);
5539 }
5540
5541 // Last return value from the callback function.
5542 while (it.next()) |entry| {
5543 var dlpi_phdr: [*]elf.Phdr = undefined;
5544 var dlpi_phnum: u16 = undefined;
5545
5546 if (entry.l_addr != 0) {
5547 const elf_header: *elf.Ehdr = @ptrFromInt(entry.l_addr);
5548 dlpi_phdr = @ptrFromInt(entry.l_addr + elf_header.e_phoff);
5549 dlpi_phnum = elf_header.e_phnum;
5550 } else {
5551 // This is the running ELF image
5552 dlpi_phdr = @ptrFromInt(elf_base + ehdr.e_phoff);
5553 dlpi_phnum = ehdr.e_phnum;
5554 }
5555
5556 var info = dl_phdr_info{
5557 .dlpi_addr = entry.l_addr,
5558 .dlpi_name = entry.l_name,
5559 .dlpi_phdr = dlpi_phdr,
5560 .dlpi_phnum = dlpi_phnum,
5561 };
5562
5563 try callback(&info, @sizeOf(dl_phdr_info), context);
5564 }
5565}
5566
5567pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError;
5568
5569/// TODO: change this to return the timespec as a return value
5570/// TODO: look into making clk_id an enum
5571pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
5572 if (native_os == .wasi and !builtin.link_libc) {
5573 var ts: timestamp_t = undefined;
5574 switch (system.clock_time_get(@bitCast(clk_id), 1, &ts)) {
5575 .SUCCESS => {
5576 tp.* = .{
5577 .tv_sec = @intCast(ts / std.time.ns_per_s),
5578 .tv_nsec = @intCast(ts % std.time.ns_per_s),
5579 };
5580 },
5581 .INVAL => return error.UnsupportedClock,
5582 else => |err| return unexpectedErrno(err),
5583 }
5584 return;
5585 }
5586 if (native_os == .windows) {
5587 if (clk_id == CLOCK.REALTIME) {
5588 var ft: windows.FILETIME = undefined;
5589 windows.kernel32.GetSystemTimeAsFileTime(&ft);
5590 // FileTime has a granularity of 100 nanoseconds and uses the NTFS/Windows epoch.
5591 const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
5592 const ft_per_s = std.time.ns_per_s / 100;
5593 tp.* = .{
5594 .tv_sec = @as(i64, @intCast(ft64 / ft_per_s)) + std.time.epoch.windows,
5595 .tv_nsec = @as(c_long, @intCast(ft64 % ft_per_s)) * 100,
5596 };
5597 return;
5598 } else {
5599 // TODO POSIX implementation of CLOCK.MONOTONIC on Windows.
5600 return error.UnsupportedClock;
5601 }
5602 }
5603
5604 switch (errno(system.clock_gettime(clk_id, tp))) {
5605 .SUCCESS => return,
5606 .FAULT => unreachable,
5607 .INVAL => return error.UnsupportedClock,
5608 else => |err| return unexpectedErrno(err),
5609 }
5610}
5611
5612pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void {
5613 if (native_os == .wasi and !builtin.link_libc) {
5614 var ts: timestamp_t = undefined;
5615 switch (system.clock_res_get(@bitCast(clk_id), &ts)) {
5616 .SUCCESS => res.* = .{
5617 .tv_sec = @intCast(ts / std.time.ns_per_s),
5618 .tv_nsec = @intCast(ts % std.time.ns_per_s),
5619 },
5620 .INVAL => return error.UnsupportedClock,
5621 else => |err| return unexpectedErrno(err),
5622 }
5623 return;
5624 }
5625
5626 switch (errno(system.clock_getres(clk_id, res))) {
5627 .SUCCESS => return,
5628 .FAULT => unreachable,
5629 .INVAL => return error.UnsupportedClock,
5630 else => |err| return unexpectedErrno(err),
5631 }
5632}
5633
5634pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError;
5635
5636pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t {
5637 var set: cpu_set_t = undefined;
5638 switch (errno(system.sched_getaffinity(pid, @sizeOf(cpu_set_t), &set))) {
5639 .SUCCESS => return set,
5640 .FAULT => unreachable,
5641 .INVAL => unreachable,
5642 .SRCH => unreachable,
5643 .PERM => return error.PermissionDenied,
5644 else => |err| return unexpectedErrno(err),
5645 }
5646}
5647
5648pub const SigaltstackError = error{
5649 /// The supplied stack size was less than MINSIGSTKSZ.
5650 SizeTooSmall,
5651
5652 /// Attempted to change the signal stack while it was active.
5653 PermissionDenied,
5654} || UnexpectedError;
5655
5656pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void {
5657 switch (errno(system.sigaltstack(ss, old_ss))) {
5658 .SUCCESS => return,
5659 .FAULT => unreachable,
5660 .INVAL => unreachable,
5661 .NOMEM => return error.SizeTooSmall,
5662 .PERM => return error.PermissionDenied,
5663 else => |err| return unexpectedErrno(err),
5664 }
5665}
5666
5667/// Examine and change a signal action.
5668pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) error{OperationNotSupported}!void {
5669 switch (errno(system.sigaction(sig, act, oact))) {
5670 .SUCCESS => return,
5671 .INVAL, .NOSYS => return error.OperationNotSupported,
5672 else => unreachable,
5673 }
5674}
5675
5676/// Sets the thread signal mask.
5677pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*sigset_t) void {
5678 switch (errno(system.sigprocmask(@bitCast(flags), set, oldset))) {
5679 .SUCCESS => return,
5680 .FAULT => unreachable,
5681 .INVAL => unreachable,
5682 else => unreachable,
5683 }
5684}
5685
5686pub const FutimensError = error{
5687 /// times is NULL, or both tv_nsec values are UTIME_NOW, and either:
5688 /// * the effective user ID of the caller does not match the owner
5689 /// of the file, the caller does not have write access to the
5690 /// file, and the caller is not privileged (Linux: does not have
5691 /// either the CAP_FOWNER or the CAP_DAC_OVERRIDE capability);
5692 /// or,
5693 /// * the file is marked immutable (see chattr(1)).
5694 AccessDenied,
5695
5696 /// The caller attempted to change one or both timestamps to a value
5697 /// other than the current time, or to change one of the timestamps
5698 /// to the current time while leaving the other timestamp unchanged,
5699 /// (i.e., times is not NULL, neither tv_nsec field is UTIME_NOW,
5700 /// and neither tv_nsec field is UTIME_OMIT) and either:
5701 /// * the caller's effective user ID does not match the owner of
5702 /// file, and the caller is not privileged (Linux: does not have
5703 /// the CAP_FOWNER capability); or,
5704 /// * the file is marked append-only or immutable (see chattr(1)).
5705 PermissionDenied,
5706
5707 ReadOnlyFileSystem,
5708} || UnexpectedError;
5709
5710pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void {
5711 if (native_os == .wasi and !builtin.link_libc) {
5712 // TODO WASI encodes `wasi.fstflags` to signify magic values
5713 // similar to UTIME_NOW and UTIME_OMIT. Currently, we ignore
5714 // this here, but we should really handle it somehow.
5715 const atim = times[0].toTimestamp();
5716 const mtim = times[1].toTimestamp();
5717 switch (wasi.fd_filestat_set_times(fd, atim, mtim, .{
5718 .ATIM = true,
5719 .MTIM = true,
5720 })) {
5721 .SUCCESS => return,
5722 .ACCES => return error.AccessDenied,
5723 .PERM => return error.PermissionDenied,
5724 .BADF => unreachable, // always a race condition
5725 .FAULT => unreachable,
5726 .INVAL => unreachable,
5727 .ROFS => return error.ReadOnlyFileSystem,
5728 else => |err| return unexpectedErrno(err),
5729 }
5730 }
5731
5732 switch (errno(system.futimens(fd, times))) {
5733 .SUCCESS => return,
5734 .ACCES => return error.AccessDenied,
5735 .PERM => return error.PermissionDenied,
5736 .BADF => unreachable, // always a race condition
5737 .FAULT => unreachable,
5738 .INVAL => unreachable,
5739 .ROFS => return error.ReadOnlyFileSystem,
5740 else => |err| return unexpectedErrno(err),
5741 }
5742}
5743
5744pub const GetHostNameError = error{PermissionDenied} || UnexpectedError;
5745
5746pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
5747 if (builtin.link_libc) {
5748 switch (errno(system.gethostname(name_buffer, name_buffer.len))) {
5749 .SUCCESS => return mem.sliceTo(name_buffer, 0),
5750 .FAULT => unreachable,
5751 .NAMETOOLONG => unreachable, // HOST_NAME_MAX prevents this
5752 .PERM => return error.PermissionDenied,
5753 else => |err| return unexpectedErrno(err),
5754 }
5755 }
5756 if (native_os == .linux) {
5757 const uts = uname();
5758 const hostname = mem.sliceTo(&uts.nodename, 0);
5759 const result = name_buffer[0..hostname.len];
5760 @memcpy(result, hostname);
5761 return result;
5762 }
5763
5764 @compileError("TODO implement gethostname for this OS");
5765}
5766
5767pub fn uname() utsname {
5768 var uts: utsname = undefined;
5769 switch (errno(system.uname(&uts))) {
5770 .SUCCESS => return uts,
5771 .FAULT => unreachable,
5772 else => unreachable,
5773 }
5774}
5775
5776pub fn res_mkquery(
5777 op: u4,
5778 dname: []const u8,
5779 class: u8,
5780 ty: u8,
5781 data: []const u8,
5782 newrr: ?[*]const u8,
5783 buf: []u8,
5784) usize {
5785 _ = data;
5786 _ = newrr;
5787 // This implementation is ported from musl libc.
5788 // A more idiomatic "ziggy" implementation would be welcome.
5789 var name = dname;
5790 if (mem.endsWith(u8, name, ".")) name.len -= 1;
5791 assert(name.len <= 253);
5792 const n = 17 + name.len + @intFromBool(name.len != 0);
5793
5794 // Construct query template - ID will be filled later
5795 var q: [280]u8 = undefined;
5796 @memset(q[0..n], 0);
5797 q[2] = @as(u8, op) * 8 + 1;
5798 q[5] = 1;
5799 @memcpy(q[13..][0..name.len], name);
5800 var i: usize = 13;
5801 var j: usize = undefined;
5802 while (q[i] != 0) : (i = j + 1) {
5803 j = i;
5804 while (q[j] != 0 and q[j] != '.') : (j += 1) {}
5805 // TODO determine the circumstances for this and whether or
5806 // not this should be an error.
5807 if (j - i - 1 > 62) unreachable;
5808 q[i - 1] = @intCast(j - i);
5809 }
5810 q[i + 1] = ty;
5811 q[i + 3] = class;
5812
5813 // Make a reasonably unpredictable id
5814 var ts: timespec = undefined;
5815 clock_gettime(CLOCK.REALTIME, &ts) catch {};
5816 const UInt = std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(ts.tv_nsec)));
5817 const unsec: UInt = @bitCast(ts.tv_nsec);
5818 const id: u32 = @truncate(unsec + unsec / 65536);
5819 q[0] = @truncate(id / 256);
5820 q[1] = @truncate(id);
5821
5822 @memcpy(buf[0..n], q[0..n]);
5823 return n;
5824}
5825
5826pub const SendError = error{
5827 /// (For UNIX domain sockets, which are identified by pathname) Write permission is denied
5828 /// on the destination socket file, or search permission is denied for one of the
5829 /// directories the path prefix. (See path_resolution(7).)
5830 /// (For UDP sockets) An attempt was made to send to a network/broadcast address as though
5831 /// it was a unicast address.
5832 AccessDenied,
5833
5834 /// The socket is marked nonblocking and the requested operation would block, and
5835 /// there is no global event loop configured.
5836 /// It's also possible to get this error under the following condition:
5837 /// (Internet domain datagram sockets) The socket referred to by sockfd had not previously
5838 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it was
5839 /// determined that all port numbers in the ephemeral port range are currently in use. See
5840 /// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
5841 WouldBlock,
5842
5843 /// Another Fast Open is already in progress.
5844 FastOpenAlreadyInProgress,
5845
5846 /// Connection reset by peer.
5847 ConnectionResetByPeer,
5848
5849 /// The socket type requires that message be sent atomically, and the size of the message
5850 /// to be sent made this impossible. The message is not transmitted.
5851 MessageTooBig,
5852
5853 /// The output queue for a network interface was full. This generally indicates that the
5854 /// interface has stopped sending, but may be caused by transient congestion. (Normally,
5855 /// this does not occur in Linux. Packets are just silently dropped when a device queue
5856 /// overflows.)
5857 /// This is also caused when there is not enough kernel memory available.
5858 SystemResources,
5859
5860 /// The local end has been shut down on a connection oriented socket. In this case, the
5861 /// process will also receive a SIGPIPE unless MSG.NOSIGNAL is set.
5862 BrokenPipe,
5863
5864 FileDescriptorNotASocket,
5865
5866 /// Network is unreachable.
5867 NetworkUnreachable,
5868
5869 /// The local network interface used to reach the destination is down.
5870 NetworkSubsystemFailed,
5871} || UnexpectedError;
5872
5873pub const SendMsgError = SendError || error{
5874 /// The passed address didn't have the correct address family in its sa_family field.
5875 AddressFamilyNotSupported,
5876
5877 /// Returned when socket is AF.UNIX and the given path has a symlink loop.
5878 SymLinkLoop,
5879
5880 /// Returned when socket is AF.UNIX and the given path length exceeds `max_path_bytes` bytes.
5881 NameTooLong,
5882
5883 /// Returned when socket is AF.UNIX and the given path does not point to an existing file.
5884 FileNotFound,
5885 NotDir,
5886
5887 /// The socket is not connected (connection-oriented sockets only).
5888 SocketNotConnected,
5889 AddressNotAvailable,
5890};
5891
5892pub fn sendmsg(
5893 /// The file descriptor of the sending socket.
5894 sockfd: socket_t,
5895 /// Message header and iovecs
5896 msg: *const msghdr_const,
5897 flags: u32,
5898) SendMsgError!usize {
5899 while (true) {
5900 const rc = system.sendmsg(sockfd, msg, flags);
5901 if (native_os == .windows) {
5902 if (rc == windows.ws2_32.SOCKET_ERROR) {
5903 switch (windows.ws2_32.WSAGetLastError()) {
5904 .WSAEACCES => return error.AccessDenied,
5905 .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
5906 .WSAECONNRESET => return error.ConnectionResetByPeer,
5907 .WSAEMSGSIZE => return error.MessageTooBig,
5908 .WSAENOBUFS => return error.SystemResources,
5909 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
5910 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
5911 .WSAEDESTADDRREQ => unreachable, // A destination address is required.
5912 .WSAEFAULT => unreachable, // The lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent, or lpCompletionRoutine parameters are not part of the user address space, or the lpTo parameter is too small.
5913 .WSAEHOSTUNREACH => return error.NetworkUnreachable,
5914 // TODO: WSAEINPROGRESS, WSAEINTR
5915 .WSAEINVAL => unreachable,
5916 .WSAENETDOWN => return error.NetworkSubsystemFailed,
5917 .WSAENETRESET => return error.ConnectionResetByPeer,
5918 .WSAENETUNREACH => return error.NetworkUnreachable,
5919 .WSAENOTCONN => return error.SocketNotConnected,
5920 .WSAESHUTDOWN => unreachable, // The socket has been shut down; it is not possible to WSASendTo on a socket after shutdown has been invoked with how set to SD_SEND or SD_BOTH.
5921 .WSAEWOULDBLOCK => return error.WouldBlock,
5922 .WSANOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function.
5923 else => |err| return windows.unexpectedWSAError(err),
5924 }
5925 } else {
5926 return @intCast(rc);
5927 }
5928 } else {
5929 switch (errno(rc)) {
5930 .SUCCESS => return @intCast(rc),
5931
5932 .ACCES => return error.AccessDenied,
5933 .AGAIN => return error.WouldBlock,
5934 .ALREADY => return error.FastOpenAlreadyInProgress,
5935 .BADF => unreachable, // always a race condition
5936 .CONNRESET => return error.ConnectionResetByPeer,
5937 .DESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
5938 .FAULT => unreachable, // An invalid user space address was specified for an argument.
5939 .INTR => continue,
5940 .INVAL => unreachable, // Invalid argument passed.
5941 .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
5942 .MSGSIZE => return error.MessageTooBig,
5943 .NOBUFS => return error.SystemResources,
5944 .NOMEM => return error.SystemResources,
5945 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
5946 .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
5947 .PIPE => return error.BrokenPipe,
5948 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
5949 .LOOP => return error.SymLinkLoop,
5950 .NAMETOOLONG => return error.NameTooLong,
5951 .NOENT => return error.FileNotFound,
5952 .NOTDIR => return error.NotDir,
5953 .HOSTUNREACH => return error.NetworkUnreachable,
5954 .NETUNREACH => return error.NetworkUnreachable,
5955 .NOTCONN => return error.SocketNotConnected,
5956 .NETDOWN => return error.NetworkSubsystemFailed,
5957 else => |err| return unexpectedErrno(err),
5958 }
5959 }
5960 }
5961}
5962
5963pub const SendToError = SendMsgError || error{
5964 /// The destination address is not reachable by the bound address.
5965 UnreachableAddress,
5966};
5967
5968/// Transmit a message to another socket.
5969///
5970/// The `sendto` call may be used only when the socket is in a connected state (so that the intended
5971/// recipient is known). The following call
5972///
5973/// send(sockfd, buf, len, flags);
5974///
5975/// is equivalent to
5976///
5977/// sendto(sockfd, buf, len, flags, NULL, 0);
5978///
5979/// If sendto() is used on a connection-mode (`SOCK.STREAM`, `SOCK.SEQPACKET`) socket, the arguments
5980/// `dest_addr` and `addrlen` are asserted to be `null` and `0` respectively, and asserted
5981/// that the socket was actually connected.
5982/// Otherwise, the address of the target is given by `dest_addr` with `addrlen` specifying its size.
5983///
5984/// If the message is too long to pass atomically through the underlying protocol,
5985/// `SendError.MessageTooBig` is returned, and the message is not transmitted.
5986///
5987/// There is no indication of failure to deliver.
5988///
5989/// When the message does not fit into the send buffer of the socket, `sendto` normally blocks,
5990/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail
5991/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is
5992/// possible to send more data.
5993pub fn sendto(
5994 /// The file descriptor of the sending socket.
5995 sockfd: socket_t,
5996 /// Message to send.
5997 buf: []const u8,
5998 flags: u32,
5999 dest_addr: ?*const sockaddr,
6000 addrlen: socklen_t,
6001) SendToError!usize {
6002 if (native_os == .windows) {
6003 switch (windows.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen)) {
6004 windows.ws2_32.SOCKET_ERROR => switch (windows.ws2_32.WSAGetLastError()) {
6005 .WSAEACCES => return error.AccessDenied,
6006 .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
6007 .WSAECONNRESET => return error.ConnectionResetByPeer,
6008 .WSAEMSGSIZE => return error.MessageTooBig,
6009 .WSAENOBUFS => return error.SystemResources,
6010 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
6011 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
6012 .WSAEDESTADDRREQ => unreachable, // A destination address is required.
6013 .WSAEFAULT => unreachable, // The lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent, or lpCompletionRoutine parameters are not part of the user address space, or the lpTo parameter is too small.
6014 .WSAEHOSTUNREACH => return error.NetworkUnreachable,
6015 // TODO: WSAEINPROGRESS, WSAEINTR
6016 .WSAEINVAL => unreachable,
6017 .WSAENETDOWN => return error.NetworkSubsystemFailed,
6018 .WSAENETRESET => return error.ConnectionResetByPeer,
6019 .WSAENETUNREACH => return error.NetworkUnreachable,
6020 .WSAENOTCONN => return error.SocketNotConnected,
6021 .WSAESHUTDOWN => unreachable, // The socket has been shut down; it is not possible to WSASendTo on a socket after shutdown has been invoked with how set to SD_SEND or SD_BOTH.
6022 .WSAEWOULDBLOCK => return error.WouldBlock,
6023 .WSANOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function.
6024 else => |err| return windows.unexpectedWSAError(err),
6025 },
6026 else => |rc| return @intCast(rc),
6027 }
6028 }
6029 while (true) {
6030 const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);
6031 switch (errno(rc)) {
6032 .SUCCESS => return @intCast(rc),
6033
6034 .ACCES => return error.AccessDenied,
6035 .AGAIN => return error.WouldBlock,
6036 .ALREADY => return error.FastOpenAlreadyInProgress,
6037 .BADF => unreachable, // always a race condition
6038 .CONNRESET => return error.ConnectionResetByPeer,
6039 .DESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
6040 .FAULT => unreachable, // An invalid user space address was specified for an argument.
6041 .INTR => continue,
6042 .INVAL => return error.UnreachableAddress,
6043 .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
6044 .MSGSIZE => return error.MessageTooBig,
6045 .NOBUFS => return error.SystemResources,
6046 .NOMEM => return error.SystemResources,
6047 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
6048 .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
6049 .PIPE => return error.BrokenPipe,
6050 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
6051 .LOOP => return error.SymLinkLoop,
6052 .NAMETOOLONG => return error.NameTooLong,
6053 .NOENT => return error.FileNotFound,
6054 .NOTDIR => return error.NotDir,
6055 .HOSTUNREACH => return error.NetworkUnreachable,
6056 .NETUNREACH => return error.NetworkUnreachable,
6057 .NOTCONN => return error.SocketNotConnected,
6058 .NETDOWN => return error.NetworkSubsystemFailed,
6059 else => |err| return unexpectedErrno(err),
6060 }
6061 }
6062}
6063
6064/// Transmit a message to another socket.
6065///
6066/// The `send` call may be used only when the socket is in a connected state (so that the intended
6067/// recipient is known). The only difference between `send` and `write` is the presence of
6068/// flags. With a zero flags argument, `send` is equivalent to `write`. Also, the following
6069/// call
6070///
6071/// send(sockfd, buf, len, flags);
6072///
6073/// is equivalent to
6074///
6075/// sendto(sockfd, buf, len, flags, NULL, 0);
6076///
6077/// There is no indication of failure to deliver.
6078///
6079/// When the message does not fit into the send buffer of the socket, `send` normally blocks,
6080/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail
6081/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is
6082/// possible to send more data.
6083pub fn send(
6084 /// The file descriptor of the sending socket.
6085 sockfd: socket_t,
6086 buf: []const u8,
6087 flags: u32,
6088) SendError!usize {
6089 return sendto(sockfd, buf, flags, null, 0) catch |err| switch (err) {
6090 error.AddressFamilyNotSupported => unreachable,
6091 error.SymLinkLoop => unreachable,
6092 error.NameTooLong => unreachable,
6093 error.FileNotFound => unreachable,
6094 error.NotDir => unreachable,
6095 error.NetworkUnreachable => unreachable,
6096 error.AddressNotAvailable => unreachable,
6097 error.SocketNotConnected => unreachable,
6098 error.UnreachableAddress => unreachable,
6099 else => |e| return e,
6100 };
6101}
6102
6103pub const SendFileError = PReadError || WriteError || SendError;
6104
6105/// Transfer data between file descriptors, with optional headers and trailers.
6106///
6107/// Returns the number of bytes written, which can be zero.
6108///
6109/// The `sendfile` call copies `in_len` bytes from one file descriptor to another. When possible,
6110/// this is done within the operating system kernel, which can provide better performance
6111/// characteristics than transferring data from kernel to user space and back, such as with
6112/// `read` and `write` calls. When `in_len` is `0`, it means to copy until the end of the input file has been
6113/// reached. Note, however, that partial writes are still possible in this case.
6114///
6115/// `in_fd` must be a file descriptor opened for reading, and `out_fd` must be a file descriptor
6116/// opened for writing. They may be any kind of file descriptor; however, if `in_fd` is not a regular
6117/// file system file, it may cause this function to fall back to calling `read` and `write`, in which case
6118/// atomicity guarantees no longer apply.
6119///
6120/// Copying begins reading at `in_offset`. The input file descriptor seek position is ignored and not updated.
6121/// If the output file descriptor has a seek position, it is updated as bytes are written. When
6122/// `in_offset` is past the end of the input file, it successfully reads 0 bytes.
6123///
6124/// `flags` has different meanings per operating system; refer to the respective man pages.
6125///
6126/// These systems support atomically sending everything, including headers and trailers:
6127/// * macOS
6128/// * FreeBSD
6129///
6130/// These systems support in-kernel data copying, but headers and trailers are not sent atomically:
6131/// * Linux
6132///
6133/// Other systems fall back to calling `read` / `write`.
6134///
6135/// Linux has a limit on how many bytes may be transferred in one `sendfile` call, which is `0x7ffff000`
6136/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
6137/// well as stuffing the errno codes into the last `4096` values. This is noted on the `sendfile` man page.
6138/// The limit on Darwin is `0x7fffffff`, trying to write more than that returns EINVAL.
6139/// The corresponding POSIX limit on this is `maxInt(isize)`.
6140pub fn sendfile(
6141 out_fd: fd_t,
6142 in_fd: fd_t,
6143 in_offset: u64,
6144 in_len: u64,
6145 headers: []const iovec_const,
6146 trailers: []const iovec_const,
6147 flags: u32,
6148) SendFileError!usize {
6149 var header_done = false;
6150 var total_written: usize = 0;
6151
6152 // Prevents EOVERFLOW.
6153 const size_t = std.meta.Int(.unsigned, @typeInfo(usize).Int.bits - 1);
6154 const max_count = switch (native_os) {
6155 .linux => 0x7ffff000,
6156 .macos, .ios, .watchos, .tvos => maxInt(i32),
6157 else => maxInt(size_t),
6158 };
6159
6160 switch (native_os) {
6161 .linux => sf: {
6162 // sendfile() first appeared in Linux 2.2, glibc 2.1.
6163 const call_sf = comptime if (builtin.link_libc)
6164 std.c.versionCheck(.{ .major = 2, .minor = 1, .patch = 0 })
6165 else
6166 builtin.os.version_range.linux.range.max.order(.{ .major = 2, .minor = 2, .patch = 0 }) != .lt;
6167 if (!call_sf) break :sf;
6168
6169 if (headers.len != 0) {
6170 const amt = try writev(out_fd, headers);
6171 total_written += amt;
6172 if (amt < count_iovec_bytes(headers)) return total_written;
6173 header_done = true;
6174 }
6175
6176 // Here we match BSD behavior, making a zero count value send as many bytes as possible.
6177 const adjusted_count = if (in_len == 0) max_count else @min(in_len, max_count);
6178
6179 const sendfile_sym = if (lfs64_abi) system.sendfile64 else system.sendfile;
6180 while (true) {
6181 var offset: off_t = @bitCast(in_offset);
6182 const rc = sendfile_sym(out_fd, in_fd, &offset, adjusted_count);
6183 switch (errno(rc)) {
6184 .SUCCESS => {
6185 const amt: usize = @bitCast(rc);
6186 total_written += amt;
6187 if (in_len == 0 and amt == 0) {
6188 // We have detected EOF from `in_fd`.
6189 break;
6190 } else if (amt < in_len) {
6191 return total_written;
6192 } else {
6193 break;
6194 }
6195 },
6196
6197 .BADF => unreachable, // Always a race condition.
6198 .FAULT => unreachable, // Segmentation fault.
6199 .OVERFLOW => unreachable, // We avoid passing too large of a `count`.
6200 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
6201
6202 .INVAL, .NOSYS => {
6203 // EINVAL could be any of the following situations:
6204 // * Descriptor is not valid or locked
6205 // * an mmap(2)-like operation is not available for in_fd
6206 // * count is negative
6207 // * out_fd has the APPEND flag set
6208 // Because of the "mmap(2)-like operation" possibility, we fall back to doing read/write
6209 // manually, the same as ENOSYS.
6210 break :sf;
6211 },
6212 .AGAIN => return error.WouldBlock,
6213 .IO => return error.InputOutput,
6214 .PIPE => return error.BrokenPipe,
6215 .NOMEM => return error.SystemResources,
6216 .NXIO => return error.Unseekable,
6217 .SPIPE => return error.Unseekable,
6218 else => |err| {
6219 unexpectedErrno(err) catch {};
6220 break :sf;
6221 },
6222 }
6223 }
6224
6225 if (trailers.len != 0) {
6226 total_written += try writev(out_fd, trailers);
6227 }
6228
6229 return total_written;
6230 },
6231 .freebsd => sf: {
6232 var hdtr_data: std.c.sf_hdtr = undefined;
6233 var hdtr: ?*std.c.sf_hdtr = null;
6234 if (headers.len != 0 or trailers.len != 0) {
6235 // Here we carefully avoid `@intCast` by returning partial writes when
6236 // too many io vectors are provided.
6237 const hdr_cnt = cast(u31, headers.len) orelse maxInt(u31);
6238 if (headers.len > hdr_cnt) return writev(out_fd, headers);
6239
6240 const trl_cnt = cast(u31, trailers.len) orelse maxInt(u31);
6241
6242 hdtr_data = std.c.sf_hdtr{
6243 .headers = headers.ptr,
6244 .hdr_cnt = hdr_cnt,
6245 .trailers = trailers.ptr,
6246 .trl_cnt = trl_cnt,
6247 };
6248 hdtr = &hdtr_data;
6249 }
6250
6251 while (true) {
6252 var sbytes: off_t = undefined;
6253 const err = errno(system.sendfile(in_fd, out_fd, @bitCast(in_offset), @min(in_len, max_count), hdtr, &sbytes, flags));
6254 const amt: usize = @bitCast(sbytes);
6255 switch (err) {
6256 .SUCCESS => return amt,
6257
6258 .BADF => unreachable, // Always a race condition.
6259 .FAULT => unreachable, // Segmentation fault.
6260 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
6261
6262 .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => {
6263 // EINVAL could be any of the following situations:
6264 // * The fd argument is not a regular file.
6265 // * The s argument is not a SOCK.STREAM type socket.
6266 // * The offset argument is negative.
6267 // Because of some of these possibilities, we fall back to doing read/write
6268 // manually, the same as ENOSYS.
6269 break :sf;
6270 },
6271
6272 .INTR => if (amt != 0) return amt else continue,
6273
6274 .AGAIN => if (amt != 0) {
6275 return amt;
6276 } else {
6277 return error.WouldBlock;
6278 },
6279
6280 .BUSY => if (amt != 0) {
6281 return amt;
6282 } else {
6283 return error.WouldBlock;
6284 },
6285
6286 .IO => return error.InputOutput,
6287 .NOBUFS => return error.SystemResources,
6288 .PIPE => return error.BrokenPipe,
6289
6290 else => {
6291 unexpectedErrno(err) catch {};
6292 if (amt != 0) {
6293 return amt;
6294 } else {
6295 break :sf;
6296 }
6297 },
6298 }
6299 }
6300 },
6301 .macos, .ios, .tvos, .watchos => sf: {
6302 var hdtr_data: std.c.sf_hdtr = undefined;
6303 var hdtr: ?*std.c.sf_hdtr = null;
6304 if (headers.len != 0 or trailers.len != 0) {
6305 // Here we carefully avoid `@intCast` by returning partial writes when
6306 // too many io vectors are provided.
6307 const hdr_cnt = cast(u31, headers.len) orelse maxInt(u31);
6308 if (headers.len > hdr_cnt) return writev(out_fd, headers);
6309
6310 const trl_cnt = cast(u31, trailers.len) orelse maxInt(u31);
6311
6312 hdtr_data = std.c.sf_hdtr{
6313 .headers = headers.ptr,
6314 .hdr_cnt = hdr_cnt,
6315 .trailers = trailers.ptr,
6316 .trl_cnt = trl_cnt,
6317 };
6318 hdtr = &hdtr_data;
6319 }
6320
6321 while (true) {
6322 var sbytes: off_t = @min(in_len, max_count);
6323 const err = errno(system.sendfile(in_fd, out_fd, @bitCast(in_offset), &sbytes, hdtr, flags));
6324 const amt: usize = @bitCast(sbytes);
6325 switch (err) {
6326 .SUCCESS => return amt,
6327
6328 .BADF => unreachable, // Always a race condition.
6329 .FAULT => unreachable, // Segmentation fault.
6330 .INVAL => unreachable,
6331 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
6332
6333 .OPNOTSUPP, .NOTSOCK, .NOSYS => break :sf,
6334
6335 .INTR => if (amt != 0) return amt else continue,
6336
6337 .AGAIN => if (amt != 0) {
6338 return amt;
6339 } else {
6340 return error.WouldBlock;
6341 },
6342
6343 .IO => return error.InputOutput,
6344 .PIPE => return error.BrokenPipe,
6345
6346 else => {
6347 unexpectedErrno(err) catch {};
6348 if (amt != 0) {
6349 return amt;
6350 } else {
6351 break :sf;
6352 }
6353 },
6354 }
6355 }
6356 },
6357 else => {}, // fall back to read/write
6358 }
6359
6360 if (headers.len != 0 and !header_done) {
6361 const amt = try writev(out_fd, headers);
6362 total_written += amt;
6363 if (amt < count_iovec_bytes(headers)) return total_written;
6364 }
6365
6366 rw: {
6367 var buf: [8 * 4096]u8 = undefined;
6368 // Here we match BSD behavior, making a zero count value send as many bytes as possible.
6369 const adjusted_count = if (in_len == 0) buf.len else @min(buf.len, in_len);
6370 const amt_read = try pread(in_fd, buf[0..adjusted_count], in_offset);
6371 if (amt_read == 0) {
6372 if (in_len == 0) {
6373 // We have detected EOF from `in_fd`.
6374 break :rw;
6375 } else {
6376 return total_written;
6377 }
6378 }
6379 const amt_written = try write(out_fd, buf[0..amt_read]);
6380 total_written += amt_written;
6381 if (amt_written < in_len or in_len == 0) return total_written;
6382 }
6383
6384 if (trailers.len != 0) {
6385 total_written += try writev(out_fd, trailers);
6386 }
6387
6388 return total_written;
6389}
6390
6391fn count_iovec_bytes(iovs: []const iovec_const) usize {
6392 var count: usize = 0;
6393 for (iovs) |iov| {
6394 count += iov.iov_len;
6395 }
6396 return count;
6397}
6398
6399pub const CopyFileRangeError = error{
6400 FileTooBig,
6401 InputOutput,
6402 /// `fd_in` is not open for reading; or `fd_out` is not open for writing;
6403 /// or the `APPEND` flag is set for `fd_out`.
6404 FilesOpenedWithWrongFlags,
6405 IsDir,
6406 OutOfMemory,
6407 NoSpaceLeft,
6408 Unseekable,
6409 PermissionDenied,
6410 SwapFile,
6411 CorruptedData,
6412} || PReadError || PWriteError || UnexpectedError;
6413
6414/// Transfer data between file descriptors at specified offsets.
6415///
6416/// Returns the number of bytes written, which can less than requested.
6417///
6418/// The `copy_file_range` call copies `len` bytes from one file descriptor to another. When possible,
6419/// this is done within the operating system kernel, which can provide better performance
6420/// characteristics than transferring data from kernel to user space and back, such as with
6421/// `pread` and `pwrite` calls.
6422///
6423/// `fd_in` must be a file descriptor opened for reading, and `fd_out` must be a file descriptor
6424/// opened for writing. They may be any kind of file descriptor; however, if `fd_in` is not a regular
6425/// file system file, it may cause this function to fall back to calling `pread` and `pwrite`, in which case
6426/// atomicity guarantees no longer apply.
6427///
6428/// If `fd_in` and `fd_out` are the same, source and target ranges must not overlap.
6429/// The file descriptor seek positions are ignored and not updated.
6430/// When `off_in` is past the end of the input file, it successfully reads 0 bytes.
6431///
6432/// `flags` has different meanings per operating system; refer to the respective man pages.
6433///
6434/// These systems support in-kernel data copying:
6435/// * Linux 4.5 (cross-filesystem 5.3)
6436/// * FreeBSD 13.0
6437///
6438/// Other systems fall back to calling `pread` / `pwrite`.
6439///
6440/// Maximum offsets on Linux and FreeBSD are `maxInt(i64)`.
6441pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len: usize, flags: u32) CopyFileRangeError!usize {
6442 const global = struct {
6443 var has_copy_file_range = true;
6444 };
6445
6446 if ((comptime builtin.os.isAtLeast(.freebsd, .{ .major = 13, .minor = 0, .patch = 0 }) orelse false) or
6447 ((comptime builtin.os.isAtLeast(.linux, .{ .major = 4, .minor = 5, .patch = 0 }) orelse false and
6448 std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 })) and
6449 @atomicLoad(bool, &global.has_copy_file_range, .monotonic)))
6450 {
6451 var off_in_copy: i64 = @bitCast(off_in);
6452 var off_out_copy: i64 = @bitCast(off_out);
6453
6454 while (true) {
6455 const rc = system.copy_file_range(fd_in, &off_in_copy, fd_out, &off_out_copy, len, flags);
6456 if (native_os == .freebsd) {
6457 switch (errno(rc)) {
6458 .SUCCESS => return @intCast(rc),
6459 .BADF => return error.FilesOpenedWithWrongFlags,
6460 .FBIG => return error.FileTooBig,
6461 .IO => return error.InputOutput,
6462 .ISDIR => return error.IsDir,
6463 .NOSPC => return error.NoSpaceLeft,
6464 .INVAL => break, // these may not be regular files, try fallback
6465 .INTEGRITY => return error.CorruptedData,
6466 .INTR => continue,
6467 else => |err| return unexpectedErrno(err),
6468 }
6469 } else { // assume linux
6470 switch (errno(rc)) {
6471 .SUCCESS => return @intCast(rc),
6472 .BADF => return error.FilesOpenedWithWrongFlags,
6473 .FBIG => return error.FileTooBig,
6474 .IO => return error.InputOutput,
6475 .ISDIR => return error.IsDir,
6476 .NOSPC => return error.NoSpaceLeft,
6477 .INVAL => break, // these may not be regular files, try fallback
6478 .NOMEM => return error.OutOfMemory,
6479 .OVERFLOW => return error.Unseekable,
6480 .PERM => return error.PermissionDenied,
6481 .TXTBSY => return error.SwapFile,
6482 .XDEV => break, // support for cross-filesystem copy added in Linux 5.3, use fallback
6483 .NOSYS => {
6484 @atomicStore(bool, &global.has_copy_file_range, false, .monotonic);
6485 break;
6486 },
6487 else => |err| return unexpectedErrno(err),
6488 }
6489 }
6490 }
6491 }
6492
6493 var buf: [8 * 4096]u8 = undefined;
6494 const amt_read = try pread(fd_in, buf[0..@min(buf.len, len)], off_in);
6495 if (amt_read == 0) return 0;
6496 return pwrite(fd_out, buf[0..amt_read], off_out);
6497}
6498
6499pub const PollError = error{
6500 /// The network subsystem has failed.
6501 NetworkSubsystemFailed,
6502
6503 /// The kernel had no space to allocate file descriptor tables.
6504 SystemResources,
6505} || UnexpectedError;
6506
6507pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
6508 while (true) {
6509 const fds_count = cast(nfds_t, fds.len) orelse return error.SystemResources;
6510 const rc = system.poll(fds.ptr, fds_count, timeout);
6511 if (native_os == .windows) {
6512 if (rc == windows.ws2_32.SOCKET_ERROR) {
6513 switch (windows.ws2_32.WSAGetLastError()) {
6514 .WSANOTINITIALISED => unreachable,
6515 .WSAENETDOWN => return error.NetworkSubsystemFailed,
6516 .WSAENOBUFS => return error.SystemResources,
6517 // TODO: handle more errors
6518 else => |err| return windows.unexpectedWSAError(err),
6519 }
6520 } else {
6521 return @intCast(rc);
6522 }
6523 } else {
6524 switch (errno(rc)) {
6525 .SUCCESS => return @intCast(rc),
6526 .FAULT => unreachable,
6527 .INTR => continue,
6528 .INVAL => unreachable,
6529 .NOMEM => return error.SystemResources,
6530 else => |err| return unexpectedErrno(err),
6531 }
6532 }
6533 unreachable;
6534 }
6535}
6536
6537pub const PPollError = error{
6538 /// The operation was interrupted by a delivery of a signal before it could complete.
6539 SignalInterrupt,
6540
6541 /// The kernel had no space to allocate file descriptor tables.
6542 SystemResources,
6543} || UnexpectedError;
6544
6545pub fn ppoll(fds: []pollfd, timeout: ?*const timespec, mask: ?*const sigset_t) PPollError!usize {
6546 var ts: timespec = undefined;
6547 var ts_ptr: ?*timespec = null;
6548 if (timeout) |timeout_ns| {
6549 ts_ptr = &ts;
6550 ts = timeout_ns.*;
6551 }
6552 const fds_count = cast(nfds_t, fds.len) orelse return error.SystemResources;
6553 const rc = system.ppoll(fds.ptr, fds_count, ts_ptr, mask);
6554 switch (errno(rc)) {
6555 .SUCCESS => return @intCast(rc),
6556 .FAULT => unreachable,
6557 .INTR => return error.SignalInterrupt,
6558 .INVAL => unreachable,
6559 .NOMEM => return error.SystemResources,
6560 else => |err| return unexpectedErrno(err),
6561 }
6562}
6563
6564pub const RecvFromError = error{
6565 /// The socket is marked nonblocking and the requested operation would block, and
6566 /// there is no global event loop configured.
6567 WouldBlock,
6568
6569 /// A remote host refused to allow the network connection, typically because it is not
6570 /// running the requested service.
6571 ConnectionRefused,
6572
6573 /// Could not allocate kernel memory.
6574 SystemResources,
6575
6576 ConnectionResetByPeer,
6577 ConnectionTimedOut,
6578
6579 /// The socket has not been bound.
6580 SocketNotBound,
6581
6582 /// The UDP message was too big for the buffer and part of it has been discarded
6583 MessageTooBig,
6584
6585 /// The network subsystem has failed.
6586 NetworkSubsystemFailed,
6587
6588 /// The socket is not connected (connection-oriented sockets only).
6589 SocketNotConnected,
6590} || UnexpectedError;
6591
6592pub fn recv(sock: socket_t, buf: []u8, flags: u32) RecvFromError!usize {
6593 return recvfrom(sock, buf, flags, null, null);
6594}
6595
6596/// If `sockfd` is opened in non blocking mode, the function will
6597/// return error.WouldBlock when EAGAIN is received.
6598pub fn recvfrom(
6599 sockfd: socket_t,
6600 buf: []u8,
6601 flags: u32,
6602 src_addr: ?*sockaddr,
6603 addrlen: ?*socklen_t,
6604) RecvFromError!usize {
6605 while (true) {
6606 const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen);
6607 if (native_os == .windows) {
6608 if (rc == windows.ws2_32.SOCKET_ERROR) {
6609 switch (windows.ws2_32.WSAGetLastError()) {
6610 .WSANOTINITIALISED => unreachable,
6611 .WSAECONNRESET => return error.ConnectionResetByPeer,
6612 .WSAEINVAL => return error.SocketNotBound,
6613 .WSAEMSGSIZE => return error.MessageTooBig,
6614 .WSAENETDOWN => return error.NetworkSubsystemFailed,
6615 .WSAENOTCONN => return error.SocketNotConnected,
6616 .WSAEWOULDBLOCK => return error.WouldBlock,
6617 .WSAETIMEDOUT => return error.ConnectionTimedOut,
6618 // TODO: handle more errors
6619 else => |err| return windows.unexpectedWSAError(err),
6620 }
6621 } else {
6622 return @intCast(rc);
6623 }
6624 } else {
6625 switch (errno(rc)) {
6626 .SUCCESS => return @intCast(rc),
6627 .BADF => unreachable, // always a race condition
6628 .FAULT => unreachable,
6629 .INVAL => unreachable,
6630 .NOTCONN => return error.SocketNotConnected,
6631 .NOTSOCK => unreachable,
6632 .INTR => continue,
6633 .AGAIN => return error.WouldBlock,
6634 .NOMEM => return error.SystemResources,
6635 .CONNREFUSED => return error.ConnectionRefused,
6636 .CONNRESET => return error.ConnectionResetByPeer,
6637 .TIMEDOUT => return error.ConnectionTimedOut,
6638 else => |err| return unexpectedErrno(err),
6639 }
6640 }
6641 }
6642}
6643
6644pub const DnExpandError = error{InvalidDnsPacket};
6645
6646pub fn dn_expand(
6647 msg: []const u8,
6648 comp_dn: []const u8,
6649 exp_dn: []u8,
6650) DnExpandError!usize {
6651 // This implementation is ported from musl libc.
6652 // A more idiomatic "ziggy" implementation would be welcome.
6653 var p = comp_dn.ptr;
6654 var len: usize = maxInt(usize);
6655 const end = msg.ptr + msg.len;
6656 if (p == end or exp_dn.len == 0) return error.InvalidDnsPacket;
6657 var dest = exp_dn.ptr;
6658 const dend = dest + @min(exp_dn.len, 254);
6659 // detect reference loop using an iteration counter
6660 var i: usize = 0;
6661 while (i < msg.len) : (i += 2) {
6662 // loop invariants: p<end, dest<dend
6663 if ((p[0] & 0xc0) != 0) {
6664 if (p + 1 == end) return error.InvalidDnsPacket;
6665 const j = @as(usize, p[0] & 0x3f) << 8 | p[1];
6666 if (len == maxInt(usize)) len = @intFromPtr(p) + 2 - @intFromPtr(comp_dn.ptr);
6667 if (j >= msg.len) return error.InvalidDnsPacket;
6668 p = msg.ptr + j;
6669 } else if (p[0] != 0) {
6670 if (dest != exp_dn.ptr) {
6671 dest[0] = '.';
6672 dest += 1;
6673 }
6674 var j = p[0];
6675 p += 1;
6676 if (j >= @intFromPtr(end) - @intFromPtr(p) or j >= @intFromPtr(dend) - @intFromPtr(dest)) {
6677 return error.InvalidDnsPacket;
6678 }
6679 while (j != 0) {
6680 j -= 1;
6681 dest[0] = p[0];
6682 dest += 1;
6683 p += 1;
6684 }
6685 } else {
6686 dest[0] = 0;
6687 if (len == maxInt(usize)) len = @intFromPtr(p) + 1 - @intFromPtr(comp_dn.ptr);
6688 return len;
6689 }
6690 }
6691 return error.InvalidDnsPacket;
6692}
6693
6694pub const SetSockOptError = error{
6695 /// The socket is already connected, and a specified option cannot be set while the socket is connected.
6696 AlreadyConnected,
6697
6698 /// The option is not supported by the protocol.
6699 InvalidProtocolOption,
6700
6701 /// The send and receive timeout values are too big to fit into the timeout fields in the socket structure.
6702 TimeoutTooBig,
6703
6704 /// Insufficient resources are available in the system to complete the call.
6705 SystemResources,
6706
6707 // Setting the socket option requires more elevated permissions.
6708 PermissionDenied,
6709
6710 NetworkSubsystemFailed,
6711 FileDescriptorNotASocket,
6712 SocketNotBound,
6713 NoDevice,
6714} || UnexpectedError;
6715
6716/// Set a socket's options.
6717pub fn setsockopt(fd: socket_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void {
6718 if (native_os == .windows) {
6719 const rc = windows.ws2_32.setsockopt(fd, @intCast(level), @intCast(optname), opt.ptr, @intCast(opt.len));
6720 if (rc == windows.ws2_32.SOCKET_ERROR) {
6721 switch (windows.ws2_32.WSAGetLastError()) {
6722 .WSANOTINITIALISED => unreachable,
6723 .WSAENETDOWN => return error.NetworkSubsystemFailed,
6724 .WSAEFAULT => unreachable,
6725 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
6726 .WSAEINVAL => return error.SocketNotBound,
6727 else => |err| return windows.unexpectedWSAError(err),
6728 }
6729 }
6730 return;
6731 } else {
6732 switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(opt.len)))) {
6733 .SUCCESS => {},
6734 .BADF => unreachable, // always a race condition
6735 .NOTSOCK => unreachable, // always a race condition
6736 .INVAL => unreachable,
6737 .FAULT => unreachable,
6738 .DOM => return error.TimeoutTooBig,
6739 .ISCONN => return error.AlreadyConnected,
6740 .NOPROTOOPT => return error.InvalidProtocolOption,
6741 .NOMEM => return error.SystemResources,
6742 .NOBUFS => return error.SystemResources,
6743 .PERM => return error.PermissionDenied,
6744 .NODEV => return error.NoDevice,
6745 else => |err| return unexpectedErrno(err),
6746 }
6747 }
6748}
6749
6750pub const MemFdCreateError = error{
6751 SystemFdQuotaExceeded,
6752 ProcessFdQuotaExceeded,
6753 OutOfMemory,
6754 /// Either the name provided exceeded `NAME_MAX`, or invalid flags were passed.
6755 NameTooLong,
6756
6757 /// memfd_create is available in Linux 3.17 and later. This error is returned
6758 /// for older kernel versions.
6759 SystemOutdated,
6760} || UnexpectedError;
6761
6762pub fn memfd_createZ(name: [*:0]const u8, flags: u32) MemFdCreateError!fd_t {
6763 switch (native_os) {
6764 .linux => {
6765 // memfd_create is available only in glibc versions starting with 2.27.
6766 const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 });
6767 const sys = if (use_c) std.c else linux;
6768 const rc = sys.memfd_create(name, flags);
6769 switch (errno(rc)) {
6770 .SUCCESS => return @intCast(rc),
6771 .FAULT => unreachable, // name has invalid memory
6772 .INVAL => return error.NameTooLong, // or, program has a bug and flags are faulty
6773 .NFILE => return error.SystemFdQuotaExceeded,
6774 .MFILE => return error.ProcessFdQuotaExceeded,
6775 .NOMEM => return error.OutOfMemory,
6776 .NOSYS => return error.SystemOutdated,
6777 else => |err| return unexpectedErrno(err),
6778 }
6779 },
6780 .freebsd => {
6781 if (comptime builtin.os.version_range.semver.max.order(.{ .major = 13, .minor = 0, .patch = 0 }) == .lt)
6782 @compileError("memfd_create is unavailable on FreeBSD < 13.0");
6783 const rc = system.memfd_create(name, flags);
6784 switch (errno(rc)) {
6785 .SUCCESS => return rc,
6786 .BADF => unreachable, // name argument NULL
6787 .INVAL => unreachable, // name too long or invalid/unsupported flags.
6788 .MFILE => return error.ProcessFdQuotaExceeded,
6789 .NFILE => return error.SystemFdQuotaExceeded,
6790 .NOSYS => return error.SystemOutdated,
6791 else => |err| return unexpectedErrno(err),
6792 }
6793 },
6794 else => @compileError("target OS does not support memfd_create()"),
6795 }
6796}
6797
6798pub fn memfd_create(name: []const u8, flags: u32) MemFdCreateError!fd_t {
6799 var buffer: [NAME_MAX - "memfd:".len - 1:0]u8 = undefined;
6800 if (name.len > buffer.len) return error.NameTooLong;
6801 @memcpy(buffer[0..name.len], name);
6802 buffer[name.len] = 0;
6803 return memfd_createZ(&buffer, flags);
6804}
6805
6806pub fn getrusage(who: i32) rusage {
6807 var result: rusage = undefined;
6808 const rc = system.getrusage(who, &result);
6809 switch (errno(rc)) {
6810 .SUCCESS => return result,
6811 .INVAL => unreachable,
6812 .FAULT => unreachable,
6813 else => unreachable,
6814 }
6815}
6816
6817pub const TIOCError = error{NotATerminal};
6818
6819pub const TermiosGetError = TIOCError || UnexpectedError;
6820
6821pub fn tcgetattr(handle: fd_t) TermiosGetError!termios {
6822 while (true) {
6823 var term: termios = undefined;
6824 switch (errno(system.tcgetattr(handle, &term))) {
6825 .SUCCESS => return term,
6826 .INTR => continue,
6827 .BADF => unreachable,
6828 .NOTTY => return error.NotATerminal,
6829 else => |err| return unexpectedErrno(err),
6830 }
6831 }
6832}
6833
6834pub const TermiosSetError = TermiosGetError || error{ProcessOrphaned};
6835
6836pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) TermiosSetError!void {
6837 while (true) {
6838 switch (errno(system.tcsetattr(handle, optional_action, &termios_p))) {
6839 .SUCCESS => return,
6840 .BADF => unreachable,
6841 .INTR => continue,
6842 .INVAL => unreachable,
6843 .NOTTY => return error.NotATerminal,
6844 .IO => return error.ProcessOrphaned,
6845 else => |err| return unexpectedErrno(err),
6846 }
6847 }
6848}
6849
6850pub const TermioGetPgrpError = TIOCError || UnexpectedError;
6851
6852/// Returns the process group ID for the TTY associated with the given handle.
6853pub fn tcgetpgrp(handle: fd_t) TermioGetPgrpError!pid_t {
6854 while (true) {
6855 var pgrp: pid_t = undefined;
6856 switch (errno(system.tcgetpgrp(handle, &pgrp))) {
6857 .SUCCESS => return pgrp,
6858 .BADF => unreachable,
6859 .INVAL => unreachable,
6860 .INTR => continue,
6861 .NOTTY => return error.NotATerminal,
6862 else => |err| return unexpectedErrno(err),
6863 }
6864 }
6865}
6866
6867pub const TermioSetPgrpError = TermioGetPgrpError || error{NotAPgrpMember};
6868
6869/// Sets the controlling process group ID for given TTY.
6870/// handle must be valid fd_t to a TTY associated with calling process.
6871/// pgrp must be a valid process group, and the calling process must be a member
6872/// of that group.
6873pub fn tcsetpgrp(handle: fd_t, pgrp: pid_t) TermioSetPgrpError!void {
6874 while (true) {
6875 switch (errno(system.tcsetpgrp(handle, &pgrp))) {
6876 .SUCCESS => return,
6877 .BADF => unreachable,
6878 .INVAL => unreachable,
6879 .INTR => continue,
6880 .NOTTY => return error.NotATerminal,
6881 .PERM => return TermioSetPgrpError.NotAPgrpMember,
6882 else => |err| return unexpectedErrno(err),
6883 }
6884 }
6885}
6886
6887pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t {
6888 const rc = system.signalfd(fd, mask, flags);
6889 switch (errno(rc)) {
6890 .SUCCESS => return @intCast(rc),
6891 .BADF, .INVAL => unreachable,
6892 .NFILE => return error.SystemFdQuotaExceeded,
6893 .NOMEM => return error.SystemResources,
6894 .MFILE => return error.ProcessResources,
6895 .NODEV => return error.InodeMountFail,
6896 .NOSYS => return error.SystemOutdated,
6897 else => |err| return unexpectedErrno(err),
6898 }
6899}
6900
6901pub const SyncError = error{
6902 InputOutput,
6903 NoSpaceLeft,
6904 DiskQuota,
6905 AccessDenied,
6906} || UnexpectedError;
6907
6908/// Write all pending file contents and metadata modifications to all filesystems.
6909pub fn sync() void {
6910 system.sync();
6911}
6912
6913/// Write all pending file contents and metadata modifications to the filesystem which contains the specified file.
6914pub fn syncfs(fd: fd_t) SyncError!void {
6915 const rc = system.syncfs(fd);
6916 switch (errno(rc)) {
6917 .SUCCESS => return,
6918 .BADF, .INVAL, .ROFS => unreachable,
6919 .IO => return error.InputOutput,
6920 .NOSPC => return error.NoSpaceLeft,
6921 .DQUOT => return error.DiskQuota,
6922 else => |err| return unexpectedErrno(err),
6923 }
6924}
6925
6926/// Write all pending file contents and metadata modifications for the specified file descriptor to the underlying filesystem.
6927pub fn fsync(fd: fd_t) SyncError!void {
6928 if (native_os == .windows) {
6929 if (windows.kernel32.FlushFileBuffers(fd) != 0)
6930 return;
6931 switch (windows.kernel32.GetLastError()) {
6932 .SUCCESS => return,
6933 .INVALID_HANDLE => unreachable,
6934 .ACCESS_DENIED => return error.AccessDenied, // a sync was performed but the system couldn't update the access time
6935 .UNEXP_NET_ERR => return error.InputOutput,
6936 else => return error.InputOutput,
6937 }
6938 }
6939 const rc = system.fsync(fd);
6940 switch (errno(rc)) {
6941 .SUCCESS => return,
6942 .BADF, .INVAL, .ROFS => unreachable,
6943 .IO => return error.InputOutput,
6944 .NOSPC => return error.NoSpaceLeft,
6945 .DQUOT => return error.DiskQuota,
6946 else => |err| return unexpectedErrno(err),
6947 }
6948}
6949
6950/// Write all pending file contents for the specified file descriptor to the underlying filesystem, but not necessarily the metadata.
6951pub fn fdatasync(fd: fd_t) SyncError!void {
6952 if (native_os == .windows) {
6953 return fsync(fd) catch |err| switch (err) {
6954 SyncError.AccessDenied => return, // fdatasync doesn't promise that the access time was synced
6955 else => return err,
6956 };
6957 }
6958 const rc = system.fdatasync(fd);
6959 switch (errno(rc)) {
6960 .SUCCESS => return,
6961 .BADF, .INVAL, .ROFS => unreachable,
6962 .IO => return error.InputOutput,
6963 .NOSPC => return error.NoSpaceLeft,
6964 .DQUOT => return error.DiskQuota,
6965 else => |err| return unexpectedErrno(err),
6966 }
6967}
6968
6969pub const PrctlError = error{
6970 /// Can only occur with PR_SET_SECCOMP/SECCOMP_MODE_FILTER or
6971 /// PR_SET_MM/PR_SET_MM_EXE_FILE
6972 AccessDenied,
6973 /// Can only occur with PR_SET_MM/PR_SET_MM_EXE_FILE
6974 InvalidFileDescriptor,
6975 InvalidAddress,
6976 /// Can only occur with PR_SET_SPECULATION_CTRL, PR_MPX_ENABLE_MANAGEMENT,
6977 /// or PR_MPX_DISABLE_MANAGEMENT
6978 UnsupportedFeature,
6979 /// Can only occur with PR_SET_FP_MODE
6980 OperationNotSupported,
6981 PermissionDenied,
6982} || UnexpectedError;
6983
6984pub fn prctl(option: PR, args: anytype) PrctlError!u31 {
6985 if (@typeInfo(@TypeOf(args)) != .Struct)
6986 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
6987 if (args.len > 4)
6988 @compileError("prctl takes a maximum of 4 optional arguments");
6989
6990 var buf: [4]usize = undefined;
6991 {
6992 comptime var i = 0;
6993 inline while (i < args.len) : (i += 1) buf[i] = args[i];
6994 }
6995
6996 const rc = system.prctl(@intFromEnum(option), buf[0], buf[1], buf[2], buf[3]);
6997 switch (errno(rc)) {
6998 .SUCCESS => return @intCast(rc),
6999 .ACCES => return error.AccessDenied,
7000 .BADF => return error.InvalidFileDescriptor,
7001 .FAULT => return error.InvalidAddress,
7002 .INVAL => unreachable,
7003 .NODEV, .NXIO => return error.UnsupportedFeature,
7004 .OPNOTSUPP => return error.OperationNotSupported,
7005 .PERM, .BUSY => return error.PermissionDenied,
7006 .RANGE => unreachable,
7007 else => |err| return unexpectedErrno(err),
7008 }
7009}
7010
7011pub const GetrlimitError = UnexpectedError;
7012
7013pub fn getrlimit(resource: rlimit_resource) GetrlimitError!rlimit {
7014 const getrlimit_sym = if (lfs64_abi) system.getrlimit64 else system.getrlimit;
7015
7016 var limits: rlimit = undefined;
7017 switch (errno(getrlimit_sym(resource, &limits))) {
7018 .SUCCESS => return limits,
7019 .FAULT => unreachable, // bogus pointer
7020 .INVAL => unreachable,
7021 else => |err| return unexpectedErrno(err),
7022 }
7023}
7024
7025pub const SetrlimitError = error{ PermissionDenied, LimitTooBig } || UnexpectedError;
7026
7027pub fn setrlimit(resource: rlimit_resource, limits: rlimit) SetrlimitError!void {
7028 const setrlimit_sym = if (lfs64_abi) system.setrlimit64 else system.setrlimit;
7029
7030 switch (errno(setrlimit_sym(resource, &limits))) {
7031 .SUCCESS => return,
7032 .FAULT => unreachable, // bogus pointer
7033 .INVAL => return error.LimitTooBig, // this could also mean "invalid resource", but that would be unreachable
7034 .PERM => return error.PermissionDenied,
7035 else => |err| return unexpectedErrno(err),
7036 }
7037}
7038
7039pub const MincoreError = error{
7040 /// A kernel resource was temporarily unavailable.
7041 SystemResources,
7042 /// vec points to an invalid address.
7043 InvalidAddress,
7044 /// addr is not page-aligned.
7045 InvalidSyscall,
7046 /// One of the following:
7047 /// * length is greater than user space TASK_SIZE - addr
7048 /// * addr + length contains unmapped memory
7049 OutOfMemory,
7050 /// The mincore syscall is not available on this version and configuration
7051 /// of this UNIX-like kernel.
7052 MincoreUnavailable,
7053} || UnexpectedError;
7054
7055/// Determine whether pages are resident in memory.
7056pub fn mincore(ptr: [*]align(mem.page_size) u8, length: usize, vec: [*]u8) MincoreError!void {
7057 return switch (errno(system.mincore(ptr, length, vec))) {
7058 .SUCCESS => {},
7059 .AGAIN => error.SystemResources,
7060 .FAULT => error.InvalidAddress,
7061 .INVAL => error.InvalidSyscall,
7062 .NOMEM => error.OutOfMemory,
7063 .NOSYS => error.MincoreUnavailable,
7064 else => |err| unexpectedErrno(err),
7065 };
7066}
7067
7068pub const MadviseError = error{
7069 /// advice is MADV.REMOVE, but the specified address range is not a shared writable mapping.
7070 AccessDenied,
7071 /// advice is MADV.HWPOISON, but the caller does not have the CAP_SYS_ADMIN capability.
7072 PermissionDenied,
7073 /// A kernel resource was temporarily unavailable.
7074 SystemResources,
7075 /// One of the following:
7076 /// * addr is not page-aligned or length is negative
7077 /// * advice is not valid
7078 /// * advice is MADV.DONTNEED or MADV.REMOVE and the specified address range
7079 /// includes locked, Huge TLB pages, or VM_PFNMAP pages.
7080 /// * advice is MADV.MERGEABLE or MADV.UNMERGEABLE, but the kernel was not
7081 /// configured with CONFIG_KSM.
7082 /// * advice is MADV.FREE or MADV.WIPEONFORK but the specified address range
7083 /// includes file, Huge TLB, MAP.SHARED, or VM_PFNMAP ranges.
7084 InvalidSyscall,
7085 /// (for MADV.WILLNEED) Paging in this area would exceed the process's
7086 /// maximum resident set size.
7087 WouldExceedMaximumResidentSetSize,
7088 /// One of the following:
7089 /// * (for MADV.WILLNEED) Not enough memory: paging in failed.
7090 /// * Addresses in the specified range are not currently mapped, or
7091 /// are outside the address space of the process.
7092 OutOfMemory,
7093 /// The madvise syscall is not available on this version and configuration
7094 /// of the Linux kernel.
7095 MadviseUnavailable,
7096 /// The operating system returned an undocumented error code.
7097 Unexpected,
7098};
7099
7100/// Give advice about use of memory.
7101/// This syscall is optional and is sometimes configured to be disabled.
7102pub fn madvise(ptr: [*]align(mem.page_size) u8, length: usize, advice: u32) MadviseError!void {
7103 switch (errno(system.madvise(ptr, length, advice))) {
7104 .SUCCESS => return,
7105 .ACCES => return error.AccessDenied,
7106 .AGAIN => return error.SystemResources,
7107 .BADF => unreachable, // The map exists, but the area maps something that isn't a file.
7108 .INVAL => return error.InvalidSyscall,
7109 .IO => return error.WouldExceedMaximumResidentSetSize,
7110 .NOMEM => return error.OutOfMemory,
7111 .NOSYS => return error.MadviseUnavailable,
7112 else => |err| return unexpectedErrno(err),
7113 }
7114}
7115
7116pub const PerfEventOpenError = error{
7117 /// Returned if the perf_event_attr size value is too small (smaller
7118 /// than PERF_ATTR_SIZE_VER0), too big (larger than the page size),
7119 /// or larger than the kernel supports and the extra bytes are not
7120 /// zero. When E2BIG is returned, the perf_event_attr size field is
7121 /// overwritten by the kernel to be the size of the structure it was
7122 /// expecting.
7123 TooBig,
7124 /// Returned when the requested event requires CAP_SYS_ADMIN permis‐
7125 /// sions (or a more permissive perf_event paranoid setting). Some
7126 /// common cases where an unprivileged process may encounter this
7127 /// error: attaching to a process owned by a different user; moni‐
7128 /// toring all processes on a given CPU (i.e., specifying the pid
7129 /// argument as -1); and not setting exclude_kernel when the para‐
7130 /// noid setting requires it.
7131 /// Also:
7132 /// Returned on many (but not all) architectures when an unsupported
7133 /// exclude_hv, exclude_idle, exclude_user, or exclude_kernel set‐
7134 /// ting is specified.
7135 /// It can also happen, as with EACCES, when the requested event re‐
7136 /// quires CAP_SYS_ADMIN permissions (or a more permissive
7137 /// perf_event paranoid setting). This includes setting a break‐
7138 /// point on a kernel address, and (since Linux 3.13) setting a ker‐
7139 /// nel function-trace tracepoint.
7140 PermissionDenied,
7141 /// Returned if another event already has exclusive access to the
7142 /// PMU.
7143 DeviceBusy,
7144 /// Each opened event uses one file descriptor. If a large number
7145 /// of events are opened, the per-process limit on the number of
7146 /// open file descriptors will be reached, and no more events can be
7147 /// created.
7148 ProcessResources,
7149 EventRequiresUnsupportedCpuFeature,
7150 /// Returned if you try to add more breakpoint
7151 /// events than supported by the hardware.
7152 TooManyBreakpoints,
7153 /// Returned if PERF_SAMPLE_STACK_USER is set in sample_type and it
7154 /// is not supported by hardware.
7155 SampleStackNotSupported,
7156 /// Returned if an event requiring a specific hardware feature is
7157 /// requested but there is no hardware support. This includes re‐
7158 /// questing low-skid events if not supported, branch tracing if it
7159 /// is not available, sampling if no PMU interrupt is available, and
7160 /// branch stacks for software events.
7161 EventNotSupported,
7162 /// Returned if PERF_SAMPLE_CALLCHAIN is requested and sam‐
7163 /// ple_max_stack is larger than the maximum specified in
7164 /// /proc/sys/kernel/perf_event_max_stack.
7165 SampleMaxStackOverflow,
7166 /// Returned if attempting to attach to a process that does not exist.
7167 ProcessNotFound,
7168} || UnexpectedError;
7169
7170pub fn perf_event_open(
7171 attr: *linux.perf_event_attr,
7172 pid: pid_t,
7173 cpu: i32,
7174 group_fd: fd_t,
7175 flags: usize,
7176) PerfEventOpenError!fd_t {
7177 const rc = linux.perf_event_open(attr, pid, cpu, group_fd, flags);
7178 switch (errno(rc)) {
7179 .SUCCESS => return @intCast(rc),
7180 .@"2BIG" => return error.TooBig,
7181 .ACCES => return error.PermissionDenied,
7182 .BADF => unreachable, // group_fd file descriptor is not valid.
7183 .BUSY => return error.DeviceBusy,
7184 .FAULT => unreachable, // Segmentation fault.
7185 .INVAL => unreachable, // Bad attr settings.
7186 .INTR => unreachable, // Mixed perf and ftrace handling for a uprobe.
7187 .MFILE => return error.ProcessResources,
7188 .NODEV => return error.EventRequiresUnsupportedCpuFeature,
7189 .NOENT => unreachable, // Invalid type setting.
7190 .NOSPC => return error.TooManyBreakpoints,
7191 .NOSYS => return error.SampleStackNotSupported,
7192 .OPNOTSUPP => return error.EventNotSupported,
7193 .OVERFLOW => return error.SampleMaxStackOverflow,
7194 .PERM => return error.PermissionDenied,
7195 .SRCH => return error.ProcessNotFound,
7196 else => |err| return unexpectedErrno(err),
7197 }
7198}
7199
7200pub const TimerFdCreateError = error{
7201 AccessDenied,
7202 ProcessFdQuotaExceeded,
7203 SystemFdQuotaExceeded,
7204 NoDevice,
7205 SystemResources,
7206} || UnexpectedError;
7207
7208pub const TimerFdGetError = error{InvalidHandle} || UnexpectedError;
7209pub const TimerFdSetError = TimerFdGetError || error{Canceled};
7210
7211pub fn timerfd_create(clokid: i32, flags: linux.TFD) TimerFdCreateError!fd_t {
7212 const rc = linux.timerfd_create(clokid, flags);
7213 return switch (errno(rc)) {
7214 .SUCCESS => @intCast(rc),
7215 .INVAL => unreachable,
7216 .MFILE => return error.ProcessFdQuotaExceeded,
7217 .NFILE => return error.SystemFdQuotaExceeded,
7218 .NODEV => return error.NoDevice,
7219 .NOMEM => return error.SystemResources,
7220 .PERM => return error.AccessDenied,
7221 else => |err| return unexpectedErrno(err),
7222 };
7223}
7224
7225pub fn timerfd_settime(
7226 fd: i32,
7227 flags: linux.TFD.TIMER,
7228 new_value: *const linux.itimerspec,
7229 old_value: ?*linux.itimerspec,
7230) TimerFdSetError!void {
7231 const rc = linux.timerfd_settime(fd, flags, new_value, old_value);
7232 return switch (errno(rc)) {
7233 .SUCCESS => {},
7234 .BADF => error.InvalidHandle,
7235 .FAULT => unreachable,
7236 .INVAL => unreachable,
7237 .CANCELED => error.Canceled,
7238 else => |err| return unexpectedErrno(err),
7239 };
7240}
7241
7242pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec {
7243 var curr_value: linux.itimerspec = undefined;
7244 const rc = linux.timerfd_gettime(fd, &curr_value);
7245 return switch (errno(rc)) {
7246 .SUCCESS => return curr_value,
7247 .BADF => error.InvalidHandle,
7248 .FAULT => unreachable,
7249 .INVAL => unreachable,
7250 else => |err| return unexpectedErrno(err),
7251 };
7252}
7253
7254pub const PtraceError = error{
7255 DeviceBusy,
7256 InputOutput,
7257 ProcessNotFound,
7258 PermissionDenied,
7259} || UnexpectedError;
7260
7261pub fn ptrace(request: u32, pid: pid_t, addr: usize, signal: usize) PtraceError!void {
7262 if (native_os == .windows or native_os == .wasi)
7263 @compileError("Unsupported OS");
7264
7265 return switch (native_os) {
7266 .linux => switch (errno(linux.ptrace(request, pid, addr, signal, 0))) {
7267 .SUCCESS => {},
7268 .SRCH => error.ProcessNotFound,
7269 .FAULT => unreachable,
7270 .INVAL => unreachable,
7271 .IO => return error.InputOutput,
7272 .PERM => error.PermissionDenied,
7273 .BUSY => error.DeviceBusy,
7274 else => |err| return unexpectedErrno(err),
7275 },
7276
7277 .macos, .ios, .tvos, .watchos => switch (errno(std.c.ptrace(
7278 @intCast(request),
7279 pid,
7280 @ptrFromInt(addr),
7281 @intCast(signal),
7282 ))) {
7283 .SUCCESS => {},
7284 .SRCH => error.ProcessNotFound,
7285 .INVAL => unreachable,
7286 .PERM => error.PermissionDenied,
7287 .BUSY => error.DeviceBusy,
7288 else => |err| return unexpectedErrno(err),
7289 },
7290
7291 else => switch (errno(system.ptrace(request, pid, addr, signal))) {
7292 .SUCCESS => {},
7293 .SRCH => error.ProcessNotFound,
7294 .INVAL => unreachable,
7295 .PERM => error.PermissionDenied,
7296 .BUSY => error.DeviceBusy,
7297 else => |err| return unexpectedErrno(err),
7298 },
7299 };
7300}
7301
7302pub const IoCtl_SIOCGIFINDEX_Error = error{
7303 FileSystem,
7304 InterfaceNotFound,
7305} || UnexpectedError;
7306
7307pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void {
7308 while (true) {
7309 switch (errno(system.ioctl(fd, SIOCGIFINDEX, @intFromPtr(ifr)))) {
7310 .SUCCESS => return,
7311 .INVAL => unreachable, // Bad parameters.
7312 .NOTTY => unreachable,
7313 .NXIO => unreachable,
7314 .BADF => unreachable, // Always a race condition.
7315 .FAULT => unreachable, // Bad pointer parameter.
7316 .INTR => continue,
7317 .IO => return error.FileSystem,
7318 .NODEV => return error.InterfaceNotFound,
7319 else => |err| return unexpectedErrno(err),
7320 }
7321 }
7322}
7323
7324const lfs64_abi = native_os == .linux and builtin.link_libc and builtin.abi.isGnu();
7325
7326/// Whether or not `error.Unexpected` will print its value and a stack trace.
7327///
7328/// If this happens the fix is to add the error code to the corresponding
7329/// switch expression, possibly introduce a new error in the error set, and
7330/// send a patch to Zig.
7331pub const unexpected_error_tracing = builtin.zig_backend == .stage2_llvm and builtin.mode == .Debug;
7332
7333pub const UnexpectedError = error{
7334 /// The Operating System returned an undocumented error code.
7335 ///
7336 /// This error is in theory not possible, but it would be better
7337 /// to handle this error than to invoke undefined behavior.
7338 ///
7339 /// When this error code is observed, it usually means the Zig Standard
7340 /// Library needs a small patch to add the error code to the error set for
7341 /// the respective function.
7342 Unexpected,
7343};
7344
7345/// Call this when you made a syscall or something that sets errno
7346/// and you get an unexpected error.
7347pub fn unexpectedErrno(err: E) UnexpectedError {
7348 if (unexpected_error_tracing) {
7349 std.debug.print("unexpected errno: {d}\n", .{@intFromEnum(err)});
7350 std.debug.dumpCurrentStackTrace(null);
7351 }
7352 return error.Unexpected;
7353}
7354
7355/// Used to convert a slice to a null terminated slice on the stack.
7356pub fn toPosixPath(file_path: []const u8) error{NameTooLong}![PATH_MAX - 1:0]u8 {
7357 if (std.debug.runtime_safety) assert(mem.indexOfScalar(u8, file_path, 0) == null);
7358 var path_with_null: [PATH_MAX - 1:0]u8 = undefined;
7359 // >= rather than > to make room for the null byte
7360 if (file_path.len >= PATH_MAX) return error.NameTooLong;
7361 @memcpy(path_with_null[0..file_path.len], file_path);
7362 path_with_null[file_path.len] = 0;
7363 return path_with_null;
7364}
lib/std/posix/test.zig created+1317
......@@ -0,0 +1,1317 @@
1const std = @import("../std.zig");
2const posix = std.posix;
3const testing = std.testing;
4const expect = testing.expect;
5const expectEqual = testing.expectEqual;
6const expectError = testing.expectError;
7const io = std.io;
8const fs = std.fs;
9const mem = std.mem;
10const elf = std.elf;
11const File = std.fs.File;
12const Thread = std.Thread;
13const linux = std.os.linux;
14
15const a = std.testing.allocator;
16
17const builtin = @import("builtin");
18const AtomicRmwOp = std.builtin.AtomicRmwOp;
19const AtomicOrder = std.builtin.AtomicOrder;
20const native_os = builtin.target.os.tag;
21const tmpDir = std.testing.tmpDir;
22const Dir = std.fs.Dir;
23const ArenaAllocator = std.heap.ArenaAllocator;
24
25test "chdir smoke test" {
26 if (native_os == .wasi) return error.SkipZigTest;
27
28 if (true) {
29 // https://github.com/ziglang/zig/issues/14968
30 return error.SkipZigTest;
31 }
32
33 // Get current working directory path
34 var old_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
35 const old_cwd = try posix.getcwd(old_cwd_buf[0..]);
36
37 {
38 // Firstly, changing to itself should have no effect
39 try posix.chdir(old_cwd);
40 var new_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
41 const new_cwd = try posix.getcwd(new_cwd_buf[0..]);
42 try expect(mem.eql(u8, old_cwd, new_cwd));
43 }
44
45 // Next, change current working directory to one level above
46 if (native_os != .wasi) { // WASI does not support navigating outside of Preopens
47 const parent = fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute
48 try posix.chdir(parent);
49
50 // Restore cwd because process may have other tests that do not tolerate chdir.
51 defer posix.chdir(old_cwd) catch unreachable;
52
53 var new_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
54 const new_cwd = try posix.getcwd(new_cwd_buf[0..]);
55 try expect(mem.eql(u8, parent, new_cwd));
56 }
57
58 // Next, change current working directory to a temp directory one level below
59 {
60 // Create a tmp directory
61 var tmp_dir_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
62 const tmp_dir_path = path: {
63 var allocator = std.heap.FixedBufferAllocator.init(&tmp_dir_buf);
64 break :path try fs.path.resolve(allocator.allocator(), &[_][]const u8{ old_cwd, "zig-test-tmp" });
65 };
66 var tmp_dir = try fs.cwd().makeOpenPath("zig-test-tmp", .{});
67
68 // Change current working directory to tmp directory
69 try posix.chdir("zig-test-tmp");
70
71 var new_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
72 const new_cwd = try posix.getcwd(new_cwd_buf[0..]);
73
74 // On Windows, fs.path.resolve returns an uppercase drive letter, but the drive letter returned by getcwd may be lowercase
75 var resolved_cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
76 const resolved_cwd = path: {
77 var allocator = std.heap.FixedBufferAllocator.init(&resolved_cwd_buf);
78 break :path try fs.path.resolve(allocator.allocator(), &[_][]const u8{new_cwd});
79 };
80 try expect(mem.eql(u8, tmp_dir_path, resolved_cwd));
81
82 // Restore cwd because process may have other tests that do not tolerate chdir.
83 tmp_dir.close();
84 posix.chdir(old_cwd) catch unreachable;
85 try fs.cwd().deleteDir("zig-test-tmp");
86 }
87}
88
89test "open smoke test" {
90 if (native_os == .wasi) return error.SkipZigTest;
91 if (native_os == .windows) return error.SkipZigTest;
92
93 // TODO verify file attributes using `fstat`
94
95 var tmp = tmpDir(.{});
96 defer tmp.cleanup();
97
98 // Get base abs path
99 var arena = ArenaAllocator.init(testing.allocator);
100 defer arena.deinit();
101 const allocator = arena.allocator();
102
103 const base_path = blk: {
104 const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
105 break :blk try fs.realpathAlloc(allocator, relative_path);
106 };
107
108 var file_path: []u8 = undefined;
109 var fd: posix.fd_t = undefined;
110 const mode: posix.mode_t = if (native_os == .windows) 0 else 0o666;
111
112 // Create some file using `open`.
113 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
114 fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode);
115 posix.close(fd);
116
117 // Try this again with the same flags. This op should fail with error.PathAlreadyExists.
118 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
119 try expectError(error.PathAlreadyExists, posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode));
120
121 // Try opening without `EXCL` flag.
122 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
123 fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true }, mode);
124 posix.close(fd);
125
126 // Try opening as a directory which should fail.
127 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
128 try expectError(error.NotDir, posix.open(file_path, .{ .ACCMODE = .RDWR, .DIRECTORY = true }, mode));
129
130 // Create some directory
131 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
132 try posix.mkdir(file_path, mode);
133
134 // Open dir using `open`
135 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
136 fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode);
137 posix.close(fd);
138
139 // Try opening as file which should fail.
140 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
141 try expectError(error.IsDir, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode));
142}
143
144test "openat smoke test" {
145 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
146 if (native_os == .windows) return error.SkipZigTest;
147
148 // TODO verify file attributes using `fstatat`
149
150 var tmp = tmpDir(.{});
151 defer tmp.cleanup();
152
153 var fd: posix.fd_t = undefined;
154 const mode: posix.mode_t = if (native_os == .windows) 0 else 0o666;
155
156 // Create some file using `openat`.
157 fd = try posix.openat(tmp.dir.fd, "some_file", CommonOpenFlags.lower(.{
158 .ACCMODE = .RDWR,
159 .CREAT = true,
160 .EXCL = true,
161 }), mode);
162 posix.close(fd);
163
164 // Try this again with the same flags. This op should fail with error.PathAlreadyExists.
165 try expectError(error.PathAlreadyExists, posix.openat(tmp.dir.fd, "some_file", CommonOpenFlags.lower(.{
166 .ACCMODE = .RDWR,
167 .CREAT = true,
168 .EXCL = true,
169 }), mode));
170
171 // Try opening without `EXCL` flag.
172 fd = try posix.openat(tmp.dir.fd, "some_file", CommonOpenFlags.lower(.{
173 .ACCMODE = .RDWR,
174 .CREAT = true,
175 }), mode);
176 posix.close(fd);
177
178 // Try opening as a directory which should fail.
179 try expectError(error.NotDir, posix.openat(tmp.dir.fd, "some_file", CommonOpenFlags.lower(.{
180 .ACCMODE = .RDWR,
181 .DIRECTORY = true,
182 }), mode));
183
184 // Create some directory
185 try posix.mkdirat(tmp.dir.fd, "some_dir", mode);
186
187 // Open dir using `open`
188 fd = try posix.openat(tmp.dir.fd, "some_dir", CommonOpenFlags.lower(.{
189 .ACCMODE = .RDONLY,
190 .DIRECTORY = true,
191 }), mode);
192 posix.close(fd);
193
194 // Try opening as file which should fail.
195 try expectError(error.IsDir, posix.openat(tmp.dir.fd, "some_dir", CommonOpenFlags.lower(.{
196 .ACCMODE = .RDWR,
197 }), mode));
198}
199
200test "symlink with relative paths" {
201 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
202
203 if (true) {
204 // https://github.com/ziglang/zig/issues/14968
205 return error.SkipZigTest;
206 }
207 const cwd = fs.cwd();
208 cwd.deleteFile("file.txt") catch {};
209 cwd.deleteFile("symlinked") catch {};
210
211 // First, try relative paths in cwd
212 try cwd.writeFile("file.txt", "nonsense");
213
214 if (native_os == .windows) {
215 std.os.windows.CreateSymbolicLink(
216 cwd.fd,
217 &[_]u16{ 's', 'y', 'm', 'l', 'i', 'n', 'k', 'e', 'd' },
218 &[_:0]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' },
219 false,
220 ) catch |err| switch (err) {
221 // Symlink requires admin privileges on windows, so this test can legitimately fail.
222 error.AccessDenied => {
223 try cwd.deleteFile("file.txt");
224 try cwd.deleteFile("symlinked");
225 return error.SkipZigTest;
226 },
227 else => return err,
228 };
229 } else {
230 try posix.symlink("file.txt", "symlinked");
231 }
232
233 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
234 const given = try posix.readlink("symlinked", buffer[0..]);
235 try expect(mem.eql(u8, "file.txt", given));
236
237 try cwd.deleteFile("file.txt");
238 try cwd.deleteFile("symlinked");
239}
240
241test "readlink on Windows" {
242 if (native_os != .windows) return error.SkipZigTest;
243
244 try testReadlink("C:\\ProgramData", "C:\\Users\\All Users");
245 try testReadlink("C:\\Users\\Default", "C:\\Users\\Default User");
246 try testReadlink("C:\\Users", "C:\\Documents and Settings");
247}
248
249fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void {
250 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
251 const given = try posix.readlink(symlink_path, buffer[0..]);
252 try expect(mem.eql(u8, target_path, given));
253}
254
255test "link with relative paths" {
256 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
257
258 switch (native_os) {
259 .wasi, .linux, .solaris, .illumos => {},
260 else => return error.SkipZigTest,
261 }
262 if (true) {
263 // https://github.com/ziglang/zig/issues/14968
264 return error.SkipZigTest;
265 }
266 var cwd = fs.cwd();
267
268 cwd.deleteFile("example.txt") catch {};
269 cwd.deleteFile("new.txt") catch {};
270
271 try cwd.writeFile("example.txt", "example");
272 try posix.link("example.txt", "new.txt", 0);
273
274 const efd = try cwd.openFile("example.txt", .{});
275 defer efd.close();
276
277 const nfd = try cwd.openFile("new.txt", .{});
278 defer nfd.close();
279
280 {
281 const estat = try posix.fstat(efd.handle);
282 const nstat = try posix.fstat(nfd.handle);
283
284 try testing.expectEqual(estat.ino, nstat.ino);
285 try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink);
286 }
287
288 try posix.unlink("new.txt");
289
290 {
291 const estat = try posix.fstat(efd.handle);
292 try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink);
293 }
294
295 try cwd.deleteFile("example.txt");
296}
297
298test "linkat with different directories" {
299 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
300
301 switch (native_os) {
302 .wasi, .linux, .solaris, .illumos => {},
303 else => return error.SkipZigTest,
304 }
305 if (true) {
306 // https://github.com/ziglang/zig/issues/14968
307 return error.SkipZigTest;
308 }
309 var cwd = fs.cwd();
310 var tmp = tmpDir(.{});
311
312 cwd.deleteFile("example.txt") catch {};
313 tmp.dir.deleteFile("new.txt") catch {};
314
315 try cwd.writeFile("example.txt", "example");
316 try posix.linkat(cwd.fd, "example.txt", tmp.dir.fd, "new.txt", 0);
317
318 const efd = try cwd.openFile("example.txt", .{});
319 defer efd.close();
320
321 const nfd = try tmp.dir.openFile("new.txt", .{});
322
323 {
324 defer nfd.close();
325 const estat = try posix.fstat(efd.handle);
326 const nstat = try posix.fstat(nfd.handle);
327
328 try testing.expectEqual(estat.ino, nstat.ino);
329 try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink);
330 }
331
332 try posix.unlinkat(tmp.dir.fd, "new.txt", 0);
333
334 {
335 const estat = try posix.fstat(efd.handle);
336 try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink);
337 }
338
339 try cwd.deleteFile("example.txt");
340}
341
342test "fstatat" {
343 // enable when `fstat` and `fstatat` are implemented on Windows
344 if (native_os == .windows) return error.SkipZigTest;
345
346 var tmp = tmpDir(.{});
347 defer tmp.cleanup();
348
349 // create dummy file
350 const contents = "nonsense";
351 try tmp.dir.writeFile("file.txt", contents);
352
353 // fetch file's info on the opened fd directly
354 const file = try tmp.dir.openFile("file.txt", .{});
355 const stat = try posix.fstat(file.handle);
356 defer file.close();
357
358 // now repeat but using `fstatat` instead
359 const flags = if (native_os == .wasi) 0x0 else posix.AT.SYMLINK_NOFOLLOW;
360 const statat = try posix.fstatat(tmp.dir.fd, "file.txt", flags);
361 try expectEqual(stat, statat);
362}
363
364test "readlinkat" {
365 var tmp = tmpDir(.{});
366 defer tmp.cleanup();
367
368 // create file
369 try tmp.dir.writeFile("file.txt", "nonsense");
370
371 // create a symbolic link
372 if (native_os == .windows) {
373 std.os.windows.CreateSymbolicLink(
374 tmp.dir.fd,
375 &[_]u16{ 'l', 'i', 'n', 'k' },
376 &[_:0]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' },
377 false,
378 ) catch |err| switch (err) {
379 // Symlink requires admin privileges on windows, so this test can legitimately fail.
380 error.AccessDenied => return error.SkipZigTest,
381 else => return err,
382 };
383 } else {
384 try posix.symlinkat("file.txt", tmp.dir.fd, "link");
385 }
386
387 // read the link
388 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
389 const read_link = try posix.readlinkat(tmp.dir.fd, "link", buffer[0..]);
390 try expect(mem.eql(u8, "file.txt", read_link));
391}
392
393fn testThreadIdFn(thread_id: *Thread.Id) void {
394 thread_id.* = Thread.getCurrentId();
395}
396
397test "Thread.getCurrentId" {
398 if (builtin.single_threaded) return error.SkipZigTest;
399
400 var thread_current_id: Thread.Id = undefined;
401 const thread = try Thread.spawn(.{}, testThreadIdFn, .{&thread_current_id});
402 thread.join();
403 try expect(Thread.getCurrentId() != thread_current_id);
404}
405
406test "spawn threads" {
407 if (builtin.single_threaded) return error.SkipZigTest;
408
409 var shared_ctx: i32 = 1;
410
411 const thread1 = try Thread.spawn(.{}, start1, .{});
412 const thread2 = try Thread.spawn(.{}, start2, .{&shared_ctx});
413 const thread3 = try Thread.spawn(.{}, start2, .{&shared_ctx});
414 const thread4 = try Thread.spawn(.{}, start2, .{&shared_ctx});
415
416 thread1.join();
417 thread2.join();
418 thread3.join();
419 thread4.join();
420
421 try expect(shared_ctx == 4);
422}
423
424fn start1() u8 {
425 return 0;
426}
427
428fn start2(ctx: *i32) u8 {
429 _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.seq_cst);
430 return 0;
431}
432
433test "cpu count" {
434 if (native_os == .wasi) return error.SkipZigTest;
435
436 const cpu_count = try Thread.getCpuCount();
437 try expect(cpu_count >= 1);
438}
439
440test "thread local storage" {
441 if (builtin.single_threaded) return error.SkipZigTest;
442
443 const thread1 = try Thread.spawn(.{}, testTls, .{});
444 const thread2 = try Thread.spawn(.{}, testTls, .{});
445 try testTls();
446 thread1.join();
447 thread2.join();
448}
449
450threadlocal var x: i32 = 1234;
451fn testTls() !void {
452 if (x != 1234) return error.TlsBadStartValue;
453 x += 1;
454 if (x != 1235) return error.TlsBadEndValue;
455}
456
457test "getrandom" {
458 var buf_a: [50]u8 = undefined;
459 var buf_b: [50]u8 = undefined;
460 try posix.getrandom(&buf_a);
461 try posix.getrandom(&buf_b);
462 // If this test fails the chance is significantly higher that there is a bug than
463 // that two sets of 50 bytes were equal.
464 try expect(!mem.eql(u8, &buf_a, &buf_b));
465}
466
467test "getcwd" {
468 // at least call it so it gets compiled
469 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
470 _ = posix.getcwd(&buf) catch undefined;
471}
472
473test "sigaltstack" {
474 if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;
475
476 var st: posix.stack_t = undefined;
477 try posix.sigaltstack(null, &st);
478 // Setting a stack size less than MINSIGSTKSZ returns ENOMEM
479 st.flags = 0;
480 st.size = 1;
481 try testing.expectError(error.SizeTooSmall, posix.sigaltstack(&st, null));
482}
483
484// If the type is not available use void to avoid erroring out when `iter_fn` is
485// analyzed
486const dl_phdr_info = if (@hasDecl(posix.system, "dl_phdr_info")) posix.dl_phdr_info else anyopaque;
487
488const IterFnError = error{
489 MissingPtLoadSegment,
490 MissingLoad,
491 BadElfMagic,
492 FailedConsistencyCheck,
493};
494
495fn iter_fn(info: *dl_phdr_info, size: usize, counter: *usize) IterFnError!void {
496 _ = size;
497 // Count how many libraries are loaded
498 counter.* += @as(usize, 1);
499
500 // The image should contain at least a PT_LOAD segment
501 if (info.dlpi_phnum < 1) return error.MissingPtLoadSegment;
502
503 // Quick & dirty validation of the phdr pointers, make sure we're not
504 // pointing to some random gibberish
505 var i: usize = 0;
506 var found_load = false;
507 while (i < info.dlpi_phnum) : (i += 1) {
508 const phdr = info.dlpi_phdr[i];
509
510 if (phdr.p_type != elf.PT_LOAD) continue;
511
512 const reloc_addr = info.dlpi_addr + phdr.p_vaddr;
513 // Find the ELF header
514 const elf_header = @as(*elf.Ehdr, @ptrFromInt(reloc_addr - phdr.p_offset));
515 // Validate the magic
516 if (!mem.eql(u8, elf_header.e_ident[0..4], elf.MAGIC)) return error.BadElfMagic;
517 // Consistency check
518 if (elf_header.e_phnum != info.dlpi_phnum) return error.FailedConsistencyCheck;
519
520 found_load = true;
521 break;
522 }
523
524 if (!found_load) return error.MissingLoad;
525}
526
527test "dl_iterate_phdr" {
528 if (builtin.object_format != .elf) return error.SkipZigTest;
529
530 var counter: usize = 0;
531 try posix.dl_iterate_phdr(&counter, IterFnError, iter_fn);
532 try expect(counter != 0);
533}
534
535test "gethostname" {
536 if (native_os == .windows or native_os == .wasi)
537 return error.SkipZigTest;
538
539 var buf: [posix.HOST_NAME_MAX]u8 = undefined;
540 const hostname = try posix.gethostname(&buf);
541 try expect(hostname.len != 0);
542}
543
544test "pipe" {
545 if (native_os == .windows or native_os == .wasi)
546 return error.SkipZigTest;
547
548 const fds = try posix.pipe();
549 try expect((try posix.write(fds[1], "hello")) == 5);
550 var buf: [16]u8 = undefined;
551 try expect((try posix.read(fds[0], buf[0..])) == 5);
552 try testing.expectEqualSlices(u8, buf[0..5], "hello");
553 posix.close(fds[1]);
554 posix.close(fds[0]);
555}
556
557test "argsAlloc" {
558 const args = try std.process.argsAlloc(std.testing.allocator);
559 std.process.argsFree(std.testing.allocator, args);
560}
561
562test "memfd_create" {
563 // memfd_create is only supported by linux and freebsd.
564 switch (native_os) {
565 .linux => {},
566 .freebsd => {
567 if (comptime builtin.os.version_range.semver.max.order(.{ .major = 13, .minor = 0, .patch = 0 }) == .lt)
568 return error.SkipZigTest;
569 },
570 else => return error.SkipZigTest,
571 }
572
573 const fd = posix.memfd_create("test", 0) catch |err| switch (err) {
574 // Related: https://github.com/ziglang/zig/issues/4019
575 error.SystemOutdated => return error.SkipZigTest,
576 else => |e| return e,
577 };
578 defer posix.close(fd);
579 try expect((try posix.write(fd, "test")) == 4);
580 try posix.lseek_SET(fd, 0);
581
582 var buf: [10]u8 = undefined;
583 const bytes_read = try posix.read(fd, &buf);
584 try expect(bytes_read == 4);
585 try expect(mem.eql(u8, buf[0..4], "test"));
586}
587
588test "mmap" {
589 if (native_os == .windows or native_os == .wasi)
590 return error.SkipZigTest;
591
592 var tmp = tmpDir(.{});
593 defer tmp.cleanup();
594
595 // Simple mmap() call with non page-aligned size
596 {
597 const data = try posix.mmap(
598 null,
599 1234,
600 posix.PROT.READ | posix.PROT.WRITE,
601 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
602 -1,
603 0,
604 );
605 defer posix.munmap(data);
606
607 try testing.expectEqual(@as(usize, 1234), data.len);
608
609 // By definition the data returned by mmap is zero-filled
610 try testing.expect(mem.eql(u8, data, &[_]u8{0x00} ** 1234));
611
612 // Make sure the memory is writeable as requested
613 @memset(data, 0x55);
614 try testing.expect(mem.eql(u8, data, &[_]u8{0x55} ** 1234));
615 }
616
617 const test_out_file = "os_tmp_test";
618 // Must be a multiple of 4096 so that the test works with mmap2
619 const alloc_size = 8 * 4096;
620
621 // Create a file used for testing mmap() calls with a file descriptor
622 {
623 const file = try tmp.dir.createFile(test_out_file, .{});
624 defer file.close();
625
626 const stream = file.writer();
627
628 var i: u32 = 0;
629 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
630 try stream.writeInt(u32, i, .little);
631 }
632 }
633
634 // Map the whole file
635 {
636 const file = try tmp.dir.openFile(test_out_file, .{});
637 defer file.close();
638
639 const data = try posix.mmap(
640 null,
641 alloc_size,
642 posix.PROT.READ,
643 .{ .TYPE = .PRIVATE },
644 file.handle,
645 0,
646 );
647 defer posix.munmap(data);
648
649 var mem_stream = io.fixedBufferStream(data);
650 const stream = mem_stream.reader();
651
652 var i: u32 = 0;
653 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
654 try testing.expectEqual(i, try stream.readInt(u32, .little));
655 }
656 }
657
658 // Map the upper half of the file
659 {
660 const file = try tmp.dir.openFile(test_out_file, .{});
661 defer file.close();
662
663 const data = try posix.mmap(
664 null,
665 alloc_size / 2,
666 posix.PROT.READ,
667 .{ .TYPE = .PRIVATE },
668 file.handle,
669 alloc_size / 2,
670 );
671 defer posix.munmap(data);
672
673 var mem_stream = io.fixedBufferStream(data);
674 const stream = mem_stream.reader();
675
676 var i: u32 = alloc_size / 2 / @sizeOf(u32);
677 while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
678 try testing.expectEqual(i, try stream.readInt(u32, .little));
679 }
680 }
681
682 try tmp.dir.deleteFile(test_out_file);
683}
684
685test "getenv" {
686 if (native_os == .wasi and !builtin.link_libc) {
687 // std.posix.getenv is not supported on WASI due to the need of allocation
688 return error.SkipZigTest;
689 }
690
691 if (native_os == .windows) {
692 try expect(std.process.getenvW(&[_:0]u16{ 'B', 'O', 'G', 'U', 'S', 0x11, 0x22, 0x33, 0x44, 0x55 }) == null);
693 } else {
694 try expect(posix.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null);
695 }
696}
697
698test "fcntl" {
699 if (native_os == .windows or native_os == .wasi)
700 return error.SkipZigTest;
701
702 var tmp = tmpDir(.{});
703 defer tmp.cleanup();
704
705 const test_out_file = "os_tmp_test";
706
707 const file = try tmp.dir.createFile(test_out_file, .{});
708 defer {
709 file.close();
710 tmp.dir.deleteFile(test_out_file) catch {};
711 }
712
713 // Note: The test assumes createFile opens the file with CLOEXEC
714 {
715 const flags = try posix.fcntl(file.handle, posix.F.GETFD, 0);
716 try expect((flags & posix.FD_CLOEXEC) != 0);
717 }
718 {
719 _ = try posix.fcntl(file.handle, posix.F.SETFD, 0);
720 const flags = try posix.fcntl(file.handle, posix.F.GETFD, 0);
721 try expect((flags & posix.FD_CLOEXEC) == 0);
722 }
723 {
724 _ = try posix.fcntl(file.handle, posix.F.SETFD, posix.FD_CLOEXEC);
725 const flags = try posix.fcntl(file.handle, posix.F.GETFD, 0);
726 try expect((flags & posix.FD_CLOEXEC) != 0);
727 }
728}
729
730test "signalfd" {
731 switch (native_os) {
732 .linux, .solaris, .illumos => {},
733 else => return error.SkipZigTest,
734 }
735 _ = &posix.signalfd;
736}
737
738test "sync" {
739 if (native_os != .linux)
740 return error.SkipZigTest;
741
742 var tmp = tmpDir(.{});
743 defer tmp.cleanup();
744
745 const test_out_file = "os_tmp_test";
746 const file = try tmp.dir.createFile(test_out_file, .{});
747 defer {
748 file.close();
749 tmp.dir.deleteFile(test_out_file) catch {};
750 }
751
752 posix.sync();
753 try posix.syncfs(file.handle);
754}
755
756test "fsync" {
757 switch (native_os) {
758 .linux, .windows, .solaris, .illumos => {},
759 else => return error.SkipZigTest,
760 }
761
762 var tmp = tmpDir(.{});
763 defer tmp.cleanup();
764
765 const test_out_file = "os_tmp_test";
766 const file = try tmp.dir.createFile(test_out_file, .{});
767 defer {
768 file.close();
769 tmp.dir.deleteFile(test_out_file) catch {};
770 }
771
772 try posix.fsync(file.handle);
773 try posix.fdatasync(file.handle);
774}
775
776test "getrlimit and setrlimit" {
777 if (!@hasDecl(posix.system, "rlimit")) {
778 return error.SkipZigTest;
779 }
780
781 inline for (std.meta.fields(posix.rlimit_resource)) |field| {
782 const resource = @as(posix.rlimit_resource, @enumFromInt(field.value));
783 const limit = try posix.getrlimit(resource);
784
785 // XNU kernel does not support RLIMIT_STACK if a custom stack is active,
786 // which looks to always be the case. EINVAL is returned.
787 // See https://github.com/apple-oss-distributions/xnu/blob/5e3eaea39dcf651e66cb99ba7d70e32cc4a99587/bsd/kern/kern_resource.c#L1173
788 if (native_os.isDarwin() and resource == .STACK) {
789 continue;
790 }
791
792 // On 32 bit MIPS musl includes a fix which changes limits greater than -1UL/2 to RLIM_INFINITY.
793 // See http://git.musl-libc.org/cgit/musl/commit/src/misc/getrlimit.c?id=8258014fd1e34e942a549c88c7e022a00445c352
794 //
795 // This happens for example if RLIMIT_MEMLOCK is bigger than ~2GiB.
796 // In that case the following the limit would be RLIM_INFINITY and the following setrlimit fails with EPERM.
797 if (comptime builtin.cpu.arch.isMIPS() and builtin.link_libc) {
798 if (limit.cur != linux.RLIM.INFINITY) {
799 try posix.setrlimit(resource, limit);
800 }
801 } else {
802 try posix.setrlimit(resource, limit);
803 }
804 }
805}
806
807test "shutdown socket" {
808 if (native_os == .wasi)
809 return error.SkipZigTest;
810 if (native_os == .windows) {
811 _ = try std.os.windows.WSAStartup(2, 2);
812 }
813 defer {
814 if (native_os == .windows) {
815 std.os.windows.WSACleanup() catch unreachable;
816 }
817 }
818 const sock = try posix.socket(posix.AF.INET, posix.SOCK.STREAM, 0);
819 posix.shutdown(sock, .both) catch |err| switch (err) {
820 error.SocketNotConnected => {},
821 else => |e| return e,
822 };
823 std.net.Stream.close(.{ .handle = sock });
824}
825
826test "sigaction" {
827 if (native_os == .wasi or native_os == .windows)
828 return error.SkipZigTest;
829
830 // https://github.com/ziglang/zig/issues/7427
831 if (native_os == .linux and builtin.target.cpu.arch == .x86)
832 return error.SkipZigTest;
833
834 // https://github.com/ziglang/zig/issues/15381
835 if (native_os == .macos and builtin.target.cpu.arch == .x86_64) {
836 return error.SkipZigTest;
837 }
838
839 const S = struct {
840 var handler_called_count: u32 = 0;
841
842 fn handler(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*const anyopaque) callconv(.C) void {
843 _ = ctx_ptr;
844 // Check that we received the correct signal.
845 switch (native_os) {
846 .netbsd => {
847 if (sig == posix.SIG.USR1 and sig == info.info.signo)
848 handler_called_count += 1;
849 },
850 else => {
851 if (sig == posix.SIG.USR1 and sig == info.signo)
852 handler_called_count += 1;
853 },
854 }
855 }
856 };
857
858 var sa: posix.Sigaction = .{
859 .handler = .{ .sigaction = &S.handler },
860 .mask = posix.empty_sigset,
861 .flags = posix.SA.SIGINFO | posix.SA.RESETHAND,
862 };
863 var old_sa: posix.Sigaction = undefined;
864
865 // Install the new signal handler.
866 try posix.sigaction(posix.SIG.USR1, &sa, null);
867
868 // Check that we can read it back correctly.
869 try posix.sigaction(posix.SIG.USR1, null, &old_sa);
870 try testing.expectEqual(&S.handler, old_sa.handler.sigaction.?);
871 try testing.expect((old_sa.flags & posix.SA.SIGINFO) != 0);
872
873 // Invoke the handler.
874 try posix.raise(posix.SIG.USR1);
875 try testing.expect(S.handler_called_count == 1);
876
877 // Check if passing RESETHAND correctly reset the handler to SIG_DFL
878 try posix.sigaction(posix.SIG.USR1, null, &old_sa);
879 try testing.expectEqual(posix.SIG.DFL, old_sa.handler.handler);
880
881 // Reinstall the signal w/o RESETHAND and re-raise
882 sa.flags = posix.SA.SIGINFO;
883 try posix.sigaction(posix.SIG.USR1, &sa, null);
884 try posix.raise(posix.SIG.USR1);
885 try testing.expect(S.handler_called_count == 2);
886
887 // Now set the signal to ignored
888 sa.handler = .{ .handler = posix.SIG.IGN };
889 sa.flags = 0;
890 try posix.sigaction(posix.SIG.USR1, &sa, null);
891
892 // Re-raise to ensure handler is actually ignored
893 try posix.raise(posix.SIG.USR1);
894 try testing.expect(S.handler_called_count == 2);
895
896 // Ensure that ignored state is returned when querying
897 try posix.sigaction(posix.SIG.USR1, null, &old_sa);
898 try testing.expectEqual(posix.SIG.IGN, old_sa.handler.handler.?);
899}
900
901test "dup & dup2" {
902 switch (native_os) {
903 .linux, .solaris, .illumos => {},
904 else => return error.SkipZigTest,
905 }
906
907 var tmp = tmpDir(.{});
908 defer tmp.cleanup();
909
910 {
911 var file = try tmp.dir.createFile("os_dup_test", .{});
912 defer file.close();
913
914 var duped = std.fs.File{ .handle = try posix.dup(file.handle) };
915 defer duped.close();
916 try duped.writeAll("dup");
917
918 // Tests aren't run in parallel so using the next fd shouldn't be an issue.
919 const new_fd = duped.handle + 1;
920 try posix.dup2(file.handle, new_fd);
921 var dup2ed = std.fs.File{ .handle = new_fd };
922 defer dup2ed.close();
923 try dup2ed.writeAll("dup2");
924 }
925
926 var file = try tmp.dir.openFile("os_dup_test", .{});
927 defer file.close();
928
929 var buf: [7]u8 = undefined;
930 try testing.expectEqualStrings("dupdup2", buf[0..try file.readAll(&buf)]);
931}
932
933test "writev longer than IOV_MAX" {
934 if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;
935
936 var tmp = tmpDir(.{});
937 defer tmp.cleanup();
938
939 var file = try tmp.dir.createFile("pwritev", .{});
940 defer file.close();
941
942 const iovecs = [_]posix.iovec_const{.{ .iov_base = "a", .iov_len = 1 }} ** (posix.IOV_MAX + 1);
943 const amt = try file.writev(&iovecs);
944 try testing.expectEqual(@as(usize, posix.IOV_MAX), amt);
945}
946
947test "POSIX file locking with fcntl" {
948 if (native_os == .windows or native_os == .wasi) {
949 // Not POSIX.
950 return error.SkipZigTest;
951 }
952
953 if (true) {
954 // https://github.com/ziglang/zig/issues/11074
955 return error.SkipZigTest;
956 }
957
958 var tmp = std.testing.tmpDir(.{});
959 defer tmp.cleanup();
960
961 // Create a temporary lock file
962 var file = try tmp.dir.createFile("lock", .{ .read = true });
963 defer file.close();
964 try file.setEndPos(2);
965 const fd = file.handle;
966
967 // Place an exclusive lock on the first byte, and a shared lock on the second byte:
968 var struct_flock = std.mem.zeroInit(posix.Flock, .{ .type = posix.F.WRLCK });
969 _ = try posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock));
970 struct_flock.start = 1;
971 struct_flock.type = posix.F.RDLCK;
972 _ = try posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock));
973
974 // Check the locks in a child process:
975 const pid = try posix.fork();
976 if (pid == 0) {
977 // child expects be denied the exclusive lock:
978 struct_flock.start = 0;
979 struct_flock.type = posix.F.WRLCK;
980 try expectError(error.Locked, posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock)));
981 // child expects to get the shared lock:
982 struct_flock.start = 1;
983 struct_flock.type = posix.F.RDLCK;
984 _ = try posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock));
985 // child waits for the exclusive lock in order to test deadlock:
986 struct_flock.start = 0;
987 struct_flock.type = posix.F.WRLCK;
988 _ = try posix.fcntl(fd, posix.F.SETLKW, @intFromPtr(&struct_flock));
989 // child exits without continuing:
990 posix.exit(0);
991 } else {
992 // parent waits for child to get shared lock:
993 std.time.sleep(1 * std.time.ns_per_ms);
994 // parent expects deadlock when attempting to upgrade the shared lock to exclusive:
995 struct_flock.start = 1;
996 struct_flock.type = posix.F.WRLCK;
997 try expectError(error.DeadLock, posix.fcntl(fd, posix.F.SETLKW, @intFromPtr(&struct_flock)));
998 // parent releases exclusive lock:
999 struct_flock.start = 0;
1000 struct_flock.type = posix.F.UNLCK;
1001 _ = try posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock));
1002 // parent releases shared lock:
1003 struct_flock.start = 1;
1004 struct_flock.type = posix.F.UNLCK;
1005 _ = try posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock));
1006 // parent waits for child:
1007 const result = posix.waitpid(pid, 0);
1008 try expect(result.status == 0 * 256);
1009 }
1010}
1011
1012test "rename smoke test" {
1013 if (native_os == .wasi) return error.SkipZigTest;
1014 if (native_os == .windows) return error.SkipZigTest;
1015
1016 var tmp = tmpDir(.{});
1017 defer tmp.cleanup();
1018
1019 // Get base abs path
1020 var arena = ArenaAllocator.init(testing.allocator);
1021 defer arena.deinit();
1022 const allocator = arena.allocator();
1023
1024 const base_path = blk: {
1025 const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
1026 break :blk try fs.realpathAlloc(allocator, relative_path);
1027 };
1028
1029 var file_path: []u8 = undefined;
1030 var fd: posix.fd_t = undefined;
1031 const mode: posix.mode_t = if (native_os == .windows) 0 else 0o666;
1032
1033 // Create some file using `open`.
1034 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1035 fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode);
1036 posix.close(fd);
1037
1038 // Rename the file
1039 var new_file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_file" });
1040 try posix.rename(file_path, new_file_path);
1041
1042 // Try opening renamed file
1043 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_file" });
1044 fd = try posix.open(file_path, .{ .ACCMODE = .RDWR }, mode);
1045 posix.close(fd);
1046
1047 // Try opening original file - should fail with error.FileNotFound
1048 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1049 try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode));
1050
1051 // Create some directory
1052 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
1053 try posix.mkdir(file_path, mode);
1054
1055 // Rename the directory
1056 new_file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_dir" });
1057 try posix.rename(file_path, new_file_path);
1058
1059 // Try opening renamed directory
1060 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_dir" });
1061 fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode);
1062 posix.close(fd);
1063
1064 // Try opening original directory - should fail with error.FileNotFound
1065 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
1066 try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode));
1067}
1068
1069test "access smoke test" {
1070 if (native_os == .wasi) return error.SkipZigTest;
1071 if (native_os == .windows) return error.SkipZigTest;
1072
1073 var tmp = tmpDir(.{});
1074 defer tmp.cleanup();
1075
1076 // Get base abs path
1077 var arena = ArenaAllocator.init(testing.allocator);
1078 defer arena.deinit();
1079 const allocator = arena.allocator();
1080
1081 const base_path = blk: {
1082 const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
1083 break :blk try fs.realpathAlloc(allocator, relative_path);
1084 };
1085
1086 var file_path: []u8 = undefined;
1087 var fd: posix.fd_t = undefined;
1088 const mode: posix.mode_t = if (native_os == .windows) 0 else 0o666;
1089
1090 // Create some file using `open`.
1091 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1092 fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode);
1093 posix.close(fd);
1094
1095 // Try to access() the file
1096 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1097 if (native_os == .windows) {
1098 try posix.access(file_path, posix.F_OK);
1099 } else {
1100 try posix.access(file_path, posix.F_OK | posix.W_OK | posix.R_OK);
1101 }
1102
1103 // Try to access() a non-existent file - should fail with error.FileNotFound
1104 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_file" });
1105 try expectError(error.FileNotFound, posix.access(file_path, posix.F_OK));
1106
1107 // Create some directory
1108 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
1109 try posix.mkdir(file_path, mode);
1110
1111 // Try to access() the directory
1112 file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" });
1113 try posix.access(file_path, posix.F_OK);
1114}
1115
1116test "timerfd" {
1117 if (native_os != .linux) return error.SkipZigTest;
1118
1119 const tfd = try posix.timerfd_create(linux.CLOCK.MONOTONIC, .{ .CLOEXEC = true });
1120 defer posix.close(tfd);
1121
1122 // Fire event 10_000_000ns = 10ms after the posix.timerfd_settime call.
1123 var sit: linux.itimerspec = .{ .it_interval = .{ .tv_sec = 0, .tv_nsec = 0 }, .it_value = .{ .tv_sec = 0, .tv_nsec = 10 * (1000 * 1000) } };
1124 try posix.timerfd_settime(tfd, .{}, &sit, null);
1125
1126 var fds: [1]posix.pollfd = .{.{ .fd = tfd, .events = linux.POLL.IN, .revents = 0 }};
1127 try expectEqual(@as(usize, 1), try posix.poll(&fds, -1)); // -1 => infinite waiting
1128
1129 const git = try posix.timerfd_gettime(tfd);
1130 const expect_disarmed_timer: linux.itimerspec = .{ .it_interval = .{ .tv_sec = 0, .tv_nsec = 0 }, .it_value = .{ .tv_sec = 0, .tv_nsec = 0 } };
1131 try expectEqual(expect_disarmed_timer, git);
1132}
1133
1134test "isatty" {
1135 var tmp = tmpDir(.{});
1136 defer tmp.cleanup();
1137
1138 var file = try tmp.dir.createFile("foo", .{});
1139 defer file.close();
1140
1141 try expectEqual(posix.isatty(file.handle), false);
1142}
1143
1144test "read with empty buffer" {
1145 if (native_os == .wasi) return error.SkipZigTest;
1146
1147 var tmp = tmpDir(.{});
1148 defer tmp.cleanup();
1149
1150 var arena = ArenaAllocator.init(testing.allocator);
1151 defer arena.deinit();
1152 const allocator = arena.allocator();
1153
1154 // Get base abs path
1155 const base_path = blk: {
1156 const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
1157 break :blk try fs.realpathAlloc(allocator, relative_path);
1158 };
1159
1160 const file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1161 var file = try fs.cwd().createFile(file_path, .{ .read = true });
1162 defer file.close();
1163
1164 const bytes = try allocator.alloc(u8, 0);
1165
1166 _ = try posix.read(file.handle, bytes);
1167}
1168
1169test "pread with empty buffer" {
1170 if (native_os == .wasi) return error.SkipZigTest;
1171
1172 var tmp = tmpDir(.{});
1173 defer tmp.cleanup();
1174
1175 var arena = ArenaAllocator.init(testing.allocator);
1176 defer arena.deinit();
1177 const allocator = arena.allocator();
1178
1179 // Get base abs path
1180 const base_path = blk: {
1181 const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
1182 break :blk try fs.realpathAlloc(allocator, relative_path);
1183 };
1184
1185 const file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1186 var file = try fs.cwd().createFile(file_path, .{ .read = true });
1187 defer file.close();
1188
1189 const bytes = try allocator.alloc(u8, 0);
1190
1191 _ = try posix.pread(file.handle, bytes, 0);
1192}
1193
1194test "write with empty buffer" {
1195 if (native_os == .wasi) return error.SkipZigTest;
1196
1197 var tmp = tmpDir(.{});
1198 defer tmp.cleanup();
1199
1200 var arena = ArenaAllocator.init(testing.allocator);
1201 defer arena.deinit();
1202 const allocator = arena.allocator();
1203
1204 // Get base abs path
1205 const base_path = blk: {
1206 const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
1207 break :blk try fs.realpathAlloc(allocator, relative_path);
1208 };
1209
1210 const file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1211 var file = try fs.cwd().createFile(file_path, .{});
1212 defer file.close();
1213
1214 const bytes = try allocator.alloc(u8, 0);
1215
1216 _ = try posix.write(file.handle, bytes);
1217}
1218
1219test "pwrite with empty buffer" {
1220 if (native_os == .wasi) return error.SkipZigTest;
1221
1222 var tmp = tmpDir(.{});
1223 defer tmp.cleanup();
1224
1225 var arena = ArenaAllocator.init(testing.allocator);
1226 defer arena.deinit();
1227 const allocator = arena.allocator();
1228
1229 // Get base abs path
1230 const base_path = blk: {
1231 const relative_path = try fs.path.join(allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
1232 break :blk try fs.realpathAlloc(allocator, relative_path);
1233 };
1234
1235 const file_path: []u8 = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" });
1236 var file = try fs.cwd().createFile(file_path, .{});
1237 defer file.close();
1238
1239 const bytes = try allocator.alloc(u8, 0);
1240
1241 _ = try posix.pwrite(file.handle, bytes, 0);
1242}
1243
1244fn expectMode(dir: posix.fd_t, file: []const u8, mode: posix.mode_t) !void {
1245 const st = try posix.fstatat(dir, file, posix.AT.SYMLINK_NOFOLLOW);
1246 try expectEqual(mode, st.mode & 0b111_111_111);
1247}
1248
1249test "fchmodat smoke test" {
1250 if (!std.fs.has_executable_bit) return error.SkipZigTest;
1251
1252 var tmp = tmpDir(.{});
1253 defer tmp.cleanup();
1254
1255 try expectError(error.FileNotFound, posix.fchmodat(tmp.dir.fd, "regfile", 0o666, 0));
1256 const fd = try posix.openat(
1257 tmp.dir.fd,
1258 "regfile",
1259 .{ .ACCMODE = .WRONLY, .CREAT = true, .EXCL = true, .TRUNC = true },
1260 0o644,
1261 );
1262 posix.close(fd);
1263 try posix.symlinkat("regfile", tmp.dir.fd, "symlink");
1264 const sym_mode = blk: {
1265 const st = try posix.fstatat(tmp.dir.fd, "symlink", posix.AT.SYMLINK_NOFOLLOW);
1266 break :blk st.mode & 0b111_111_111;
1267 };
1268
1269 try posix.fchmodat(tmp.dir.fd, "regfile", 0o640, 0);
1270 try expectMode(tmp.dir.fd, "regfile", 0o640);
1271 try posix.fchmodat(tmp.dir.fd, "regfile", 0o600, posix.AT.SYMLINK_NOFOLLOW);
1272 try expectMode(tmp.dir.fd, "regfile", 0o600);
1273
1274 try posix.fchmodat(tmp.dir.fd, "symlink", 0o640, 0);
1275 try expectMode(tmp.dir.fd, "regfile", 0o640);
1276 try expectMode(tmp.dir.fd, "symlink", sym_mode);
1277
1278 var test_link = true;
1279 posix.fchmodat(tmp.dir.fd, "symlink", 0o600, posix.AT.SYMLINK_NOFOLLOW) catch |err| switch (err) {
1280 error.OperationNotSupported => test_link = false,
1281 else => |e| return e,
1282 };
1283 if (test_link)
1284 try expectMode(tmp.dir.fd, "symlink", 0o600);
1285 try expectMode(tmp.dir.fd, "regfile", 0o640);
1286}
1287
1288const CommonOpenFlags = packed struct {
1289 ACCMODE: posix.ACCMODE = .RDONLY,
1290 CREAT: bool = false,
1291 EXCL: bool = false,
1292 LARGEFILE: bool = false,
1293 DIRECTORY: bool = false,
1294 CLOEXEC: bool = false,
1295 NONBLOCK: bool = false,
1296
1297 pub fn lower(cof: CommonOpenFlags) posix.O {
1298 if (native_os == .wasi) return .{
1299 .read = cof.ACCMODE != .WRONLY,
1300 .write = cof.ACCMODE != .RDONLY,
1301 .CREAT = cof.CREAT,
1302 .EXCL = cof.EXCL,
1303 .DIRECTORY = cof.DIRECTORY,
1304 .NONBLOCK = cof.NONBLOCK,
1305 };
1306 var result: posix.O = .{
1307 .ACCMODE = cof.ACCMODE,
1308 .CREAT = cof.CREAT,
1309 .EXCL = cof.EXCL,
1310 .DIRECTORY = cof.DIRECTORY,
1311 .NONBLOCK = cof.NONBLOCK,
1312 .CLOEXEC = cof.CLOEXEC,
1313 };
1314 if (@hasField(posix.O, "LARGEFILE")) result.LARGEFILE = cof.LARGEFILE;
1315 return result;
1316 }
1317};
lib/std/process.zig+123-74
......@@ -1,6 +1,5 @@
11const std = @import("std.zig");
22const builtin = @import("builtin");
3const os = std.os;
43const fs = std.fs;
54const mem = std.mem;
65const math = std.math;
......@@ -8,20 +7,27 @@ const Allocator = mem.Allocator;
87const assert = std.debug.assert;
98const testing = std.testing;
109const child_process = @import("child_process.zig");
10const native_os = builtin.os.tag;
11const posix = std.posix;
12const windows = std.os.windows;
1113
1214pub const Child = child_process.ChildProcess;
13pub const abort = os.abort;
14pub const exit = os.exit;
15pub const changeCurDir = os.chdir;
16pub const changeCurDirC = os.chdirC;
15pub const abort = posix.abort;
16pub const exit = posix.exit;
17pub const changeCurDir = posix.chdir;
18pub const changeCurDirC = posix.chdirC;
19
20pub const GetCwdError = posix.GetCwdError;
1721
1822/// The result is a slice of `out_buffer`, from index `0`.
1923/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2024/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
2125pub fn getCwd(out_buffer: []u8) ![]u8 {
22 return os.getcwd(out_buffer);
26 return posix.getcwd(out_buffer);
2327}
2428
29pub const GetCwdAllocError = Allocator.Error || posix.GetCwdError;
30
2531/// Caller must free the returned memory.
2632/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
2733/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
......@@ -34,7 +40,7 @@ pub fn getCwdAlloc(allocator: Allocator) ![]u8 {
3440
3541 var current_buf: []u8 = &stack_buf;
3642 while (true) {
37 if (os.getcwd(current_buf)) |slice| {
43 if (posix.getcwd(current_buf)) |slice| {
3844 return allocator.dupe(u8, slice);
3945 } else |err| switch (err) {
4046 error.NameTooLong => {
......@@ -51,7 +57,7 @@ pub fn getCwdAlloc(allocator: Allocator) ![]u8 {
5157}
5258
5359test getCwdAlloc {
54 if (builtin.os.tag == .wasi) return error.SkipZigTest;
60 if (native_os == .wasi) return error.SkipZigTest;
5561
5662 const cwd = try getCwdAlloc(testing.allocator);
5763 testing.allocator.free(cwd);
......@@ -72,13 +78,13 @@ pub const EnvMap = struct {
7278 pub const EnvNameHashContext = struct {
7379 fn upcase(c: u21) u21 {
7480 if (c <= std.math.maxInt(u16))
75 return std.os.windows.ntdll.RtlUpcaseUnicodeChar(@as(u16, @intCast(c)));
81 return windows.ntdll.RtlUpcaseUnicodeChar(@as(u16, @intCast(c)));
7682 return c;
7783 }
7884
7985 pub fn hash(self: @This(), s: []const u8) u64 {
8086 _ = self;
81 if (builtin.os.tag == .windows) {
87 if (native_os == .windows) {
8288 var h = std.hash.Wyhash.init(0);
8389 var it = std.unicode.Wtf8View.initUnchecked(s).iterator();
8490 while (it.nextCodepoint()) |cp| {
......@@ -96,7 +102,7 @@ pub const EnvMap = struct {
96102
97103 pub fn eql(self: @This(), a: []const u8, b: []const u8) bool {
98104 _ = self;
99 if (builtin.os.tag == .windows) {
105 if (native_os == .windows) {
100106 var it_a = std.unicode.Wtf8View.initUnchecked(a).iterator();
101107 var it_b = std.unicode.Wtf8View.initUnchecked(b).iterator();
102108 while (true) {
......@@ -228,7 +234,7 @@ test "EnvMap" {
228234 try testing.expectEqual(@as(EnvMap.Size, 2), env.count());
229235
230236 // case insensitivity on Windows only
231 if (builtin.os.tag == .windows) {
237 if (native_os == .windows) {
232238 try testing.expectEqualStrings("1", env.get("something_New_aNd_LONGER").?);
233239 } else {
234240 try testing.expect(null == env.get("something_New_aNd_LONGER"));
......@@ -248,7 +254,7 @@ test "EnvMap" {
248254
249255 try testing.expectEqual(@as(EnvMap.Size, 1), env.count());
250256
251 if (builtin.os.tag == .windows) {
257 if (native_os == .windows) {
252258 // test Unicode case-insensitivity on Windows
253259 try env.put("КИРиллИЦА", "something else");
254260 try testing.expectEqualStrings("something else", env.get("кириллица").?);
......@@ -279,8 +285,8 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap {
279285 var result = EnvMap.init(allocator);
280286 errdefer result.deinit();
281287
282 if (builtin.os.tag == .windows) {
283 const ptr = os.windows.peb().ProcessParameters.Environment;
288 if (native_os == .windows) {
289 const ptr = windows.peb().ProcessParameters.Environment;
284290
285291 var i: usize = 0;
286292 while (ptr[i] != 0) {
......@@ -310,13 +316,13 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap {
310316 try result.putMove(key, value);
311317 }
312318 return result;
313 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
319 } else if (native_os == .wasi and !builtin.link_libc) {
314320 var environ_count: usize = undefined;
315321 var environ_buf_size: usize = undefined;
316322
317 const environ_sizes_get_ret = os.wasi.environ_sizes_get(&environ_count, &environ_buf_size);
323 const environ_sizes_get_ret = std.os.wasi.environ_sizes_get(&environ_count, &environ_buf_size);
318324 if (environ_sizes_get_ret != .SUCCESS) {
319 return os.unexpectedErrno(environ_sizes_get_ret);
325 return posix.unexpectedErrno(environ_sizes_get_ret);
320326 }
321327
322328 if (environ_count == 0) {
......@@ -328,9 +334,9 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap {
328334 const environ_buf = try allocator.alloc(u8, environ_buf_size);
329335 defer allocator.free(environ_buf);
330336
331 const environ_get_ret = os.wasi.environ_get(environ.ptr, environ_buf.ptr);
337 const environ_get_ret = std.os.wasi.environ_get(environ.ptr, environ_buf.ptr);
332338 if (environ_get_ret != .SUCCESS) {
333 return os.unexpectedErrno(environ_get_ret);
339 return posix.unexpectedErrno(environ_get_ret);
334340 }
335341
336342 for (environ) |env| {
......@@ -356,7 +362,7 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap {
356362 }
357363 return result;
358364 } else {
359 for (os.environ) |line| {
365 for (std.os.environ) |line| {
360366 var line_i: usize = 0;
361367 while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
362368 const key = line[0..line_i];
......@@ -391,37 +397,37 @@ pub const GetEnvVarOwnedError = error{
391397/// On Windows, the value is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
392398/// On other platforms, the value is an opaque sequence of bytes with no particular encoding.
393399pub fn getEnvVarOwned(allocator: Allocator, key: []const u8) GetEnvVarOwnedError![]u8 {
394 if (builtin.os.tag == .windows) {
400 if (native_os == .windows) {
395401 const result_w = blk: {
396402 var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator);
397403 const stack_allocator = stack_alloc.get();
398404 const key_w = try std.unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key);
399405 defer stack_allocator.free(key_w);
400406
401 break :blk std.os.getenvW(key_w) orelse return error.EnvironmentVariableNotFound;
407 break :blk getenvW(key_w) orelse return error.EnvironmentVariableNotFound;
402408 };
403409 // wtf16LeToWtf8Alloc can only fail with OutOfMemory
404410 return std.unicode.wtf16LeToWtf8Alloc(allocator, result_w);
405 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
411 } else if (native_os == .wasi and !builtin.link_libc) {
406412 var envmap = getEnvMap(allocator) catch return error.OutOfMemory;
407413 defer envmap.deinit();
408414 const val = envmap.get(key) orelse return error.EnvironmentVariableNotFound;
409415 return allocator.dupe(u8, val);
410416 } else {
411 const result = os.getenv(key) orelse return error.EnvironmentVariableNotFound;
417 const result = posix.getenv(key) orelse return error.EnvironmentVariableNotFound;
412418 return allocator.dupe(u8, result);
413419 }
414420}
415421
416422/// On Windows, `key` must be valid UTF-8.
417423pub fn hasEnvVarConstant(comptime key: []const u8) bool {
418 if (builtin.os.tag == .windows) {
424 if (native_os == .windows) {
419425 const key_w = comptime std.unicode.utf8ToUtf16LeStringLiteral(key);
420 return std.os.getenvW(key_w) != null;
421 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
426 return getenvW(key_w) != null;
427 } else if (native_os == .wasi and !builtin.link_libc) {
422428 @compileError("hasEnvVarConstant is not supported for WASI without libc");
423429 } else {
424 return os.getenv(key) != null;
430 return posix.getenv(key) != null;
425431 }
426432}
427433
......@@ -436,19 +442,63 @@ pub const HasEnvVarError = error{
436442/// On Windows, if `key` is not valid [WTF-8](https://simonsapin.github.io/wtf-8/),
437443/// then `error.InvalidWtf8` is returned.
438444pub fn hasEnvVar(allocator: Allocator, key: []const u8) HasEnvVarError!bool {
439 if (builtin.os.tag == .windows) {
445 if (native_os == .windows) {
440446 var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator);
441447 const stack_allocator = stack_alloc.get();
442448 const key_w = try std.unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key);
443449 defer stack_allocator.free(key_w);
444 return std.os.getenvW(key_w) != null;
445 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
450 return getenvW(key_w) != null;
451 } else if (native_os == .wasi and !builtin.link_libc) {
446452 var envmap = getEnvMap(allocator) catch return error.OutOfMemory;
447453 defer envmap.deinit();
448454 return envmap.getPtr(key) != null;
449455 } else {
450 return os.getenv(key) != null;
456 return posix.getenv(key) != null;
457 }
458}
459
460/// Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name.
461///
462/// This function performs a Unicode-aware case-insensitive lookup using RtlEqualUnicodeString.
463///
464/// See also:
465/// * `std.posix.getenv`
466/// * `getEnvMap`
467/// * `getEnvVarOwned`
468/// * `hasEnvVarConstant`
469/// * `hasEnvVar`
470pub fn getenvW(key: [*:0]const u16) ?[:0]const u16 {
471 if (native_os != .windows) {
472 @compileError("Windows-only");
473 }
474 const key_slice = mem.sliceTo(key, 0);
475 const ptr = windows.peb().ProcessParameters.Environment;
476 var i: usize = 0;
477 while (ptr[i] != 0) {
478 const key_start = i;
479
480 // There are some special environment variables that start with =,
481 // so we need a special case to not treat = as a key/value separator
482 // if it's the first character.
483 // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
484 if (ptr[key_start] == '=') i += 1;
485
486 while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {}
487 const this_key = ptr[key_start..i];
488
489 if (ptr[i] == '=') i += 1;
490
491 const value_start = i;
492 while (ptr[i] != 0) : (i += 1) {}
493 const this_value = ptr[value_start..i :0];
494
495 if (windows.eqlIgnoreCaseWTF16(key_slice, this_key)) {
496 return this_value;
497 }
498
499 i += 1; // skip over null byte
451500 }
501 return null;
452502}
453503
454504test getEnvVarOwned {
......@@ -459,7 +509,7 @@ test getEnvVarOwned {
459509}
460510
461511test hasEnvVarConstant {
462 if (builtin.os.tag == .wasi and !builtin.link_libc) return error.SkipZigTest;
512 if (native_os == .wasi and !builtin.link_libc) return error.SkipZigTest;
463513
464514 try testing.expect(!hasEnvVarConstant("BADENV"));
465515}
......@@ -478,14 +528,14 @@ pub const ArgIteratorPosix = struct {
478528 pub fn init() ArgIteratorPosix {
479529 return ArgIteratorPosix{
480530 .index = 0,
481 .count = os.argv.len,
531 .count = std.os.argv.len,
482532 };
483533 }
484534
485535 pub fn next(self: *ArgIteratorPosix) ?[:0]const u8 {
486536 if (self.index == self.count) return null;
487537
488 const s = os.argv[self.index];
538 const s = std.os.argv[self.index];
489539 self.index += 1;
490540 return mem.sliceTo(s, 0);
491541 }
......@@ -503,7 +553,7 @@ pub const ArgIteratorWasi = struct {
503553 index: usize,
504554 args: [][:0]u8,
505555
506 pub const InitError = error{OutOfMemory} || os.UnexpectedError;
556 pub const InitError = error{OutOfMemory} || posix.UnexpectedError;
507557
508558 /// You must call deinit to free the internal buffer of the
509559 /// iterator after you are done.
......@@ -517,13 +567,12 @@ pub const ArgIteratorWasi = struct {
517567 }
518568
519569 fn internalInit(allocator: Allocator) InitError![][:0]u8 {
520 const w = os.wasi;
521570 var count: usize = undefined;
522571 var buf_size: usize = undefined;
523572
524 switch (w.args_sizes_get(&count, &buf_size)) {
573 switch (std.os.wasi.args_sizes_get(&count, &buf_size)) {
525574 .SUCCESS => {},
526 else => |err| return os.unexpectedErrno(err),
575 else => |err| return posix.unexpectedErrno(err),
527576 }
528577
529578 if (count == 0) {
......@@ -535,9 +584,9 @@ pub const ArgIteratorWasi = struct {
535584
536585 const argv_buf = try allocator.alloc(u8, buf_size);
537586
538 switch (w.args_get(argv.ptr, argv_buf.ptr)) {
587 switch (std.os.wasi.args_get(argv.ptr, argv_buf.ptr)) {
539588 .SUCCESS => {},
540 else => |err| return os.unexpectedErrno(err),
589 else => |err| return posix.unexpectedErrno(err),
541590 }
542591
543592 var result_args = try allocator.alloc([:0]u8, count);
......@@ -1007,7 +1056,7 @@ pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type {
10071056
10081057/// Cross-platform command line argument iterator.
10091058pub const ArgIterator = struct {
1010 const InnerType = switch (builtin.os.tag) {
1059 const InnerType = switch (native_os) {
10111060 .windows => ArgIteratorWindows,
10121061 .wasi => if (builtin.link_libc) ArgIteratorPosix else ArgIteratorWasi,
10131062 else => ArgIteratorPosix,
......@@ -1018,10 +1067,10 @@ pub const ArgIterator = struct {
10181067 /// Initialize the args iterator. Consider using initWithAllocator() instead
10191068 /// for cross-platform compatibility.
10201069 pub fn init() ArgIterator {
1021 if (builtin.os.tag == .wasi) {
1070 if (native_os == .wasi) {
10221071 @compileError("In WASI, use initWithAllocator instead.");
10231072 }
1024 if (builtin.os.tag == .windows) {
1073 if (native_os == .windows) {
10251074 @compileError("In Windows, use initWithAllocator instead.");
10261075 }
10271076
......@@ -1032,11 +1081,11 @@ pub const ArgIterator = struct {
10321081
10331082 /// You must deinitialize iterator's internal buffers by calling `deinit` when done.
10341083 pub fn initWithAllocator(allocator: Allocator) InitError!ArgIterator {
1035 if (builtin.os.tag == .wasi and !builtin.link_libc) {
1084 if (native_os == .wasi and !builtin.link_libc) {
10361085 return ArgIterator{ .inner = try InnerType.init(allocator) };
10371086 }
1038 if (builtin.os.tag == .windows) {
1039 const cmd_line_w = os.windows.kernel32.GetCommandLineW();
1087 if (native_os == .windows) {
1088 const cmd_line_w = windows.kernel32.GetCommandLineW();
10401089 return ArgIterator{ .inner = try InnerType.init(allocator, cmd_line_w) };
10411090 }
10421091
......@@ -1061,11 +1110,11 @@ pub const ArgIterator = struct {
10611110 /// was created with `initWithAllocator` function.
10621111 pub fn deinit(self: *ArgIterator) void {
10631112 // Unless we're targeting WASI or Windows, this is a no-op.
1064 if (builtin.os.tag == .wasi and !builtin.link_libc) {
1113 if (native_os == .wasi and !builtin.link_libc) {
10651114 self.inner.deinit();
10661115 }
10671116
1068 if (builtin.os.tag == .windows) {
1117 if (native_os == .windows) {
10691118 self.inner.deinit();
10701119 }
10711120 }
......@@ -1334,13 +1383,13 @@ fn testResponseFileCmdLine(input_cmd_line: []const u8, expected_args: []const []
13341383}
13351384
13361385pub const UserInfo = struct {
1337 uid: os.uid_t,
1338 gid: os.gid_t,
1386 uid: posix.uid_t,
1387 gid: posix.gid_t,
13391388};
13401389
13411390/// POSIX function which gets a uid from username.
13421391pub fn getUserInfo(name: []const u8) !UserInfo {
1343 return switch (builtin.os.tag) {
1392 return switch (native_os) {
13441393 .linux,
13451394 .macos,
13461395 .watchos,
......@@ -1376,8 +1425,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
13761425 var buf: [std.mem.page_size]u8 = undefined;
13771426 var name_index: usize = 0;
13781427 var state = State.Start;
1379 var uid: os.uid_t = 0;
1380 var gid: os.gid_t = 0;
1428 var uid: posix.uid_t = 0;
1429 var gid: posix.gid_t = 0;
13811430
13821431 while (true) {
13831432 const amt_read = try reader.read(buf[0..]);
......@@ -1462,36 +1511,36 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
14621511}
14631512
14641513pub fn getBaseAddress() usize {
1465 switch (builtin.os.tag) {
1514 switch (native_os) {
14661515 .linux => {
1467 const base = os.system.getauxval(std.elf.AT_BASE);
1516 const base = std.os.linux.getauxval(std.elf.AT_BASE);
14681517 if (base != 0) {
14691518 return base;
14701519 }
1471 const phdr = os.system.getauxval(std.elf.AT_PHDR);
1520 const phdr = std.os.linux.getauxval(std.elf.AT_PHDR);
14721521 return phdr - @sizeOf(std.elf.Ehdr);
14731522 },
14741523 .macos, .freebsd, .netbsd => {
14751524 return @intFromPtr(&std.c._mh_execute_header);
14761525 },
1477 .windows => return @intFromPtr(os.windows.kernel32.GetModuleHandleW(null)),
1526 .windows => return @intFromPtr(windows.kernel32.GetModuleHandleW(null)),
14781527 else => @compileError("Unsupported OS"),
14791528 }
14801529}
14811530
14821531/// Tells whether calling the `execv` or `execve` functions will be a compile error.
1483pub const can_execv = switch (builtin.os.tag) {
1532pub const can_execv = switch (native_os) {
14841533 .windows, .haiku, .wasi => false,
14851534 else => true,
14861535};
14871536
14881537/// Tells whether spawning child processes is supported (e.g. via ChildProcess)
1489pub const can_spawn = switch (builtin.os.tag) {
1538pub const can_spawn = switch (native_os) {
14901539 .wasi, .watchos, .tvos => false,
14911540 else => true,
14921541};
14931542
1494pub const ExecvError = std.os.ExecveError || error{OutOfMemory};
1543pub const ExecvError = std.posix.ExecveError || error{OutOfMemory};
14951544
14961545/// Replaces the current process image with the executed process.
14971546/// This function must allocate memory to add a null terminating bytes on path and each arg.
......@@ -1500,7 +1549,7 @@ pub const ExecvError = std.os.ExecveError || error{OutOfMemory};
15001549/// `argv[0]` is the executable path.
15011550/// This function also uses the PATH environment variable to get the full path to the executable.
15021551/// Due to the heap-allocation, it is illegal to call this function in a fork() child.
1503/// For that use case, use the `std.os` functions directly.
1552/// For that use case, use the `std.posix` functions directly.
15041553pub fn execv(allocator: Allocator, argv: []const []const u8) ExecvError {
15051554 return execve(allocator, argv, null);
15061555}
......@@ -1512,7 +1561,7 @@ pub fn execv(allocator: Allocator, argv: []const []const u8) ExecvError {
15121561/// `argv[0]` is the executable path.
15131562/// This function also uses the PATH environment variable to get the full path to the executable.
15141563/// Due to the heap-allocation, it is illegal to call this function in a fork() child.
1515/// For that use case, use the `std.os` functions directly.
1564/// For that use case, use the `std.posix` functions directly.
15161565pub fn execve(
15171566 allocator: Allocator,
15181567 argv: []const []const u8,
......@@ -1536,14 +1585,14 @@ pub fn execve(
15361585 } else if (builtin.output_mode == .Exe) {
15371586 // Then we have Zig start code and this works.
15381587 // TODO type-safety for null-termination of `os.environ`.
1539 break :m @as([*:null]const ?[*:0]const u8, @ptrCast(os.environ.ptr));
1588 break :m @as([*:null]const ?[*:0]const u8, @ptrCast(std.os.environ.ptr));
15401589 } else {
15411590 // TODO come up with a solution for this.
15421591 @compileError("missing std lib enhancement: std.process.execv implementation has no way to collect the environment variables to forward to the child process");
15431592 }
15441593 };
15451594
1546 return os.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp);
1595 return posix.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp);
15471596}
15481597
15491598pub const TotalSystemMemoryError = error{
......@@ -1555,14 +1604,14 @@ pub const TotalSystemMemoryError = error{
15551604/// and Linux's /proc/meminfo reporting more memory when
15561605/// using QEMU user mode emulation.
15571606pub fn totalSystemMemory() TotalSystemMemoryError!u64 {
1558 switch (builtin.os.tag) {
1607 switch (native_os) {
15591608 .linux => {
15601609 return totalSystemMemoryLinux() catch return error.UnknownTotalSystemMemory;
15611610 },
15621611 .freebsd => {
15631612 var physmem: c_ulong = undefined;
15641613 var len: usize = @sizeOf(c_ulong);
1565 os.sysctlbynameZ("hw.physmem", &physmem, &len, null, 0) catch |err| switch (err) {
1614 posix.sysctlbynameZ("hw.physmem", &physmem, &len, null, 0) catch |err| switch (err) {
15661615 error.NameTooLong, error.UnknownName => unreachable,
15671616 else => return error.UnknownTotalSystemMemory,
15681617 };
......@@ -1570,12 +1619,12 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 {
15701619 },
15711620 .openbsd => {
15721621 const mib: [2]c_int = [_]c_int{
1573 std.os.CTL.HW,
1574 std.os.HW.PHYSMEM64,
1622 posix.CTL.HW,
1623 posix.HW.PHYSMEM64,
15751624 };
15761625 var physmem: i64 = undefined;
15771626 var len: usize = @sizeOf(@TypeOf(physmem));
1578 std.os.sysctl(&mib, &physmem, &len, null, 0) catch |err| switch (err) {
1627 posix.sysctl(&mib, &physmem, &len, null, 0) catch |err| switch (err) {
15791628 error.NameTooLong => unreachable, // constant, known good value
15801629 error.PermissionDenied => unreachable, // only when setting values,
15811630 error.SystemResources => unreachable, // memory already on the stack
......@@ -1586,11 +1635,11 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 {
15861635 return @as(u64, @bitCast(physmem));
15871636 },
15881637 .windows => {
1589 var sbi: std.os.windows.SYSTEM_BASIC_INFORMATION = undefined;
1590 const rc = std.os.windows.ntdll.NtQuerySystemInformation(
1638 var sbi: windows.SYSTEM_BASIC_INFORMATION = undefined;
1639 const rc = windows.ntdll.NtQuerySystemInformation(
15911640 .SystemBasicInformation,
15921641 &sbi,
1593 @sizeOf(std.os.windows.SYSTEM_BASIC_INFORMATION),
1642 @sizeOf(windows.SYSTEM_BASIC_INFORMATION),
15941643 null,
15951644 );
15961645 if (rc != .SUCCESS) {
lib/std/start.zig+40-5
......@@ -407,7 +407,7 @@ fn posixCallMainAndExit() callconv(.C) noreturn {
407407 // FIXME: Make __aeabi_read_tp call the kernel helper kuser_get_tls
408408 // For the time being use a simple abort instead of a @panic call to
409409 // keep the binary bloat under control.
410 std.os.abort();
410 std.posix.abort();
411411 }
412412 }
413413
......@@ -422,7 +422,7 @@ fn posixCallMainAndExit() callconv(.C) noreturn {
422422 expandStackSize(phdrs);
423423 }
424424
425 std.os.exit(callMainWithArgs(argc, argv, envp));
425 std.posix.exit(callMainWithArgs(argc, argv, envp));
426426}
427427
428428fn expandStackSize(phdrs: []elf.Phdr) void {
......@@ -432,13 +432,13 @@ fn expandStackSize(phdrs: []elf.Phdr) void {
432432 assert(phdr.p_memsz % std.mem.page_size == 0);
433433
434434 // Silently fail if we are unable to get limits.
435 const limits = std.os.getrlimit(.STACK) catch break;
435 const limits = std.posix.getrlimit(.STACK) catch break;
436436
437437 // Clamp to limits.max .
438438 const wanted_stack_size = @min(phdr.p_memsz, limits.max);
439439
440440 if (wanted_stack_size > limits.cur) {
441 std.os.setrlimit(.STACK, .{
441 std.posix.setrlimit(.STACK, .{
442442 .cur = wanted_stack_size,
443443 .max = limits.max,
444444 }) catch {
......@@ -464,7 +464,7 @@ inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 {
464464 std.os.environ = envp;
465465
466466 std.debug.maybeEnableSegfaultHandler();
467 std.os.maybeIgnoreSigpipe();
467 maybeIgnoreSigpipe();
468468
469469 return callMain();
470470}
......@@ -563,3 +563,38 @@ pub fn call_wWinMain() std.os.windows.INT {
563563 // second parameter hPrevInstance, MSDN: "This parameter is always NULL"
564564 return root.wWinMain(hInstance, null, lpCmdLine, nCmdShow);
565565}
566
567fn maybeIgnoreSigpipe() void {
568 const have_sigpipe_support = switch (builtin.os.tag) {
569 .linux,
570 .plan9,
571 .solaris,
572 .netbsd,
573 .openbsd,
574 .haiku,
575 .macos,
576 .ios,
577 .watchos,
578 .tvos,
579 .dragonfly,
580 .freebsd,
581 => true,
582
583 else => false,
584 };
585
586 if (have_sigpipe_support and !std.options.keep_sigpipe) {
587 const posix = std.posix;
588 const act: posix.Sigaction = .{
589 // Set handler to a noop function instead of `SIG.IGN` to prevent
590 // leaking signal disposition to a child process.
591 .handler = .{ .handler = noopSigHandler },
592 .mask = posix.empty_sigset,
593 .flags = 0,
594 };
595 posix.sigaction(posix.SIG.PIPE, &act, null) catch |err|
596 std.debug.panic("failed to set noop SIGPIPE handler: {s}", .{@errorName(err)});
597 }
598}
599
600fn noopSigHandler(_: c_int) callconv(.C) void {}
lib/std/std.zig+5-2
......@@ -85,12 +85,11 @@ pub const math = @import("math.zig");
8585pub const mem = @import("mem.zig");
8686pub const meta = @import("meta.zig");
8787pub const net = @import("net.zig");
88pub const posix = @import("os.zig");
89/// Non-portable Operating System-specific API.
9088pub const os = @import("os.zig");
9189pub const once = @import("once.zig").once;
9290pub const packed_int_array = @import("packed_int_array.zig");
9391pub const pdb = @import("pdb.zig");
92pub const posix = @import("posix.zig");
9493pub const process = @import("process.zig");
9594/// Deprecated: use `Random` instead.
9695pub const rand = Random;
......@@ -170,3 +169,7 @@ comptime {
170169test {
171170 testing.refAllDecls(@This());
172171}
172
173comptime {
174 debug.assert(@import("std") == @This()); // std lib tests require --zig-lib-dir
175}
lib/std/time.zig+28-27
......@@ -2,8 +2,9 @@ const std = @import("std.zig");
22const builtin = @import("builtin");
33const assert = std.debug.assert;
44const testing = std.testing;
5const os = std.os;
65const math = std.math;
6const windows = std.os.windows;
7const posix = std.posix;
78
89pub const epoch = @import("time/epoch.zig");
910
......@@ -11,8 +12,8 @@ pub const epoch = @import("time/epoch.zig");
1112pub fn sleep(nanoseconds: u64) void {
1213 if (builtin.os.tag == .windows) {
1314 const big_ms_from_ns = nanoseconds / ns_per_ms;
14 const ms = math.cast(os.windows.DWORD, big_ms_from_ns) orelse math.maxInt(os.windows.DWORD);
15 os.windows.kernel32.Sleep(ms);
15 const ms = math.cast(windows.DWORD, big_ms_from_ns) orelse math.maxInt(windows.DWORD);
16 windows.kernel32.Sleep(ms);
1617 return;
1718 }
1819
......@@ -40,7 +41,7 @@ pub fn sleep(nanoseconds: u64) void {
4041 }
4142
4243 if (builtin.os.tag == .uefi) {
43 const boot_services = os.uefi.system_table.boot_services.?;
44 const boot_services = std.os.uefi.system_table.boot_services.?;
4445 const us_from_ns = nanoseconds / ns_per_us;
4546 const us = math.cast(usize, us_from_ns) orelse math.maxInt(usize);
4647 _ = boot_services.stall(us);
......@@ -49,7 +50,7 @@ pub fn sleep(nanoseconds: u64) void {
4950
5051 const s = nanoseconds / ns_per_s;
5152 const ns = nanoseconds % ns_per_s;
52 std.os.nanosleep(s, ns);
53 posix.nanosleep(s, ns);
5354}
5455
5556test "sleep" {
......@@ -60,7 +61,7 @@ test "sleep" {
6061/// Precision of timing depends on the hardware and operating system.
6162/// The return value is signed because it is possible to have a date that is
6263/// before the epoch.
63/// See `std.os.clock_gettime` for a POSIX timestamp.
64/// See `posix.clock_gettime` for a POSIX timestamp.
6465pub fn timestamp() i64 {
6566 return @divFloor(milliTimestamp(), ms_per_s);
6667}
......@@ -69,7 +70,7 @@ pub fn timestamp() i64 {
6970/// Precision of timing depends on the hardware and operating system.
7071/// The return value is signed because it is possible to have a date that is
7172/// before the epoch.
72/// See `std.os.clock_gettime` for a POSIX timestamp.
73/// See `posix.clock_gettime` for a POSIX timestamp.
7374pub fn milliTimestamp() i64 {
7475 return @as(i64, @intCast(@divFloor(nanoTimestamp(), ns_per_ms)));
7576}
......@@ -78,7 +79,7 @@ pub fn milliTimestamp() i64 {
7879/// Precision of timing depends on the hardware and operating system.
7980/// The return value is signed because it is possible to have a date that is
8081/// before the epoch.
81/// See `std.os.clock_gettime` for a POSIX timestamp.
82/// See `posix.clock_gettime` for a POSIX timestamp.
8283pub fn microTimestamp() i64 {
8384 return @as(i64, @intCast(@divFloor(nanoTimestamp(), ns_per_us)));
8485}
......@@ -88,21 +89,21 @@ pub fn microTimestamp() i64 {
8889/// On Windows this has a maximum granularity of 100 nanoseconds.
8990/// The return value is signed because it is possible to have a date that is
9091/// before the epoch.
91/// See `std.os.clock_gettime` for a POSIX timestamp.
92/// See `posix.clock_gettime` for a POSIX timestamp.
9293pub fn nanoTimestamp() i128 {
9394 switch (builtin.os.tag) {
9495 .windows => {
9596 // FileTime has a granularity of 100 nanoseconds and uses the NTFS/Windows epoch,
9697 // which is 1601-01-01.
9798 const epoch_adj = epoch.windows * (ns_per_s / 100);
98 var ft: os.windows.FILETIME = undefined;
99 os.windows.kernel32.GetSystemTimeAsFileTime(&ft);
99 var ft: windows.FILETIME = undefined;
100 windows.kernel32.GetSystemTimeAsFileTime(&ft);
100101 const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
101102 return @as(i128, @as(i64, @bitCast(ft64)) + epoch_adj) * 100;
102103 },
103104 .wasi => {
104 var ns: os.wasi.timestamp_t = undefined;
105 const err = os.wasi.clock_time_get(.REALTIME, 1, &ns);
105 var ns: std.os.wasi.timestamp_t = undefined;
106 const err = std.os.wasi.clock_time_get(.REALTIME, 1, &ns);
106107 assert(err == .SUCCESS);
107108 return ns;
108109 },
......@@ -113,8 +114,8 @@ pub fn nanoTimestamp() i128 {
113114 return value.toEpoch();
114115 },
115116 else => {
116 var ts: os.timespec = undefined;
117 os.clock_gettime(os.CLOCK.REALTIME, &ts) catch |err| switch (err) {
117 var ts: posix.timespec = undefined;
118 posix.clock_gettime(posix.CLOCK.REALTIME, &ts) catch |err| switch (err) {
118119 error.UnsupportedClock, error.Unexpected => return 0, // "Precision of timing depends on hardware and OS".
119120 };
120121 return (@as(i128, ts.tv_sec) * ns_per_s) + ts.tv_nsec;
......@@ -172,7 +173,7 @@ pub const s_per_week = s_per_day * 7;
172173/// It also tries to be monotonic, but this is not a guarantee due to OS/hardware bugs.
173174/// If you need monotonic readings for elapsed time, consider `Timer` instead.
174175pub const Instant = struct {
175 timestamp: if (is_posix) os.timespec else u64,
176 timestamp: if (is_posix) posix.timespec else u64,
176177
177178 // true if we should use clock_gettime()
178179 const is_posix = switch (builtin.os.tag) {
......@@ -188,11 +189,11 @@ pub const Instant = struct {
188189 const clock_id = switch (builtin.os.tag) {
189190 .windows => {
190191 // QPC on windows doesn't fail on >= XP/2000 and includes time suspended.
191 return Instant{ .timestamp = os.windows.QueryPerformanceCounter() };
192 return Instant{ .timestamp = windows.QueryPerformanceCounter() };
192193 },
193194 .wasi => {
194 var ns: os.wasi.timestamp_t = undefined;
195 const rc = os.wasi.clock_time_get(.MONOTONIC, 1, &ns);
195 var ns: std.os.wasi.timestamp_t = undefined;
196 const rc = std.os.wasi.clock_time_get(.MONOTONIC, 1, &ns);
196197 if (rc != .SUCCESS) return error.Unsupported;
197198 return .{ .timestamp = ns };
198199 },
......@@ -204,21 +205,21 @@ pub const Instant = struct {
204205 },
205206 // On darwin, use UPTIME_RAW instead of MONOTONIC as it ticks while
206207 // suspended.
207 .macos, .ios, .tvos, .watchos => os.CLOCK.UPTIME_RAW,
208 .macos, .ios, .tvos, .watchos => posix.CLOCK.UPTIME_RAW,
208209 // On freebsd derivatives, use MONOTONIC_FAST as currently there's
209210 // no precision tradeoff.
210 .freebsd, .dragonfly => os.CLOCK.MONOTONIC_FAST,
211 .freebsd, .dragonfly => posix.CLOCK.MONOTONIC_FAST,
211212 // On linux, use BOOTTIME instead of MONOTONIC as it ticks while
212213 // suspended.
213 .linux => os.CLOCK.BOOTTIME,
214 .linux => posix.CLOCK.BOOTTIME,
214215 // On other posix systems, MONOTONIC is generally the fastest and
215216 // ticks while suspended.
216 else => os.CLOCK.MONOTONIC,
217 else => posix.CLOCK.MONOTONIC,
217218 };
218219
219 var ts: os.timespec = undefined;
220 os.clock_gettime(clock_id, &ts) catch return error.Unsupported;
221 return Instant{ .timestamp = ts };
220 var ts: posix.timespec = undefined;
221 posix.clock_gettime(clock_id, &ts) catch return error.Unsupported;
222 return .{ .timestamp = ts };
222223 }
223224
224225 /// Quickly compares two instances between each other.
......@@ -245,7 +246,7 @@ pub const Instant = struct {
245246 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
246247 // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
247248 const qpc = self.timestamp - earlier.timestamp;
248 const qpf = os.windows.QueryPerformanceFrequency();
249 const qpf = windows.QueryPerformanceFrequency();
249250
250251 // 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it.
251252 // https://github.com/microsoft/STL/blob/785143a0c73f030238ef618890fd4d6ae2b3a3a0/stl/inc/chrono#L694-L701
lib/std/zig.zig+1-1
......@@ -1002,7 +1002,7 @@ pub const EnvVar = enum {
10021002 }
10031003
10041004 pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 {
1005 return std.os.getenvZ(@tagName(ev));
1005 return std.posix.getenvZ(@tagName(ev));
10061006 }
10071007};
10081008
lib/std/zig/Server.zig+1-1
......@@ -146,7 +146,7 @@ pub fn serveMessage(
146146 header: OutMessage.Header,
147147 bufs: []const []const u8,
148148) !void {
149 var iovecs: [10]std.os.iovec_const = undefined;
149 var iovecs: [10]std.posix.iovec_const = undefined;
150150 const header_le = bswap(header);
151151 iovecs[0] = .{
152152 .iov_base = @as([*]const u8, @ptrCast(&header_le)),
lib/std/zig/system.zig+10-9
......@@ -168,7 +168,7 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {
168168 if (query.os_tag == null) {
169169 switch (builtin.target.os.tag) {
170170 .linux => {
171 const uts = std.os.uname();
171 const uts = posix.uname();
172172 const release = mem.sliceTo(&uts.release, 0);
173173 // The release field sometimes has a weird format,
174174 // `Version.parse` will attempt to find some meaningful interpretation.
......@@ -181,7 +181,7 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {
181181 }
182182 },
183183 .solaris, .illumos => {
184 const uts = std.os.uname();
184 const uts = posix.uname();
185185 const release = mem.sliceTo(&uts.release, 0);
186186 if (std.SemanticVersion.parse(release)) |ver| {
187187 os.version_range.semver.min = ver;
......@@ -206,7 +206,7 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {
206206 var value: u32 = undefined;
207207 var len: usize = @sizeOf(@TypeOf(value));
208208
209 std.os.sysctlbynameZ(key, &value, &len, null, 0) catch |err| switch (err) {
209 posix.sysctlbynameZ(key, &value, &len, null, 0) catch |err| switch (err) {
210210 error.NameTooLong => unreachable, // constant, known good value
211211 error.PermissionDenied => unreachable, // only when setting values,
212212 error.SystemResources => unreachable, // memory already on the stack
......@@ -257,15 +257,15 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {
257257 },
258258 .openbsd => {
259259 const mib: [2]c_int = [_]c_int{
260 std.os.CTL.KERN,
261 std.os.KERN.OSRELEASE,
260 posix.CTL.KERN,
261 posix.KERN.OSRELEASE,
262262 };
263263 var buf: [64]u8 = undefined;
264264 // consider that sysctl result includes null-termination
265265 // reserve 1 byte to ensure we never overflow when appending ".0"
266266 var len: usize = buf.len - 1;
267267
268 std.os.sysctl(&mib, &buf, &len, null, 0) catch |err| switch (err) {
268 posix.sysctl(&mib, &buf, &len, null, 0) catch |err| switch (err) {
269269 error.NameTooLong => unreachable, // constant, known good value
270270 error.PermissionDenied => unreachable, // only when setting values,
271271 error.SystemResources => unreachable, // memory already on the stack
......@@ -636,8 +636,8 @@ pub fn abiAndDynamicLinkerFromFile(
636636
637637 // So far, no luck. Next we try to see if the information is
638638 // present in the symlink data for the dynamic linker path.
639 var link_buf: [std.os.PATH_MAX]u8 = undefined;
640 const link_name = std.os.readlink(dl_path, &link_buf) catch |err| switch (err) {
639 var link_buf: [posix.PATH_MAX]u8 = undefined;
640 const link_name = posix.readlink(dl_path, &link_buf) catch |err| switch (err) {
641641 error.NameTooLong => unreachable,
642642 error.InvalidUtf8 => unreachable, // WASI only
643643 error.InvalidWtf8 => unreachable, // Windows only
......@@ -670,7 +670,7 @@ pub fn abiAndDynamicLinkerFromFile(
670670
671671 // Nothing worked so far. Finally we fall back to hard-coded search paths.
672672 // Some distros such as Debian keep their libc.so.6 in `/lib/$triple/`.
673 var path_buf: [std.os.PATH_MAX]u8 = undefined;
673 var path_buf: [posix.PATH_MAX]u8 = undefined;
674674 var index: usize = 0;
675675 const prefix = "/lib/";
676676 const cpu_arch = @tagName(result.cpu.arch);
......@@ -1138,6 +1138,7 @@ const fs = std.fs;
11381138const assert = std.debug.assert;
11391139const Target = std.Target;
11401140const native_endian = builtin.cpu.arch.endian();
1141const posix = std.posix;
11411142
11421143test {
11431144 _ = NativePaths;
lib/std/zig/system/NativePaths.zig+3-3
......@@ -137,21 +137,21 @@ pub fn detect(arena: Allocator, native_target: std.Target) !NativePaths {
137137 // variables to search for headers and libraries.
138138 // We use os.getenv here since this part won't be executed on
139139 // windows, to get rid of unnecessary error handling.
140 if (std.os.getenv("C_INCLUDE_PATH")) |c_include_path| {
140 if (std.posix.getenv("C_INCLUDE_PATH")) |c_include_path| {
141141 var it = mem.tokenizeScalar(u8, c_include_path, ':');
142142 while (it.next()) |dir| {
143143 try self.addIncludeDir(dir);
144144 }
145145 }
146146
147 if (std.os.getenv("CPLUS_INCLUDE_PATH")) |cplus_include_path| {
147 if (std.posix.getenv("CPLUS_INCLUDE_PATH")) |cplus_include_path| {
148148 var it = mem.tokenizeScalar(u8, cplus_include_path, ':');
149149 while (it.next()) |dir| {
150150 try self.addIncludeDir(dir);
151151 }
152152 }
153153
154 if (std.os.getenv("LIBRARY_PATH")) |library_path| {
154 if (std.posix.getenv("LIBRARY_PATH")) |library_path| {
155155 var it = mem.tokenizeScalar(u8, library_path, ':');
156156 while (it.next()) |dir| {
157157 try self.addLibDir(dir);
lib/std/zig/system/darwin/macos.zig+1-2
......@@ -3,7 +3,6 @@ const builtin = @import("builtin");
33const assert = std.debug.assert;
44const mem = std.mem;
55const testing = std.testing;
6const os = std.os;
76
87const Target = std.Target;
98
......@@ -397,7 +396,7 @@ test "detect" {
397396pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
398397 var cpu_family: std.c.CPUFAMILY = undefined;
399398 var len: usize = @sizeOf(std.c.CPUFAMILY);
400 os.sysctlbynameZ("hw.cpufamily", &cpu_family, &len, null, 0) catch |err| switch (err) {
399 std.posix.sysctlbynameZ("hw.cpufamily", &cpu_family, &len, null, 0) catch |err| switch (err) {
401400 error.NameTooLong => unreachable, // constant, known good value
402401 error.PermissionDenied => unreachable, // only when setting values,
403402 error.SystemResources => unreachable, // memory already on the stack
src/Compilation.zig+4-4
......@@ -1137,7 +1137,7 @@ fn addModuleTableToCacheHash(
11371137 root_mod: *Package.Module,
11381138 main_mod: *Package.Module,
11391139 hash_type: union(enum) { path_bytes, files: *Cache.Manifest },
1140) (error{OutOfMemory} || std.os.GetCwdError)!void {
1140) (error{OutOfMemory} || std.process.GetCwdError)!void {
11411141 var seen_table: std.AutoArrayHashMapUnmanaged(*Package.Module, void) = .{};
11421142 defer seen_table.deinit(gpa);
11431143
......@@ -2741,7 +2741,7 @@ const Header = extern struct {
27412741/// saved, such as the target and most CLI flags. A cache hit will only occur
27422742/// when subsequent compiler invocations use the same set of flags.
27432743pub fn saveState(comp: *Compilation) !void {
2744 var bufs_list: [19]std.os.iovec_const = undefined;
2744 var bufs_list: [19]std.posix.iovec_const = undefined;
27452745 var bufs_len: usize = 0;
27462746
27472747 const lf = comp.bin_file orelse return;
......@@ -2808,7 +2808,7 @@ pub fn saveState(comp: *Compilation) !void {
28082808 try af.finish();
28092809}
28102810
2811fn addBuf(bufs_list: []std.os.iovec_const, bufs_len: *usize, buf: []const u8) void {
2811fn addBuf(bufs_list: []std.posix.iovec_const, bufs_len: *usize, buf: []const u8) void {
28122812 const i = bufs_len.*;
28132813 bufs_len.* = i + 1;
28142814 bufs_list[i] = .{
......@@ -3791,7 +3791,7 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void {
37913791 break :p padding_buffer[0..n];
37923792 };
37933793
3794 var header_and_trailer: [2]std.os.iovec_const = .{
3794 var header_and_trailer: [2]std.posix.iovec_const = .{
37953795 .{ .iov_base = header_bytes.ptr, .iov_len = header_bytes.len },
37963796 .{ .iov_base = padding.ptr, .iov_len = padding.len },
37973797 };
src/DarwinPosixSpawn.zig created+224
......@@ -0,0 +1,224 @@
1const errno = std.posix.errno;
2const unexpectedErrno = std.posix.unexpectedErrno;
3
4pub const Error = error{
5 SystemResources,
6 InvalidFileDescriptor,
7 NameTooLong,
8 TooBig,
9 PermissionDenied,
10 InputOutput,
11 FileSystem,
12 FileNotFound,
13 InvalidExe,
14 NotDir,
15 FileBusy,
16 /// Returned when the child fails to execute either in the pre-exec() initialization step, or
17 /// when exec(3) is invoked.
18 ChildExecFailed,
19} || std.posix.UnexpectedError;
20
21pub const Attr = struct {
22 attr: std.c.posix_spawnattr_t,
23
24 pub fn init() Error!Attr {
25 var attr: std.c.posix_spawnattr_t = undefined;
26 switch (errno(std.c.posix_spawnattr_init(&attr))) {
27 .SUCCESS => return Attr{ .attr = attr },
28 .NOMEM => return error.SystemResources,
29 .INVAL => unreachable,
30 else => |err| return unexpectedErrno(err),
31 }
32 }
33
34 pub fn deinit(self: *Attr) void {
35 defer self.* = undefined;
36 switch (errno(std.c.posix_spawnattr_destroy(&self.attr))) {
37 .SUCCESS => return,
38 .INVAL => unreachable, // Invalid parameters.
39 else => unreachable,
40 }
41 }
42
43 pub fn get(self: Attr) Error!u16 {
44 var flags: c_short = undefined;
45 switch (errno(std.c.posix_spawnattr_getflags(&self.attr, &flags))) {
46 .SUCCESS => return @as(u16, @bitCast(flags)),
47 .INVAL => unreachable,
48 else => |err| return unexpectedErrno(err),
49 }
50 }
51
52 pub fn set(self: *Attr, flags: u16) Error!void {
53 switch (errno(std.c.posix_spawnattr_setflags(&self.attr, @as(c_short, @bitCast(flags))))) {
54 .SUCCESS => return,
55 .INVAL => unreachable,
56 else => |err| return unexpectedErrno(err),
57 }
58 }
59};
60
61pub const Actions = struct {
62 actions: std.c.posix_spawn_file_actions_t,
63
64 pub fn init() Error!Actions {
65 var actions: std.c.posix_spawn_file_actions_t = undefined;
66 switch (errno(std.c.posix_spawn_file_actions_init(&actions))) {
67 .SUCCESS => return Actions{ .actions = actions },
68 .NOMEM => return error.SystemResources,
69 .INVAL => unreachable,
70 else => |err| return unexpectedErrno(err),
71 }
72 }
73
74 pub fn deinit(self: *Actions) void {
75 defer self.* = undefined;
76 switch (errno(std.c.posix_spawn_file_actions_destroy(&self.actions))) {
77 .SUCCESS => return,
78 .INVAL => unreachable, // Invalid parameters.
79 else => unreachable,
80 }
81 }
82
83 pub fn open(self: *Actions, fd: std.c.fd_t, path: []const u8, flags: u32, mode: std.c.mode_t) Error!void {
84 const posix_path = try std.os.toPosixPath(path);
85 return self.openZ(fd, &posix_path, flags, mode);
86 }
87
88 pub fn openZ(self: *Actions, fd: std.c.fd_t, path: [*:0]const u8, flags: u32, mode: std.c.mode_t) Error!void {
89 switch (errno(std.c.posix_spawn_file_actions_addopen(&self.actions, fd, path, @as(c_int, @bitCast(flags)), mode))) {
90 .SUCCESS => return,
91 .BADF => return error.InvalidFileDescriptor,
92 .NOMEM => return error.SystemResources,
93 .NAMETOOLONG => return error.NameTooLong,
94 .INVAL => unreachable, // the value of file actions is invalid
95 else => |err| return unexpectedErrno(err),
96 }
97 }
98
99 pub fn close(self: *Actions, fd: std.c.fd_t) Error!void {
100 switch (errno(std.c.posix_spawn_file_actions_addclose(&self.actions, fd))) {
101 .SUCCESS => return,
102 .BADF => return error.InvalidFileDescriptor,
103 .NOMEM => return error.SystemResources,
104 .INVAL => unreachable, // the value of file actions is invalid
105 .NAMETOOLONG => unreachable,
106 else => |err| return unexpectedErrno(err),
107 }
108 }
109
110 pub fn dup2(self: *Actions, fd: std.c.fd_t, newfd: std.c.fd_t) Error!void {
111 switch (errno(std.c.posix_spawn_file_actions_adddup2(&self.actions, fd, newfd))) {
112 .SUCCESS => return,
113 .BADF => return error.InvalidFileDescriptor,
114 .NOMEM => return error.SystemResources,
115 .INVAL => unreachable, // the value of file actions is invalid
116 .NAMETOOLONG => unreachable,
117 else => |err| return unexpectedErrno(err),
118 }
119 }
120
121 pub fn inherit(self: *Actions, fd: std.c.fd_t) Error!void {
122 switch (errno(std.c.posix_spawn_file_actions_addinherit_np(&self.actions, fd))) {
123 .SUCCESS => return,
124 .BADF => return error.InvalidFileDescriptor,
125 .NOMEM => return error.SystemResources,
126 .INVAL => unreachable, // the value of file actions is invalid
127 .NAMETOOLONG => unreachable,
128 else => |err| return unexpectedErrno(err),
129 }
130 }
131
132 pub fn chdir(self: *Actions, path: []const u8) Error!void {
133 const posix_path = try std.os.toPosixPath(path);
134 return self.chdirZ(&posix_path);
135 }
136
137 pub fn chdirZ(self: *Actions, path: [*:0]const u8) Error!void {
138 switch (errno(std.c.posix_spawn_file_actions_addchdir_np(&self.actions, path))) {
139 .SUCCESS => return,
140 .NOMEM => return error.SystemResources,
141 .NAMETOOLONG => return error.NameTooLong,
142 .BADF => unreachable,
143 .INVAL => unreachable, // the value of file actions is invalid
144 else => |err| return unexpectedErrno(err),
145 }
146 }
147
148 pub fn fchdir(self: *Actions, fd: std.c.fd_t) Error!void {
149 switch (errno(std.c.posix_spawn_file_actions_addfchdir_np(&self.actions, fd))) {
150 .SUCCESS => return,
151 .BADF => return error.InvalidFileDescriptor,
152 .NOMEM => return error.SystemResources,
153 .INVAL => unreachable, // the value of file actions is invalid
154 .NAMETOOLONG => unreachable,
155 else => |err| return unexpectedErrno(err),
156 }
157 }
158};
159
160pub fn spawn(
161 path: []const u8,
162 actions: ?Actions,
163 attr: ?Attr,
164 argv: [*:null]?[*:0]const u8,
165 envp: [*:null]?[*:0]const u8,
166) Error!std.c.pid_t {
167 const posix_path = try std.os.toPosixPath(path);
168 return spawnZ(&posix_path, actions, attr, argv, envp);
169}
170
171pub fn spawnZ(
172 path: [*:0]const u8,
173 actions: ?Actions,
174 attr: ?Attr,
175 argv: [*:null]?[*:0]const u8,
176 envp: [*:null]?[*:0]const u8,
177) Error!std.c.pid_t {
178 var pid: std.c.pid_t = undefined;
179 switch (errno(std.c.posix_spawn(
180 &pid,
181 path,
182 if (actions) |a| &a.actions else null,
183 if (attr) |a| &a.attr else null,
184 argv,
185 envp,
186 ))) {
187 .SUCCESS => return pid,
188 .@"2BIG" => return error.TooBig,
189 .NOMEM => return error.SystemResources,
190 .BADF => return error.InvalidFileDescriptor,
191 .ACCES => return error.PermissionDenied,
192 .IO => return error.InputOutput,
193 .LOOP => return error.FileSystem,
194 .NAMETOOLONG => return error.NameTooLong,
195 .NOENT => return error.FileNotFound,
196 .NOEXEC => return error.InvalidExe,
197 .NOTDIR => return error.NotDir,
198 .TXTBSY => return error.FileBusy,
199 .BADARCH => return error.InvalidExe,
200 .BADEXEC => return error.InvalidExe,
201 .FAULT => unreachable,
202 .INVAL => unreachable,
203 else => |err| return unexpectedErrno(err),
204 }
205}
206
207pub fn waitpid(pid: std.c.pid_t, flags: u32) Error!std.os.WaitPidResult {
208 var status: c_int = undefined;
209 while (true) {
210 const rc = waitpid(pid, &status, @as(c_int, @intCast(flags)));
211 switch (errno(rc)) {
212 .SUCCESS => return std.os.WaitPidResult{
213 .pid = @as(std.c.pid_t, @intCast(rc)),
214 .status = @as(u32, @bitCast(status)),
215 },
216 .INTR => continue,
217 .CHILD => return error.ChildExecFailed,
218 .INVAL => unreachable, // Invalid flags.
219 else => unreachable,
220 }
221 }
222}
223
224const std = @import("std");
src/Module.zig+2-2
......@@ -2396,7 +2396,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
23962396 .stat_inode = stat.inode,
23972397 .stat_mtime = stat.mtime,
23982398 };
2399 var iovecs = [_]std.os.iovec_const{
2399 var iovecs = [_]std.posix.iovec_const{
24002400 .{
24012401 .iov_base = @as([*]const u8, @ptrCast(&header)),
24022402 .iov_len = @sizeOf(Zir.Header),
......@@ -2484,7 +2484,7 @@ fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.File)
24842484 else
24852485 @as([*]u8, @ptrCast(zir.instructions.items(.data).ptr));
24862486
2487 var iovecs = [_]std.os.iovec{
2487 var iovecs = [_]std.posix.iovec{
24882488 .{
24892489 .iov_base = @as([*]u8, @ptrCast(zir.instructions.items(.tag).ptr)),
24902490 .iov_len = header.instructions_len,
src/Package/Fetch.zig+10-9
......@@ -482,7 +482,7 @@ fn runResource(
482482 // Compute the package hash based on the remaining files in the temporary
483483 // directory.
484484
485 if (builtin.os.tag == .linux and f.job_queue.work_around_btrfs_bug) {
485 if (native_os == .linux and f.job_queue.work_around_btrfs_bug) {
486486 // https://github.com/ziglang/zig/issues/17095
487487 tmp_directory.handle.close();
488488 tmp_directory.handle = cache_root.handle.makeOpenPath(tmp_dir_sub_path, .{
......@@ -1153,11 +1153,7 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!void {
11531153 std.tar.pipeToFileSystem(out_dir, reader, .{
11541154 .diagnostics = &diagnostics,
11551155 .strip_components = 1,
1156 // TODO: we would like to set this to executable_bit_only, but two
1157 // things need to happen before that:
1158 // 1. the tar implementation needs to support it
1159 // 2. the hashing algorithm here needs to support detecting the is_executable
1160 // bit on Windows from the ACLs (see the isExecutable function).
1156 // https://github.com/ziglang/zig/issues/17463
11611157 .mode_mode = .ignore,
11621158 .exclude_empty_directories = true,
11631159 }) catch |err| return f.fail(f.location_tok, try eb.printString(
......@@ -1542,6 +1538,8 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void
15421538 .file => {
15431539 var file = try dir.openFile(hashed_file.fs_path, .{});
15441540 defer file.close();
1541 // When implementing https://github.com/ziglang/zig/issues/17463
1542 // this will change to hard-coded `false`.
15451543 hasher.update(&.{ 0, @intFromBool(try isExecutable(file)) });
15461544 while (true) {
15471545 const bytes_read = try file.read(&buf);
......@@ -1568,15 +1566,17 @@ fn deleteFileFallible(dir: fs.Dir, deleted_file: *DeletedFile) DeletedFile.Error
15681566}
15691567
15701568fn isExecutable(file: fs.File) !bool {
1571 if (builtin.os.tag == .windows) {
1572 // TODO check the ACL on Windows.
1569 // When implementing https://github.com/ziglang/zig/issues/17463
1570 // this function will not check the mode but instead check if the file is an ELF
1571 // file or has a shebang line.
1572 if (native_os == .windows) {
15731573 // Until this is implemented, this could be a false negative on
15741574 // Windows, which is why we do not yet set executable_bit_only above
15751575 // when unpacking the tarball.
15761576 return false;
15771577 } else {
15781578 const stat = try file.stat();
1579 return (stat.mode & std.os.S.IXUSR) != 0;
1579 return (stat.mode & std.posix.S.IXUSR) != 0;
15801580 }
15811581}
15821582
......@@ -1694,6 +1694,7 @@ const git = @import("Fetch/git.zig");
16941694const Package = @import("../Package.zig");
16951695const Manifest = Package.Manifest;
16961696const ErrorBundle = std.zig.ErrorBundle;
1697const native_os = builtin.os.tag;
16971698
16981699test {
16991700 _ = Filter;
src/crash_report.zig+21-20
......@@ -2,9 +2,10 @@ const std = @import("std");
22const builtin = @import("builtin");
33const build_options = @import("build_options");
44const debug = std.debug;
5const os = std.os;
65const io = std.io;
76const print_zir = @import("print_zir.zig");
7const windows = std.os.windows;
8const posix = std.posix;
89const native_os = builtin.os.tag;
910
1011const Module = @import("Module.zig");
......@@ -156,14 +157,14 @@ pub fn attachSegfaultHandler() void {
156157 if (!debug.have_segfault_handling_support) {
157158 @compileError("segfault handler not supported for this target");
158159 }
159 if (builtin.os.tag == .windows) {
160 _ = os.windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
160 if (native_os == .windows) {
161 _ = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
161162 return;
162163 }
163 var act = os.Sigaction{
164 var act: posix.Sigaction = .{
164165 .handler = .{ .sigaction = handleSegfaultPosix },
165 .mask = os.empty_sigset,
166 .flags = (os.SA.SIGINFO | os.SA.RESTART | os.SA.RESETHAND),
166 .mask = posix.empty_sigset,
167 .flags = (posix.SA.SIGINFO | posix.SA.RESTART | posix.SA.RESETHAND),
167168 };
168169
169170 debug.updateSegfaultHandler(&act) catch {
......@@ -171,11 +172,11 @@ pub fn attachSegfaultHandler() void {
171172 };
172173}
173174
174fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const anyopaque) callconv(.C) noreturn {
175fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*const anyopaque) callconv(.C) noreturn {
175176 // TODO: use alarm() here to prevent infinite loops
176177 PanicSwitch.preDispatch();
177178
178 const addr = switch (builtin.os.tag) {
179 const addr = switch (native_os) {
179180 .linux => @intFromPtr(info.fields.sigfault.addr),
180181 .freebsd, .macos => @intFromPtr(info.addr),
181182 .netbsd => @intFromPtr(info.info.reason.fault.addr),
......@@ -186,9 +187,9 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any
186187
187188 var err_buffer: [128]u8 = undefined;
188189 const error_msg = switch (sig) {
189 os.SIG.SEGV => std.fmt.bufPrint(&err_buffer, "Segmentation fault at address 0x{x}", .{addr}) catch "Segmentation fault",
190 os.SIG.ILL => std.fmt.bufPrint(&err_buffer, "Illegal instruction at address 0x{x}", .{addr}) catch "Illegal instruction",
191 os.SIG.BUS => std.fmt.bufPrint(&err_buffer, "Bus error at address 0x{x}", .{addr}) catch "Bus error",
190 posix.SIG.SEGV => std.fmt.bufPrint(&err_buffer, "Segmentation fault at address 0x{x}", .{addr}) catch "Segmentation fault",
191 posix.SIG.ILL => std.fmt.bufPrint(&err_buffer, "Illegal instruction at address 0x{x}", .{addr}) catch "Illegal instruction",
192 posix.SIG.BUS => std.fmt.bufPrint(&err_buffer, "Bus error at address 0x{x}", .{addr}) catch "Bus error",
192193 else => std.fmt.bufPrint(&err_buffer, "Unknown error (signal {}) at address 0x{x}", .{ sig, addr }) catch "Unknown error",
193194 };
194195
......@@ -210,20 +211,20 @@ const WindowsSegfaultMessage = union(enum) {
210211 illegal_instruction: void,
211212};
212213
213fn handleSegfaultWindows(info: *os.windows.EXCEPTION_POINTERS) callconv(os.windows.WINAPI) c_long {
214fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(windows.WINAPI) c_long {
214215 switch (info.ExceptionRecord.ExceptionCode) {
215 os.windows.EXCEPTION_DATATYPE_MISALIGNMENT => handleSegfaultWindowsExtra(info, .{ .literal = "Unaligned Memory Access" }),
216 os.windows.EXCEPTION_ACCESS_VIOLATION => handleSegfaultWindowsExtra(info, .segfault),
217 os.windows.EXCEPTION_ILLEGAL_INSTRUCTION => handleSegfaultWindowsExtra(info, .illegal_instruction),
218 os.windows.EXCEPTION_STACK_OVERFLOW => handleSegfaultWindowsExtra(info, .{ .literal = "Stack Overflow" }),
219 else => return os.windows.EXCEPTION_CONTINUE_SEARCH,
216 windows.EXCEPTION_DATATYPE_MISALIGNMENT => handleSegfaultWindowsExtra(info, .{ .literal = "Unaligned Memory Access" }),
217 windows.EXCEPTION_ACCESS_VIOLATION => handleSegfaultWindowsExtra(info, .segfault),
218 windows.EXCEPTION_ILLEGAL_INSTRUCTION => handleSegfaultWindowsExtra(info, .illegal_instruction),
219 windows.EXCEPTION_STACK_OVERFLOW => handleSegfaultWindowsExtra(info, .{ .literal = "Stack Overflow" }),
220 else => return windows.EXCEPTION_CONTINUE_SEARCH,
220221 }
221222}
222223
223fn handleSegfaultWindowsExtra(info: *os.windows.EXCEPTION_POINTERS, comptime msg: WindowsSegfaultMessage) noreturn {
224fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, comptime msg: WindowsSegfaultMessage) noreturn {
224225 PanicSwitch.preDispatch();
225226
226 const stack_ctx = if (@hasDecl(os.windows, "CONTEXT"))
227 const stack_ctx = if (@hasDecl(windows, "CONTEXT"))
227228 StackContext{ .exception = info.ContextRecord }
228229 else ctx: {
229230 const addr = @intFromPtr(info.ExceptionRecord.ExceptionAddress);
......@@ -488,7 +489,7 @@ const PanicSwitch = struct {
488489 }
489490
490491 noinline fn abort() noreturn {
491 os.abort();
492 std.process.abort();
492493 }
493494
494495 inline fn goTo(comptime func: anytype, args: anytype) noreturn {
src/link.zig+2-2
......@@ -254,7 +254,7 @@ pub const File = struct {
254254 try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{});
255255 try emit.directory.handle.rename(tmp_sub_path, emit.sub_path);
256256 switch (builtin.os.tag) {
257 .linux => std.os.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0) catch |err| {
257 .linux => std.posix.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0) catch |err| {
258258 log.warn("ptrace failure: {s}", .{@errorName(err)});
259259 },
260260 .macos => base.cast(MachO).?.ptraceAttach(pid) catch |err| {
......@@ -305,7 +305,7 @@ pub const File = struct {
305305
306306 if (base.child_pid) |pid| {
307307 switch (builtin.os.tag) {
308 .linux => std.os.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0) catch |err| {
308 .linux => std.posix.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0) catch |err| {
309309 log.warn("ptrace failure: {s}", .{@errorName(err)});
310310 },
311311 else => return error.HotSwapUnavailableOnHostOperatingSystem,
src/link/C.zig+2-2
......@@ -518,7 +518,7 @@ const Flush = struct {
518518 asm_buf: std.ArrayListUnmanaged(u8) = .{},
519519
520520 /// We collect a list of buffers to write, and write them all at once with pwritev 😎
521 all_buffers: std.ArrayListUnmanaged(std.os.iovec_const) = .{},
521 all_buffers: std.ArrayListUnmanaged(std.posix.iovec_const) = .{},
522522 /// Keeps track of the total bytes of `all_buffers`.
523523 file_size: u64 = 0,
524524
......@@ -752,7 +752,7 @@ pub fn flushEmitH(module: *Module) !void {
752752
753753 // We collect a list of buffers to write, and write them all at once with pwritev 😎
754754 const num_buffers = emit_h.decl_table.count() + 1;
755 var all_buffers = try std.ArrayList(std.os.iovec_const).initCapacity(module.gpa, num_buffers);
755 var all_buffers = try std.ArrayList(std.posix.iovec_const).initCapacity(module.gpa, num_buffers);
756756 defer all_buffers.deinit();
757757
758758 var file_size: u64 = zig_h.len;
src/link/Dwarf.zig+4-4
......@@ -2118,7 +2118,7 @@ fn pwriteDbgLineNops(
21182118
21192119 const page_of_nops = [1]u8{DW.LNS.negate_stmt} ** 4096;
21202120 const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 };
2121 var vecs: [512]std.os.iovec_const = undefined;
2121 var vecs: [512]std.posix.iovec_const = undefined;
21222122 var vec_index: usize = 0;
21232123 {
21242124 var padding_left = prev_padding_size;
......@@ -2235,7 +2235,7 @@ fn pwriteDbgInfoNops(
22352235 defer tracy.end();
22362236
22372237 const page_of_nops = [1]u8{@intFromEnum(AbbrevCode.padding)} ** 4096;
2238 var vecs: [32]std.os.iovec_const = undefined;
2238 var vecs: [32]std.posix.iovec_const = undefined;
22392239 var vec_index: usize = 0;
22402240 {
22412241 var padding_left = prev_padding_size;
......@@ -2807,10 +2807,10 @@ fn genIncludeDirsAndFileNames(self: *Dwarf, arena: Allocator) !struct {
28072807 const full_path = try dif.mod.root.joinString(arena, dif.sub_file_path);
28082808 const dir_path = std.fs.path.dirname(full_path) orelse ".";
28092809 const sub_file_path = std.fs.path.basename(full_path);
2810 // TODO re-investigate if realpath is needed here
2810 // https://github.com/ziglang/zig/issues/19353
28112811 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
28122812 const resolved = if (!std.fs.path.isAbsolute(dir_path))
2813 std.os.realpath(dir_path, &buffer) catch dir_path
2813 std.posix.realpath(dir_path, &buffer) catch dir_path
28142814 else
28152815 dir_path;
28162816
src/link/Elf.zig+8-8
......@@ -422,7 +422,7 @@ pub fn createEmpty(
422422 const index: File.Index = @intCast(try self.files.addOne(gpa));
423423 self.files.set(index, .{ .zig_object = .{
424424 .index = index,
425 .path = try std.fmt.allocPrint(arena, "{s}.o", .{std.fs.path.stem(
425 .path = try std.fmt.allocPrint(arena, "{s}.o", .{fs.path.stem(
426426 zcu.main_mod.root_src_path,
427427 )}),
428428 } });
......@@ -1673,7 +1673,7 @@ pub const ParseError = error{
16731673 NotSupported,
16741674 InvalidCharacter,
16751675 UnknownFileType,
1676} || LdScript.Error || std.os.AccessError || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError;
1676} || LdScript.Error || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError;
16771677
16781678pub fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
16791679 const tracy = trace(@src());
......@@ -1703,7 +1703,7 @@ fn parseObject(self: *Elf, path: []const u8) ParseError!void {
17031703 defer tracy.end();
17041704
17051705 const gpa = self.base.comp.gpa;
1706 const handle = try std.fs.cwd().openFile(path, .{});
1706 const handle = try fs.cwd().openFile(path, .{});
17071707 const fh = try self.addFileHandle(handle);
17081708
17091709 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
......@@ -1723,7 +1723,7 @@ fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
17231723 defer tracy.end();
17241724
17251725 const gpa = self.base.comp.gpa;
1726 const handle = try std.fs.cwd().openFile(path, .{});
1726 const handle = try fs.cwd().openFile(path, .{});
17271727 const fh = try self.addFileHandle(handle);
17281728
17291729 var archive = Archive{};
......@@ -1749,7 +1749,7 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void {
17491749 defer tracy.end();
17501750
17511751 const gpa = self.base.comp.gpa;
1752 const handle = try std.fs.cwd().openFile(lib.path, .{});
1752 const handle = try fs.cwd().openFile(lib.path, .{});
17531753 defer handle.close();
17541754
17551755 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
......@@ -1770,7 +1770,7 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
17701770 defer tracy.end();
17711771
17721772 const gpa = self.base.comp.gpa;
1773 const in_file = try std.fs.cwd().openFile(lib.path, .{});
1773 const in_file = try fs.cwd().openFile(lib.path, .{});
17741774 defer in_file.close();
17751775 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
17761776 defer gpa.free(data);
......@@ -5468,7 +5468,7 @@ pub fn file(self: *Elf, index: File.Index) ?File {
54685468 };
54695469}
54705470
5471pub fn addFileHandle(self: *Elf, handle: std.fs.File) !File.HandleIndex {
5471pub fn addFileHandle(self: *Elf, handle: fs.File) !File.HandleIndex {
54725472 const gpa = self.base.comp.gpa;
54735473 const index: File.HandleIndex = @intCast(self.file_handles.items.len);
54745474 const fh = try self.file_handles.addOne(gpa);
......@@ -6045,7 +6045,7 @@ fn fmtDumpState(
60456045}
60466046
60476047/// Caller owns the memory.
6048pub fn preadAllAlloc(allocator: Allocator, handle: std.fs.File, offset: u64, size: u64) ![]u8 {
6048pub fn preadAllAlloc(allocator: Allocator, handle: fs.File, offset: u64, size: u64) ![]u8 {
60496049 const buffer = try allocator.alloc(u8, math.cast(usize, size) orelse return error.Overflow);
60506050 errdefer allocator.free(buffer);
60516051 const amt = try handle.preadAll(buffer, offset);
src/link/Elf/ZigObject.zig+3-3
......@@ -965,16 +965,16 @@ fn updateDeclCode(
965965 if (elf_file.base.child_pid) |pid| {
966966 switch (builtin.os.tag) {
967967 .linux => {
968 var code_vec: [1]std.os.iovec_const = .{.{
968 var code_vec: [1]std.posix.iovec_const = .{.{
969969 .iov_base = code.ptr,
970970 .iov_len = code.len,
971971 }};
972 var remote_vec: [1]std.os.iovec_const = .{.{
972 var remote_vec: [1]std.posix.iovec_const = .{.{
973973 .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(sym.address(.{}, elf_file))))),
974974 .iov_len = code.len,
975975 }};
976976 const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0);
977 switch (std.os.errno(rc)) {
977 switch (std.os.linux.E.init(rc)) {
978978 .SUCCESS => assert(rc == code.len),
979979 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
980980 }
src/link/Elf/synthetic_sections.zig+3-3
......@@ -317,16 +317,16 @@ pub const ZigGotSection = struct {
317317 if (elf_file.base.child_pid) |pid| {
318318 switch (builtin.os.tag) {
319319 .linux => {
320 var local_vec: [1]std.os.iovec_const = .{.{
320 var local_vec: [1]std.posix.iovec_const = .{.{
321321 .iov_base = &buf,
322322 .iov_len = buf.len,
323323 }};
324 var remote_vec: [1]std.os.iovec_const = .{.{
324 var remote_vec: [1]std.posix.iovec_const = .{.{
325325 .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(vaddr)))),
326326 .iov_len = buf.len,
327327 }};
328328 const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0);
329 switch (std.os.errno(rc)) {
329 switch (std.os.linux.E.init(rc)) {
330330 .SUCCESS => assert(rc == buf.len),
331331 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
332332 }
src/link/MachO.zig+27-27
......@@ -258,7 +258,7 @@ pub fn createEmpty(
258258 const index: File.Index = @intCast(try self.files.addOne(gpa));
259259 self.files.set(index, .{ .zig_object = .{
260260 .index = index,
261 .path = try std.fmt.allocPrint(arena, "{s}.o", .{std.fs.path.stem(
261 .path = try std.fmt.allocPrint(arena, "{s}.o", .{fs.path.stem(
262262 zcu.main_mod.root_src_path,
263263 )}),
264264 } });
......@@ -843,7 +843,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
843843 }
844844
845845 for (self.frameworks) |framework| {
846 const name = std.fs.path.stem(framework.path);
846 const name = fs.path.stem(framework.path);
847847 const arg = if (framework.needed)
848848 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name})
849849 else if (framework.weak)
......@@ -917,7 +917,7 @@ pub const ParseError = error{
917917 NotSupported,
918918 Unhandled,
919919 UnknownFileType,
920} || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError || tapi.TapiError;
920} || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError || tapi.TapiError;
921921
922922pub fn parsePositional(self: *MachO, path: []const u8, must_link: bool) ParseError!void {
923923 const tracy = trace(@src());
......@@ -956,7 +956,7 @@ fn parseObject(self: *MachO, path: []const u8) ParseError!void {
956956 defer tracy.end();
957957
958958 const gpa = self.base.comp.gpa;
959 const file = try std.fs.cwd().openFile(path, .{});
959 const file = try fs.cwd().openFile(path, .{});
960960 const handle = try self.addFileHandle(file);
961961 const mtime: u64 = mtime: {
962962 const stat = file.stat() catch break :mtime 0;
......@@ -992,7 +992,7 @@ fn parseArchive(self: *MachO, lib: SystemLib, must_link: bool, fat_arch: ?fat.Ar
992992
993993 const gpa = self.base.comp.gpa;
994994
995 const file = try std.fs.cwd().openFile(lib.path, .{});
995 const file = try fs.cwd().openFile(lib.path, .{});
996996 const handle = try self.addFileHandle(file);
997997
998998 var archive = Archive{};
......@@ -1029,7 +1029,7 @@ fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch)
10291029
10301030 const gpa = self.base.comp.gpa;
10311031
1032 const file = try std.fs.cwd().openFile(lib.path, .{});
1032 const file = try fs.cwd().openFile(lib.path, .{});
10331033 defer file.close();
10341034
10351035 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
......@@ -1054,7 +1054,7 @@ fn parseTbd(self: *MachO, lib: SystemLib, explicit: bool) ParseError!File.Index
10541054 defer tracy.end();
10551055
10561056 const gpa = self.base.comp.gpa;
1057 const file = try std.fs.cwd().openFile(lib.path, .{});
1057 const file = try fs.cwd().openFile(lib.path, .{});
10581058 defer file.close();
10591059
10601060 var lib_stub = LibStub.loadFromFile(gpa, file) catch return error.MalformedTbd; // TODO actually handle different errors
......@@ -1080,10 +1080,10 @@ fn parseTbd(self: *MachO, lib: SystemLib, explicit: bool) ParseError!File.Index
10801080/// image unless overriden by -no_implicit_dylibs.
10811081fn isHoisted(self: *MachO, install_name: []const u8) bool {
10821082 if (self.no_implicit_dylibs) return true;
1083 if (std.fs.path.dirname(install_name)) |dirname| {
1083 if (fs.path.dirname(install_name)) |dirname| {
10841084 if (mem.startsWith(u8, dirname, "/usr/lib")) return true;
10851085 if (eatPrefix(dirname, "/System/Library/Frameworks/")) |path| {
1086 const basename = std.fs.path.basename(install_name);
1086 const basename = fs.path.basename(install_name);
10871087 if (mem.indexOfScalar(u8, path, '.')) |index| {
10881088 if (mem.eql(u8, basename, path[0..index])) return true;
10891089 }
......@@ -1105,7 +1105,7 @@ fn accessLibPath(
11051105 test_path.clearRetainingCapacity();
11061106 try test_path.writer().print("{s}" ++ sep ++ "lib{s}{s}", .{ search_dir, name, ext });
11071107 try checked_paths.append(try arena.dupe(u8, test_path.items));
1108 std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
1108 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
11091109 error.FileNotFound => continue,
11101110 else => |e| return e,
11111111 };
......@@ -1133,7 +1133,7 @@ fn accessFrameworkPath(
11331133 ext,
11341134 });
11351135 try checked_paths.append(try arena.dupe(u8, test_path.items));
1136 std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
1136 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
11371137 error.FileNotFound => continue,
11381138 else => |e| return e,
11391139 };
......@@ -1181,7 +1181,7 @@ fn parseDependentDylibs(self: *MachO) !void {
11811181
11821182 const full_path = full_path: {
11831183 {
1184 const stem = std.fs.path.stem(id.name);
1184 const stem = fs.path.stem(id.name);
11851185
11861186 // Framework
11871187 for (framework_dirs) |dir| {
......@@ -1197,18 +1197,18 @@ fn parseDependentDylibs(self: *MachO) !void {
11971197 }
11981198 }
11991199
1200 if (std.fs.path.isAbsolute(id.name)) {
1201 const existing_ext = std.fs.path.extension(id.name);
1200 if (fs.path.isAbsolute(id.name)) {
1201 const existing_ext = fs.path.extension(id.name);
12021202 const path = if (existing_ext.len > 0) id.name[0 .. id.name.len - existing_ext.len] else id.name;
12031203 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
12041204 test_path.clearRetainingCapacity();
12051205 if (self.base.comp.sysroot) |root| {
1206 try test_path.writer().print("{s}" ++ std.fs.path.sep_str ++ "{s}{s}", .{ root, path, ext });
1206 try test_path.writer().print("{s}" ++ fs.path.sep_str ++ "{s}{s}", .{ root, path, ext });
12071207 } else {
12081208 try test_path.writer().print("{s}{s}", .{ path, ext });
12091209 }
12101210 try checked_paths.append(try arena.dupe(u8, test_path.items));
1211 std.fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
1211 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
12121212 error.FileNotFound => continue,
12131213 else => |e| return e,
12141214 };
......@@ -1220,10 +1220,10 @@ fn parseDependentDylibs(self: *MachO) !void {
12201220 const dylib = self.getFile(dylib_index).?.dylib;
12211221 for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| {
12221222 const prefix = eatPrefix(rpath, "@loader_path/") orelse rpath;
1223 const rel_path = try std.fs.path.join(arena, &.{ prefix, path });
1223 const rel_path = try fs.path.join(arena, &.{ prefix, path });
12241224 try checked_paths.append(rel_path);
1225 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
1226 const full_path = std.fs.realpath(rel_path, &buffer) catch continue;
1225 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
1226 const full_path = fs.realpath(rel_path, &buffer) catch continue;
12271227 break :full_path try arena.dupe(u8, full_path);
12281228 }
12291229 } else if (eatPrefix(id.name, "@loader_path/")) |_| {
......@@ -1235,8 +1235,8 @@ fn parseDependentDylibs(self: *MachO) !void {
12351235 }
12361236
12371237 try checked_paths.append(try arena.dupe(u8, id.name));
1238 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
1239 if (std.fs.realpath(id.name, &buffer)) |full_path| {
1238 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
1239 if (fs.realpath(id.name, &buffer)) |full_path| {
12401240 break :full_path try arena.dupe(u8, full_path);
12411241 } else |_| {
12421242 try self.reportMissingDependencyError(
......@@ -3651,7 +3651,7 @@ pub fn getTarget(self: MachO) std.Target {
36513651/// into a new inode, remove the original file, and rename the copy to match
36523652/// the original file. This is super messy, but there doesn't seem any other
36533653/// way to please the XNU.
3654pub fn invalidateKernelCache(dir: std.fs.Dir, sub_path: []const u8) !void {
3654pub fn invalidateKernelCache(dir: fs.Dir, sub_path: []const u8) !void {
36553655 if (comptime builtin.target.isDarwin() and builtin.target.cpu.arch == .aarch64) {
36563656 try dir.copyFile(sub_path, dir, sub_path, .{});
36573657 }
......@@ -3839,7 +3839,7 @@ pub fn getInternalObject(self: *MachO) ?*InternalObject {
38393839 return self.getFile(index).?.internal;
38403840}
38413841
3842pub fn addFileHandle(self: *MachO, file: std.fs.File) !File.HandleIndex {
3842pub fn addFileHandle(self: *MachO, file: fs.File) !File.HandleIndex {
38433843 const gpa = self.base.comp.gpa;
38443844 const index: File.HandleIndex = @intCast(self.file_handles.items.len);
38453845 const fh = try self.file_handles.addOne(gpa);
......@@ -4530,7 +4530,7 @@ fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersi
45304530
45314531 const sdk_dir = switch (sdk_layout) {
45324532 .sdk => comp.sysroot.?,
4533 .vendored => std.fs.path.join(arena, &.{ comp.zig_lib_directory.path.?, "libc", "darwin" }) catch return null,
4533 .vendored => fs.path.join(arena, &.{ comp.zig_lib_directory.path.?, "libc", "darwin" }) catch return null,
45344534 };
45354535 if (readSdkVersionFromSettings(arena, sdk_dir)) |ver| {
45364536 return parseSdkVersion(ver);
......@@ -4541,7 +4541,7 @@ fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersi
45414541 }
45424542
45434543 // infer from pathname
4544 const stem = std.fs.path.stem(sdk_dir);
4544 const stem = fs.path.stem(sdk_dir);
45454545 const start = for (stem, 0..) |c, i| {
45464546 if (std.ascii.isDigit(c)) break i;
45474547 } else stem.len;
......@@ -4556,8 +4556,8 @@ fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersi
45564556// Use property `MinimalDisplayName` to determine version.
45574557// The file/property is also available with vendored libc.
45584558fn readSdkVersionFromSettings(arena: Allocator, dir: []const u8) ![]const u8 {
4559 const sdk_path = try std.fs.path.join(arena, &.{ dir, "SDKSettings.json" });
4560 const contents = try std.fs.cwd().readFileAlloc(arena, sdk_path, std.math.maxInt(u16));
4559 const sdk_path = try fs.path.join(arena, &.{ dir, "SDKSettings.json" });
4560 const contents = try fs.cwd().readFileAlloc(arena, sdk_path, std.math.maxInt(u16));
45614561 const parsed = try std.json.parseFromSlice(std.json.Value, arena, contents, .{});
45624562 if (parsed.value.object.get("MinimalDisplayName")) |ver| return ver.string;
45634563 return error.SdkVersionFailure;
src/link/Plan9.zig+2-2
......@@ -368,7 +368,7 @@ fn putFn(self: *Plan9, decl_index: InternPool.DeclIndex, out: FnDeclOutput) !voi
368368 // getting the full file path
369369 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
370370 const full_path = try std.fs.path.join(arena, &.{
371 file.mod.root.root_dir.path orelse try std.os.getcwd(&buf),
371 file.mod.root.root_dir.path orelse try std.posix.getcwd(&buf),
372372 file.mod.root.sub_path,
373373 file.sub_file_path,
374374 });
......@@ -722,7 +722,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, prog_node: *std.Progress.Node
722722 defer gpa.free(got_table);
723723
724724 // + 4 for header, got, symbols, linecountinfo
725 var iovecs = try gpa.alloc(std.os.iovec_const, self.atomCount() + 4 - self.externCount());
725 var iovecs = try gpa.alloc(std.posix.iovec_const, self.atomCount() + 4 - self.externCount());
726726 defer gpa.free(iovecs);
727727
728728 const file = self.base.file.?;
src/link/Wasm.zig+2-2
......@@ -3046,7 +3046,7 @@ fn writeToFile(
30463046 }
30473047
30483048 // finally, write the entire binary into the file.
3049 var iovec = [_]std.os.iovec_const{.{
3049 var iovec = [_]std.posix.iovec_const{.{
30503050 .iov_base = binary_bytes.items.ptr,
30513051 .iov_len = binary_bytes.items.len,
30523052 }};
......@@ -3709,7 +3709,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) !vo
37093709 // report a nice error here with the file path if it fails instead of
37103710 // just returning the error code.
37113711 // chmod does not interact with umask, so we use a conservative -rwxr--r-- here.
3712 std.os.fchmodat(fs.cwd().fd, full_out_path, 0o744, 0) catch |err| switch (err) {
3712 std.posix.fchmodat(fs.cwd().fd, full_out_path, 0o744, 0) catch |err| switch (err) {
37133713 error.OperationNotSupported => unreachable, // Not a symlink.
37143714 else => |e| return e,
37153715 };
src/link/Wasm/Archive.zig+1-1
......@@ -193,7 +193,7 @@ pub fn parseObject(archive: Archive, wasm_file: *const Wasm, file_offset: u32) !
193193 const object_name = try archive.parseName(header);
194194 const name = name: {
195195 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
196 const path = try std.os.realpath(archive.name, &buffer);
196 const path = try std.posix.realpath(archive.name, &buffer);
197197 break :name try std.fmt.allocPrint(gpa, "{s}({s})", .{ path, object_name });
198198 };
199199 defer gpa.free(name);
src/main.zig+8-8
......@@ -48,7 +48,7 @@ pub const panic = crash_report.panic;
4848var wasi_preopens: fs.wasi.Preopens = undefined;
4949pub fn wasi_cwd() std.os.wasi.fd_t {
5050 // Expect the first preopen to be current working directory.
51 const cwd_fd: std.os.fd_t = 3;
51 const cwd_fd: std.posix.fd_t = 3;
5252 assert(mem.eql(u8, wasi_preopens.names[cwd_fd], "."));
5353 return cwd_fd;
5454}
......@@ -222,7 +222,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
222222 fatal("expected command argument", .{});
223223 }
224224
225 if (process.can_execv and std.os.getenvZ("ZIG_IS_DETECTING_LIBC_PATHS") != null) {
225 if (process.can_execv and std.posix.getenvZ("ZIG_IS_DETECTING_LIBC_PATHS") != null) {
226226 // In this case we have accidentally invoked ourselves as "the system C compiler"
227227 // to figure out where libc is installed. This is essentially infinite recursion
228228 // via child process execution due to the CC environment variable pointing to Zig.
......@@ -4434,16 +4434,16 @@ fn runOrTestHotSwap(
44344434
44354435 switch (builtin.target.os.tag) {
44364436 .macos, .ios, .tvos, .watchos => {
4437 const PosixSpawn = std.os.darwin.PosixSpawn;
4437 const PosixSpawn = @import("DarwinPosixSpawn.zig");
44384438
44394439 var attr = try PosixSpawn.Attr.init();
44404440 defer attr.deinit();
44414441
44424442 // ASLR is probably a good default for better debugging experience/programming
44434443 // with hot-code updates in mind. However, we can also make it work with ASLR on.
4444 const flags: u16 = std.os.darwin.POSIX_SPAWN.SETSIGDEF |
4445 std.os.darwin.POSIX_SPAWN.SETSIGMASK |
4446 std.os.darwin.POSIX_SPAWN.DISABLE_ASLR;
4444 const flags: u16 = std.c.POSIX_SPAWN.SETSIGDEF |
4445 std.c.POSIX_SPAWN.SETSIGMASK |
4446 std.c.POSIX_SPAWN.DISABLE_ASLR;
44474447 try attr.set(flags);
44484448
44494449 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
......@@ -5985,8 +5985,8 @@ fn parseCodeModel(arg: []const u8) std.builtin.CodeModel {
59855985/// garbage collector to run concurrently to zig processes, and to allow multiple
59865986/// zig processes to run concurrently with each other, without clobbering each other.
59875987fn gimmeMoreOfThoseSweetSweetFileDescriptors() void {
5988 if (!@hasDecl(std.os.system, "rlimit")) return;
5989 const posix = std.os;
5988 const posix = std.posix;
5989 if (!@hasDecl(posix, "rlimit")) return;
59905990
59915991 var lim = posix.getrlimit(.NOFILE) catch return; // Oh well; we tried.
59925992 if (comptime builtin.target.isDarwin()) {