authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-03-20 00:34:28-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-03-20 00:34:28-07:00
log3a836b4800337567735b2619075d8bb4e9f21c4d
treeab80d1a4f48e7401ad04dfb7855ad654e3761f63
parent7057bffc14602add697eb566b83934b7ad3fd81c
parent8c94950c2496a5fa69f35b0f06f221e2c12339cf
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #19354 from ziglang/std.posix

extract std.posix from std.os

115 files changed, 11531 insertions(+), 11594 deletions(-)

CMakeLists.txt-1
...@@ -286,7 +286,6 @@ set(ZIG_STAGE2_SOURCES...@@ -286,7 +286,6 @@ set(ZIG_STAGE2_SOURCES
286 "${CMAKE_SOURCE_DIR}/lib/std/multi_array_list.zig"286 "${CMAKE_SOURCE_DIR}/lib/std/multi_array_list.zig"
287 "${CMAKE_SOURCE_DIR}/lib/std/os.zig"287 "${CMAKE_SOURCE_DIR}/lib/std/os.zig"
288 "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"288 "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"
289 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/errno/generic.zig"
290 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig"289 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig"
291 "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"290 "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig"
292 "${CMAKE_SOURCE_DIR}/lib/std/os/linux/IoUring.zig"291 "${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 {...@@ -882,7 +882,7 @@ fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
882 return path;882 return path;
883 } else |_| {883 } else |_| {
884 std.log.err("Could not open provided config.h: \"{s}\"", .{path});884 std.log.err("Could not open provided config.h: \"{s}\"", .{path});
885 std.os.exit(1);885 std.process.exit(1);
886 }886 }
887 }887 }
888888
lib/compiler/objcopy.zig+1-1
...@@ -1285,7 +1285,7 @@ const ElfFileHelper = struct {...@@ -1285,7 +1285,7 @@ const ElfFileHelper = struct {
1285 for (consolidated.items) |cmd| {1285 for (consolidated.items) |cmd| {
1286 switch (cmd) {1286 switch (cmd) {
1287 .write_data => |data| {1287 .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 }};
1289 try out_file.pwritevAll(&iovec, data.out_offset);1289 try out_file.pwritevAll(&iovec, data.out_offset);
1290 },1290 },
1291 .copy_range => |range| {1291 .copy_range => |range| {
lib/compiler/resinator/main.zig+11-11
...@@ -26,7 +26,7 @@ pub fn main() !void {...@@ -26,7 +26,7 @@ pub fn main() !void {
2626
27 if (args.len < 2) {27 if (args.len < 2) {
28 try renderErrorMessage(stderr.writer(), stderr_config, .err, "expected zig lib dir as first argument", .{});28 try renderErrorMessage(stderr.writer(), stderr_config, .err, "expected zig lib dir as first argument", .{});
29 std.os.exit(1);29 std.process.exit(1);
30 }30 }
31 const zig_lib_dir = args[1];31 const zig_lib_dir = args[1];
32 var cli_args = args[2..];32 var cli_args = args[2..];
...@@ -62,7 +62,7 @@ pub fn main() !void {...@@ -62,7 +62,7 @@ pub fn main() !void {
62 var options = cli.parse(allocator, cli_args, &cli_diagnostics) catch |err| switch (err) {62 var options = cli.parse(allocator, cli_args, &cli_diagnostics) catch |err| switch (err) {
63 error.ParseError => {63 error.ParseError => {
64 try error_handler.emitCliDiagnostics(allocator, cli_args, &cli_diagnostics);64 try error_handler.emitCliDiagnostics(allocator, cli_args, &cli_diagnostics);
65 std.os.exit(1);65 std.process.exit(1);
66 },66 },
67 else => |e| return e,67 else => |e| return e,
68 };68 };
...@@ -117,7 +117,7 @@ pub fn main() !void {...@@ -117,7 +117,7 @@ pub fn main() !void {
117 },117 },
118 }118 }
119 try error_handler.emitMessage(allocator, .note, "to disable auto includes, use the option /:auto-includes none", .{});119 try error_handler.emitMessage(allocator, .note, "to disable auto includes, use the option /:auto-includes none", .{});
120 std.os.exit(1);120 std.process.exit(1);
121 },121 },
122 };122 };
123123
...@@ -153,16 +153,16 @@ pub fn main() !void {...@@ -153,16 +153,16 @@ pub fn main() !void {
153 preprocess.preprocess(&comp, preprocessed_buf.writer(), argv.items, maybe_dependencies_list) catch |err| switch (err) {153 preprocess.preprocess(&comp, preprocessed_buf.writer(), argv.items, maybe_dependencies_list) catch |err| switch (err) {
154 error.GeneratedSourceError => {154 error.GeneratedSourceError => {
155 try error_handler.emitAroDiagnostics(allocator, "failed during preprocessor setup (this is always a bug):", &comp);155 try error_handler.emitAroDiagnostics(allocator, "failed during preprocessor setup (this is always a bug):", &comp);
156 std.os.exit(1);156 std.process.exit(1);
157 },157 },
158 // ArgError can occur if e.g. the .rc file is not found158 // ArgError can occur if e.g. the .rc file is not found
159 error.ArgError, error.PreprocessError => {159 error.ArgError, error.PreprocessError => {
160 try error_handler.emitAroDiagnostics(allocator, "failed during preprocessing:", &comp);160 try error_handler.emitAroDiagnostics(allocator, "failed during preprocessing:", &comp);
161 std.os.exit(1);161 std.process.exit(1);
162 },162 },
163 error.StreamTooLong => {163 error.StreamTooLong => {
164 try error_handler.emitMessage(allocator, .err, "failed during preprocessing: maximum file size exceeded", .{});164 try error_handler.emitMessage(allocator, .err, "failed during preprocessing: maximum file size exceeded", .{});
165 std.os.exit(1);165 std.process.exit(1);
166 },166 },
167 error.OutOfMemory => |e| return e,167 error.OutOfMemory => |e| return e,
168 };168 };
...@@ -171,7 +171,7 @@ pub fn main() !void {...@@ -171,7 +171,7 @@ pub fn main() !void {
171 } else {171 } else {
172 break :full_input std.fs.cwd().readFileAlloc(allocator, options.input_filename, std.math.maxInt(usize)) catch |err| {172 break :full_input std.fs.cwd().readFileAlloc(allocator, options.input_filename, std.math.maxInt(usize)) catch |err| {
173 try error_handler.emitMessage(allocator, .err, "unable to read input file path '{s}': {s}", .{ options.input_filename, @errorName(err) });173 try error_handler.emitMessage(allocator, .err, "unable to read input file path '{s}': {s}", .{ options.input_filename, @errorName(err) });
174 std.os.exit(1);174 std.process.exit(1);
175 };175 };
176 }176 }
177 };177 };
...@@ -191,14 +191,14 @@ pub fn main() !void {...@@ -191,14 +191,14 @@ pub fn main() !void {
191 const final_input = removeComments(mapping_results.result, mapping_results.result, &mapping_results.mappings) catch |err| switch (err) {191 const final_input = removeComments(mapping_results.result, mapping_results.result, &mapping_results.mappings) catch |err| switch (err) {
192 error.InvalidSourceMappingCollapse => {192 error.InvalidSourceMappingCollapse => {
193 try error_handler.emitMessage(allocator, .err, "failed during comment removal; this is a known bug", .{});193 try error_handler.emitMessage(allocator, .err, "failed during comment removal; this is a known bug", .{});
194 std.os.exit(1);194 std.process.exit(1);
195 },195 },
196 else => |e| return e,196 else => |e| return e,
197 };197 };
198198
199 var output_file = std.fs.cwd().createFile(options.output_filename, .{}) catch |err| {199 var output_file = std.fs.cwd().createFile(options.output_filename, .{}) catch |err| {
200 try error_handler.emitMessage(allocator, .err, "unable to create output file '{s}': {s}", .{ options.output_filename, @errorName(err) });200 try error_handler.emitMessage(allocator, .err, "unable to create output file '{s}': {s}", .{ options.output_filename, @errorName(err) });
201 std.os.exit(1);201 std.process.exit(1);
202 };202 };
203 var output_file_closed = false;203 var output_file_closed = false;
204 defer if (!output_file_closed) output_file.close();204 defer if (!output_file_closed) output_file.close();
...@@ -231,7 +231,7 @@ pub fn main() !void {...@@ -231,7 +231,7 @@ pub fn main() !void {
231 output_file_closed = true;231 output_file_closed = true;
232 // Failing to delete is not really a big deal, so swallow any errors232 // Failing to delete is not really a big deal, so swallow any errors
233 std.fs.cwd().deleteFile(options.output_filename) catch {};233 std.fs.cwd().deleteFile(options.output_filename) catch {};
234 std.os.exit(1);234 std.process.exit(1);
235 },235 },
236 else => |e| return e,236 else => |e| return e,
237 };237 };
...@@ -247,7 +247,7 @@ pub fn main() !void {...@@ -247,7 +247,7 @@ pub fn main() !void {
247 if (options.depfile_path) |depfile_path| {247 if (options.depfile_path) |depfile_path| {
248 var depfile = std.fs.cwd().createFile(depfile_path, .{}) catch |err| {248 var depfile = std.fs.cwd().createFile(depfile_path, .{}) catch |err| {
249 try error_handler.emitMessage(allocator, .err, "unable to create depfile '{s}': {s}", .{ depfile_path, @errorName(err) });249 try error_handler.emitMessage(allocator, .err, "unable to create depfile '{s}': {s}", .{ depfile_path, @errorName(err) });
250 std.os.exit(1);250 std.process.exit(1);
251 };251 };
252 defer depfile.close();252 defer depfile.close();
253253
lib/std/Random/benchmark.zig+3-3
...@@ -144,7 +144,7 @@ pub fn main() !void {...@@ -144,7 +144,7 @@ pub fn main() !void {
144 i += 1;144 i += 1;
145 if (i == args.len) {145 if (i == args.len) {
146 usage();146 usage();
147 std.os.exit(1);147 std.process.exit(1);
148 }148 }
149149
150 filter = args[i];150 filter = args[i];
...@@ -152,7 +152,7 @@ pub fn main() !void {...@@ -152,7 +152,7 @@ pub fn main() !void {
152 i += 1;152 i += 1;
153 if (i == args.len) {153 if (i == args.len) {
154 usage();154 usage();
155 std.os.exit(1);155 std.process.exit(1);
156 }156 }
157157
158 const c = try std.fmt.parseUnsigned(usize, args[i], 10);158 const c = try std.fmt.parseUnsigned(usize, args[i], 10);
...@@ -170,7 +170,7 @@ pub fn main() !void {...@@ -170,7 +170,7 @@ pub fn main() !void {
170 return;170 return;
171 } else {171 } else {
172 usage();172 usage();
173 std.os.exit(1);173 std.process.exit(1);
174 }174 }
175 }175 }
176176
lib/std/Thread.zig+72-72
...@@ -5,9 +5,11 @@...@@ -5,9 +5,11 @@
5const std = @import("std.zig");5const std = @import("std.zig");
6const builtin = @import("builtin");6const builtin = @import("builtin");
7const math = std.math;7const math = std.math;
8const os = std.os;
9const assert = std.debug.assert;8const assert = std.debug.assert;
10const target = builtin.target;9const target = builtin.target;
10const native_os = builtin.os.tag;
11const posix = std.posix;
12const windows = std.os.windows;
1113
12pub const Futex = @import("Thread/Futex.zig");14pub const Futex = @import("Thread/Futex.zig");
13pub const ResetEvent = @import("Thread/ResetEvent.zig");15pub const ResetEvent = @import("Thread/ResetEvent.zig");
...@@ -18,23 +20,23 @@ pub const RwLock = @import("Thread/RwLock.zig");...@@ -18,23 +20,23 @@ pub const RwLock = @import("Thread/RwLock.zig");
18pub const Pool = @import("Thread/Pool.zig");20pub const Pool = @import("Thread/Pool.zig");
19pub const WaitGroup = @import("Thread/WaitGroup.zig");21pub 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
23const Thread = @This();25const Thread = @This();
24const Impl = if (target.os.tag == .windows)26const Impl = if (native_os == .windows)
25 WindowsThreadImpl27 WindowsThreadImpl
26else if (use_pthreads)28else if (use_pthreads)
27 PosixThreadImpl29 PosixThreadImpl
28else if (target.os.tag == .linux)30else if (native_os == .linux)
29 LinuxThreadImpl31 LinuxThreadImpl
30else if (target.os.tag == .wasi)32else if (native_os == .wasi)
31 WasiThreadImpl33 WasiThreadImpl
32else34else
33 UnsupportedImpl;35 UnsupportedImpl;
3436
35impl: Impl,37impl: Impl,
3638
37pub const max_name_len = switch (target.os.tag) {39pub const max_name_len = switch (native_os) {
38 .linux => 15,40 .linux => 15,
39 .windows => 31,41 .windows => 31,
40 .macos, .ios, .watchos, .tvos => 63,42 .macos, .ios, .watchos, .tvos => 63,
...@@ -50,7 +52,7 @@ pub const SetNameError = error{...@@ -50,7 +52,7 @@ pub const SetNameError = error{
50 NameTooLong,52 NameTooLong,
51 Unsupported,53 Unsupported,
52 Unexpected,54 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
55pub fn setName(self: Thread, name: []const u8) SetNameError!void {57pub fn setName(self: Thread, name: []const u8) SetNameError!void {
56 if (name.len > max_name_len) return error.NameTooLong;58 if (name.len > max_name_len) return error.NameTooLong;
...@@ -62,21 +64,21 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {...@@ -62,21 +64,21 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
62 break :blk name_buf[0..name.len :0];64 break :blk name_buf[0..name.len :0];
63 };65 };
6466
65 switch (target.os.tag) {67 switch (native_os) {
66 .linux => if (use_pthreads) {68 .linux => if (use_pthreads) {
67 if (self.getHandle() == std.c.pthread_self()) {69 if (self.getHandle() == std.c.pthread_self()) {
68 // Set the name of the calling thread (no thread id required).70 // Set the name of the calling thread (no thread id required).
69 const err = try os.prctl(.SET_NAME, .{@intFromPtr(name_with_terminator.ptr)});71 const err = try posix.prctl(.SET_NAME, .{@intFromPtr(name_with_terminator.ptr)});
70 switch (@as(os.E, @enumFromInt(err))) {72 switch (@as(posix.E, @enumFromInt(err))) {
71 .SUCCESS => return,73 .SUCCESS => return,
72 else => |e| return os.unexpectedErrno(e),74 else => |e| return posix.unexpectedErrno(e),
73 }75 }
74 } else {76 } else {
75 const err = std.c.pthread_setname_np(self.getHandle(), name_with_terminator.ptr);77 const err = std.c.pthread_setname_np(self.getHandle(), name_with_terminator.ptr);
76 switch (err) {78 switch (err) {
77 .SUCCESS => return,79 .SUCCESS => return,
78 .RANGE => unreachable,80 .RANGE => unreachable,
79 else => |e| return os.unexpectedErrno(e),81 else => |e| return posix.unexpectedErrno(e),
80 }82 }
81 }83 }
82 } else {84 } else {
...@@ -95,21 +97,21 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {...@@ -95,21 +97,21 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
95 const byte_len = math.cast(c_ushort, len * 2) orelse return error.NameTooLong;97 const byte_len = math.cast(c_ushort, len * 2) orelse return error.NameTooLong;
9698
97 // Note: NT allocates its own copy, no use-after-free here.99 // 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{
99 .Length = byte_len,101 .Length = byte_len,
100 .MaximumLength = byte_len,102 .MaximumLength = byte_len,
101 .Buffer = &buf,103 .Buffer = &buf,
102 };104 };
103105
104 switch (os.windows.ntdll.NtSetInformationThread(106 switch (windows.ntdll.NtSetInformationThread(
105 self.getHandle(),107 self.getHandle(),
106 .ThreadNameInformation,108 .ThreadNameInformation,
107 &unicode_string,109 &unicode_string,
108 @sizeOf(os.windows.UNICODE_STRING),110 @sizeOf(windows.UNICODE_STRING),
109 )) {111 )) {
110 .SUCCESS => return,112 .SUCCESS => return,
111 .NOT_IMPLEMENTED => return error.Unsupported,113 .NOT_IMPLEMENTED => return error.Unsupported,
112 else => |err| return os.windows.unexpectedStatus(err),114 else => |err| return windows.unexpectedStatus(err),
113 }115 }
114 },116 },
115 .macos, .ios, .watchos, .tvos => if (use_pthreads) {117 .macos, .ios, .watchos, .tvos => if (use_pthreads) {
...@@ -119,7 +121,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {...@@ -119,7 +121,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
119 const err = std.c.pthread_setname_np(name_with_terminator.ptr);121 const err = std.c.pthread_setname_np(name_with_terminator.ptr);
120 switch (err) {122 switch (err) {
121 .SUCCESS => return,123 .SUCCESS => return,
122 else => |e| return os.unexpectedErrno(e),124 else => |e| return posix.unexpectedErrno(e),
123 }125 }
124 },126 },
125 .netbsd, .solaris, .illumos => if (use_pthreads) {127 .netbsd, .solaris, .illumos => if (use_pthreads) {
...@@ -129,7 +131,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {...@@ -129,7 +131,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
129 .INVAL => unreachable,131 .INVAL => unreachable,
130 .SRCH => unreachable,132 .SRCH => unreachable,
131 .NOMEM => unreachable,133 .NOMEM => unreachable,
132 else => |e| return os.unexpectedErrno(e),134 else => |e| return posix.unexpectedErrno(e),
133 }135 }
134 },136 },
135 .freebsd, .openbsd => if (use_pthreads) {137 .freebsd, .openbsd => if (use_pthreads) {
...@@ -148,7 +150,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {...@@ -148,7 +150,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
148 .FAULT => unreachable,150 .FAULT => unreachable,
149 .NAMETOOLONG => unreachable, // already checked151 .NAMETOOLONG => unreachable, // already checked
150 .SRCH => unreachable,152 .SRCH => unreachable,
151 else => |e| return os.unexpectedErrno(e),153 else => |e| return posix.unexpectedErrno(e),
152 }154 }
153 },155 },
154 else => {},156 else => {},
...@@ -159,7 +161,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {...@@ -159,7 +161,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
159pub const GetNameError = error{161pub const GetNameError = error{
160 Unsupported,162 Unsupported,
161 Unexpected,163 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
164/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).166/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
165/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.167/// 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...@@ -167,21 +169,21 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
167 buffer_ptr[max_name_len] = 0;169 buffer_ptr[max_name_len] = 0;
168 var buffer: [:0]u8 = buffer_ptr;170 var buffer: [:0]u8 = buffer_ptr;
169171
170 switch (target.os.tag) {172 switch (native_os) {
171 .linux => if (use_pthreads) {173 .linux => if (use_pthreads) {
172 if (self.getHandle() == std.c.pthread_self()) {174 if (self.getHandle() == std.c.pthread_self()) {
173 // Get the name of the calling thread (no thread id required).175 // Get the name of the calling thread (no thread id required).
174 const err = try os.prctl(.GET_NAME, .{@intFromPtr(buffer.ptr)});176 const err = try posix.prctl(.GET_NAME, .{@intFromPtr(buffer.ptr)});
175 switch (@as(os.E, @enumFromInt(err))) {177 switch (@as(posix.E, @enumFromInt(err))) {
176 .SUCCESS => return std.mem.sliceTo(buffer, 0),178 .SUCCESS => return std.mem.sliceTo(buffer, 0),
177 else => |e| return os.unexpectedErrno(e),179 else => |e| return posix.unexpectedErrno(e),
178 }180 }
179 } else {181 } else {
180 const err = std.c.pthread_getname_np(self.getHandle(), buffer.ptr, max_name_len + 1);182 const err = std.c.pthread_getname_np(self.getHandle(), buffer.ptr, max_name_len + 1);
181 switch (err) {183 switch (err) {
182 .SUCCESS => return std.mem.sliceTo(buffer, 0),184 .SUCCESS => return std.mem.sliceTo(buffer, 0),
183 .RANGE => unreachable,185 .RANGE => unreachable,
184 else => |e| return os.unexpectedErrno(e),186 else => |e| return posix.unexpectedErrno(e),
185 }187 }
186 }188 }
187 } else {189 } else {
...@@ -196,10 +198,10 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co...@@ -196,10 +198,10 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
196 return if (data_len >= 1) buffer[0 .. data_len - 1] else null;198 return if (data_len >= 1) buffer[0 .. data_len - 1] else null;
197 },199 },
198 .windows => {200 .windows => {
199 const buf_capacity = @sizeOf(os.windows.UNICODE_STRING) + (@sizeOf(u16) * max_name_len);201 const buf_capacity = @sizeOf(windows.UNICODE_STRING) + (@sizeOf(u16) * max_name_len);
200 var buf: [buf_capacity]u8 align(@alignOf(os.windows.UNICODE_STRING)) = undefined;202 var buf: [buf_capacity]u8 align(@alignOf(windows.UNICODE_STRING)) = undefined;
201203
202 switch (os.windows.ntdll.NtQueryInformationThread(204 switch (windows.ntdll.NtQueryInformationThread(
203 self.getHandle(),205 self.getHandle(),
204 .ThreadNameInformation,206 .ThreadNameInformation,
205 &buf,207 &buf,
...@@ -207,12 +209,12 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co...@@ -207,12 +209,12 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
207 null,209 null,
208 )) {210 )) {
209 .SUCCESS => {211 .SUCCESS => {
210 const string = @as(*const os.windows.UNICODE_STRING, @ptrCast(&buf));212 const string = @as(*const windows.UNICODE_STRING, @ptrCast(&buf));
211 const len = std.unicode.wtf16LeToWtf8(buffer, string.Buffer.?[0 .. string.Length / 2]);213 const len = std.unicode.wtf16LeToWtf8(buffer, string.Buffer.?[0 .. string.Length / 2]);
212 return if (len > 0) buffer[0..len] else null;214 return if (len > 0) buffer[0..len] else null;
213 },215 },
214 .NOT_IMPLEMENTED => return error.Unsupported,216 .NOT_IMPLEMENTED => return error.Unsupported,
215 else => |err| return os.windows.unexpectedStatus(err),217 else => |err| return windows.unexpectedStatus(err),
216 }218 }
217 },219 },
218 .macos, .ios, .watchos, .tvos => if (use_pthreads) {220 .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...@@ -220,7 +222,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
220 switch (err) {222 switch (err) {
221 .SUCCESS => return std.mem.sliceTo(buffer, 0),223 .SUCCESS => return std.mem.sliceTo(buffer, 0),
222 .SRCH => unreachable,224 .SRCH => unreachable,
223 else => |e| return os.unexpectedErrno(e),225 else => |e| return posix.unexpectedErrno(e),
224 }226 }
225 },227 },
226 .netbsd, .solaris, .illumos => if (use_pthreads) {228 .netbsd, .solaris, .illumos => if (use_pthreads) {
...@@ -229,7 +231,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co...@@ -229,7 +231,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
229 .SUCCESS => return std.mem.sliceTo(buffer, 0),231 .SUCCESS => return std.mem.sliceTo(buffer, 0),
230 .INVAL => unreachable,232 .INVAL => unreachable,
231 .SRCH => unreachable,233 .SRCH => unreachable,
232 else => |e| return os.unexpectedErrno(e),234 else => |e| return posix.unexpectedErrno(e),
233 }235 }
234 },236 },
235 .freebsd, .openbsd => if (use_pthreads) {237 .freebsd, .openbsd => if (use_pthreads) {
...@@ -246,7 +248,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co...@@ -246,7 +248,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
246 .INVAL => unreachable,248 .INVAL => unreachable,
247 .FAULT => unreachable,249 .FAULT => unreachable,
248 .SRCH => unreachable,250 .SRCH => unreachable,
249 else => |e| return os.unexpectedErrno(e),251 else => |e| return posix.unexpectedErrno(e),
250 }252 }
251 },253 },
252 else => {},254 else => {},
...@@ -255,7 +257,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co...@@ -255,7 +257,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
255}257}
256258
257/// Represents an ID per thread guaranteed to be unique only within a process.259/// 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) {
259 .linux,261 .linux,
260 .dragonfly,262 .dragonfly,
261 .netbsd,263 .netbsd,
...@@ -265,7 +267,7 @@ pub const Id = switch (target.os.tag) {...@@ -265,7 +267,7 @@ pub const Id = switch (target.os.tag) {
265 .wasi,267 .wasi,
266 => u32,268 => u32,
267 .macos, .ios, .watchos, .tvos => u64,269 .macos, .ios, .watchos, .tvos => u64,
268 .windows => os.windows.DWORD,270 .windows => windows.DWORD,
269 else => usize,271 else => usize,
270};272};
271273
...@@ -368,13 +370,13 @@ pub const YieldError = error{...@@ -368,13 +370,13 @@ pub const YieldError = error{
368370
369/// Yields the current thread potentially allowing other threads to run.371/// Yields the current thread potentially allowing other threads to run.
370pub fn yield() YieldError!void {372pub fn yield() YieldError!void {
371 if (builtin.os.tag == .windows) {373 if (native_os == .windows) {
372 // The return value has to do with how many other threads there are; it is not374 // The return value has to do with how many other threads there are; it is not
373 // an error condition on Windows.375 // an error condition on Windows.
374 _ = os.windows.kernel32.SwitchToThread();376 _ = windows.kernel32.SwitchToThread();
375 return;377 return;
376 }378 }
377 switch (os.errno(os.system.sched_yield())) {379 switch (posix.errno(posix.system.sched_yield())) {
378 .SUCCESS => return,380 .SUCCESS => return,
379 .NOSYS => return error.SystemCannotYield,381 .NOSYS => return error.SystemCannotYield,
380 else => return error.SystemCannotYield,382 else => return error.SystemCannotYield,
...@@ -390,7 +392,7 @@ const Completion = std.atomic.Value(enum(u8) {...@@ -390,7 +392,7 @@ const Completion = std.atomic.Value(enum(u8) {
390392
391/// Used by the Thread implementations to call the spawned function with the arguments.393/// Used by the Thread implementations to call the spawned function with the arguments.
392fn callFn(comptime f: anytype, args: anytype) switch (Impl) {394fn callFn(comptime f: anytype, args: anytype) switch (Impl) {
393 WindowsThreadImpl => std.os.windows.DWORD,395 WindowsThreadImpl => windows.DWORD,
394 LinuxThreadImpl => u8,396 LinuxThreadImpl => u8,
395 PosixThreadImpl => ?*anyopaque,397 PosixThreadImpl => ?*anyopaque,
396 else => unreachable,398 else => unreachable,
...@@ -470,13 +472,11 @@ const UnsupportedImpl = struct {...@@ -470,13 +472,11 @@ const UnsupportedImpl = struct {
470472
471 fn unsupported(unused: anytype) noreturn {473 fn unsupported(unused: anytype) noreturn {
472 _ = unused;474 _ = unused;
473 @compileError("Unsupported operating system " ++ @tagName(target.os.tag));475 @compileError("Unsupported operating system " ++ @tagName(native_os));
474 }476 }
475};477};
476478
477const WindowsThreadImpl = struct {479const WindowsThreadImpl = struct {
478 const windows = os.windows;
479
480 pub const ThreadHandle = windows.HANDLE;480 pub const ThreadHandle = windows.HANDLE;
481481
482 fn getCurrentId() windows.DWORD {482 fn getCurrentId() windows.DWORD {
...@@ -584,7 +584,7 @@ const PosixThreadImpl = struct {...@@ -584,7 +584,7 @@ const PosixThreadImpl = struct {
584 pub const ThreadHandle = c.pthread_t;584 pub const ThreadHandle = c.pthread_t;
585585
586 fn getCurrentId() Id {586 fn getCurrentId() Id {
587 switch (target.os.tag) {587 switch (native_os) {
588 .linux => {588 .linux => {
589 return LinuxThreadImpl.getCurrentId();589 return LinuxThreadImpl.getCurrentId();
590 },590 },
...@@ -616,15 +616,15 @@ const PosixThreadImpl = struct {...@@ -616,15 +616,15 @@ const PosixThreadImpl = struct {
616 }616 }
617617
618 fn getCpuCount() !usize {618 fn getCpuCount() !usize {
619 switch (target.os.tag) {619 switch (native_os) {
620 .linux => {620 .linux => {
621 return LinuxThreadImpl.getCpuCount();621 return LinuxThreadImpl.getCpuCount();
622 },622 },
623 .openbsd => {623 .openbsd => {
624 var count: c_int = undefined;624 var count: c_int = undefined;
625 var count_size: usize = @sizeOf(c_int);625 var count_size: usize = @sizeOf(c_int);
626 const mib = [_]c_int{ os.CTL.HW, os.system.HW.NCPUONLINE };626 const mib = [_]c_int{ std.c.CTL.HW, std.c.HW.NCPUONLINE };
627 os.sysctl(&mib, &count, &count_size, null, 0) catch |err| switch (err) {627 std.c.sysctl(&mib, &count, &count_size, null, 0) catch |err| switch (err) {
628 error.NameTooLong, error.UnknownName => unreachable,628 error.NameTooLong, error.UnknownName => unreachable,
629 else => |e| return e,629 else => |e| return e,
630 };630 };
...@@ -634,25 +634,25 @@ const PosixThreadImpl = struct {...@@ -634,25 +634,25 @@ const PosixThreadImpl = struct {
634 // The "proper" way to get the cpu count would be to query634 // The "proper" way to get the cpu count would be to query
635 // /dev/kstat via ioctls, and traverse a linked list for each635 // /dev/kstat via ioctls, and traverse a linked list for each
636 // cpu.636 // cpu.
637 const rc = c.sysconf(os._SC.NPROCESSORS_ONLN);637 const rc = c.sysconf(std.c._SC.NPROCESSORS_ONLN);
638 return switch (os.errno(rc)) {638 return switch (posix.errno(rc)) {
639 .SUCCESS => @as(usize, @intCast(rc)),639 .SUCCESS => @as(usize, @intCast(rc)),
640 else => |err| os.unexpectedErrno(err),640 else => |err| posix.unexpectedErrno(err),
641 };641 };
642 },642 },
643 .haiku => {643 .haiku => {
644 var system_info: os.system.system_info = undefined;644 var system_info: std.c.system_info = undefined;
645 const rc = os.system.get_system_info(&system_info); // always returns B_OK645 const rc = std.c.get_system_info(&system_info); // always returns B_OK
646 return switch (os.errno(rc)) {646 return switch (posix.errno(rc)) {
647 .SUCCESS => @as(usize, @intCast(system_info.cpu_count)),647 .SUCCESS => @as(usize, @intCast(system_info.cpu_count)),
648 else => |err| os.unexpectedErrno(err),648 else => |err| posix.unexpectedErrno(err),
649 };649 };
650 },650 },
651 else => {651 else => {
652 var count: c_int = undefined;652 var count: c_int = undefined;
653 var count_len: usize = @sizeOf(c_int);653 var count_len: usize = @sizeOf(c_int);
654 const name = if (comptime target.isDarwin()) "hw.logicalcpu" else "hw.ncpu";654 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) {
656 error.NameTooLong, error.UnknownName => unreachable,656 error.NameTooLong, error.UnknownName => unreachable,
657 else => |e| return e,657 else => |e| return e,
658 };658 };
...@@ -699,7 +699,7 @@ const PosixThreadImpl = struct {...@@ -699,7 +699,7 @@ const PosixThreadImpl = struct {
699 .AGAIN => return error.SystemResources,699 .AGAIN => return error.SystemResources,
700 .PERM => unreachable,700 .PERM => unreachable,
701 .INVAL => unreachable,701 .INVAL => unreachable,
702 else => |err| return os.unexpectedErrno(err),702 else => |err| return posix.unexpectedErrno(err),
703 }703 }
704 }704 }
705705
...@@ -1013,7 +1013,7 @@ const WasiThreadImpl = struct {...@@ -1013,7 +1013,7 @@ const WasiThreadImpl = struct {
1013};1013};
10141014
1015const LinuxThreadImpl = struct {1015const LinuxThreadImpl = struct {
1016 const linux = os.linux;1016 const linux = std.os.linux;
10171017
1018 pub const ThreadHandle = i32;1018 pub const ThreadHandle = i32;
10191019
...@@ -1028,9 +1028,9 @@ const LinuxThreadImpl = struct {...@@ -1028,9 +1028,9 @@ const LinuxThreadImpl = struct {
1028 }1028 }
10291029
1030 fn getCpuCount() !usize {1030 fn getCpuCount() !usize {
1031 const cpu_set = try os.sched_getaffinity(0);1031 const cpu_set = try posix.sched_getaffinity(0);
1032 // TODO: should not need this usize cast1032 // 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));
1034 }1034 }
10351035
1036 thread: *ThreadCompletion,1036 thread: *ThreadCompletion,
...@@ -1228,10 +1228,10 @@ const LinuxThreadImpl = struct {...@@ -1228,10 +1228,10 @@ const LinuxThreadImpl = struct {
1228 // map all memory needed without read/write permissions1228 // map all memory needed without read/write permissions
1229 // to avoid committing the whole region right away1229 // to avoid committing the whole region right away
1230 // anonymous mapping ensures file descriptor limits are not exceeded1230 // anonymous mapping ensures file descriptor limits are not exceeded
1231 const mapped = os.mmap(1231 const mapped = posix.mmap(
1232 null,1232 null,
1233 map_bytes,1233 map_bytes,
1234 os.PROT.NONE,1234 posix.PROT.NONE,
1235 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },1235 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
1236 -1,1236 -1,
1237 0,1237 0,
...@@ -1244,24 +1244,24 @@ const LinuxThreadImpl = struct {...@@ -1244,24 +1244,24 @@ const LinuxThreadImpl = struct {
1244 else => |e| return e,1244 else => |e| return e,
1245 };1245 };
1246 assert(mapped.len >= map_bytes);1246 assert(mapped.len >= map_bytes);
1247 errdefer os.munmap(mapped);1247 errdefer posix.munmap(mapped);
12481248
1249 // map everything but the guard page as read/write1249 // map everything but the guard page as read/write
1250 os.mprotect(1250 posix.mprotect(
1251 @alignCast(mapped[guard_offset..]),1251 @alignCast(mapped[guard_offset..]),
1252 os.PROT.READ | os.PROT.WRITE,1252 posix.PROT.READ | posix.PROT.WRITE,
1253 ) catch |err| switch (err) {1253 ) catch |err| switch (err) {
1254 error.AccessDenied => unreachable,1254 error.AccessDenied => unreachable,
1255 else => |e| return e,1255 else => |e| return e,
1256 };1256 };
12571257
1258 // Prepare the TLS segment and prepare a user_desc struct when needed on x861258 // 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..]);1259 var tls_ptr = linux.tls.prepareTLS(mapped[tls_offset..]);
1260 var user_desc: if (target.cpu.arch == .x86) os.linux.user_desc else void = undefined;1260 var user_desc: if (target.cpu.arch == .x86) linux.user_desc else void = undefined;
1261 if (target.cpu.arch == .x86) {1261 if (target.cpu.arch == .x86) {
1262 defer tls_ptr = @intFromPtr(&user_desc);1262 defer tls_ptr = @intFromPtr(&user_desc);
1263 user_desc = .{1263 user_desc = .{
1264 .entry_number = os.linux.tls.tls_image.gdt_entry_number,1264 .entry_number = linux.tls.tls_image.gdt_entry_number,
1265 .base_addr = tls_ptr,1265 .base_addr = tls_ptr,
1266 .limit = 0xfffff,1266 .limit = 0xfffff,
1267 .flags = .{1267 .flags = .{
...@@ -1286,7 +1286,7 @@ const LinuxThreadImpl = struct {...@@ -1286,7 +1286,7 @@ const LinuxThreadImpl = struct {
1286 linux.CLONE.PARENT_SETTID | linux.CLONE.CHILD_CLEARTID |1286 linux.CLONE.PARENT_SETTID | linux.CLONE.CHILD_CLEARTID |
1287 linux.CLONE.SIGHAND | linux.CLONE.SYSVSEM | linux.CLONE.SETTLS;1287 linux.CLONE.SIGHAND | linux.CLONE.SYSVSEM | linux.CLONE.SETTLS;
12881288
1289 switch (linux.getErrno(linux.clone(1289 switch (linux.E.init(linux.clone(
1290 Instance.entryFn,1290 Instance.entryFn,
1291 @intFromPtr(&mapped[stack_offset]),1291 @intFromPtr(&mapped[stack_offset]),
1292 flags,1292 flags,
...@@ -1302,7 +1302,7 @@ const LinuxThreadImpl = struct {...@@ -1302,7 +1302,7 @@ const LinuxThreadImpl = struct {
1302 .NOSPC => unreachable,1302 .NOSPC => unreachable,
1303 .PERM => unreachable,1303 .PERM => unreachable,
1304 .USERS => unreachable,1304 .USERS => unreachable,
1305 else => |err| return os.unexpectedErrno(err),1305 else => |err| return posix.unexpectedErrno(err),
1306 }1306 }
1307 }1307 }
13081308
...@@ -1319,7 +1319,7 @@ const LinuxThreadImpl = struct {...@@ -1319,7 +1319,7 @@ const LinuxThreadImpl = struct {
1319 }1319 }
13201320
1321 fn join(self: Impl) void {1321 fn join(self: Impl) void {
1322 defer os.munmap(self.thread.mapped);1322 defer posix.munmap(self.thread.mapped);
13231323
1324 var spin: u8 = 10;1324 var spin: u8 = 10;
1325 while (true) {1325 while (true) {
...@@ -1334,7 +1334,7 @@ const LinuxThreadImpl = struct {...@@ -1334,7 +1334,7 @@ const LinuxThreadImpl = struct {
1334 continue;1334 continue;
1335 }1335 }
13361336
1337 switch (linux.getErrno(linux.futex_wait(1337 switch (linux.E.init(linux.futex_wait(
1338 &self.thread.child_tid.raw,1338 &self.thread.child_tid.raw,
1339 linux.FUTEX.WAIT,1339 linux.FUTEX.WAIT,
1340 tid,1340 tid,
...@@ -1383,7 +1383,7 @@ test "setName, getName" {...@@ -1383,7 +1383,7 @@ test "setName, getName" {
1383 // Wait for the main thread to have set the thread field in the context.1383 // Wait for the main thread to have set the thread field in the context.
1384 ctx.start_wait_event.wait();1384 ctx.start_wait_event.wait();
13851385
1386 switch (target.os.tag) {1386 switch (native_os) {
1387 .windows => testThreadName(&ctx.thread) catch |err| switch (err) {1387 .windows => testThreadName(&ctx.thread) catch |err| switch (err) {
1388 error.Unsupported => return error.SkipZigTest,1388 error.Unsupported => return error.SkipZigTest,
1389 else => return err,1389 else => return err,
...@@ -1406,7 +1406,7 @@ test "setName, getName" {...@@ -1406,7 +1406,7 @@ test "setName, getName" {
1406 context.start_wait_event.set();1406 context.start_wait_event.set();
1407 context.test_done_event.wait();1407 context.test_done_event.wait();
14081408
1409 switch (target.os.tag) {1409 switch (native_os) {
1410 .macos, .ios, .watchos, .tvos => {1410 .macos, .ios, .watchos, .tvos => {
1411 const res = thread.setName("foobar");1411 const res = thread.setName("foobar");
1412 try std.testing.expectError(error.Unsupported, res);1412 try std.testing.expectError(error.Unsupported, res);
lib/std/Thread/Futex.zig+71-64
...@@ -1,13 +1,20 @@...@@ -1,13 +1,20 @@
1//! Futex is a mechanism used to block (`wait`) and unblock (`wake`) threads using a 32bit memory address as hints.1//! A mechanism used to block (`wait`) and unblock (`wake`) threads using a
2//! Blocking a thread is acknowledged only if the 32bit memory address is equal to a given value.2//! 32bit memory address as hints.
3//! This check helps avoid block/unblock deadlocks which occur if a `wake()` happens before a `wait()`.3//!
4//! Using Futex, other Thread synchronization primitives can be built which efficiently wait for cross-thread events or signals.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
6const std = @import("../std.zig");11const std = @import("../std.zig");
7const builtin = @import("builtin");12const builtin = @import("builtin");
8const Futex = @This();13const Futex = @This();
14const windows = std.os.windows;
15const linux = std.os.linux;
16const c = std.c;
917
10const os = std.os;
11const assert = std.debug.assert;18const assert = std.debug.assert;
12const testing = std.testing;19const testing = std.testing;
13const atomic = std.atomic;20const atomic = std.atomic;
...@@ -124,18 +131,18 @@ const SingleThreadedImpl = struct {...@@ -124,18 +131,18 @@ const SingleThreadedImpl = struct {
124// as it's generally already a linked target and is autoloaded into all processes anyway.131// as it's generally already a linked target and is autoloaded into all processes anyway.
125const WindowsImpl = struct {132const WindowsImpl = struct {
126 fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void {133 fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void {
127 var timeout_value: os.windows.LARGE_INTEGER = undefined;134 var timeout_value: windows.LARGE_INTEGER = undefined;
128 var timeout_ptr: ?*const os.windows.LARGE_INTEGER = null;135 var timeout_ptr: ?*const windows.LARGE_INTEGER = null;
129136
130 // NTDLL functions work with time in units of 100 nanoseconds.137 // NTDLL functions work with time in units of 100 nanoseconds.
131 // Positive values are absolute deadlines while negative values are relative durations.138 // Positive values are absolute deadlines while negative values are relative durations.
132 if (timeout) |delay| {139 if (timeout) |delay| {
133 timeout_value = @as(os.windows.LARGE_INTEGER, @intCast(delay / 100));140 timeout_value = @as(windows.LARGE_INTEGER, @intCast(delay / 100));
134 timeout_value = -timeout_value;141 timeout_value = -timeout_value;
135 timeout_ptr = &timeout_value;142 timeout_ptr = &timeout_value;
136 }143 }
137144
138 const rc = os.windows.ntdll.RtlWaitOnAddress(145 const rc = windows.ntdll.RtlWaitOnAddress(
139 ptr,146 ptr,
140 &expect,147 &expect,
141 @sizeOf(@TypeOf(expect)),148 @sizeOf(@TypeOf(expect)),
...@@ -157,8 +164,8 @@ const WindowsImpl = struct {...@@ -157,8 +164,8 @@ const WindowsImpl = struct {
157 assert(max_waiters != 0);164 assert(max_waiters != 0);
158165
159 switch (max_waiters) {166 switch (max_waiters) {
160 1 => os.windows.ntdll.RtlWakeAddressSingle(address),167 1 => windows.ntdll.RtlWakeAddressSingle(address),
161 else => os.windows.ntdll.RtlWakeAddressAll(address),168 else => windows.ntdll.RtlWakeAddressAll(address),
162 }169 }
163 }170 }
164};171};
...@@ -189,10 +196,10 @@ const DarwinImpl = struct {...@@ -189,10 +196,10 @@ const DarwinImpl = struct {
189 var timeout_overflowed = false;196 var timeout_overflowed = false;
190197
191 const addr: *const anyopaque = ptr;198 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;
193 const status = blk: {200 const status = blk: {
194 if (supports_ulock_wait2) {201 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);
196 }203 }
197204
198 const timeout_us = std.math.cast(u32, timeout_ns / std.time.ns_per_us) orelse overflow: {205 const timeout_us = std.math.cast(u32, timeout_ns / std.time.ns_per_us) orelse overflow: {
...@@ -200,11 +207,11 @@ const DarwinImpl = struct {...@@ -200,11 +207,11 @@ const DarwinImpl = struct {
200 break :overflow std.math.maxInt(u32);207 break :overflow std.math.maxInt(u32);
201 };208 };
202209
203 break :blk os.darwin.__ulock_wait(flags, addr, expect, timeout_us);210 break :blk c.__ulock_wait(flags, addr, expect, timeout_us);
204 };211 };
205212
206 if (status >= 0) return;213 if (status >= 0) return;
207 switch (@as(std.os.E, @enumFromInt(-status))) {214 switch (@as(c.E, @enumFromInt(-status))) {
208 // Wait was interrupted by the OS or other spurious signalling.215 // Wait was interrupted by the OS or other spurious signalling.
209 .INTR => {},216 .INTR => {},
210 // Address of the futex was paged out. This is unlikely, but possible in theory, and217 // Address of the futex was paged out. This is unlikely, but possible in theory, and
...@@ -221,17 +228,17 @@ const DarwinImpl = struct {...@@ -221,17 +228,17 @@ const DarwinImpl = struct {
221 }228 }
222229
223 fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void {230 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;
225 if (max_waiters > 1) {232 if (max_waiters > 1) {
226 flags |= os.darwin.ULF_WAKE_ALL;233 flags |= c.ULF_WAKE_ALL;
227 }234 }
228235
229 while (true) {236 while (true) {
230 const addr: *const anyopaque = ptr;237 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
233 if (status >= 0) return;240 if (status >= 0) return;
234 switch (@as(std.os.E, @enumFromInt(-status))) {241 switch (@as(c.E, @enumFromInt(-status))) {
235 .INTR => continue, // spurious wake()242 .INTR => continue, // spurious wake()
236 .FAULT => unreachable, // __ulock_wake doesn't generate EFAULT according to darwin pthread_cond_t243 .FAULT => unreachable, // __ulock_wake doesn't generate EFAULT according to darwin pthread_cond_t
237 .NOENT => return, // nothing was woken up244 .NOENT => return, // nothing was woken up
...@@ -245,20 +252,20 @@ const DarwinImpl = struct {...@@ -245,20 +252,20 @@ const DarwinImpl = struct {
245// https://man7.org/linux/man-pages/man2/futex.2.html252// https://man7.org/linux/man-pages/man2/futex.2.html
246const LinuxImpl = struct {253const LinuxImpl = struct {
247 fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void {254 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;
249 if (timeout) |timeout_ns| {256 if (timeout) |timeout_ns| {
250 ts.tv_sec = @as(@TypeOf(ts.tv_sec), @intCast(timeout_ns / std.time.ns_per_s));257 ts.tv_sec = @as(@TypeOf(ts.tv_sec), @intCast(timeout_ns / std.time.ns_per_s));
251 ts.tv_nsec = @as(@TypeOf(ts.tv_nsec), @intCast(timeout_ns % std.time.ns_per_s));258 ts.tv_nsec = @as(@TypeOf(ts.tv_nsec), @intCast(timeout_ns % std.time.ns_per_s));
252 }259 }
253260
254 const rc = os.linux.futex_wait(261 const rc = linux.futex_wait(
255 @as(*const i32, @ptrCast(&ptr.raw)),262 @as(*const i32, @ptrCast(&ptr.raw)),
256 os.linux.FUTEX.PRIVATE_FLAG | os.linux.FUTEX.WAIT,263 linux.FUTEX.PRIVATE_FLAG | linux.FUTEX.WAIT,
257 @as(i32, @bitCast(expect)),264 @as(i32, @bitCast(expect)),
258 if (timeout != null) &ts else null,265 if (timeout != null) &ts else null,
259 );266 );
260267
261 switch (os.linux.getErrno(rc)) {268 switch (linux.E.init(rc)) {
262 .SUCCESS => {}, // notified by `wake()`269 .SUCCESS => {}, // notified by `wake()`
263 .INTR => {}, // spurious wakeup270 .INTR => {}, // spurious wakeup
264 .AGAIN => {}, // ptr.* != expect271 .AGAIN => {}, // ptr.* != expect
...@@ -273,13 +280,13 @@ const LinuxImpl = struct {...@@ -273,13 +280,13 @@ const LinuxImpl = struct {
273 }280 }
274281
275 fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void {282 fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void {
276 const rc = os.linux.futex_wake(283 const rc = linux.futex_wake(
277 @as(*const i32, @ptrCast(&ptr.raw)),284 @as(*const i32, @ptrCast(&ptr.raw)),
278 os.linux.FUTEX.PRIVATE_FLAG | os.linux.FUTEX.WAKE,285 linux.FUTEX.PRIVATE_FLAG | linux.FUTEX.WAKE,
279 std.math.cast(i32, max_waiters) orelse std.math.maxInt(i32),286 std.math.cast(i32, max_waiters) orelse std.math.maxInt(i32),
280 );287 );
281288
282 switch (os.linux.getErrno(rc)) {289 switch (linux.E.init(rc)) {
283 .SUCCESS => {}, // successful wake up290 .SUCCESS => {}, // successful wake up
284 .INVAL => {}, // invalid futex_wait() on ptr done elsewhere291 .INVAL => {}, // invalid futex_wait() on ptr done elsewhere
285 .FAULT => {}, // pointer became invalid while doing the wake292 .FAULT => {}, // pointer became invalid while doing the wake
...@@ -292,28 +299,28 @@ const LinuxImpl = struct {...@@ -292,28 +299,28 @@ const LinuxImpl = struct {
292const FreebsdImpl = struct {299const FreebsdImpl = struct {
293 fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void {300 fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void {
294 var tm_size: usize = 0;301 var tm_size: usize = 0;
295 var tm: os.freebsd._umtx_time = undefined;302 var tm: c._umtx_time = undefined;
296 var tm_ptr: ?*const os.freebsd._umtx_time = null;303 var tm_ptr: ?*const c._umtx_time = null;
297304
298 if (timeout) |timeout_ns| {305 if (timeout) |timeout_ns| {
299 tm_ptr = &tm;306 tm_ptr = &tm;
300 tm_size = @sizeOf(@TypeOf(tm));307 tm_size = @sizeOf(@TypeOf(tm));
301308
302 tm._flags = 0; // use relative time not UMTX_ABSTIME309 tm._flags = 0; // use relative time not UMTX_ABSTIME
303 tm._clockid = os.CLOCK.MONOTONIC;310 tm._clockid = c.CLOCK.MONOTONIC;
304 tm._timeout.tv_sec = @as(@TypeOf(tm._timeout.tv_sec), @intCast(timeout_ns / std.time.ns_per_s));311 tm._timeout.tv_sec = @as(@TypeOf(tm._timeout.tv_sec), @intCast(timeout_ns / std.time.ns_per_s));
305 tm._timeout.tv_nsec = @as(@TypeOf(tm._timeout.tv_nsec), @intCast(timeout_ns % std.time.ns_per_s));312 tm._timeout.tv_nsec = @as(@TypeOf(tm._timeout.tv_nsec), @intCast(timeout_ns % std.time.ns_per_s));
306 }313 }
307314
308 const rc = os.freebsd._umtx_op(315 const rc = c._umtx_op(
309 @intFromPtr(&ptr.raw),316 @intFromPtr(&ptr.raw),
310 @intFromEnum(os.freebsd.UMTX_OP.WAIT_UINT_PRIVATE),317 @intFromEnum(c.UMTX_OP.WAIT_UINT_PRIVATE),
311 @as(c_ulong, expect),318 @as(c_ulong, expect),
312 tm_size,319 tm_size,
313 @intFromPtr(tm_ptr),320 @intFromPtr(tm_ptr),
314 );321 );
315322
316 switch (os.errno(rc)) {323 switch (std.posix.errno(rc)) {
317 .SUCCESS => {},324 .SUCCESS => {},
318 .FAULT => unreachable, // one of the args points to invalid memory325 .FAULT => unreachable, // one of the args points to invalid memory
319 .INVAL => unreachable, // arguments should be correct326 .INVAL => unreachable, // arguments should be correct
...@@ -327,15 +334,15 @@ const FreebsdImpl = struct {...@@ -327,15 +334,15 @@ const FreebsdImpl = struct {
327 }334 }
328335
329 fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void {336 fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void {
330 const rc = os.freebsd._umtx_op(337 const rc = c._umtx_op(
331 @intFromPtr(&ptr.raw),338 @intFromPtr(&ptr.raw),
332 @intFromEnum(os.freebsd.UMTX_OP.WAKE_PRIVATE),339 @intFromEnum(c.UMTX_OP.WAKE_PRIVATE),
333 @as(c_ulong, max_waiters),340 @as(c_ulong, max_waiters),
334 0, // there is no timeout struct341 0, // there is no timeout struct
335 0, // there is no timeout struct pointer342 0, // there is no timeout struct pointer
336 );343 );
337344
338 switch (os.errno(rc)) {345 switch (std.posix.errno(rc)) {
339 .SUCCESS => {},346 .SUCCESS => {},
340 .FAULT => {}, // it's ok if the ptr doesn't point to valid memory347 .FAULT => {}, // it's ok if the ptr doesn't point to valid memory
341 .INVAL => unreachable, // arguments should be correct348 .INVAL => unreachable, // arguments should be correct
...@@ -347,21 +354,21 @@ const FreebsdImpl = struct {...@@ -347,21 +354,21 @@ const FreebsdImpl = struct {
347// https://man.openbsd.org/futex.2354// https://man.openbsd.org/futex.2
348const OpenbsdImpl = struct {355const OpenbsdImpl = struct {
349 fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void {356 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;
351 if (timeout) |timeout_ns| {358 if (timeout) |timeout_ns| {
352 ts.tv_sec = @as(@TypeOf(ts.tv_sec), @intCast(timeout_ns / std.time.ns_per_s));359 ts.tv_sec = @as(@TypeOf(ts.tv_sec), @intCast(timeout_ns / std.time.ns_per_s));
353 ts.tv_nsec = @as(@TypeOf(ts.tv_nsec), @intCast(timeout_ns % std.time.ns_per_s));360 ts.tv_nsec = @as(@TypeOf(ts.tv_nsec), @intCast(timeout_ns % std.time.ns_per_s));
354 }361 }
355362
356 const rc = os.openbsd.futex(363 const rc = c.futex(
357 @as(*const volatile u32, @ptrCast(&ptr.raw)),364 @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,
359 @as(c_int, @bitCast(expect)),366 @as(c_int, @bitCast(expect)),
360 if (timeout != null) &ts else null,367 if (timeout != null) &ts else null,
361 null, // FUTEX_WAIT takes no requeue address368 null, // FUTEX_WAIT takes no requeue address
362 );369 );
363370
364 switch (os.errno(rc)) {371 switch (std.posix.errno(rc)) {
365 .SUCCESS => {}, // woken up by wake372 .SUCCESS => {}, // woken up by wake
366 .NOSYS => unreachable, // the futex operation shouldn't be invalid373 .NOSYS => unreachable, // the futex operation shouldn't be invalid
367 .FAULT => unreachable, // ptr was invalid374 .FAULT => unreachable, // ptr was invalid
...@@ -378,9 +385,9 @@ const OpenbsdImpl = struct {...@@ -378,9 +385,9 @@ const OpenbsdImpl = struct {
378 }385 }
379386
380 fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void {387 fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void {
381 const rc = os.openbsd.futex(388 const rc = c.futex(
382 @as(*const volatile u32, @ptrCast(&ptr.raw)),389 @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,
384 std.math.cast(c_int, max_waiters) orelse std.math.maxInt(c_int),391 std.math.cast(c_int, max_waiters) orelse std.math.maxInt(c_int),
385 null, // FUTEX_WAKE takes no timeout ptr392 null, // FUTEX_WAKE takes no timeout ptr
386 null, // FUTEX_WAKE takes no requeue address393 null, // FUTEX_WAKE takes no requeue address
...@@ -415,9 +422,9 @@ const DragonflyImpl = struct {...@@ -415,9 +422,9 @@ const DragonflyImpl = struct {
415422
416 const value = @as(c_int, @bitCast(expect));423 const value = @as(c_int, @bitCast(expect));
417 const addr = @as(*const volatile c_int, @ptrCast(&ptr.raw));424 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)) {
421 .SUCCESS => {},428 .SUCCESS => {},
422 .BUSY => {}, // ptr != expect429 .BUSY => {}, // ptr != expect
423 .AGAIN => { // maybe timed out, or paged out, or hit 2s kernel refresh430 .AGAIN => { // maybe timed out, or paged out, or hit 2s kernel refresh
...@@ -444,7 +451,7 @@ const DragonflyImpl = struct {...@@ -444,7 +451,7 @@ const DragonflyImpl = struct {
444 // > umtx_wakeup() will generally return 0 unless the address is bad.451 // > umtx_wakeup() will generally return 0 unless the address is bad.
445 // We are fine with the address being bad (e.g. for Semaphore.post() where Semaphore.wait() frees the Semaphore)452 // We are fine with the address being bad (e.g. for Semaphore.post() where Semaphore.wait() frees the Semaphore)
446 const addr = @as(*const volatile c_int, @ptrCast(&ptr.raw));453 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);
448 }455 }
449};456};
450457
...@@ -496,8 +503,8 @@ const WasmImpl = struct {...@@ -496,8 +503,8 @@ const WasmImpl = struct {
496/// https://go.dev/src/runtime/sema.go503/// https://go.dev/src/runtime/sema.go
497const PosixImpl = struct {504const PosixImpl = struct {
498 const Event = struct {505 const Event = struct {
499 cond: std.c.pthread_cond_t,506 cond: c.pthread_cond_t,
500 mutex: std.c.pthread_mutex_t,507 mutex: c.pthread_mutex_t,
501 state: enum { empty, waiting, notified },508 state: enum { empty, waiting, notified },
502509
503 fn init(self: *Event) void {510 fn init(self: *Event) void {
...@@ -509,18 +516,18 @@ const PosixImpl = struct {...@@ -509,18 +516,18 @@ const PosixImpl = struct {
509516
510 fn deinit(self: *Event) void {517 fn deinit(self: *Event) void {
511 // Some platforms reportedly give EINVAL for statically initialized pthread types.518 // 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);
513 assert(rc == .SUCCESS or rc == .INVAL);520 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);
516 assert(rm == .SUCCESS or rm == .INVAL);523 assert(rm == .SUCCESS or rm == .INVAL);
517524
518 self.* = undefined;525 self.* = undefined;
519 }526 }
520527
521 fn wait(self: *Event, timeout: ?u64) error{Timeout}!void {528 fn wait(self: *Event, timeout: ?u64) error{Timeout}!void {
522 assert(std.c.pthread_mutex_lock(&self.mutex) == .SUCCESS);529 assert(c.pthread_mutex_lock(&self.mutex) == .SUCCESS);
523 defer assert(std.c.pthread_mutex_unlock(&self.mutex) == .SUCCESS);530 defer assert(c.pthread_mutex_unlock(&self.mutex) == .SUCCESS);
524531
525 // Early return if the event was already set.532 // Early return if the event was already set.
526 if (self.state == .notified) {533 if (self.state == .notified) {
...@@ -530,9 +537,9 @@ const PosixImpl = struct {...@@ -530,9 +537,9 @@ const PosixImpl = struct {
530 // Compute the absolute timeout if one was specified.537 // Compute the absolute timeout if one was specified.
531 // POSIX requires that REALTIME is used by default for the pthread timedwait functions.538 // POSIX requires that REALTIME is used by default for the pthread timedwait functions.
532 // This can be changed with pthread_condattr_setclock, but it's an extension and may not be available everywhere.539 // 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;
534 if (timeout) |timeout_ns| {541 if (timeout) |timeout_ns| {
535 os.clock_gettime(os.CLOCK.REALTIME, &ts) catch unreachable;542 c.clock_gettime(c.CLOCK.REALTIME, &ts) catch unreachable;
536 ts.tv_sec +|= @as(@TypeOf(ts.tv_sec), @intCast(timeout_ns / std.time.ns_per_s));543 ts.tv_sec +|= @as(@TypeOf(ts.tv_sec), @intCast(timeout_ns / std.time.ns_per_s));
537 ts.tv_nsec += @as(@TypeOf(ts.tv_nsec), @intCast(timeout_ns % std.time.ns_per_s));544 ts.tv_nsec += @as(@TypeOf(ts.tv_nsec), @intCast(timeout_ns % std.time.ns_per_s));
538545
...@@ -549,8 +556,8 @@ const PosixImpl = struct {...@@ -549,8 +556,8 @@ const PosixImpl = struct {
549 while (true) {556 while (true) {
550 // Block using either pthread_cond_wait or pthread_cond_timewait if there's an absolute timeout.557 // Block using either pthread_cond_wait or pthread_cond_timewait if there's an absolute timeout.
551 const rc = blk: {558 const rc = blk: {
552 if (timeout == null) break :blk std.c.pthread_cond_wait(&self.cond, &self.mutex);559 if (timeout == null) break :blk c.pthread_cond_wait(&self.cond, &self.mutex);
553 break :blk std.c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts);560 break :blk c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts);
554 };561 };
555562
556 // After waking up, check if the event was set.563 // After waking up, check if the event was set.
...@@ -574,8 +581,8 @@ const PosixImpl = struct {...@@ -574,8 +581,8 @@ const PosixImpl = struct {
574 }581 }
575582
576 fn set(self: *Event) void {583 fn set(self: *Event) void {
577 assert(std.c.pthread_mutex_lock(&self.mutex) == .SUCCESS);584 assert(c.pthread_mutex_lock(&self.mutex) == .SUCCESS);
578 defer assert(std.c.pthread_mutex_unlock(&self.mutex) == .SUCCESS);585 defer assert(c.pthread_mutex_unlock(&self.mutex) == .SUCCESS);
579586
580 // Make sure that multiple calls to set() were not done on the same Event.587 // Make sure that multiple calls to set() were not done on the same Event.
581 const old_state = self.state;588 const old_state = self.state;
...@@ -586,7 +593,7 @@ const PosixImpl = struct {...@@ -586,7 +593,7 @@ const PosixImpl = struct {
586 // the condition variable once it observes the new state, potentially causing a UAF if done unlocked.593 // the condition variable once it observes the new state, potentially causing a UAF if done unlocked.
587 self.state = .notified;594 self.state = .notified;
588 if (old_state == .waiting) {595 if (old_state == .waiting) {
589 assert(std.c.pthread_cond_signal(&self.cond) == .SUCCESS);596 assert(c.pthread_cond_signal(&self.cond) == .SUCCESS);
590 }597 }
591 }598 }
592 };599 };
...@@ -732,7 +739,7 @@ const PosixImpl = struct {...@@ -732,7 +739,7 @@ const PosixImpl = struct {
732 };739 };
733740
734 const Bucket = struct {741 const Bucket = struct {
735 mutex: std.c.pthread_mutex_t align(atomic.cache_line) = .{},742 mutex: c.pthread_mutex_t align(atomic.cache_line) = .{},
736 pending: atomic.Value(usize) = atomic.Value(usize).init(0),743 pending: atomic.Value(usize) = atomic.Value(usize).init(0),
737 treap: Treap = .{},744 treap: Treap = .{},
738745
...@@ -798,8 +805,8 @@ const PosixImpl = struct {...@@ -798,8 +805,8 @@ const PosixImpl = struct {
798805
799 var waiter: Waiter = undefined;806 var waiter: Waiter = undefined;
800 {807 {
801 assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);808 assert(c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);
802 defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);809 defer assert(c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);
803810
804 cancelled = ptr.load(.monotonic) != expect;811 cancelled = ptr.load(.monotonic) != expect;
805 if (cancelled) {812 if (cancelled) {
...@@ -821,8 +828,8 @@ const PosixImpl = struct {...@@ -821,8 +828,8 @@ const PosixImpl = struct {
821 // If we return early without waiting, the waiter on the stack would be invalidated and the wake() thread risks a UAF.828 // If we return early without waiting, the waiter on the stack would be invalidated and the wake() thread risks a UAF.
822 defer if (!cancelled) waiter.event.wait(null) catch unreachable;829 defer if (!cancelled) waiter.event.wait(null) catch unreachable;
823830
824 assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);831 assert(c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);
825 defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);832 defer assert(c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);
826833
827 cancelled = WaitQueue.tryRemove(&bucket.treap, address, &waiter);834 cancelled = WaitQueue.tryRemove(&bucket.treap, address, &waiter);
828 if (cancelled) {835 if (cancelled) {
...@@ -871,8 +878,8 @@ const PosixImpl = struct {...@@ -871,8 +878,8 @@ const PosixImpl = struct {
871 }878 }
872 };879 };
873880
874 assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);881 assert(c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS);
875 defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);882 defer assert(c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS);
876883
877 // Another pending check again to avoid the WaitQueue lookup if not necessary.884 // Another pending check again to avoid the WaitQueue lookup if not necessary.
878 if (bucket.pending.load(.monotonic) > 0) {885 if (bucket.pending.load(.monotonic) > 0) {
lib/std/Thread/Mutex.zig+12-9
...@@ -23,7 +23,6 @@ const std = @import("../std.zig");...@@ -23,7 +23,6 @@ const std = @import("../std.zig");
23const builtin = @import("builtin");23const builtin = @import("builtin");
24const Mutex = @This();24const Mutex = @This();
2525
26const os = std.os;
27const assert = std.debug.assert;26const assert = std.debug.assert;
28const testing = std.testing;27const testing = std.testing;
29const Thread = std.Thread;28const Thread = std.Thread;
...@@ -117,36 +116,40 @@ const SingleThreadedImpl = struct {...@@ -117,36 +116,40 @@ const SingleThreadedImpl = struct {
117// SRWLOCK on windows is almost always faster than Futex solution.116// SRWLOCK on windows is almost always faster than Futex solution.
118// It also implements an efficient Condition with requeue support for us.117// It also implements an efficient Condition with requeue support for us.
119const WindowsImpl = struct {118const WindowsImpl = struct {
120 srwlock: os.windows.SRWLOCK = .{},119 srwlock: windows.SRWLOCK = .{},
121120
122 fn tryLock(self: *@This()) bool {121 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;
124 }123 }
125124
126 fn lock(self: *@This()) void {125 fn lock(self: *@This()) void {
127 os.windows.kernel32.AcquireSRWLockExclusive(&self.srwlock);126 windows.kernel32.AcquireSRWLockExclusive(&self.srwlock);
128 }127 }
129128
130 fn unlock(self: *@This()) void {129 fn unlock(self: *@This()) void {
131 os.windows.kernel32.ReleaseSRWLockExclusive(&self.srwlock);130 windows.kernel32.ReleaseSRWLockExclusive(&self.srwlock);
132 }131 }
132
133 const windows = std.os.windows;
133};134};
134135
135// os_unfair_lock on darwin supports priority inheritance and is generally faster than Futex solutions.136// os_unfair_lock on darwin supports priority inheritance and is generally faster than Futex solutions.
136const DarwinImpl = struct {137const DarwinImpl = struct {
137 oul: os.darwin.os_unfair_lock = .{},138 oul: c.os_unfair_lock = .{},
138139
139 fn tryLock(self: *@This()) bool {140 fn tryLock(self: *@This()) bool {
140 return os.darwin.os_unfair_lock_trylock(&self.oul);141 return c.os_unfair_lock_trylock(&self.oul);
141 }142 }
142143
143 fn lock(self: *@This()) void {144 fn lock(self: *@This()) void {
144 os.darwin.os_unfair_lock_lock(&self.oul);145 c.os_unfair_lock_lock(&self.oul);
145 }146 }
146147
147 fn unlock(self: *@This()) void {148 fn unlock(self: *@This()) void {
148 os.darwin.os_unfair_lock_unlock(&self.oul);149 c.os_unfair_lock_unlock(&self.oul);
149 }150 }
151
152 const c = std.c;
150};153};
151154
152const FutexImpl = struct {155const 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...@@ -782,7 +782,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr
782 },782 },
783 .wasi => {783 .wasi => {
784 std.debug.print("{s}", .{msg});784 std.debug.print("{s}", .{msg});
785 std.os.abort();785 std.posix.abort();
786 },786 },
787 .uefi => {787 .uefi => {
788 const uefi = std.os.uefi;788 const uefi = std.os.uefi;
...@@ -830,9 +830,9 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr...@@ -830,9 +830,9 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr
830 }830 }
831831
832 // Didn't have boot_services, just fallback to whatever.832 // Didn't have boot_services, just fallback to whatever.
833 std.os.abort();833 std.posix.abort();
834 },834 },
835 .cuda, .amdhsa => std.os.abort(),835 .cuda, .amdhsa => std.posix.abort(),
836 .plan9 => {836 .plan9 => {
837 var status: [std.os.plan9.ERRMAX]u8 = undefined;837 var status: [std.os.plan9.ERRMAX]u8 = undefined;
838 const len = @min(msg.len, status.len - 1);838 const len = @min(msg.len, status.len - 1);
lib/std/c.zig+25-32
...@@ -2,12 +2,13 @@ const std = @import("std");...@@ -2,12 +2,13 @@ const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const c = @This();3const c = @This();
4const page_size = std.mem.page_size;4const page_size = std.mem.page_size;
5const iovec = std.os.iovec;5const iovec = std.posix.iovec;
6const iovec_const = std.os.iovec_const;6const iovec_const = std.posix.iovec_const;
7const wasi = @import("c/wasi.zig");7const wasi = @import("c/wasi.zig");
8const native_abi = builtin.abi;8const native_abi = builtin.abi;
9const native_arch = builtin.cpu.arch;9const native_arch = builtin.cpu.arch;
10const native_os = builtin.os.tag;10const native_os = builtin.os.tag;
11const linux = std.os.linux;
1112
12/// If not linking libc, returns false.13/// If not linking libc, returns false.
13/// If linking musl libc, returns true.14/// If linking musl libc, returns true.
...@@ -208,7 +209,7 @@ pub const pthread_rwlock_t = switch (native_os) {...@@ -208,7 +209,7 @@ pub const pthread_rwlock_t = switch (native_os) {
208};209};
209210
210pub const AT = switch (native_os) {211pub const AT = switch (native_os) {
211 .linux => std.os.linux.AT,212 .linux => linux.AT,
212 .windows => struct {213 .windows => struct {
213 /// Remove directory instead of unlinking file214 /// Remove directory instead of unlinking file
214 pub const REMOVEDIR = 0x200;215 pub const REMOVEDIR = 0x200;
...@@ -326,9 +327,9 @@ pub const AT = switch (native_os) {...@@ -326,9 +327,9 @@ pub const AT = switch (native_os) {
326};327};
327328
328pub const O = switch (native_os) {329pub const O = switch (native_os) {
329 .linux => std.os.linux.O,330 .linux => linux.O,
330 .emscripten => packed struct(u32) {331 .emscripten => packed struct(u32) {
331 ACCMODE: std.os.ACCMODE = .RDONLY,332 ACCMODE: std.posix.ACCMODE = .RDONLY,
332 _2: u4 = 0,333 _2: u4 = 0,
333 CREAT: bool = false,334 CREAT: bool = false,
334 EXCL: bool = false,335 EXCL: bool = false,
...@@ -369,7 +370,7 @@ pub const O = switch (native_os) {...@@ -369,7 +370,7 @@ pub const O = switch (native_os) {
369 _: u3 = 0,370 _: u3 = 0,
370 },371 },
371 .solaris, .illumos => packed struct(u32) {372 .solaris, .illumos => packed struct(u32) {
372 ACCMODE: std.os.ACCMODE = .RDONLY,373 ACCMODE: std.posix.ACCMODE = .RDONLY,
373 NDELAY: bool = false,374 NDELAY: bool = false,
374 APPEND: bool = false,375 APPEND: bool = false,
375 SYNC: bool = false,376 SYNC: bool = false,
...@@ -396,7 +397,7 @@ pub const O = switch (native_os) {...@@ -396,7 +397,7 @@ pub const O = switch (native_os) {
396 _: u6 = 0,397 _: u6 = 0,
397 },398 },
398 .netbsd => packed struct(u32) {399 .netbsd => packed struct(u32) {
399 ACCMODE: std.os.ACCMODE = .RDONLY,400 ACCMODE: std.posix.ACCMODE = .RDONLY,
400 NONBLOCK: bool = false,401 NONBLOCK: bool = false,
401 APPEND: bool = false,402 APPEND: bool = false,
402 SHLOCK: bool = false,403 SHLOCK: bool = false,
...@@ -420,7 +421,7 @@ pub const O = switch (native_os) {...@@ -420,7 +421,7 @@ pub const O = switch (native_os) {
420 _: u8 = 0,421 _: u8 = 0,
421 },422 },
422 .openbsd => packed struct(u32) {423 .openbsd => packed struct(u32) {
423 ACCMODE: std.os.ACCMODE = .RDONLY,424 ACCMODE: std.posix.ACCMODE = .RDONLY,
424 NONBLOCK: bool = false,425 NONBLOCK: bool = false,
425 APPEND: bool = false,426 APPEND: bool = false,
426 SHLOCK: bool = false,427 SHLOCK: bool = false,
...@@ -438,7 +439,7 @@ pub const O = switch (native_os) {...@@ -438,7 +439,7 @@ pub const O = switch (native_os) {
438 _: u14 = 0,439 _: u14 = 0,
439 },440 },
440 .haiku => packed struct(u32) {441 .haiku => packed struct(u32) {
441 ACCMODE: std.os.ACCMODE = .RDONLY,442 ACCMODE: std.posix.ACCMODE = .RDONLY,
442 _2: u4 = 0,443 _2: u4 = 0,
443 CLOEXEC: bool = false,444 CLOEXEC: bool = false,
444 NONBLOCK: bool = false,445 NONBLOCK: bool = false,
...@@ -458,7 +459,7 @@ pub const O = switch (native_os) {...@@ -458,7 +459,7 @@ pub const O = switch (native_os) {
458 _: u10 = 0,459 _: u10 = 0,
459 },460 },
460 .macos, .ios, .tvos, .watchos => packed struct(u32) {461 .macos, .ios, .tvos, .watchos => packed struct(u32) {
461 ACCMODE: std.os.ACCMODE = .RDONLY,462 ACCMODE: std.posix.ACCMODE = .RDONLY,
462 NONBLOCK: bool = false,463 NONBLOCK: bool = false,
463 APPEND: bool = false,464 APPEND: bool = false,
464 SHLOCK: bool = false,465 SHLOCK: bool = false,
...@@ -485,7 +486,7 @@ pub const O = switch (native_os) {...@@ -485,7 +486,7 @@ pub const O = switch (native_os) {
485 POPUP: bool = false,486 POPUP: bool = false,
486 },487 },
487 .dragonfly => packed struct(u32) {488 .dragonfly => packed struct(u32) {
488 ACCMODE: std.os.ACCMODE = .RDONLY,489 ACCMODE: std.posix.ACCMODE = .RDONLY,
489 NONBLOCK: bool = false,490 NONBLOCK: bool = false,
490 APPEND: bool = false,491 APPEND: bool = false,
491 SHLOCK: bool = false,492 SHLOCK: bool = false,
...@@ -511,7 +512,7 @@ pub const O = switch (native_os) {...@@ -511,7 +512,7 @@ pub const O = switch (native_os) {
511 _: u4 = 0,512 _: u4 = 0,
512 },513 },
513 .freebsd => packed struct(u32) {514 .freebsd => packed struct(u32) {
514 ACCMODE: std.os.ACCMODE = .RDONLY,515 ACCMODE: std.posix.ACCMODE = .RDONLY,
515 NONBLOCK: bool = false,516 NONBLOCK: bool = false,
516 APPEND: bool = false,517 APPEND: bool = false,
517 SHLOCK: bool = false,518 SHLOCK: bool = false,
...@@ -538,7 +539,7 @@ pub const O = switch (native_os) {...@@ -538,7 +539,7 @@ pub const O = switch (native_os) {
538};539};
539540
540pub const MAP = switch (native_os) {541pub const MAP = switch (native_os) {
541 .linux => std.os.linux.MAP,542 .linux => linux.MAP,
542 .emscripten => packed struct(u32) {543 .emscripten => packed struct(u32) {
543 TYPE: enum(u4) {544 TYPE: enum(u4) {
544 SHARED = 0x01,545 SHARED = 0x01,
...@@ -683,7 +684,7 @@ pub const cc_t = u8;...@@ -683,7 +684,7 @@ pub const cc_t = u8;
683684
684/// Indices into the `cc` array in the `termios` struct.685/// Indices into the `cc` array in the `termios` struct.
685pub const V = switch (native_os) {686pub const V = switch (native_os) {
686 .linux => std.os.linux.V,687 .linux => linux.V,
687 .macos, .ios, .tvos, .watchos, .netbsd, .openbsd => enum {688 .macos, .ios, .tvos, .watchos, .netbsd, .openbsd => enum {
688 EOF,689 EOF,
689 EOL,690 EOL,
...@@ -782,7 +783,7 @@ pub const V = switch (native_os) {...@@ -782,7 +783,7 @@ pub const V = switch (native_os) {
782};783};
783784
784pub const NCCS = switch (native_os) {785pub const NCCS = switch (native_os) {
785 .linux => std.os.linux.NCCS,786 .linux => linux.NCCS,
786 .macos, .ios, .tvos, .watchos, .freebsd, .kfreebsd, .netbsd, .openbsd, .dragonfly => 20,787 .macos, .ios, .tvos, .watchos, .freebsd, .kfreebsd, .netbsd, .openbsd, .dragonfly => 20,
787 .haiku => 11,788 .haiku => 11,
788 .solaris, .illumos => 19,789 .solaris, .illumos => 19,
...@@ -791,7 +792,7 @@ pub const NCCS = switch (native_os) {...@@ -791,7 +792,7 @@ pub const NCCS = switch (native_os) {
791};792};
792793
793pub const termios = switch (native_os) {794pub const termios = switch (native_os) {
794 .linux => std.os.linux.termios,795 .linux => linux.termios,
795 .macos, .ios, .tvos, .watchos => extern struct {796 .macos, .ios, .tvos, .watchos => extern struct {
796 iflag: tc_iflag_t,797 iflag: tc_iflag_t,
797 oflag: tc_oflag_t,798 oflag: tc_oflag_t,
...@@ -841,7 +842,7 @@ pub const termios = switch (native_os) {...@@ -841,7 +842,7 @@ pub const termios = switch (native_os) {
841};842};
842843
843pub const tc_iflag_t = switch (native_os) {844pub const tc_iflag_t = switch (native_os) {
844 .linux => std.os.linux.tc_iflag_t,845 .linux => linux.tc_iflag_t,
845 .macos, .ios, .tvos, .watchos => packed struct(u64) {846 .macos, .ios, .tvos, .watchos => packed struct(u64) {
846 IGNBRK: bool = false,847 IGNBRK: bool = false,
847 BRKINT: bool = false,848 BRKINT: bool = false,
...@@ -951,7 +952,7 @@ pub const tc_iflag_t = switch (native_os) {...@@ -951,7 +952,7 @@ pub const tc_iflag_t = switch (native_os) {
951};952};
952953
953pub const tc_oflag_t = switch (native_os) {954pub const tc_oflag_t = switch (native_os) {
954 .linux => std.os.linux.tc_oflag_t,955 .linux => linux.tc_oflag_t,
955 .macos, .ios, .tvos, .watchos => packed struct(u64) {956 .macos, .ios, .tvos, .watchos => packed struct(u64) {
956 OPOST: bool = false,957 OPOST: bool = false,
957 ONLCR: bool = false,958 ONLCR: bool = false,
...@@ -1042,13 +1043,13 @@ pub const tc_oflag_t = switch (native_os) {...@@ -1042,13 +1043,13 @@ pub const tc_oflag_t = switch (native_os) {
1042};1043};
10431044
1044pub const CSIZE = switch (native_os) {1045pub const CSIZE = switch (native_os) {
1045 .linux => std.os.linux.CSIZE,1046 .linux => linux.CSIZE,
1046 .haiku => enum(u1) { CS7, CS8 },1047 .haiku => enum(u1) { CS7, CS8 },
1047 else => enum(u2) { CS5, CS6, CS7, CS8 },1048 else => enum(u2) { CS5, CS6, CS7, CS8 },
1048};1049};
10491050
1050pub const tc_cflag_t = switch (native_os) {1051pub const tc_cflag_t = switch (native_os) {
1051 .linux => std.os.linux.tc_cflag_t,1052 .linux => linux.tc_cflag_t,
1052 .macos, .ios, .tvos, .watchos => packed struct(u64) {1053 .macos, .ios, .tvos, .watchos => packed struct(u64) {
1053 CIGNORE: bool = false,1054 CIGNORE: bool = false,
1054 _1: u5 = 0,1055 _1: u5 = 0,
...@@ -1184,7 +1185,7 @@ pub const tc_cflag_t = switch (native_os) {...@@ -1184,7 +1185,7 @@ pub const tc_cflag_t = switch (native_os) {
1184};1185};
11851186
1186pub const tc_lflag_t = switch (native_os) {1187pub const tc_lflag_t = switch (native_os) {
1187 .linux => std.os.linux.tc_lflag_t,1188 .linux => linux.tc_lflag_t,
1188 .macos, .ios, .tvos, .watchos => packed struct(u64) {1189 .macos, .ios, .tvos, .watchos => packed struct(u64) {
1189 ECHOKE: bool = false,1190 ECHOKE: bool = false,
1190 ECHOE: bool = false,1191 ECHOE: bool = false,
...@@ -1310,7 +1311,7 @@ pub const tc_lflag_t = switch (native_os) {...@@ -1310,7 +1311,7 @@ pub const tc_lflag_t = switch (native_os) {
1310};1311};
13111312
1312pub const speed_t = switch (native_os) {1313pub const speed_t = switch (native_os) {
1313 .linux => std.os.linux.speed_t,1314 .linux => linux.speed_t,
1314 .macos, .ios, .tvos, .watchos, .openbsd => enum(u64) {1315 .macos, .ios, .tvos, .watchos, .openbsd => enum(u64) {
1315 B0 = 0,1316 B0 = 0,
1316 B50 = 50,1317 B50 = 50,
...@@ -1605,14 +1606,6 @@ pub const stat = switch (native_os) {...@@ -1605,14 +1606,6 @@ pub const stat = switch (native_os) {
1605 else => private.stat,1606 else => private.stat,
1606};1607};
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
1616pub extern "c" var environ: [*:null]?[*:0]u8;1609pub extern "c" var environ: [*:null]?[*:0]u8;
16171610
1618pub extern "c" fn fopen(noalias filename: [*:0]const u8, noalias modes: [*:0]const u8) ?*FILE;1611pub 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;...@@ -1905,10 +1898,10 @@ pub extern "c" fn if_nametoindex([*:0]const u8) c_int;
1905pub const getcontext = if (builtin.target.isAndroid())1898pub const getcontext = if (builtin.target.isAndroid())
1906 @compileError("android bionic libc does not implement getcontext")1899 @compileError("android bionic libc does not implement getcontext")
1907else if (native_os == .linux and builtin.target.isMusl())1900else if (native_os == .linux and builtin.target.isMusl())
1908 std.os.linux.getcontext1901 linux.getcontext
1909else1902else
1910 struct {1903 struct {
1911 extern fn getcontext(ucp: *std.os.ucontext_t) c_int;1904 extern fn getcontext(ucp: *std.posix.ucontext_t) c_int;
1912 }.getcontext;1905 }.getcontext;
19131906
1914pub const max_align_t = if (native_abi == .msvc)1907pub const max_align_t = if (native_abi == .msvc)
lib/std/c/darwin.zig+7-232
...@@ -4,7 +4,7 @@ const assert = std.debug.assert;...@@ -4,7 +4,7 @@ const assert = std.debug.assert;
4const macho = std.macho;4const macho = std.macho;
5const native_arch = builtin.target.cpu.arch;5const native_arch = builtin.target.cpu.arch;
6const maxInt = std.math.maxInt;6const maxInt = std.math.maxInt;
7const iovec_const = std.os.iovec_const;7const iovec_const = std.posix.iovec_const;
88
9pub const aarch64 = @import("darwin/aarch64.zig");9pub const aarch64 = @import("darwin/aarch64.zig");
10pub const x86_64 = @import("darwin/x86_64.zig");10pub const x86_64 = @import("darwin/x86_64.zig");
...@@ -2826,237 +2826,12 @@ pub extern "c" fn posix_spawnp(...@@ -2826,237 +2826,12 @@ pub extern "c" fn posix_spawnp(
2826 env: [*:null]?[*:0]const u8,2826 env: [*:null]?[*:0]const u8,
2827) c_int;2827) 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
3054pub fn getKernError(err: kern_return_t) KernE {2829pub fn getKernError(err: kern_return_t) KernE {
3055 return @as(KernE, @enumFromInt(@as(u32, @truncate(@as(usize, @intCast(err))))));2830 return @as(KernE, @enumFromInt(@as(u32, @truncate(@as(usize, @intCast(err))))));
3056}2831}
30572832
3058pub fn unexpectedKernError(err: KernE) std.os.UnexpectedError {2833pub fn unexpectedKernError(err: KernE) std.posix.UnexpectedError {
3059 if (std.os.unexpected_error_tracing) {2834 if (std.posix.unexpected_error_tracing) {
3060 std.debug.print("unexpected error: {d}\n", .{@intFromEnum(err)});2835 std.debug.print("unexpected error: {d}\n", .{@intFromEnum(err)});
3061 std.debug.dumpCurrentStackTrace(null);2836 std.debug.dumpCurrentStackTrace(null);
3062 }2837 }
...@@ -3067,7 +2842,7 @@ pub const MachError = error{...@@ -3067,7 +2842,7 @@ pub const MachError = error{
3067 /// Not enough permissions held to perform the requested kernel2842 /// Not enough permissions held to perform the requested kernel
3068 /// call.2843 /// call.
3069 PermissionDenied,2844 PermissionDenied,
3070} || std.os.UnexpectedError;2845} || std.posix.UnexpectedError;
30712846
3072pub const MachTask = extern struct {2847pub const MachTask = extern struct {
3073 port: mach_port_name_t,2848 port: mach_port_name_t,
...@@ -3076,8 +2851,8 @@ pub const MachTask = extern struct {...@@ -3076,8 +2851,8 @@ pub const MachTask = extern struct {
3076 return self.port != TASK_NULL;2851 return self.port != TASK_NULL;
3077 }2852 }
30782853
3079 pub fn pidForTask(self: MachTask) MachError!std.os.pid_t {2854 pub fn pidForTask(self: MachTask) MachError!std.c.pid_t {
3080 var pid: std.os.pid_t = undefined;2855 var pid: std.c.pid_t = undefined;
3081 switch (getKernError(pid_for_task(self.port, &pid))) {2856 switch (getKernError(pid_for_task(self.port, &pid))) {
3082 .SUCCESS => return pid,2857 .SUCCESS => return pid,
3083 .FAILURE => return error.PermissionDenied,2858 .FAILURE => return error.PermissionDenied,
...@@ -3517,7 +3292,7 @@ pub const MachThread = extern struct {...@@ -3517,7 +3292,7 @@ pub const MachThread = extern struct {
3517 }3292 }
3518};3293};
35193294
3520pub fn machTaskForPid(pid: std.os.pid_t) MachError!MachTask {3295pub fn machTaskForPid(pid: std.c.pid_t) MachError!MachTask {
3521 var port: mach_port_name_t = undefined;3296 var port: mach_port_name_t = undefined;
3522 switch (getKernError(task_for_pid(mach_task_self(), pid, &port))) {3297 switch (getKernError(task_for_pid(mach_task_self(), pid, &port))) {
3523 .SUCCESS => {},3298 .SUCCESS => {},
lib/std/c/dragonfly.zig+1-1
...@@ -2,7 +2,7 @@ const builtin = @import("builtin");...@@ -2,7 +2,7 @@ const builtin = @import("builtin");
2const std = @import("../std.zig");2const std = @import("../std.zig");
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const maxInt = std.math.maxInt;4const maxInt = std.math.maxInt;
5const iovec = std.os.iovec;5const iovec = std.posix.iovec;
66
7extern "c" threadlocal var errno: c_int;7extern "c" threadlocal var errno: c_int;
8pub fn _errno() *c_int {8pub fn _errno() *c_int {
lib/std/c/freebsd.zig+2-2
...@@ -2,8 +2,8 @@ const std = @import("../std.zig");...@@ -2,8 +2,8 @@ const std = @import("../std.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const builtin = @import("builtin");3const builtin = @import("builtin");
4const maxInt = std.math.maxInt;4const maxInt = std.math.maxInt;
5const iovec = std.os.iovec;5const iovec = std.posix.iovec;
6const iovec_const = std.os.iovec_const;6const iovec_const = std.posix.iovec_const;
77
8extern "c" fn __error() *c_int;8extern "c" fn __error() *c_int;
9pub const _errno = __error;9pub const _errno = __error;
lib/std/c/haiku.zig+2-2
...@@ -2,8 +2,8 @@ const std = @import("../std.zig");...@@ -2,8 +2,8 @@ const std = @import("../std.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const builtin = @import("builtin");3const builtin = @import("builtin");
4const maxInt = std.math.maxInt;4const maxInt = std.math.maxInt;
5const iovec = std.os.iovec;5const iovec = std.posix.iovec;
6const iovec_const = std.os.iovec_const;6const iovec_const = std.posix.iovec_const;
77
8extern "c" fn _errnop() *c_int;8extern "c" fn _errnop() *c_int;
99
lib/std/c/linux.zig+2-2
...@@ -3,8 +3,8 @@ const builtin = @import("builtin");...@@ -3,8 +3,8 @@ const builtin = @import("builtin");
3const native_abi = builtin.abi;3const native_abi = builtin.abi;
4const native_arch = builtin.cpu.arch;4const native_arch = builtin.cpu.arch;
5const linux = std.os.linux;5const linux = std.os.linux;
6const iovec = std.os.iovec;6const iovec = std.posix.iovec;
7const iovec_const = std.os.iovec_const;7const iovec_const = std.posix.iovec_const;
8const FILE = std.c.FILE;8const FILE = std.c.FILE;
99
10pub const AF = linux.AF;10pub const AF = linux.AF;
lib/std/c/netbsd.zig+2-2
...@@ -2,8 +2,8 @@ const std = @import("../std.zig");...@@ -2,8 +2,8 @@ const std = @import("../std.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const builtin = @import("builtin");3const builtin = @import("builtin");
4const maxInt = std.math.maxInt;4const maxInt = std.math.maxInt;
5const iovec = std.os.iovec;5const iovec = std.posix.iovec;
6const iovec_const = std.os.iovec_const;6const iovec_const = std.posix.iovec_const;
7const timezone = std.c.timezone;7const timezone = std.c.timezone;
8const rusage = std.c.rusage;8const rusage = std.c.rusage;
99
lib/std/c/openbsd.zig+2-2
...@@ -2,8 +2,8 @@ const std = @import("../std.zig");...@@ -2,8 +2,8 @@ const std = @import("../std.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const maxInt = std.math.maxInt;3const maxInt = std.math.maxInt;
4const builtin = @import("builtin");4const builtin = @import("builtin");
5const iovec = std.os.iovec;5const iovec = std.posix.iovec;
6const iovec_const = std.os.iovec_const;6const iovec_const = std.posix.iovec_const;
77
8extern "c" fn __errno() *c_int;8extern "c" fn __errno() *c_int;
9pub const _errno = __errno;9pub const _errno = __errno;
lib/std/c/solaris.zig+2-2
...@@ -2,8 +2,8 @@ const std = @import("../std.zig");...@@ -2,8 +2,8 @@ const std = @import("../std.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const builtin = @import("builtin");3const builtin = @import("builtin");
4const maxInt = std.math.maxInt;4const maxInt = std.math.maxInt;
5const iovec = std.os.iovec;5const iovec = std.posix.iovec;
6const iovec_const = std.os.iovec_const;6const iovec_const = std.posix.iovec_const;
7const timezone = std.c.timezone;7const timezone = std.c.timezone;
88
9extern "c" fn ___errno() *c_int;9extern "c" fn ___errno() *c_int;
lib/std/c/wasi.zig+2
...@@ -8,6 +8,8 @@ pub fn _errno() *c_int {...@@ -8,6 +8,8 @@ pub fn _errno() *c_int {
8 return &errno;8 return &errno;
9}9}
1010
11pub const PATH_MAX = 4096;
12
11pub const mode_t = u32;13pub const mode_t = u32;
12pub const time_t = i64;14pub const time_t = i64;
1315
lib/std/child_process.zig+91-90
...@@ -3,30 +3,31 @@ const builtin = @import("builtin");...@@ -3,30 +3,31 @@ const builtin = @import("builtin");
3const unicode = std.unicode;3const unicode = std.unicode;
4const io = std.io;4const io = std.io;
5const fs = std.fs;5const fs = std.fs;
6const os = std.os;
7const process = std.process;6const process = std.process;
8const File = std.fs.File;7const File = std.fs.File;
9const windows = os.windows;8const windows = std.os.windows;
10const linux = os.linux;9const linux = std.os.linux;
10const posix = std.posix;
11const mem = std.mem;11const mem = std.mem;
12const math = std.math;12const math = std.math;
13const debug = std.debug;13const debug = std.debug;
14const EnvMap = process.EnvMap;14const EnvMap = process.EnvMap;
15const maxInt = std.math.maxInt;15const maxInt = std.math.maxInt;
16const assert = std.debug.assert;16const assert = std.debug.assert;
17const native_os = builtin.os.tag;
1718
18pub const ChildProcess = struct {19pub const ChildProcess = struct {
19 pub const Id = switch (builtin.os.tag) {20 pub const Id = switch (native_os) {
20 .windows => windows.HANDLE,21 .windows => windows.HANDLE,
21 .wasi => void,22 .wasi => void,
22 else => os.pid_t,23 else => posix.pid_t,
23 };24 };
2425
25 /// Available after calling `spawn()`. This becomes `undefined` after calling `wait()`.26 /// Available after calling `spawn()`. This becomes `undefined` after calling `wait()`.
26 /// On Windows this is the hProcess.27 /// On Windows this is the hProcess.
27 /// On POSIX this is the pid.28 /// On POSIX this is the pid.
28 id: Id,29 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
31 allocator: mem.Allocator,32 allocator: mem.Allocator,
3233
...@@ -46,10 +47,10 @@ pub const ChildProcess = struct {...@@ -46,10 +47,10 @@ pub const ChildProcess = struct {
46 stderr_behavior: StdIo,47 stderr_behavior: StdIo,
4748
48 /// Set to change the user id when spawning the child process.49 /// 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
51 /// Set to change the group id when spawning the child process.52 /// 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
54 /// Set to change the current working directory when spawning the child process.55 /// Set to change the current working directory when spawning the child process.
55 cwd: ?[]const u8,56 cwd: ?[]const u8,
...@@ -58,7 +59,7 @@ pub const ChildProcess = struct {...@@ -58,7 +59,7 @@ pub const ChildProcess = struct {
58 /// Once that is done, `cwd` will be deprecated in favor of this field.59 /// Once that is done, `cwd` will be deprecated in favor of this field.
59 cwd_dir: ?fs.Dir = null,60 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
63 expand_arg0: Arg0Expand,64 expand_arg0: Arg0Expand,
6465
...@@ -87,7 +88,7 @@ pub const ChildProcess = struct {...@@ -87,7 +88,7 @@ pub const ChildProcess = struct {
87 /// Returns the peak resident set size of the child process, in bytes,88 /// Returns the peak resident set size of the child process, in bytes,
88 /// if available.89 /// if available.
89 pub inline fn getMaxRss(rus: ResourceUsageStatistics) ?usize {90 pub inline fn getMaxRss(rus: ResourceUsageStatistics) ?usize {
90 switch (builtin.os.tag) {91 switch (native_os) {
91 .linux => {92 .linux => {
92 if (rus.rusage) |ru| {93 if (rus.rusage) |ru| {
93 return @as(usize, @intCast(ru.maxrss)) * 1024;94 return @as(usize, @intCast(ru.maxrss)) * 1024;
...@@ -114,14 +115,14 @@ pub const ChildProcess = struct {...@@ -114,14 +115,14 @@ pub const ChildProcess = struct {
114 }115 }
115 }116 }
116117
117 const rusage_init = switch (builtin.os.tag) {118 const rusage_init = switch (native_os) {
118 .linux, .macos, .ios => @as(?std.os.rusage, null),119 .linux, .macos, .ios => @as(?posix.rusage, null),
119 .windows => @as(?windows.VM_COUNTERS, null),120 .windows => @as(?windows.VM_COUNTERS, null),
120 else => {},121 else => {},
121 };122 };
122 };123 };
123124
124 pub const Arg0Expand = os.Arg0Expand;125 pub const Arg0Expand = posix.Arg0Expand;
125126
126 pub const SpawnError = error{127 pub const SpawnError = error{
127 OutOfMemory,128 OutOfMemory,
...@@ -136,9 +137,9 @@ pub const ChildProcess = struct {...@@ -136,9 +137,9 @@ pub const ChildProcess = struct {
136 /// Windows-only. `cwd` was provided, but the path did not exist when spawning the child process.137 /// Windows-only. `cwd` was provided, but the path did not exist when spawning the child process.
137 CurrentWorkingDirectoryUnlinked,138 CurrentWorkingDirectoryUnlinked,
138 } ||139 } ||
139 os.ExecveError ||140 posix.ExecveError ||
140 os.SetIdError ||141 posix.SetIdError ||
141 os.ChangeCurDirError ||142 posix.ChangeCurDirError ||
142 windows.CreateProcessError ||143 windows.CreateProcessError ||
143 windows.GetProcessMemoryInfoError ||144 windows.GetProcessMemoryInfoError ||
144 windows.WaitForSingleObjectError;145 windows.WaitForSingleObjectError;
...@@ -168,8 +169,8 @@ pub const ChildProcess = struct {...@@ -168,8 +169,8 @@ pub const ChildProcess = struct {
168 .term = null,169 .term = null,
169 .env_map = null,170 .env_map = null,
170 .cwd = null,171 .cwd = null,
171 .uid = if (builtin.os.tag == .windows or builtin.os.tag == .wasi) {} else null,172 .uid = if (native_os == .windows or native_os == .wasi) {} else null,
172 .gid = if (builtin.os.tag == .windows or builtin.os.tag == .wasi) {} else null,173 .gid = if (native_os == .windows or native_os == .wasi) {} else null,
173 .stdin = null,174 .stdin = null,
174 .stdout = null,175 .stdout = null,
175 .stderr = null,176 .stderr = null,
...@@ -193,7 +194,7 @@ pub const ChildProcess = struct {...@@ -193,7 +194,7 @@ pub const ChildProcess = struct {
193 @compileError("the target operating system cannot spawn processes");194 @compileError("the target operating system cannot spawn processes");
194 }195 }
195196
196 if (builtin.os.tag == .windows) {197 if (native_os == .windows) {
197 return self.spawnWindows();198 return self.spawnWindows();
198 } else {199 } else {
199 return self.spawnPosix();200 return self.spawnPosix();
...@@ -207,7 +208,7 @@ pub const ChildProcess = struct {...@@ -207,7 +208,7 @@ pub const ChildProcess = struct {
207208
208 /// Forcibly terminates child process and then cleans up all resources.209 /// Forcibly terminates child process and then cleans up all resources.
209 pub fn kill(self: *ChildProcess) !Term {210 pub fn kill(self: *ChildProcess) !Term {
210 if (builtin.os.tag == .windows) {211 if (native_os == .windows) {
211 return self.killWindows(1);212 return self.killWindows(1);
212 } else {213 } else {
213 return self.killPosix();214 return self.killPosix();
...@@ -241,7 +242,7 @@ pub const ChildProcess = struct {...@@ -241,7 +242,7 @@ pub const ChildProcess = struct {
241 self.cleanupStreams();242 self.cleanupStreams();
242 return term;243 return term;
243 }244 }
244 os.kill(self.id, os.SIG.TERM) catch |err| switch (err) {245 posix.kill(self.id, posix.SIG.TERM) catch |err| switch (err) {
245 error.ProcessNotFound => return error.AlreadyTerminated,246 error.ProcessNotFound => return error.AlreadyTerminated,
246 else => return err,247 else => return err,
247 };248 };
...@@ -251,7 +252,7 @@ pub const ChildProcess = struct {...@@ -251,7 +252,7 @@ pub const ChildProcess = struct {
251252
252 /// Blocks until child process terminates and then cleans up all resources.253 /// Blocks until child process terminates and then cleans up all resources.
253 pub fn wait(self: *ChildProcess) !Term {254 pub fn wait(self: *ChildProcess) !Term {
254 const term = if (builtin.os.tag == .windows)255 const term = if (native_os == .windows)
255 try self.waitWindows()256 try self.waitWindows()
256 else257 else
257 try self.waitPosix();258 try self.waitPosix();
...@@ -318,7 +319,7 @@ pub const ChildProcess = struct {...@@ -318,7 +319,7 @@ pub const ChildProcess = struct {
318 stderr.* = fifoToOwnedArrayList(poller.fifo(.stderr));319 stderr.* = fifoToOwnedArrayList(poller.fifo(.stderr));
319 }320 }
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{
322 StdoutStreamTooLong,323 StdoutStreamTooLong,
323 StderrStreamTooLong,324 StderrStreamTooLong,
324 };325 };
...@@ -396,19 +397,19 @@ pub const ChildProcess = struct {...@@ -396,19 +397,19 @@ pub const ChildProcess = struct {
396 self.resource_usage_statistics.rusage = try windows.GetProcessMemoryInfo(self.id);397 self.resource_usage_statistics.rusage = try windows.GetProcessMemoryInfo(self.id);
397 }398 }
398399
399 os.close(self.id);400 posix.close(self.id);
400 os.close(self.thread_handle);401 posix.close(self.thread_handle);
401 self.cleanupStreams();402 self.cleanupStreams();
402 return result;403 return result;
403 }404 }
404405
405 fn waitUnwrapped(self: *ChildProcess) !void {406 fn waitUnwrapped(self: *ChildProcess) !void {
406 const res: os.WaitPidResult = res: {407 const res: posix.WaitPidResult = res: {
407 if (self.request_resource_usage_statistics) {408 if (self.request_resource_usage_statistics) {
408 switch (builtin.os.tag) {409 switch (native_os) {
409 .linux, .macos, .ios => {410 .linux, .macos, .ios => {
410 var ru: std.os.rusage = undefined;411 var ru: posix.rusage = undefined;
411 const res = os.wait4(self.id, 0, &ru);412 const res = posix.wait4(self.id, 0, &ru);
412 self.resource_usage_statistics.rusage = ru;413 self.resource_usage_statistics.rusage = ru;
413 break :res res;414 break :res res;
414 },415 },
...@@ -416,7 +417,7 @@ pub const ChildProcess = struct {...@@ -416,7 +417,7 @@ pub const ChildProcess = struct {
416 }417 }
417 }418 }
418419
419 break :res os.waitpid(self.id, 0);420 break :res posix.waitpid(self.id, 0);
420 };421 };
421 const status = res.status;422 const status = res.status;
422 self.cleanupStreams();423 self.cleanupStreams();
...@@ -446,20 +447,20 @@ pub const ChildProcess = struct {...@@ -446,20 +447,20 @@ pub const ChildProcess = struct {
446 if (self.err_pipe) |err_pipe| {447 if (self.err_pipe) |err_pipe| {
447 defer destroyPipe(err_pipe);448 defer destroyPipe(err_pipe);
448449
449 if (builtin.os.tag == .linux) {450 if (native_os == .linux) {
450 var fd = [1]std.os.pollfd{std.os.pollfd{451 var fd = [1]posix.pollfd{posix.pollfd{
451 .fd = err_pipe[0],452 .fd = err_pipe[0],
452 .events = std.os.POLL.IN,453 .events = posix.POLL.IN,
453 .revents = undefined,454 .revents = undefined,
454 }};455 }};
455456
456 // Check if the eventfd buffer stores a non-zero value by polling457 // Check if the eventfd buffer stores a non-zero value by polling
457 // it, that's the error code returned by the child process.458 // 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
460 // According to eventfd(2) the descriptor is readable if the counter461 // According to eventfd(2) the descriptor is readable if the counter
461 // has a value greater than 0462 // 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) {
463 const err_int = try readIntFd(err_pipe[0]);464 const err_int = try readIntFd(err_pipe[0]);
464 return @as(SpawnError, @errorCast(@errorFromInt(err_int)));465 return @as(SpawnError, @errorCast(@errorFromInt(err_int)));
465 }466 }
...@@ -483,36 +484,36 @@ pub const ChildProcess = struct {...@@ -483,36 +484,36 @@ pub const ChildProcess = struct {
483 }484 }
484485
485 fn statusToTerm(status: u32) Term {486 fn statusToTerm(status: u32) Term {
486 return if (os.W.IFEXITED(status))487 return if (posix.W.IFEXITED(status))
487 Term{ .Exited = os.W.EXITSTATUS(status) }488 Term{ .Exited = posix.W.EXITSTATUS(status) }
488 else if (os.W.IFSIGNALED(status))489 else if (posix.W.IFSIGNALED(status))
489 Term{ .Signal = os.W.TERMSIG(status) }490 Term{ .Signal = posix.W.TERMSIG(status) }
490 else if (os.W.IFSTOPPED(status))491 else if (posix.W.IFSTOPPED(status))
491 Term{ .Stopped = os.W.STOPSIG(status) }492 Term{ .Stopped = posix.W.STOPSIG(status) }
492 else493 else
493 Term{ .Unknown = status };494 Term{ .Unknown = status };
494 }495 }
495496
496 fn spawnPosix(self: *ChildProcess) SpawnError!void {497 fn spawnPosix(self: *ChildProcess) SpawnError!void {
497 const pipe_flags: os.O = .{};498 const pipe_flags: posix.O = .{};
498 const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined;499 const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try posix.pipe2(pipe_flags) else undefined;
499 errdefer if (self.stdin_behavior == StdIo.Pipe) {500 errdefer if (self.stdin_behavior == StdIo.Pipe) {
500 destroyPipe(stdin_pipe);501 destroyPipe(stdin_pipe);
501 };502 };
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;
504 errdefer if (self.stdout_behavior == StdIo.Pipe) {505 errdefer if (self.stdout_behavior == StdIo.Pipe) {
505 destroyPipe(stdout_pipe);506 destroyPipe(stdout_pipe);
506 };507 };
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;
509 errdefer if (self.stderr_behavior == StdIo.Pipe) {510 errdefer if (self.stderr_behavior == StdIo.Pipe) {
510 destroyPipe(stderr_pipe);511 destroyPipe(stderr_pipe);
511 };512 };
512513
513 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);514 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
514 const dev_null_fd = if (any_ignore)515 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) {
516 error.PathAlreadyExists => unreachable,517 error.PathAlreadyExists => unreachable,
517 error.NoSpaceLeft => unreachable,518 error.NoSpaceLeft => unreachable,
518 error.FileTooBig => unreachable,519 error.FileTooBig => unreachable,
...@@ -526,7 +527,7 @@ pub const ChildProcess = struct {...@@ -526,7 +527,7 @@ pub const ChildProcess = struct {
526 else527 else
527 undefined;528 undefined;
528 defer {529 defer {
529 if (any_ignore) os.close(dev_null_fd);530 if (any_ignore) posix.close(dev_null_fd);
530 }531 }
531532
532 var arena_allocator = std.heap.ArenaAllocator.init(self.allocator);533 var arena_allocator = std.heap.ArenaAllocator.init(self.allocator);
...@@ -554,7 +555,7 @@ pub const ChildProcess = struct {...@@ -554,7 +555,7 @@ pub const ChildProcess = struct {
554 } else if (builtin.output_mode == .Exe) {555 } else if (builtin.output_mode == .Exe) {
555 // Then we have Zig start code and this works.556 // Then we have Zig start code and this works.
556 // TODO type-safety for null-termination of `os.environ`.557 // 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));
558 } else {559 } else {
559 // TODO come up with a solution for this.560 // TODO come up with a solution for this.
560 @compileError("missing std lib enhancement: ChildProcess implementation has no way to collect the environment variables to forward to the child process");561 @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 {...@@ -564,60 +565,60 @@ pub const ChildProcess = struct {
564 // This pipe is used to communicate errors between the time of fork565 // This pipe is used to communicate errors between the time of fork
565 // and execve from the child process to the parent process.566 // and execve from the child process to the parent process.
566 const err_pipe = blk: {567 const err_pipe = blk: {
567 if (builtin.os.tag == .linux) {568 if (native_os == .linux) {
568 const fd = try os.eventfd(0, linux.EFD.CLOEXEC);569 const fd = try posix.eventfd(0, linux.EFD.CLOEXEC);
569 // There's no distinction between the readable and the writeable570 // There's no distinction between the readable and the writeable
570 // end with eventfd571 // end with eventfd
571 break :blk [2]os.fd_t{ fd, fd };572 break :blk [2]posix.fd_t{ fd, fd };
572 } else {573 } else {
573 break :blk try os.pipe2(.{ .CLOEXEC = true });574 break :blk try posix.pipe2(.{ .CLOEXEC = true });
574 }575 }
575 };576 };
576 errdefer destroyPipe(err_pipe);577 errdefer destroyPipe(err_pipe);
577578
578 const pid_result = try os.fork();579 const pid_result = try posix.fork();
579 if (pid_result == 0) {580 if (pid_result == 0) {
580 // we are the child581 // 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.stdin_behavior, stdin_pipe[0], posix.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.stdout_behavior, stdout_pipe[1], posix.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);584 setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err);
584585
585 if (self.stdin_behavior == .Pipe) {586 if (self.stdin_behavior == .Pipe) {
586 os.close(stdin_pipe[0]);587 posix.close(stdin_pipe[0]);
587 os.close(stdin_pipe[1]);588 posix.close(stdin_pipe[1]);
588 }589 }
589 if (self.stdout_behavior == .Pipe) {590 if (self.stdout_behavior == .Pipe) {
590 os.close(stdout_pipe[0]);591 posix.close(stdout_pipe[0]);
591 os.close(stdout_pipe[1]);592 posix.close(stdout_pipe[1]);
592 }593 }
593 if (self.stderr_behavior == .Pipe) {594 if (self.stderr_behavior == .Pipe) {
594 os.close(stderr_pipe[0]);595 posix.close(stderr_pipe[0]);
595 os.close(stderr_pipe[1]);596 posix.close(stderr_pipe[1]);
596 }597 }
597598
598 if (self.cwd_dir) |cwd| {599 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);
600 } else if (self.cwd) |cwd| {601 } 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);
602 }603 }
603604
604 if (self.gid) |gid| {605 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);
606 }607 }
607608
608 if (self.uid) |uid| {609 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);
610 }611 }
611612
612 const err = switch (self.expand_arg0) {613 const err = switch (self.expand_arg0) {
613 .expand => os.execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),614 .expand => posix.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),615 .no_expand => posix.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp),
615 };616 };
616 forkChildErrReport(err_pipe[1], err);617 forkChildErrReport(err_pipe[1], err);
617 }618 }
618619
619 // we are the parent620 // we are the parent
620 const pid = @as(i32, @intCast(pid_result));621 const pid: i32 = @intCast(pid_result);
621 if (self.stdin_behavior == StdIo.Pipe) {622 if (self.stdin_behavior == StdIo.Pipe) {
622 self.stdin = File{ .handle = stdin_pipe[1] };623 self.stdin = File{ .handle = stdin_pipe[1] };
623 } else {624 } else {
...@@ -639,13 +640,13 @@ pub const ChildProcess = struct {...@@ -639,13 +640,13 @@ pub const ChildProcess = struct {
639 self.term = null;640 self.term = null;
640641
641 if (self.stdin_behavior == StdIo.Pipe) {642 if (self.stdin_behavior == StdIo.Pipe) {
642 os.close(stdin_pipe[0]);643 posix.close(stdin_pipe[0]);
643 }644 }
644 if (self.stdout_behavior == StdIo.Pipe) {645 if (self.stdout_behavior == StdIo.Pipe) {
645 os.close(stdout_pipe[1]);646 posix.close(stdout_pipe[1]);
646 }647 }
647 if (self.stderr_behavior == StdIo.Pipe) {648 if (self.stderr_behavior == StdIo.Pipe) {
648 os.close(stderr_pipe[1]);649 posix.close(stderr_pipe[1]);
649 }650 }
650 }651 }
651652
...@@ -679,7 +680,7 @@ pub const ChildProcess = struct {...@@ -679,7 +680,7 @@ pub const ChildProcess = struct {
679 else680 else
680 undefined;681 undefined;
681 defer {682 defer {
682 if (any_ignore) os.close(nul_handle);683 if (any_ignore) posix.close(nul_handle);
683 }684 }
684685
685 var g_hChildStd_IN_Rd: ?windows.HANDLE = null;686 var g_hChildStd_IN_Rd: ?windows.HANDLE = null;
...@@ -821,8 +822,8 @@ pub const ChildProcess = struct {...@@ -821,8 +822,8 @@ pub const ChildProcess = struct {
821 defer self.allocator.free(cmd_line_w);822 defer self.allocator.free(cmd_line_w);
822823
823 run: {824 run: {
824 const PATH: [:0]const u16 = std.os.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse &[_:0]u16{};825 const PATH: [:0]const u16 = std.process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse &[_:0]u16{};
825 const PATHEXT: [:0]const u16 = std.os.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATHEXT")) orelse &[_:0]u16{};826 const PATHEXT: [:0]const u16 = std.process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATHEXT")) orelse &[_:0]u16{};
826827
827 var app_buf = std.ArrayListUnmanaged(u16){};828 var app_buf = std.ArrayListUnmanaged(u16){};
828 defer app_buf.deinit(self.allocator);829 defer app_buf.deinit(self.allocator);
...@@ -905,22 +906,22 @@ pub const ChildProcess = struct {...@@ -905,22 +906,22 @@ pub const ChildProcess = struct {
905 self.term = null;906 self.term = null;
906907
907 if (self.stdin_behavior == StdIo.Pipe) {908 if (self.stdin_behavior == StdIo.Pipe) {
908 os.close(g_hChildStd_IN_Rd.?);909 posix.close(g_hChildStd_IN_Rd.?);
909 }910 }
910 if (self.stderr_behavior == StdIo.Pipe) {911 if (self.stderr_behavior == StdIo.Pipe) {
911 os.close(g_hChildStd_ERR_Wr.?);912 posix.close(g_hChildStd_ERR_Wr.?);
912 }913 }
913 if (self.stdout_behavior == StdIo.Pipe) {914 if (self.stdout_behavior == StdIo.Pipe) {
914 os.close(g_hChildStd_OUT_Wr.?);915 posix.close(g_hChildStd_OUT_Wr.?);
915 }916 }
916 }917 }
917918
918 fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void {919 fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void {
919 switch (stdio) {920 switch (stdio) {
920 .Pipe => try os.dup2(pipe_fd, std_fileno),921 .Pipe => try posix.dup2(pipe_fd, std_fileno),
921 .Close => os.close(std_fileno),922 .Close => posix.close(std_fileno),
922 .Inherit => {},923 .Inherit => {},
923 .Ignore => try os.dup2(dev_null_fd, std_fileno),924 .Ignore => try posix.dup2(dev_null_fd, std_fileno),
924 }925 }
925 }926 }
926};927};
...@@ -987,7 +988,7 @@ fn windowsCreateProcessPathExt(...@@ -987,7 +988,7 @@ fn windowsCreateProcessPathExt(
987988
988 // This 2048 is arbitrary, we just want it to be large enough to get multiple FILE_DIRECTORY_INFORMATION entries989 // This 2048 is arbitrary, we just want it to be large enough to get multiple FILE_DIRECTORY_INFORMATION entries
989 // returned per NtQueryDirectoryFile call.990 // 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;
991 const file_info_maximum_single_entry_size = @sizeOf(windows.FILE_DIRECTORY_INFORMATION) + (windows.NAME_MAX * 2);992 const file_info_maximum_single_entry_size = @sizeOf(windows.FILE_DIRECTORY_INFORMATION) + (windows.NAME_MAX * 2);
992 if (file_information_buf.len < file_info_maximum_single_entry_size) {993 if (file_information_buf.len < file_info_maximum_single_entry_size) {
993 @compileError("file_information_buf must be large enough to contain at least one maximum size FILE_DIRECTORY_INFORMATION entry");994 @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...@@ -1391,8 +1392,8 @@ fn testArgvToCommandLineWindows(argv: []const []const u8, expected_cmd_line: []c
1391}1392}
13921393
1393fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void {1394fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void {
1394 if (rd) |h| os.close(h);1395 if (rd) |h| posix.close(h);
1395 if (wr) |h| os.close(h);1396 if (wr) |h| posix.close(h);
1396}1397}
13971398
1398fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void {1399fn 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...@@ -1443,7 +1444,7 @@ fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *cons
1443 else => |err| return windows.unexpectedError(err),1444 else => |err| return windows.unexpectedError(err),
1444 }1445 }
1445 }1446 }
1446 errdefer os.close(read_handle);1447 errdefer posix.close(read_handle);
14471448
1448 var sattr_copy = sattr.*;1449 var sattr_copy = sattr.*;
1449 const write_handle = windows.kernel32.CreateFileW(1450 const write_handle = windows.kernel32.CreateFileW(
...@@ -1460,7 +1461,7 @@ fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *cons...@@ -1460,7 +1461,7 @@ fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *cons
1460 else => |err| return windows.unexpectedError(err),1461 else => |err| return windows.unexpectedError(err),
1461 }1462 }
1462 }1463 }
1463 errdefer os.close(write_handle);1464 errdefer posix.close(write_handle);
14641465
1465 try windows.SetHandleInformation(read_handle, windows.HANDLE_FLAG_INHERIT, 0);1466 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...@@ -1468,9 +1469,9 @@ fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *cons
1468 wr.* = write_handle;1469 wr.* = write_handle;
1469}1470}
14701471
1471fn destroyPipe(pipe: [2]os.fd_t) void {1472fn destroyPipe(pipe: [2]posix.fd_t) void {
1472 os.close(pipe[0]);1473 posix.close(pipe[0]);
1473 if (pipe[0] != pipe[1]) os.close(pipe[1]);1474 if (pipe[0] != pipe[1]) posix.close(pipe[1]);
1474}1475}
14751476
1476// Child of fork calls this to report an error to the fork parent.1477// 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 {...@@ -1485,7 +1486,7 @@ fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
1485 // The _exit(2) function does nothing but make the exit syscall, unlike exit(3)1486 // The _exit(2) function does nothing but make the exit syscall, unlike exit(3)
1486 std.c._exit(1);1487 std.c._exit(1);
1487 }1488 }
1488 os.exit(1);1489 posix.exit(1);
1489}1490}
14901491
1491const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8);1492const 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...@@ -125,7 +125,7 @@ fn rescanBSD(cb: *Bundle, gpa: Allocator, cert_file_path: []const u8) RescanBSDE
125 cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);125 cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);
126}126}
127127
128const RescanWindowsError = Allocator.Error || ParseCertError || std.os.UnexpectedError || error{FileNotFound};128const RescanWindowsError = Allocator.Error || ParseCertError || std.posix.UnexpectedError || error{FileNotFound};
129129
130fn rescanWindows(cb: *Bundle, gpa: Allocator) RescanWindowsError!void {130fn rescanWindows(cb: *Bundle, gpa: Allocator) RescanWindowsError!void {
131 cb.bytes.clearRetainingCapacity();131 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 {...@@ -42,7 +42,7 @@ pub fn rescanMac(cb: *Bundle, gpa: Allocator) RescanMacError!void {
4242
43 const table_header = try reader.readStructEndian(TableHeader, .big);43 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) {
46 continue;46 continue;
47 }47 }
4848
lib/std/crypto/tlcsprng.zig+9-8
...@@ -6,7 +6,8 @@...@@ -6,7 +6,8 @@
6const std = @import("std");6const std = @import("std");
7const builtin = @import("builtin");7const builtin = @import("builtin");
8const mem = std.mem;8const mem = std.mem;
9const os = std.os;9const native_os = builtin.os.tag;
10const posix = std.posix;
1011
11/// We use this as a layer of indirection because global const pointers cannot12/// We use this as a layer of indirection because global const pointers cannot
12/// point to thread-local variables.13/// point to thread-local variables.
...@@ -15,7 +16,7 @@ pub const interface = std.Random{...@@ -15,7 +16,7 @@ pub const interface = std.Random{
15 .fillFn = tlsCsprngFill,16 .fillFn = tlsCsprngFill,
16};17};
1718
18const os_has_fork = switch (builtin.os.tag) {19const os_has_fork = switch (native_os) {
19 .dragonfly,20 .dragonfly,
20 .freebsd,21 .freebsd,
21 .ios,22 .ios,
...@@ -41,7 +42,7 @@ const maybe_have_wipe_on_fork = builtin.os.isAtLeast(.linux, .{...@@ -41,7 +42,7 @@ const maybe_have_wipe_on_fork = builtin.os.isAtLeast(.linux, .{
41 .minor = 14,42 .minor = 14,
42 .patch = 0,43 .patch = 0,
43}) orelse true;44}) orelse true;
44const is_haiku = builtin.os.tag == .haiku;45const is_haiku = native_os == .haiku;
4546
46const Rng = std.Random.DefaultCsprng;47const Rng = std.Random.DefaultCsprng;
4748
...@@ -79,10 +80,10 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {...@@ -79,10 +80,10 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
79 if (want_fork_safety and maybe_have_wipe_on_fork or is_haiku) {80 if (want_fork_safety and maybe_have_wipe_on_fork or is_haiku) {
80 // Allocate a per-process page, madvise operates with page81 // Allocate a per-process page, madvise operates with page
81 // granularity.82 // granularity.
82 wipe_mem = os.mmap(83 wipe_mem = posix.mmap(
83 null,84 null,
84 @sizeOf(Context),85 @sizeOf(Context),
85 os.PROT.READ | os.PROT.WRITE,86 posix.PROT.READ | posix.PROT.WRITE,
86 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },87 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
87 -1,88 -1,
88 0,89 0,
...@@ -115,11 +116,11 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {...@@ -115,11 +116,11 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
115 // Qemu user-mode emulation ignores any valid/invalid madvise116 // Qemu user-mode emulation ignores any valid/invalid madvise
116 // hint and returns success. Check if this is the case by117 // hint and returns success. Check if this is the case by
117 // passing bogus parameters, we expect EINVAL as result.118 // 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)) |_| {
119 break :wof;120 break :wof;
120 } else |_| {}121 } 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)) |_| {
123 return initAndFill(buffer);124 return initAndFill(buffer);
124 } else |_| {}125 } else |_| {}
125 }126 }
...@@ -164,7 +165,7 @@ fn fillWithCsprng(buffer: []u8) void {...@@ -164,7 +165,7 @@ fn fillWithCsprng(buffer: []u8) void {
164}165}
165166
166pub fn defaultRandomSeed(buffer: []u8) void {167pub 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");
168}169}
169170
170fn initAndFill(buffer: []u8) void {171fn initAndFill(buffer: []u8) void {
lib/std/crypto/tls/Client.zig+14-14
...@@ -62,7 +62,7 @@ pub const StreamInterface = struct {...@@ -62,7 +62,7 @@ pub const StreamInterface = struct {
62 /// The `iovecs` parameter is mutable because so that function may to62 /// The `iovecs` parameter is mutable because so that function may to
63 /// mutate the fields in order to handle partial reads from the underlying63 /// mutate the fields in order to handle partial reads from the underlying
64 /// stream layer.64 /// 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 {
66 _ = .{ this, iovecs };66 _ = .{ this, iovecs };
67 @panic("unimplemented");67 @panic("unimplemented");
68 }68 }
...@@ -72,7 +72,7 @@ pub const StreamInterface = struct {...@@ -72,7 +72,7 @@ pub const StreamInterface = struct {
7272
73 /// Returns the number of bytes read, which may be less than the buffer73 /// Returns the number of bytes read, which may be less than the buffer
74 /// space provided. A short read does not indicate end-of-stream.74 /// 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 {
76 _ = .{ this, iovecs };76 _ = .{ this, iovecs };
77 @panic("unimplemented");77 @panic("unimplemented");
78 }78 }
...@@ -81,7 +81,7 @@ pub const StreamInterface = struct {...@@ -81,7 +81,7 @@ pub const StreamInterface = struct {
81 /// space provided, indicating end-of-stream.81 /// space provided, indicating end-of-stream.
82 /// The `iovecs` parameter is mutable in case this function needs to mutate82 /// The `iovecs` parameter is mutable in case this function needs to mutate
83 /// the fields in order to handle partial writes from the underlying layer.83 /// 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 {
85 // This can be implemented in terms of writev, or specialized if desired.85 // This can be implemented in terms of writev, or specialized if desired.
86 _ = .{ this, iovecs };86 _ = .{ this, iovecs };
87 @panic("unimplemented");87 @panic("unimplemented");
...@@ -215,7 +215,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -215,7 +215,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
215 } ++ int2(@intCast(out_handshake.len + host_len)) ++ out_handshake;215 } ++ int2(@intCast(out_handshake.len + host_len)) ++ out_handshake;
216216
217 {217 {
218 var iovecs = [_]std.os.iovec_const{218 var iovecs = [_]std.posix.iovec_const{
219 .{219 .{
220 .iov_base = &plaintext_header,220 .iov_base = &plaintext_header,
221 .iov_len = plaintext_header.len,221 .iov_len = plaintext_header.len,
...@@ -677,7 +677,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In...@@ -677,7 +677,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In
677 P.AEAD.encrypt(ciphertext, auth_tag, &out_cleartext, ad, nonce, p.client_handshake_key);677 P.AEAD.encrypt(ciphertext, auth_tag, &out_cleartext, ad, nonce, p.client_handshake_key);
678678
679 const both_msgs = client_change_cipher_spec_msg ++ finished_msg;679 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{.{
681 .iov_base = &both_msgs,681 .iov_base = &both_msgs,
682 .iov_len = both_msgs.len,682 .iov_len = both_msgs.len,
683 }};683 }};
...@@ -755,7 +755,7 @@ pub fn writeAllEnd(c: *Client, stream: anytype, bytes: []const u8, end: bool) !v...@@ -755,7 +755,7 @@ pub fn writeAllEnd(c: *Client, stream: anytype, bytes: []const u8, end: bool) !v
755/// TLS session, or a truncation attack.755/// TLS session, or a truncation attack.
756pub fn writeEnd(c: *Client, stream: anytype, bytes: []const u8, end: bool) !usize {756pub fn writeEnd(c: *Client, stream: anytype, bytes: []const u8, end: bool) !usize {
757 var ciphertext_buf: [tls.max_ciphertext_record_len * 4]u8 = undefined;757 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;
759 var prepared = prepareCiphertextRecord(c, &iovecs_buf, &ciphertext_buf, bytes, .application_data);759 var prepared = prepareCiphertextRecord(c, &iovecs_buf, &ciphertext_buf, bytes, .application_data);
760 if (end) {760 if (end) {
761 prepared.iovec_end += prepareCiphertextRecord(761 prepared.iovec_end += prepareCiphertextRecord(
...@@ -796,7 +796,7 @@ pub fn writeEnd(c: *Client, stream: anytype, bytes: []const u8, end: bool) !usiz...@@ -796,7 +796,7 @@ pub fn writeEnd(c: *Client, stream: anytype, bytes: []const u8, end: bool) !usiz
796796
797fn prepareCiphertextRecord(797fn prepareCiphertextRecord(
798 c: *Client,798 c: *Client,
799 iovecs: []std.os.iovec_const,799 iovecs: []std.posix.iovec_const,
800 ciphertext_buf: []u8,800 ciphertext_buf: []u8,
801 bytes: []const u8,801 bytes: []const u8,
802 inner_content_type: tls.ContentType,802 inner_content_type: tls.ContentType,
...@@ -885,7 +885,7 @@ pub fn eof(c: Client) bool {...@@ -885,7 +885,7 @@ pub fn eof(c: Client) bool {
885/// If the number read is less than `len` it means the stream reached the end.885/// If the number read is less than `len` it means the stream reached the end.
886/// Reaching the end of the stream is not an error condition.886/// Reaching the end of the stream is not an error condition.
887pub fn readAtLeast(c: *Client, stream: anytype, buffer: []u8, len: usize) !usize {887pub 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 }};
889 return readvAtLeast(c, stream, &iovecs, len);889 return readvAtLeast(c, stream, &iovecs, len);
890}890}
891891
...@@ -908,7 +908,7 @@ pub fn readAll(c: *Client, stream: anytype, buffer: []u8) !usize {...@@ -908,7 +908,7 @@ pub fn readAll(c: *Client, stream: anytype, buffer: []u8) !usize {
908/// stream is not an error condition.908/// stream is not an error condition.
909/// The `iovecs` parameter is mutable because this function needs to mutate the fields in909/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
910/// order to handle partial reads from the underlying stream layer.910/// 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 {
912 return readvAtLeast(c, stream, iovecs, 1);912 return readvAtLeast(c, stream, iovecs, 1);
913}913}
914914
...@@ -919,7 +919,7 @@ pub fn readv(c: *Client, stream: anytype, iovecs: []std.os.iovec) !usize {...@@ -919,7 +919,7 @@ pub fn readv(c: *Client, stream: anytype, iovecs: []std.os.iovec) !usize {
919/// Reaching the end of the stream is not an error condition.919/// Reaching the end of the stream is not an error condition.
920/// The `iovecs` parameter is mutable because this function needs to mutate the fields in920/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
921/// order to handle partial reads from the underlying stream layer.921/// 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 {
923 if (c.eof()) return 0;923 if (c.eof()) return 0;
924924
925 var off_i: usize = 0;925 var off_i: usize = 0;
...@@ -945,7 +945,7 @@ pub fn readvAtLeast(c: *Client, stream: anytype, iovecs: []std.os.iovec, len: us...@@ -945,7 +945,7 @@ pub fn readvAtLeast(c: *Client, stream: anytype, iovecs: []std.os.iovec, len: us
945/// function asserts that `eof()` is `false`.945/// function asserts that `eof()` is `false`.
946/// See `readv` for a higher level function that has the same, familiar API as946/// See `readv` for a higher level function that has the same, familiar API as
947/// other read functions, such as `std.fs.File.read`.947/// 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 {
949 var vp: VecPut = .{ .iovecs = iovecs };949 var vp: VecPut = .{ .iovecs = iovecs };
950950
951 // Give away the buffered cleartext we have, if any.951 // 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)...@@ -998,7 +998,7 @@ pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec)
998 c.partial_cleartext_idx = 0;998 c.partial_cleartext_idx = 0;
999 const first_iov = c.partially_read_buffer[c.partial_ciphertext_end..];999 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 = .{
1002 .{1002 .{
1003 .iov_base = first_iov.ptr,1003 .iov_base = first_iov.ptr,
1004 .iov_len = first_iov.len,1004 .iov_len = first_iov.len,
...@@ -1352,7 +1352,7 @@ fn SchemeEddsa(comptime scheme: tls.SignatureScheme) type {...@@ -1352,7 +1352,7 @@ fn SchemeEddsa(comptime scheme: tls.SignatureScheme) type {
13521352
1353/// Abstraction for sending multiple byte buffers to a slice of iovecs.1353/// Abstraction for sending multiple byte buffers to a slice of iovecs.
1354const VecPut = struct {1354const VecPut = struct {
1355 iovecs: []const std.os.iovec,1355 iovecs: []const std.posix.iovec,
1356 idx: usize = 0,1356 idx: usize = 0,
1357 off: usize = 0,1357 off: usize = 0,
1358 total: usize = 0,1358 total: usize = 0,
...@@ -1413,7 +1413,7 @@ const VecPut = struct {...@@ -1413,7 +1413,7 @@ const VecPut = struct {
1413};1413};
14141414
1415/// Limit iovecs to a specific byte size.1415/// 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 {
1417 var bytes_left: usize = len;1417 var bytes_left: usize = len;
1418 for (iovecs, 0..) |*iovec, vec_i| {1418 for (iovecs, 0..) |*iovec, vec_i| {
1419 if (bytes_left <= iovec.iov_len) {1419 if (bytes_left <= iovec.iov_len) {
lib/std/debug.zig+57-60
...@@ -3,7 +3,7 @@ const builtin = @import("builtin");...@@ -3,7 +3,7 @@ const builtin = @import("builtin");
3const math = std.math;3const math = std.math;
4const mem = std.mem;4const mem = std.mem;
5const io = std.io;5const io = std.io;
6const os = std.os;6const posix = std.posix;
7const fs = std.fs;7const fs = std.fs;
8const testing = std.testing;8const testing = std.testing;
9const elf = std.elf;9const elf = std.elf;
...@@ -34,7 +34,7 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) {...@@ -34,7 +34,7 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) {
34 // "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address".34 // "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address".
35 .wasm32,35 .wasm32,
36 .wasm64,36 .wasm64,
37 => builtin.os.tag == .emscripten,37 => native_os == .emscripten,
3838
39 // `@returnAddress()` is unsupported in LLVM 13.39 // `@returnAddress()` is unsupported in LLVM 13.
40 .bpfel,40 .bpfel,
...@@ -192,8 +192,8 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {...@@ -192,8 +192,8 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
192 }192 }
193}193}
194194
195pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and195pub const have_ucontext = @hasDecl(posix.system, "ucontext_t") and
196 (builtin.os.tag != .linux or switch (builtin.cpu.arch) {196 (native_os != .linux or switch (builtin.cpu.arch) {
197 .mips, .mipsel, .mips64, .mips64el, .riscv64 => false,197 .mips, .mipsel, .mips64, .mips64el, .riscv64 => false,
198 else => true,198 else => true,
199});199});
...@@ -203,9 +203,9 @@ pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and...@@ -203,9 +203,9 @@ pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and
203/// use internal pointers within this structure. To make a copy, use `copyContext`.203/// use internal pointers within this structure. To make a copy, use `copyContext`.
204pub const ThreadContext = blk: {204pub const ThreadContext = blk: {
205 if (native_os == .windows) {205 if (native_os == .windows) {
206 break :blk std.os.windows.CONTEXT;206 break :blk windows.CONTEXT;
207 } else if (have_ucontext) {207 } else if (have_ucontext) {
208 break :blk os.ucontext_t;208 break :blk posix.ucontext_t;
209 } else {209 } else {
210 break :blk void;210 break :blk void;
211 }211 }
...@@ -228,9 +228,9 @@ pub fn relocateContext(context: *ThreadContext) void {...@@ -228,9 +228,9 @@ pub fn relocateContext(context: *ThreadContext) void {
228 };228 };
229}229}
230230
231pub const have_getcontext = @hasDecl(os.system, "getcontext") and231pub const have_getcontext = @hasDecl(posix.system, "getcontext") and
232 builtin.os.tag != .openbsd and232 native_os != .openbsd and
233 (builtin.os.tag != .linux or switch (builtin.cpu.arch) {233 (native_os != .linux or switch (builtin.cpu.arch) {
234 .x86,234 .x86,
235 .x86_64,235 .x86_64,
236 => true,236 => true,
...@@ -249,7 +249,7 @@ pub inline fn getContext(context: *ThreadContext) bool {...@@ -249,7 +249,7 @@ pub inline fn getContext(context: *ThreadContext) bool {
249 return true;249 return true;
250 }250 }
251251
252 const result = have_getcontext and os.system.getcontext(context) == 0;252 const result = have_getcontext and posix.system.getcontext(context) == 0;
253 if (native_os == .macos) {253 if (native_os == .macos) {
254 assert(context.mcsize == @sizeOf(std.c.mcontext_t));254 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...@@ -470,12 +470,12 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize
470470
471 const stderr = io.getStdErr().writer();471 const stderr = io.getStdErr().writer();
472 if (builtin.single_threaded) {472 if (builtin.single_threaded) {
473 stderr.print("panic: ", .{}) catch os.abort();473 stderr.print("panic: ", .{}) catch posix.abort();
474 } else {474 } else {
475 const current_thread_id = std.Thread.getCurrentId();475 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();
477 }477 }
478 stderr.print("{s}\n", .{msg}) catch os.abort();478 stderr.print("{s}\n", .{msg}) catch posix.abort();
479 if (trace) |t| {479 if (trace) |t| {
480 dumpStackTrace(t.*);480 dumpStackTrace(t.*);
481 }481 }
...@@ -491,14 +491,14 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize...@@ -491,14 +491,14 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize
491 // we're still holding the mutex but that's fine as we're going to491 // we're still holding the mutex but that's fine as we're going to
492 // call abort()492 // call abort()
493 const stderr = io.getStdErr().writer();493 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();
495 },495 },
496 else => {496 else => {
497 // Panicked while printing "Panicked during a panic."497 // Panicked while printing "Panicked during a panic."
498 },498 },
499 };499 };
500500
501 os.abort();501 posix.abort();
502}502}
503503
504/// Must be called only after adding 1 to `panicking`. There are three callsites.504/// Must be called only after adding 1 to `panicking`. There are three callsites.
...@@ -584,7 +584,7 @@ pub const StackIterator = struct {...@@ -584,7 +584,7 @@ pub const StackIterator = struct {
584 };584 };
585 }585 }
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 {
588 // The implementation of DWARF unwinding on aarch64-macos is not complete. However, Apple mandates that588 // The implementation of DWARF unwinding on aarch64-macos is not complete. However, Apple mandates that
589 // the frame pointer register is always used, so on this platform we can safely use the FP-based unwinder.589 // the frame pointer register is always used, so on this platform we can safely use the FP-based unwinder.
590 if (comptime builtin.target.isDarwin() and native_arch == .aarch64) {590 if (comptime builtin.target.isDarwin() and native_arch == .aarch64) {
...@@ -668,12 +668,11 @@ pub const StackIterator = struct {...@@ -668,12 +668,11 @@ pub const StackIterator = struct {
668 const aligned_memory = @as([*]align(mem.page_size) u8, @ptrFromInt(aligned_address))[0..mem.page_size];668 const aligned_memory = @as([*]align(mem.page_size) u8, @ptrFromInt(aligned_address))[0..mem.page_size];
669669
670 if (native_os == .windows) {670 if (native_os == .windows) {
671 const w = os.windows;671 var memory_info: windows.MEMORY_BASIC_INFORMATION = undefined;
672 var memory_info: w.MEMORY_BASIC_INFORMATION = undefined;
673672
674 // The only error this function can throw is ERROR_INVALID_PARAMETER.673 // The only error this function can throw is ERROR_INVALID_PARAMETER.
675 // supply an address that invalid i'll be thrown.674 // 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 {
677 return false;676 return false;
678 };677 };
679678
...@@ -683,17 +682,15 @@ pub const StackIterator = struct {...@@ -683,17 +682,15 @@ pub const StackIterator = struct {
683 }682 }
684683
685 // Free pages cannot be read, they are unmapped684 // Free pages cannot be read, they are unmapped
686 if (memory_info.State == w.MEM_FREE) {685 if (memory_info.State == windows.MEM_FREE) {
687 return false;686 return false;
688 }687 }
689688
690 return true;689 return true;
691 } else if (@hasDecl(os.system, "msync") and native_os != .wasi and native_os != .emscripten) {690 } else if (@hasDecl(posix.system, "msync") and native_os != .wasi and native_os != .emscripten) {
692 os.msync(aligned_memory, os.MSF.ASYNC) catch |err| {691 posix.msync(aligned_memory, posix.MSF.ASYNC) catch |err| {
693 switch (err) {692 switch (err) {
694 os.MSyncError.UnmappedMemory => {693 error.UnmappedMemory => return false,
695 return false;
696 },
697 else => unreachable,694 else => unreachable,
698 }695 }
699 };696 };
...@@ -1296,7 +1293,7 @@ pub fn readElfDebugInfo(...@@ -1296,7 +1293,7 @@ pub fn readElfDebugInfo(
1296 }1293 }
12971294
1298 var cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;1295 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
1301 // <global debug directory>/<absolute folder of current binary>/<gnu_debuglink>1298 // <global debug directory>/<absolute folder of current binary>/<gnu_debuglink>
1302 for (global_debug_directories) |global_directory| {1299 for (global_debug_directories) |global_directory| {
...@@ -1651,15 +1648,15 @@ fn mapWholeFile(file: File) ![]align(mem.page_size) const u8 {...@@ -1651,15 +1648,15 @@ fn mapWholeFile(file: File) ![]align(mem.page_size) const u8 {
1651 defer file.close();1648 defer file.close();
16521649
1653 const file_len = math.cast(usize, try file.getEndPos()) orelse math.maxInt(usize);1650 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(
1655 null,1652 null,
1656 file_len,1653 file_len,
1657 os.PROT.READ,1654 posix.PROT.READ,
1658 .{ .TYPE = .SHARED },1655 .{ .TYPE = .SHARED },
1659 file.handle,1656 file.handle,
1660 0,1657 0,
1661 );1658 );
1662 errdefer os.munmap(mapped_mem);1659 errdefer posix.munmap(mapped_mem);
16631660
1664 return mapped_mem;1661 return mapped_mem;
1665 }1662 }
...@@ -1997,8 +1994,8 @@ pub const DebugInfo = struct {...@@ -1997,8 +1994,8 @@ pub const DebugInfo = struct {
1997 } = .{ .address = address };1994 } = .{ .address = address };
1998 const CtxTy = @TypeOf(ctx);1995 const CtxTy = @TypeOf(ctx);
19991996
2000 if (os.dl_iterate_phdr(&ctx, error{Found}, struct {1997 if (posix.dl_iterate_phdr(&ctx, error{Found}, struct {
2001 fn callback(info: *os.dl_phdr_info, size: usize, context: *CtxTy) !void {1998 fn callback(info: *posix.dl_phdr_info, size: usize, context: *CtxTy) !void {
2002 _ = size;1999 _ = size;
2003 if (context.address < info.dlpi_addr) return;2000 if (context.address < info.dlpi_addr) return;
2004 const phdrs = info.dlpi_phdr[0..info.dlpi_phnum];2001 const phdrs = info.dlpi_phdr[0..info.dlpi_phnum];
...@@ -2036,8 +2033,8 @@ pub const DebugInfo = struct {...@@ -2036,8 +2033,8 @@ pub const DebugInfo = struct {
2036 } = .{ .address = address };2033 } = .{ .address = address };
2037 const CtxTy = @TypeOf(ctx);2034 const CtxTy = @TypeOf(ctx);
20382035
2039 if (os.dl_iterate_phdr(&ctx, error{Found}, struct {2036 if (posix.dl_iterate_phdr(&ctx, error{Found}, struct {
2040 fn callback(info: *os.dl_phdr_info, size: usize, context: *CtxTy) !void {2037 fn callback(info: *posix.dl_phdr_info, size: usize, context: *CtxTy) !void {
2041 _ = size;2038 _ = size;
2042 // The base address is too high2039 // The base address is too high
2043 if (context.address < info.dlpi_addr)2040 if (context.address < info.dlpi_addr)
...@@ -2159,7 +2156,7 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -2159,7 +2156,7 @@ pub const ModuleDebugInfo = switch (native_os) {
2159 }2156 }
2160 self.ofiles.deinit();2157 self.ofiles.deinit();
2161 allocator.free(self.symbols);2158 allocator.free(self.symbols);
2162 os.munmap(self.mapped_memory);2159 posix.munmap(self.mapped_memory);
2163 }2160 }
21642161
2165 fn loadOFile(self: *@This(), allocator: mem.Allocator, o_file_path: []const u8) !*OFileInfo {2162 fn loadOFile(self: *@This(), allocator: mem.Allocator, o_file_path: []const u8) !*OFileInfo {
...@@ -2433,8 +2430,8 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -2433,8 +2430,8 @@ pub const ModuleDebugInfo = switch (native_os) {
24332430
2434 pub fn deinit(self: *@This(), allocator: mem.Allocator) void {2431 pub fn deinit(self: *@This(), allocator: mem.Allocator) void {
2435 self.dwarf.deinit(allocator);2432 self.dwarf.deinit(allocator);
2436 os.munmap(self.mapped_memory);2433 posix.munmap(self.mapped_memory);
2437 if (self.external_mapped_memory) |m| os.munmap(m);2434 if (self.external_mapped_memory) |m| posix.munmap(m);
2438 }2435 }
24392436
2440 pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo {2437 pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo {
...@@ -2514,7 +2511,7 @@ pub const have_segfault_handling_support = switch (native_os) {...@@ -2514,7 +2511,7 @@ pub const have_segfault_handling_support = switch (native_os) {
2514 .windows,2511 .windows,
2515 => true,2512 => true,
25162513
2517 .freebsd, .openbsd => @hasDecl(os.system, "ucontext_t"),2514 .freebsd, .openbsd => @hasDecl(std.c, "ucontext_t"),
2518 else => false,2515 else => false,
2519};2516};
25202517
...@@ -2529,11 +2526,11 @@ pub fn maybeEnableSegfaultHandler() void {...@@ -2529,11 +2526,11 @@ pub fn maybeEnableSegfaultHandler() void {
25292526
2530var windows_segfault_handle: ?windows.HANDLE = null;2527var windows_segfault_handle: ?windows.HANDLE = null;
25312528
2532pub fn updateSegfaultHandler(act: ?*const os.Sigaction) error{OperationNotSupported}!void {2529pub fn updateSegfaultHandler(act: ?*const posix.Sigaction) error{OperationNotSupported}!void {
2533 try os.sigaction(os.SIG.SEGV, act, null);2530 try posix.sigaction(posix.SIG.SEGV, act, null);
2534 try os.sigaction(os.SIG.ILL, act, null);2531 try posix.sigaction(posix.SIG.ILL, act, null);
2535 try os.sigaction(os.SIG.BUS, act, null);2532 try posix.sigaction(posix.SIG.BUS, act, null);
2536 try os.sigaction(os.SIG.FPE, act, null);2533 try posix.sigaction(posix.SIG.FPE, act, null);
2537}2534}
25382535
2539/// Attaches a global SIGSEGV handler which calls `@panic("segmentation fault");`2536/// Attaches a global SIGSEGV handler which calls `@panic("segmentation fault");`
...@@ -2545,10 +2542,10 @@ pub fn attachSegfaultHandler() void {...@@ -2545,10 +2542,10 @@ pub fn attachSegfaultHandler() void {
2545 windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);2542 windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
2546 return;2543 return;
2547 }2544 }
2548 var act = os.Sigaction{2545 var act = posix.Sigaction{
2549 .handler = .{ .sigaction = handleSegfaultPosix },2546 .handler = .{ .sigaction = handleSegfaultPosix },
2550 .mask = os.empty_sigset,2547 .mask = posix.empty_sigset,
2551 .flags = (os.SA.SIGINFO | os.SA.RESTART | os.SA.RESETHAND),2548 .flags = (posix.SA.SIGINFO | posix.SA.RESTART | posix.SA.RESETHAND),
2552 };2549 };
25532550
2554 updateSegfaultHandler(&act) catch {2551 updateSegfaultHandler(&act) catch {
...@@ -2564,16 +2561,16 @@ fn resetSegfaultHandler() void {...@@ -2564,16 +2561,16 @@ fn resetSegfaultHandler() void {
2564 }2561 }
2565 return;2562 return;
2566 }2563 }
2567 var act = os.Sigaction{2564 var act = posix.Sigaction{
2568 .handler = .{ .handler = os.SIG.DFL },2565 .handler = .{ .handler = posix.SIG.DFL },
2569 .mask = os.empty_sigset,2566 .mask = posix.empty_sigset,
2570 .flags = 0,2567 .flags = 0,
2571 };2568 };
2572 // To avoid a double-panic, do nothing if an error happens here.2569 // To avoid a double-panic, do nothing if an error happens here.
2573 updateSegfaultHandler(&act) catch {};2570 updateSegfaultHandler(&act) catch {};
2574}2571}
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 {
2577 // Reset to the default handler so that if a segfault happens in this handler it will crash2574 // Reset to the default handler so that if a segfault happens in this handler it will crash
2578 // the process. Also when this handler returns, the original instruction will be repeated2575 // the process. Also when this handler returns, the original instruction will be repeated
2579 // and the resulting segfault will crash the process rather than continually dump stack traces.2576 // 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...@@ -2612,13 +2609,13 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any
2612 // We cannot allow the signal handler to return because when it runs the original instruction2609 // We cannot allow the signal handler to return because when it runs the original instruction
2613 // again, the memory may be mapped and undefined behavior would occur rather than repeating2610 // again, the memory may be mapped and undefined behavior would occur rather than repeating
2614 // the segfault. So we simply abort here.2611 // the segfault. So we simply abort here.
2615 os.abort();2612 posix.abort();
2616}2613}
26172614
2618fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*const anyopaque) void {2615fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*const anyopaque) void {
2619 const stderr = io.getStdErr().writer();2616 const stderr = io.getStdErr().writer();
2620 _ = switch (sig) {2617 _ = switch (sig) {
2621 os.SIG.SEGV => if (native_arch == .x86_64 and native_os == .linux and code == 128) // SI_KERNEL2618 posix.SIG.SEGV => if (native_arch == .x86_64 and native_os == .linux and code == 128) // SI_KERNEL
2622 // x86_64 doesn't have a full 64-bit virtual address space.2619 // x86_64 doesn't have a full 64-bit virtual address space.
2623 // Addresses outside of that address space are non-canonical2620 // Addresses outside of that address space are non-canonical
2624 // and the CPU won't provide the faulting address to us.2621 // 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...@@ -2629,11 +2626,11 @@ fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*const anyo
2629 stderr.print("General protection exception (no address available)\n", .{})2626 stderr.print("General protection exception (no address available)\n", .{})
2630 else2627 else
2631 stderr.print("Segmentation fault at address 0x{x}\n", .{addr}),2628 stderr.print("Segmentation fault at address 0x{x}\n", .{addr}),
2632 os.SIG.ILL => stderr.print("Illegal instruction at address 0x{x}\n", .{addr}),2629 posix.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}),2630 posix.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}),2631 posix.SIG.FPE => stderr.print("Arithmetic exception at address 0x{x}\n", .{addr}),
2635 else => unreachable,2632 else => unreachable,
2636 } catch os.abort();2633 } catch posix.abort();
26372634
2638 switch (native_arch) {2635 switch (native_arch) {
2639 .x86,2636 .x86,
...@@ -2641,7 +2638,7 @@ fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*const anyo...@@ -2641,7 +2638,7 @@ fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*const anyo
2641 .arm,2638 .arm,
2642 .aarch64,2639 .aarch64,
2643 => {2640 => {
2644 const ctx: *const os.ucontext_t = @ptrCast(@alignCast(ctx_ptr));2641 const ctx: *const posix.ucontext_t = @ptrCast(@alignCast(ctx_ptr));
2645 dumpStackTraceFromBase(ctx);2642 dumpStackTraceFromBase(ctx);
2646 },2643 },
2647 else => {},2644 else => {},
...@@ -2684,7 +2681,7 @@ fn handleSegfaultWindowsExtra(...@@ -2684,7 +2681,7 @@ fn handleSegfaultWindowsExtra(
2684 dumpSegfaultInfoWindows(info, msg, label);2681 dumpSegfaultInfoWindows(info, msg, label);
2685 },2682 },
2686 };2683 };
2687 os.abort();2684 posix.abort();
2688 } else {2685 } else {
2689 switch (msg) {2686 switch (msg) {
2690 0 => panicImpl(null, exception_address, "{s}", label.?),2687 0 => panicImpl(null, exception_address, "{s}", label.?),
...@@ -2707,7 +2704,7 @@ fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[...@@ -2707,7 +2704,7 @@ fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[
2707 1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}),2704 1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}),
2708 2 => stderr.print("Illegal instruction at address 0x{x}\n", .{info.ContextRecord.getRegs().ip}),2705 2 => stderr.print("Illegal instruction at address 0x{x}\n", .{info.ContextRecord.getRegs().ip}),
2709 else => unreachable,2706 else => unreachable,
2710 } catch os.abort();2707 } catch posix.abort();
27112708
2712 dumpStackTraceFromBase(info.ContextRecord);2709 dumpStackTraceFromBase(info.ContextRecord);
2713}2710}
...@@ -2722,9 +2719,9 @@ pub fn dumpStackPointerAddr(prefix: []const u8) void {...@@ -2722,9 +2719,9 @@ pub fn dumpStackPointerAddr(prefix: []const u8) void {
2722test "manage resources correctly" {2719test "manage resources correctly" {
2723 if (builtin.strip_debug_info) return error.SkipZigTest;2720 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) {
2728 // https://github.com/ziglang/zig/issues/139632725 // https://github.com/ziglang/zig/issues/13963
2729 return error.SkipZigTest;2726 return error.SkipZigTest;
2730 }2727 }
lib/std/dwarf.zig+2-2
...@@ -2188,12 +2188,12 @@ pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void {...@@ -2188,12 +2188,12 @@ pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void {
2188/// This function is to make it handy to comment out the return and make it2188/// This function is to make it handy to comment out the return and make it
2189/// into a crash when working on this file.2189/// into a crash when working on this file.
2190fn badDwarf() error{InvalidDebugInfo} {2190fn badDwarf() error{InvalidDebugInfo} {
2191 //std.os.abort(); // can be handy to uncomment when working on this file2191 //if (true) @panic("badDwarf"); // can be handy to uncomment when working on this file
2192 return error.InvalidDebugInfo;2192 return error.InvalidDebugInfo;
2193}2193}
21942194
2195fn missingDwarf() error{MissingDebugInfo} {2195fn missingDwarf() error{MissingDebugInfo} {
2196 //std.os.abort(); // can be handy to uncomment when working on this file2196 //if (true) @panic("missingDwarf"); // can be handy to uncomment when working on this file
2197 return error.MissingDebugInfo;2197 return error.MissingDebugInfo;
2198}2198}
21992199
lib/std/dwarf/abi.zig+43-42
...@@ -1,7 +1,8 @@...@@ -1,7 +1,8 @@
1const builtin = @import("builtin");1const builtin = @import("builtin");
2const std = @import("../std.zig");2const std = @import("../std.zig");
3const os = std.os;
4const mem = std.mem;3const mem = std.mem;
4const native_os = builtin.os.tag;
5const posix = std.posix;
56
6pub fn supportsUnwinding(target: std.Target) bool {7pub fn supportsUnwinding(target: std.Target) bool {
7 return switch (target.cpu.arch) {8 return switch (target.cpu.arch) {
...@@ -138,7 +139,7 @@ pub fn regBytes(...@@ -138,7 +139,7 @@ pub fn regBytes(
138 reg_number: u8,139 reg_number: u8,
139 reg_context: ?RegisterContext,140 reg_context: ?RegisterContext,
140) AbiError!RegBytesReturnType(@TypeOf(thread_context_ptr)) {141) AbiError!RegBytesReturnType(@TypeOf(thread_context_ptr)) {
141 if (builtin.os.tag == .windows) {142 if (native_os == .windows) {
142 return switch (builtin.cpu.arch) {143 return switch (builtin.cpu.arch) {
143 .x86 => switch (reg_number) {144 .x86 => switch (reg_number) {
144 0 => mem.asBytes(&thread_context_ptr.Eax),145 0 => mem.asBytes(&thread_context_ptr.Eax),
...@@ -193,61 +194,61 @@ pub fn regBytes(...@@ -193,61 +194,61 @@ pub fn regBytes(
193194
194 const ucontext_ptr = thread_context_ptr;195 const ucontext_ptr = thread_context_ptr;
195 return switch (builtin.cpu.arch) {196 return switch (builtin.cpu.arch) {
196 .x86 => switch (builtin.os.tag) {197 .x86 => switch (native_os) {
197 .linux, .netbsd, .solaris, .illumos => switch (reg_number) {198 .linux, .netbsd, .solaris, .illumos => switch (reg_number) {
198 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EAX]),199 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EAX]),
199 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]),200 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ECX]),
200 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]),201 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EDX]),
201 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBX]),202 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBX]),
202 4...5 => if (reg_context) |r| bytes: {203 4...5 => if (reg_context) |r| bytes: {
203 if (reg_number == 4) {204 if (reg_number == 4) {
204 break :bytes if (r.eh_frame and r.is_macho)205 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])
206 else207 else
207 mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESP]);208 mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESP]);
208 } else {209 } else {
209 break :bytes if (r.eh_frame and r.is_macho)210 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])
211 else212 else
212 mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP]);213 mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBP]);
213 }214 }
214 } else error.RegisterContextRequired,215 } else error.RegisterContextRequired,
215 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESI]),216 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESI]),
216 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDI]),217 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EDI]),
217 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EIP]),218 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EIP]),
218 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EFL]),219 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EFL]),
219 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.CS]),220 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.CS]),
220 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.SS]),221 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.SS]),
221 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.DS]),222 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.DS]),
222 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ES]),223 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ES]),
223 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]),224 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.FS]),
224 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]),225 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.GS]),
225 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs226 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs
226 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs227 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs
227 else => error.InvalidRegister,228 else => error.InvalidRegister,
228 },229 },
229 else => error.UnimplementedOs,230 else => error.UnimplementedOs,
230 },231 },
231 .x86_64 => switch (builtin.os.tag) {232 .x86_64 => switch (native_os) {
232 .linux, .solaris, .illumos => switch (reg_number) {233 .linux, .solaris, .illumos => switch (reg_number) {
233 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RAX]),234 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RAX]),
234 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RDX]),235 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDX]),
235 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RCX]),236 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RCX]),
236 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RBX]),237 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RBX]),
237 4 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RSI]),238 4 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RSI]),
238 5 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RDI]),239 5 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDI]),
239 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RBP]),240 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RBP]),
240 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RSP]),241 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RSP]),
241 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R8]),242 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R8]),
242 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R9]),243 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R9]),
243 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R10]),244 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R10]),
244 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R11]),245 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R11]),
245 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R12]),246 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R12]),
246 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R13]),247 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R13]),
247 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R14]),248 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R14]),
248 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R15]),249 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R15]),
249 16 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RIP]),250 16 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RIP]),
250 17...32 => |i| if (builtin.os.tag.isSolarish())251 17...32 => |i| if (native_os.isSolarish())
251 mem.asBytes(&ucontext_ptr.mcontext.fpregs.chip_state.xmm[i - 17])252 mem.asBytes(&ucontext_ptr.mcontext.fpregs.chip_state.xmm[i - 17])
252 else253 else
253 mem.asBytes(&ucontext_ptr.mcontext.fpregs.xmm[i - 17]),254 mem.asBytes(&ucontext_ptr.mcontext.fpregs.xmm[i - 17]),
...@@ -317,7 +318,7 @@ pub fn regBytes(...@@ -317,7 +318,7 @@ pub fn regBytes(
317 },318 },
318 else => error.UnimplementedOs,319 else => error.UnimplementedOs,
319 },320 },
320 .arm => switch (builtin.os.tag) {321 .arm => switch (native_os) {
321 .linux => switch (reg_number) {322 .linux => switch (reg_number) {
322 0 => mem.asBytes(&ucontext_ptr.mcontext.arm_r0),323 0 => mem.asBytes(&ucontext_ptr.mcontext.arm_r0),
323 1 => mem.asBytes(&ucontext_ptr.mcontext.arm_r1),324 1 => mem.asBytes(&ucontext_ptr.mcontext.arm_r1),
...@@ -340,7 +341,7 @@ pub fn regBytes(...@@ -340,7 +341,7 @@ pub fn regBytes(
340 },341 },
341 else => error.UnimplementedOs,342 else => error.UnimplementedOs,
342 },343 },
343 .aarch64 => switch (builtin.os.tag) {344 .aarch64 => switch (native_os) {
344 .macos, .ios => switch (reg_number) {345 .macos, .ios => switch (reg_number) {
345 0...28 => mem.asBytes(&ucontext_ptr.mcontext.ss.regs[reg_number]),346 0...28 => mem.asBytes(&ucontext_ptr.mcontext.ss.regs[reg_number]),
346 29 => mem.asBytes(&ucontext_ptr.mcontext.ss.fp),347 29 => mem.asBytes(&ucontext_ptr.mcontext.ss.fp),
lib/std/dynamic_library.zig+30-30
...@@ -1,16 +1,16 @@...@@ -1,16 +1,16 @@
1const std = @import("std.zig");1const std = @import("std.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const mem = std.mem;3const mem = std.mem;
4const os = std.os;
5const testing = std.testing;4const testing = std.testing;
6const elf = std.elf;5const elf = std.elf;
7const windows = std.os.windows;6const windows = std.os.windows;
8const system = std.os.system;7const native_os = builtin.os.tag;
8const posix = std.posix;
99
10/// Cross-platform dynamic library loading and symbol lookup.10/// Cross-platform dynamic library loading and symbol lookup.
11/// Platform-specific functionality is available through the `inner` field.11/// Platform-specific functionality is available through the `inner` field.
12pub const DynLib = struct {12pub const DynLib = struct {
13 const InnerType = switch (builtin.os.tag) {13 const InnerType = switch (native_os) {
14 .linux => if (!builtin.link_libc or builtin.abi == .musl and builtin.link_mode == .static)14 .linux => if (!builtin.link_libc or builtin.abi == .musl and builtin.link_mode == .static)
15 ElfDynLib15 ElfDynLib
16 else16 else
...@@ -125,7 +125,7 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) error{InvalidExe}!LinkMap.Iterator {...@@ -125,7 +125,7 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) error{InvalidExe}!LinkMap.Iterator {
125pub const ElfDynLib = struct {125pub const ElfDynLib = struct {
126 strings: [*:0]u8,126 strings: [*:0]u8,
127 syms: [*]elf.Sym,127 syms: [*]elf.Sym,
128 hashtab: [*]os.Elf_Symndx,128 hashtab: [*]posix.Elf_Symndx,
129 versym: ?[*]u16,129 versym: ?[*]u16,
130 verdef: ?*elf.Verdef,130 verdef: ?*elf.Verdef,
131 memory: []align(mem.page_size) u8,131 memory: []align(mem.page_size) u8,
...@@ -138,27 +138,27 @@ pub const ElfDynLib = struct {...@@ -138,27 +138,27 @@ pub const ElfDynLib = struct {
138 ElfStringSectionNotFound,138 ElfStringSectionNotFound,
139 ElfSymSectionNotFound,139 ElfSymSectionNotFound,
140 ElfHashTableNotFound,140 ElfHashTableNotFound,
141 } || os.OpenError || os.MMapError;141 } || posix.OpenError || posix.MMapError;
142142
143 /// Trusts the file. Malicious file will be able to execute arbitrary code.143 /// Trusts the file. Malicious file will be able to execute arbitrary code.
144 pub fn open(path: []const u8) Error!ElfDynLib {144 pub fn open(path: []const u8) Error!ElfDynLib {
145 const fd = try os.open(path, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);145 const fd = try posix.open(path, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
146 defer os.close(fd);146 defer posix.close(fd);
147147
148 const stat = try os.fstat(fd);148 const stat = try posix.fstat(fd);
149 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;149 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;
150150
151 // This one is to read the ELF info. We do more mmapping later151 // This one is to read the ELF info. We do more mmapping later
152 // corresponding to the actual LOAD sections.152 // corresponding to the actual LOAD sections.
153 const file_bytes = try os.mmap(153 const file_bytes = try posix.mmap(
154 null,154 null,
155 mem.alignForward(usize, size, mem.page_size),155 mem.alignForward(usize, size, mem.page_size),
156 os.PROT.READ,156 posix.PROT.READ,
157 .{ .TYPE = .PRIVATE },157 .{ .TYPE = .PRIVATE },
158 fd,158 fd,
159 0,159 0,
160 );160 );
161 defer os.munmap(file_bytes);161 defer posix.munmap(file_bytes);
162162
163 const eh = @as(*elf.Ehdr, @ptrCast(file_bytes.ptr));163 const eh = @as(*elf.Ehdr, @ptrCast(file_bytes.ptr));
164 if (!mem.eql(u8, eh.e_ident[0..4], elf.MAGIC)) return error.NotElfFile;164 if (!mem.eql(u8, eh.e_ident[0..4], elf.MAGIC)) return error.NotElfFile;
...@@ -188,15 +188,15 @@ pub const ElfDynLib = struct {...@@ -188,15 +188,15 @@ pub const ElfDynLib = struct {
188 const dynv = maybe_dynv orelse return error.MissingDynamicLinkingInformation;188 const dynv = maybe_dynv orelse return error.MissingDynamicLinkingInformation;
189189
190 // Reserve the entire range (with no permissions) so that we can do MAP.FIXED below.190 // 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(
192 null,192 null,
193 virt_addr_end,193 virt_addr_end,
194 os.PROT.NONE,194 posix.PROT.NONE,
195 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },195 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
196 -1,196 -1,
197 0,197 0,
198 );198 );
199 errdefer os.munmap(all_loaded_mem);199 errdefer posix.munmap(all_loaded_mem);
200200
201 const base = @intFromPtr(all_loaded_mem.ptr);201 const base = @intFromPtr(all_loaded_mem.ptr);
202202
...@@ -220,7 +220,7 @@ pub const ElfDynLib = struct {...@@ -220,7 +220,7 @@ pub const ElfDynLib = struct {
220 const prot = elfToMmapProt(ph.p_flags);220 const prot = elfToMmapProt(ph.p_flags);
221 if ((ph.p_flags & elf.PF_W) == 0) {221 if ((ph.p_flags & elf.PF_W) == 0) {
222 // If it does not need write access, it can be mapped from the fd.222 // If it does not need write access, it can be mapped from the fd.
223 _ = try os.mmap(223 _ = try posix.mmap(
224 ptr,224 ptr,
225 extended_memsz,225 extended_memsz,
226 prot,226 prot,
...@@ -229,7 +229,7 @@ pub const ElfDynLib = struct {...@@ -229,7 +229,7 @@ pub const ElfDynLib = struct {
229 ph.p_offset - extra_bytes,229 ph.p_offset - extra_bytes,
230 );230 );
231 } else {231 } else {
232 const sect_mem = try os.mmap(232 const sect_mem = try posix.mmap(
233 ptr,233 ptr,
234 extended_memsz,234 extended_memsz,
235 prot,235 prot,
...@@ -247,7 +247,7 @@ pub const ElfDynLib = struct {...@@ -247,7 +247,7 @@ pub const ElfDynLib = struct {
247247
248 var maybe_strings: ?[*:0]u8 = null;248 var maybe_strings: ?[*:0]u8 = null;
249 var maybe_syms: ?[*]elf.Sym = null;249 var maybe_syms: ?[*]elf.Sym = null;
250 var maybe_hashtab: ?[*]os.Elf_Symndx = null;250 var maybe_hashtab: ?[*]posix.Elf_Symndx = null;
251 var maybe_versym: ?[*]u16 = null;251 var maybe_versym: ?[*]u16 = null;
252 var maybe_verdef: ?*elf.Verdef = null;252 var maybe_verdef: ?*elf.Verdef = null;
253253
...@@ -258,7 +258,7 @@ pub const ElfDynLib = struct {...@@ -258,7 +258,7 @@ pub const ElfDynLib = struct {
258 switch (dynv[i]) {258 switch (dynv[i]) {
259 elf.DT_STRTAB => maybe_strings = @as([*:0]u8, @ptrFromInt(p)),259 elf.DT_STRTAB => maybe_strings = @as([*:0]u8, @ptrFromInt(p)),
260 elf.DT_SYMTAB => maybe_syms = @as([*]elf.Sym, @ptrFromInt(p)),260 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)),
262 elf.DT_VERSYM => maybe_versym = @as([*]u16, @ptrFromInt(p)),262 elf.DT_VERSYM => maybe_versym = @as([*]u16, @ptrFromInt(p)),
263 elf.DT_VERDEF => maybe_verdef = @as(*elf.Verdef, @ptrFromInt(p)),263 elf.DT_VERDEF => maybe_verdef = @as(*elf.Verdef, @ptrFromInt(p)),
264 else => {},264 else => {},
...@@ -283,7 +283,7 @@ pub const ElfDynLib = struct {...@@ -283,7 +283,7 @@ pub const ElfDynLib = struct {
283283
284 /// Trusts the file284 /// Trusts the file
285 pub fn close(self: *ElfDynLib) void {285 pub fn close(self: *ElfDynLib) void {
286 os.munmap(self.memory);286 posix.munmap(self.memory);
287 self.* = undefined;287 self.* = undefined;
288 }288 }
289289
...@@ -320,10 +320,10 @@ pub const ElfDynLib = struct {...@@ -320,10 +320,10 @@ pub const ElfDynLib = struct {
320 }320 }
321321
322 fn elfToMmapProt(elf_prot: u64) u32 {322 fn elfToMmapProt(elf_prot: u64) u32 {
323 var result: u32 = os.PROT.NONE;323 var result: u32 = posix.PROT.NONE;
324 if ((elf_prot & elf.PF_R) != 0) result |= os.PROT.READ;324 if ((elf_prot & elf.PF_R) != 0) result |= posix.PROT.READ;
325 if ((elf_prot & elf.PF_W) != 0) result |= os.PROT.WRITE;325 if ((elf_prot & elf.PF_W) != 0) result |= posix.PROT.WRITE;
326 if ((elf_prot & elf.PF_X) != 0) result |= os.PROT.EXEC;326 if ((elf_prot & elf.PF_X) != 0) result |= posix.PROT.EXEC;
327 return result;327 return result;
328 }328 }
329};329};
...@@ -343,7 +343,7 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [...@@ -343,7 +343,7 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [
343}343}
344344
345test "ElfDynLib" {345test "ElfDynLib" {
346 if (builtin.os.tag != .linux) {346 if (native_os != .linux) {
347 return error.SkipZigTest;347 return error.SkipZigTest;
348 }348 }
349349
...@@ -419,20 +419,20 @@ pub const DlDynLib = struct {...@@ -419,20 +419,20 @@ pub const DlDynLib = struct {
419 handle: *anyopaque,419 handle: *anyopaque,
420420
421 pub fn open(path: []const u8) Error!DlDynLib {421 pub fn open(path: []const u8) Error!DlDynLib {
422 const path_c = try os.toPosixPath(path);422 const path_c = try posix.toPosixPath(path);
423 return openZ(&path_c);423 return openZ(&path_c);
424 }424 }
425425
426 pub fn openZ(path_c: [*:0]const u8) Error!DlDynLib {426 pub fn openZ(path_c: [*:0]const u8) Error!DlDynLib {
427 return .{427 return .{
428 .handle = system.dlopen(path_c, system.RTLD.LAZY) orelse {428 .handle = std.c.dlopen(path_c, std.c.RTLD.LAZY) orelse {
429 return error.FileNotFound;429 return error.FileNotFound;
430 },430 },
431 };431 };
432 }432 }
433433
434 pub fn close(self: *DlDynLib) void {434 pub fn close(self: *DlDynLib) void {
435 switch (std.os.errno(system.dlclose(self.handle))) {435 switch (posix.errno(std.c.dlclose(self.handle))) {
436 .SUCCESS => return,436 .SUCCESS => return,
437 else => unreachable,437 else => unreachable,
438 }438 }
...@@ -442,7 +442,7 @@ pub const DlDynLib = struct {...@@ -442,7 +442,7 @@ pub const DlDynLib = struct {
442 pub fn lookup(self: *DlDynLib, comptime T: type, name: [:0]const u8) ?T {442 pub fn lookup(self: *DlDynLib, comptime T: type, name: [:0]const u8) ?T {
443 // dlsym (and other dl-functions) secretly take shadow parameter - return address on stack443 // dlsym (and other dl-functions) secretly take shadow parameter - return address on stack
444 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66826444 // 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| {
446 return @as(T, @ptrCast(@alignCast(symbol)));446 return @as(T, @ptrCast(@alignCast(symbol)));
447 } else {447 } else {
448 return null;448 return null;
...@@ -453,12 +453,12 @@ pub const DlDynLib = struct {...@@ -453,12 +453,12 @@ pub const DlDynLib = struct {
453 /// Returns human readable string describing most recent error than occurred from `lookup`453 /// Returns human readable string describing most recent error than occurred from `lookup`
454 /// or `null` if no error has occurred since initialization or when `getError` was last called.454 /// or `null` if no error has occurred since initialization or when `getError` was last called.
455 pub fn getError() ?[:0]const u8 {455 pub fn getError() ?[:0]const u8 {
456 return mem.span(system.dlerror());456 return mem.span(std.c.dlerror());
457 }457 }
458};458};
459459
460test "dynamic_library" {460test "dynamic_library" {
461 const libname = switch (builtin.os.tag) {461 const libname = switch (native_os) {
462 .linux, .freebsd, .openbsd, .solaris, .illumos => "invalid_so.so",462 .linux, .freebsd, .openbsd, .solaris, .illumos => "invalid_so.so",
463 .windows => "invalid_dll.dll",463 .windows => "invalid_dll.dll",
464 .macos, .tvos, .watchos, .ios => "invalid_dylib.dylib",464 .macos, .tvos, .watchos, .ios => "invalid_dylib.dylib",
lib/std/fs.zig+89-82
...@@ -3,21 +3,23 @@...@@ -3,21 +3,23 @@
3const std = @import("std.zig");3const std = @import("std.zig");
4const builtin = @import("builtin");4const builtin = @import("builtin");
5const root = @import("root");5const root = @import("root");
6const os = std.os;
7const mem = std.mem;6const mem = std.mem;
8const base64 = std.base64;7const base64 = std.base64;
9const crypto = std.crypto;8const crypto = std.crypto;
10const Allocator = std.mem.Allocator;9const Allocator = std.mem.Allocator;
11const assert = std.debug.assert;10const 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
15pub const AtomicFile = @import("fs/AtomicFile.zig");17pub const AtomicFile = @import("fs/AtomicFile.zig");
16pub const Dir = @import("fs/Dir.zig");18pub const Dir = @import("fs/Dir.zig");
17pub const File = @import("fs/File.zig");19pub const File = @import("fs/File.zig");
18pub const path = @import("fs/path.zig");20pub const path = @import("fs/path.zig");
1921
20pub const has_executable_bit = switch (builtin.os.tag) {22pub const has_executable_bit = switch (native_os) {
21 .windows, .wasi => false,23 .windows, .wasi => false,
22 else => true,24 else => true,
23};25};
...@@ -26,36 +28,41 @@ pub const wasi = @import("fs/wasi.zig");...@@ -26,36 +28,41 @@ pub const wasi = @import("fs/wasi.zig");
2628
27// TODO audit these APIs with respect to Dir and absolute paths29// TODO audit these APIs with respect to Dir and absolute paths
2830
29pub const realpath = os.realpath;31pub const realpath = posix.realpath;
30pub const realpathZ = os.realpathZ;32pub const realpathZ = posix.realpathZ;
31pub const realpathW = os.realpathW;33pub const realpathW = posix.realpathW;
3234
33pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir;35pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir;
34pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError;36pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError;
3537
36/// This represents the maximum size of a `[]u8` file path that the38/// Deprecated: use `max_path_bytes`.
37/// operating system will accept. Paths, including those returned from file39pub const MAX_PATH_BYTES = max_path_bytes;
38/// system operations, may be longer than this length, but such paths cannot40
39/// be successfully passed back in other file system operations. However,41/// The maximum length of a file path that the operating system will accept.
40/// all path components returned by file system operations are assumed to42///
41/// fit into a `u8` array of this length.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///
42/// The byte count includes room for a null sentinel byte.48/// 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/).49///
44/// On WASI, `[]u8` file paths are encoded as valid UTF-8.50/// * On Windows, `[]u8` file paths are encoded as
45/// On other platforms, `[]u8` file paths are opaque sequences of bytes with no particular encoding.51/// [WTF-8](https://simonsapin.github.io/wtf-8/).
46pub const MAX_PATH_BYTES = switch (builtin.os.tag) {52/// * On WASI, `[]u8` file paths are encoded as valid UTF-8.
47 .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .haiku, .solaris, .illumos, .plan9, .emscripten => os.PATH_MAX,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, .wasi => posix.PATH_MAX,
48 // Each WTF-16LE code unit may be expanded to 3 WTF-8 bytes.57 // Each WTF-16LE code unit may be expanded to 3 WTF-8 bytes.
49 // If it would require 4 WTF-8 bytes, then there would be a surrogate58 // If it would require 4 WTF-8 bytes, then there would be a surrogate
50 // pair in the WTF-16LE, and we (over)account 3 bytes for it that way.59 // pair in the WTF-16LE, and we (over)account 3 bytes for it that way.
51 // +1 for the null byte at the end, which can be encoded in 1 byte.60 // +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,
53 // TODO work out what a reasonable value we should use here
54 .wasi => 4096,
55 else => if (@hasDecl(root, "os") and @hasDecl(root.os, "PATH_MAX"))62 else => if (@hasDecl(root, "os") and @hasDecl(root.os, "PATH_MAX"))
56 root.os.PATH_MAX63 root.os.PATH_MAX
57 else64 else
58 @compileError("PATH_MAX not implemented for " ++ @tagName(builtin.os.tag)),65 @compileError("PATH_MAX not implemented for " ++ @tagName(native_os)),
59};66};
6067
61/// This represents the maximum size of a `[]u8` file name component that68/// This represents the maximum size of a `[]u8` file name component that
...@@ -66,22 +73,22 @@ pub const MAX_PATH_BYTES = switch (builtin.os.tag) {...@@ -66,22 +73,22 @@ pub const MAX_PATH_BYTES = switch (builtin.os.tag) {
66/// On Windows, `[]u8` file name components are encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).73/// On Windows, `[]u8` file name components are encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
67/// On WASI, file name components are encoded as valid UTF-8.74/// On WASI, file name components are encoded as valid UTF-8.
68/// On other platforms, `[]u8` components are an opaque sequence of bytes with no particular encoding.75/// On other platforms, `[]u8` components are an opaque sequence of bytes with no particular encoding.
69pub const MAX_NAME_BYTES = switch (builtin.os.tag) {76pub const MAX_NAME_BYTES = switch (native_os) {
70 .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .illumos => os.NAME_MAX,77 .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .illumos => posix.NAME_MAX,
71 // Haiku's NAME_MAX includes the null terminator, so subtract one.78 // Haiku's NAME_MAX includes the null terminator, so subtract one.
72 .haiku => os.NAME_MAX - 1,79 .haiku => posix.NAME_MAX - 1,
73 // Each WTF-16LE character may be expanded to 3 WTF-8 bytes.80 // Each WTF-16LE character may be expanded to 3 WTF-8 bytes.
74 // If it would require 4 WTF-8 bytes, then there would be a surrogate81 // If it would require 4 WTF-8 bytes, then there would be a surrogate
75 // pair in the WTF-16LE, and we (over)account 3 bytes for it that way.82 // pair in the WTF-16LE, and we (over)account 3 bytes for it that way.
76 .windows => os.windows.NAME_MAX * 3,83 .windows => windows.NAME_MAX * 3,
77 // For WASI, the MAX_NAME will depend on the host OS, so it needs to be84 // For WASI, the MAX_NAME will depend on the host OS, so it needs to be
78 // as large as the largest MAX_NAME_BYTES (Windows) in order to work on any host OS.85 // as large as the largest MAX_NAME_BYTES (Windows) in order to work on any host OS.
79 // TODO determine if this is a reasonable approach86 // TODO determine if this is a reasonable approach
80 .wasi => os.windows.NAME_MAX * 3,87 .wasi => windows.NAME_MAX * 3,
81 else => if (@hasDecl(root, "os") and @hasDecl(root.os, "NAME_MAX"))88 else => if (@hasDecl(root, "os") and @hasDecl(root.os, "NAME_MAX"))
82 root.os.NAME_MAX89 root.os.NAME_MAX
83 else90 else
84 @compileError("NAME_MAX not implemented for " ++ @tagName(builtin.os.tag)),91 @compileError("NAME_MAX not implemented for " ++ @tagName(native_os)),
85};92};
8693
87pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*;94pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*;
...@@ -167,19 +174,19 @@ pub fn copyFileAbsolute(...@@ -167,19 +174,19 @@ pub fn copyFileAbsolute(
167/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding.174/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding.
168pub fn makeDirAbsolute(absolute_path: []const u8) !void {175pub fn makeDirAbsolute(absolute_path: []const u8) !void {
169 assert(path.isAbsolute(absolute_path));176 assert(path.isAbsolute(absolute_path));
170 return os.mkdir(absolute_path, Dir.default_mode);177 return posix.mkdir(absolute_path, Dir.default_mode);
171}178}
172179
173/// Same as `makeDirAbsolute` except the parameter is null-terminated.180/// Same as `makeDirAbsolute` except the parameter is null-terminated.
174pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void {181pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void {
175 assert(path.isAbsoluteZ(absolute_path_z));182 assert(path.isAbsoluteZ(absolute_path_z));
176 return os.mkdirZ(absolute_path_z, Dir.default_mode);183 return posix.mkdirZ(absolute_path_z, Dir.default_mode);
177}184}
178185
179/// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 LE-encoded string.186/// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 LE-encoded string.
180pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {187pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {
181 assert(path.isAbsoluteWindowsW(absolute_path_w));188 assert(path.isAbsoluteWindowsW(absolute_path_w));
182 return os.mkdirW(absolute_path_w, Dir.default_mode);189 return posix.mkdirW(absolute_path_w, Dir.default_mode);
183}190}
184191
185/// Same as `Dir.deleteDir` except the path is absolute.192/// Same as `Dir.deleteDir` except the path is absolute.
...@@ -188,19 +195,19 @@ pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {...@@ -188,19 +195,19 @@ pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {
188/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.195/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
189pub fn deleteDirAbsolute(dir_path: []const u8) !void {196pub fn deleteDirAbsolute(dir_path: []const u8) !void {
190 assert(path.isAbsolute(dir_path));197 assert(path.isAbsolute(dir_path));
191 return os.rmdir(dir_path);198 return posix.rmdir(dir_path);
192}199}
193200
194/// Same as `deleteDirAbsolute` except the path parameter is null-terminated.201/// Same as `deleteDirAbsolute` except the path parameter is null-terminated.
195pub fn deleteDirAbsoluteZ(dir_path: [*:0]const u8) !void {202pub fn deleteDirAbsoluteZ(dir_path: [*:0]const u8) !void {
196 assert(path.isAbsoluteZ(dir_path));203 assert(path.isAbsoluteZ(dir_path));
197 return os.rmdirZ(dir_path);204 return posix.rmdirZ(dir_path);
198}205}
199206
200/// Same as `deleteDirAbsolute` except the path parameter is WTF-16 and target OS is assumed Windows.207/// Same as `deleteDirAbsolute` except the path parameter is WTF-16 and target OS is assumed Windows.
201pub fn deleteDirAbsoluteW(dir_path: [*:0]const u16) !void {208pub fn deleteDirAbsoluteW(dir_path: [*:0]const u16) !void {
202 assert(path.isAbsoluteWindowsW(dir_path));209 assert(path.isAbsoluteWindowsW(dir_path));
203 return os.rmdirW(dir_path);210 return posix.rmdirW(dir_path);
204}211}
205212
206/// Same as `Dir.rename` except the paths are absolute.213/// Same as `Dir.rename` except the paths are absolute.
...@@ -210,49 +217,49 @@ pub fn deleteDirAbsoluteW(dir_path: [*:0]const u16) !void {...@@ -210,49 +217,49 @@ pub fn deleteDirAbsoluteW(dir_path: [*:0]const u16) !void {
210pub fn renameAbsolute(old_path: []const u8, new_path: []const u8) !void {217pub fn renameAbsolute(old_path: []const u8, new_path: []const u8) !void {
211 assert(path.isAbsolute(old_path));218 assert(path.isAbsolute(old_path));
212 assert(path.isAbsolute(new_path));219 assert(path.isAbsolute(new_path));
213 return os.rename(old_path, new_path);220 return posix.rename(old_path, new_path);
214}221}
215222
216/// Same as `renameAbsolute` except the path parameters are null-terminated.223/// Same as `renameAbsolute` except the path parameters are null-terminated.
217pub fn renameAbsoluteZ(old_path: [*:0]const u8, new_path: [*:0]const u8) !void {224pub fn renameAbsoluteZ(old_path: [*:0]const u8, new_path: [*:0]const u8) !void {
218 assert(path.isAbsoluteZ(old_path));225 assert(path.isAbsoluteZ(old_path));
219 assert(path.isAbsoluteZ(new_path));226 assert(path.isAbsoluteZ(new_path));
220 return os.renameZ(old_path, new_path);227 return posix.renameZ(old_path, new_path);
221}228}
222229
223/// Same as `renameAbsolute` except the path parameters are WTF-16 and target OS is assumed Windows.230/// Same as `renameAbsolute` except the path parameters are WTF-16 and target OS is assumed Windows.
224pub fn renameAbsoluteW(old_path: [*:0]const u16, new_path: [*:0]const u16) !void {231pub fn renameAbsoluteW(old_path: [*:0]const u16, new_path: [*:0]const u16) !void {
225 assert(path.isAbsoluteWindowsW(old_path));232 assert(path.isAbsoluteWindowsW(old_path));
226 assert(path.isAbsoluteWindowsW(new_path));233 assert(path.isAbsoluteWindowsW(new_path));
227 return os.renameW(old_path, new_path);234 return posix.renameW(old_path, new_path);
228}235}
229236
230/// Same as `Dir.rename`, except `new_sub_path` is relative to `new_dir`237/// Same as `Dir.rename`, except `new_sub_path` is relative to `new_dir`
231pub fn rename(old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8) !void {238pub 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);239 return posix.renameat(old_dir.fd, old_sub_path, new_dir.fd, new_sub_path);
233}240}
234241
235/// Same as `rename` except the parameters are null-terminated.242/// Same as `rename` except the parameters are null-terminated.
236pub fn renameZ(old_dir: Dir, old_sub_path_z: [*:0]const u8, new_dir: Dir, new_sub_path_z: [*:0]const u8) !void {243pub 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);244 return posix.renameatZ(old_dir.fd, old_sub_path_z, new_dir.fd, new_sub_path_z);
238}245}
239246
240/// Same as `rename` except the parameters are WTF16LE, NT prefixed.247/// Same as `rename` except the parameters are WTF16LE, NT prefixed.
241/// This function is Windows-only.248/// This function is Windows-only.
242pub fn renameW(old_dir: Dir, old_sub_path_w: []const u16, new_dir: Dir, new_sub_path_w: []const u16) !void {249pub 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);250 return posix.renameatW(old_dir.fd, old_sub_path_w, new_dir.fd, new_sub_path_w);
244}251}
245252
246/// Returns a handle to the current working directory. It is not opened with iteration capability.253/// Returns a handle to the current working directory. It is not opened with iteration capability.
247/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.254/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.
248/// On POSIX targets, this function is comptime-callable.255/// On POSIX targets, this function is comptime-callable.
249pub fn cwd() Dir {256pub fn cwd() Dir {
250 if (builtin.os.tag == .windows) {257 if (native_os == .windows) {
251 return .{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };258 return .{ .fd = windows.peb().ProcessParameters.CurrentDirectory.Handle };
252 } else if (builtin.os.tag == .wasi) {259 } else if (native_os == .wasi) {
253 return .{ .fd = std.options.wasiCwd() };260 return .{ .fd = std.options.wasiCwd() };
254 } else {261 } else {
255 return .{ .fd = os.AT.FDCWD };262 return .{ .fd = posix.AT.FDCWD };
256 }263 }
257}264}
258265
...@@ -412,20 +419,20 @@ pub fn deleteTreeAbsolute(absolute_path: []const u8) !void {...@@ -412,20 +419,20 @@ pub fn deleteTreeAbsolute(absolute_path: []const u8) !void {
412/// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding.419/// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding.
413pub fn readLinkAbsolute(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {420pub fn readLinkAbsolute(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
414 assert(path.isAbsolute(pathname));421 assert(path.isAbsolute(pathname));
415 return os.readlink(pathname, buffer);422 return posix.readlink(pathname, buffer);
416}423}
417424
418/// Windows-only. Same as `readlinkW`, except the path parameter is null-terminated, WTF16425/// Windows-only. Same as `readlinkW`, except the path parameter is null-terminated, WTF16
419/// encoded.426/// encoded.
420pub fn readlinkAbsoluteW(pathname_w: [*:0]const u16, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {427pub fn readlinkAbsoluteW(pathname_w: [*:0]const u16, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
421 assert(path.isAbsoluteWindowsW(pathname_w));428 assert(path.isAbsoluteWindowsW(pathname_w));
422 return os.readlinkW(pathname_w, buffer);429 return posix.readlinkW(pathname_w, buffer);
423}430}
424431
425/// Same as `readLink`, except the path parameter is null-terminated.432/// Same as `readLink`, except the path parameter is null-terminated.
426pub fn readLinkAbsoluteZ(pathname_c: [*:0]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {433pub fn readLinkAbsoluteZ(pathname_c: [*:0]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
427 assert(path.isAbsoluteZ(pathname_c));434 assert(path.isAbsoluteZ(pathname_c));
428 return os.readlinkZ(pathname_c, buffer);435 return posix.readlinkZ(pathname_c, buffer);
429}436}
430437
431/// Creates a symbolic link named `sym_link_path` which contains the string `target_path`.438/// Creates a symbolic link named `sym_link_path` which contains the string `target_path`.
...@@ -443,12 +450,12 @@ pub fn symLinkAbsolute(...@@ -443,12 +450,12 @@ pub fn symLinkAbsolute(
443) !void {450) !void {
444 assert(path.isAbsolute(target_path));451 assert(path.isAbsolute(target_path));
445 assert(path.isAbsolute(sym_link_path));452 assert(path.isAbsolute(sym_link_path));
446 if (builtin.os.tag == .windows) {453 if (native_os == .windows) {
447 const target_path_w = try os.windows.sliceToPrefixedFileW(null, target_path);454 const target_path_w = try windows.sliceToPrefixedFileW(null, target_path);
448 const sym_link_path_w = try os.windows.sliceToPrefixedFileW(null, sym_link_path);455 const sym_link_path_w = try windows.sliceToPrefixedFileW(null, sym_link_path);
449 return os.windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory);456 return windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
450 }457 }
451 return os.symlink(target_path, sym_link_path);458 return posix.symlink(target_path, sym_link_path);
452}459}
453460
454/// Windows-only. Same as `symLinkAbsolute` except the parameters are null-terminated, WTF16 LE encoded.461/// Windows-only. Same as `symLinkAbsolute` except the parameters are null-terminated, WTF16 LE encoded.
...@@ -462,7 +469,7 @@ pub fn symLinkAbsoluteW(...@@ -462,7 +469,7 @@ pub fn symLinkAbsoluteW(
462) !void {469) !void {
463 assert(path.isAbsoluteWindowsWTF16(target_path_w));470 assert(path.isAbsoluteWindowsWTF16(target_path_w));
464 assert(path.isAbsoluteWindowsWTF16(sym_link_path_w));471 assert(path.isAbsoluteWindowsWTF16(sym_link_path_w));
465 return os.windows.CreateSymbolicLink(null, sym_link_path_w, target_path_w, flags.is_directory);472 return windows.CreateSymbolicLink(null, sym_link_path_w, target_path_w, flags.is_directory);
466}473}
467474
468/// Same as `symLinkAbsolute` except the parameters are null-terminated pointers.475/// Same as `symLinkAbsolute` except the parameters are null-terminated pointers.
...@@ -474,27 +481,27 @@ pub fn symLinkAbsoluteZ(...@@ -474,27 +481,27 @@ pub fn symLinkAbsoluteZ(
474) !void {481) !void {
475 assert(path.isAbsoluteZ(target_path_c));482 assert(path.isAbsoluteZ(target_path_c));
476 assert(path.isAbsoluteZ(sym_link_path_c));483 assert(path.isAbsoluteZ(sym_link_path_c));
477 if (builtin.os.tag == .windows) {484 if (native_os == .windows) {
478 const target_path_w = try os.windows.cStrToPrefixedFileW(null, target_path_c);485 const target_path_w = try windows.cStrToPrefixedFileW(null, target_path_c);
479 const sym_link_path_w = try os.windows.cStrToPrefixedFileW(null, sym_link_path_c);486 const sym_link_path_w = try 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);487 return windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
481 }488 }
482 return os.symlinkZ(target_path_c, sym_link_path_c);489 return posix.symlinkZ(target_path_c, sym_link_path_c);
483}490}
484491
485pub const OpenSelfExeError = os.OpenError || SelfExePathError || os.FlockError;492pub const OpenSelfExeError = posix.OpenError || SelfExePathError || posix.FlockError;
486493
487pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {494pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {
488 if (builtin.os.tag == .linux) {495 if (native_os == .linux) {
489 return openFileAbsoluteZ("/proc/self/exe", flags);496 return openFileAbsoluteZ("/proc/self/exe", flags);
490 }497 }
491 if (builtin.os.tag == .windows) {498 if (native_os == .windows) {
492 // If ImagePathName is a symlink, then it will contain the path of the symlink,499 // If ImagePathName is a symlink, then it will contain the path of the symlink,
493 // not the path that the symlink points to. However, because we are opening500 // not the path that the symlink points to. However, because we are opening
494 // the file, we can let the openFileW call follow the symlink for us.501 // the file, we can let the openFileW call follow the symlink for us.
495 const image_path_unicode_string = &os.windows.peb().ProcessParameters.ImagePathName;502 const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
496 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];503 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);504 const prefixed_path_w = try windows.wToPrefixedFileW(null, image_path_name);
498 return cwd().openFileW(prefixed_path_w.span(), flags);505 return cwd().openFileW(prefixed_path_w.span(), flags);
499 }506 }
500 // Use of MAX_PATH_BYTES here is valid as the resulting path is immediately507 // Use of MAX_PATH_BYTES here is valid as the resulting path is immediately
...@@ -505,7 +512,7 @@ pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {...@@ -505,7 +512,7 @@ pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {
505 return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, flags);512 return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, flags);
506}513}
507514
508// This is os.ReadLinkError || os.RealPathError with impossible errors excluded515// This is `posix.ReadLinkError || posix.RealPathError` with impossible errors excluded
509pub const SelfExePathError = error{516pub const SelfExePathError = error{
510 FileNotFound,517 FileNotFound,
511 AccessDenied,518 AccessDenied,
...@@ -542,7 +549,7 @@ pub const SelfExePathError = error{...@@ -542,7 +549,7 @@ pub const SelfExePathError = error{
542 /// On Windows, the volume does not contain a recognized file system. File549 /// On Windows, the volume does not contain a recognized file system. File
543 /// system drivers might not be loaded, or the volume may be corrupt.550 /// system drivers might not be loaded, or the volume may be corrupt.
544 UnrecognizedVolume,551 UnrecognizedVolume,
545} || os.SysCtlError;552} || posix.SysCtlError;
546553
547/// `selfExePath` except allocates the result on the heap.554/// `selfExePath` except allocates the result on the heap.
548/// Caller owns returned memory.555/// Caller owns returned memory.
...@@ -580,7 +587,7 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {...@@ -580,7 +587,7 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
580 if (rc != 0) return error.NameTooLong;587 if (rc != 0) return error.NameTooLong;
581588
582 var real_path_buf: [MAX_PATH_BYTES]u8 = undefined;589 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) {590 const real_path = std.posix.realpathZ(&symlink_path_buf, &real_path_buf) catch |err| switch (err) {
584 error.InvalidWtf8 => unreachable, // Windows-only591 error.InvalidWtf8 => unreachable, // Windows-only
585 error.NetworkNotFound => unreachable, // Windows-only592 error.NetworkNotFound => unreachable, // Windows-only
586 else => |e| return e,593 else => |e| return e,
...@@ -590,15 +597,15 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {...@@ -590,15 +597,15 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
590 @memcpy(result, real_path);597 @memcpy(result, real_path);
591 return result;598 return result;
592 }599 }
593 switch (builtin.os.tag) {600 switch (native_os) {
594 .linux => return os.readlinkZ("/proc/self/exe", out_buffer) catch |err| switch (err) {601 .linux => return posix.readlinkZ("/proc/self/exe", out_buffer) catch |err| switch (err) {
595 error.InvalidUtf8 => unreachable, // WASI-only602 error.InvalidUtf8 => unreachable, // WASI-only
596 error.InvalidWtf8 => unreachable, // Windows-only603 error.InvalidWtf8 => unreachable, // Windows-only
597 error.UnsupportedReparsePointType => unreachable, // Windows-only604 error.UnsupportedReparsePointType => unreachable, // Windows-only
598 error.NetworkNotFound => unreachable, // Windows-only605 error.NetworkNotFound => unreachable, // Windows-only
599 else => |e| return e,606 else => |e| return e,
600 },607 },
601 .solaris, .illumos => return os.readlinkZ("/proc/self/path/a.out", out_buffer) catch |err| switch (err) {608 .solaris, .illumos => return posix.readlinkZ("/proc/self/path/a.out", out_buffer) catch |err| switch (err) {
602 error.InvalidUtf8 => unreachable, // WASI-only609 error.InvalidUtf8 => unreachable, // WASI-only
603 error.InvalidWtf8 => unreachable, // Windows-only610 error.InvalidWtf8 => unreachable, // Windows-only
604 error.UnsupportedReparsePointType => unreachable, // Windows-only611 error.UnsupportedReparsePointType => unreachable, // Windows-only
...@@ -606,29 +613,29 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {...@@ -606,29 +613,29 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
606 else => |e| return e,613 else => |e| return e,
607 },614 },
608 .freebsd, .dragonfly => {615 .freebsd, .dragonfly => {
609 var mib = [4]c_int{ os.CTL.KERN, os.KERN.PROC, os.KERN.PROC_PATHNAME, -1 };616 var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC, posix.KERN.PROC_PATHNAME, -1 };
610 var out_len: usize = out_buffer.len;617 var out_len: usize = out_buffer.len;
611 try os.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);618 try posix.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
612 // TODO could this slice from 0 to out_len instead?619 // TODO could this slice from 0 to out_len instead?
613 return mem.sliceTo(out_buffer, 0);620 return mem.sliceTo(out_buffer, 0);
614 },621 },
615 .netbsd => {622 .netbsd => {
616 var mib = [4]c_int{ os.CTL.KERN, os.KERN.PROC_ARGS, -1, os.KERN.PROC_PATHNAME };623 var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC_ARGS, -1, posix.KERN.PROC_PATHNAME };
617 var out_len: usize = out_buffer.len;624 var out_len: usize = out_buffer.len;
618 try os.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);625 try posix.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
619 // TODO could this slice from 0 to out_len instead?626 // TODO could this slice from 0 to out_len instead?
620 return mem.sliceTo(out_buffer, 0);627 return mem.sliceTo(out_buffer, 0);
621 },628 },
622 .openbsd, .haiku => {629 .openbsd, .haiku => {
623 // OpenBSD doesn't support getting the path of a running process, so try to guess it630 // OpenBSD doesn't support getting the path of a running process, so try to guess it
624 if (os.argv.len == 0)631 if (std.os.argv.len == 0)
625 return error.FileNotFound;632 return error.FileNotFound;
626633
627 const argv0 = mem.span(os.argv[0]);634 const argv0 = mem.span(std.os.argv[0]);
628 if (mem.indexOf(u8, argv0, "/") != null) {635 if (mem.indexOf(u8, argv0, "/") != null) {
629 // argv[0] is a path (relative or absolute): use realpath(3) directly636 // argv[0] is a path (relative or absolute): use realpath(3) directly
630 var real_path_buf: [MAX_PATH_BYTES]u8 = undefined;637 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) {638 const real_path = posix.realpathZ(std.os.argv[0], &real_path_buf) catch |err| switch (err) {
632 error.InvalidWtf8 => unreachable, // Windows-only639 error.InvalidWtf8 => unreachable, // Windows-only
633 error.NetworkNotFound => unreachable, // Windows-only640 error.NetworkNotFound => unreachable, // Windows-only
634 else => |e| return e,641 else => |e| return e,
...@@ -640,17 +647,17 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {...@@ -640,17 +647,17 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
640 return result;647 return result;
641 } else if (argv0.len != 0) {648 } else if (argv0.len != 0) {
642 // argv[0] is not empty (and not a path): search it inside PATH649 // argv[0] is not empty (and not a path): search it inside PATH
643 const PATH = std.os.getenvZ("PATH") orelse return error.FileNotFound;650 const PATH = posix.getenvZ("PATH") orelse return error.FileNotFound;
644 var path_it = mem.tokenizeScalar(u8, PATH, path.delimiter);651 var path_it = mem.tokenizeScalar(u8, PATH, path.delimiter);
645 while (path_it.next()) |a_path| {652 while (path_it.next()) |a_path| {
646 var resolved_path_buf: [MAX_PATH_BYTES - 1:0]u8 = undefined;653 var resolved_path_buf: [MAX_PATH_BYTES - 1:0]u8 = undefined;
647 const resolved_path = std.fmt.bufPrintZ(&resolved_path_buf, "{s}/{s}", .{654 const resolved_path = std.fmt.bufPrintZ(&resolved_path_buf, "{s}/{s}", .{
648 a_path,655 a_path,
649 os.argv[0],656 std.os.argv[0],
650 }) catch continue;657 }) catch continue;
651658
652 var real_path_buf: [MAX_PATH_BYTES]u8 = undefined;659 var real_path_buf: [MAX_PATH_BYTES]u8 = undefined;
653 if (os.realpathZ(resolved_path, &real_path_buf)) |real_path| {660 if (posix.realpathZ(resolved_path, &real_path_buf)) |real_path| {
654 // found a file, and hope it is the right file661 // found a file, and hope it is the right file
655 if (real_path.len > out_buffer.len)662 if (real_path.len > out_buffer.len)
656 return error.NameTooLong;663 return error.NameTooLong;
...@@ -663,13 +670,13 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {...@@ -663,13 +670,13 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
663 return error.FileNotFound;670 return error.FileNotFound;
664 },671 },
665 .windows => {672 .windows => {
666 const image_path_unicode_string = &os.windows.peb().ProcessParameters.ImagePathName;673 const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
667 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];674 const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
668675
669 // If ImagePathName is a symlink, then it will contain the path of the676 // If ImagePathName is a symlink, then it will contain the path of the
670 // symlink, not the path that the symlink points to. We want the path677 // symlink, not the path that the symlink points to. We want the path
671 // that the symlink points to, though, so we need to get the realpath.678 // 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);679 const pathname_w = try windows.wToPrefixedFileW(null, image_path_name);
673 return std.fs.cwd().realpathW(pathname_w.span(), out_buffer) catch |err| switch (err) {680 return std.fs.cwd().realpathW(pathname_w.span(), out_buffer) catch |err| switch (err) {
674 error.InvalidWtf8 => unreachable,681 error.InvalidWtf8 => unreachable,
675 else => |e| return e,682 else => |e| return e,
...@@ -718,11 +725,11 @@ pub fn realpathAlloc(allocator: Allocator, pathname: []const u8) ![]u8 {...@@ -718,11 +725,11 @@ pub fn realpathAlloc(allocator: Allocator, pathname: []const u8) ![]u8 {
718 // paths. musl supports passing NULL but restricts the output to PATH_MAX725 // paths. musl supports passing NULL but restricts the output to PATH_MAX
719 // anyway.726 // anyway.
720 var buf: [MAX_PATH_BYTES]u8 = undefined;727 var buf: [MAX_PATH_BYTES]u8 = undefined;
721 return allocator.dupe(u8, try os.realpath(pathname, &buf));728 return allocator.dupe(u8, try posix.realpath(pathname, &buf));
722}729}
723730
724test {731test {
725 if (builtin.os.tag != .wasi) {732 if (native_os != .wasi) {
726 _ = &makeDirAbsolute;733 _ = &makeDirAbsolute;
727 _ = &makeDirAbsoluteZ;734 _ = &makeDirAbsoluteZ;
728 _ = &copyFileAbsolute;735 _ = &copyFileAbsolute;
lib/std/fs/AtomicFile.zig+1-2
...@@ -85,5 +85,4 @@ const File = std.fs.File;...@@ -85,5 +85,4 @@ const File = std.fs.File;
85const Dir = std.fs.Dir;85const Dir = std.fs.Dir;
86const fs = std.fs;86const fs = std.fs;
87const assert = std.debug.assert;87const assert = std.debug.assert;
88// https://github.com/ziglang/zig/issues/501988const posix = std.posix;
89const posix = std.os;
lib/std/fs/Dir.zig+76-81
...@@ -18,7 +18,7 @@ const IteratorError = error{...@@ -18,7 +18,7 @@ const IteratorError = error{
18 InvalidUtf8,18 InvalidUtf8,
19} || posix.UnexpectedError;19} || posix.UnexpectedError;
2020
21pub const Iterator = switch (builtin.os.tag) {21pub const Iterator = switch (native_os) {
22 .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris, .illumos => struct {22 .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris, .illumos => struct {
23 dir: Dir,23 dir: Dir,
24 seek: i64,24 seek: i64,
...@@ -34,7 +34,7 @@ pub const Iterator = switch (builtin.os.tag) {...@@ -34,7 +34,7 @@ pub const Iterator = switch (builtin.os.tag) {
34 /// Memory such as file names referenced in this returned entry becomes invalid34 /// Memory such as file names referenced in this returned entry becomes invalid
35 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.35 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
36 pub fn next(self: *Self) Error!?Entry {36 pub fn next(self: *Self) Error!?Entry {
37 switch (builtin.os.tag) {37 switch (native_os) {
38 .macos, .ios => return self.nextDarwin(),38 .macos, .ios => return self.nextDarwin(),
39 .freebsd, .netbsd, .dragonfly, .openbsd => return self.nextBsd(),39 .freebsd, .netbsd, .dragonfly, .openbsd => return self.nextBsd(),
40 .solaris, .illumos => return self.nextSolaris(),40 .solaris, .illumos => return self.nextSolaris(),
...@@ -183,7 +183,7 @@ pub const Iterator = switch (builtin.os.tag) {...@@ -183,7 +183,7 @@ pub const Iterator = switch (builtin.os.tag) {
183183
184 const name = @as([*]u8, @ptrCast(&bsd_entry.name))[0..bsd_entry.namlen];184 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) {
187 // fileno=0 is used to mark invalid entries or deleted files.187 // fileno=0 is used to mark invalid entries or deleted files.
188 .openbsd, .netbsd => true,188 .openbsd, .netbsd => true,
189 else => false,189 else => false,
...@@ -315,7 +315,7 @@ pub const Iterator = switch (builtin.os.tag) {...@@ -315,7 +315,7 @@ pub const Iterator = switch (builtin.os.tag) {
315 dir: Dir,315 dir: Dir,
316 // The if guard is solely there to prevent compile errors from missing `linux.dirent64`316 // The if guard is solely there to prevent compile errors from missing `linux.dirent64`
317 // definition when compiling for other OSes. It doesn't do anything when compiling for Linux.317 // 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)),
319 index: usize,319 index: usize,
320 end_index: usize,320 end_index: usize,
321 first_iter: bool,321 first_iter: bool,
...@@ -348,7 +348,7 @@ pub const Iterator = switch (builtin.os.tag) {...@@ -348,7 +348,7 @@ pub const Iterator = switch (builtin.os.tag) {
348 self.first_iter = false;348 self.first_iter = false;
349 }349 }
350 const rc = linux.getdents64(self.dir.fd, &self.buf, self.buf.len);350 const rc = linux.getdents64(self.dir.fd, &self.buf, self.buf.len);
351 switch (linux.getErrno(rc)) {351 switch (linux.E.init(rc)) {
352 .SUCCESS => {},352 .SUCCESS => {},
353 .BADF => unreachable, // Dir is invalid or was opened without iteration ability353 .BADF => unreachable, // Dir is invalid or was opened without iteration ability
354 .FAULT => unreachable,354 .FAULT => unreachable,
...@@ -398,7 +398,7 @@ pub const Iterator = switch (builtin.os.tag) {...@@ -398,7 +398,7 @@ pub const Iterator = switch (builtin.os.tag) {
398 },398 },
399 .windows => struct {399 .windows => struct {
400 dir: Dir,400 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)),
402 index: usize,402 index: usize,
403 end_index: usize,403 end_index: usize,
404 first_iter: bool,404 first_iter: bool,
...@@ -411,8 +411,8 @@ pub const Iterator = switch (builtin.os.tag) {...@@ -411,8 +411,8 @@ pub const Iterator = switch (builtin.os.tag) {
411 /// Memory such as file names referenced in this returned entry becomes invalid411 /// Memory such as file names referenced in this returned entry becomes invalid
412 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.412 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
413 pub fn next(self: *Self) Error!?Entry {413 pub fn next(self: *Self) Error!?Entry {
414 const w = windows;
414 while (true) {415 while (true) {
415 const w = std.os.windows;
416 if (self.index >= self.end_index) {416 if (self.index >= self.end_index) {
417 var io: w.IO_STATUS_BLOCK = undefined;417 var io: w.IO_STATUS_BLOCK = undefined;
418 const rc = w.ntdll.NtQueryDirectoryFile(418 const rc = w.ntdll.NtQueryDirectoryFile(
...@@ -582,7 +582,7 @@ pub fn iterateAssumeFirstIteration(self: Dir) Iterator {...@@ -582,7 +582,7 @@ pub fn iterateAssumeFirstIteration(self: Dir) Iterator {
582}582}
583583
584fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator {584fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator {
585 switch (builtin.os.tag) {585 switch (native_os) {
586 .macos,586 .macos,
587 .ios,587 .ios,
588 .freebsd,588 .freebsd,
...@@ -770,11 +770,11 @@ pub fn close(self: *Dir) void {...@@ -770,11 +770,11 @@ pub fn close(self: *Dir) void {
770/// On WASI, `sub_path` should be encoded as valid UTF-8.770/// On WASI, `sub_path` should be encoded as valid UTF-8.
771/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.771/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
772pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {772pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
773 if (builtin.os.tag == .windows) {773 if (native_os == .windows) {
774 const path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path);774 const path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
775 return self.openFileW(path_w.span(), flags);775 return self.openFileW(path_w.span(), flags);
776 }776 }
777 if (builtin.os.tag == .wasi) {777 if (native_os == .wasi) {
778 var base: std.os.wasi.rights_t = .{};778 var base: std.os.wasi.rights_t = .{};
779 if (flags.isRead()) {779 if (flags.isRead()) {
780 base.FD_READ = true;780 base.FD_READ = true;
...@@ -803,9 +803,9 @@ pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.Ope...@@ -803,9 +803,9 @@ pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.Ope
803803
804/// Same as `openFile` but the path parameter is null-terminated.804/// Same as `openFile` but the path parameter is null-terminated.
805pub fn openFileZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File {805pub fn openFileZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File {
806 switch (builtin.os.tag) {806 switch (native_os) {
807 .windows => {807 .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);
809 return self.openFileW(path_w.span(), flags);809 return self.openFileW(path_w.span(), flags);
810 },810 },
811 .wasi => {811 .wasi => {
...@@ -884,7 +884,7 @@ pub fn openFileZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File...@@ -884,7 +884,7 @@ pub fn openFileZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File
884/// Same as `openFile` but Windows-only and the path parameter is884/// Same as `openFile` but Windows-only and the path parameter is
885/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.885/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
886pub fn openFileW(self: Dir, sub_path_w: []const u16, flags: File.OpenFlags) File.OpenError!File {886pub 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;
888 const file: File = .{888 const file: File = .{
889 .handle = try w.OpenFile(sub_path_w, .{889 .handle = try w.OpenFile(sub_path_w, .{
890 .dir = self.fd,890 .dir = self.fd,
...@@ -925,11 +925,11 @@ pub fn openFileW(self: Dir, sub_path_w: []const u16, flags: File.OpenFlags) File...@@ -925,11 +925,11 @@ pub fn openFileW(self: Dir, sub_path_w: []const u16, flags: File.OpenFlags) File
925/// On WASI, `sub_path` should be encoded as valid UTF-8.925/// On WASI, `sub_path` should be encoded as valid UTF-8.
926/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.926/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
927pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {927pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
928 if (builtin.os.tag == .windows) {928 if (native_os == .windows) {
929 const path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path);929 const path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
930 return self.createFileW(path_w.span(), flags);930 return self.createFileW(path_w.span(), flags);
931 }931 }
932 if (builtin.os.tag == .wasi) {932 if (native_os == .wasi) {
933 return .{933 return .{
934 .handle = try posix.openatWasi(self.fd, sub_path, .{}, .{934 .handle = try posix.openatWasi(self.fd, sub_path, .{}, .{
935 .CREAT = true,935 .CREAT = true,
...@@ -957,9 +957,9 @@ pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File...@@ -957,9 +957,9 @@ pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File
957957
958/// Same as `createFile` but the path parameter is null-terminated.958/// Same as `createFile` but the path parameter is null-terminated.
959pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File {959pub 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) {
961 .windows => {961 .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);
963 return self.createFileW(path_w.span(), flags);963 return self.createFileW(path_w.span(), flags);
964 },964 },
965 .wasi => {965 .wasi => {
...@@ -968,7 +968,7 @@ pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags...@@ -968,7 +968,7 @@ pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags
968 else => {},968 else => {},
969 }969 }
970970
971 var os_flags: std.os.O = .{971 var os_flags: posix.O = .{
972 .ACCMODE = if (flags.read) .RDWR else .WRONLY,972 .ACCMODE = if (flags.read) .RDWR else .WRONLY,
973 .CREAT = true,973 .CREAT = true,
974 .TRUNC = flags.truncate,974 .TRUNC = flags.truncate,
...@@ -1032,7 +1032,7 @@ pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags...@@ -1032,7 +1032,7 @@ pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags
1032/// Same as `createFile` but Windows-only and the path parameter is1032/// Same as `createFile` but Windows-only and the path parameter is
1033/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.1033/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
1034pub fn createFileW(self: Dir, sub_path_w: []const u16, flags: File.CreateFlags) File.OpenError!File {1034pub 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;
1036 const read_flag = if (flags.read) @as(u32, w.GENERIC_READ) else 0;1036 const read_flag = if (flags.read) @as(u32, w.GENERIC_READ) else 0;
1037 const file: File = .{1037 const file: File = .{
1038 .handle = try w.OpenFile(sub_path_w, .{1038 .handle = try w.OpenFile(sub_path_w, .{
...@@ -1145,7 +1145,7 @@ pub fn makePath(self: Dir, sub_path: []const u8) !void {...@@ -1145,7 +1145,7 @@ pub fn makePath(self: Dir, sub_path: []const u8) !void {
1145/// have been modified regardless.1145/// have been modified regardless.
1146/// `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).1146/// `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
1147fn makeOpenPathAccessMaskW(self: Dir, sub_path: []const u8, access_mask: u32, no_follow: bool) OpenError!Dir {1147fn 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;
1149 var it = try fs.path.componentIterator(sub_path);1149 var it = try fs.path.componentIterator(sub_path);
1150 // If there are no components in the path, then create a dummy component with the full path.1150 // If there are no components in the path, then create a dummy component with the full path.
1151 var component = it.last() orelse fs.path.NativeComponentIterator.Component{1151 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...@@ -1180,9 +1180,9 @@ fn makeOpenPathAccessMaskW(self: Dir, sub_path: []const u8, access_mask: u32, no
1180/// On WASI, `sub_path` should be encoded as valid UTF-8.1180/// On WASI, `sub_path` should be encoded as valid UTF-8.
1181/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.1181/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
1182pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOptions) !Dir {1182pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOptions) !Dir {
1183 return switch (builtin.os.tag) {1183 return switch (native_os) {
1184 .windows => {1184 .windows => {
1185 const w = std.os.windows;1185 const w = windows;
1186 const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |1186 const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |
1187 w.SYNCHRONIZE | w.FILE_TRAVERSE |1187 w.SYNCHRONIZE | w.FILE_TRAVERSE |
1188 (if (open_dir_options.iterate) w.FILE_LIST_DIRECTORY else @as(u32, 0));1188 (if (open_dir_options.iterate) w.FILE_LIST_DIRECTORY else @as(u32, 0));
...@@ -1215,11 +1215,11 @@ pub const RealPathError = posix.RealPathError;...@@ -1215,11 +1215,11 @@ pub const RealPathError = posix.RealPathError;
1215/// Currently supported hosts are: Linux, macOS, and Windows.1215/// Currently supported hosts are: Linux, macOS, and Windows.
1216/// See also `Dir.realpathZ`, `Dir.realpathW`, and `Dir.realpathAlloc`.1216/// See also `Dir.realpathZ`, `Dir.realpathW`, and `Dir.realpathAlloc`.
1217pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError![]u8 {1217pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError![]u8 {
1218 if (builtin.os.tag == .wasi) {1218 if (native_os == .wasi) {
1219 @compileError("realpath is not available on WASI");1219 @compileError("realpath is not available on WASI");
1220 }1220 }
1221 if (builtin.os.tag == .windows) {1221 if (native_os == .windows) {
1222 const pathname_w = try std.os.windows.sliceToPrefixedFileW(self.fd, pathname);1222 const pathname_w = try windows.sliceToPrefixedFileW(self.fd, pathname);
1223 return self.realpathW(pathname_w.span(), out_buffer);1223 return self.realpathW(pathname_w.span(), out_buffer);
1224 }1224 }
1225 const pathname_c = try posix.toPosixPath(pathname);1225 const pathname_c = try posix.toPosixPath(pathname);
...@@ -1229,12 +1229,12 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError...@@ -1229,12 +1229,12 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError
1229/// Same as `Dir.realpath` except `pathname` is null-terminated.1229/// Same as `Dir.realpath` except `pathname` is null-terminated.
1230/// See also `Dir.realpath`, `realpathZ`.1230/// See also `Dir.realpath`, `realpathZ`.
1231pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathError![]u8 {1231pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathError![]u8 {
1232 if (builtin.os.tag == .windows) {1232 if (native_os == .windows) {
1233 const pathname_w = try posix.windows.cStrToPrefixedFileW(self.fd, pathname);1233 const pathname_w = try windows.cStrToPrefixedFileW(self.fd, pathname);
1234 return self.realpathW(pathname_w.span(), out_buffer);1234 return self.realpathW(pathname_w.span(), out_buffer);
1235 }1235 }
12361236
1237 const flags: posix.O = switch (builtin.os.tag) {1237 const flags: posix.O = switch (native_os) {
1238 .linux => .{1238 .linux => .{
1239 .NONBLOCK = true,1239 .NONBLOCK = true,
1240 .CLOEXEC = true,1240 .CLOEXEC = true,
...@@ -1255,14 +1255,8 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathE...@@ -1255,14 +1255,8 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathE
1255 };1255 };
1256 defer posix.close(fd);1256 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.
1264 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;1258 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
1267 if (out_path.len > out_buffer.len) {1261 if (out_path.len > out_buffer.len) {
1268 return error.NameTooLong;1262 return error.NameTooLong;
...@@ -1277,7 +1271,7 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathE...@@ -1277,7 +1271,7 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathE
1277/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).1271/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
1278/// See also `Dir.realpath`, `realpathW`.1272/// See also `Dir.realpath`, `realpathW`.
1279pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) RealPathError![]u8 {1273pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) RealPathError![]u8 {
1280 const w = std.os.windows;1274 const w = windows;
12811275
1282 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;1276 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
1283 const share_access = w.FILE_SHARE_READ;1277 const share_access = w.FILE_SHARE_READ;
...@@ -1331,16 +1325,16 @@ pub fn realpathAlloc(self: Dir, allocator: Allocator, pathname: []const u8) Real...@@ -1331,16 +1325,16 @@ pub fn realpathAlloc(self: Dir, allocator: Allocator, pathname: []const u8) Real
1331/// Not all targets support this. For example, WASI does not have the concept1325/// Not all targets support this. For example, WASI does not have the concept
1332/// of a current working directory.1326/// of a current working directory.
1333pub fn setAsCwd(self: Dir) !void {1327pub fn setAsCwd(self: Dir) !void {
1334 if (builtin.os.tag == .wasi) {1328 if (native_os == .wasi) {
1335 @compileError("changing cwd is not currently possible in WASI");1329 @compileError("changing cwd is not currently possible in WASI");
1336 }1330 }
1337 if (builtin.os.tag == .windows) {1331 if (native_os == .windows) {
1338 var dir_path_buffer: [std.os.windows.PATH_MAX_WIDE]u16 = undefined;1332 var dir_path_buffer: [windows.PATH_MAX_WIDE]u16 = undefined;
1339 const dir_path = try std.os.windows.GetFinalPathNameByHandle(self.fd, .{}, &dir_path_buffer);1333 const dir_path = try windows.GetFinalPathNameByHandle(self.fd, .{}, &dir_path_buffer);
1340 if (builtin.link_libc) {1334 if (builtin.link_libc) {
1341 return posix.chdirW(dir_path);1335 return posix.chdirW(dir_path);
1342 }1336 }
1343 return std.os.windows.SetCurrentDirectory(dir_path);1337 return windows.SetCurrentDirectory(dir_path);
1344 }1338 }
1345 try posix.fchdir(self.fd);1339 try posix.fchdir(self.fd);
1346}1340}
...@@ -1368,9 +1362,9 @@ pub const OpenDirOptions = struct {...@@ -1368,9 +1362,9 @@ pub const OpenDirOptions = struct {
1368/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.1362/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
1369/// Asserts that the path parameter has no null bytes.1363/// Asserts that the path parameter has no null bytes.
1370pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir {1364pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir {
1371 switch (builtin.os.tag) {1365 switch (native_os) {
1372 .windows => {1366 .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);
1374 return self.openDirW(sub_path_w.span().ptr, args);1368 return self.openDirW(sub_path_w.span().ptr, args);
1375 },1369 },
1376 .wasi => {1370 .wasi => {
...@@ -1427,9 +1421,9 @@ pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!...@@ -1427,9 +1421,9 @@ pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!
14271421
1428/// Same as `openDir` except the parameter is null-terminated.1422/// Same as `openDir` except the parameter is null-terminated.
1429pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir {1423pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir {
1430 switch (builtin.os.tag) {1424 switch (native_os) {
1431 .windows => {1425 .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);
1433 return self.openDirW(sub_path_w.span().ptr, args);1427 return self.openDirW(sub_path_w.span().ptr, args);
1434 },1428 },
1435 .wasi => {1429 .wasi => {
...@@ -1453,7 +1447,7 @@ pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) Open...@@ -1453,7 +1447,7 @@ pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) Open
1453/// Same as `openDir` except the path parameter is WTF-16 LE encoded, NT-prefixed.1447/// Same as `openDir` except the path parameter is WTF-16 LE encoded, NT-prefixed.
1454/// This function asserts the target OS is Windows.1448/// This function asserts the target OS is Windows.
1455pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenDirOptions) OpenError!Dir {1449pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenDirOptions) OpenError!Dir {
1456 const w = std.os.windows;1450 const w = windows;
1457 // TODO remove some of these flags if args.access_sub_paths is false1451 // TODO remove some of these flags if args.access_sub_paths is false
1458 const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |1452 const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |
1459 w.SYNCHRONIZE | w.FILE_TRAVERSE;1453 w.SYNCHRONIZE | w.FILE_TRAVERSE;
...@@ -1487,7 +1481,7 @@ const MakeOpenDirAccessMaskWOptions = struct {...@@ -1487,7 +1481,7 @@ const MakeOpenDirAccessMaskWOptions = struct {
1487};1481};
14881482
1489fn makeOpenDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32, flags: MakeOpenDirAccessMaskWOptions) OpenError!Dir {1483fn 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
1492 var result = Dir{1486 var result = Dir{
1493 .fd = undefined,1487 .fd = undefined,
...@@ -1545,10 +1539,10 @@ pub const DeleteFileError = posix.UnlinkError;...@@ -1545,10 +1539,10 @@ pub const DeleteFileError = posix.UnlinkError;
1545/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.1539/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
1546/// Asserts that the path parameter has no null bytes.1540/// Asserts that the path parameter has no null bytes.
1547pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {1541pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
1548 if (builtin.os.tag == .windows) {1542 if (native_os == .windows) {
1549 const sub_path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path);1543 const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
1550 return self.deleteFileW(sub_path_w.span());1544 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) {
1552 posix.unlinkat(self.fd, sub_path, 0) catch |err| switch (err) {1546 posix.unlinkat(self.fd, sub_path, 0) catch |err| switch (err) {
1553 error.DirNotEmpty => unreachable, // not passing AT.REMOVEDIR1547 error.DirNotEmpty => unreachable, // not passing AT.REMOVEDIR
1554 else => |e| return e,1548 else => |e| return e,
...@@ -1563,7 +1557,7 @@ pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {...@@ -1563,7 +1557,7 @@ pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
1563pub fn deleteFileZ(self: Dir, sub_path_c: [*:0]const u8) DeleteFileError!void {1557pub fn deleteFileZ(self: Dir, sub_path_c: [*:0]const u8) DeleteFileError!void {
1564 posix.unlinkatZ(self.fd, sub_path_c, 0) catch |err| switch (err) {1558 posix.unlinkatZ(self.fd, sub_path_c, 0) catch |err| switch (err) {
1565 error.DirNotEmpty => unreachable, // not passing AT.REMOVEDIR1559 error.DirNotEmpty => unreachable, // not passing AT.REMOVEDIR
1566 error.AccessDenied => |e| switch (builtin.os.tag) {1560 error.AccessDenied => |e| switch (native_os) {
1567 // non-Linux POSIX systems return EPERM when trying to delete a directory, so1561 // non-Linux POSIX systems return EPERM when trying to delete a directory, so
1568 // we need to handle that case specifically and translate the error1562 // we need to handle that case specifically and translate the error
1569 .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris, .illumos => {1563 .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris, .illumos => {
...@@ -1615,10 +1609,10 @@ pub const DeleteDirError = error{...@@ -1615,10 +1609,10 @@ pub const DeleteDirError = error{
1615/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.1609/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
1616/// Asserts that the path parameter has no null bytes.1610/// Asserts that the path parameter has no null bytes.
1617pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {1611pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
1618 if (builtin.os.tag == .windows) {1612 if (native_os == .windows) {
1619 const sub_path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path);1613 const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
1620 return self.deleteDirW(sub_path_w.span());1614 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) {
1622 posix.unlinkat(self.fd, sub_path, posix.AT.REMOVEDIR) catch |err| switch (err) {1616 posix.unlinkat(self.fd, sub_path, posix.AT.REMOVEDIR) catch |err| switch (err) {
1623 error.IsDir => unreachable, // not possible since we pass AT.REMOVEDIR1617 error.IsDir => unreachable, // not possible since we pass AT.REMOVEDIR
1624 else => |e| return e,1618 else => |e| return e,
...@@ -1691,15 +1685,15 @@ pub fn symLink(...@@ -1691,15 +1685,15 @@ pub fn symLink(
1691 sym_link_path: []const u8,1685 sym_link_path: []const u8,
1692 flags: SymLinkFlags,1686 flags: SymLinkFlags,
1693) !void {1687) !void {
1694 if (builtin.os.tag == .wasi and !builtin.link_libc) {1688 if (native_os == .wasi and !builtin.link_libc) {
1695 return self.symLinkWasi(target_path, sym_link_path, flags);1689 return self.symLinkWasi(target_path, sym_link_path, flags);
1696 }1690 }
1697 if (builtin.os.tag == .windows) {1691 if (native_os == .windows) {
1698 // Target path does not use sliceToPrefixedFileW because certain paths1692 // Target path does not use sliceToPrefixedFileW because certain paths
1699 // are handled differently when creating a symlink than they would be1693 // are handled differently when creating a symlink than they would be
1700 // when converting to an NT namespaced path. CreateSymbolicLink in1694 // when converting to an NT namespaced path. CreateSymbolicLink in
1701 // symLinkW will handle the necessary conversion.1695 // symLinkW will handle the necessary conversion.
1702 var target_path_w: std.os.windows.PathSpace = undefined;1696 var target_path_w: windows.PathSpace = undefined;
1703 target_path_w.len = try std.unicode.wtf8ToWtf16Le(&target_path_w.data, target_path);1697 target_path_w.len = try std.unicode.wtf8ToWtf16Le(&target_path_w.data, target_path);
1704 target_path_w.data[target_path_w.len] = 0;1698 target_path_w.data[target_path_w.len] = 0;
1705 // However, we need to canonicalize any path separators to `\`, since if1699 // However, we need to canonicalize any path separators to `\`, since if
...@@ -1711,7 +1705,7 @@ pub fn symLink(...@@ -1711,7 +1705,7 @@ pub fn symLink(
1711 mem.nativeToLittle(u16, '\\'),1705 mem.nativeToLittle(u16, '\\'),
1712 );1706 );
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);
1715 return self.symLinkW(target_path_w.span(), sym_link_path_w.span(), flags);1709 return self.symLinkW(target_path_w.span(), sym_link_path_w.span(), flags);
1716 }1710 }
1717 const target_path_c = try posix.toPosixPath(target_path);1711 const target_path_c = try posix.toPosixPath(target_path);
...@@ -1736,9 +1730,9 @@ pub fn symLinkZ(...@@ -1736,9 +1730,9 @@ pub fn symLinkZ(
1736 sym_link_path_c: [*:0]const u8,1730 sym_link_path_c: [*:0]const u8,
1737 flags: SymLinkFlags,1731 flags: SymLinkFlags,
1738) !void {1732) !void {
1739 if (builtin.os.tag == .windows) {1733 if (native_os == .windows) {
1740 const target_path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, target_path_c);1734 const target_path_w = try windows.cStrToPrefixedFileW(self.fd, target_path_c);
1741 const sym_link_path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, sym_link_path_c);1735 const sym_link_path_w = try windows.cStrToPrefixedFileW(self.fd, sym_link_path_c);
1742 return self.symLinkW(target_path_w.span(), sym_link_path_w.span(), flags);1736 return self.symLinkW(target_path_w.span(), sym_link_path_w.span(), flags);
1743 }1737 }
1744 return posix.symlinkatZ(target_path_c, self.fd, sym_link_path_c);1738 return posix.symlinkatZ(target_path_c, self.fd, sym_link_path_c);
...@@ -1756,7 +1750,7 @@ pub fn symLinkW(...@@ -1756,7 +1750,7 @@ pub fn symLinkW(
1756 sym_link_path_w: []const u16,1750 sym_link_path_w: []const u16,
1757 flags: SymLinkFlags,1751 flags: SymLinkFlags,
1758) !void {1752) !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);
1760}1754}
17611755
1762pub const ReadLinkError = posix.ReadLinkError;1756pub const ReadLinkError = posix.ReadLinkError;
...@@ -1768,11 +1762,11 @@ pub const ReadLinkError = posix.ReadLinkError;...@@ -1768,11 +1762,11 @@ pub const ReadLinkError = posix.ReadLinkError;
1768/// On WASI, `sub_path` should be encoded as valid UTF-8.1762/// On WASI, `sub_path` should be encoded as valid UTF-8.
1769/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.1763/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
1770pub fn readLink(self: Dir, sub_path: []const u8, buffer: []u8) ReadLinkError![]u8 {1764pub 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) {
1772 return self.readLinkWasi(sub_path, buffer);1766 return self.readLinkWasi(sub_path, buffer);
1773 }1767 }
1774 if (builtin.os.tag == .windows) {1768 if (native_os == .windows) {
1775 const sub_path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path);1769 const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
1776 return self.readLinkW(sub_path_w.span(), buffer);1770 return self.readLinkW(sub_path_w.span(), buffer);
1777 }1771 }
1778 const sub_path_c = try posix.toPosixPath(sub_path);1772 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 {...@@ -1786,8 +1780,8 @@ pub fn readLinkWasi(self: Dir, sub_path: []const u8, buffer: []u8) ![]u8 {
17861780
1787/// Same as `readLink`, except the `sub_path_c` parameter is null-terminated.1781/// Same as `readLink`, except the `sub_path_c` parameter is null-terminated.
1788pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {1782pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {
1789 if (builtin.os.tag == .windows) {1783 if (native_os == .windows) {
1790 const sub_path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, sub_path_c);1784 const sub_path_w = try windows.cStrToPrefixedFileW(self.fd, sub_path_c);
1791 return self.readLinkW(sub_path_w.span(), buffer);1785 return self.readLinkW(sub_path_w.span(), buffer);
1792 }1786 }
1793 return posix.readlinkatZ(self.fd, sub_path_c, buffer);1787 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 {...@@ -1796,7 +1790,7 @@ pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {
1796/// Windows-only. Same as `readLink` except the pathname parameter1790/// Windows-only. Same as `readLink` except the pathname parameter
1797/// is WTF16 LE encoded.1791/// is WTF16 LE encoded.
1798pub fn readLinkW(self: Dir, sub_path_w: []const u16, buffer: []u8) ![]u8 {1792pub 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);
1800}1794}
18011795
1802/// Read all of file contents using a preallocated buffer.1796/// Read all of file contents using a preallocated buffer.
...@@ -2319,8 +2313,8 @@ pub const AccessError = posix.AccessError;...@@ -2319,8 +2313,8 @@ pub const AccessError = posix.AccessError;
2319/// For example, instead of testing if a file exists and then opening it, just2313/// For example, instead of testing if a file exists and then opening it, just
2320/// open it and handle the error for file not found.2314/// open it and handle the error for file not found.
2321pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void {2315pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void {
2322 if (builtin.os.tag == .windows) {2316 if (native_os == .windows) {
2323 const sub_path_w = std.os.windows.sliceToPrefixedFileW(self.fd, sub_path) catch |err| switch (err) {2317 const sub_path_w = windows.sliceToPrefixedFileW(self.fd, sub_path) catch |err| switch (err) {
2324 error.AccessDenied => return error.PermissionDenied,2318 error.AccessDenied => return error.PermissionDenied,
2325 else => |e| return e,2319 else => |e| return e,
2326 };2320 };
...@@ -2332,8 +2326,8 @@ pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessErro...@@ -2332,8 +2326,8 @@ pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessErro
23322326
2333/// Same as `access` except the path parameter is null-terminated.2327/// Same as `access` except the path parameter is null-terminated.
2334pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void {2328pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void {
2335 if (builtin.os.tag == .windows) {2329 if (native_os == .windows) {
2336 const sub_path_w = std.os.windows.cStrToPrefixedFileW(self.fd, sub_path) catch |err| switch (err) {2330 const sub_path_w = windows.cStrToPrefixedFileW(self.fd, sub_path) catch |err| switch (err) {
2337 error.AccessDenied => return error.PermissionDenied,2331 error.AccessDenied => return error.PermissionDenied,
2338 else => |e| return e,2332 else => |e| return e,
2339 };2333 };
...@@ -2355,7 +2349,7 @@ pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) Access...@@ -2355,7 +2349,7 @@ pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) Access
2355/// TODO currently this ignores `flags`.2349/// TODO currently this ignores `flags`.
2356pub fn accessW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) AccessError!void {2350pub fn accessW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) AccessError!void {
2357 _ = flags;2351 _ = flags;
2358 return posix.faccessatW(self.fd, sub_path_w, 0, 0);2352 return posix.faccessatW(self.fd, sub_path_w);
2359}2353}
23602354
2361pub const CopyFileOptions = struct {2355pub const CopyFileOptions = struct {
...@@ -2473,7 +2467,7 @@ fn copy_file(fd_in: posix.fd_t, fd_out: posix.fd_t, maybe_size: ?u64) CopyFileRa...@@ -2473,7 +2467,7 @@ fn copy_file(fd_in: posix.fd_t, fd_out: posix.fd_t, maybe_size: ?u64) CopyFileRa
2473 }2467 }
2474 }2468 }
24752469
2476 if (builtin.os.tag == .linux) {2470 if (native_os == .linux) {
2477 // Try copy_file_range first as that works at the FS level and is the2471 // Try copy_file_range first as that works at the FS level and is the
2478 // most efficient method (if available).2472 // most efficient method (if available).
2479 var offset: u64 = 0;2473 var offset: u64 = 0;
...@@ -2555,13 +2549,13 @@ pub const StatFileError = File.OpenError || File.StatError || posix.FStatAtError...@@ -2555,13 +2549,13 @@ pub const StatFileError = File.OpenError || File.StatError || posix.FStatAtError
2555/// On WASI, `sub_path` should be encoded as valid UTF-8.2549/// On WASI, `sub_path` should be encoded as valid UTF-8.
2556/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.2550/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
2557pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat {2551pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat {
2558 if (builtin.os.tag == .windows) {2552 if (native_os == .windows) {
2559 var file = try self.openFile(sub_path, .{});2553 var file = try self.openFile(sub_path, .{});
2560 defer file.close();2554 defer file.close();
2561 return file.stat();2555 return file.stat();
2562 }2556 }
2563 if (builtin.os.tag == .wasi and !builtin.link_libc) {2557 if (native_os == .wasi and !builtin.link_libc) {
2564 const st = try posix.fstatat_wasi(self.fd, sub_path, .{ .SYMLINK_FOLLOW = true });2558 const st = try std.os.fstatat_wasi(self.fd, sub_path, .{ .SYMLINK_FOLLOW = true });
2565 return Stat.fromWasi(st);2559 return Stat.fromWasi(st);
2566 }2560 }
2567 const st = try posix.fstatat(self.fd, sub_path, 0);2561 const st = try posix.fstatat(self.fd, sub_path, 0);
...@@ -2617,9 +2611,10 @@ const builtin = @import("builtin");...@@ -2617,9 +2611,10 @@ const builtin = @import("builtin");
2617const std = @import("../std.zig");2611const std = @import("../std.zig");
2618const File = std.fs.File;2612const File = std.fs.File;
2619const AtomicFile = std.fs.AtomicFile;2613const AtomicFile = std.fs.AtomicFile;
2620// https://github.com/ziglang/zig/issues/50192614const posix = std.posix;
2621const posix = std.os;
2622const mem = std.mem;2615const mem = std.mem;
2623const fs = std.fs;2616const fs = std.fs;
2624const Allocator = std.mem.Allocator;2617const Allocator = std.mem.Allocator;
2625const assert = std.debug.assert;2618const assert = std.debug.assert;
2619const windows = std.os.windows;
2620const native_os = builtin.os.tag;
lib/std/fs/File.zig+56-5
...@@ -193,6 +193,58 @@ pub fn isTty(self: File) bool {...@@ -193,6 +193,58 @@ pub fn isTty(self: File) bool {
193 return posix.isatty(self.handle);193 return posix.isatty(self.handle);
194}194}
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
196/// Test whether ANSI escape codes will be treated as such.248/// Test whether ANSI escape codes will be treated as such.
197pub fn supportsAnsiEscapeCodes(self: File) bool {249pub fn supportsAnsiEscapeCodes(self: File) bool {
198 if (builtin.os.tag == .windows) {250 if (builtin.os.tag == .windows) {
...@@ -201,7 +253,7 @@ pub fn supportsAnsiEscapeCodes(self: File) bool {...@@ -201,7 +253,7 @@ pub fn supportsAnsiEscapeCodes(self: File) bool {
201 if (console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return true;253 if (console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return true;
202 }254 }
203255
204 return posix.isCygwinPty(self.handle);256 return self.isCygwinPty();
205 }257 }
206 if (builtin.os.tag == .wasi) {258 if (builtin.os.tag == .wasi) {
207 // WASI sanitizes stdout when fd is a tty so ANSI escape codes259 // WASI sanitizes stdout when fd is a tty so ANSI escape codes
...@@ -405,7 +457,7 @@ pub fn stat(self: File) StatError!Stat {...@@ -405,7 +457,7 @@ pub fn stat(self: File) StatError!Stat {
405 }457 }
406458
407 if (builtin.os.tag == .wasi and !builtin.link_libc) {459 if (builtin.os.tag == .wasi and !builtin.link_libc) {
408 const st = try posix.fstat_wasi(self.handle);460 const st = try std.os.fstat_wasi(self.handle);
409 return Stat.fromWasi(st);461 return Stat.fromWasi(st);
410 }462 }
411463
...@@ -952,7 +1004,7 @@ pub fn metadata(self: File) MetadataError!Metadata {...@@ -952,7 +1004,7 @@ pub fn metadata(self: File) MetadataError!Metadata {
952 .statx = stx,1004 .statx = stx,
953 };1005 };
954 },1006 },
955 .wasi => .{ .stat = try posix.fstat_wasi(self.handle) },1007 .wasi => .{ .stat = try std.os.fstat_wasi(self.handle) },
956 else => .{ .stat = try posix.fstat(self.handle) },1008 else => .{ .stat = try posix.fstat(self.handle) },
957 },1009 },
958 };1010 };
...@@ -1634,8 +1686,7 @@ const File = @This();...@@ -1634,8 +1686,7 @@ const File = @This();
1634const std = @import("../std.zig");1686const std = @import("../std.zig");
1635const builtin = @import("builtin");1687const builtin = @import("builtin");
1636const Allocator = std.mem.Allocator;1688const Allocator = std.mem.Allocator;
1637// https://github.com/ziglang/zig/issues/50191689const posix = std.posix;
1638const posix = std.os;
1639const io = std.io;1690const io = std.io;
1640const math = std.math;1691const math = std.math;
1641const assert = std.debug.assert;1692const assert = std.debug.assert;
lib/std/fs/get_app_data_dir.zig+8-7
...@@ -3,7 +3,8 @@ const builtin = @import("builtin");...@@ -3,7 +3,8 @@ const builtin = @import("builtin");
3const unicode = std.unicode;3const unicode = std.unicode;
4const mem = std.mem;4const mem = std.mem;
5const fs = std.fs;5const fs = std.fs;
6const os = std.os;6const native_os = builtin.os.tag;
7const posix = std.posix;
78
8pub const GetAppDataDirError = error{9pub const GetAppDataDirError = error{
9 OutOfMemory,10 OutOfMemory,
...@@ -13,7 +14,7 @@ pub const GetAppDataDirError = error{...@@ -13,7 +14,7 @@ pub const GetAppDataDirError = error{
13/// Caller owns returned memory.14/// Caller owns returned memory.
14/// TODO determine if we can remove the allocator requirement15/// TODO determine if we can remove the allocator requirement
15pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {16pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
16 switch (builtin.os.tag) {17 switch (native_os) {
17 .windows => {18 .windows => {
18 const local_app_data_dir = std.process.getEnvVarOwned(allocator, "LOCALAPPDATA") catch |err| switch (err) {19 const local_app_data_dir = std.process.getEnvVarOwned(allocator, "LOCALAPPDATA") catch |err| switch (err) {
19 error.OutOfMemory => |e| return e,20 error.OutOfMemory => |e| return e,
...@@ -23,18 +24,18 @@ pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDi...@@ -23,18 +24,18 @@ pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDi
23 return fs.path.join(allocator, &[_][]const u8{ local_app_data_dir, appname });24 return fs.path.join(allocator, &[_][]const u8{ local_app_data_dir, appname });
24 },25 },
25 .macos => {26 .macos => {
26 const home_dir = os.getenv("HOME") orelse {27 const home_dir = posix.getenv("HOME") orelse {
27 // TODO look in /etc/passwd28 // TODO look in /etc/passwd
28 return error.AppDataDirUnavailable;29 return error.AppDataDirUnavailable;
29 };30 };
30 return fs.path.join(allocator, &[_][]const u8{ home_dir, "Library", "Application Support", appname });31 return fs.path.join(allocator, &[_][]const u8{ home_dir, "Library", "Application Support", appname });
31 },32 },
32 .linux, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris, .illumos => {33 .linux, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris, .illumos => {
33 if (os.getenv("XDG_DATA_HOME")) |xdg| {34 if (posix.getenv("XDG_DATA_HOME")) |xdg| {
34 return fs.path.join(allocator, &[_][]const u8{ xdg, appname });35 return fs.path.join(allocator, &[_][]const u8{ xdg, appname });
35 }36 }
3637
37 const home_dir = os.getenv("HOME") orelse {38 const home_dir = posix.getenv("HOME") orelse {
38 // TODO look in /etc/passwd39 // TODO look in /etc/passwd
39 return error.AppDataDirUnavailable;40 return error.AppDataDirUnavailable;
40 };41 };
...@@ -48,7 +49,7 @@ pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDi...@@ -48,7 +49,7 @@ pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDi
48 }49 }
49 // TODO look into directory_which50 // TODO look into directory_which
50 const be_user_settings = 0xbbe;51 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);
52 const settings_dir = try allocator.dupeZ(u8, mem.sliceTo(dir_path_ptr, 0));53 const settings_dir = try allocator.dupeZ(u8, mem.sliceTo(dir_path_ptr, 0));
53 defer allocator.free(settings_dir);54 defer allocator.free(settings_dir);
54 switch (rc) {55 switch (rc) {
...@@ -61,7 +62,7 @@ pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDi...@@ -61,7 +62,7 @@ pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDi
61}62}
6263
63test "getAppDataDir" {64test "getAppDataDir" {
64 if (builtin.os.tag == .wasi) return error.SkipZigTest;65 if (native_os == .wasi) return error.SkipZigTest;
6566
66 // We can't actually validate the result67 // We can't actually validate the result
67 const dir = getAppDataDir(std.testing.allocator, "zig") catch return;68 const dir = getAppDataDir(std.testing.allocator, "zig") catch return;
lib/std/fs/test.zig+71-71
...@@ -1,10 +1,12 @@...@@ -1,10 +1,12 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const testing = std.testing;3const testing = std.testing;
4const os = std.os;
5const fs = std.fs;4const fs = std.fs;
6const mem = std.mem;5const mem = std.mem;
7const wasi = std.os.wasi;6const wasi = std.os.wasi;
7const native_os = builtin.os.tag;
8const windows = std.os.windows;
9const posix = std.posix;
810
9const ArenaAllocator = std.heap.ArenaAllocator;11const ArenaAllocator = std.heap.ArenaAllocator;
10const Dir = std.fs.Dir;12const Dir = std.fs.Dir;
...@@ -25,7 +27,7 @@ const PathType = enum {...@@ -25,7 +27,7 @@ const PathType = enum {
25 };27 };
26 }28 }
2729
28 pub const TransformError = std.os.RealPathError || error{OutOfMemory};30 pub const TransformError = posix.RealPathError || error{OutOfMemory};
29 pub const TransformFn = fn (allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8;31 pub const TransformFn = fn (allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8;
3032
31 pub fn getTransformFn(comptime path_type: PathType) TransformFn {33 pub fn getTransformFn(comptime path_type: PathType) TransformFn {
...@@ -42,7 +44,7 @@ const PathType = enum {...@@ -42,7 +44,7 @@ const PathType = enum {
42 // The final path may not actually exist which would cause realpath to fail.44 // The final path may not actually exist which would cause realpath to fail.
43 // So instead, we get the path of the dir and join it with the relative path.45 // So instead, we get the path of the dir and join it with the relative path.
44 var fd_path_buf: [fs.MAX_PATH_BYTES]u8 = undefined;46 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);
46 return fs.path.joinZ(allocator, &.{ dir_path, relative_path });48 return fs.path.joinZ(allocator, &.{ dir_path, relative_path });
47 }49 }
48 }.transform,50 }.transform,
...@@ -51,8 +53,8 @@ const PathType = enum {...@@ -51,8 +53,8 @@ const PathType = enum {
51 // Any drive absolute path (C:\foo) can be converted into a UNC path by53 // Any drive absolute path (C:\foo) can be converted into a UNC path by
52 // using '127.0.0.1' as the server name and '<drive letter>$' as the share name.54 // using '127.0.0.1' as the server name and '<drive letter>$' as the share name.
53 var fd_path_buf: [fs.MAX_PATH_BYTES]u8 = undefined;55 var fd_path_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
54 const dir_path = try os.getFdPath(dir.fd, &fd_path_buf);56 const dir_path = try std.os.getFdPath(dir.fd, &fd_path_buf);
55 const windows_path_type = std.os.windows.getUnprefixedPathType(u8, dir_path);57 const windows_path_type = windows.getUnprefixedPathType(u8, dir_path);
56 switch (windows_path_type) {58 switch (windows_path_type) {
57 .unc_absolute => return fs.path.joinZ(allocator, &.{ dir_path, relative_path }),59 .unc_absolute => return fs.path.joinZ(allocator, &.{ dir_path, relative_path }),
58 .drive_absolute => {60 .drive_absolute => {
...@@ -102,7 +104,7 @@ const TestContext = struct {...@@ -102,7 +104,7 @@ const TestContext = struct {
102 pub fn transformPath(self: *TestContext, relative_path: [:0]const u8) ![:0]const u8 {104 pub fn transformPath(self: *TestContext, relative_path: [:0]const u8) ![:0]const u8 {
103 const allocator = self.arena.allocator();105 const allocator = self.arena.allocator();
104 const transformed_path = try self.transform_fn(allocator, self.dir, relative_path);106 const transformed_path = try self.transform_fn(allocator, self.dir, relative_path);
105 if (builtin.os.tag == .windows) {107 if (native_os == .windows) {
106 const transformed_sep_path = try allocator.dupeZ(u8, transformed_path);108 const transformed_sep_path = try allocator.dupeZ(u8, transformed_path);
107 std.mem.replaceScalar(u8, transformed_sep_path, switch (self.path_sep) {109 std.mem.replaceScalar(u8, transformed_sep_path, switch (self.path_sep) {
108 '/' => '\\',110 '/' => '\\',
...@@ -119,7 +121,7 @@ const TestContext = struct {...@@ -119,7 +121,7 @@ const TestContext = struct {
119 /// If path separators are replaced, then the result is allocated by the121 /// If path separators are replaced, then the result is allocated by the
120 /// TestContext's arena and will be free'd during `TestContext.deinit`.122 /// TestContext's arena and will be free'd during `TestContext.deinit`.
121 pub fn toCanonicalPathSep(self: *TestContext, path: [:0]const u8) ![:0]const u8 {123 pub fn toCanonicalPathSep(self: *TestContext, path: [:0]const u8) ![:0]const u8 {
122 if (builtin.os.tag == .windows) {124 if (native_os == .windows) {
123 const allocator = self.arena.allocator();125 const allocator = self.arena.allocator();
124 const transformed_sep_path = try allocator.dupeZ(u8, path);126 const transformed_sep_path = try allocator.dupeZ(u8, path);
125 std.mem.replaceScalar(u8, transformed_sep_path, '/', '\\');127 std.mem.replaceScalar(u8, transformed_sep_path, '/', '\\');
...@@ -157,7 +159,7 @@ fn testWithPathTypeIfSupported(comptime path_type: PathType, comptime path_sep:...@@ -157,7 +159,7 @@ fn testWithPathTypeIfSupported(comptime path_type: PathType, comptime path_sep:
157fn setupSymlink(dir: Dir, target: []const u8, link: []const u8, flags: SymLinkFlags) !void {159fn setupSymlink(dir: Dir, target: []const u8, link: []const u8, flags: SymLinkFlags) !void {
158 return dir.symLink(target, link, flags) catch |err| switch (err) {160 return dir.symLink(target, link, flags) catch |err| switch (err) {
159 // Symlink requires admin privileges on windows, so this test can legitimately fail.161 // 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,
161 else => return err,163 else => return err,
162 };164 };
163}165}
...@@ -166,7 +168,7 @@ fn setupSymlink(dir: Dir, target: []const u8, link: []const u8, flags: SymLinkFl...@@ -166,7 +168,7 @@ fn setupSymlink(dir: Dir, target: []const u8, link: []const u8, flags: SymLinkFl
166// AccessDenied, then make the test failure silent (it is not a Zig failure).168// AccessDenied, then make the test failure silent (it is not a Zig failure).
167fn setupSymlinkAbsolute(target: []const u8, link: []const u8, flags: SymLinkFlags) !void {169fn setupSymlinkAbsolute(target: []const u8, link: []const u8, flags: SymLinkFlags) !void {
168 return fs.symLinkAbsolute(target, link, flags) catch |err| switch (err) {170 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,
170 else => return err,172 else => return err,
171 };173 };
172}174}
...@@ -232,60 +234,58 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" {...@@ -232,60 +234,58 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" {
232234
233 var symlink = switch (builtin.target.os.tag) {235 var symlink = switch (builtin.target.os.tag) {
234 .windows => windows_symlink: {236 .windows => windows_symlink: {
235 const w = std.os.windows;237 const sub_path_w = try windows.cStrToPrefixedFileW(ctx.dir.fd, "symlink");
236
237 const sub_path_w = try std.os.windows.cStrToPrefixedFileW(ctx.dir.fd, "symlink");
238238
239 var result = Dir{239 var result = Dir{
240 .fd = undefined,240 .fd = undefined,
241 };241 };
242242
243 const path_len_bytes = @as(u16, @intCast(sub_path_w.span().len * 2));243 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{
245 .Length = path_len_bytes,245 .Length = path_len_bytes,
246 .MaximumLength = path_len_bytes,246 .MaximumLength = path_len_bytes,
247 .Buffer = @constCast(&sub_path_w.data),247 .Buffer = @constCast(&sub_path_w.data),
248 };248 };
249 var attr = w.OBJECT_ATTRIBUTES{249 var attr = windows.OBJECT_ATTRIBUTES{
250 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),250 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
251 .RootDirectory = if (fs.path.isAbsoluteWindowsW(sub_path_w.span())) null else ctx.dir.fd,251 .RootDirectory = if (fs.path.isAbsoluteWindowsW(sub_path_w.span())) null else ctx.dir.fd,
252 .Attributes = 0,252 .Attributes = 0,
253 .ObjectName = &nt_name,253 .ObjectName = &nt_name,
254 .SecurityDescriptor = null,254 .SecurityDescriptor = null,
255 .SecurityQualityOfService = null,255 .SecurityQualityOfService = null,
256 };256 };
257 var io: w.IO_STATUS_BLOCK = undefined;257 var io: windows.IO_STATUS_BLOCK = undefined;
258 const rc = w.ntdll.NtCreateFile(258 const rc = windows.ntdll.NtCreateFile(
259 &result.fd,259 &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,
261 &attr,261 &attr,
262 &io,262 &io,
263 null,263 null,
264 w.FILE_ATTRIBUTE_NORMAL,264 windows.FILE_ATTRIBUTE_NORMAL,
265 w.FILE_SHARE_READ | w.FILE_SHARE_WRITE,265 windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE,
266 w.FILE_OPEN,266 windows.FILE_OPEN,
267 // FILE_OPEN_REPARSE_POINT is the important thing here267 // 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,
269 null,269 null,
270 0,270 0,
271 );271 );
272272
273 switch (rc) {273 switch (rc) {
274 .SUCCESS => break :windows_symlink result,274 .SUCCESS => break :windows_symlink result,
275 else => return w.unexpectedStatus(rc),275 else => return windows.unexpectedStatus(rc),
276 }276 }
277 },277 },
278 .linux => linux_symlink: {278 .linux => linux_symlink: {
279 const sub_path_c = try os.toPosixPath("symlink");279 const sub_path_c = try posix.toPosixPath("symlink");
280 // the O_NOFOLLOW | O_PATH combination can obtain a fd to a symlink280 // the O_NOFOLLOW | O_PATH combination can obtain a fd to a symlink
281 // note that if O_DIRECTORY is set, then this will error with ENOTDIR281 // note that if O_DIRECTORY is set, then this will error with ENOTDIR
282 const flags: os.O = .{282 const flags: posix.O = .{
283 .NOFOLLOW = true,283 .NOFOLLOW = true,
284 .PATH = true,284 .PATH = true,
285 .ACCMODE = .RDONLY,285 .ACCMODE = .RDONLY,
286 .CLOEXEC = true,286 .CLOEXEC = true,
287 };287 };
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);
289 break :linux_symlink Dir{ .fd = fd };289 break :linux_symlink Dir{ .fd = fd };
290 },290 },
291 else => unreachable,291 else => unreachable,
...@@ -315,7 +315,7 @@ test "openDir" {...@@ -315,7 +315,7 @@ test "openDir" {
315}315}
316316
317test "accessAbsolute" {317test "accessAbsolute" {
318 if (builtin.os.tag == .wasi) return error.SkipZigTest;318 if (native_os == .wasi) return error.SkipZigTest;
319319
320 var tmp = tmpDir(.{});320 var tmp = tmpDir(.{});
321 defer tmp.cleanup();321 defer tmp.cleanup();
...@@ -333,7 +333,7 @@ test "accessAbsolute" {...@@ -333,7 +333,7 @@ test "accessAbsolute" {
333}333}
334334
335test "openDirAbsolute" {335test "openDirAbsolute" {
336 if (builtin.os.tag == .wasi) return error.SkipZigTest;336 if (native_os == .wasi) return error.SkipZigTest;
337337
338 var tmp = tmpDir(.{});338 var tmp = tmpDir(.{});
339 defer tmp.cleanup();339 defer tmp.cleanup();
...@@ -361,14 +361,14 @@ test "openDirAbsolute" {...@@ -361,14 +361,14 @@ test "openDirAbsolute" {
361}361}
362362
363test "openDir cwd parent '..'" {363test "openDir cwd parent '..'" {
364 if (builtin.os.tag == .wasi) return error.SkipZigTest;364 if (native_os == .wasi) return error.SkipZigTest;
365365
366 var dir = try fs.cwd().openDir("..", .{});366 var dir = try fs.cwd().openDir("..", .{});
367 defer dir.close();367 defer dir.close();
368}368}
369369
370test "openDir non-cwd parent '..'" {370test "openDir non-cwd parent '..'" {
371 switch (builtin.os.tag) {371 switch (native_os) {
372 .wasi, .netbsd, .openbsd => return error.SkipZigTest,372 .wasi, .netbsd, .openbsd => return error.SkipZigTest,
373 else => {},373 else => {},
374 }374 }
...@@ -392,7 +392,7 @@ test "openDir non-cwd parent '..'" {...@@ -392,7 +392,7 @@ test "openDir non-cwd parent '..'" {
392}392}
393393
394test "readLinkAbsolute" {394test "readLinkAbsolute" {
395 if (builtin.os.tag == .wasi) return error.SkipZigTest;395 if (native_os == .wasi) return error.SkipZigTest;
396396
397 var tmp = tmpDir(.{});397 var tmp = tmpDir(.{});
398 defer tmp.cleanup();398 defer tmp.cleanup();
...@@ -587,7 +587,7 @@ test "Dir.Iterator but dir is deleted during iteration" {...@@ -587,7 +587,7 @@ test "Dir.Iterator but dir is deleted during iteration" {
587 try std.testing.expect(entry == null);587 try std.testing.expect(entry == null);
588588
589 // On Linux, we can opt-in to receiving a more specific error by calling `nextLinux`589 // 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) {
591 try std.testing.expectError(error.DirNotFound, iterator.nextLinux());591 try std.testing.expectError(error.DirNotFound, iterator.nextLinux());
592 }592 }
593}593}
...@@ -744,7 +744,7 @@ test "directory operations on files" {...@@ -744,7 +744,7 @@ test "directory operations on files" {
744744
745test "file operations on directories" {745test "file operations on directories" {
746 // TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759746 // 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
749 try testWithAllSupportedPathTypes(struct {749 try testWithAllSupportedPathTypes(struct {
750 fn impl(ctx: *TestContext) !void {750 fn impl(ctx: *TestContext) !void {
...@@ -754,7 +754,7 @@ test "file operations on directories" {...@@ -754,7 +754,7 @@ test "file operations on directories" {
754754
755 try testing.expectError(error.IsDir, ctx.dir.createFile(test_dir_name, .{}));755 try testing.expectError(error.IsDir, ctx.dir.createFile(test_dir_name, .{}));
756 try testing.expectError(error.IsDir, ctx.dir.deleteFile(test_dir_name));756 try testing.expectError(error.IsDir, ctx.dir.deleteFile(test_dir_name));
757 switch (builtin.os.tag) {757 switch (native_os) {
758 // no error when reading a directory.758 // no error when reading a directory.
759 .dragonfly, .netbsd => {},759 .dragonfly, .netbsd => {},
760 // Currently, WASI will return error.Unexpected (via ENOTCAPABLE) when attempting fd_read on a directory handle.760 // Currently, WASI will return error.Unexpected (via ENOTCAPABLE) when attempting fd_read on a directory handle.
...@@ -895,7 +895,7 @@ test "Dir.rename directories" {...@@ -895,7 +895,7 @@ test "Dir.rename directories" {
895895
896test "Dir.rename directory onto empty dir" {896test "Dir.rename directory onto empty dir" {
897 // TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364897 // 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
900 try testWithAllSupportedPathTypes(struct {900 try testWithAllSupportedPathTypes(struct {
901 fn impl(ctx: *TestContext) !void {901 fn impl(ctx: *TestContext) !void {
...@@ -916,7 +916,7 @@ test "Dir.rename directory onto empty dir" {...@@ -916,7 +916,7 @@ test "Dir.rename directory onto empty dir" {
916916
917test "Dir.rename directory onto non-empty dir" {917test "Dir.rename directory onto non-empty dir" {
918 // TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364918 // 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
921 try testWithAllSupportedPathTypes(struct {921 try testWithAllSupportedPathTypes(struct {
922 fn impl(ctx: *TestContext) !void {922 fn impl(ctx: *TestContext) !void {
...@@ -942,7 +942,7 @@ test "Dir.rename directory onto non-empty dir" {...@@ -942,7 +942,7 @@ test "Dir.rename directory onto non-empty dir" {
942942
943test "Dir.rename file <-> dir" {943test "Dir.rename file <-> dir" {
944 // TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364944 // 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
947 try testWithAllSupportedPathTypes(struct {947 try testWithAllSupportedPathTypes(struct {
948 fn impl(ctx: *TestContext) !void {948 fn impl(ctx: *TestContext) !void {
...@@ -979,7 +979,7 @@ test "rename" {...@@ -979,7 +979,7 @@ test "rename" {
979}979}
980980
981test "renameAbsolute" {981test "renameAbsolute" {
982 if (builtin.os.tag == .wasi) return error.SkipZigTest;982 if (native_os == .wasi) return error.SkipZigTest;
983983
984 var tmp_dir = tmpDir(.{});984 var tmp_dir = tmpDir(.{});
985 defer tmp_dir.cleanup();985 defer tmp_dir.cleanup();
...@@ -1032,14 +1032,14 @@ test "renameAbsolute" {...@@ -1032,14 +1032,14 @@ test "renameAbsolute" {
1032}1032}
10331033
1034test "openSelfExe" {1034test "openSelfExe" {
1035 if (builtin.os.tag == .wasi) return error.SkipZigTest;1035 if (native_os == .wasi) return error.SkipZigTest;
10361036
1037 const self_exe_file = try std.fs.openSelfExe(.{});1037 const self_exe_file = try std.fs.openSelfExe(.{});
1038 self_exe_file.close();1038 self_exe_file.close();
1039}1039}
10401040
1041test "selfExePath" {1041test "selfExePath" {
1042 if (builtin.os.tag == .wasi) return error.SkipZigTest;1042 if (native_os == .wasi) return error.SkipZigTest;
10431043
1044 var buf: [fs.MAX_PATH_BYTES]u8 = undefined;1044 var buf: [fs.MAX_PATH_BYTES]u8 = undefined;
1045 const buf_self_exe_path = try std.fs.selfExePath(&buf);1045 const buf_self_exe_path = try std.fs.selfExePath(&buf);
...@@ -1120,7 +1120,7 @@ test "makePath, put some files in it, deleteTreeMinStackSize" {...@@ -1120,7 +1120,7 @@ test "makePath, put some files in it, deleteTreeMinStackSize" {
1120}1120}
11211121
1122test "makePath in a directory that no longer exists" {1122test "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 dir1123 if (native_os == .windows) return error.SkipZigTest; // Windows returns FileBusy if attempting to remove an open dir
11241124
1125 var tmp = tmpDir(.{});1125 var tmp = tmpDir(.{});
1126 defer tmp.cleanup();1126 defer tmp.cleanup();
...@@ -1182,7 +1182,7 @@ test "makepath relative walks" {...@@ -1182,7 +1182,7 @@ test "makepath relative walks" {
1182 try tmp.dir.makePath(relPath);1182 try tmp.dir.makePath(relPath);
11831183
1184 // How .. is handled is different on Windows than non-Windows1184 // How .. is handled is different on Windows than non-Windows
1185 switch (builtin.os.tag) {1185 switch (native_os) {
1186 .windows => {1186 .windows => {
1187 // On Windows, .. is resolved before passing the path to NtCreateFile,1187 // On Windows, .. is resolved before passing the path to NtCreateFile,
1188 // meaning everything except `first/C` drops out.1188 // meaning everything except `first/C` drops out.
...@@ -1248,12 +1248,12 @@ test "max file name component lengths" {...@@ -1248,12 +1248,12 @@ test "max file name component lengths" {
1248 var tmp = tmpDir(.{ .iterate = true });1248 var tmp = tmpDir(.{ .iterate = true });
1249 defer tmp.cleanup();1249 defer tmp.cleanup();
12501250
1251 if (builtin.os.tag == .windows) {1251 if (native_os == .windows) {
1252 // U+FFFF is the character with the largest code point that is encoded as a single1252 // U+FFFF is the character with the largest code point that is encoded as a single
1253 // UTF-16 code unit, so Windows allows for NAME_MAX of them.1253 // 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;
1255 try testFilenameLimits(tmp.dir, &maxed_windows_filename);1255 try testFilenameLimits(tmp.dir, &maxed_windows_filename);
1256 } else if (builtin.os.tag == .wasi) {1256 } else if (native_os == .wasi) {
1257 // On WASI, the maxed filename depends on the host OS, so in order for this test to1257 // On WASI, the maxed filename depends on the host OS, so in order for this test to
1258 // work on any host, we need to use a length that will work for all platforms1258 // work on any host, we need to use a length that will work for all platforms
1259 // (i.e. the minimum MAX_NAME_BYTES of all supported platforms).1259 // (i.e. the minimum MAX_NAME_BYTES of all supported platforms).
...@@ -1274,7 +1274,7 @@ test "writev, readv" {...@@ -1274,7 +1274,7 @@ test "writev, readv" {
12741274
1275 var buf1: [line1.len]u8 = undefined;1275 var buf1: [line1.len]u8 = undefined;
1276 var buf2: [line2.len]u8 = undefined;1276 var buf2: [line2.len]u8 = undefined;
1277 var write_vecs = [_]std.os.iovec_const{1277 var write_vecs = [_]posix.iovec_const{
1278 .{1278 .{
1279 .iov_base = line1,1279 .iov_base = line1,
1280 .iov_len = line1.len,1280 .iov_len = line1.len,
...@@ -1284,7 +1284,7 @@ test "writev, readv" {...@@ -1284,7 +1284,7 @@ test "writev, readv" {
1284 .iov_len = line2.len,1284 .iov_len = line2.len,
1285 },1285 },
1286 };1286 };
1287 var read_vecs = [_]std.os.iovec{1287 var read_vecs = [_]posix.iovec{
1288 .{1288 .{
1289 .iov_base = &buf2,1289 .iov_base = &buf2,
1290 .iov_len = buf2.len,1290 .iov_len = buf2.len,
...@@ -1316,7 +1316,7 @@ test "pwritev, preadv" {...@@ -1316,7 +1316,7 @@ test "pwritev, preadv" {
13161316
1317 var buf1: [line1.len]u8 = undefined;1317 var buf1: [line1.len]u8 = undefined;
1318 var buf2: [line2.len]u8 = undefined;1318 var buf2: [line2.len]u8 = undefined;
1319 var write_vecs = [_]std.os.iovec_const{1319 var write_vecs = [_]posix.iovec_const{
1320 .{1320 .{
1321 .iov_base = line1,1321 .iov_base = line1,
1322 .iov_len = line1.len,1322 .iov_len = line1.len,
...@@ -1326,7 +1326,7 @@ test "pwritev, preadv" {...@@ -1326,7 +1326,7 @@ test "pwritev, preadv" {
1326 .iov_len = line2.len,1326 .iov_len = line2.len,
1327 },1327 },
1328 };1328 };
1329 var read_vecs = [_]std.os.iovec{1329 var read_vecs = [_]posix.iovec{
1330 .{1330 .{
1331 .iov_base = &buf2,1331 .iov_base = &buf2,
1332 .iov_len = buf2.len,1332 .iov_len = buf2.len,
...@@ -1376,7 +1376,7 @@ test "sendfile" {...@@ -1376,7 +1376,7 @@ test "sendfile" {
13761376
1377 const line1 = "line1\n";1377 const line1 = "line1\n";
1378 const line2 = "second line\n";1378 const line2 = "second line\n";
1379 var vecs = [_]std.os.iovec_const{1379 var vecs = [_]posix.iovec_const{
1380 .{1380 .{
1381 .iov_base = line1,1381 .iov_base = line1,
1382 .iov_len = line1.len,1382 .iov_len = line1.len,
...@@ -1399,7 +1399,7 @@ test "sendfile" {...@@ -1399,7 +1399,7 @@ test "sendfile" {
1399 const header2 = "second header\n";1399 const header2 = "second header\n";
1400 const trailer1 = "trailer1\n";1400 const trailer1 = "trailer1\n";
1401 const trailer2 = "second trailer\n";1401 const trailer2 = "second trailer\n";
1402 var hdtr = [_]std.os.iovec_const{1402 var hdtr = [_]posix.iovec_const{
1403 .{1403 .{
1404 .iov_base = header1,1404 .iov_base = header1,
1405 .iov_len = header1.len,1405 .iov_len = header1.len,
...@@ -1510,7 +1510,7 @@ test "AtomicFile" {...@@ -1510,7 +1510,7 @@ test "AtomicFile" {
1510}1510}
15111511
1512test "open file with exclusive nonblocking lock twice" {1512test "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
1515 try testWithAllSupportedPathTypes(struct {1515 try testWithAllSupportedPathTypes(struct {
1516 fn impl(ctx: *TestContext) !void {1516 fn impl(ctx: *TestContext) !void {
...@@ -1526,7 +1526,7 @@ test "open file with exclusive nonblocking lock twice" {...@@ -1526,7 +1526,7 @@ test "open file with exclusive nonblocking lock twice" {
1526}1526}
15271527
1528test "open file with shared and exclusive nonblocking lock" {1528test "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
1531 try testWithAllSupportedPathTypes(struct {1531 try testWithAllSupportedPathTypes(struct {
1532 fn impl(ctx: *TestContext) !void {1532 fn impl(ctx: *TestContext) !void {
...@@ -1542,7 +1542,7 @@ test "open file with shared and exclusive nonblocking lock" {...@@ -1542,7 +1542,7 @@ test "open file with shared and exclusive nonblocking lock" {
1542}1542}
15431543
1544test "open file with exclusive and shared nonblocking lock" {1544test "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
1547 try testWithAllSupportedPathTypes(struct {1547 try testWithAllSupportedPathTypes(struct {
1548 fn impl(ctx: *TestContext) !void {1548 fn impl(ctx: *TestContext) !void {
...@@ -1601,7 +1601,7 @@ test "open file with exclusive lock twice, make sure second lock waits" {...@@ -1601,7 +1601,7 @@ test "open file with exclusive lock twice, make sure second lock waits" {
1601}1601}
16021602
1603test "open file with exclusive nonblocking lock twice (absolute paths)" {1603test "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
1606 var random_bytes: [12]u8 = undefined;1606 var random_bytes: [12]u8 = undefined;
1607 std.crypto.random.bytes(&random_bytes);1607 std.crypto.random.bytes(&random_bytes);
...@@ -1634,7 +1634,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {...@@ -1634,7 +1634,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
1634}1634}
16351635
1636test "walker" {1636test "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
1639 var tmp = tmpDir(.{ .iterate = true });1639 var tmp = tmpDir(.{ .iterate = true });
1640 defer tmp.cleanup();1640 defer tmp.cleanup();
...@@ -1687,7 +1687,7 @@ test "walker" {...@@ -1687,7 +1687,7 @@ test "walker" {
1687}1687}
16881688
1689test "walker without fully iterating" {1689test "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
1692 var tmp = tmpDir(.{ .iterate = true });1692 var tmp = tmpDir(.{ .iterate = true });
1693 defer tmp.cleanup();1693 defer tmp.cleanup();
...@@ -1710,9 +1710,9 @@ test "walker without fully iterating" {...@@ -1710,9 +1710,9 @@ test "walker without fully iterating" {
1710}1710}
17111711
1712test "'.' and '..' in fs.Dir functions" {1712test "'.' 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) {
1716 // https://github.com/ziglang/zig/issues/171341716 // https://github.com/ziglang/zig/issues/17134
1717 return error.SkipZigTest;1717 return error.SkipZigTest;
1718 }1718 }
...@@ -1750,7 +1750,7 @@ test "'.' and '..' in fs.Dir functions" {...@@ -1750,7 +1750,7 @@ test "'.' and '..' in fs.Dir functions" {
1750}1750}
17511751
1752test "'.' and '..' in absolute functions" {1752test "'.' and '..' in absolute functions" {
1753 if (builtin.os.tag == .wasi) return error.SkipZigTest;1753 if (native_os == .wasi) return error.SkipZigTest;
17541754
1755 var tmp = tmpDir(.{});1755 var tmp = tmpDir(.{});
1756 defer tmp.cleanup();1756 defer tmp.cleanup();
...@@ -1794,7 +1794,7 @@ test "'.' and '..' in absolute functions" {...@@ -1794,7 +1794,7 @@ test "'.' and '..' in absolute functions" {
1794}1794}
17951795
1796test "chmod" {1796test "chmod" {
1797 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)1797 if (native_os == .windows or native_os == .wasi)
1798 return error.SkipZigTest;1798 return error.SkipZigTest;
17991799
1800 var tmp = tmpDir(.{});1800 var tmp = tmpDir(.{});
...@@ -1816,7 +1816,7 @@ test "chmod" {...@@ -1816,7 +1816,7 @@ test "chmod" {
1816}1816}
18171817
1818test "chown" {1818test "chown" {
1819 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)1819 if (native_os == .windows or native_os == .wasi)
1820 return error.SkipZigTest;1820 return error.SkipZigTest;
18211821
1822 var tmp = tmpDir(.{});1822 var tmp = tmpDir(.{});
...@@ -1849,7 +1849,7 @@ test "File.Metadata" {...@@ -1849,7 +1849,7 @@ test "File.Metadata" {
1849}1849}
18501850
1851test "File.Permissions" {1851test "File.Permissions" {
1852 if (builtin.os.tag == .wasi)1852 if (native_os == .wasi)
1853 return error.SkipZigTest;1853 return error.SkipZigTest;
18541854
1855 var tmp = tmpDir(.{});1855 var tmp = tmpDir(.{});
...@@ -1875,7 +1875,7 @@ test "File.Permissions" {...@@ -1875,7 +1875,7 @@ test "File.Permissions" {
1875}1875}
18761876
1877test "File.PermissionsUnix" {1877test "File.PermissionsUnix" {
1878 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)1878 if (native_os == .windows or native_os == .wasi)
1879 return error.SkipZigTest;1879 return error.SkipZigTest;
18801880
1881 var tmp = tmpDir(.{});1881 var tmp = tmpDir(.{});
...@@ -1910,7 +1910,7 @@ test "File.PermissionsUnix" {...@@ -1910,7 +1910,7 @@ test "File.PermissionsUnix" {
1910}1910}
19111911
1912test "delete a read-only file on windows" {1912test "delete a read-only file on windows" {
1913 if (builtin.os.tag != .windows)1913 if (native_os != .windows)
1914 return error.SkipZigTest;1914 return error.SkipZigTest;
19151915
1916 var tmp = testing.tmpDir(.{});1916 var tmp = testing.tmpDir(.{});
...@@ -1941,7 +1941,7 @@ test "delete a read-only file on windows" {...@@ -1941,7 +1941,7 @@ test "delete a read-only file on windows" {
1941}1941}
19421942
1943test "delete a setAsCwd directory on Windows" {1943test "delete a setAsCwd directory on Windows" {
1944 if (builtin.os.tag != .windows) return error.SkipZigTest;1944 if (native_os != .windows) return error.SkipZigTest;
19451945
1946 var tmp = tmpDir(.{});1946 var tmp = tmpDir(.{});
1947 // Set tmp dir as current working directory.1947 // Set tmp dir as current working directory.
...@@ -1956,7 +1956,7 @@ test "delete a setAsCwd directory on Windows" {...@@ -1956,7 +1956,7 @@ test "delete a setAsCwd directory on Windows" {
1956}1956}
19571957
1958test "invalid UTF-8/WTF-8 paths" {1958test "invalid UTF-8/WTF-8 paths" {
1959 const expected_err = switch (builtin.os.tag) {1959 const expected_err = switch (native_os) {
1960 .wasi => error.InvalidUtf8,1960 .wasi => error.InvalidUtf8,
1961 .windows => error.InvalidWtf8,1961 .windows => error.InvalidWtf8,
1962 else => return error.SkipZigTest,1962 else => return error.SkipZigTest,
...@@ -1993,13 +1993,13 @@ test "invalid UTF-8/WTF-8 paths" {...@@ -1993,13 +1993,13 @@ test "invalid UTF-8/WTF-8 paths" {
19931993
1994 try testing.expectError(expected_err, ctx.dir.symLink(invalid_path, invalid_path, .{}));1994 try testing.expectError(expected_err, ctx.dir.symLink(invalid_path, invalid_path, .{}));
1995 try testing.expectError(expected_err, ctx.dir.symLinkZ(invalid_path, invalid_path, .{}));1995 try testing.expectError(expected_err, ctx.dir.symLinkZ(invalid_path, invalid_path, .{}));
1996 if (builtin.os.tag == .wasi) {1996 if (native_os == .wasi) {
1997 try testing.expectError(expected_err, ctx.dir.symLinkWasi(invalid_path, invalid_path, .{}));1997 try testing.expectError(expected_err, ctx.dir.symLinkWasi(invalid_path, invalid_path, .{}));
1998 }1998 }
19991999
2000 try testing.expectError(expected_err, ctx.dir.readLink(invalid_path, &[_]u8{}));2000 try testing.expectError(expected_err, ctx.dir.readLink(invalid_path, &[_]u8{}));
2001 try testing.expectError(expected_err, ctx.dir.readLinkZ(invalid_path, &[_]u8{}));2001 try testing.expectError(expected_err, ctx.dir.readLinkZ(invalid_path, &[_]u8{}));
2002 if (builtin.os.tag == .wasi) {2002 if (native_os == .wasi) {
2003 try testing.expectError(expected_err, ctx.dir.readLinkWasi(invalid_path, &[_]u8{}));2003 try testing.expectError(expected_err, ctx.dir.readLinkWasi(invalid_path, &[_]u8{}));
2004 }2004 }
20052005
...@@ -2023,7 +2023,7 @@ test "invalid UTF-8/WTF-8 paths" {...@@ -2023,7 +2023,7 @@ test "invalid UTF-8/WTF-8 paths" {
20232023
2024 try testing.expectError(expected_err, ctx.dir.statFile(invalid_path));2024 try testing.expectError(expected_err, ctx.dir.statFile(invalid_path));
20252025
2026 if (builtin.os.tag != .wasi) {2026 if (native_os != .wasi) {
2027 try testing.expectError(expected_err, ctx.dir.realpath(invalid_path, &[_]u8{}));2027 try testing.expectError(expected_err, ctx.dir.realpath(invalid_path, &[_]u8{}));
2028 try testing.expectError(expected_err, ctx.dir.realpathZ(invalid_path, &[_]u8{}));2028 try testing.expectError(expected_err, ctx.dir.realpathZ(invalid_path, &[_]u8{}));
2029 try testing.expectError(expected_err, ctx.dir.realpathAlloc(testing.allocator, invalid_path));2029 try testing.expectError(expected_err, ctx.dir.realpathAlloc(testing.allocator, invalid_path));
...@@ -2032,7 +2032,7 @@ test "invalid UTF-8/WTF-8 paths" {...@@ -2032,7 +2032,7 @@ test "invalid UTF-8/WTF-8 paths" {
2032 try testing.expectError(expected_err, fs.rename(ctx.dir, invalid_path, ctx.dir, invalid_path));2032 try testing.expectError(expected_err, fs.rename(ctx.dir, invalid_path, ctx.dir, invalid_path));
2033 try testing.expectError(expected_err, fs.renameZ(ctx.dir, invalid_path, ctx.dir, invalid_path));2033 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) {
2036 try testing.expectError(expected_err, fs.updateFileAbsolute(invalid_path, invalid_path, .{}));2036 try testing.expectError(expected_err, fs.updateFileAbsolute(invalid_path, invalid_path, .{}));
2037 try testing.expectError(expected_err, fs.copyFileAbsolute(invalid_path, invalid_path, .{}));2037 try testing.expectError(expected_err, fs.copyFileAbsolute(invalid_path, invalid_path, .{}));
2038 try testing.expectError(expected_err, fs.makeDirAbsolute(invalid_path));2038 try testing.expectError(expected_err, fs.makeDirAbsolute(invalid_path));
lib/std/fs/wasi.zig+2-3
...@@ -1,6 +1,5 @@...@@ -1,6 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const os = std.os;
4const mem = std.mem;3const mem = std.mem;
5const math = std.math;4const math = std.math;
6const fs = std.fs;5const fs = std.fs;
...@@ -14,10 +13,10 @@ pub const Preopens = struct {...@@ -14,10 +13,10 @@ pub const Preopens = struct {
14 // Indexed by file descriptor number.13 // Indexed by file descriptor number.
15 names: []const []const u8,14 names: []const []const u8,
1615
17 pub fn find(p: Preopens, name: []const u8) ?os.fd_t {16 pub fn find(p: Preopens, name: []const u8) ?std.posix.fd_t {
18 for (p.names, 0..) |elem_name, i| {17 for (p.names, 0..) |elem_name, i| {
19 if (mem.eql(u8, elem_name, name)) {18 if (mem.eql(u8, elem_name, name)) {
20 return @as(os.fd_t, @intCast(i));19 return @intCast(i);
21 }20 }
22 }21 }
23 return null;22 return null;
lib/std/hash/benchmark.zig+6-6
...@@ -367,7 +367,7 @@ pub fn main() !void {...@@ -367,7 +367,7 @@ pub fn main() !void {
367 i += 1;367 i += 1;
368 if (i == args.len) {368 if (i == args.len) {
369 usage();369 usage();
370 std.os.exit(1);370 std.process.exit(1);
371 }371 }
372372
373 seed = try std.fmt.parseUnsigned(u32, args[i], 10);373 seed = try std.fmt.parseUnsigned(u32, args[i], 10);
...@@ -376,7 +376,7 @@ pub fn main() !void {...@@ -376,7 +376,7 @@ pub fn main() !void {
376 i += 1;376 i += 1;
377 if (i == args.len) {377 if (i == args.len) {
378 usage();378 usage();
379 std.os.exit(1);379 std.process.exit(1);
380 }380 }
381381
382 filter = args[i];382 filter = args[i];
...@@ -384,7 +384,7 @@ pub fn main() !void {...@@ -384,7 +384,7 @@ pub fn main() !void {
384 i += 1;384 i += 1;
385 if (i == args.len) {385 if (i == args.len) {
386 usage();386 usage();
387 std.os.exit(1);387 std.process.exit(1);
388 }388 }
389389
390 const c = try std.fmt.parseUnsigned(usize, args[i], 10);390 const c = try std.fmt.parseUnsigned(usize, args[i], 10);
...@@ -393,13 +393,13 @@ pub fn main() !void {...@@ -393,13 +393,13 @@ pub fn main() !void {
393 i += 1;393 i += 1;
394 if (i == args.len) {394 if (i == args.len) {
395 usage();395 usage();
396 std.os.exit(1);396 std.process.exit(1);
397 }397 }
398398
399 key_size = try std.fmt.parseUnsigned(usize, args[i], 10);399 key_size = try std.fmt.parseUnsigned(usize, args[i], 10);
400 if (key_size.? > block_size) {400 if (key_size.? > block_size) {
401 try stdout.print("key_size cannot exceed block size of {}\n", .{block_size});401 try stdout.print("key_size cannot exceed block size of {}\n", .{block_size});
402 std.os.exit(1);402 std.process.exit(1);
403 }403 }
404 } else if (std.mem.eql(u8, args[i], "--iterative-only")) {404 } else if (std.mem.eql(u8, args[i], "--iterative-only")) {
405 test_iterative_only = true;405 test_iterative_only = true;
...@@ -410,7 +410,7 @@ pub fn main() !void {...@@ -410,7 +410,7 @@ pub fn main() !void {
410 return;410 return;
411 } else {411 } else {
412 usage();412 usage();
413 std.os.exit(1);413 std.process.exit(1);
414 }414 }
415 }415 }
416416
lib/std/heap.zig+10-10
...@@ -4,9 +4,9 @@ const root = @import("root");...@@ -4,9 +4,9 @@ const root = @import("root");
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const testing = std.testing;5const testing = std.testing;
6const mem = std.mem;6const mem = std.mem;
7const os = std.os;
8const c = std.c;7const c = std.c;
9const Allocator = std.mem.Allocator;8const Allocator = std.mem.Allocator;
9const windows = std.os.windows;
1010
11pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;11pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;
12pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;12pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;
...@@ -263,7 +263,7 @@ pub const HeapAllocator = switch (builtin.os.tag) {...@@ -263,7 +263,7 @@ pub const HeapAllocator = switch (builtin.os.tag) {
263 .windows => struct {263 .windows => struct {
264 heap_handle: ?HeapHandle,264 heap_handle: ?HeapHandle,
265265
266 const HeapHandle = os.windows.HANDLE;266 const HeapHandle = windows.HANDLE;
267267
268 pub fn init() HeapAllocator {268 pub fn init() HeapAllocator {
269 return HeapAllocator{269 return HeapAllocator{
...@@ -284,7 +284,7 @@ pub const HeapAllocator = switch (builtin.os.tag) {...@@ -284,7 +284,7 @@ pub const HeapAllocator = switch (builtin.os.tag) {
284284
285 pub fn deinit(self: *HeapAllocator) void {285 pub fn deinit(self: *HeapAllocator) void {
286 if (self.heap_handle) |heap_handle| {286 if (self.heap_handle) |heap_handle| {
287 os.windows.HeapDestroy(heap_handle);287 windows.HeapDestroy(heap_handle);
288 }288 }
289 }289 }
290290
...@@ -305,13 +305,13 @@ pub const HeapAllocator = switch (builtin.os.tag) {...@@ -305,13 +305,13 @@ pub const HeapAllocator = switch (builtin.os.tag) {
305 const amt = n + ptr_align - 1 + @sizeOf(usize);305 const amt = n + ptr_align - 1 + @sizeOf(usize);
306 const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, .seq_cst);306 const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, .seq_cst);
307 const heap_handle = optional_heap_handle orelse blk: {307 const heap_handle = optional_heap_handle orelse blk: {
308 const options = if (builtin.single_threaded) os.windows.HEAP_NO_SERIALIZE else 0;308 const options = if (builtin.single_threaded) windows.HEAP_NO_SERIALIZE else 0;
309 const hh = os.windows.kernel32.HeapCreate(options, amt, 0) orelse return null;309 const hh = windows.kernel32.HeapCreate(options, amt, 0) orelse return null;
310 const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, .seq_cst, .seq_cst) orelse break :blk hh;310 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);
312 break :blk other_hh.?; // can't be null because of the cmpxchg312 break :blk other_hh.?; // can't be null because of the cmpxchg
313 };313 };
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;
315 const root_addr = @intFromPtr(ptr);315 const root_addr = @intFromPtr(ptr);
316 const aligned_addr = mem.alignForward(usize, root_addr, ptr_align);316 const aligned_addr = mem.alignForward(usize, root_addr, ptr_align);
317 const buf = @as([*]u8, @ptrFromInt(aligned_addr))[0..n];317 const buf = @as([*]u8, @ptrFromInt(aligned_addr))[0..n];
...@@ -333,9 +333,9 @@ pub const HeapAllocator = switch (builtin.os.tag) {...@@ -333,9 +333,9 @@ pub const HeapAllocator = switch (builtin.os.tag) {
333 const root_addr = getRecordPtr(buf).*;333 const root_addr = getRecordPtr(buf).*;
334 const align_offset = @intFromPtr(buf.ptr) - root_addr;334 const align_offset = @intFromPtr(buf.ptr) - root_addr;
335 const amt = align_offset + new_size + @sizeOf(usize);335 const amt = align_offset + new_size + @sizeOf(usize);
336 const new_ptr = os.windows.kernel32.HeapReAlloc(336 const new_ptr = windows.kernel32.HeapReAlloc(
337 self.heap_handle.?,337 self.heap_handle.?,
338 os.windows.HEAP_REALLOC_IN_PLACE_ONLY,338 windows.HEAP_REALLOC_IN_PLACE_ONLY,
339 @as(*anyopaque, @ptrFromInt(root_addr)),339 @as(*anyopaque, @ptrFromInt(root_addr)),
340 amt,340 amt,
341 ) orelse return false;341 ) orelse return false;
...@@ -353,7 +353,7 @@ pub const HeapAllocator = switch (builtin.os.tag) {...@@ -353,7 +353,7 @@ pub const HeapAllocator = switch (builtin.os.tag) {
353 _ = log2_buf_align;353 _ = log2_buf_align;
354 _ = return_address;354 _ = return_address;
355 const self: *HeapAllocator = @ptrCast(@alignCast(ctx));355 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).*)));
357 }357 }
358 },358 },
359 else => @compileError("Unsupported OS"),359 else => @compileError("Unsupported OS"),
lib/std/heap/PageAllocator.zig+16-16
...@@ -2,9 +2,11 @@ const std = @import("../std.zig");...@@ -2,9 +2,11 @@ const std = @import("../std.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Allocator = std.mem.Allocator;3const Allocator = std.mem.Allocator;
4const mem = std.mem;4const mem = std.mem;
5const os = std.os;
6const maxInt = std.math.maxInt;5const maxInt = std.math.maxInt;
7const assert = std.debug.assert;6const assert = std.debug.assert;
7const native_os = builtin.os.tag;
8const windows = std.os.windows;
9const posix = std.posix;
810
9pub const vtable = Allocator.VTable{11pub const vtable = Allocator.VTable{
10 .alloc = alloc,12 .alloc = alloc,
...@@ -19,22 +21,21 @@ fn alloc(_: *anyopaque, n: usize, log2_align: u8, ra: usize) ?[*]u8 {...@@ -19,22 +21,21 @@ fn alloc(_: *anyopaque, n: usize, log2_align: u8, ra: usize) ?[*]u8 {
19 if (n > maxInt(usize) - (mem.page_size - 1)) return null;21 if (n > maxInt(usize) - (mem.page_size - 1)) return null;
20 const aligned_len = mem.alignForward(usize, n, mem.page_size);22 const aligned_len = mem.alignForward(usize, n, mem.page_size);
2123
22 if (builtin.os.tag == .windows) {24 if (native_os == .windows) {
23 const w = os.windows;25 const addr = windows.VirtualAlloc(
24 const addr = w.VirtualAlloc(
25 null,26 null,
26 aligned_len,27 aligned_len,
27 w.MEM_COMMIT | w.MEM_RESERVE,28 windows.MEM_COMMIT | windows.MEM_RESERVE,
28 w.PAGE_READWRITE,29 windows.PAGE_READWRITE,
29 ) catch return null;30 ) catch return null;
30 return @ptrCast(addr);31 return @ptrCast(addr);
31 }32 }
3233
33 const hint = @atomicLoad(@TypeOf(std.heap.next_mmap_addr_hint), &std.heap.next_mmap_addr_hint, .unordered);34 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(
35 hint,36 hint,
36 aligned_len,37 aligned_len,
37 os.PROT.READ | os.PROT.WRITE,38 posix.PROT.READ | posix.PROT.WRITE,
38 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },39 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
39 -1,40 -1,
40 0,41 0,
...@@ -56,8 +57,7 @@ fn resize(...@@ -56,8 +57,7 @@ fn resize(
56 _ = return_address;57 _ = return_address;
57 const new_size_aligned = mem.alignForward(usize, new_size, mem.page_size);58 const new_size_aligned = mem.alignForward(usize, new_size, mem.page_size);
5859
59 if (builtin.os.tag == .windows) {60 if (native_os == .windows) {
60 const w = os.windows;
61 if (new_size <= buf_unaligned.len) {61 if (new_size <= buf_unaligned.len) {
62 const base_addr = @intFromPtr(buf_unaligned.ptr);62 const base_addr = @intFromPtr(buf_unaligned.ptr);
63 const old_addr_end = base_addr + buf_unaligned.len;63 const old_addr_end = base_addr + buf_unaligned.len;
...@@ -65,10 +65,10 @@ fn resize(...@@ -65,10 +65,10 @@ fn resize(
65 if (old_addr_end > new_addr_end) {65 if (old_addr_end > new_addr_end) {
66 // For shrinking that is not releasing, we will only66 // For shrinking that is not releasing, we will only
67 // decommit the pages not needed anymore.67 // decommit the pages not needed anymore.
68 w.VirtualFree(68 windows.VirtualFree(
69 @as(*anyopaque, @ptrFromInt(new_addr_end)),69 @as(*anyopaque, @ptrFromInt(new_addr_end)),
70 old_addr_end - new_addr_end,70 old_addr_end - new_addr_end,
71 w.MEM_DECOMMIT,71 windows.MEM_DECOMMIT,
72 );72 );
73 }73 }
74 return true;74 return true;
...@@ -87,7 +87,7 @@ fn resize(...@@ -87,7 +87,7 @@ fn resize(
87 if (new_size_aligned < buf_aligned_len) {87 if (new_size_aligned < buf_aligned_len) {
88 const ptr = buf_unaligned.ptr + new_size_aligned;88 const ptr = buf_unaligned.ptr + new_size_aligned;
89 // TODO: if the next_mmap_addr_hint is within the unmapped range, update it89 // 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]));
91 return true;91 return true;
92 }92 }
9393
...@@ -100,10 +100,10 @@ fn free(_: *anyopaque, slice: []u8, log2_buf_align: u8, return_address: usize) v...@@ -100,10 +100,10 @@ fn free(_: *anyopaque, slice: []u8, log2_buf_align: u8, return_address: usize) v
100 _ = log2_buf_align;100 _ = log2_buf_align;
101 _ = return_address;101 _ = return_address;
102102
103 if (builtin.os.tag == .windows) {103 if (native_os == .windows) {
104 os.windows.VirtualFree(slice.ptr, 0, os.windows.MEM_RELEASE);104 windows.VirtualFree(slice.ptr, 0, windows.MEM_RELEASE);
105 } else {105 } else {
106 const buf_aligned_len = mem.alignForward(usize, slice.len, mem.page_size);106 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]));
108 }108 }
109}109}
lib/std/http/Client.zig+5-5
...@@ -220,7 +220,7 @@ pub const Connection = struct {...@@ -220,7 +220,7 @@ pub const Connection = struct {
220220
221 pub const Protocol = enum { plain, tls };221 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 {
224 return conn.tls_client.readv(conn.stream, buffers) catch |err| {224 return conn.tls_client.readv(conn.stream, buffers) catch |err| {
225 // https://github.com/ziglang/zig/issues/2473225 // https://github.com/ziglang/zig/issues/2473
226 if (mem.startsWith(u8, @errorName(err), "TlsAlert")) return error.TlsAlert;226 if (mem.startsWith(u8, @errorName(err), "TlsAlert")) return error.TlsAlert;
...@@ -234,7 +234,7 @@ pub const Connection = struct {...@@ -234,7 +234,7 @@ pub const Connection = struct {
234 };234 };
235 }235 }
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 {
238 if (conn.protocol == .tls) {238 if (conn.protocol == .tls) {
239 if (disable_tls) unreachable;239 if (disable_tls) unreachable;
240240
...@@ -252,7 +252,7 @@ pub const Connection = struct {...@@ -252,7 +252,7 @@ pub const Connection = struct {
252 pub fn fill(conn: *Connection) ReadError!void {252 pub fn fill(conn: *Connection) ReadError!void {
253 if (conn.read_end != conn.read_start) return;253 if (conn.read_end != conn.read_start) return;
254254
255 var iovecs = [1]std.os.iovec{255 var iovecs = [1]std.posix.iovec{
256 .{ .iov_base = &conn.read_buf, .iov_len = conn.read_buf.len },256 .{ .iov_base = &conn.read_buf, .iov_len = conn.read_buf.len },
257 };257 };
258 const nread = try conn.readvDirect(&iovecs);258 const nread = try conn.readvDirect(&iovecs);
...@@ -288,7 +288,7 @@ pub const Connection = struct {...@@ -288,7 +288,7 @@ pub const Connection = struct {
288 return available_read;288 return available_read;
289 }289 }
290290
291 var iovecs = [2]std.os.iovec{291 var iovecs = [2]std.posix.iovec{
292 .{ .iov_base = buffer.ptr, .iov_len = buffer.len },292 .{ .iov_base = buffer.ptr, .iov_len = buffer.len },
293 .{ .iov_base = &conn.read_buf, .iov_len = conn.read_buf.len },293 .{ .iov_base = &conn.read_buf, .iov_len = conn.read_buf.len },
294 };294 };
...@@ -1387,7 +1387,7 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec...@@ -1387,7 +1387,7 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec
1387 return &conn.data;1387 return &conn.data;
1388}1388}
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
1392/// Connect to `path` as a unix domain socket. This will reuse a connection if one is already open.1392/// Connect to `path` as a unix domain socket. This will reuse a connection if one is already open.
1393///1393///
lib/std/io.zig+54-52
...@@ -2,74 +2,76 @@ const std = @import("std.zig");...@@ -2,74 +2,76 @@ const std = @import("std.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const root = @import("root");3const root = @import("root");
4const c = std.c;4const c = std.c;
5const is_windows = builtin.os.tag == .windows;
6const windows = std.os.windows;
7const posix = std.posix;
58
6const math = std.math;9const math = std.math;
7const assert = std.debug.assert;10const assert = std.debug.assert;
8const os = std.os;
9const fs = std.fs;11const fs = std.fs;
10const mem = std.mem;12const mem = std.mem;
11const meta = std.meta;13const meta = std.meta;
12const File = std.fs.File;14const File = std.fs.File;
13const Allocator = std.mem.Allocator;15const Allocator = std.mem.Allocator;
1416
15fn getStdOutHandle() os.fd_t {17fn getStdOutHandle() posix.fd_t {
16 if (builtin.os.tag == .windows) {18 if (is_windows) {
17 if (builtin.zig_backend == .stage2_aarch64) {19 if (builtin.zig_backend == .stage2_aarch64) {
18 // TODO: this is just a temporary workaround until we advance aarch64 backend further along.20 // 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;
20 }22 }
21 return os.windows.peb().ProcessParameters.hStdOutput;23 return windows.peb().ProcessParameters.hStdOutput;
22 }24 }
2325
24 if (@hasDecl(root, "os") and @hasDecl(root.os, "io") and @hasDecl(root.os.io, "getStdOutHandle")) {26 if (@hasDecl(root, "os") and @hasDecl(root.os, "io") and @hasDecl(root.os.io, "getStdOutHandle")) {
25 return root.os.io.getStdOutHandle();27 return root.os.io.getStdOutHandle();
26 }28 }
2729
28 return os.STDOUT_FILENO;30 return posix.STDOUT_FILENO;
29}31}
3032
31pub fn getStdOut() File {33pub fn getStdOut() File {
32 return File{ .handle = getStdOutHandle() };34 return .{ .handle = getStdOutHandle() };
33}35}
3436
35fn getStdErrHandle() os.fd_t {37fn getStdErrHandle() posix.fd_t {
36 if (builtin.os.tag == .windows) {38 if (is_windows) {
37 if (builtin.zig_backend == .stage2_aarch64) {39 if (builtin.zig_backend == .stage2_aarch64) {
38 // TODO: this is just a temporary workaround until we advance aarch64 backend further along.40 // 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;
40 }42 }
41 return os.windows.peb().ProcessParameters.hStdError;43 return windows.peb().ProcessParameters.hStdError;
42 }44 }
4345
44 if (@hasDecl(root, "os") and @hasDecl(root.os, "io") and @hasDecl(root.os.io, "getStdErrHandle")) {46 if (@hasDecl(root, "os") and @hasDecl(root.os, "io") and @hasDecl(root.os.io, "getStdErrHandle")) {
45 return root.os.io.getStdErrHandle();47 return root.os.io.getStdErrHandle();
46 }48 }
4749
48 return os.STDERR_FILENO;50 return posix.STDERR_FILENO;
49}51}
5052
51pub fn getStdErr() File {53pub fn getStdErr() File {
52 return File{ .handle = getStdErrHandle() };54 return .{ .handle = getStdErrHandle() };
53}55}
5456
55fn getStdInHandle() os.fd_t {57fn getStdInHandle() posix.fd_t {
56 if (builtin.os.tag == .windows) {58 if (is_windows) {
57 if (builtin.zig_backend == .stage2_aarch64) {59 if (builtin.zig_backend == .stage2_aarch64) {
58 // TODO: this is just a temporary workaround until we advance aarch64 backend further along.60 // 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;
60 }62 }
61 return os.windows.peb().ProcessParameters.hStdInput;63 return windows.peb().ProcessParameters.hStdInput;
62 }64 }
6365
64 if (@hasDecl(root, "os") and @hasDecl(root.os, "io") and @hasDecl(root.os.io, "getStdInHandle")) {66 if (@hasDecl(root, "os") and @hasDecl(root.os, "io") and @hasDecl(root.os.io, "getStdInHandle")) {
65 return root.os.io.getStdInHandle();67 return root.os.io.getStdInHandle();
66 }68 }
6769
68 return os.STDIN_FILENO;70 return posix.STDIN_FILENO;
69}71}
7072
71pub fn getStdIn() File {73pub fn getStdIn() File {
72 return File{ .handle = getStdInHandle() };74 return .{ .handle = getStdInHandle() };
73}75}
7476
75pub fn GenericReader(77pub fn GenericReader(
...@@ -434,10 +436,10 @@ pub fn poll(...@@ -434,10 +436,10 @@ pub fn poll(
434 const enum_fields = @typeInfo(StreamEnum).Enum.fields;436 const enum_fields = @typeInfo(StreamEnum).Enum.fields;
435 var result: Poller(StreamEnum) = undefined;437 var result: Poller(StreamEnum) = undefined;
436438
437 if (builtin.os.tag == .windows) result.windows = .{439 if (is_windows) result.windows = .{
438 .first_read_done = false,440 .first_read_done = false,
439 .overlapped = [1]os.windows.OVERLAPPED{441 .overlapped = [1]windows.OVERLAPPED{
440 mem.zeroes(os.windows.OVERLAPPED),442 mem.zeroes(windows.OVERLAPPED),
441 } ** enum_fields.len,443 } ** enum_fields.len,
442 .active = .{444 .active = .{
443 .count = 0,445 .count = 0,
...@@ -453,12 +455,12 @@ pub fn poll(...@@ -453,12 +455,12 @@ pub fn poll(
453 .head = 0,455 .head = 0,
454 .count = 0,456 .count = 0,
455 };457 };
456 if (builtin.os.tag == .windows) {458 if (is_windows) {
457 result.windows.active.handles_buf[i] = @field(files, enum_fields[i].name).handle;459 result.windows.active.handles_buf[i] = @field(files, enum_fields[i].name).handle;
458 } else {460 } else {
459 result.poll_fds[i] = .{461 result.poll_fds[i] = .{
460 .fd = @field(files, enum_fields[i].name).handle,462 .fd = @field(files, enum_fields[i].name).handle,
461 .events = os.POLL.IN,463 .events = posix.POLL.IN,
462 .revents = undefined,464 .revents = undefined,
463 };465 };
464 }466 }
...@@ -471,16 +473,16 @@ pub const PollFifo = std.fifo.LinearFifo(u8, .Dynamic);...@@ -471,16 +473,16 @@ pub const PollFifo = std.fifo.LinearFifo(u8, .Dynamic);
471pub fn Poller(comptime StreamEnum: type) type {473pub fn Poller(comptime StreamEnum: type) type {
472 return struct {474 return struct {
473 const enum_fields = @typeInfo(StreamEnum).Enum.fields;475 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
476 fifos: [enum_fields.len]PollFifo,478 fifos: [enum_fields.len]PollFifo,
477 poll_fds: [enum_fields.len]PollFd,479 poll_fds: [enum_fields.len]PollFd,
478 windows: if (builtin.os.tag == .windows) struct {480 windows: if (is_windows) struct {
479 first_read_done: bool,481 first_read_done: bool,
480 overlapped: [enum_fields.len]os.windows.OVERLAPPED,482 overlapped: [enum_fields.len]windows.OVERLAPPED,
481 active: struct {483 active: struct {
482 count: math.IntFittingRange(0, enum_fields.len),484 count: math.IntFittingRange(0, enum_fields.len),
483 handles_buf: [enum_fields.len]os.windows.HANDLE,485 handles_buf: [enum_fields.len]windows.HANDLE,
484 stream_map: [enum_fields.len]StreamEnum,486 stream_map: [enum_fields.len]StreamEnum,
485487
486 pub fn removeAt(self: *@This(), index: u32) void {488 pub fn removeAt(self: *@This(), index: u32) void {
...@@ -497,10 +499,10 @@ pub fn Poller(comptime StreamEnum: type) type {...@@ -497,10 +499,10 @@ pub fn Poller(comptime StreamEnum: type) type {
497 const Self = @This();499 const Self = @This();
498500
499 pub fn deinit(self: *Self) void {501 pub fn deinit(self: *Self) void {
500 if (builtin.os.tag == .windows) {502 if (is_windows) {
501 // cancel any pending IO to prevent clobbering OVERLAPPED value503 // cancel any pending IO to prevent clobbering OVERLAPPED value
502 for (self.windows.active.handles_buf[0..self.windows.active.count]) |h| {504 for (self.windows.active.handles_buf[0..self.windows.active.count]) |h| {
503 _ = os.windows.kernel32.CancelIo(h);505 _ = windows.kernel32.CancelIo(h);
504 }506 }
505 }507 }
506 inline for (&self.fifos) |*q| q.deinit();508 inline for (&self.fifos) |*q| q.deinit();
...@@ -508,7 +510,7 @@ pub fn Poller(comptime StreamEnum: type) type {...@@ -508,7 +510,7 @@ pub fn Poller(comptime StreamEnum: type) type {
508 }510 }
509511
510 pub fn poll(self: *Self) !bool {512 pub fn poll(self: *Self) !bool {
511 if (builtin.os.tag == .windows) {513 if (is_windows) {
512 return pollWindows(self, null);514 return pollWindows(self, null);
513 } else {515 } else {
514 return pollPosix(self, null);516 return pollPosix(self, null);
...@@ -516,7 +518,7 @@ pub fn Poller(comptime StreamEnum: type) type {...@@ -516,7 +518,7 @@ pub fn Poller(comptime StreamEnum: type) type {
516 }518 }
517519
518 pub fn pollTimeout(self: *Self, nanoseconds: u64) !bool {520 pub fn pollTimeout(self: *Self, nanoseconds: u64) !bool {
519 if (builtin.os.tag == .windows) {521 if (is_windows) {
520 return pollWindows(self, nanoseconds);522 return pollWindows(self, nanoseconds);
521 } else {523 } else {
522 return pollPosix(self, nanoseconds);524 return pollPosix(self, nanoseconds);
...@@ -554,39 +556,39 @@ pub fn Poller(comptime StreamEnum: type) type {...@@ -554,39 +556,39 @@ pub fn Poller(comptime StreamEnum: type) type {
554 while (true) {556 while (true) {
555 if (self.windows.active.count == 0) return false;557 if (self.windows.active.count == 0) return false;
556558
557 const status = os.windows.kernel32.WaitForMultipleObjects(559 const status = windows.kernel32.WaitForMultipleObjects(
558 self.windows.active.count,560 self.windows.active.count,
559 &self.windows.active.handles_buf,561 &self.windows.active.handles_buf,
560 0,562 0,
561 if (nanoseconds) |ns|563 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)
563 else565 else
564 os.windows.INFINITE,566 windows.INFINITE,
565 );567 );
566 if (status == os.windows.WAIT_FAILED)568 if (status == windows.WAIT_FAILED)
567 return os.windows.unexpectedError(os.windows.kernel32.GetLastError());569 return windows.unexpectedError(windows.kernel32.GetLastError());
568 if (status == os.windows.WAIT_TIMEOUT)570 if (status == windows.WAIT_TIMEOUT)
569 return true;571 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)
572 unreachable;574 unreachable;
573575
574 const active_idx = status - os.windows.WAIT_OBJECT_0;576 const active_idx = status - windows.WAIT_OBJECT_0;
575577
576 const handle = self.windows.active.handles_buf[active_idx];578 const handle = self.windows.active.handles_buf[active_idx];
577 const stream_idx = @intFromEnum(self.windows.active.stream_map[active_idx]);579 const stream_idx = @intFromEnum(self.windows.active.stream_map[active_idx]);
578 var read_bytes: u32 = undefined;580 var read_bytes: u32 = undefined;
579 if (0 == os.windows.kernel32.GetOverlappedResult(581 if (0 == windows.kernel32.GetOverlappedResult(
580 handle,582 handle,
581 &self.windows.overlapped[stream_idx],583 &self.windows.overlapped[stream_idx],
582 &read_bytes,584 &read_bytes,
583 0,585 0,
584 )) switch (os.windows.kernel32.GetLastError()) {586 )) switch (windows.kernel32.GetLastError()) {
585 .BROKEN_PIPE => {587 .BROKEN_PIPE => {
586 self.windows.active.removeAt(active_idx);588 self.windows.active.removeAt(active_idx);
587 continue;589 continue;
588 },590 },
589 else => |err| return os.windows.unexpectedError(err),591 else => |err| return windows.unexpectedError(err),
590 };592 };
591593
592 self.fifos[stream_idx].update(read_bytes);594 self.fifos[stream_idx].update(read_bytes);
...@@ -611,9 +613,9 @@ pub fn Poller(comptime StreamEnum: type) type {...@@ -611,9 +613,9 @@ pub fn Poller(comptime StreamEnum: type) type {
611 // allocate grows exponentially.613 // allocate grows exponentially.
612 const bump_amt = 512;614 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|
617 std.math.cast(i32, ns / std.time.ns_per_ms) orelse std.math.maxInt(i32)619 std.math.cast(i32, ns / std.time.ns_per_ms) orelse std.math.maxInt(i32)
618 else620 else
619 -1);621 -1);
...@@ -629,9 +631,9 @@ pub fn Poller(comptime StreamEnum: type) type {...@@ -629,9 +631,9 @@ pub fn Poller(comptime StreamEnum: type) type {
629 // conditions.631 // conditions.
630 // It's still possible to read after a POLL.HUP is received,632 // It's still possible to read after a POLL.HUP is received,
631 // always check if there's some data waiting to be read first.633 // 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) {
633 const buf = try q.writableWithSize(bump_amt);635 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);
635 q.update(amt);637 q.update(amt);
636 if (amt == 0) {638 if (amt == 0) {
637 // Remove the fd when the EOF condition is met.639 // Remove the fd when the EOF condition is met.
...@@ -652,19 +654,19 @@ pub fn Poller(comptime StreamEnum: type) type {...@@ -652,19 +654,19 @@ pub fn Poller(comptime StreamEnum: type) type {
652}654}
653655
654fn windowsAsyncRead(656fn windowsAsyncRead(
655 handle: os.windows.HANDLE,657 handle: windows.HANDLE,
656 overlapped: *os.windows.OVERLAPPED,658 overlapped: *windows.OVERLAPPED,
657 fifo: *PollFifo,659 fifo: *PollFifo,
658 bump_amt: usize,660 bump_amt: usize,
659) !enum { pending, closed } {661) !enum { pending, closed } {
660 while (true) {662 while (true) {
661 const buf = try fifo.writableWithSize(bump_amt);663 const buf = try fifo.writableWithSize(bump_amt);
662 var read_bytes: u32 = undefined;664 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);665 const read_result = 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()) {666 if (read_result == 0) return switch (windows.kernel32.GetLastError()) {
665 .IO_PENDING => .pending,667 .IO_PENDING => .pending,
666 .BROKEN_PIPE => .closed,668 .BROKEN_PIPE => .closed,
667 else => |err| os.windows.unexpectedError(err),669 else => |err| windows.unexpectedError(err),
668 };670 };
669 fifo.update(read_bytes);671 fifo.update(read_bytes);
670 }672 }
lib/std/io/c_writer.zig+3-4
...@@ -2,7 +2,6 @@ const std = @import("../std.zig");...@@ -2,7 +2,6 @@ const std = @import("../std.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const io = std.io;3const io = std.io;
4const testing = std.testing;4const testing = std.testing;
5const os = std.os;
65
7pub const CWriter = io.Writer(*std.c.FILE, std.fs.File.WriteError, cWriterWrite);6pub 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 {...@@ -13,7 +12,7 @@ pub fn cWriter(c_file: *std.c.FILE) CWriter {
13fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!usize {12fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!usize {
14 const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, c_file);13 const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, c_file);
15 if (amt_written >= 0) return amt_written;14 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().*))) {
17 .SUCCESS => unreachable,16 .SUCCESS => unreachable,
18 .INVAL => unreachable,17 .INVAL => unreachable,
19 .FAULT => unreachable,18 .FAULT => unreachable,
...@@ -26,11 +25,11 @@ fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!u...@@ -26,11 +25,11 @@ fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!u
26 .NOSPC => return error.NoSpaceLeft,25 .NOSPC => return error.NoSpaceLeft,
27 .PERM => return error.AccessDenied,26 .PERM => return error.AccessDenied,
28 .PIPE => return error.BrokenPipe,27 .PIPE => return error.BrokenPipe,
29 else => |err| return os.unexpectedErrno(err),28 else => |err| return std.posix.unexpectedErrno(err),
30 }29 }
31}30}
3231
33test "C Writer" {32test cWriter {
34 if (!builtin.link_libc or builtin.os.tag == .wasi) return error.SkipZigTest;33 if (!builtin.link_libc or builtin.os.tag == .wasi) return error.SkipZigTest;
3534
36 const filename = "tmp_io_test_file.txt";35 const filename = "tmp_io_test_file.txt";
lib/std/net.zig+167-166
...@@ -5,15 +5,16 @@ const builtin = @import("builtin");...@@ -5,15 +5,16 @@ const builtin = @import("builtin");
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const net = @This();6const net = @This();
7const mem = std.mem;7const mem = std.mem;
8const os = std.os;
9const posix = std.posix;8const posix = std.posix;
10const fs = std.fs;9const fs = std.fs;
11const io = std.io;10const io = std.io;
12const native_endian = builtin.target.cpu.arch.endian();11const native_endian = builtin.target.cpu.arch.endian();
12const native_os = builtin.os.tag;
13const windows = std.os.windows;
1314
14// Windows 10 added support for unix sockets in build 17063, redstone 4 is the15// Windows 10 added support for unix sockets in build 17063, redstone 4 is the
15// first release to support them.16// first release to support them.
16pub const has_unix_sockets = switch (builtin.os.tag) {17pub const has_unix_sockets = switch (native_os) {
17 .windows => builtin.os.version_range.windows.isAtLeast(.win10_rs4) orelse false,18 .windows => builtin.os.version_range.windows.isAtLeast(.win10_rs4) orelse false,
18 else => true,19 else => true,
19};20};
...@@ -28,14 +29,14 @@ pub const IPParseError = error{...@@ -28,14 +29,14 @@ pub const IPParseError = error{
28pub const IPv4ParseError = IPParseError || error{NonCanonical};29pub const IPv4ParseError = IPParseError || error{NonCanonical};
2930
30pub const IPv6ParseError = IPParseError || error{InvalidIpv4Mapping};31pub 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};
32pub const IPv6ResolveError = IPv6ParseError || IPv6InterfaceError;33pub const IPv6ResolveError = IPv6ParseError || IPv6InterfaceError;
3334
34pub const Address = extern union {35pub const Address = extern union {
35 any: os.sockaddr,36 any: posix.sockaddr,
36 in: Ip4Address,37 in: Ip4Address,
37 in6: Ip6Address,38 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
40 /// Parse the given IP address string into an Address value.41 /// Parse the given IP address string into an Address value.
41 /// It is recommended to use `resolveIp` instead, to handle42 /// It is recommended to use `resolveIp` instead, to handle
...@@ -85,38 +86,38 @@ pub const Address = extern union {...@@ -85,38 +86,38 @@ pub const Address = extern union {
85 return error.InvalidIPAddressFormat;86 return error.InvalidIPAddressFormat;
86 }87 }
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 {
89 switch (family) {90 switch (family) {
90 os.AF.INET => return parseIp4(name, port),91 posix.AF.INET => return parseIp4(name, port),
91 os.AF.INET6 => return parseIp6(name, port),92 posix.AF.INET6 => return parseIp6(name, port),
92 os.AF.UNSPEC => return parseIp(name, port),93 posix.AF.UNSPEC => return parseIp(name, port),
93 else => unreachable,94 else => unreachable,
94 }95 }
95 }96 }
9697
97 pub fn parseIp6(buf: []const u8, port: u16) IPv6ParseError!Address {98 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) };
99 }100 }
100101
101 pub fn resolveIp6(buf: []const u8, port: u16) IPv6ResolveError!Address {102 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) };
103 }104 }
104105
105 pub fn parseIp4(buf: []const u8, port: u16) IPv4ParseError!Address {106 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) };
107 }108 }
108109
109 pub fn initIp4(addr: [4]u8, port: u16) Address {110 pub fn initIp4(addr: [4]u8, port: u16) Address {
110 return Address{ .in = Ip4Address.init(addr, port) };111 return .{ .in = Ip4Address.init(addr, port) };
111 }112 }
112113
113 pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Address {114 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) };
115 }116 }
116117
117 pub fn initUnix(path: []const u8) !Address {118 pub fn initUnix(path: []const u8) !Address {
118 var sock_addr = os.sockaddr.un{119 var sock_addr = posix.sockaddr.un{
119 .family = os.AF.UNIX,120 .family = posix.AF.UNIX,
120 .path = undefined,121 .path = undefined,
121 };122 };
122123
...@@ -133,8 +134,8 @@ pub const Address = extern union {...@@ -133,8 +134,8 @@ pub const Address = extern union {
133 /// Asserts that the address is ip4 or ip6.134 /// Asserts that the address is ip4 or ip6.
134 pub fn getPort(self: Address) u16 {135 pub fn getPort(self: Address) u16 {
135 return switch (self.any.family) {136 return switch (self.any.family) {
136 os.AF.INET => self.in.getPort(),137 posix.AF.INET => self.in.getPort(),
137 os.AF.INET6 => self.in6.getPort(),138 posix.AF.INET6 => self.in6.getPort(),
138 else => unreachable,139 else => unreachable,
139 };140 };
140 }141 }
...@@ -143,8 +144,8 @@ pub const Address = extern union {...@@ -143,8 +144,8 @@ pub const Address = extern union {
143 /// Asserts that the address is ip4 or ip6.144 /// Asserts that the address is ip4 or ip6.
144 pub fn setPort(self: *Address, port: u16) void {145 pub fn setPort(self: *Address, port: u16) void {
145 switch (self.any.family) {146 switch (self.any.family) {
146 os.AF.INET => self.in.setPort(port),147 posix.AF.INET => self.in.setPort(port),
147 os.AF.INET6 => self.in6.setPort(port),148 posix.AF.INET6 => self.in6.setPort(port),
148 else => unreachable,149 else => unreachable,
149 }150 }
150 }151 }
...@@ -152,10 +153,10 @@ pub const Address = extern union {...@@ -152,10 +153,10 @@ pub const Address = extern union {
152 /// Asserts that `addr` is an IP address.153 /// Asserts that `addr` is an IP address.
153 /// This function will read past the end of the pointer, with a size depending154 /// This function will read past the end of the pointer, with a size depending
154 /// on the address family.155 /// 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 {
156 switch (addr.family) {157 switch (addr.family) {
157 os.AF.INET => return Address{ .in = Ip4Address{ .sa = @as(*const os.sockaddr.in, @ptrCast(addr)).* } },158 posix.AF.INET => return Address{ .in = Ip4Address{ .sa = @as(*const posix.sockaddr.in, @ptrCast(addr)).* } },
158 os.AF.INET6 => return Address{ .in6 = Ip6Address{ .sa = @as(*const os.sockaddr.in6, @ptrCast(addr)).* } },159 posix.AF.INET6 => return Address{ .in6 = Ip6Address{ .sa = @as(*const posix.sockaddr.in6, @ptrCast(addr)).* } },
159 else => unreachable,160 else => unreachable,
160 }161 }
161 }162 }
...@@ -168,9 +169,9 @@ pub const Address = extern union {...@@ -168,9 +169,9 @@ pub const Address = extern union {
168 ) !void {169 ) !void {
169 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);170 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self);
170 switch (self.any.family) {171 switch (self.any.family) {
171 os.AF.INET => try self.in.format(fmt, options, out_stream),172 posix.AF.INET => try self.in.format(fmt, options, out_stream),
172 os.AF.INET6 => try self.in6.format(fmt, options, out_stream),173 posix.AF.INET6 => try self.in6.format(fmt, options, out_stream),
173 os.AF.UNIX => {174 posix.AF.UNIX => {
174 if (!has_unix_sockets) {175 if (!has_unix_sockets) {
175 unreachable;176 unreachable;
176 }177 }
...@@ -187,11 +188,11 @@ pub const Address = extern union {...@@ -187,11 +188,11 @@ pub const Address = extern union {
187 return mem.eql(u8, a_bytes, b_bytes);188 return mem.eql(u8, a_bytes, b_bytes);
188 }189 }
189190
190 pub fn getOsSockLen(self: Address) os.socklen_t {191 pub fn getOsSockLen(self: Address) posix.socklen_t {
191 switch (self.any.family) {192 switch (self.any.family) {
192 os.AF.INET => return self.in.getOsSockLen(),193 posix.AF.INET => return self.in.getOsSockLen(),
193 os.AF.INET6 => return self.in6.getOsSockLen(),194 posix.AF.INET6 => return self.in6.getOsSockLen(),
194 os.AF.UNIX => {195 posix.AF.UNIX => {
195 if (!has_unix_sockets) {196 if (!has_unix_sockets) {
196 unreachable;197 unreachable;
197 }198 }
...@@ -204,7 +205,7 @@ pub const Address = extern union {...@@ -204,7 +205,7 @@ pub const Address = extern union {
204 // provide the full buffer size (e.g. getsockname, getpeername, recvfrom, accept).205 // provide the full buffer size (e.g. getsockname, getpeername, recvfrom, accept).
205 //206 //
206 // To access the path, std.mem.sliceTo(&address.un.path, 0) should be used.207 // 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)));
208 },209 },
209210
210 else => unreachable,211 else => unreachable,
...@@ -247,7 +248,7 @@ pub const Address = extern union {...@@ -247,7 +248,7 @@ pub const Address = extern union {
247 posix.SO.REUSEADDR,248 posix.SO.REUSEADDR,
248 &mem.toBytes(@as(c_int, 1)),249 &mem.toBytes(@as(c_int, 1)),
249 );250 );
250 switch (builtin.os.tag) {251 switch (native_os) {
251 .windows => {},252 .windows => {},
252 else => try posix.setsockopt(253 else => try posix.setsockopt(
253 sockfd,254 sockfd,
...@@ -267,7 +268,7 @@ pub const Address = extern union {...@@ -267,7 +268,7 @@ pub const Address = extern union {
267};268};
268269
269pub const Ip4Address = extern struct {270pub const Ip4Address = extern struct {
270 sa: os.sockaddr.in,271 sa: posix.sockaddr.in,
271272
272 pub fn parse(buf: []const u8, port: u16) IPv4ParseError!Ip4Address {273 pub fn parse(buf: []const u8, port: u16) IPv4ParseError!Ip4Address {
273 var result = Ip4Address{274 var result = Ip4Address{
...@@ -330,7 +331,7 @@ pub const Ip4Address = extern struct {...@@ -330,7 +331,7 @@ pub const Ip4Address = extern struct {
330331
331 pub fn init(addr: [4]u8, port: u16) Ip4Address {332 pub fn init(addr: [4]u8, port: u16) Ip4Address {
332 return Ip4Address{333 return Ip4Address{
333 .sa = os.sockaddr.in{334 .sa = posix.sockaddr.in{
334 .port = mem.nativeToBig(u16, port),335 .port = mem.nativeToBig(u16, port),
335 .addr = @as(*align(1) const u32, @ptrCast(&addr)).*,336 .addr = @as(*align(1) const u32, @ptrCast(&addr)).*,
336 },337 },
...@@ -367,21 +368,21 @@ pub const Ip4Address = extern struct {...@@ -367,21 +368,21 @@ pub const Ip4Address = extern struct {
367 });368 });
368 }369 }
369370
370 pub fn getOsSockLen(self: Ip4Address) os.socklen_t {371 pub fn getOsSockLen(self: Ip4Address) posix.socklen_t {
371 _ = self;372 _ = self;
372 return @sizeOf(os.sockaddr.in);373 return @sizeOf(posix.sockaddr.in);
373 }374 }
374};375};
375376
376pub const Ip6Address = extern struct {377pub const Ip6Address = extern struct {
377 sa: os.sockaddr.in6,378 sa: posix.sockaddr.in6,
378379
379 /// Parse a given IPv6 address string into an Address.380 /// Parse a given IPv6 address string into an Address.
380 /// Assumes the Scope ID of the address is fully numeric.381 /// Assumes the Scope ID of the address is fully numeric.
381 /// For non-numeric addresses, see `resolveIp6`.382 /// For non-numeric addresses, see `resolveIp6`.
382 pub fn parse(buf: []const u8, port: u16) IPv6ParseError!Ip6Address {383 pub fn parse(buf: []const u8, port: u16) IPv6ParseError!Ip6Address {
383 var result = Ip6Address{384 var result = Ip6Address{
384 .sa = os.sockaddr.in6{385 .sa = posix.sockaddr.in6{
385 .scope_id = 0,386 .scope_id = 0,
386 .port = mem.nativeToBig(u16, port),387 .port = mem.nativeToBig(u16, port),
387 .flowinfo = 0,388 .flowinfo = 0,
...@@ -499,7 +500,7 @@ pub const Ip6Address = extern struct {...@@ -499,7 +500,7 @@ pub const Ip6Address = extern struct {
499 pub fn resolve(buf: []const u8, port: u16) IPv6ResolveError!Ip6Address {500 pub fn resolve(buf: []const u8, port: u16) IPv6ResolveError!Ip6Address {
500 // TODO: Unify the implementations of resolveIp6 and parseIp6.501 // TODO: Unify the implementations of resolveIp6 and parseIp6.
501 var result = Ip6Address{502 var result = Ip6Address{
502 .sa = os.sockaddr.in6{503 .sa = posix.sockaddr.in6{
503 .scope_id = 0,504 .scope_id = 0,
504 .port = mem.nativeToBig(u16, port),505 .port = mem.nativeToBig(u16, port),
505 .flowinfo = 0,506 .flowinfo = 0,
...@@ -516,7 +517,7 @@ pub const Ip6Address = extern struct {...@@ -516,7 +517,7 @@ pub const Ip6Address = extern struct {
516 var abbrv = false;517 var abbrv = false;
517518
518 var scope_id = false;519 var scope_id = false;
519 var scope_id_value: [os.IFNAMESIZE - 1]u8 = undefined;520 var scope_id_value: [posix.IFNAMESIZE - 1]u8 = undefined;
520 var scope_id_index: usize = 0;521 var scope_id_index: usize = 0;
521522
522 for (buf, 0..) |c, i| {523 for (buf, 0..) |c, i| {
...@@ -632,7 +633,7 @@ pub const Ip6Address = extern struct {...@@ -632,7 +633,7 @@ pub const Ip6Address = extern struct {
632633
633 pub fn init(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Ip6Address {634 pub fn init(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Ip6Address {
634 return Ip6Address{635 return Ip6Address{
635 .sa = os.sockaddr.in6{636 .sa = posix.sockaddr.in6{
636 .addr = addr,637 .addr = addr,
637 .port = mem.nativeToBig(u16, port),638 .port = mem.nativeToBig(u16, port),
638 .flowinfo = flowinfo,639 .flowinfo = flowinfo,
...@@ -702,51 +703,51 @@ pub const Ip6Address = extern struct {...@@ -702,51 +703,51 @@ pub const Ip6Address = extern struct {
702 try std.fmt.format(out_stream, "]:{}", .{port});703 try std.fmt.format(out_stream, "]:{}", .{port});
703 }704 }
704705
705 pub fn getOsSockLen(self: Ip6Address) os.socklen_t {706 pub fn getOsSockLen(self: Ip6Address) posix.socklen_t {
706 _ = self;707 _ = self;
707 return @sizeOf(os.sockaddr.in6);708 return @sizeOf(posix.sockaddr.in6);
708 }709 }
709};710};
710711
711pub fn connectUnixSocket(path: []const u8) !Stream {712pub fn connectUnixSocket(path: []const u8) !Stream {
712 const opt_non_block = 0;713 const opt_non_block = 0;
713 const sockfd = try os.socket(714 const sockfd = try posix.socket(
714 os.AF.UNIX,715 posix.AF.UNIX,
715 os.SOCK.STREAM | os.SOCK.CLOEXEC | opt_non_block,716 posix.SOCK.STREAM | posix.SOCK.CLOEXEC | opt_non_block,
716 0,717 0,
717 );718 );
718 errdefer Stream.close(.{ .handle = sockfd });719 errdefer Stream.close(.{ .handle = sockfd });
719720
720 var addr = try std.net.Address.initUnix(path);721 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 };
724}725}
725726
726fn if_nametoindex(name: []const u8) IPv6InterfaceError!u32 {727fn if_nametoindex(name: []const u8) IPv6InterfaceError!u32 {
727 if (builtin.target.os.tag == .linux) {728 if (native_os == .linux) {
728 var ifr: os.ifreq = undefined;729 var ifr: posix.ifreq = undefined;
729 const sockfd = try os.socket(os.AF.UNIX, os.SOCK.DGRAM | os.SOCK.CLOEXEC, 0);730 const sockfd = try posix.socket(posix.AF.UNIX, posix.SOCK.DGRAM | posix.SOCK.CLOEXEC, 0);
730 defer Stream.close(.{ .handle = sockfd });731 defer Stream.close(.{ .handle = sockfd });
731732
732 @memcpy(ifr.ifrn.name[0..name.len], name);733 @memcpy(ifr.ifrn.name[0..name.len], name);
733 ifr.ifrn.name[name.len] = 0;734 ifr.ifrn.name[name.len] = 0;
734735
735 // TODO investigate if this needs to be integrated with evented I/O.736 // 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);
739 }740 }
740741
741 if (comptime builtin.target.os.tag.isDarwin()) {742 if (native_os.isDarwin()) {
742 if (name.len >= os.IFNAMESIZE)743 if (name.len >= posix.IFNAMESIZE)
743 return error.NameTooLong;744 return error.NameTooLong;
744745
745 var if_name: [os.IFNAMESIZE:0]u8 = undefined;746 var if_name: [posix.IFNAMESIZE:0]u8 = undefined;
746 @memcpy(if_name[0..name.len], name);747 @memcpy(if_name[0..name.len], name);
747 if_name[name.len] = 0;748 if_name[name.len] = 0;
748 const if_slice = if_name[0..name.len :0];749 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);
750 if (index == 0)751 if (index == 0)
751 return error.InterfaceNotFound;752 return error.InterfaceNotFound;
752 return @as(u32, @bitCast(index));753 return @as(u32, @bitCast(index));
...@@ -786,24 +787,24 @@ pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) T...@@ -786,24 +787,24 @@ pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) T
786 else => return err,787 else => return err,
787 };788 };
788 }789 }
789 return std.os.ConnectError.ConnectionRefused;790 return posix.ConnectError.ConnectionRefused;
790}791}
791792
792pub const TcpConnectToAddressError = std.os.SocketError || std.os.ConnectError;793pub const TcpConnectToAddressError = posix.SocketError || posix.ConnectError;
793794
794pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream {795pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream {
795 const nonblock = 0;796 const nonblock = 0;
796 const sock_flags = os.SOCK.STREAM | nonblock |797 const sock_flags = posix.SOCK.STREAM | nonblock |
797 (if (builtin.target.os.tag == .windows) 0 else os.SOCK.CLOEXEC);798 (if (native_os == .windows) 0 else posix.SOCK.CLOEXEC);
798 const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO.TCP);799 const sockfd = try posix.socket(address.any.family, sock_flags, posix.IPPROTO.TCP);
799 errdefer Stream.close(.{ .handle = sockfd });800 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
803 return Stream{ .handle = sockfd };804 return Stream{ .handle = sockfd };
804}805}
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{
807 // TODO: break this up into error sets from the various underlying functions808 // TODO: break this up into error sets from the various underlying functions
808809
809 TemporaryNameServerFailure,810 TemporaryNameServerFailure,
...@@ -844,30 +845,30 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get...@@ -844,30 +845,30 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
844 const arena = result.arena.allocator();845 const arena = result.arena.allocator();
845 errdefer result.deinit();846 errdefer result.deinit();
846847
847 if (builtin.target.os.tag == .windows) {848 if (native_os == .windows) {
848 const name_c = try allocator.dupeZ(u8, name);849 const name_c = try allocator.dupeZ(u8, name);
849 defer allocator.free(name_c);850 defer allocator.free(name_c);
850851
851 const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port});852 const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port});
852 defer allocator.free(port_c);853 defer allocator.free(port_c);
853854
854 const ws2_32 = os.windows.ws2_32;855 const ws2_32 = windows.ws2_32;
855 const hints = os.addrinfo{856 const hints = posix.addrinfo{
856 .flags = ws2_32.AI.NUMERICSERV,857 .flags = ws2_32.AI.NUMERICSERV,
857 .family = os.AF.UNSPEC,858 .family = posix.AF.UNSPEC,
858 .socktype = os.SOCK.STREAM,859 .socktype = posix.SOCK.STREAM,
859 .protocol = os.IPPROTO.TCP,860 .protocol = posix.IPPROTO.TCP,
860 .canonname = null,861 .canonname = null,
861 .addr = null,862 .addr = null,
862 .addrlen = 0,863 .addrlen = 0,
863 .next = null,864 .next = null,
864 };865 };
865 var res: ?*os.addrinfo = null;866 var res: ?*posix.addrinfo = null;
866 var first = true;867 var first = true;
867 while (true) {868 while (true) {
868 const rc = ws2_32.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res);869 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 switch (@as(windows.ws2_32.WinsockError, @enumFromInt(@as(u16, @intCast(rc))))) {
870 @as(os.windows.ws2_32.WinsockError, @enumFromInt(0)) => break,871 @as(windows.ws2_32.WinsockError, @enumFromInt(0)) => break,
871 .WSATRY_AGAIN => return error.TemporaryNameServerFailure,872 .WSATRY_AGAIN => return error.TemporaryNameServerFailure,
872 .WSANO_RECOVERY => return error.NameServerFailure,873 .WSANO_RECOVERY => return error.NameServerFailure,
873 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,874 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
...@@ -879,10 +880,10 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get...@@ -879,10 +880,10 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
879 .WSANOTINITIALISED => {880 .WSANOTINITIALISED => {
880 if (!first) return error.Unexpected;881 if (!first) return error.Unexpected;
881 first = false;882 first = false;
882 try os.windows.callWSAStartup();883 try windows.callWSAStartup();
883 continue;884 continue;
884 },885 },
885 else => |err| return os.windows.unexpectedWSAError(err),886 else => |err| return windows.unexpectedWSAError(err),
886 }887 }
887 }888 }
888 defer ws2_32.freeaddrinfo(res);889 defer ws2_32.freeaddrinfo(res);
...@@ -923,18 +924,18 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get...@@ -923,18 +924,18 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
923 const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port});924 const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port});
924 defer allocator.free(port_c);925 defer allocator.free(port_c);
925926
926 const sys = if (builtin.target.os.tag == .windows) os.windows.ws2_32 else os.system;927 const sys = if (native_os == .windows) windows.ws2_32 else posix.system;
927 const hints = os.addrinfo{928 const hints = posix.addrinfo{
928 .flags = sys.AI.NUMERICSERV,929 .flags = sys.AI.NUMERICSERV,
929 .family = os.AF.UNSPEC,930 .family = posix.AF.UNSPEC,
930 .socktype = os.SOCK.STREAM,931 .socktype = posix.SOCK.STREAM,
931 .protocol = os.IPPROTO.TCP,932 .protocol = posix.IPPROTO.TCP,
932 .canonname = null,933 .canonname = null,
933 .addr = null,934 .addr = null,
934 .addrlen = 0,935 .addrlen = 0,
935 .next = null,936 .next = null,
936 };937 };
937 var res: ?*os.addrinfo = null;938 var res: ?*posix.addrinfo = null;
938 switch (sys.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) {939 switch (sys.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) {
939 @as(sys.EAI, @enumFromInt(0)) => {},940 @as(sys.EAI, @enumFromInt(0)) => {},
940 .ADDRFAMILY => return error.HostLacksNetworkAddresses,941 .ADDRFAMILY => return error.HostLacksNetworkAddresses,
...@@ -947,8 +948,8 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get...@@ -947,8 +948,8 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
947 .NONAME => return error.UnknownHostName,948 .NONAME => return error.UnknownHostName,
948 .SERVICE => return error.ServiceUnavailable,949 .SERVICE => return error.ServiceUnavailable,
949 .SOCKTYPE => unreachable, // Invalid socket type requested in hints950 .SOCKTYPE => unreachable, // Invalid socket type requested in hints
950 .SYSTEM => switch (os.errno(-1)) {951 .SYSTEM => switch (posix.errno(-1)) {
951 else => |e| return os.unexpectedErrno(e),952 else => |e| return posix.unexpectedErrno(e),
952 },953 },
953 else => unreachable,954 else => unreachable,
954 }955 }
...@@ -983,9 +984,9 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get...@@ -983,9 +984,9 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get
983 return result;984 return result;
984 }985 }
985986
986 if (builtin.target.os.tag == .linux) {987 if (native_os == .linux) {
987 const flags = std.c.AI.NUMERICSERV;988 const flags = std.c.AI.NUMERICSERV;
988 const family = os.AF.UNSPEC;989 const family = posix.AF.UNSPEC;
989 var lookup_addrs = std.ArrayList(LookupAddr).init(allocator);990 var lookup_addrs = std.ArrayList(LookupAddr).init(allocator);
990 defer lookup_addrs.deinit();991 defer lookup_addrs.deinit();
991992
...@@ -1026,7 +1027,7 @@ fn linuxLookupName(...@@ -1026,7 +1027,7 @@ fn linuxLookupName(
1026 addrs: *std.ArrayList(LookupAddr),1027 addrs: *std.ArrayList(LookupAddr),
1027 canon: *std.ArrayList(u8),1028 canon: *std.ArrayList(u8),
1028 opt_name: ?[]const u8,1029 opt_name: ?[]const u8,
1029 family: os.sa_family_t,1030 family: posix.sa_family_t,
1030 flags: u32,1031 flags: u32,
1031 port: u16,1032 port: u16,
1032) !void {1033) !void {
...@@ -1066,9 +1067,9 @@ fn linuxLookupName(...@@ -1066,9 +1067,9 @@ fn linuxLookupName(
10661067
1067 // No further processing is needed if there are fewer than 21068 // No further processing is needed if there are fewer than 2
1068 // results or if there are only IPv4 results.1069 // 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;
1070 const all_ip4 = for (addrs.items) |addr| {1071 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;
1072 } else true;1073 } else true;
1073 if (all_ip4) return;1074 if (all_ip4) return;
10741075
...@@ -1081,42 +1082,42 @@ fn linuxLookupName(...@@ -1081,42 +1082,42 @@ fn linuxLookupName(
1081 // A more idiomatic "ziggy" implementation would be welcome.1082 // A more idiomatic "ziggy" implementation would be welcome.
1082 for (addrs.items, 0..) |*addr, i| {1083 for (addrs.items, 0..) |*addr, i| {
1083 var key: i32 = 0;1084 var key: i32 = 0;
1084 var sa6: os.sockaddr.in6 = undefined;1085 var sa6: posix.sockaddr.in6 = undefined;
1085 @memset(@as([*]u8, @ptrCast(&sa6))[0..@sizeOf(os.sockaddr.in6)], 0);1086 @memset(@as([*]u8, @ptrCast(&sa6))[0..@sizeOf(posix.sockaddr.in6)], 0);
1086 var da6 = os.sockaddr.in6{1087 var da6 = posix.sockaddr.in6{
1087 .family = os.AF.INET6,1088 .family = posix.AF.INET6,
1088 .scope_id = addr.addr.in6.sa.scope_id,1089 .scope_id = addr.addr.in6.sa.scope_id,
1089 .port = 65535,1090 .port = 65535,
1090 .flowinfo = 0,1091 .flowinfo = 0,
1091 .addr = [1]u8{0} ** 16,1092 .addr = [1]u8{0} ** 16,
1092 };1093 };
1093 var sa4: os.sockaddr.in = undefined;1094 var sa4: posix.sockaddr.in = undefined;
1094 @memset(@as([*]u8, @ptrCast(&sa4))[0..@sizeOf(os.sockaddr.in)], 0);1095 @memset(@as([*]u8, @ptrCast(&sa4))[0..@sizeOf(posix.sockaddr.in)], 0);
1095 var da4 = os.sockaddr.in{1096 var da4 = posix.sockaddr.in{
1096 .family = os.AF.INET,1097 .family = posix.AF.INET,
1097 .port = 65535,1098 .port = 65535,
1098 .addr = 0,1099 .addr = 0,
1099 .zero = [1]u8{0} ** 8,1100 .zero = [1]u8{0} ** 8,
1100 };1101 };
1101 var sa: *align(4) os.sockaddr = undefined;1102 var sa: *align(4) posix.sockaddr = undefined;
1102 var da: *align(4) os.sockaddr = undefined;1103 var da: *align(4) posix.sockaddr = undefined;
1103 var salen: os.socklen_t = undefined;1104 var salen: posix.socklen_t = undefined;
1104 var dalen: os.socklen_t = undefined;1105 var dalen: posix.socklen_t = undefined;
1105 if (addr.addr.any.family == os.AF.INET6) {1106 if (addr.addr.any.family == posix.AF.INET6) {
1106 da6.addr = addr.addr.in6.sa.addr;1107 da6.addr = addr.addr.in6.sa.addr;
1107 da = @ptrCast(&da6);1108 da = @ptrCast(&da6);
1108 dalen = @sizeOf(os.sockaddr.in6);1109 dalen = @sizeOf(posix.sockaddr.in6);
1109 sa = @ptrCast(&sa6);1110 sa = @ptrCast(&sa6);
1110 salen = @sizeOf(os.sockaddr.in6);1111 salen = @sizeOf(posix.sockaddr.in6);
1111 } else {1112 } else {
1112 sa6.addr[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*;1113 sa6.addr[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*;
1113 da6.addr[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*;1114 da6.addr[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*;
1114 mem.writeInt(u32, da6.addr[12..], addr.addr.in.sa.addr, native_endian);1115 mem.writeInt(u32, da6.addr[12..], addr.addr.in.sa.addr, native_endian);
1115 da4.addr = addr.addr.in.sa.addr;1116 da4.addr = addr.addr.in.sa.addr;
1116 da = @ptrCast(&da4);1117 da = @ptrCast(&da4);
1117 dalen = @sizeOf(os.sockaddr.in);1118 dalen = @sizeOf(posix.sockaddr.in);
1118 sa = @ptrCast(&sa4);1119 sa = @ptrCast(&sa4);
1119 salen = @sizeOf(os.sockaddr.in);1120 salen = @sizeOf(posix.sockaddr.in);
1120 }1121 }
1121 const dpolicy = policyOf(da6.addr);1122 const dpolicy = policyOf(da6.addr);
1122 const dscope: i32 = scopeOf(da6.addr);1123 const dscope: i32 = scopeOf(da6.addr);
...@@ -1124,13 +1125,13 @@ fn linuxLookupName(...@@ -1124,13 +1125,13 @@ fn linuxLookupName(
1124 const dprec: i32 = dpolicy.prec;1125 const dprec: i32 = dpolicy.prec;
1125 const MAXADDRS = 3;1126 const MAXADDRS = 3;
1126 var prefixlen: i32 = 0;1127 var prefixlen: i32 = 0;
1127 const sock_flags = os.SOCK.DGRAM | os.SOCK.CLOEXEC;1128 const sock_flags = posix.SOCK.DGRAM | posix.SOCK.CLOEXEC;
1128 if (os.socket(addr.addr.any.family, sock_flags, os.IPPROTO.UDP)) |fd| syscalls: {1129 if (posix.socket(addr.addr.any.family, sock_flags, posix.IPPROTO.UDP)) |fd| syscalls: {
1129 defer Stream.close(.{ .handle = fd });1130 defer Stream.close(.{ .handle = fd });
1130 os.connect(fd, da, dalen) catch break :syscalls;1131 posix.connect(fd, da, dalen) catch break :syscalls;
1131 key |= DAS_USABLE;1132 key |= DAS_USABLE;
1132 os.getsockname(fd, sa, &salen) catch break :syscalls;1133 posix.getsockname(fd, sa, &salen) catch break :syscalls;
1133 if (addr.addr.any.family == os.AF.INET) {1134 if (addr.addr.any.family == posix.AF.INET) {
1134 mem.writeInt(u32, sa6.addr[12..16], sa4.addr, native_endian);1135 mem.writeInt(u32, sa6.addr[12..16], sa4.addr, native_endian);
1135 }1136 }
1136 if (dscope == @as(i32, scopeOf(sa6.addr))) key |= DAS_MATCHINGSCOPE;1137 if (dscope == @as(i32, scopeOf(sa6.addr))) key |= DAS_MATCHINGSCOPE;
...@@ -1267,28 +1268,28 @@ fn addrCmpLessThan(context: void, b: LookupAddr, a: LookupAddr) bool {...@@ -1267,28 +1268,28 @@ fn addrCmpLessThan(context: void, b: LookupAddr, a: LookupAddr) bool {
12671268
1268fn linuxLookupNameFromNull(1269fn linuxLookupNameFromNull(
1269 addrs: *std.ArrayList(LookupAddr),1270 addrs: *std.ArrayList(LookupAddr),
1270 family: os.sa_family_t,1271 family: posix.sa_family_t,
1271 flags: u32,1272 flags: u32,
1272 port: u16,1273 port: u16,
1273) !void {1274) !void {
1274 if ((flags & std.c.AI.PASSIVE) != 0) {1275 if ((flags & std.c.AI.PASSIVE) != 0) {
1275 if (family != os.AF.INET6) {1276 if (family != posix.AF.INET6) {
1276 (try addrs.addOne()).* = LookupAddr{1277 (try addrs.addOne()).* = LookupAddr{
1277 .addr = Address.initIp4([1]u8{0} ** 4, port),1278 .addr = Address.initIp4([1]u8{0} ** 4, port),
1278 };1279 };
1279 }1280 }
1280 if (family != os.AF.INET) {1281 if (family != posix.AF.INET) {
1281 (try addrs.addOne()).* = LookupAddr{1282 (try addrs.addOne()).* = LookupAddr{
1282 .addr = Address.initIp6([1]u8{0} ** 16, port, 0, 0),1283 .addr = Address.initIp6([1]u8{0} ** 16, port, 0, 0),
1283 };1284 };
1284 }1285 }
1285 } else {1286 } else {
1286 if (family != os.AF.INET6) {1287 if (family != posix.AF.INET6) {
1287 (try addrs.addOne()).* = LookupAddr{1288 (try addrs.addOne()).* = LookupAddr{
1288 .addr = Address.initIp4([4]u8{ 127, 0, 0, 1 }, port),1289 .addr = Address.initIp4([4]u8{ 127, 0, 0, 1 }, port),
1289 };1290 };
1290 }1291 }
1291 if (family != os.AF.INET) {1292 if (family != posix.AF.INET) {
1292 (try addrs.addOne()).* = LookupAddr{1293 (try addrs.addOne()).* = LookupAddr{
1293 .addr = Address.initIp6(([1]u8{0} ** 15) ++ [1]u8{1}, port, 0, 0),1294 .addr = Address.initIp6(([1]u8{0} ** 15) ++ [1]u8{1}, port, 0, 0),
1294 };1295 };
...@@ -1300,7 +1301,7 @@ fn linuxLookupNameFromHosts(...@@ -1300,7 +1301,7 @@ fn linuxLookupNameFromHosts(
1300 addrs: *std.ArrayList(LookupAddr),1301 addrs: *std.ArrayList(LookupAddr),
1301 canon: *std.ArrayList(u8),1302 canon: *std.ArrayList(u8),
1302 name: []const u8,1303 name: []const u8,
1303 family: os.sa_family_t,1304 family: posix.sa_family_t,
1304 port: u16,1305 port: u16,
1305) !void {1306) !void {
1306 const file = fs.openFileAbsoluteZ("/etc/hosts", .{}) catch |err| switch (err) {1307 const file = fs.openFileAbsoluteZ("/etc/hosts", .{}) catch |err| switch (err) {
...@@ -1374,7 +1375,7 @@ fn linuxLookupNameFromDnsSearch(...@@ -1374,7 +1375,7 @@ fn linuxLookupNameFromDnsSearch(
1374 addrs: *std.ArrayList(LookupAddr),1375 addrs: *std.ArrayList(LookupAddr),
1375 canon: *std.ArrayList(u8),1376 canon: *std.ArrayList(u8),
1376 name: []const u8,1377 name: []const u8,
1377 family: os.sa_family_t,1378 family: posix.sa_family_t,
1378 port: u16,1379 port: u16,
1379) !void {1380) !void {
1380 var rc: ResolvConf = undefined;1381 var rc: ResolvConf = undefined;
...@@ -1429,7 +1430,7 @@ fn linuxLookupNameFromDns(...@@ -1429,7 +1430,7 @@ fn linuxLookupNameFromDns(
1429 addrs: *std.ArrayList(LookupAddr),1430 addrs: *std.ArrayList(LookupAddr),
1430 canon: *std.ArrayList(u8),1431 canon: *std.ArrayList(u8),
1431 name: []const u8,1432 name: []const u8,
1432 family: os.sa_family_t,1433 family: posix.sa_family_t,
1433 rc: ResolvConf,1434 rc: ResolvConf,
1434 port: u16,1435 port: u16,
1435) !void {1436) !void {
...@@ -1439,12 +1440,12 @@ fn linuxLookupNameFromDns(...@@ -1439,12 +1440,12 @@ fn linuxLookupNameFromDns(
1439 .port = port,1440 .port = port,
1440 };1441 };
1441 const AfRr = struct {1442 const AfRr = struct {
1442 af: os.sa_family_t,1443 af: posix.sa_family_t,
1443 rr: u8,1444 rr: u8,
1444 };1445 };
1445 const afrrs = [_]AfRr{1446 const afrrs = [_]AfRr{
1446 AfRr{ .af = os.AF.INET6, .rr = os.RR.A },1447 AfRr{ .af = posix.AF.INET6, .rr = posix.RR.A },
1447 AfRr{ .af = os.AF.INET, .rr = os.RR.AAAA },1448 AfRr{ .af = posix.AF.INET, .rr = posix.RR.AAAA },
1448 };1449 };
1449 var qbuf: [2][280]u8 = undefined;1450 var qbuf: [2][280]u8 = undefined;
1450 var abuf: [2][512]u8 = undefined;1451 var abuf: [2][512]u8 = undefined;
...@@ -1454,7 +1455,7 @@ fn linuxLookupNameFromDns(...@@ -1454,7 +1455,7 @@ fn linuxLookupNameFromDns(
14541455
1455 for (afrrs) |afrr| {1456 for (afrrs) |afrr| {
1456 if (family != afrr.af) {1457 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]);
1458 qp[nq] = qbuf[nq][0..len];1459 qp[nq] = qbuf[nq][0..len];
1459 nq += 1;1460 nq += 1;
1460 }1461 }
...@@ -1582,8 +1583,8 @@ fn resMSendRc(...@@ -1582,8 +1583,8 @@ fn resMSendRc(
1582 const timeout = 1000 * rc.timeout;1583 const timeout = 1000 * rc.timeout;
1583 const attempts = rc.attempts;1584 const attempts = rc.attempts;
15841585
1585 var sl: os.socklen_t = @sizeOf(os.sockaddr.in);1586 var sl: posix.socklen_t = @sizeOf(posix.sockaddr.in);
1586 var family: os.sa_family_t = os.AF.INET;1587 var family: posix.sa_family_t = posix.AF.INET;
15871588
1588 var ns_list = std.ArrayList(Address).init(rc.ns.allocator);1589 var ns_list = std.ArrayList(Address).init(rc.ns.allocator);
1589 defer ns_list.deinit();1590 defer ns_list.deinit();
...@@ -1594,18 +1595,18 @@ fn resMSendRc(...@@ -1594,18 +1595,18 @@ fn resMSendRc(
1594 for (rc.ns.items, 0..) |iplit, i| {1595 for (rc.ns.items, 0..) |iplit, i| {
1595 ns[i] = iplit.addr;1596 ns[i] = iplit.addr;
1596 assert(ns[i].getPort() == 53);1597 assert(ns[i].getPort() == 53);
1597 if (iplit.addr.any.family != os.AF.INET) {1598 if (iplit.addr.any.family != posix.AF.INET) {
1598 family = os.AF.INET6;1599 family = posix.AF.INET6;
1599 }1600 }
1600 }1601 }
16011602
1602 const flags = os.SOCK.DGRAM | os.SOCK.CLOEXEC | os.SOCK.NONBLOCK;1603 const flags = posix.SOCK.DGRAM | posix.SOCK.CLOEXEC | posix.SOCK.NONBLOCK;
1603 const fd = os.socket(family, flags, 0) catch |err| switch (err) {1604 const fd = posix.socket(family, flags, 0) catch |err| switch (err) {
1604 error.AddressFamilyNotSupported => blk: {1605 error.AddressFamilyNotSupported => blk: {
1605 // Handle case where system lacks IPv6 support1606 // Handle case where system lacks IPv6 support
1606 if (family == os.AF.INET6) {1607 if (family == posix.AF.INET6) {
1607 family = os.AF.INET;1608 family = posix.AF.INET;
1608 break :blk try os.socket(os.AF.INET, flags, 0);1609 break :blk try posix.socket(posix.AF.INET, flags, 0);
1609 }1610 }
1610 return err;1611 return err;
1611 },1612 },
...@@ -1618,33 +1619,33 @@ fn resMSendRc(...@@ -1618,33 +1619,33 @@ fn resMSendRc(
1618 // packet which is up to the caller to interpret.1619 // packet which is up to the caller to interpret.
16191620
1620 // Convert any IPv4 addresses in a mixed environment to v4-mapped1621 // Convert any IPv4 addresses in a mixed environment to v4-mapped
1621 if (family == os.AF.INET6) {1622 if (family == posix.AF.INET6) {
1622 try os.setsockopt(1623 try posix.setsockopt(
1623 fd,1624 fd,
1624 os.SOL.IPV6,1625 posix.SOL.IPV6,
1625 os.linux.IPV6.V6ONLY,1626 std.os.linux.IPV6.V6ONLY,
1626 &mem.toBytes(@as(c_int, 0)),1627 &mem.toBytes(@as(c_int, 0)),
1627 );1628 );
1628 for (0..ns.len) |i| {1629 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;
1630 mem.writeInt(u32, ns[i].in6.sa.addr[12..], ns[i].in.sa.addr, native_endian);1631 mem.writeInt(u32, ns[i].in6.sa.addr[12..], ns[i].in.sa.addr, native_endian);
1631 ns[i].in6.sa.addr[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*;1632 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;
1633 ns[i].in6.sa.flowinfo = 0;1634 ns[i].in6.sa.flowinfo = 0;
1634 ns[i].in6.sa.scope_id = 0;1635 ns[i].in6.sa.scope_id = 0;
1635 }1636 }
1636 sl = @sizeOf(os.sockaddr.in6);1637 sl = @sizeOf(posix.sockaddr.in6);
1637 }1638 }
16381639
1639 // Get local address and open/bind a socket1640 // Get local address and open/bind a socket
1640 var sa: Address = undefined;1641 var sa: Address = undefined;
1641 @memset(@as([*]u8, @ptrCast(&sa))[0..@sizeOf(Address)], 0);1642 @memset(@as([*]u8, @ptrCast(&sa))[0..@sizeOf(Address)], 0);
1642 sa.any.family = family;1643 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{
1646 .fd = fd,1647 .fd = fd,
1647 .events = os.POLL.IN,1648 .events = posix.POLL.IN,
1648 .revents = undefined,1649 .revents = undefined,
1649 }};1650 }};
1650 const retry_interval = timeout / attempts;1651 const retry_interval = timeout / attempts;
...@@ -1663,7 +1664,7 @@ fn resMSendRc(...@@ -1663,7 +1664,7 @@ fn resMSendRc(
1663 if (answers[i].len == 0) {1664 if (answers[i].len == 0) {
1664 var j: usize = 0;1665 var j: usize = 0;
1665 while (j < ns.len) : (j += 1) {1666 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;
1667 }1668 }
1668 }1669 }
1669 }1670 }
...@@ -1673,12 +1674,12 @@ fn resMSendRc(...@@ -1673,12 +1674,12 @@ fn resMSendRc(
16731674
1674 // Wait for a response, or until time to retry1675 // Wait for a response, or until time to retry
1675 const clamped_timeout = @min(@as(u31, std.math.maxInt(u31)), t1 + retry_interval - t2);1676 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;
1677 if (nevents == 0) continue;1678 if (nevents == 0) continue;
16781679
1679 while (true) {1680 while (true) {
1680 var sl_copy = sl;1681 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
1683 // Ignore non-identifiable packets1684 // Ignore non-identifiable packets
1684 if (rlen < 4) continue;1685 if (rlen < 4) continue;
...@@ -1704,7 +1705,7 @@ fn resMSendRc(...@@ -1704,7 +1705,7 @@ fn resMSendRc(
1704 0, 3 => {},1705 0, 3 => {},
1705 2 => if (servfail_retry != 0) {1706 2 => if (servfail_retry != 0) {
1706 servfail_retry -= 1;1707 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;
1708 },1709 },
1709 else => continue,1710 else => continue,
1710 }1711 }
...@@ -1758,24 +1759,24 @@ fn dnsParse(...@@ -1758,24 +1759,24 @@ fn dnsParse(
17581759
1759fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) !void {1760fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) !void {
1760 switch (rr) {1761 switch (rr) {
1761 os.RR.A => {1762 posix.RR.A => {
1762 if (data.len != 4) return error.InvalidDnsARecord;1763 if (data.len != 4) return error.InvalidDnsARecord;
1763 const new_addr = try ctx.addrs.addOne();1764 const new_addr = try ctx.addrs.addOne();
1764 new_addr.* = LookupAddr{1765 new_addr.* = LookupAddr{
1765 .addr = Address.initIp4(data[0..4].*, ctx.port),1766 .addr = Address.initIp4(data[0..4].*, ctx.port),
1766 };1767 };
1767 },1768 },
1768 os.RR.AAAA => {1769 posix.RR.AAAA => {
1769 if (data.len != 16) return error.InvalidDnsAAAARecord;1770 if (data.len != 16) return error.InvalidDnsAAAARecord;
1770 const new_addr = try ctx.addrs.addOne();1771 const new_addr = try ctx.addrs.addOne();
1771 new_addr.* = LookupAddr{1772 new_addr.* = LookupAddr{
1772 .addr = Address.initIp6(data[0..16].*, ctx.port, 0, 0),1773 .addr = Address.initIp6(data[0..16].*, ctx.port, 0, 0),
1773 };1774 };
1774 },1775 },
1775 os.RR.CNAME => {1776 posix.RR.CNAME => {
1776 var tmp: [256]u8 = undefined;1777 var tmp: [256]u8 = undefined;
1777 // Returns len of compressed name. strlen to get canon name.1778 // 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);
1779 const canon_name = mem.sliceTo(&tmp, 0);1780 const canon_name = mem.sliceTo(&tmp, 0);
1780 if (isValidHostName(canon_name)) {1781 if (isValidHostName(canon_name)) {
1781 ctx.canon.items.len = 0;1782 ctx.canon.items.len = 0;
...@@ -1792,14 +1793,14 @@ pub const Stream = struct {...@@ -1792,14 +1793,14 @@ pub const Stream = struct {
1792 handle: posix.socket_t,1793 handle: posix.socket_t,
17931794
1794 pub fn close(s: Stream) void {1795 pub fn close(s: Stream) void {
1795 switch (builtin.os.tag) {1796 switch (native_os) {
1796 .windows => std.os.windows.closesocket(s.handle) catch unreachable,1797 .windows => windows.closesocket(s.handle) catch unreachable,
1797 else => posix.close(s.handle),1798 else => posix.close(s.handle),
1798 }1799 }
1799 }1800 }
18001801
1801 pub const ReadError = os.ReadError;1802 pub const ReadError = posix.ReadError;
1802 pub const WriteError = os.WriteError;1803 pub const WriteError = posix.WriteError;
18031804
1804 pub const Reader = io.Reader(Stream, ReadError, read);1805 pub const Reader = io.Reader(Stream, ReadError, read);
1805 pub const Writer = io.Writer(Stream, WriteError, write);1806 pub const Writer = io.Writer(Stream, WriteError, write);
...@@ -1813,22 +1814,22 @@ pub const Stream = struct {...@@ -1813,22 +1814,22 @@ pub const Stream = struct {
1813 }1814 }
18141815
1815 pub fn read(self: Stream, buffer: []u8) ReadError!usize {1816 pub fn read(self: Stream, buffer: []u8) ReadError!usize {
1816 if (builtin.os.tag == .windows) {1817 if (native_os == .windows) {
1817 return os.windows.ReadFile(self.handle, buffer, null);1818 return windows.ReadFile(self.handle, buffer, null);
1818 }1819 }
18191820
1820 return os.read(self.handle, buffer);1821 return posix.read(self.handle, buffer);
1821 }1822 }
18221823
1823 pub fn readv(s: Stream, iovecs: []const os.iovec) ReadError!usize {1824 pub fn readv(s: Stream, iovecs: []const posix.iovec) ReadError!usize {
1824 if (builtin.os.tag == .windows) {1825 if (native_os == .windows) {
1825 // TODO improve this to use ReadFileScatter1826 // TODO improve this to use ReadFileScatter
1826 if (iovecs.len == 0) return @as(usize, 0);1827 if (iovecs.len == 0) return @as(usize, 0);
1827 const first = iovecs[0];1828 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);
1829 }1830 }
18301831
1831 return os.readv(s.handle, iovecs);1832 return posix.readv(s.handle, iovecs);
1832 }1833 }
18331834
1834 /// Returns the number of bytes read. If the number read is smaller than1835 /// Returns the number of bytes read. If the number read is smaller than
...@@ -1858,11 +1859,11 @@ pub const Stream = struct {...@@ -1858,11 +1859,11 @@ pub const Stream = struct {
1858 /// file system thread instead of non-blocking. It needs to be reworked to properly1859 /// file system thread instead of non-blocking. It needs to be reworked to properly
1859 /// use non-blocking I/O.1860 /// use non-blocking I/O.
1860 pub fn write(self: Stream, buffer: []const u8) WriteError!usize {1861 pub fn write(self: Stream, buffer: []const u8) WriteError!usize {
1861 if (builtin.os.tag == .windows) {1862 if (native_os == .windows) {
1862 return os.windows.WriteFile(self.handle, buffer, null);1863 return windows.WriteFile(self.handle, buffer, null);
1863 }1864 }
18641865
1865 return os.write(self.handle, buffer);1866 return posix.write(self.handle, buffer);
1866 }1867 }
18671868
1868 pub fn writeAll(self: Stream, bytes: []const u8) WriteError!void {1869 pub fn writeAll(self: Stream, bytes: []const u8) WriteError!void {
...@@ -1874,15 +1875,15 @@ pub const Stream = struct {...@@ -1874,15 +1875,15 @@ pub const Stream = struct {
18741875
1875 /// See https://github.com/ziglang/zig/issues/76991876 /// See https://github.com/ziglang/zig/issues/7699
1876 /// See equivalent function: `std.fs.File.writev`.1877 /// See equivalent function: `std.fs.File.writev`.
1877 pub fn writev(self: Stream, iovecs: []const os.iovec_const) WriteError!usize {1878 pub fn writev(self: Stream, iovecs: []const posix.iovec_const) WriteError!usize {
1878 return os.writev(self.handle, iovecs);1879 return posix.writev(self.handle, iovecs);
1879 }1880 }
18801881
1881 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in1882 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
1882 /// order to handle partial writes from the underlying OS layer.1883 /// order to handle partial writes from the underlying OS layer.
1883 /// See https://github.com/ziglang/zig/issues/76991884 /// See https://github.com/ziglang/zig/issues/7699
1884 /// See equivalent function: `std.fs.File.writevAll`.1885 /// 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 {
1886 if (iovecs.len == 0) return;1887 if (iovecs.len == 0) return;
18871888
1888 var i: usize = 0;1889 var i: usize = 0;
lib/std/os.zig+183-7607
...@@ -11,8 +11,6 @@...@@ -11,8 +11,6 @@
11//! On Linux libc can be side-stepped by using `std.os.linux` directly.11//! On Linux libc can be side-stepped by using `std.os.linux` directly.
12//! * For Windows, this file represents the API that libc would provide for12//! * For Windows, this file represents the API that libc would provide for
13//! Windows. For thin wrappers around Windows-specific APIs, see `std.os.windows`.13//! 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
17const root = @import("root");15const root = @import("root");
18const std = @import("std.zig");16const std = @import("std.zig");
...@@ -24,15 +22,8 @@ const elf = std.elf;...@@ -24,15 +22,8 @@ const elf = std.elf;
24const fs = std.fs;22const fs = std.fs;
25const dl = @import("dynamic_library.zig");23const dl = @import("dynamic_library.zig");
26const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES;24const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES;
25const posix = std.posix;
2726
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;
36pub const linux = @import("os/linux.zig");27pub const linux = @import("os/linux.zig");
37pub const plan9 = @import("os/plan9.zig");28pub const plan9 = @import("os/plan9.zig");
38pub const uefi = @import("os/uefi.zig");29pub const uefi = @import("os/uefi.zig");
...@@ -40,214 +31,15 @@ pub const wasi = @import("os/wasi.zig");...@@ -40,214 +31,15 @@ pub const wasi = @import("os/wasi.zig");
40pub const emscripten = @import("os/emscripten.zig");31pub const emscripten = @import("os/emscripten.zig");
41pub const windows = @import("os/windows.zig");32pub const windows = @import("os/windows.zig");
4233
43comptime {
44 assert(@import("std") == std); // std lib tests require --zig-lib-dir
45}
46
47test {34test {
48 _ = darwin;
49 _ = linux;35 _ = linux;
50 if (builtin.os.tag == .uefi) {36 if (builtin.os.tag == .uefi) {
51 _ = uefi;37 _ = uefi;
52 }38 }
53 _ = wasi;39 _ = wasi;
54 _ = windows;40 _ = windows;
55
56 _ = @import("os/test.zig");
57}41}
5842
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
251/// See also `getenv`. Populated by startup code before main().43/// See also `getenv`. Populated by startup code before main().
252/// TODO this is a footgun because the value will be undefined when using `zig build-lib`.44/// TODO this is a footgun because the value will be undefined when using `zig build-lib`.
253/// https://github.com/ziglang/zig/issues/452445/// https://github.com/ziglang/zig/issues/4524
...@@ -262,7433 +54,217 @@ pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (builtin....@@ -262,7433 +54,217 @@ pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (builtin.
262 else => undefined,54 else => undefined,
263};55};
26456
265pub const have_sigpipe_support = @hasDecl(@This(), "SIG") and @hasDecl(SIG, "PIPE");57/// Call from Windows-specific code if you already have a WTF-16LE encoded, null terminated string.
26658/// Otherwise use `access` or `accessZ`.
267fn noopSigHandler(_: c_int) callconv(.C) void {}59pub fn accessW(path: [*:0]const u16) windows.GetFileAttributesError!void {
26860 const ret = try windows.GetFileAttributesW(path);
269/// On default executed by posix startup code before main(), if SIGPIPE is supported.61 if (ret != windows.INVALID_FILE_ATTRIBUTES) {
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);
303 return;62 return;
304 }63 }
305 if (builtin.target.isDarwin()) {64 switch (windows.kernel32.GetLastError()) {
306 // This avoids the EINTR problem.65 .FILE_NOT_FOUND => return error.FileNotFound,
307 switch (darwin.getErrno(darwin.@"close$NOCANCEL"(fd))) {66 .PATH_NOT_FOUND => return error.FileNotFound,
308 .BADF => unreachable, // Always a race condition.67 .ACCESS_DENIED => return error.PermissionDenied,
309 else => return,68 else => |err| return windows.unexpectedError(err),
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,
316 }69 }
317}70}
31871
319pub const FChmodError = error{72pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool {
320 AccessDenied,73 return switch (os.tag) {
321 InputOutput,74 .windows,
322 SymLinkLoop,75 .macos,
323 FileNotFound,76 .ios,
324 SystemResources,77 .watchos,
325 ReadOnlyFileSystem,78 .tvos,
326} || UnexpectedError;79 .linux,
32780 .solaris,
328/// Changes the mode of the file referred to by the file descriptor.81 .illumos,
329/// The process must have the correct privileges in order to do this82 .freebsd,
330/// successfully, or must have the effective user ID matching the owner83 => true,
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);
33784
338 switch (system.getErrno(res)) {85 .dragonfly => os.version_range.semver.max.order(.{ .major = 6, .minor = 0, .patch = 0 }) != .lt,
339 .SUCCESS => return,86 .netbsd => os.version_range.semver.max.order(.{ .major = 10, .minor = 0, .patch = 0 }) != .lt,
340 .INTR => continue,87 else => false,
341 .BADF => unreachable,88 };
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 }
355}89}
35690
357const FChmodAtError = FChmodError || error{91/// Return canonical path of handle `fd`.
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.
381///92///
382/// On Linux the `fchmodat2` syscall will be used if available, otherwise a93/// This function is very host-specific and is not universally supported by all hosts.
383/// workaround using procfs will be employed. Changing the mode of a symbolic94/// For example, while it generally works on Linux, macOS, FreeBSD or Windows, it is
384/// link with `AT.SYMLINK_NOFOLLOW` set will also return95/// unsupported on WASI.
385/// `OperationNotSupported`, as:
386///96///
387/// 1. Permissions on the link are ignored when resolving its target.97/// * On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
388/// 2. This operation has been known to invoke undefined behaviour across98/// * On other platforms, the result is an opaque sequence of bytes with no particular encoding.
389/// different filesystems[1].
390///99///
391/// [1]: https://sourceware.org/legacy-ml/libc-alpha/2020-02/msg00467.html.100/// Calling this function is usually a bug.
392pub inline fn fchmodat(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void {101pub fn getFdPath(fd: std.posix.fd_t, out_buffer: *[MAX_PATH_BYTES]u8) std.posix.RealPathError![]u8 {
393 if (!std.fs.has_executable_bit) @compileError("fchmodat unsupported by target OS");102 if (!comptime isGetFdPathSupportedOnTarget(builtin.os)) {
394103 @compileError("querying for canonical path of a handle is unsupported on this host");
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 }
529 }104 }
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 {
547 switch (builtin.os.tag) {105 switch (builtin.os.tag) {
548 .windows, .wasi => @compileError("Unsupported OS"),106 .windows => {
549 else => {},107 var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
550 }108 const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]);
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};
592109
593pub fn reboot(cmd: RebootCommand) RebootError!void {110 const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
594 switch (builtin.os.tag) {111 return out_buffer[0..end_index];
595 .linux => {112 },
596 switch (system.getErrno(linux.reboot(113 .macos, .ios, .watchos, .tvos => {
597 .MAGIC1,114 // On macOS, we can use F.GETPATH fcntl command to query the OS for
598 .MAGIC2,115 // the path to the file descriptor.
599 cmd,116 @memset(out_buffer[0..MAX_PATH_BYTES], 0);
600 switch (cmd) {117 switch (posix.errno(posix.system.fcntl(fd, posix.F.GETPATH, out_buffer))) {
601 .RESTART2 => |s| s,
602 else => null,
603 },
604 ))) {
605 .SUCCESS => {},118 .SUCCESS => {},
606 .PERM => return error.PermissionDenied,119 .BADF => return error.FileNotFound,
607 else => |err| return std.os.unexpectedErrno(err),120 .NOSPC => return error.NameTooLong,
608 }121 // TODO man pages for fcntl on macOS don't really tell you what
609 switch (cmd) {122 // errno values to expect when command is F.GETPATH...
610 .CAD_OFF => {},123 else => |err| return posix.unexpectedErrno(err),
611 .CAD_ON => {},
612 .SW_SUSPEND => {},
613
614 .HALT => unreachable,
615 .KEXEC => unreachable,
616 .POWER_OFF => unreachable,
617 .RESTART => unreachable,
618 .RESTART2 => unreachable,
619 }124 }
125 const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse MAX_PATH_BYTES;
126 return out_buffer[0..len];
620 },127 },
621 else => @compileError("Unsupported OS"),128 .linux => {
622 }129 var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
623}130 const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/fd/{d}", .{fd}) catch unreachable;
624
625pub const GetRandomError = OpenError;
626131
627/// Obtain a series of random bytes. These bytes can be used to seed user-space132 const target = posix.readlinkZ(proc_path, out_buffer) catch |err| {
628/// random number generators or for cryptographic purposes.133 switch (err) {
629/// When linking against libc, this calls the134 error.NotLink => unreachable,
630/// appropriate OS-specific library call. Otherwise it uses the zig standard135 error.BadPathName => unreachable,
631/// library implementation.136 error.InvalidUtf8 => unreachable, // WASI-only
632pub fn getrandom(buffer: []u8) GetRandomError!void {137 error.InvalidWtf8 => unreachable, // Windows-only
633 if (builtin.os.tag == .windows) {138 error.UnsupportedReparsePointType => unreachable, // Windows-only
634 return windows.RtlGenRandom(buffer);139 error.NetworkNotFound => unreachable, // Windows-only
635 }140 else => |e| return e,
636 if (builtin.os.tag == .linux or builtin.os.tag == .freebsd) {141 }
637 var buf = buffer;142 };
638 const use_c = builtin.os.tag != .linux or143 return target;
639 std.c.versionCheck(std.SemanticVersion{ .major = 2, .minor = 25, .patch = 0 });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) {149 const target = posix.readlinkZ(proc_path, out_buffer) catch |err| switch (err) {
642 const num_read: usize, const err = if (use_c) res: {150 error.UnsupportedReparsePointType => unreachable,
643 const rc = std.c.getrandom(buf.ptr, buf.len, 0);151 error.NotLink => unreachable,
644 break :res .{152 else => |e| return e,
645 @bitCast(rc),153 };
646 std.c.getErrno(rc),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,
647 };182 };
648 } else res: {183 len = len * 4 / 3;
649 const rc = linux.getrandom(buf.ptr, buf.len, 0);184 const buf = std.heap.c_allocator.alloc(u8, len) catch return error.SystemResources;
650 break :res .{185 defer std.heap.c_allocator.free(buf);
651 rc,186 len = buf.len;
652 linux.getErrno(rc),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,
653 };193 };
654 };194 var i: usize = 0;
655195 while (i < len) {
656 switch (err) {196 const kf: *align(1) std.c.kinfo_file = @ptrCast(&buf[i]);
657 .SUCCESS => buf = buf[num_read..],197 if (kf.fd == fd) {
658 .INVAL => unreachable,198 len = mem.indexOfScalar(u8, &kf.path, 0) orelse MAX_PATH_BYTES;
659 .FAULT => unreachable,199 if (len == 0) return error.NameTooLong;
660 .INTR => continue,200 const result = out_buffer[0..len];
661 .NOSYS => return getRandomBytesDevURandom(buf),201 @memcpy(result, kf.path[0..len]);
662 else => return unexpectedErrno(err),202 return result;
203 }
204 i += @intCast(kf.structsize);
205 }
206 return error.FileNotFound;
663 }207 }
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;
678 },208 },
679 .wasi => switch (wasi.random_get(buffer.ptr, buffer.len)) {209 .dragonfly => {
680 .SUCCESS => return,210 @memset(out_buffer[0..MAX_PATH_BYTES], 0);
681 else => |err| return unexpectedErrno(err),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];
682 },233 },
683 else => return getRandomBytesDevURandom(buffer),234 else => unreachable, // made unreachable by isGetFdPathSupportedOnTarget above
684 }235 }
685}236}
686237
687fn getRandomBytesDevURandom(buf: []u8) !void {238/// WASI-only. Same as `fstatat` but targeting WASI.
688 const fd = try openZ("/dev/urandom", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);239/// `pathname` should be encoded as valid UTF-8.
689 defer close(fd);240/// See also `fstatat`.
690241pub fn fstatat_wasi(dirfd: posix.fd_t, pathname: []const u8, flags: wasi.lookupflags_t) posix.FStatAtError!wasi.filestat_t {
691 const st = try fstat(fd);242 var stat: wasi.filestat_t = undefined;
692 if (!S.ISCHR(st.mode)) {243 switch (wasi.path_filestat_get(dirfd, flags, pathname.ptr, pathname.len, &stat)) {
693 return error.NoDevice;244 .SUCCESS => return stat,
245 .INVAL => unreachable,
246 .BADF => unreachable, // Always a race condition.
247 .NOMEM => return error.SystemResources,
248 .ACCES => return error.AccessDenied,
249 .FAULT => unreachable,
250 .NAMETOOLONG => return error.NameTooLong,
251 .NOENT => return error.FileNotFound,
252 .NOTDIR => return error.FileNotFound,
253 .NOTCAPABLE => return error.AccessDenied,
254 .ILSEQ => return error.InvalidUtf8,
255 else => |err| return posix.unexpectedErrno(err),
694 }256 }
695
696 const file = std.fs.File{ .handle = fd };
697 const stream = file.reader();
698 stream.readNoEof(buf) catch return error.Unexpected;
699}257}
700258
701/// Causes abnormal process termination.259pub fn fstat_wasi(fd: posix.fd_t) posix.FStatError!wasi.filestat_t {
702/// If linking against libc, this calls the abort() libc function. Otherwise260 var stat: wasi.filestat_t = undefined;
703/// it raises SIGABRT followed by SIGKILL and finally lo261 switch (wasi.fd_filestat_get(fd, &stat)) {
704/// Invokes the current signal handler for SIGABRT, if any.262 .SUCCESS => return stat,
705pub fn abort() noreturn {263 .INVAL => unreachable,
706 @setCold(true);264 .BADF => unreachable, // Always a race condition.
707 // MSVCRT abort() sometimes opens a popup window which is undesirable, so265 .NOMEM => return error.SystemResources,
708 // even when linking libc on Windows we use our own abort implementation.266 .ACCES => return error.AccessDenied,
709 // See https://github.com/ziglang/zig/issues/2071 for more details.267 .NOTCAPABLE => return error.AccessDenied,
710 if (builtin.os.tag == .windows) {268 else => |err| return posix.unexpectedErrno(err),
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 }269 }
759}270}
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);
825 }
826 system.exit(status);
827}
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 @@...@@ -1,8 +1,8 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const wasi = std.os.wasi;3const wasi = std.os.wasi;
4const iovec = std.os.iovec;4const iovec = std.posix.iovec;
5const iovec_const = std.os.iovec_const;5const iovec_const = std.posix.iovec_const;
6const c = std.c;6const c = std.c;
77
8pub const FILE = c.FILE;8pub const FILE = c.FILE;
lib/std/os/linux.zig+610-13
...@@ -18,9 +18,9 @@ const is_mips = native_arch.isMIPS();...@@ -18,9 +18,9 @@ const is_mips = native_arch.isMIPS();
18const is_ppc = native_arch.isPPC();18const is_ppc = native_arch.isPPC();
19const is_ppc64 = native_arch.isPPC64();19const is_ppc64 = native_arch.isPPC64();
20const is_sparc = native_arch.isSPARC();20const is_sparc = native_arch.isSPARC();
21const iovec = std.os.iovec;21const iovec = std.posix.iovec;
22const iovec_const = std.os.iovec_const;22const iovec_const = std.posix.iovec_const;
23const ACCMODE = std.os.ACCMODE;23const ACCMODE = std.posix.ACCMODE;
2424
25test {25test {
26 if (builtin.os.tag == .linux) {26 if (builtin.os.tag == .linux) {
...@@ -451,10 +451,11 @@ fn splitValue64(val: i64) [2]u32 {...@@ -451,10 +451,11 @@ fn splitValue64(val: i64) [2]u32 {
451}451}
452452
453/// Get the errno from a syscall return value, or 0 for no error.453/// Get the errno from a syscall return value, or 0 for no error.
454pub fn getErrno(r: usize) E {454/// The public API is exposed via the `E` namespace.
455 const signed_r = @as(isize, @bitCast(r));455fn errnoFromSyscall(r: usize) E {
456 const signed_r: isize = @bitCast(r);
456 const int = if (signed_r > -4096 and signed_r < 0) -signed_r else 0;457 const int = if (signed_r > -4096 and signed_r < 0) -signed_r else 0;
457 return @as(E, @enumFromInt(int));458 return @enumFromInt(int);
458}459}
459460
460pub fn dup(old: i32) usize {461pub fn dup(old: i32) usize {
...@@ -1561,7 +1562,7 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact...@@ -1561,7 +1562,7 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact
1561 .sparc, .sparc64 => syscall5(.rt_sigaction, sig, ksa_arg, oldksa_arg, @intFromPtr(ksa.restorer), mask_size),1562 .sparc, .sparc64 => syscall5(.rt_sigaction, sig, ksa_arg, oldksa_arg, @intFromPtr(ksa.restorer), mask_size),
1562 else => syscall4(.rt_sigaction, sig, ksa_arg, oldksa_arg, mask_size),1563 else => syscall4(.rt_sigaction, sig, ksa_arg, oldksa_arg, mask_size),
1563 };1564 };
1564 if (getErrno(result) != .SUCCESS) return result;1565 if (E.init(result) != .SUCCESS) return result;
15651566
1566 if (oact) |old| {1567 if (oact) |old| {
1567 old.handler.handler = oldksa.handler;1568 old.handler.handler = oldksa.handler;
...@@ -1648,12 +1649,12 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize...@@ -1648,12 +1649,12 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize
1648 if (next_unsent < i) {1649 if (next_unsent < i) {
1649 const batch_size = i - next_unsent;1650 const batch_size = i - next_unsent;
1650 const r = syscall4(.sendmmsg, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(&msgvec[next_unsent]), batch_size, flags);1651 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;
1652 if (r < batch_size) return next_unsent + r;1653 if (r < batch_size) return next_unsent + r;
1653 }1654 }
1654 // send current message as own packet1655 // send current message as own packet
1655 const r = sendmsg(fd, &msg.msg_hdr, flags);1656 const r = sendmsg(fd, &msg.msg_hdr, flags);
1656 if (getErrno(r) != 0) return r;1657 if (E.init(r) != 0) return r;
1657 // Linux limits the total bytes sent by sendmsg to INT_MAX, so this cast is safe.1658 // Linux limits the total bytes sent by sendmsg to INT_MAX, so this cast is safe.
1658 msg.msg_len = @as(u32, @intCast(r));1659 msg.msg_len = @as(u32, @intCast(r));
1659 next_unsent = i + 1;1660 next_unsent = i + 1;
...@@ -1665,7 +1666,7 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize...@@ -1665,7 +1666,7 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize
1665 if (next_unsent < kvlen or next_unsent == 0) { // want to make sure at least one syscall occurs (e.g. to trigger MSG.EOR)1666 if (next_unsent < kvlen or next_unsent == 0) { // want to make sure at least one syscall occurs (e.g. to trigger MSG.EOR)
1666 const batch_size = kvlen - next_unsent;1667 const batch_size = kvlen - next_unsent;
1667 const r = syscall4(.sendmmsg, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(&msgvec[next_unsent]), batch_size, flags);1668 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;
1669 return next_unsent + r;1670 return next_unsent + r;
1670 }1671 }
1671 return kvlen;1672 return kvlen;
...@@ -2263,13 +2264,609 @@ pub fn map_shadow_stack(addr: u64, size: u64, flags: u32) usize {...@@ -2263,13 +2264,609 @@ pub fn map_shadow_stack(addr: u64, size: u64, flags: u32) usize {
2263}2264}
22642265
2265pub const E = switch (native_arch) {2266pub const E = switch (native_arch) {
2266 .mips, .mipsel => @import("linux/errno/mips.zig").E,2267 .mips, .mipsel => enum(i32) {
2267 .sparc, .sparcel, .sparc64 => @import("linux/errno/sparc.zig").E,2268 /// No error occurred.
2268 else => @import("linux/errno/generic.zig").E,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 },
2269};2865};
22702866
2271pub const pid_t = i32;2867pub const pid_t = i32;
2272pub const fd_t = i32;2868pub const fd_t = i32;
2869pub const socket_t = i32;
2273pub const uid_t = u32;2870pub const uid_t = u32;
2274pub const gid_t = u32;2871pub const gid_t = u32;
2275pub const clock_t = isize;2872pub const clock_t = isize;
lib/std/os/linux/IoUring.zig+251-251
...@@ -4,12 +4,12 @@ const builtin = @import("builtin");...@@ -4,12 +4,12 @@ const builtin = @import("builtin");
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const mem = std.mem;5const mem = std.mem;
6const net = std.net;6const net = std.net;
7const os = std.os;
8const posix = std.posix;7const posix = std.posix;
9const linux = os.linux;8const linux = std.os.linux;
10const testing = std.testing;9const testing = std.testing;
10const is_linux = builtin.os.tag == .linux;
1111
12fd: os.fd_t = -1,12fd: posix.fd_t = -1,
13sq: SubmissionQueue,13sq: SubmissionQueue,
14cq: CompletionQueue,14cq: CompletionQueue,
15flags: u32,15flags: u32,
...@@ -45,7 +45,7 @@ pub fn init_params(entries: u16, p: *linux.io_uring_params) !IoUring {...@@ -45,7 +45,7 @@ pub fn init_params(entries: u16, p: *linux.io_uring_params) !IoUring {
45 assert(p.resv[2] == 0);45 assert(p.resv[2] == 0);
4646
47 const res = linux.io_uring_setup(entries, p);47 const res = linux.io_uring_setup(entries, p);
48 switch (linux.getErrno(res)) {48 switch (linux.E.init(res)) {
49 .SUCCESS => {},49 .SUCCESS => {},
50 .FAULT => return error.ParamsOutsideAccessibleAddressSpace,50 .FAULT => return error.ParamsOutsideAccessibleAddressSpace,
51 // The resv array contains non-zero data, p.flags contains an unsupported flag,51 // 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 {...@@ -59,11 +59,11 @@ pub fn init_params(entries: u16, p: *linux.io_uring_params) !IoUring {
59 // or a container seccomp policy prohibits io_uring syscalls:59 // or a container seccomp policy prohibits io_uring syscalls:
60 .PERM => return error.PermissionDenied,60 .PERM => return error.PermissionDenied,
61 .NOSYS => return error.SystemOutdated,61 .NOSYS => return error.SystemOutdated,
62 else => |errno| return os.unexpectedErrno(errno),62 else => |errno| return posix.unexpectedErrno(errno),
63 }63 }
64 const fd = @as(os.fd_t, @intCast(res));64 const fd = @as(posix.fd_t, @intCast(res));
65 assert(fd >= 0);65 assert(fd >= 0);
66 errdefer os.close(fd);66 errdefer posix.close(fd);
6767
68 // Kernel versions 5.4 and up use only one mmap() for the submission and completion queues.68 // Kernel versions 5.4 and up use only one mmap() for the submission and completion queues.
69 // This is not an optional feature for us... if the kernel does it, we have to do it.69 // 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 {...@@ -121,7 +121,7 @@ pub fn deinit(self: *IoUring) void {
121 // The mmaps depend on the fd, so the order of these calls is important:121 // The mmaps depend on the fd, so the order of these calls is important:
122 self.cq.deinit();122 self.cq.deinit();
123 self.sq.deinit();123 self.sq.deinit();
124 os.close(self.fd);124 posix.close(self.fd);
125 self.fd = -1;125 self.fd = -1;
126}126}
127127
...@@ -174,7 +174,7 @@ pub fn submit_and_wait(self: *IoUring, wait_nr: u32) !u32 {...@@ -174,7 +174,7 @@ pub fn submit_and_wait(self: *IoUring, wait_nr: u32) !u32 {
174pub fn enter(self: *IoUring, to_submit: u32, min_complete: u32, flags: u32) !u32 {174pub fn enter(self: *IoUring, to_submit: u32, min_complete: u32, flags: u32) !u32 {
175 assert(self.fd >= 0);175 assert(self.fd >= 0);
176 const res = linux.io_uring_enter(self.fd, to_submit, min_complete, flags, null);176 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)) {
178 .SUCCESS => {},178 .SUCCESS => {},
179 // The kernel was unable to allocate memory or ran out of resources for the request.179 // The kernel was unable to allocate memory or ran out of resources for the request.
180 // The application should wait for some completions and try again:180 // 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...@@ -200,7 +200,7 @@ pub fn enter(self: *IoUring, to_submit: u32, min_complete: u32, flags: u32) !u32
200 // The operation was interrupted by a delivery of a signal before it could complete.200 // The operation was interrupted by a delivery of a signal before it could complete.
201 // This can happen while waiting for events with IORING_ENTER_GETEVENTS:201 // This can happen while waiting for events with IORING_ENTER_GETEVENTS:
202 .INTR => return error.SignalInterrupt,202 .INTR => return error.SignalInterrupt,
203 else => |errno| return os.unexpectedErrno(errno),203 else => |errno| return posix.unexpectedErrno(errno),
204 }204 }
205 return @as(u32, @intCast(res));205 return @as(u32, @intCast(res));
206}206}
...@@ -344,7 +344,7 @@ pub fn cq_advance(self: *IoUring, count: u32) void {...@@ -344,7 +344,7 @@ pub fn cq_advance(self: *IoUring, count: u32) void {
344/// apply to the write, since the fsync may complete before the write is issued to the disk.344/// apply to the write, since the fsync may complete before the write is issued to the disk.
345/// You should preferably use `link_with_next_sqe()` on a write's SQE to link it with an fsync,345/// You should preferably use `link_with_next_sqe()` on a write's SQE to link it with an fsync,
346/// or else insert a full write barrier using `drain_previous_sqes()` when queueing an fsync.346/// 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 {
348 const sqe = try self.get_sqe();348 const sqe = try self.get_sqe();
349 sqe.prep_fsync(fd, flags);349 sqe.prep_fsync(fd, flags);
350 sqe.user_data = user_data;350 sqe.user_data = user_data;
...@@ -369,7 +369,7 @@ pub const ReadBuffer = union(enum) {...@@ -369,7 +369,7 @@ pub const ReadBuffer = union(enum) {
369 buffer: []u8,369 buffer: []u8,
370370
371 /// io_uring will read directly into these buffers using readv.371 /// io_uring will read directly into these buffers using readv.
372 iovecs: []const os.iovec,372 iovecs: []const posix.iovec,
373373
374 /// io_uring will select a buffer that has previously been provided with `provide_buffers`.374 /// io_uring will select a buffer that has previously been provided with `provide_buffers`.
375 /// The buffer group reference by `group_id` must contain at least one buffer for the read to work.375 /// 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) {...@@ -389,7 +389,7 @@ pub const ReadBuffer = union(enum) {
389pub fn read(389pub fn read(
390 self: *IoUring,390 self: *IoUring,
391 user_data: u64,391 user_data: u64,
392 fd: os.fd_t,392 fd: posix.fd_t,
393 buffer: ReadBuffer,393 buffer: ReadBuffer,
394 offset: u64,394 offset: u64,
395) !*linux.io_uring_sqe {395) !*linux.io_uring_sqe {
...@@ -412,7 +412,7 @@ pub fn read(...@@ -412,7 +412,7 @@ pub fn read(
412pub fn write(412pub fn write(
413 self: *IoUring,413 self: *IoUring,
414 user_data: u64,414 user_data: u64,
415 fd: os.fd_t,415 fd: posix.fd_t,
416 buffer: []const u8,416 buffer: []const u8,
417 offset: u64,417 offset: u64,
418) !*linux.io_uring_sqe {418) !*linux.io_uring_sqe {
...@@ -436,7 +436,7 @@ pub fn write(...@@ -436,7 +436,7 @@ pub fn write(
436/// See https://github.com/axboe/liburing/issues/291436/// See https://github.com/axboe/liburing/issues/291
437///437///
438/// Returns a pointer to the SQE so that you can further modify the SQE for advanced use cases.438/// 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 {
440 const sqe = try self.get_sqe();440 const sqe = try self.get_sqe();
441 sqe.prep_splice(fd_in, off_in, fd_out, off_out, len);441 sqe.prep_splice(fd_in, off_in, fd_out, off_out, len);
442 sqe.user_data = user_data;442 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...@@ -451,8 +451,8 @@ pub fn splice(self: *IoUring, user_data: u64, fd_in: os.fd_t, off_in: u64, fd_ou
451pub fn read_fixed(451pub fn read_fixed(
452 self: *IoUring,452 self: *IoUring,
453 user_data: u64,453 user_data: u64,
454 fd: os.fd_t,454 fd: posix.fd_t,
455 buffer: *os.iovec,455 buffer: *posix.iovec,
456 offset: u64,456 offset: u64,
457 buffer_index: u16,457 buffer_index: u16,
458) !*linux.io_uring_sqe {458) !*linux.io_uring_sqe {
...@@ -469,8 +469,8 @@ pub fn read_fixed(...@@ -469,8 +469,8 @@ pub fn read_fixed(
469pub fn writev(469pub fn writev(
470 self: *IoUring,470 self: *IoUring,
471 user_data: u64,471 user_data: u64,
472 fd: os.fd_t,472 fd: posix.fd_t,
473 iovecs: []const os.iovec_const,473 iovecs: []const posix.iovec_const,
474 offset: u64,474 offset: u64,
475) !*linux.io_uring_sqe {475) !*linux.io_uring_sqe {
476 const sqe = try self.get_sqe();476 const sqe = try self.get_sqe();
...@@ -487,8 +487,8 @@ pub fn writev(...@@ -487,8 +487,8 @@ pub fn writev(
487pub fn write_fixed(487pub fn write_fixed(
488 self: *IoUring,488 self: *IoUring,
489 user_data: u64,489 user_data: u64,
490 fd: os.fd_t,490 fd: posix.fd_t,
491 buffer: *os.iovec,491 buffer: *posix.iovec,
492 offset: u64,492 offset: u64,
493 buffer_index: u16,493 buffer_index: u16,
494) !*linux.io_uring_sqe {494) !*linux.io_uring_sqe {
...@@ -504,9 +504,9 @@ pub fn write_fixed(...@@ -504,9 +504,9 @@ pub fn write_fixed(
504pub fn accept(504pub fn accept(
505 self: *IoUring,505 self: *IoUring,
506 user_data: u64,506 user_data: u64,
507 fd: os.fd_t,507 fd: posix.fd_t,
508 addr: ?*os.sockaddr,508 addr: ?*posix.sockaddr,
509 addrlen: ?*os.socklen_t,509 addrlen: ?*posix.socklen_t,
510 flags: u32,510 flags: u32,
511) !*linux.io_uring_sqe {511) !*linux.io_uring_sqe {
512 const sqe = try self.get_sqe();512 const sqe = try self.get_sqe();
...@@ -526,9 +526,9 @@ pub fn accept(...@@ -526,9 +526,9 @@ pub fn accept(
526pub fn accept_multishot(526pub fn accept_multishot(
527 self: *IoUring,527 self: *IoUring,
528 user_data: u64,528 user_data: u64,
529 fd: os.fd_t,529 fd: posix.fd_t,
530 addr: ?*os.sockaddr,530 addr: ?*posix.sockaddr,
531 addrlen: ?*os.socklen_t,531 addrlen: ?*posix.socklen_t,
532 flags: u32,532 flags: u32,
533) !*linux.io_uring_sqe {533) !*linux.io_uring_sqe {
534 const sqe = try self.get_sqe();534 const sqe = try self.get_sqe();
...@@ -551,9 +551,9 @@ pub fn accept_multishot(...@@ -551,9 +551,9 @@ pub fn accept_multishot(
551pub fn accept_direct(551pub fn accept_direct(
552 self: *IoUring,552 self: *IoUring,
553 user_data: u64,553 user_data: u64,
554 fd: os.fd_t,554 fd: posix.fd_t,
555 addr: ?*os.sockaddr,555 addr: ?*posix.sockaddr,
556 addrlen: ?*os.socklen_t,556 addrlen: ?*posix.socklen_t,
557 flags: u32,557 flags: u32,
558) !*linux.io_uring_sqe {558) !*linux.io_uring_sqe {
559 const sqe = try self.get_sqe();559 const sqe = try self.get_sqe();
...@@ -567,9 +567,9 @@ pub fn accept_direct(...@@ -567,9 +567,9 @@ pub fn accept_direct(
567pub fn accept_multishot_direct(567pub fn accept_multishot_direct(
568 self: *IoUring,568 self: *IoUring,
569 user_data: u64,569 user_data: u64,
570 fd: os.fd_t,570 fd: posix.fd_t,
571 addr: ?*os.sockaddr,571 addr: ?*posix.sockaddr,
572 addrlen: ?*os.socklen_t,572 addrlen: ?*posix.socklen_t,
573 flags: u32,573 flags: u32,
574) !*linux.io_uring_sqe {574) !*linux.io_uring_sqe {
575 const sqe = try self.get_sqe();575 const sqe = try self.get_sqe();
...@@ -583,9 +583,9 @@ pub fn accept_multishot_direct(...@@ -583,9 +583,9 @@ pub fn accept_multishot_direct(
583pub fn connect(583pub fn connect(
584 self: *IoUring,584 self: *IoUring,
585 user_data: u64,585 user_data: u64,
586 fd: os.fd_t,586 fd: posix.fd_t,
587 addr: *const os.sockaddr,587 addr: *const posix.sockaddr,
588 addrlen: os.socklen_t,588 addrlen: posix.socklen_t,
589) !*linux.io_uring_sqe {589) !*linux.io_uring_sqe {
590 const sqe = try self.get_sqe();590 const sqe = try self.get_sqe();
591 sqe.prep_connect(fd, addr, addrlen);591 sqe.prep_connect(fd, addr, addrlen);
...@@ -598,8 +598,8 @@ pub fn connect(...@@ -598,8 +598,8 @@ pub fn connect(
598pub fn epoll_ctl(598pub fn epoll_ctl(
599 self: *IoUring,599 self: *IoUring,
600 user_data: u64,600 user_data: u64,
601 epfd: os.fd_t,601 epfd: posix.fd_t,
602 fd: os.fd_t,602 fd: posix.fd_t,
603 op: u32,603 op: u32,
604 ev: ?*linux.epoll_event,604 ev: ?*linux.epoll_event,
605) !*linux.io_uring_sqe {605) !*linux.io_uring_sqe {
...@@ -629,7 +629,7 @@ pub const RecvBuffer = union(enum) {...@@ -629,7 +629,7 @@ pub const RecvBuffer = union(enum) {
629pub fn recv(629pub fn recv(
630 self: *IoUring,630 self: *IoUring,
631 user_data: u64,631 user_data: u64,
632 fd: os.fd_t,632 fd: posix.fd_t,
633 buffer: RecvBuffer,633 buffer: RecvBuffer,
634 flags: u32,634 flags: u32,
635) !*linux.io_uring_sqe {635) !*linux.io_uring_sqe {
...@@ -653,7 +653,7 @@ pub fn recv(...@@ -653,7 +653,7 @@ pub fn recv(
653pub fn send(653pub fn send(
654 self: *IoUring,654 self: *IoUring,
655 user_data: u64,655 user_data: u64,
656 fd: os.fd_t,656 fd: posix.fd_t,
657 buffer: []const u8,657 buffer: []const u8,
658 flags: u32,658 flags: u32,
659) !*linux.io_uring_sqe {659) !*linux.io_uring_sqe {
...@@ -681,7 +681,7 @@ pub fn send(...@@ -681,7 +681,7 @@ pub fn send(
681pub fn send_zc(681pub fn send_zc(
682 self: *IoUring,682 self: *IoUring,
683 user_data: u64,683 user_data: u64,
684 fd: os.fd_t,684 fd: posix.fd_t,
685 buffer: []const u8,685 buffer: []const u8,
686 send_flags: u32,686 send_flags: u32,
687 zc_flags: u16,687 zc_flags: u16,
...@@ -698,7 +698,7 @@ pub fn send_zc(...@@ -698,7 +698,7 @@ pub fn send_zc(
698pub fn send_zc_fixed(698pub fn send_zc_fixed(
699 self: *IoUring,699 self: *IoUring,
700 user_data: u64,700 user_data: u64,
701 fd: os.fd_t,701 fd: posix.fd_t,
702 buffer: []const u8,702 buffer: []const u8,
703 send_flags: u32,703 send_flags: u32,
704 zc_flags: u16,704 zc_flags: u16,
...@@ -716,8 +716,8 @@ pub fn send_zc_fixed(...@@ -716,8 +716,8 @@ pub fn send_zc_fixed(
716pub fn recvmsg(716pub fn recvmsg(
717 self: *IoUring,717 self: *IoUring,
718 user_data: u64,718 user_data: u64,
719 fd: os.fd_t,719 fd: posix.fd_t,
720 msg: *os.msghdr,720 msg: *posix.msghdr,
721 flags: u32,721 flags: u32,
722) !*linux.io_uring_sqe {722) !*linux.io_uring_sqe {
723 const sqe = try self.get_sqe();723 const sqe = try self.get_sqe();
...@@ -732,8 +732,8 @@ pub fn recvmsg(...@@ -732,8 +732,8 @@ pub fn recvmsg(
732pub fn sendmsg(732pub fn sendmsg(
733 self: *IoUring,733 self: *IoUring,
734 user_data: u64,734 user_data: u64,
735 fd: os.fd_t,735 fd: posix.fd_t,
736 msg: *const os.msghdr_const,736 msg: *const posix.msghdr_const,
737 flags: u32,737 flags: u32,
738) !*linux.io_uring_sqe {738) !*linux.io_uring_sqe {
739 const sqe = try self.get_sqe();739 const sqe = try self.get_sqe();
...@@ -748,8 +748,8 @@ pub fn sendmsg(...@@ -748,8 +748,8 @@ pub fn sendmsg(
748pub fn sendmsg_zc(748pub fn sendmsg_zc(
749 self: *IoUring,749 self: *IoUring,
750 user_data: u64,750 user_data: u64,
751 fd: os.fd_t,751 fd: posix.fd_t,
752 msg: *const os.msghdr_const,752 msg: *const posix.msghdr_const,
753 flags: u32,753 flags: u32,
754) !*linux.io_uring_sqe {754) !*linux.io_uring_sqe {
755 const sqe = try self.get_sqe();755 const sqe = try self.get_sqe();
...@@ -764,10 +764,10 @@ pub fn sendmsg_zc(...@@ -764,10 +764,10 @@ pub fn sendmsg_zc(
764pub fn openat(764pub fn openat(
765 self: *IoUring,765 self: *IoUring,
766 user_data: u64,766 user_data: u64,
767 fd: os.fd_t,767 fd: posix.fd_t,
768 path: [*:0]const u8,768 path: [*:0]const u8,
769 flags: linux.O,769 flags: linux.O,
770 mode: os.mode_t,770 mode: posix.mode_t,
771) !*linux.io_uring_sqe {771) !*linux.io_uring_sqe {
772 const sqe = try self.get_sqe();772 const sqe = try self.get_sqe();
773 sqe.prep_openat(fd, path, flags, mode);773 sqe.prep_openat(fd, path, flags, mode);
...@@ -789,10 +789,10 @@ pub fn openat(...@@ -789,10 +789,10 @@ pub fn openat(
789pub fn openat_direct(789pub fn openat_direct(
790 self: *IoUring,790 self: *IoUring,
791 user_data: u64,791 user_data: u64,
792 fd: os.fd_t,792 fd: posix.fd_t,
793 path: [*:0]const u8,793 path: [*:0]const u8,
794 flags: linux.O,794 flags: linux.O,
795 mode: os.mode_t,795 mode: posix.mode_t,
796 file_index: u32,796 file_index: u32,
797) !*linux.io_uring_sqe {797) !*linux.io_uring_sqe {
798 const sqe = try self.get_sqe();798 const sqe = try self.get_sqe();
...@@ -804,7 +804,7 @@ pub fn openat_direct(...@@ -804,7 +804,7 @@ pub fn openat_direct(
804/// Queues (but does not submit) an SQE to perform a `close(2)`.804/// Queues (but does not submit) an SQE to perform a `close(2)`.
805/// Returns a pointer to the SQE.805/// Returns a pointer to the SQE.
806/// Available since 5.6.806/// 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 {
808 const sqe = try self.get_sqe();808 const sqe = try self.get_sqe();
809 sqe.prep_close(fd);809 sqe.prep_close(fd);
810 sqe.user_data = user_data;810 sqe.user_data = user_data;
...@@ -836,7 +836,7 @@ pub fn close_direct(self: *IoUring, user_data: u64, file_index: u32) !*linux.io_...@@ -836,7 +836,7 @@ pub fn close_direct(self: *IoUring, user_data: u64, file_index: u32) !*linux.io_
836pub fn timeout(836pub fn timeout(
837 self: *IoUring,837 self: *IoUring,
838 user_data: u64,838 user_data: u64,
839 ts: *const os.linux.kernel_timespec,839 ts: *const linux.kernel_timespec,
840 count: u32,840 count: u32,
841 flags: u32,841 flags: u32,
842) !*linux.io_uring_sqe {842) !*linux.io_uring_sqe {
...@@ -885,7 +885,7 @@ pub fn timeout_remove(...@@ -885,7 +885,7 @@ pub fn timeout_remove(
885pub fn link_timeout(885pub fn link_timeout(
886 self: *IoUring,886 self: *IoUring,
887 user_data: u64,887 user_data: u64,
888 ts: *const os.linux.kernel_timespec,888 ts: *const linux.kernel_timespec,
889 flags: u32,889 flags: u32,
890) !*linux.io_uring_sqe {890) !*linux.io_uring_sqe {
891 const sqe = try self.get_sqe();891 const sqe = try self.get_sqe();
...@@ -899,7 +899,7 @@ pub fn link_timeout(...@@ -899,7 +899,7 @@ pub fn link_timeout(
899pub fn poll_add(899pub fn poll_add(
900 self: *IoUring,900 self: *IoUring,
901 user_data: u64,901 user_data: u64,
902 fd: os.fd_t,902 fd: posix.fd_t,
903 poll_mask: u32,903 poll_mask: u32,
904) !*linux.io_uring_sqe {904) !*linux.io_uring_sqe {
905 const sqe = try self.get_sqe();905 const sqe = try self.get_sqe();
...@@ -942,7 +942,7 @@ pub fn poll_update(...@@ -942,7 +942,7 @@ pub fn poll_update(
942pub fn fallocate(942pub fn fallocate(
943 self: *IoUring,943 self: *IoUring,
944 user_data: u64,944 user_data: u64,
945 fd: os.fd_t,945 fd: posix.fd_t,
946 mode: i32,946 mode: i32,
947 offset: u64,947 offset: u64,
948 len: u64,948 len: u64,
...@@ -958,7 +958,7 @@ pub fn fallocate(...@@ -958,7 +958,7 @@ pub fn fallocate(
958pub fn statx(958pub fn statx(
959 self: *IoUring,959 self: *IoUring,
960 user_data: u64,960 user_data: u64,
961 fd: os.fd_t,961 fd: posix.fd_t,
962 path: [:0]const u8,962 path: [:0]const u8,
963 flags: u32,963 flags: u32,
964 mask: u32,964 mask: u32,
...@@ -997,7 +997,7 @@ pub fn cancel(...@@ -997,7 +997,7 @@ pub fn cancel(
997pub fn shutdown(997pub fn shutdown(
998 self: *IoUring,998 self: *IoUring,
999 user_data: u64,999 user_data: u64,
1000 sockfd: os.socket_t,1000 sockfd: posix.socket_t,
1001 how: u32,1001 how: u32,
1002) !*linux.io_uring_sqe {1002) !*linux.io_uring_sqe {
1003 const sqe = try self.get_sqe();1003 const sqe = try self.get_sqe();
...@@ -1011,9 +1011,9 @@ pub fn shutdown(...@@ -1011,9 +1011,9 @@ pub fn shutdown(
1011pub fn renameat(1011pub fn renameat(
1012 self: *IoUring,1012 self: *IoUring,
1013 user_data: u64,1013 user_data: u64,
1014 old_dir_fd: os.fd_t,1014 old_dir_fd: posix.fd_t,
1015 old_path: [*:0]const u8,1015 old_path: [*:0]const u8,
1016 new_dir_fd: os.fd_t,1016 new_dir_fd: posix.fd_t,
1017 new_path: [*:0]const u8,1017 new_path: [*:0]const u8,
1018 flags: u32,1018 flags: u32,
1019) !*linux.io_uring_sqe {1019) !*linux.io_uring_sqe {
...@@ -1028,7 +1028,7 @@ pub fn renameat(...@@ -1028,7 +1028,7 @@ pub fn renameat(
1028pub fn unlinkat(1028pub fn unlinkat(
1029 self: *IoUring,1029 self: *IoUring,
1030 user_data: u64,1030 user_data: u64,
1031 dir_fd: os.fd_t,1031 dir_fd: posix.fd_t,
1032 path: [*:0]const u8,1032 path: [*:0]const u8,
1033 flags: u32,1033 flags: u32,
1034) !*linux.io_uring_sqe {1034) !*linux.io_uring_sqe {
...@@ -1043,9 +1043,9 @@ pub fn unlinkat(...@@ -1043,9 +1043,9 @@ pub fn unlinkat(
1043pub fn mkdirat(1043pub fn mkdirat(
1044 self: *IoUring,1044 self: *IoUring,
1045 user_data: u64,1045 user_data: u64,
1046 dir_fd: os.fd_t,1046 dir_fd: posix.fd_t,
1047 path: [*:0]const u8,1047 path: [*:0]const u8,
1048 mode: os.mode_t,1048 mode: posix.mode_t,
1049) !*linux.io_uring_sqe {1049) !*linux.io_uring_sqe {
1050 const sqe = try self.get_sqe();1050 const sqe = try self.get_sqe();
1051 sqe.prep_mkdirat(dir_fd, path, mode);1051 sqe.prep_mkdirat(dir_fd, path, mode);
...@@ -1059,7 +1059,7 @@ pub fn symlinkat(...@@ -1059,7 +1059,7 @@ pub fn symlinkat(
1059 self: *IoUring,1059 self: *IoUring,
1060 user_data: u64,1060 user_data: u64,
1061 target: [*:0]const u8,1061 target: [*:0]const u8,
1062 new_dir_fd: os.fd_t,1062 new_dir_fd: posix.fd_t,
1063 link_path: [*:0]const u8,1063 link_path: [*:0]const u8,
1064) !*linux.io_uring_sqe {1064) !*linux.io_uring_sqe {
1065 const sqe = try self.get_sqe();1065 const sqe = try self.get_sqe();
...@@ -1073,9 +1073,9 @@ pub fn symlinkat(...@@ -1073,9 +1073,9 @@ pub fn symlinkat(
1073pub fn linkat(1073pub fn linkat(
1074 self: *IoUring,1074 self: *IoUring,
1075 user_data: u64,1075 user_data: u64,
1076 old_dir_fd: os.fd_t,1076 old_dir_fd: posix.fd_t,
1077 old_path: [*:0]const u8,1077 old_path: [*:0]const u8,
1078 new_dir_fd: os.fd_t,1078 new_dir_fd: posix.fd_t,
1079 new_path: [*:0]const u8,1079 new_path: [*:0]const u8,
1080 flags: u32,1080 flags: u32,
1081) !*linux.io_uring_sqe {1081) !*linux.io_uring_sqe {
...@@ -1147,7 +1147,7 @@ pub fn waitid(...@@ -1147,7 +1147,7 @@ pub fn waitid(
1147/// Registering file descriptors will wait for the ring to idle.1147/// Registering file descriptors will wait for the ring to idle.
1148/// Files are automatically unregistered by the kernel when the ring is torn down.1148/// Files are automatically unregistered by the kernel when the ring is torn down.
1149/// An application need unregister only if it wants to register a new array of file descriptors.1149/// 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 {
1151 assert(self.fd >= 0);1151 assert(self.fd >= 0);
1152 const res = linux.io_uring_register(1152 const res = linux.io_uring_register(
1153 self.fd,1153 self.fd,
...@@ -1166,7 +1166,7 @@ pub fn register_files(self: *IoUring, fds: []const os.fd_t) !void {...@@ -1166,7 +1166,7 @@ pub fn register_files(self: *IoUring, fds: []const os.fd_t) !void {
1166/// * removing an existing entry (set the fd to -1)1166/// * removing an existing entry (set the fd to -1)
1167/// * replacing an existing entry with a new fd1167/// * replacing an existing entry with a new fd
1168/// Adding new file descriptors must be done with `register_files`.1168/// 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 {
1170 assert(self.fd >= 0);1170 assert(self.fd >= 0);
11711171
1172 const FilesUpdate = extern struct {1172 const FilesUpdate = extern struct {
...@@ -1192,7 +1192,7 @@ pub fn register_files_update(self: *IoUring, offset: u32, fds: []const os.fd_t)...@@ -1192,7 +1192,7 @@ pub fn register_files_update(self: *IoUring, offset: u32, fds: []const os.fd_t)
1192/// Registers the file descriptor for an eventfd that will be notified of completion events on1192/// Registers the file descriptor for an eventfd that will be notified of completion events on
1193/// an io_uring instance.1193/// an io_uring instance.
1194/// Only a single a eventfd can be registered at any given point in time.1194/// 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 {
1196 assert(self.fd >= 0);1196 assert(self.fd >= 0);
1197 const res = linux.io_uring_register(1197 const res = linux.io_uring_register(
1198 self.fd,1198 self.fd,
...@@ -1207,7 +1207,7 @@ pub fn register_eventfd(self: *IoUring, fd: os.fd_t) !void {...@@ -1207,7 +1207,7 @@ pub fn register_eventfd(self: *IoUring, fd: os.fd_t) !void {
1207/// an io_uring instance. Notifications are only posted for events that complete in an async manner.1207/// an io_uring instance. Notifications are only posted for events that complete in an async manner.
1208/// This means that events that complete inline while being submitted do not trigger a notification event.1208/// This means that events that complete inline while being submitted do not trigger a notification event.
1209/// Only a single eventfd can be registered at any given point in time.1209/// 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 {
1211 assert(self.fd >= 0);1211 assert(self.fd >= 0);
1212 const res = linux.io_uring_register(1212 const res = linux.io_uring_register(
1213 self.fd,1213 self.fd,
...@@ -1231,7 +1231,7 @@ pub fn unregister_eventfd(self: *IoUring) !void {...@@ -1231,7 +1231,7 @@ pub fn unregister_eventfd(self: *IoUring) !void {
1231}1231}
12321232
1233/// Registers an array of buffers for use with `read_fixed` and `write_fixed`.1233/// 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 {
1235 assert(self.fd >= 0);1235 assert(self.fd >= 0);
1236 const res = linux.io_uring_register(1236 const res = linux.io_uring_register(
1237 self.fd,1237 self.fd,
...@@ -1246,15 +1246,15 @@ pub fn register_buffers(self: *IoUring, buffers: []const os.iovec) !void {...@@ -1246,15 +1246,15 @@ pub fn register_buffers(self: *IoUring, buffers: []const os.iovec) !void {
1246pub fn unregister_buffers(self: *IoUring) !void {1246pub fn unregister_buffers(self: *IoUring) !void {
1247 assert(self.fd >= 0);1247 assert(self.fd >= 0);
1248 const res = linux.io_uring_register(self.fd, .UNREGISTER_BUFFERS, null, 0);1248 const res = linux.io_uring_register(self.fd, .UNREGISTER_BUFFERS, null, 0);
1249 switch (linux.getErrno(res)) {1249 switch (linux.E.init(res)) {
1250 .SUCCESS => {},1250 .SUCCESS => {},
1251 .NXIO => return error.BuffersNotRegistered,1251 .NXIO => return error.BuffersNotRegistered,
1252 else => |errno| return os.unexpectedErrno(errno),1252 else => |errno| return posix.unexpectedErrno(errno),
1253 }1253 }
1254}1254}
12551255
1256fn handle_registration_result(res: usize) !void {1256fn handle_registration_result(res: usize) !void {
1257 switch (linux.getErrno(res)) {1257 switch (linux.E.init(res)) {
1258 .SUCCESS => {},1258 .SUCCESS => {},
1259 // One or more fds in the array are invalid, or the kernel does not support sparse sets:1259 // One or more fds in the array are invalid, or the kernel does not support sparse sets:
1260 .BADF => return error.FileDescriptorInvalid,1260 .BADF => return error.FileDescriptorInvalid,
...@@ -1271,7 +1271,7 @@ fn handle_registration_result(res: usize) !void {...@@ -1271,7 +1271,7 @@ fn handle_registration_result(res: usize) !void {
1271 .NOMEM => return error.SystemResources,1271 .NOMEM => return error.SystemResources,
1272 // Attempt to register files on a ring already registering files or being torn down:1272 // Attempt to register files on a ring already registering files or being torn down:
1273 .NXIO => return error.RingShuttingDownOrAlreadyRegisteringFiles,1273 .NXIO => return error.RingShuttingDownOrAlreadyRegisteringFiles,
1274 else => |errno| return os.unexpectedErrno(errno),1274 else => |errno| return posix.unexpectedErrno(errno),
1275 }1275 }
1276}1276}
12771277
...@@ -1279,10 +1279,10 @@ fn handle_registration_result(res: usize) !void {...@@ -1279,10 +1279,10 @@ fn handle_registration_result(res: usize) !void {
1279pub fn unregister_files(self: *IoUring) !void {1279pub fn unregister_files(self: *IoUring) !void {
1280 assert(self.fd >= 0);1280 assert(self.fd >= 0);
1281 const res = linux.io_uring_register(self.fd, .UNREGISTER_FILES, null, 0);1281 const res = linux.io_uring_register(self.fd, .UNREGISTER_FILES, null, 0);
1282 switch (linux.getErrno(res)) {1282 switch (linux.E.init(res)) {
1283 .SUCCESS => {},1283 .SUCCESS => {},
1284 .NXIO => return error.FilesNotRegistered,1284 .NXIO => return error.FilesNotRegistered,
1285 else => |errno| return os.unexpectedErrno(errno),1285 else => |errno| return posix.unexpectedErrno(errno),
1286 }1286 }
1287}1287}
12881288
...@@ -1355,36 +1355,36 @@ pub const SubmissionQueue = struct {...@@ -1355,36 +1355,36 @@ pub const SubmissionQueue = struct {
1355 sqe_head: u32 = 0,1355 sqe_head: u32 = 0,
1356 sqe_tail: u32 = 0,1356 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 {
1359 assert(fd >= 0);1359 assert(fd >= 0);
1360 assert((p.features & linux.IORING_FEAT_SINGLE_MMAP) != 0);1360 assert((p.features & linux.IORING_FEAT_SINGLE_MMAP) != 0);
1361 const size = @max(1361 const size = @max(
1362 p.sq_off.array + p.sq_entries * @sizeOf(u32),1362 p.sq_off.array + p.sq_entries * @sizeOf(u32),
1363 p.cq_off.cqes + p.cq_entries * @sizeOf(linux.io_uring_cqe),1363 p.cq_off.cqes + p.cq_entries * @sizeOf(linux.io_uring_cqe),
1364 );1364 );
1365 const mmap = try os.mmap(1365 const mmap = try posix.mmap(
1366 null,1366 null,
1367 size,1367 size,
1368 os.PROT.READ | os.PROT.WRITE,1368 posix.PROT.READ | posix.PROT.WRITE,
1369 .{ .TYPE = .SHARED, .POPULATE = true },1369 .{ .TYPE = .SHARED, .POPULATE = true },
1370 fd,1370 fd,
1371 linux.IORING_OFF_SQ_RING,1371 linux.IORING_OFF_SQ_RING,
1372 );1372 );
1373 errdefer os.munmap(mmap);1373 errdefer posix.munmap(mmap);
1374 assert(mmap.len == size);1374 assert(mmap.len == size);
13751375
1376 // The motivation for the `sqes` and `array` indirection is to make it possible for the1376 // The motivation for the `sqes` and `array` indirection is to make it possible for the
1377 // application to preallocate static linux.io_uring_sqe entries and then replay them when needed.1377 // application to preallocate static linux.io_uring_sqe entries and then replay them when needed.
1378 const size_sqes = p.sq_entries * @sizeOf(linux.io_uring_sqe);1378 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(
1380 null,1380 null,
1381 size_sqes,1381 size_sqes,
1382 os.PROT.READ | os.PROT.WRITE,1382 posix.PROT.READ | posix.PROT.WRITE,
1383 .{ .TYPE = .SHARED, .POPULATE = true },1383 .{ .TYPE = .SHARED, .POPULATE = true },
1384 fd,1384 fd,
1385 linux.IORING_OFF_SQES,1385 linux.IORING_OFF_SQES,
1386 );1386 );
1387 errdefer os.munmap(mmap_sqes);1387 errdefer posix.munmap(mmap_sqes);
1388 assert(mmap_sqes.len == size_sqes);1388 assert(mmap_sqes.len == size_sqes);
13891389
1390 const array: [*]u32 = @ptrCast(@alignCast(&mmap[p.sq_off.array]));1390 const array: [*]u32 = @ptrCast(@alignCast(&mmap[p.sq_off.array]));
...@@ -1406,8 +1406,8 @@ pub const SubmissionQueue = struct {...@@ -1406,8 +1406,8 @@ pub const SubmissionQueue = struct {
1406 }1406 }
14071407
1408 pub fn deinit(self: *SubmissionQueue) void {1408 pub fn deinit(self: *SubmissionQueue) void {
1409 os.munmap(self.mmap_sqes);1409 posix.munmap(self.mmap_sqes);
1410 os.munmap(self.mmap);1410 posix.munmap(self.mmap);
1411 }1411 }
1412};1412};
14131413
...@@ -1418,7 +1418,7 @@ pub const CompletionQueue = struct {...@@ -1418,7 +1418,7 @@ pub const CompletionQueue = struct {
1418 overflow: *u32,1418 overflow: *u32,
1419 cqes: []linux.io_uring_cqe,1419 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 {
1422 assert(fd >= 0);1422 assert(fd >= 0);
1423 assert((p.features & linux.IORING_FEAT_SINGLE_MMAP) != 0);1423 assert((p.features & linux.IORING_FEAT_SINGLE_MMAP) != 0);
1424 const mmap = sq.mmap;1424 const mmap = sq.mmap;
...@@ -1506,7 +1506,7 @@ pub const BufferGroup = struct {...@@ -1506,7 +1506,7 @@ pub const BufferGroup = struct {
1506 }1506 }
15071507
1508 // Prepare recv operation which will select buffer from this group.1508 // 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 {
1510 var sqe = try self.ring.get_sqe();1510 var sqe = try self.ring.get_sqe();
1511 sqe.prep_rw(.RECV, fd, 0, 0, 0);1511 sqe.prep_rw(.RECV, fd, 0, 0, 0);
1512 sqe.rw_flags = flags;1512 sqe.rw_flags = flags;
...@@ -1517,7 +1517,7 @@ pub const BufferGroup = struct {...@@ -1517,7 +1517,7 @@ pub const BufferGroup = struct {
1517 }1517 }
15181518
1519 // Prepare multishot recv operation which will select buffer from this group.1519 // 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 {
1521 var sqe = try self.recv(user_data, fd, flags);1521 var sqe = try self.recv(user_data, fd, flags);
1522 sqe.ioprio |= linux.IORING_RECV_MULTISHOT;1522 sqe.ioprio |= linux.IORING_RECV_MULTISHOT;
1523 return sqe;1523 return sqe;
...@@ -1559,20 +1559,20 @@ pub const BufferGroup = struct {...@@ -1559,20 +1559,20 @@ pub const BufferGroup = struct {
1559/// `fd` is IO_Uring.fd for which the provided buffer ring is being registered.1559/// `fd` is IO_Uring.fd for which the provided buffer ring is being registered.
1560/// `entries` is the number of entries requested in the buffer ring, must be power of 2.1560/// `entries` is the number of entries requested in the buffer ring, must be power of 2.
1561/// `group_id` is the chosen buffer group ID, unique in IO_Uring.1561/// `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 {
1563 if (entries == 0 or entries > 1 << 15) return error.EntriesNotInRange;1563 if (entries == 0 or entries > 1 << 15) return error.EntriesNotInRange;
1564 if (!std.math.isPowerOfTwo(entries)) return error.EntriesNotPowerOfTwo;1564 if (!std.math.isPowerOfTwo(entries)) return error.EntriesNotPowerOfTwo;
15651565
1566 const mmap_size = entries * @sizeOf(linux.io_uring_buf);1566 const mmap_size = entries * @sizeOf(linux.io_uring_buf);
1567 const mmap = try os.mmap(1567 const mmap = try posix.mmap(
1568 null,1568 null,
1569 mmap_size,1569 mmap_size,
1570 os.PROT.READ | os.PROT.WRITE,1570 posix.PROT.READ | posix.PROT.WRITE,
1571 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },1571 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
1572 -1,1572 -1,
1573 0,1573 0,
1574 );1574 );
1575 errdefer os.munmap(mmap);1575 errdefer posix.munmap(mmap);
1576 assert(mmap.len == mmap_size);1576 assert(mmap.len == mmap_size);
15771577
1578 const br: *align(mem.page_size) linux.io_uring_buf_ring = @ptrCast(mmap.ptr);1578 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...@@ -1580,7 +1580,7 @@ pub fn setup_buf_ring(fd: os.fd_t, entries: u16, group_id: u16) !*align(mem.page
1580 return br;1580 return br;
1581}1581}
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 {
1584 var reg = mem.zeroInit(linux.io_uring_buf_reg, .{1584 var reg = mem.zeroInit(linux.io_uring_buf_reg, .{
1585 .ring_addr = addr,1585 .ring_addr = addr,
1586 .ring_entries = entries,1586 .ring_entries = entries,
...@@ -1595,7 +1595,7 @@ fn register_buf_ring(fd: os.fd_t, addr: u64, entries: u32, group_id: u16) !void...@@ -1595,7 +1595,7 @@ fn register_buf_ring(fd: os.fd_t, addr: u64, entries: u32, group_id: u16) !void
1595 try handle_register_buf_ring_result(res);1595 try handle_register_buf_ring_result(res);
1596}1596}
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 {
1599 var reg = mem.zeroInit(linux.io_uring_buf_reg, .{1599 var reg = mem.zeroInit(linux.io_uring_buf_reg, .{
1600 .bgid = group_id,1600 .bgid = group_id,
1601 });1601 });
...@@ -1609,20 +1609,20 @@ fn unregister_buf_ring(fd: os.fd_t, group_id: u16) !void {...@@ -1609,20 +1609,20 @@ fn unregister_buf_ring(fd: os.fd_t, group_id: u16) !void {
1609}1609}
16101610
1611fn handle_register_buf_ring_result(res: usize) !void {1611fn handle_register_buf_ring_result(res: usize) !void {
1612 switch (linux.getErrno(res)) {1612 switch (linux.E.init(res)) {
1613 .SUCCESS => {},1613 .SUCCESS => {},
1614 .INVAL => return error.ArgumentsInvalid,1614 .INVAL => return error.ArgumentsInvalid,
1615 else => |errno| return os.unexpectedErrno(errno),1615 else => |errno| return posix.unexpectedErrno(errno),
1616 }1616 }
1617}1617}
16181618
1619// Unregisters a previously registered shared buffer ring, returned from io_uring_setup_buf_ring.1619// 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 {
1621 unregister_buf_ring(fd, group_id) catch {};1621 unregister_buf_ring(fd, group_id) catch {};
1622 var mmap: []align(mem.page_size) u8 = undefined;1622 var mmap: []align(mem.page_size) u8 = undefined;
1623 mmap.ptr = @ptrCast(br);1623 mmap.ptr = @ptrCast(br);
1624 mmap.len = entries * @sizeOf(linux.io_uring_buf);1624 mmap.len = entries * @sizeOf(linux.io_uring_buf);
1625 os.munmap(mmap);1625 posix.munmap(mmap);
1626}1626}
16271627
1628/// Initialises `br` so that it is ready to be used.1628/// 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 {...@@ -1664,7 +1664,7 @@ pub fn buf_ring_advance(br: *linux.io_uring_buf_ring, count: u16) void {
1664}1664}
16651665
1666test "structs/offsets/entries" {1666test "structs/offsets/entries" {
1667 if (builtin.os.tag != .linux) return error.SkipZigTest;1667 if (!is_linux) return error.SkipZigTest;
16681668
1669 try testing.expectEqual(@as(usize, 120), @sizeOf(linux.io_uring_params));1669 try testing.expectEqual(@as(usize, 120), @sizeOf(linux.io_uring_params));
1670 try testing.expectEqual(@as(usize, 64), @sizeOf(linux.io_uring_sqe));1670 try testing.expectEqual(@as(usize, 64), @sizeOf(linux.io_uring_sqe));
...@@ -1679,7 +1679,7 @@ test "structs/offsets/entries" {...@@ -1679,7 +1679,7 @@ test "structs/offsets/entries" {
1679}1679}
16801680
1681test "nop" {1681test "nop" {
1682 if (builtin.os.tag != .linux) return error.SkipZigTest;1682 if (!is_linux) return error.SkipZigTest;
16831683
1684 var ring = IoUring.init(1, 0) catch |err| switch (err) {1684 var ring = IoUring.init(1, 0) catch |err| switch (err) {
1685 error.SystemOutdated => return error.SkipZigTest,1685 error.SystemOutdated => return error.SkipZigTest,
...@@ -1688,7 +1688,7 @@ test "nop" {...@@ -1688,7 +1688,7 @@ test "nop" {
1688 };1688 };
1689 defer {1689 defer {
1690 ring.deinit();1690 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");
1692 }1692 }
16931693
1694 const sqe = try ring.nop(0xaaaaaaaa);1694 const sqe = try ring.nop(0xaaaaaaaa);
...@@ -1746,7 +1746,7 @@ test "nop" {...@@ -1746,7 +1746,7 @@ test "nop" {
1746}1746}
17471747
1748test "readv" {1748test "readv" {
1749 if (builtin.os.tag != .linux) return error.SkipZigTest;1749 if (!is_linux) return error.SkipZigTest;
17501750
1751 var ring = IoUring.init(1, 0) catch |err| switch (err) {1751 var ring = IoUring.init(1, 0) catch |err| switch (err) {
1752 error.SystemOutdated => return error.SkipZigTest,1752 error.SystemOutdated => return error.SkipZigTest,
...@@ -1755,8 +1755,8 @@ test "readv" {...@@ -1755,8 +1755,8 @@ test "readv" {
1755 };1755 };
1756 defer ring.deinit();1756 defer ring.deinit();
17571757
1758 const fd = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);1758 const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
1759 defer os.close(fd);1759 defer posix.close(fd);
17601760
1761 // Linux Kernel 5.4 supports IORING_REGISTER_FILES but not sparse fd sets (i.e. an fd of -1).1761 // Linux Kernel 5.4 supports IORING_REGISTER_FILES but not sparse fd sets (i.e. an fd of -1).
1762 // Linux Kernel 5.5 adds support for sparse fd sets.1762 // Linux Kernel 5.5 adds support for sparse fd sets.
...@@ -1764,13 +1764,13 @@ test "readv" {...@@ -1764,13 +1764,13 @@ test "readv" {
1764 // https://github.com/torvalds/linux/blob/v5.4/fs/io_uring.c#L3119-L3124 vs1764 // https://github.com/torvalds/linux/blob/v5.4/fs/io_uring.c#L3119-L3124 vs
1765 // https://github.com/torvalds/linux/blob/v5.8/fs/io_uring.c#L6687-L66911765 // https://github.com/torvalds/linux/blob/v5.8/fs/io_uring.c#L6687-L6691
1766 // We therefore avoid stressing sparse fd sets here:1766 // 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;
1768 const fd_index = 0;1768 const fd_index = 0;
1769 registered_fds[fd_index] = fd;1769 registered_fds[fd_index] = fd;
1770 try ring.register_files(registered_fds[0..]);1770 try ring.register_files(registered_fds[0..]);
17711771
1772 var buffer = [_]u8{42} ** 128;1772 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 }};
1774 const sqe = try ring.read(0xcccccccc, fd_index, .{ .iovecs = iovecs[0..] }, 0);1774 const sqe = try ring.read(0xcccccccc, fd_index, .{ .iovecs = iovecs[0..] }, 0);
1775 try testing.expectEqual(linux.IORING_OP.READV, sqe.opcode);1775 try testing.expectEqual(linux.IORING_OP.READV, sqe.opcode);
1776 sqe.flags |= linux.IOSQE_FIXED_FILE;1776 sqe.flags |= linux.IOSQE_FIXED_FILE;
...@@ -1788,7 +1788,7 @@ test "readv" {...@@ -1788,7 +1788,7 @@ test "readv" {
1788}1788}
17891789
1790test "writev/fsync/readv" {1790test "writev/fsync/readv" {
1791 if (builtin.os.tag != .linux) return error.SkipZigTest;1791 if (!is_linux) return error.SkipZigTest;
17921792
1793 var ring = IoUring.init(4, 0) catch |err| switch (err) {1793 var ring = IoUring.init(4, 0) catch |err| switch (err) {
1794 error.SystemOutdated => return error.SkipZigTest,1794 error.SystemOutdated => return error.SkipZigTest,
...@@ -1806,12 +1806,12 @@ test "writev/fsync/readv" {...@@ -1806,12 +1806,12 @@ test "writev/fsync/readv" {
1806 const fd = file.handle;1806 const fd = file.handle;
18071807
1808 const buffer_write = [_]u8{42} ** 128;1808 const buffer_write = [_]u8{42} ** 128;
1809 const iovecs_write = [_]os.iovec_const{1809 const iovecs_write = [_]posix.iovec_const{
1810 os.iovec_const{ .iov_base = &buffer_write, .iov_len = buffer_write.len },1810 posix.iovec_const{ .iov_base = &buffer_write, .iov_len = buffer_write.len },
1811 };1811 };
1812 var buffer_read = [_]u8{0} ** 128;1812 var buffer_read = [_]u8{0} ** 128;
1813 var iovecs_read = [_]os.iovec{1813 var iovecs_read = [_]posix.iovec{
1814 os.iovec{ .iov_base = &buffer_read, .iov_len = buffer_read.len },1814 posix.iovec{ .iov_base = &buffer_read, .iov_len = buffer_read.len },
1815 };1815 };
18161816
1817 const sqe_writev = try ring.writev(0xdddddddd, fd, iovecs_write[0..], 17);1817 const sqe_writev = try ring.writev(0xdddddddd, fd, iovecs_write[0..], 17);
...@@ -1858,7 +1858,7 @@ test "writev/fsync/readv" {...@@ -1858,7 +1858,7 @@ test "writev/fsync/readv" {
1858}1858}
18591859
1860test "write/read" {1860test "write/read" {
1861 if (builtin.os.tag != .linux) return error.SkipZigTest;1861 if (!is_linux) return error.SkipZigTest;
18621862
1863 var ring = IoUring.init(2, 0) catch |err| switch (err) {1863 var ring = IoUring.init(2, 0) catch |err| switch (err) {
1864 error.SystemOutdated => return error.SkipZigTest,1864 error.SystemOutdated => return error.SkipZigTest,
...@@ -1905,7 +1905,7 @@ test "write/read" {...@@ -1905,7 +1905,7 @@ test "write/read" {
1905}1905}
19061906
1907test "splice/read" {1907test "splice/read" {
1908 if (builtin.os.tag != .linux) return error.SkipZigTest;1908 if (!is_linux) return error.SkipZigTest;
19091909
1910 var ring = IoUring.init(4, 0) catch |err| switch (err) {1910 var ring = IoUring.init(4, 0) catch |err| switch (err) {
1911 error.SystemOutdated => return error.SkipZigTest,1911 error.SystemOutdated => return error.SkipZigTest,
...@@ -1929,7 +1929,7 @@ test "splice/read" {...@@ -1929,7 +1929,7 @@ test "splice/read" {
1929 var buffer_read = [_]u8{98} ** 20;1929 var buffer_read = [_]u8{98} ** 20;
1930 _ = try file_src.write(&buffer_write);1930 _ = try file_src.write(&buffer_write);
19311931
1932 const fds = try os.pipe();1932 const fds = try posix.pipe();
1933 const pipe_offset: u64 = std.math.maxInt(u64);1933 const pipe_offset: u64 = std.math.maxInt(u64);
19341934
1935 const sqe_splice_to_pipe = try ring.splice(0x11111111, fd_src, 0, fds[1], pipe_offset, buffer_write.len);1935 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" {...@@ -1976,7 +1976,7 @@ test "splice/read" {
1976}1976}
19771977
1978test "write_fixed/read_fixed" {1978test "write_fixed/read_fixed" {
1979 if (builtin.os.tag != .linux) return error.SkipZigTest;1979 if (!is_linux) return error.SkipZigTest;
19801980
1981 var ring = IoUring.init(2, 0) catch |err| switch (err) {1981 var ring = IoUring.init(2, 0) catch |err| switch (err) {
1982 error.SystemOutdated => return error.SkipZigTest,1982 error.SystemOutdated => return error.SkipZigTest,
...@@ -1998,7 +1998,7 @@ test "write_fixed/read_fixed" {...@@ -1998,7 +1998,7 @@ test "write_fixed/read_fixed" {
1998 @memset(&raw_buffers[0], 'z');1998 @memset(&raw_buffers[0], 'z');
1999 raw_buffers[0][0.."foobar".len].* = "foobar".*;1999 raw_buffers[0][0.."foobar".len].* = "foobar".*;
20002000
2001 var buffers = [2]os.iovec{2001 var buffers = [2]posix.iovec{
2002 .{ .iov_base = &raw_buffers[0], .iov_len = raw_buffers[0].len },2002 .{ .iov_base = &raw_buffers[0], .iov_len = raw_buffers[0].len },
2003 .{ .iov_base = &raw_buffers[1], .iov_len = raw_buffers[1].len },2003 .{ .iov_base = &raw_buffers[1], .iov_len = raw_buffers[1].len },
2004 };2004 };
...@@ -2041,7 +2041,7 @@ test "write_fixed/read_fixed" {...@@ -2041,7 +2041,7 @@ test "write_fixed/read_fixed" {
2041}2041}
20422042
2043test "openat" {2043test "openat" {
2044 if (builtin.os.tag != .linux) return error.SkipZigTest;2044 if (!is_linux) return error.SkipZigTest;
20452045
2046 var ring = IoUring.init(1, 0) catch |err| switch (err) {2046 var ring = IoUring.init(1, 0) catch |err| switch (err) {
2047 error.SystemOutdated => return error.SkipZigTest,2047 error.SystemOutdated => return error.SkipZigTest,
...@@ -2063,7 +2063,7 @@ test "openat" {...@@ -2063,7 +2063,7 @@ test "openat" {
2063 } else @intFromPtr(path);2063 } else @intFromPtr(path);
20642064
2065 const flags: linux.O = .{ .CLOEXEC = true, .ACCMODE = .RDWR, .CREAT = true };2065 const flags: linux.O = .{ .CLOEXEC = true, .ACCMODE = .RDWR, .CREAT = true };
2066 const mode: os.mode_t = 0o666;2066 const mode: posix.mode_t = 0o666;
2067 const sqe_openat = try ring.openat(0x33333333, tmp.dir.fd, path, flags, mode);2067 const sqe_openat = try ring.openat(0x33333333, tmp.dir.fd, path, flags, mode);
2068 try testing.expectEqual(linux.io_uring_sqe{2068 try testing.expectEqual(linux.io_uring_sqe{
2069 .opcode = .OPENAT,2069 .opcode = .OPENAT,
...@@ -2091,11 +2091,11 @@ test "openat" {...@@ -2091,11 +2091,11 @@ test "openat" {
2091 try testing.expect(cqe_openat.res > 0);2091 try testing.expect(cqe_openat.res > 0);
2092 try testing.expectEqual(@as(u32, 0), cqe_openat.flags);2092 try testing.expectEqual(@as(u32, 0), cqe_openat.flags);
20932093
2094 os.close(cqe_openat.res);2094 posix.close(cqe_openat.res);
2095}2095}
20962096
2097test "close" {2097test "close" {
2098 if (builtin.os.tag != .linux) return error.SkipZigTest;2098 if (!is_linux) return error.SkipZigTest;
20992099
2100 var ring = IoUring.init(1, 0) catch |err| switch (err) {2100 var ring = IoUring.init(1, 0) catch |err| switch (err) {
2101 error.SystemOutdated => return error.SkipZigTest,2101 error.SystemOutdated => return error.SkipZigTest,
...@@ -2126,7 +2126,7 @@ test "close" {...@@ -2126,7 +2126,7 @@ test "close" {
2126}2126}
21272127
2128test "accept/connect/send/recv" {2128test "accept/connect/send/recv" {
2129 if (builtin.os.tag != .linux) return error.SkipZigTest;2129 if (!is_linux) return error.SkipZigTest;
21302130
2131 var ring = IoUring.init(16, 0) catch |err| switch (err) {2131 var ring = IoUring.init(16, 0) catch |err| switch (err) {
2132 error.SystemOutdated => return error.SkipZigTest,2132 error.SystemOutdated => return error.SkipZigTest,
...@@ -2167,7 +2167,7 @@ test "accept/connect/send/recv" {...@@ -2167,7 +2167,7 @@ test "accept/connect/send/recv" {
2167}2167}
21682168
2169test "sendmsg/recvmsg" {2169test "sendmsg/recvmsg" {
2170 if (builtin.os.tag != .linux) return error.SkipZigTest;2170 if (!is_linux) return error.SkipZigTest;
21712171
2172 var ring = IoUring.init(2, 0) catch |err| switch (err) {2172 var ring = IoUring.init(2, 0) catch |err| switch (err) {
2173 error.SystemOutdated => return error.SkipZigTest,2173 error.SystemOutdated => return error.SkipZigTest,
...@@ -2178,24 +2178,24 @@ test "sendmsg/recvmsg" {...@@ -2178,24 +2178,24 @@ test "sendmsg/recvmsg" {
21782178
2179 var address_server = try net.Address.parseIp4("127.0.0.1", 0);2179 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);2181 const server = try posix.socket(address_server.any.family, posix.SOCK.DGRAM, 0);
2182 defer os.close(server);2182 defer posix.close(server);
2183 try os.setsockopt(server, os.SOL.SOCKET, os.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1)));2183 try posix.setsockopt(server, posix.SOL.SOCKET, posix.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)));2184 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
2185 try os.bind(server, &address_server.any, address_server.getOsSockLen());2185 try posix.bind(server, &address_server.any, address_server.getOsSockLen());
21862186
2187 // set address_server to the OS-chosen IP/port.2187 // set address_server to the OS-chosen IP/port.
2188 var slen: os.socklen_t = address_server.getOsSockLen();2188 var slen: posix.socklen_t = address_server.getOsSockLen();
2189 try os.getsockname(server, &address_server.any, &slen);2189 try posix.getsockname(server, &address_server.any, &slen);
21902190
2191 const client = try os.socket(address_server.any.family, os.SOCK.DGRAM, 0);2191 const client = try posix.socket(address_server.any.family, posix.SOCK.DGRAM, 0);
2192 defer os.close(client);2192 defer posix.close(client);
21932193
2194 const buffer_send = [_]u8{42} ** 128;2194 const buffer_send = [_]u8{42} ** 128;
2195 const iovecs_send = [_]os.iovec_const{2195 const iovecs_send = [_]posix.iovec_const{
2196 os.iovec_const{ .iov_base = &buffer_send, .iov_len = buffer_send.len },2196 posix.iovec_const{ .iov_base = &buffer_send, .iov_len = buffer_send.len },
2197 };2197 };
2198 const msg_send = os.msghdr_const{2198 const msg_send: posix.msghdr_const = .{
2199 .name = &address_server.any,2199 .name = &address_server.any,
2200 .namelen = address_server.getOsSockLen(),2200 .namelen = address_server.getOsSockLen(),
2201 .iov = &iovecs_send,2201 .iov = &iovecs_send,
...@@ -2210,12 +2210,12 @@ test "sendmsg/recvmsg" {...@@ -2210,12 +2210,12 @@ test "sendmsg/recvmsg" {
2210 try testing.expectEqual(client, sqe_sendmsg.fd);2210 try testing.expectEqual(client, sqe_sendmsg.fd);
22112211
2212 var buffer_recv = [_]u8{0} ** 128;2212 var buffer_recv = [_]u8{0} ** 128;
2213 var iovecs_recv = [_]os.iovec{2213 var iovecs_recv = [_]posix.iovec{
2214 os.iovec{ .iov_base = &buffer_recv, .iov_len = buffer_recv.len },2214 posix.iovec{ .iov_base = &buffer_recv, .iov_len = buffer_recv.len },
2215 };2215 };
2216 const addr = [_]u8{0} ** 4;2216 const addr = [_]u8{0} ** 4;
2217 var address_recv = net.Address.initIp4(addr, 0);2217 var address_recv = net.Address.initIp4(addr, 0);
2218 var msg_recv: os.msghdr = os.msghdr{2218 var msg_recv: posix.msghdr = .{
2219 .name = &address_recv.any,2219 .name = &address_recv.any,
2220 .namelen = address_recv.getOsSockLen(),2220 .namelen = address_recv.getOsSockLen(),
2221 .iov = &iovecs_recv,2221 .iov = &iovecs_recv,
...@@ -2254,7 +2254,7 @@ test "sendmsg/recvmsg" {...@@ -2254,7 +2254,7 @@ test "sendmsg/recvmsg" {
2254}2254}
22552255
2256test "timeout (after a relative time)" {2256test "timeout (after a relative time)" {
2257 if (builtin.os.tag != .linux) return error.SkipZigTest;2257 if (!is_linux) return error.SkipZigTest;
22582258
2259 var ring = IoUring.init(1, 0) catch |err| switch (err) {2259 var ring = IoUring.init(1, 0) catch |err| switch (err) {
2260 error.SystemOutdated => return error.SkipZigTest,2260 error.SystemOutdated => return error.SkipZigTest,
...@@ -2265,7 +2265,7 @@ test "timeout (after a relative time)" {...@@ -2265,7 +2265,7 @@ test "timeout (after a relative time)" {
22652265
2266 const ms = 10;2266 const ms = 10;
2267 const margin = 5;2267 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
2270 const started = std.time.milliTimestamp();2270 const started = std.time.milliTimestamp();
2271 const sqe = try ring.timeout(0x55555555, &ts, 0, 0);2271 const sqe = try ring.timeout(0x55555555, &ts, 0, 0);
...@@ -2285,7 +2285,7 @@ test "timeout (after a relative time)" {...@@ -2285,7 +2285,7 @@ test "timeout (after a relative time)" {
2285}2285}
22862286
2287test "timeout (after a number of completions)" {2287test "timeout (after a number of completions)" {
2288 if (builtin.os.tag != .linux) return error.SkipZigTest;2288 if (!is_linux) return error.SkipZigTest;
22892289
2290 var ring = IoUring.init(2, 0) catch |err| switch (err) {2290 var ring = IoUring.init(2, 0) catch |err| switch (err) {
2291 error.SystemOutdated => return error.SkipZigTest,2291 error.SystemOutdated => return error.SkipZigTest,
...@@ -2294,7 +2294,7 @@ test "timeout (after a number of completions)" {...@@ -2294,7 +2294,7 @@ test "timeout (after a number of completions)" {
2294 };2294 };
2295 defer ring.deinit();2295 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 };
2298 const count_completions: u64 = 1;2298 const count_completions: u64 = 1;
2299 const sqe_timeout = try ring.timeout(0x66666666, &ts, count_completions, 0);2299 const sqe_timeout = try ring.timeout(0x66666666, &ts, count_completions, 0);
2300 try testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe_timeout.opcode);2300 try testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe_timeout.opcode);
...@@ -2318,7 +2318,7 @@ test "timeout (after a number of completions)" {...@@ -2318,7 +2318,7 @@ test "timeout (after a number of completions)" {
2318}2318}
23192319
2320test "timeout_remove" {2320test "timeout_remove" {
2321 if (builtin.os.tag != .linux) return error.SkipZigTest;2321 if (!is_linux) return error.SkipZigTest;
23222322
2323 var ring = IoUring.init(2, 0) catch |err| switch (err) {2323 var ring = IoUring.init(2, 0) catch |err| switch (err) {
2324 error.SystemOutdated => return error.SkipZigTest,2324 error.SystemOutdated => return error.SkipZigTest,
...@@ -2327,7 +2327,7 @@ test "timeout_remove" {...@@ -2327,7 +2327,7 @@ test "timeout_remove" {
2327 };2327 };
2328 defer ring.deinit();2328 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 };
2331 const sqe_timeout = try ring.timeout(0x88888888, &ts, 0, 0);2331 const sqe_timeout = try ring.timeout(0x88888888, &ts, 0, 0);
2332 try testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe_timeout.opcode);2332 try testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe_timeout.opcode);
2333 try testing.expectEqual(@as(u64, 0x88888888), sqe_timeout.user_data);2333 try testing.expectEqual(@as(u64, 0x88888888), sqe_timeout.user_data);
...@@ -2343,7 +2343,7 @@ test "timeout_remove" {...@@ -2343,7 +2343,7 @@ test "timeout_remove" {
2343 // * kernel 5.10 gives user data 0x88888888 first, 0x99999999 second2343 // * kernel 5.10 gives user data 0x88888888 first, 0x99999999 second
2344 // * kernel 5.18 gives user data 0x99999999 first, 0x88888888 second2344 // * 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;
2347 cqes[0] = try ring.copy_cqe();2347 cqes[0] = try ring.copy_cqe();
2348 cqes[1] = try ring.copy_cqe();2348 cqes[1] = try ring.copy_cqe();
23492349
...@@ -2378,7 +2378,7 @@ test "timeout_remove" {...@@ -2378,7 +2378,7 @@ test "timeout_remove" {
2378}2378}
23792379
2380test "accept/connect/recv/link_timeout" {2380test "accept/connect/recv/link_timeout" {
2381 if (builtin.os.tag != .linux) return error.SkipZigTest;2381 if (!is_linux) return error.SkipZigTest;
23822382
2383 var ring = IoUring.init(16, 0) catch |err| switch (err) {2383 var ring = IoUring.init(16, 0) catch |err| switch (err) {
2384 error.SystemOutdated => return error.SkipZigTest,2384 error.SystemOutdated => return error.SkipZigTest,
...@@ -2395,7 +2395,7 @@ test "accept/connect/recv/link_timeout" {...@@ -2395,7 +2395,7 @@ test "accept/connect/recv/link_timeout" {
2395 const sqe_recv = try ring.recv(0xffffffff, socket_test_harness.server, .{ .buffer = buffer_recv[0..] }, 0);2395 const sqe_recv = try ring.recv(0xffffffff, socket_test_harness.server, .{ .buffer = buffer_recv[0..] }, 0);
2396 sqe_recv.flags |= linux.IOSQE_IO_LINK;2396 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 };
2399 _ = try ring.link_timeout(0x22222222, &ts, 0);2399 _ = try ring.link_timeout(0x22222222, &ts, 0);
24002400
2401 const nr_wait = try ring.submit();2401 const nr_wait = try ring.submit();
...@@ -2427,7 +2427,7 @@ test "accept/connect/recv/link_timeout" {...@@ -2427,7 +2427,7 @@ test "accept/connect/recv/link_timeout" {
2427}2427}
24282428
2429test "fallocate" {2429test "fallocate" {
2430 if (builtin.os.tag != .linux) return error.SkipZigTest;2430 if (!is_linux) return error.SkipZigTest;
24312431
2432 var ring = IoUring.init(1, 0) catch |err| switch (err) {2432 var ring = IoUring.init(1, 0) catch |err| switch (err) {
2433 error.SystemOutdated => return error.SkipZigTest,2433 error.SystemOutdated => return error.SkipZigTest,
...@@ -2473,7 +2473,7 @@ test "fallocate" {...@@ -2473,7 +2473,7 @@ test "fallocate" {
2473}2473}
24742474
2475test "statx" {2475test "statx" {
2476 if (builtin.os.tag != .linux) return error.SkipZigTest;2476 if (!is_linux) return error.SkipZigTest;
24772477
2478 var ring = IoUring.init(1, 0) catch |err| switch (err) {2478 var ring = IoUring.init(1, 0) catch |err| switch (err) {
2479 error.SystemOutdated => return error.SkipZigTest,2479 error.SystemOutdated => return error.SkipZigTest,
...@@ -2525,12 +2525,12 @@ test "statx" {...@@ -2525,12 +2525,12 @@ test "statx" {
2525 .flags = 0,2525 .flags = 0,
2526 }, cqe);2526 }, 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);
2529 try testing.expectEqual(@as(u64, 6), buf.size);2529 try testing.expectEqual(@as(u64, 6), buf.size);
2530}2530}
25312531
2532test "accept/connect/recv/cancel" {2532test "accept/connect/recv/cancel" {
2533 if (builtin.os.tag != .linux) return error.SkipZigTest;2533 if (!is_linux) return error.SkipZigTest;
25342534
2535 var ring = IoUring.init(16, 0) catch |err| switch (err) {2535 var ring = IoUring.init(16, 0) catch |err| switch (err) {
2536 error.SystemOutdated => return error.SkipZigTest,2536 error.SystemOutdated => return error.SkipZigTest,
...@@ -2580,7 +2580,7 @@ test "accept/connect/recv/cancel" {...@@ -2580,7 +2580,7 @@ test "accept/connect/recv/cancel" {
2580}2580}
25812581
2582test "register_files_update" {2582test "register_files_update" {
2583 if (builtin.os.tag != .linux) return error.SkipZigTest;2583 if (!is_linux) return error.SkipZigTest;
25842584
2585 var ring = IoUring.init(1, 0) catch |err| switch (err) {2585 var ring = IoUring.init(1, 0) catch |err| switch (err) {
2586 error.SystemOutdated => return error.SkipZigTest,2586 error.SystemOutdated => return error.SkipZigTest,
...@@ -2589,10 +2589,10 @@ test "register_files_update" {...@@ -2589,10 +2589,10 @@ test "register_files_update" {
2589 };2589 };
2590 defer ring.deinit();2590 defer ring.deinit();
25912591
2592 const fd = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);2592 const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
2593 defer os.close(fd);2593 defer posix.close(fd);
25942594
2595 var registered_fds = [_]os.fd_t{0} ** 2;2595 var registered_fds = [_]posix.fd_t{0} ** 2;
2596 const fd_index = 0;2596 const fd_index = 0;
2597 const fd_index2 = 1;2597 const fd_index2 = 1;
2598 registered_fds[fd_index] = fd;2598 registered_fds[fd_index] = fd;
...@@ -2607,8 +2607,8 @@ test "register_files_update" {...@@ -2607,8 +2607,8 @@ test "register_files_update" {
2607 // Test IORING_REGISTER_FILES_UPDATE2607 // Test IORING_REGISTER_FILES_UPDATE
2608 // Only available since Linux 5.52608 // Only available since Linux 5.5
26092609
2610 const fd2 = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);2610 const fd2 = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
2611 defer os.close(fd2);2611 defer posix.close(fd2);
26122612
2613 registered_fds[fd_index] = fd2;2613 registered_fds[fd_index] = fd2;
2614 registered_fds[fd_index2] = -1;2614 registered_fds[fd_index2] = -1;
...@@ -2660,14 +2660,14 @@ test "register_files_update" {...@@ -2660,14 +2660,14 @@ test "register_files_update" {
26602660
2661 try testing.expectEqual(@as(u32, 1), try ring.submit());2661 try testing.expectEqual(@as(u32, 1), try ring.submit());
2662 const cqe = try ring.copy_cqe();2662 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());
2664 }2664 }
26652665
2666 try ring.unregister_files();2666 try ring.unregister_files();
2667}2667}
26682668
2669test "shutdown" {2669test "shutdown" {
2670 if (builtin.os.tag != .linux) return error.SkipZigTest;2670 if (!is_linux) return error.SkipZigTest;
26712671
2672 var ring = IoUring.init(16, 0) catch |err| switch (err) {2672 var ring = IoUring.init(16, 0) catch |err| switch (err) {
2673 error.SystemOutdated => return error.SkipZigTest,2673 error.SystemOutdated => return error.SkipZigTest,
...@@ -2680,17 +2680,17 @@ test "shutdown" {...@@ -2680,17 +2680,17 @@ test "shutdown" {
26802680
2681 // Socket bound, expect shutdown to work2681 // Socket bound, expect shutdown to work
2682 {2682 {
2683 const server = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);2683 const server = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
2684 defer os.close(server);2684 defer posix.close(server);
2685 try os.setsockopt(server, os.SOL.SOCKET, os.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));2685 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
2686 try os.bind(server, &address.any, address.getOsSockLen());2686 try posix.bind(server, &address.any, address.getOsSockLen());
2687 try os.listen(server, 1);2687 try posix.listen(server, 1);
26882688
2689 // set address to the OS-chosen IP/port.2689 // set address to the OS-chosen IP/port.
2690 var slen: os.socklen_t = address.getOsSockLen();2690 var slen: posix.socklen_t = address.getOsSockLen();
2691 try os.getsockname(server, &address.any, &slen);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);
2694 try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode);2694 try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode);
2695 try testing.expectEqual(@as(i32, server), shutdown_sqe.fd);2695 try testing.expectEqual(@as(i32, server), shutdown_sqe.fd);
26962696
...@@ -2713,10 +2713,10 @@ test "shutdown" {...@@ -2713,10 +2713,10 @@ test "shutdown" {
27132713
2714 // Socket not bound, expect to fail with ENOTCONN2714 // Socket not bound, expect to fail with ENOTCONN
2715 {2715 {
2716 const server = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);2716 const server = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
2717 defer os.close(server);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) {
2720 else => |errno| std.debug.panic("unhandled errno: {}", .{errno}),2720 else => |errno| std.debug.panic("unhandled errno: {}", .{errno}),
2721 };2721 };
2722 try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode);2722 try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode);
...@@ -2726,12 +2726,12 @@ test "shutdown" {...@@ -2726,12 +2726,12 @@ test "shutdown" {
27262726
2727 const cqe = try ring.copy_cqe();2727 const cqe = try ring.copy_cqe();
2728 try testing.expectEqual(@as(u64, 0x445445445), cqe.user_data);2728 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());
2730 }2730 }
2731}2731}
27322732
2733test "renameat" {2733test "renameat" {
2734 if (builtin.os.tag != .linux) return error.SkipZigTest;2734 if (!is_linux) return error.SkipZigTest;
27352735
2736 var ring = IoUring.init(1, 0) catch |err| switch (err) {2736 var ring = IoUring.init(1, 0) catch |err| switch (err) {
2737 error.SystemOutdated => return error.SkipZigTest,2737 error.SystemOutdated => return error.SkipZigTest,
...@@ -2800,7 +2800,7 @@ test "renameat" {...@@ -2800,7 +2800,7 @@ test "renameat" {
2800}2800}
28012801
2802test "unlinkat" {2802test "unlinkat" {
2803 if (builtin.os.tag != .linux) return error.SkipZigTest;2803 if (!is_linux) return error.SkipZigTest;
28042804
2805 var ring = IoUring.init(1, 0) catch |err| switch (err) {2805 var ring = IoUring.init(1, 0) catch |err| switch (err) {
2806 error.SystemOutdated => return error.SkipZigTest,2806 error.SystemOutdated => return error.SkipZigTest,
...@@ -2852,7 +2852,7 @@ test "unlinkat" {...@@ -2852,7 +2852,7 @@ test "unlinkat" {
2852}2852}
28532853
2854test "mkdirat" {2854test "mkdirat" {
2855 if (builtin.os.tag != .linux) return error.SkipZigTest;2855 if (!is_linux) return error.SkipZigTest;
28562856
2857 var ring = IoUring.init(1, 0) catch |err| switch (err) {2857 var ring = IoUring.init(1, 0) catch |err| switch (err) {
2858 error.SystemOutdated => return error.SkipZigTest,2858 error.SystemOutdated => return error.SkipZigTest,
...@@ -2896,7 +2896,7 @@ test "mkdirat" {...@@ -2896,7 +2896,7 @@ test "mkdirat" {
2896}2896}
28972897
2898test "symlinkat" {2898test "symlinkat" {
2899 if (builtin.os.tag != .linux) return error.SkipZigTest;2899 if (!is_linux) return error.SkipZigTest;
29002900
2901 var ring = IoUring.init(1, 0) catch |err| switch (err) {2901 var ring = IoUring.init(1, 0) catch |err| switch (err) {
2902 error.SystemOutdated => return error.SkipZigTest,2902 error.SystemOutdated => return error.SkipZigTest,
...@@ -2944,7 +2944,7 @@ test "symlinkat" {...@@ -2944,7 +2944,7 @@ test "symlinkat" {
2944}2944}
29452945
2946test "linkat" {2946test "linkat" {
2947 if (builtin.os.tag != .linux) return error.SkipZigTest;2947 if (!is_linux) return error.SkipZigTest;
29482948
2949 var ring = IoUring.init(1, 0) catch |err| switch (err) {2949 var ring = IoUring.init(1, 0) catch |err| switch (err) {
2950 error.SystemOutdated => return error.SkipZigTest,2950 error.SystemOutdated => return error.SkipZigTest,
...@@ -3003,7 +3003,7 @@ test "linkat" {...@@ -3003,7 +3003,7 @@ test "linkat" {
3003}3003}
30043004
3005test "provide_buffers: read" {3005test "provide_buffers: read" {
3006 if (builtin.os.tag != .linux) return error.SkipZigTest;3006 if (!is_linux) return error.SkipZigTest;
30073007
3008 var ring = IoUring.init(1, 0) catch |err| switch (err) {3008 var ring = IoUring.init(1, 0) catch |err| switch (err) {
3009 error.SystemOutdated => return error.SkipZigTest,3009 error.SystemOutdated => return error.SkipZigTest,
...@@ -3012,8 +3012,8 @@ test "provide_buffers: read" {...@@ -3012,8 +3012,8 @@ test "provide_buffers: read" {
3012 };3012 };
3013 defer ring.deinit();3013 defer ring.deinit();
30143014
3015 const fd = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);3015 const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
3016 defer os.close(fd);3016 defer posix.close(fd);
30173017
3018 const group_id = 1337;3018 const group_id = 1337;
3019 const buffer_id = 0;3019 const buffer_id = 0;
...@@ -3135,7 +3135,7 @@ test "provide_buffers: read" {...@@ -3135,7 +3135,7 @@ test "provide_buffers: read" {
3135}3135}
31363136
3137test "remove_buffers" {3137test "remove_buffers" {
3138 if (builtin.os.tag != .linux) return error.SkipZigTest;3138 if (!is_linux) return error.SkipZigTest;
31393139
3140 var ring = IoUring.init(1, 0) catch |err| switch (err) {3140 var ring = IoUring.init(1, 0) catch |err| switch (err) {
3141 error.SystemOutdated => return error.SkipZigTest,3141 error.SystemOutdated => return error.SkipZigTest,
...@@ -3144,8 +3144,8 @@ test "remove_buffers" {...@@ -3144,8 +3144,8 @@ test "remove_buffers" {
3144 };3144 };
3145 defer ring.deinit();3145 defer ring.deinit();
31463146
3147 const fd = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);3147 const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
3148 defer os.close(fd);3148 defer posix.close(fd);
31493149
3150 const group_id = 1337;3150 const group_id = 1337;
3151 const buffer_id = 0;3151 const buffer_id = 0;
...@@ -3224,7 +3224,7 @@ test "remove_buffers" {...@@ -3224,7 +3224,7 @@ test "remove_buffers" {
3224}3224}
32253225
3226test "provide_buffers: accept/connect/send/recv" {3226test "provide_buffers: accept/connect/send/recv" {
3227 if (builtin.os.tag != .linux) return error.SkipZigTest;3227 if (!is_linux) return error.SkipZigTest;
32283228
3229 var ring = IoUring.init(16, 0) catch |err| switch (err) {3229 var ring = IoUring.init(16, 0) catch |err| switch (err) {
3230 error.SystemOutdated => return error.SkipZigTest,3230 error.SystemOutdated => return error.SkipZigTest,
...@@ -3391,9 +3391,9 @@ test "provide_buffers: accept/connect/send/recv" {...@@ -3391,9 +3391,9 @@ test "provide_buffers: accept/connect/send/recv" {
33913391
3392/// Used for testing server/client interactions.3392/// Used for testing server/client interactions.
3393const SocketTestHarness = struct {3393const SocketTestHarness = struct {
3394 listener: os.socket_t,3394 listener: posix.socket_t,
3395 server: os.socket_t,3395 server: posix.socket_t,
3396 client: os.socket_t,3396 client: posix.socket_t,
33973397
3398 fn close(self: SocketTestHarness) void {3398 fn close(self: SocketTestHarness) void {
3399 posix.close(self.client);3399 posix.close(self.client);
...@@ -3408,12 +3408,12 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {...@@ -3408,12 +3408,12 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
3408 errdefer posix.close(listener_socket);3408 errdefer posix.close(listener_socket);
34093409
3410 // Submit 1 accept3410 // Submit 1 accept
3411 var accept_addr: os.sockaddr = undefined;3411 var accept_addr: posix.sockaddr = undefined;
3412 var accept_addr_len: os.socklen_t = @sizeOf(@TypeOf(accept_addr));3412 var accept_addr_len: posix.socklen_t = @sizeOf(@TypeOf(accept_addr));
3413 _ = try ring.accept(0xaaaaaaaa, listener_socket, &accept_addr, &accept_addr_len, 0);3413 _ = try ring.accept(0xaaaaaaaa, listener_socket, &accept_addr, &accept_addr_len, 0);
34143414
3415 // Create a TCP client socket3415 // 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);
3417 errdefer posix.close(client);3417 errdefer posix.close(client);
3418 _ = try ring.connect(0xcccccccc, client, &address.any, address.getOsSockLen());3418 _ = try ring.connect(0xcccccccc, client, &address.any, address.getOsSockLen());
34193419
...@@ -3451,24 +3451,24 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {...@@ -3451,24 +3451,24 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
3451 };3451 };
3452}3452}
34533453
3454fn createListenerSocket(address: *net.Address) !os.socket_t {3454fn createListenerSocket(address: *net.Address) !posix.socket_t {
3455 const kernel_backlog = 1;3455 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);
3457 errdefer posix.close(listener_socket);3457 errdefer posix.close(listener_socket);
34583458
3459 try os.setsockopt(listener_socket, os.SOL.SOCKET, os.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));3459 try posix.setsockopt(listener_socket, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
3460 try os.bind(listener_socket, &address.any, address.getOsSockLen());3460 try posix.bind(listener_socket, &address.any, address.getOsSockLen());
3461 try os.listen(listener_socket, kernel_backlog);3461 try posix.listen(listener_socket, kernel_backlog);
34623462
3463 // set address to the OS-chosen IP/port.3463 // set address to the OS-chosen IP/port.
3464 var slen: os.socklen_t = address.getOsSockLen();3464 var slen: posix.socklen_t = address.getOsSockLen();
3465 try os.getsockname(listener_socket, &address.any, &slen);3465 try posix.getsockname(listener_socket, &address.any, &slen);
34663466
3467 return listener_socket;3467 return listener_socket;
3468}3468}
34693469
3470test "accept multishot" {3470test "accept multishot" {
3471 if (builtin.os.tag != .linux) return error.SkipZigTest;3471 if (!is_linux) return error.SkipZigTest;
34723472
3473 var ring = IoUring.init(16, 0) catch |err| switch (err) {3473 var ring = IoUring.init(16, 0) catch |err| switch (err) {
3474 error.SystemOutdated => return error.SkipZigTest,3474 error.SystemOutdated => return error.SkipZigTest,
...@@ -3482,8 +3482,8 @@ test "accept multishot" {...@@ -3482,8 +3482,8 @@ test "accept multishot" {
3482 defer posix.close(listener_socket);3482 defer posix.close(listener_socket);
34833483
3484 // submit multishot accept operation3484 // submit multishot accept operation
3485 var addr: os.sockaddr = undefined;3485 var addr: posix.sockaddr = undefined;
3486 var addr_len: os.socklen_t = @sizeOf(@TypeOf(addr));3486 var addr_len: posix.socklen_t = @sizeOf(@TypeOf(addr));
3487 const userdata: u64 = 0xaaaaaaaa;3487 const userdata: u64 = 0xaaaaaaaa;
3488 _ = try ring.accept_multishot(userdata, listener_socket, &addr, &addr_len, 0);3488 _ = try ring.accept_multishot(userdata, listener_socket, &addr, &addr_len, 0);
3489 try testing.expectEqual(@as(u32, 1), try ring.submit());3489 try testing.expectEqual(@as(u32, 1), try ring.submit());
...@@ -3491,9 +3491,9 @@ test "accept multishot" {...@@ -3491,9 +3491,9 @@ test "accept multishot" {
3491 var nr: usize = 4; // number of clients to connect3491 var nr: usize = 4; // number of clients to connect
3492 while (nr > 0) : (nr -= 1) {3492 while (nr > 0) : (nr -= 1) {
3493 // connect client3493 // 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);
3495 errdefer posix.close(client);3495 errdefer posix.close(client);
3496 try os.connect(client, &address.any, address.getOsSockLen());3496 try posix.connect(client, &address.any, address.getOsSockLen());
34973497
3498 // test accept completion3498 // test accept completion
3499 var cqe = try ring.copy_cqe();3499 var cqe = try ring.copy_cqe();
...@@ -3571,7 +3571,7 @@ test "accept_direct" {...@@ -3571,7 +3571,7 @@ test "accept_direct" {
3571 var address = try net.Address.parseIp4("127.0.0.1", 0);3571 var address = try net.Address.parseIp4("127.0.0.1", 0);
35723572
3573 // register direct file descriptors3573 // register direct file descriptors
3574 var registered_fds = [_]os.fd_t{-1} ** 2;3574 var registered_fds = [_]posix.fd_t{-1} ** 2;
3575 try ring.register_files(registered_fds[0..]);3575 try ring.register_files(registered_fds[0..]);
35763576
3577 const listener_socket = try createListenerSocket(&address);3577 const listener_socket = try createListenerSocket(&address);
...@@ -3591,19 +3591,19 @@ test "accept_direct" {...@@ -3591,19 +3591,19 @@ test "accept_direct" {
3591 try testing.expectEqual(@as(u32, 1), try ring.submit());3591 try testing.expectEqual(@as(u32, 1), try ring.submit());
35923592
3593 // connect3593 // connect
3594 const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);3594 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3595 try os.connect(client, &address.any, address.getOsSockLen());3595 try posix.connect(client, &address.any, address.getOsSockLen());
3596 defer posix.close(client);3596 defer posix.close(client);
35973597
3598 // accept completion3598 // accept completion
3599 const cqe_accept = try ring.copy_cqe();3599 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());
3601 const fd_index = cqe_accept.res;3601 const fd_index = cqe_accept.res;
3602 try testing.expect(fd_index < registered_fds.len);3602 try testing.expect(fd_index < registered_fds.len);
3603 try testing.expect(cqe_accept.user_data == accept_userdata);3603 try testing.expect(cqe_accept.user_data == accept_userdata);
36043604
3605 // send data3605 // send data
3606 _ = try os.send(client, buffer_send, 0);3606 _ = try posix.send(client, buffer_send, 0);
36073607
3608 // Example of how to use registered fd:3608 // Example of how to use registered fd:
3609 // Submit receive to fixed file returned by accept (fd_index).3609 // Submit receive to fixed file returned by accept (fd_index).
...@@ -3625,13 +3625,13 @@ test "accept_direct" {...@@ -3625,13 +3625,13 @@ test "accept_direct" {
3625 _ = try ring.accept_direct(accept_userdata, listener_socket, null, null, 0);3625 _ = try ring.accept_direct(accept_userdata, listener_socket, null, null, 0);
3626 try testing.expectEqual(@as(u32, 1), try ring.submit());3626 try testing.expectEqual(@as(u32, 1), try ring.submit());
3627 // connect3627 // connect
3628 const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);3628 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3629 try os.connect(client, &address.any, address.getOsSockLen());3629 try posix.connect(client, &address.any, address.getOsSockLen());
3630 defer posix.close(client);3630 defer posix.close(client);
3631 // completion with error3631 // completion with error
3632 const cqe_accept = try ring.copy_cqe();3632 const cqe_accept = try ring.copy_cqe();
3633 try testing.expect(cqe_accept.user_data == accept_userdata);3633 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());
3635 }3635 }
3636 // return file descriptors to kernel3636 // return file descriptors to kernel
3637 try ring.register_files_update(0, registered_fds[0..]);3637 try ring.register_files_update(0, registered_fds[0..]);
...@@ -3651,7 +3651,7 @@ test "accept_multishot_direct" {...@@ -3651,7 +3651,7 @@ test "accept_multishot_direct" {
36513651
3652 var address = try net.Address.parseIp4("127.0.0.1", 0);3652 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;
3655 try ring.register_files(registered_fds[0..]);3655 try ring.register_files(registered_fds[0..]);
36563656
3657 const listener_socket = try createListenerSocket(&address);3657 const listener_socket = try createListenerSocket(&address);
...@@ -3667,8 +3667,8 @@ test "accept_multishot_direct" {...@@ -3667,8 +3667,8 @@ test "accept_multishot_direct" {
36673667
3668 for (registered_fds) |_| {3668 for (registered_fds) |_| {
3669 // connect3669 // connect
3670 const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);3670 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3671 try os.connect(client, &address.any, address.getOsSockLen());3671 try posix.connect(client, &address.any, address.getOsSockLen());
3672 defer posix.close(client);3672 defer posix.close(client);
36733673
3674 // accept completion3674 // accept completion
...@@ -3682,13 +3682,13 @@ test "accept_multishot_direct" {...@@ -3682,13 +3682,13 @@ test "accept_multishot_direct" {
3682 // Multishot is terminated (more flag is not set).3682 // Multishot is terminated (more flag is not set).
3683 {3683 {
3684 // connect3684 // connect
3685 const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0);3685 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3686 try os.connect(client, &address.any, address.getOsSockLen());3686 try posix.connect(client, &address.any, address.getOsSockLen());
3687 defer posix.close(client);3687 defer posix.close(client);
3688 // completion with error3688 // completion with error
3689 const cqe_accept = try ring.copy_cqe();3689 const cqe_accept = try ring.copy_cqe();
3690 try testing.expect(cqe_accept.user_data == accept_userdata);3690 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());
3692 try testing.expect(cqe_accept.flags & linux.IORING_CQE_F_MORE == 0); // has more is not set3692 try testing.expect(cqe_accept.flags & linux.IORING_CQE_F_MORE == 0); // has more is not set
3693 }3693 }
3694 // return file descriptors to kernel3694 // return file descriptors to kernel
...@@ -3708,16 +3708,16 @@ test "socket" {...@@ -3708,16 +3708,16 @@ test "socket" {
3708 defer ring.deinit();3708 defer ring.deinit();
37093709
3710 // prepare, submit socket operation3710 // 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);
3712 try testing.expectEqual(@as(u32, 1), try ring.submit());3712 try testing.expectEqual(@as(u32, 1), try ring.submit());
37133713
3714 // test completion3714 // test completion
3715 var cqe = try ring.copy_cqe();3715 var cqe = try ring.copy_cqe();
3716 try testing.expectEqual(os.E.SUCCESS, cqe.err());3716 try testing.expectEqual(posix.E.SUCCESS, cqe.err());
3717 const fd: os.fd_t = @intCast(cqe.res);3717 const fd: posix.fd_t = @intCast(cqe.res);
3718 try testing.expect(fd > 2);3718 try testing.expect(fd > 2);
37193719
3720 os.close(fd);3720 posix.close(fd);
3721}3721}
37223722
3723test "socket_direct/socket_direct_alloc/close_direct" {3723test "socket_direct/socket_direct_alloc/close_direct" {
...@@ -3730,29 +3730,29 @@ test "socket_direct/socket_direct_alloc/close_direct" {...@@ -3730,29 +3730,29 @@ test "socket_direct/socket_direct_alloc/close_direct" {
3730 };3730 };
3731 defer ring.deinit();3731 defer ring.deinit();
37323732
3733 var registered_fds = [_]os.fd_t{-1} ** 3;3733 var registered_fds = [_]posix.fd_t{-1} ** 3;
3734 try ring.register_files(registered_fds[0..]);3734 try ring.register_files(registered_fds[0..]);
37353735
3736 // create socket in registered file descriptor at index 0 (last param)3736 // 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);
3738 try testing.expectEqual(@as(u32, 1), try ring.submit());3738 try testing.expectEqual(@as(u32, 1), try ring.submit());
3739 var cqe_socket = try ring.copy_cqe();3739 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());
3741 try testing.expect(cqe_socket.res == 0);3741 try testing.expect(cqe_socket.res == 0);
37423742
3743 // create socket in registered file descriptor at index 1 (last param)3743 // 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);
3745 try testing.expectEqual(@as(u32, 1), try ring.submit());3745 try testing.expectEqual(@as(u32, 1), try ring.submit());
3746 cqe_socket = try ring.copy_cqe();3746 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());
3748 try testing.expect(cqe_socket.res == 0); // res is 0 when index is specified3748 try testing.expect(cqe_socket.res == 0); // res is 0 when index is specified
37493749
3750 // create socket in kernel chosen file descriptor index (_alloc version)3750 // create socket in kernel chosen file descriptor index (_alloc version)
3751 // completion res has index from registered files3751 // 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);
3753 try testing.expectEqual(@as(u32, 1), try ring.submit());3753 try testing.expectEqual(@as(u32, 1), try ring.submit());
3754 cqe_socket = try ring.copy_cqe();3754 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());
3756 try testing.expect(cqe_socket.res == 2); // returns registered file index3756 try testing.expect(cqe_socket.res == 2); // returns registered file index
37573757
3758 // use sockets from registered_fds in connect operation3758 // use sockets from registered_fds in connect operation
...@@ -3782,17 +3782,17 @@ test "socket_direct/socket_direct_alloc/close_direct" {...@@ -3782,17 +3782,17 @@ test "socket_direct/socket_direct_alloc/close_direct" {
3782 }3782 }
3783 // test connect completion3783 // test connect completion
3784 try testing.expect(cqe_connect.user_data == connect_userdata);3784 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());
3786 // test accept completion3786 // test accept completion
3787 try testing.expect(cqe_accept.user_data == accept_userdata);3787 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
3790 // submit and test close_direct3790 // submit and test close_direct
3791 _ = try ring.close_direct(close_userdata, @intCast(fd_index));3791 _ = try ring.close_direct(close_userdata, @intCast(fd_index));
3792 try testing.expectEqual(@as(u32, 1), try ring.submit());3792 try testing.expectEqual(@as(u32, 1), try ring.submit());
3793 var cqe_close = try ring.copy_cqe();3793 var cqe_close = try ring.copy_cqe();
3794 try testing.expect(cqe_close.user_data == close_userdata);3794 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());
3796 }3796 }
37973797
3798 try ring.unregister_files();3798 try ring.unregister_files();
...@@ -3808,35 +3808,35 @@ test "openat_direct/close_direct" {...@@ -3808,35 +3808,35 @@ test "openat_direct/close_direct" {
3808 };3808 };
3809 defer ring.deinit();3809 defer ring.deinit();
38103810
3811 var registered_fds = [_]os.fd_t{-1} ** 3;3811 var registered_fds = [_]posix.fd_t{-1} ** 3;
3812 try ring.register_files(registered_fds[0..]);3812 try ring.register_files(registered_fds[0..]);
38133813
3814 var tmp = std.testing.tmpDir(.{});3814 var tmp = std.testing.tmpDir(.{});
3815 defer tmp.cleanup();3815 defer tmp.cleanup();
3816 const path = "test_io_uring_close_direct";3816 const path = "test_io_uring_close_direct";
3817 const flags: linux.O = .{ .ACCMODE = .RDWR, .CREAT = true };3817 const flags: linux.O = .{ .ACCMODE = .RDWR, .CREAT = true };
3818 const mode: os.mode_t = 0o666;3818 const mode: posix.mode_t = 0o666;
3819 const user_data: u64 = 0;3819 const user_data: u64 = 0;
38203820
3821 // use registered file at index 0 (last param)3821 // use registered file at index 0 (last param)
3822 _ = try ring.openat_direct(user_data, tmp.dir.fd, path, flags, mode, 0);3822 _ = try ring.openat_direct(user_data, tmp.dir.fd, path, flags, mode, 0);
3823 try testing.expectEqual(@as(u32, 1), try ring.submit());3823 try testing.expectEqual(@as(u32, 1), try ring.submit());
3824 var cqe = try ring.copy_cqe();3824 var cqe = try ring.copy_cqe();
3825 try testing.expectEqual(os.E.SUCCESS, cqe.err());3825 try testing.expectEqual(posix.E.SUCCESS, cqe.err());
3826 try testing.expect(cqe.res == 0);3826 try testing.expect(cqe.res == 0);
38273827
3828 // use registered file at index 13828 // use registered file at index 1
3829 _ = try ring.openat_direct(user_data, tmp.dir.fd, path, flags, mode, 1);3829 _ = try ring.openat_direct(user_data, tmp.dir.fd, path, flags, mode, 1);
3830 try testing.expectEqual(@as(u32, 1), try ring.submit());3830 try testing.expectEqual(@as(u32, 1), try ring.submit());
3831 cqe = try ring.copy_cqe();3831 cqe = try ring.copy_cqe();
3832 try testing.expectEqual(os.E.SUCCESS, cqe.err());3832 try testing.expectEqual(posix.E.SUCCESS, cqe.err());
3833 try testing.expect(cqe.res == 0); // res is 0 when we specify index3833 try testing.expect(cqe.res == 0); // res is 0 when we specify index
38343834
3835 // let kernel choose registered file index3835 // let kernel choose registered file index
3836 _ = try ring.openat_direct(user_data, tmp.dir.fd, path, flags, mode, linux.IORING_FILE_INDEX_ALLOC);3836 _ = try ring.openat_direct(user_data, tmp.dir.fd, path, flags, mode, linux.IORING_FILE_INDEX_ALLOC);
3837 try testing.expectEqual(@as(u32, 1), try ring.submit());3837 try testing.expectEqual(@as(u32, 1), try ring.submit());
3838 cqe = try ring.copy_cqe();3838 cqe = try ring.copy_cqe();
3839 try testing.expectEqual(os.E.SUCCESS, cqe.err());3839 try testing.expectEqual(posix.E.SUCCESS, cqe.err());
3840 try testing.expect(cqe.res == 2); // chosen index is in res3840 try testing.expect(cqe.res == 2); // chosen index is in res
38413841
3842 // close all open file descriptors3842 // close all open file descriptors
...@@ -3844,7 +3844,7 @@ test "openat_direct/close_direct" {...@@ -3844,7 +3844,7 @@ test "openat_direct/close_direct" {
3844 _ = try ring.close_direct(user_data, @intCast(fd_index));3844 _ = try ring.close_direct(user_data, @intCast(fd_index));
3845 try testing.expectEqual(@as(u32, 1), try ring.submit());3845 try testing.expectEqual(@as(u32, 1), try ring.submit());
3846 var cqe_close = try ring.copy_cqe();3846 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());
3848 }3848 }
3849 try ring.unregister_files();3849 try ring.unregister_files();
3850}3850}
...@@ -3859,13 +3859,13 @@ test "waitid" {...@@ -3859,13 +3859,13 @@ test "waitid" {
3859 };3859 };
3860 defer ring.deinit();3860 defer ring.deinit();
38613861
3862 const pid = try os.fork();3862 const pid = try posix.fork();
3863 if (pid == 0) {3863 if (pid == 0) {
3864 os.exit(7);3864 posix.exit(7);
3865 }3865 }
38663866
3867 var siginfo: os.siginfo_t = undefined;3867 var siginfo: posix.siginfo_t = undefined;
3868 _ = try ring.waitid(0, .PID, pid, &siginfo, os.W.EXITED, 0);3868 _ = try ring.waitid(0, .PID, pid, &siginfo, posix.W.EXITED, 0);
38693869
3870 try testing.expectEqual(1, try ring.submit());3870 try testing.expectEqual(1, try ring.submit());
38713871
...@@ -3877,13 +3877,13 @@ test "waitid" {...@@ -3877,13 +3877,13 @@ test "waitid" {
38773877
3878/// For use in tests. Returns SkipZigTest if kernel version is less than required.3878/// For use in tests. Returns SkipZigTest if kernel version is less than required.
3879inline fn skipKernelLessThan(required: std.SemanticVersion) !void {3879inline fn skipKernelLessThan(required: std.SemanticVersion) !void {
3880 if (builtin.os.tag != .linux) return error.SkipZigTest;3880 if (!is_linux) return error.SkipZigTest;
38813881
3882 var uts: linux.utsname = undefined;3882 var uts: linux.utsname = undefined;
3883 const res = linux.uname(&uts);3883 const res = linux.uname(&uts);
3884 switch (linux.getErrno(res)) {3884 switch (linux.E.init(res)) {
3885 .SUCCESS => {},3885 .SUCCESS => {},
3886 else => |errno| return os.unexpectedErrno(errno),3886 else => |errno| return posix.unexpectedErrno(errno),
3887 }3887 }
38883888
3889 const release = mem.sliceTo(&uts.release, 0);3889 const release = mem.sliceTo(&uts.release, 0);
...@@ -3893,7 +3893,7 @@ inline fn skipKernelLessThan(required: std.SemanticVersion) !void {...@@ -3893,7 +3893,7 @@ inline fn skipKernelLessThan(required: std.SemanticVersion) !void {
3893}3893}
38943894
3895test BufferGroup {3895test BufferGroup {
3896 if (builtin.os.tag != .linux) return error.SkipZigTest;3896 if (!is_linux) return error.SkipZigTest;
38973897
3898 // Init IoUring3898 // Init IoUring
3899 var ring = IoUring.init(16, 0) catch |err| switch (err) {3899 var ring = IoUring.init(16, 0) catch |err| switch (err) {
...@@ -3948,7 +3948,7 @@ test BufferGroup {...@@ -3948,7 +3948,7 @@ test BufferGroup {
3948 const cqe = try ring.copy_cqe();3948 const cqe = try ring.copy_cqe();
3949 try testing.expectEqual(2, cqe.user_data); // matches submitted user_data3949 try testing.expectEqual(2, cqe.user_data); // matches submitted user_data
3950 try testing.expect(cqe.res >= 0); // success3950 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());
3952 try testing.expectEqual(data.len, @as(usize, @intCast(cqe.res))); // cqe.res holds received data len3952 try testing.expectEqual(data.len, @as(usize, @intCast(cqe.res))); // cqe.res holds received data len
39533953
3954 // Read buffer_id and used buffer len from cqe3954 // Read buffer_id and used buffer len from cqe
...@@ -3963,7 +3963,7 @@ test BufferGroup {...@@ -3963,7 +3963,7 @@ test BufferGroup {
3963}3963}
39643964
3965test "ring mapped buffers recv" {3965test "ring mapped buffers recv" {
3966 if (builtin.os.tag != .linux) return error.SkipZigTest;3966 if (!is_linux) return error.SkipZigTest;
39673967
3968 var ring = IoUring.init(16, 0) catch |err| switch (err) {3968 var ring = IoUring.init(16, 0) catch |err| switch (err) {
3969 error.SystemOutdated => return error.SkipZigTest,3969 error.SystemOutdated => return error.SkipZigTest,
...@@ -4029,7 +4029,7 @@ test "ring mapped buffers recv" {...@@ -4029,7 +4029,7 @@ test "ring mapped buffers recv" {
4029 const cqe = try ring.copy_cqe();4029 const cqe = try ring.copy_cqe();
4030 try testing.expectEqual(user_data, cqe.user_data);4030 try testing.expectEqual(user_data, cqe.user_data);
4031 try testing.expect(cqe.res < 0); // fail4031 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());
4033 try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == 0); // IORING_CQE_F_BUFFER flags is set on success only4033 try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == 0); // IORING_CQE_F_BUFFER flags is set on success only
4034 try testing.expectError(error.NoBufferSelected, cqe.buffer_id());4034 try testing.expectError(error.NoBufferSelected, cqe.buffer_id());
4035 }4035 }
...@@ -4049,7 +4049,7 @@ test "ring mapped buffers recv" {...@@ -4049,7 +4049,7 @@ test "ring mapped buffers recv" {
4049}4049}
40504050
4051test "ring mapped buffers multishot recv" {4051test "ring mapped buffers multishot recv" {
4052 if (builtin.os.tag != .linux) return error.SkipZigTest;4052 if (!is_linux) return error.SkipZigTest;
40534053
4054 var ring = IoUring.init(16, 0) catch |err| switch (err) {4054 var ring = IoUring.init(16, 0) catch |err| switch (err) {
4055 error.SystemOutdated => return error.SkipZigTest,4055 error.SystemOutdated => return error.SkipZigTest,
...@@ -4120,7 +4120,7 @@ test "ring mapped buffers multishot recv" {...@@ -4120,7 +4120,7 @@ test "ring mapped buffers multishot recv" {
4120 const cqe = try ring.copy_cqe();4120 const cqe = try ring.copy_cqe();
4121 try testing.expectEqual(recv_user_data, cqe.user_data);4121 try testing.expectEqual(recv_user_data, cqe.user_data);
4122 try testing.expect(cqe.res < 0); // fail4122 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());
4124 try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == 0); // IORING_CQE_F_BUFFER flags is set on success only4124 try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == 0); // IORING_CQE_F_BUFFER flags is set on success only
4125 // has more is not set4125 // has more is not set
4126 // indicates that multishot is finished4126 // indicates that multishot is finished
...@@ -4194,7 +4194,7 @@ test "ring mapped buffers multishot recv" {...@@ -4194,7 +4194,7 @@ test "ring mapped buffers multishot recv" {
4194fn expect_buf_grp_recv(4194fn expect_buf_grp_recv(
4195 ring: *IoUring,4195 ring: *IoUring,
4196 buf_grp: *BufferGroup,4196 buf_grp: *BufferGroup,
4197 fd: os.fd_t,4197 fd: posix.fd_t,
4198 user_data: u64,4198 user_data: u64,
4199 expected: []const u8,4199 expected: []const u8,
4200) !u16 {4200) !u16 {
...@@ -4220,7 +4220,7 @@ fn expect_buf_grp_cqe(...@@ -4220,7 +4220,7 @@ fn expect_buf_grp_cqe(
4220 try testing.expect(cqe.res >= 0); // success4220 try testing.expect(cqe.res >= 0); // success
4221 try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == linux.IORING_CQE_F_BUFFER); // IORING_CQE_F_BUFFER flag is set4221 try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == linux.IORING_CQE_F_BUFFER); // IORING_CQE_F_BUFFER flag is set
4222 try testing.expectEqual(expected.len, @as(usize, @intCast(cqe.res)));4222 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
4225 // get buffer from pool4225 // get buffer from pool
4226 const buffer_id = try cqe.buffer_id();4226 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");...@@ -2,8 +2,8 @@ const std = @import("../../std.zig");
2const maxInt = std.math.maxInt;2const maxInt = std.math.maxInt;
3const linux = std.os.linux;3const linux = std.os.linux;
4const SYS = linux.SYS;4const SYS = linux.SYS;
5const iovec = std.os.iovec;5const iovec = std.posix.iovec;
6const iovec_const = std.os.iovec_const;6const iovec_const = std.posix.iovec_const;
7const socklen_t = linux.socklen_t;7const socklen_t = linux.socklen_t;
8const stack_t = linux.stack_t;8const stack_t = linux.stack_t;
9const sigset_t = linux.sigset_t;9const sigset_t = linux.sigset_t;
lib/std/os/linux/arm64.zig+2-2
...@@ -4,8 +4,8 @@ const linux = std.os.linux;...@@ -4,8 +4,8 @@ const linux = std.os.linux;
4const SYS = linux.SYS;4const SYS = linux.SYS;
5const socklen_t = linux.socklen_t;5const socklen_t = linux.socklen_t;
6const sockaddr = linux.sockaddr;6const sockaddr = linux.sockaddr;
7const iovec = std.os.iovec;7const iovec = std.posix.iovec;
8const iovec_const = std.os.iovec_const;8const iovec_const = std.posix.iovec_const;
9const uid_t = linux.uid_t;9const uid_t = linux.uid_t;
10const gid_t = linux.gid_t;10const gid_t = linux.gid_t;
11const pid_t = linux.pid_t;11const pid_t = linux.pid_t;
lib/std/os/linux/bpf.zig+1-2
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1const std = @import("../../std.zig");1const std = @import("../../std.zig");
2const errno = getErrno;2const errno = linux.E.init;
3const unexpectedErrno = std.os.unexpectedErrno;3const unexpectedErrno = std.os.unexpectedErrno;
4const expectEqual = std.testing.expectEqual;4const expectEqual = std.testing.expectEqual;
5const expectError = std.testing.expectError;5const expectError = std.testing.expectError;
...@@ -8,7 +8,6 @@ const expect = std.testing.expect;...@@ -8,7 +8,6 @@ const expect = std.testing.expect;
8const linux = std.os.linux;8const linux = std.os.linux;
9const fd_t = linux.fd_t;9const fd_t = linux.fd_t;
10const pid_t = linux.pid_t;10const pid_t = linux.pid_t;
11const getErrno = linux.getErrno;
1211
13pub const btf = @import("bpf/btf.zig");12pub const btf = @import("bpf/btf.zig");
14pub const kern = @import("bpf/kern.zig");13pub 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 @@...@@ -2,8 +2,7 @@
2//! Split into its own file to compartmentalize the initialization methods.2//! Split into its own file to compartmentalize the initialization methods.
33
4const std = @import("../../std.zig");4const std = @import("../../std.zig");
5const os = std.os;5const linux = std.os.linux;
6const linux = os.linux;
76
8pub const io_uring_sqe = extern struct {7pub const io_uring_sqe = extern struct {
9 opcode: linux.IORING_OP,8 opcode: linux.IORING_OP,
...@@ -40,7 +39,7 @@ pub const io_uring_sqe = extern struct {...@@ -40,7 +39,7 @@ pub const io_uring_sqe = extern struct {
40 };39 };
41 }40 }
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 {
44 sqe.* = .{43 sqe.* = .{
45 .opcode = .FSYNC,44 .opcode = .FSYNC,
46 .flags = 0,45 .flags = 0,
...@@ -62,7 +61,7 @@ pub const io_uring_sqe = extern struct {...@@ -62,7 +61,7 @@ pub const io_uring_sqe = extern struct {
62 pub fn prep_rw(61 pub fn prep_rw(
63 sqe: *linux.io_uring_sqe,62 sqe: *linux.io_uring_sqe,
64 op: linux.IORING_OP,63 op: linux.IORING_OP,
65 fd: os.fd_t,64 fd: linux.fd_t,
66 addr: u64,65 addr: u64,
67 len: usize,66 len: usize,
68 offset: u64,67 offset: u64,
...@@ -85,15 +84,15 @@ pub const io_uring_sqe = extern struct {...@@ -85,15 +84,15 @@ pub const io_uring_sqe = extern struct {
85 };84 };
86 }85 }
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 {
89 sqe.prep_rw(.READ, fd, @intFromPtr(buffer.ptr), buffer.len, offset);88 sqe.prep_rw(.READ, fd, @intFromPtr(buffer.ptr), buffer.len, offset);
90 }89 }
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 {
93 sqe.prep_rw(.WRITE, fd, @intFromPtr(buffer.ptr), buffer.len, offset);92 sqe.prep_rw(.WRITE, fd, @intFromPtr(buffer.ptr), buffer.len, offset);
94 }93 }
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 {
97 sqe.prep_rw(.SPLICE, fd_out, undefined, len, off_out);96 sqe.prep_rw(.SPLICE, fd_out, undefined, len, off_out);
98 sqe.addr = off_in;97 sqe.addr = off_in;
99 sqe.splice_fd_in = fd_in;98 sqe.splice_fd_in = fd_in;
...@@ -101,8 +100,8 @@ pub const io_uring_sqe = extern struct {...@@ -101,8 +100,8 @@ pub const io_uring_sqe = extern struct {
101100
102 pub fn prep_readv(101 pub fn prep_readv(
103 sqe: *linux.io_uring_sqe,102 sqe: *linux.io_uring_sqe,
104 fd: os.fd_t,103 fd: linux.fd_t,
105 iovecs: []const os.iovec,104 iovecs: []const std.posix.iovec,
106 offset: u64,105 offset: u64,
107 ) void {106 ) void {
108 sqe.prep_rw(.READV, fd, @intFromPtr(iovecs.ptr), iovecs.len, offset);107 sqe.prep_rw(.READV, fd, @intFromPtr(iovecs.ptr), iovecs.len, offset);
...@@ -110,28 +109,28 @@ pub const io_uring_sqe = extern struct {...@@ -110,28 +109,28 @@ pub const io_uring_sqe = extern struct {
110109
111 pub fn prep_writev(110 pub fn prep_writev(
112 sqe: *linux.io_uring_sqe,111 sqe: *linux.io_uring_sqe,
113 fd: os.fd_t,112 fd: linux.fd_t,
114 iovecs: []const os.iovec_const,113 iovecs: []const std.posix.iovec_const,
115 offset: u64,114 offset: u64,
116 ) void {115 ) void {
117 sqe.prep_rw(.WRITEV, fd, @intFromPtr(iovecs.ptr), iovecs.len, offset);116 sqe.prep_rw(.WRITEV, fd, @intFromPtr(iovecs.ptr), iovecs.len, offset);
118 }117 }
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 {
121 sqe.prep_rw(.READ_FIXED, fd, @intFromPtr(buffer.iov_base), buffer.iov_len, offset);120 sqe.prep_rw(.READ_FIXED, fd, @intFromPtr(buffer.iov_base), buffer.iov_len, offset);
122 sqe.buf_index = buffer_index;121 sqe.buf_index = buffer_index;
123 }122 }
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 {
126 sqe.prep_rw(.WRITE_FIXED, fd, @intFromPtr(buffer.iov_base), buffer.iov_len, offset);125 sqe.prep_rw(.WRITE_FIXED, fd, @intFromPtr(buffer.iov_base), buffer.iov_len, offset);
127 sqe.buf_index = buffer_index;126 sqe.buf_index = buffer_index;
128 }127 }
129128
130 pub fn prep_accept(129 pub fn prep_accept(
131 sqe: *linux.io_uring_sqe,130 sqe: *linux.io_uring_sqe,
132 fd: os.fd_t,131 fd: linux.fd_t,
133 addr: ?*os.sockaddr,132 addr: ?*linux.sockaddr,
134 addrlen: ?*os.socklen_t,133 addrlen: ?*linux.socklen_t,
135 flags: u32,134 flags: u32,
136 ) void {135 ) void {
137 // `addr` holds a pointer to `sockaddr`, and `addr2` holds a pointer to socklen_t`.136 // `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 {...@@ -142,9 +141,9 @@ pub const io_uring_sqe = extern struct {
142141
143 pub fn prep_accept_direct(142 pub fn prep_accept_direct(
144 sqe: *linux.io_uring_sqe,143 sqe: *linux.io_uring_sqe,
145 fd: os.fd_t,144 fd: linux.fd_t,
146 addr: ?*os.sockaddr,145 addr: ?*linux.sockaddr,
147 addrlen: ?*os.socklen_t,146 addrlen: ?*linux.socklen_t,
148 flags: u32,147 flags: u32,
149 file_index: u32,148 file_index: u32,
150 ) void {149 ) void {
...@@ -154,9 +153,9 @@ pub const io_uring_sqe = extern struct {...@@ -154,9 +153,9 @@ pub const io_uring_sqe = extern struct {
154153
155 pub fn prep_multishot_accept_direct(154 pub fn prep_multishot_accept_direct(
156 sqe: *linux.io_uring_sqe,155 sqe: *linux.io_uring_sqe,
157 fd: os.fd_t,156 fd: linux.fd_t,
158 addr: ?*os.sockaddr,157 addr: ?*linux.sockaddr,
159 addrlen: ?*os.socklen_t,158 addrlen: ?*linux.socklen_t,
160 flags: u32,159 flags: u32,
161 ) void {160 ) void {
162 prep_multishot_accept(sqe, fd, addr, addrlen, flags);161 prep_multishot_accept(sqe, fd, addr, addrlen, flags);
...@@ -177,9 +176,9 @@ pub const io_uring_sqe = extern struct {...@@ -177,9 +176,9 @@ pub const io_uring_sqe = extern struct {
177176
178 pub fn prep_connect(177 pub fn prep_connect(
179 sqe: *linux.io_uring_sqe,178 sqe: *linux.io_uring_sqe,
180 fd: os.fd_t,179 fd: linux.fd_t,
181 addr: *const os.sockaddr,180 addr: *const linux.sockaddr,
182 addrlen: os.socklen_t,181 addrlen: linux.socklen_t,
183 ) void {182 ) void {
184 // `addrlen` maps to `sqe.off` (u64) instead of `sqe.len` (which is only a u32).183 // `addrlen` maps to `sqe.off` (u64) instead of `sqe.len` (which is only a u32).
185 sqe.prep_rw(.CONNECT, fd, @intFromPtr(addr), 0, addrlen);184 sqe.prep_rw(.CONNECT, fd, @intFromPtr(addr), 0, addrlen);
...@@ -187,22 +186,22 @@ pub const io_uring_sqe = extern struct {...@@ -187,22 +186,22 @@ pub const io_uring_sqe = extern struct {
187186
188 pub fn prep_epoll_ctl(187 pub fn prep_epoll_ctl(
189 sqe: *linux.io_uring_sqe,188 sqe: *linux.io_uring_sqe,
190 epfd: os.fd_t,189 epfd: linux.fd_t,
191 fd: os.fd_t,190 fd: linux.fd_t,
192 op: u32,191 op: u32,
193 ev: ?*linux.epoll_event,192 ev: ?*linux.epoll_event,
194 ) void {193 ) void {
195 sqe.prep_rw(.EPOLL_CTL, epfd, @intFromPtr(ev), op, @intCast(fd));194 sqe.prep_rw(.EPOLL_CTL, epfd, @intFromPtr(ev), op, @intCast(fd));
196 }195 }
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 {
199 sqe.prep_rw(.RECV, fd, @intFromPtr(buffer.ptr), buffer.len, 0);198 sqe.prep_rw(.RECV, fd, @intFromPtr(buffer.ptr), buffer.len, 0);
200 sqe.rw_flags = flags;199 sqe.rw_flags = flags;
201 }200 }
202201
203 pub fn prep_recv_multishot(202 pub fn prep_recv_multishot(
204 sqe: *linux.io_uring_sqe,203 sqe: *linux.io_uring_sqe,
205 fd: os.fd_t,204 fd: linux.fd_t,
206 buffer: []u8,205 buffer: []u8,
207 flags: u32,206 flags: u32,
208 ) void {207 ) void {
...@@ -212,8 +211,8 @@ pub const io_uring_sqe = extern struct {...@@ -212,8 +211,8 @@ pub const io_uring_sqe = extern struct {
212211
213 pub fn prep_recvmsg(212 pub fn prep_recvmsg(
214 sqe: *linux.io_uring_sqe,213 sqe: *linux.io_uring_sqe,
215 fd: os.fd_t,214 fd: linux.fd_t,
216 msg: *os.msghdr,215 msg: *linux.msghdr,
217 flags: u32,216 flags: u32,
218 ) void {217 ) void {
219 sqe.prep_rw(.RECVMSG, fd, @intFromPtr(msg), 1, 0);218 sqe.prep_rw(.RECVMSG, fd, @intFromPtr(msg), 1, 0);
...@@ -222,26 +221,26 @@ pub const io_uring_sqe = extern struct {...@@ -222,26 +221,26 @@ pub const io_uring_sqe = extern struct {
222221
223 pub fn prep_recvmsg_multishot(222 pub fn prep_recvmsg_multishot(
224 sqe: *linux.io_uring_sqe,223 sqe: *linux.io_uring_sqe,
225 fd: os.fd_t,224 fd: linux.fd_t,
226 msg: *os.msghdr,225 msg: *linux.msghdr,
227 flags: u32,226 flags: u32,
228 ) void {227 ) void {
229 sqe.prep_recvmsg(fd, msg, flags);228 sqe.prep_recvmsg(fd, msg, flags);
230 sqe.ioprio |= linux.IORING_RECV_MULTISHOT;229 sqe.ioprio |= linux.IORING_RECV_MULTISHOT;
231 }230 }
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 {
234 sqe.prep_rw(.SEND, fd, @intFromPtr(buffer.ptr), buffer.len, 0);233 sqe.prep_rw(.SEND, fd, @intFromPtr(buffer.ptr), buffer.len, 0);
235 sqe.rw_flags = flags;234 sqe.rw_flags = flags;
236 }235 }
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 {
239 sqe.prep_rw(.SEND_ZC, fd, @intFromPtr(buffer.ptr), buffer.len, 0);238 sqe.prep_rw(.SEND_ZC, fd, @intFromPtr(buffer.ptr), buffer.len, 0);
240 sqe.rw_flags = flags;239 sqe.rw_flags = flags;
241 sqe.ioprio = zc_flags;240 sqe.ioprio = zc_flags;
242 }241 }
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 {
245 prep_send_zc(sqe, fd, buffer, flags, zc_flags);244 prep_send_zc(sqe, fd, buffer, flags, zc_flags);
246 sqe.ioprio |= linux.IORING_RECVSEND_FIXED_BUF;245 sqe.ioprio |= linux.IORING_RECVSEND_FIXED_BUF;
247 sqe.buf_index = buf_index;246 sqe.buf_index = buf_index;
...@@ -249,8 +248,8 @@ pub const io_uring_sqe = extern struct {...@@ -249,8 +248,8 @@ pub const io_uring_sqe = extern struct {
249248
250 pub fn prep_sendmsg_zc(249 pub fn prep_sendmsg_zc(
251 sqe: *linux.io_uring_sqe,250 sqe: *linux.io_uring_sqe,
252 fd: os.fd_t,251 fd: linux.fd_t,
253 msg: *const os.msghdr_const,252 msg: *const linux.msghdr_const,
254 flags: u32,253 flags: u32,
255 ) void {254 ) void {
256 prep_sendmsg(sqe, fd, msg, flags);255 prep_sendmsg(sqe, fd, msg, flags);
...@@ -259,8 +258,8 @@ pub const io_uring_sqe = extern struct {...@@ -259,8 +258,8 @@ pub const io_uring_sqe = extern struct {
259258
260 pub fn prep_sendmsg(259 pub fn prep_sendmsg(
261 sqe: *linux.io_uring_sqe,260 sqe: *linux.io_uring_sqe,
262 fd: os.fd_t,261 fd: linux.fd_t,
263 msg: *const os.msghdr_const,262 msg: *const linux.msghdr_const,
264 flags: u32,263 flags: u32,
265 ) void {264 ) void {
266 sqe.prep_rw(.SENDMSG, fd, @intFromPtr(msg), 1, 0);265 sqe.prep_rw(.SENDMSG, fd, @intFromPtr(msg), 1, 0);
...@@ -269,10 +268,10 @@ pub const io_uring_sqe = extern struct {...@@ -269,10 +268,10 @@ pub const io_uring_sqe = extern struct {
269268
270 pub fn prep_openat(269 pub fn prep_openat(
271 sqe: *linux.io_uring_sqe,270 sqe: *linux.io_uring_sqe,
272 fd: os.fd_t,271 fd: linux.fd_t,
273 path: [*:0]const u8,272 path: [*:0]const u8,
274 flags: linux.O,273 flags: linux.O,
275 mode: os.mode_t,274 mode: linux.mode_t,
276 ) void {275 ) void {
277 sqe.prep_rw(.OPENAT, fd, @intFromPtr(path), mode, 0);276 sqe.prep_rw(.OPENAT, fd, @intFromPtr(path), mode, 0);
278 sqe.rw_flags = @bitCast(flags);277 sqe.rw_flags = @bitCast(flags);
...@@ -280,17 +279,17 @@ pub const io_uring_sqe = extern struct {...@@ -280,17 +279,17 @@ pub const io_uring_sqe = extern struct {
280279
281 pub fn prep_openat_direct(280 pub fn prep_openat_direct(
282 sqe: *linux.io_uring_sqe,281 sqe: *linux.io_uring_sqe,
283 fd: os.fd_t,282 fd: linux.fd_t,
284 path: [*:0]const u8,283 path: [*:0]const u8,
285 flags: linux.O,284 flags: linux.O,
286 mode: os.mode_t,285 mode: linux.mode_t,
287 file_index: u32,286 file_index: u32,
288 ) void {287 ) void {
289 prep_openat(sqe, fd, path, flags, mode);288 prep_openat(sqe, fd, path, flags, mode);
290 __io_uring_set_target_fixed_file(sqe, file_index);289 __io_uring_set_target_fixed_file(sqe, file_index);
291 }290 }
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 {
294 sqe.* = .{293 sqe.* = .{
295 .opcode = .CLOSE,294 .opcode = .CLOSE,
296 .flags = 0,295 .flags = 0,
...@@ -316,7 +315,7 @@ pub const io_uring_sqe = extern struct {...@@ -316,7 +315,7 @@ pub const io_uring_sqe = extern struct {
316315
317 pub fn prep_timeout(316 pub fn prep_timeout(
318 sqe: *linux.io_uring_sqe,317 sqe: *linux.io_uring_sqe,
319 ts: *const os.linux.kernel_timespec,318 ts: *const linux.kernel_timespec,
320 count: u32,319 count: u32,
321 flags: u32,320 flags: u32,
322 ) void {321 ) void {
...@@ -345,7 +344,7 @@ pub const io_uring_sqe = extern struct {...@@ -345,7 +344,7 @@ pub const io_uring_sqe = extern struct {
345344
346 pub fn prep_link_timeout(345 pub fn prep_link_timeout(
347 sqe: *linux.io_uring_sqe,346 sqe: *linux.io_uring_sqe,
348 ts: *const os.linux.kernel_timespec,347 ts: *const linux.kernel_timespec,
349 flags: u32,348 flags: u32,
350 ) void {349 ) void {
351 sqe.prep_rw(.LINK_TIMEOUT, -1, @intFromPtr(ts), 1, 0);350 sqe.prep_rw(.LINK_TIMEOUT, -1, @intFromPtr(ts), 1, 0);
...@@ -354,7 +353,7 @@ pub const io_uring_sqe = extern struct {...@@ -354,7 +353,7 @@ pub const io_uring_sqe = extern struct {
354353
355 pub fn prep_poll_add(354 pub fn prep_poll_add(
356 sqe: *linux.io_uring_sqe,355 sqe: *linux.io_uring_sqe,
357 fd: os.fd_t,356 fd: linux.fd_t,
358 poll_mask: u32,357 poll_mask: u32,
359 ) void {358 ) void {
360 sqe.prep_rw(.POLL_ADD, fd, @intFromPtr(@as(?*anyopaque, null)), 0, 0);359 sqe.prep_rw(.POLL_ADD, fd, @intFromPtr(@as(?*anyopaque, null)), 0, 0);
...@@ -393,7 +392,7 @@ pub const io_uring_sqe = extern struct {...@@ -393,7 +392,7 @@ pub const io_uring_sqe = extern struct {
393392
394 pub fn prep_fallocate(393 pub fn prep_fallocate(
395 sqe: *linux.io_uring_sqe,394 sqe: *linux.io_uring_sqe,
396 fd: os.fd_t,395 fd: linux.fd_t,
397 mode: i32,396 mode: i32,
398 offset: u64,397 offset: u64,
399 len: u64,398 len: u64,
...@@ -418,7 +417,7 @@ pub const io_uring_sqe = extern struct {...@@ -418,7 +417,7 @@ pub const io_uring_sqe = extern struct {
418417
419 pub fn prep_statx(418 pub fn prep_statx(
420 sqe: *linux.io_uring_sqe,419 sqe: *linux.io_uring_sqe,
421 fd: os.fd_t,420 fd: linux.fd_t,
422 path: [*:0]const u8,421 path: [*:0]const u8,
423 flags: u32,422 flags: u32,
424 mask: u32,423 mask: u32,
...@@ -439,7 +438,7 @@ pub const io_uring_sqe = extern struct {...@@ -439,7 +438,7 @@ pub const io_uring_sqe = extern struct {
439438
440 pub fn prep_shutdown(439 pub fn prep_shutdown(
441 sqe: *linux.io_uring_sqe,440 sqe: *linux.io_uring_sqe,
442 sockfd: os.socket_t,441 sockfd: linux.socket_t,
443 how: u32,442 how: u32,
444 ) void {443 ) void {
445 sqe.prep_rw(.SHUTDOWN, sockfd, 0, how, 0);444 sqe.prep_rw(.SHUTDOWN, sockfd, 0, how, 0);
...@@ -447,9 +446,9 @@ pub const io_uring_sqe = extern struct {...@@ -447,9 +446,9 @@ pub const io_uring_sqe = extern struct {
447446
448 pub fn prep_renameat(447 pub fn prep_renameat(
449 sqe: *linux.io_uring_sqe,448 sqe: *linux.io_uring_sqe,
450 old_dir_fd: os.fd_t,449 old_dir_fd: linux.fd_t,
451 old_path: [*:0]const u8,450 old_path: [*:0]const u8,
452 new_dir_fd: os.fd_t,451 new_dir_fd: linux.fd_t,
453 new_path: [*:0]const u8,452 new_path: [*:0]const u8,
454 flags: u32,453 flags: u32,
455 ) void {454 ) void {
...@@ -466,7 +465,7 @@ pub const io_uring_sqe = extern struct {...@@ -466,7 +465,7 @@ pub const io_uring_sqe = extern struct {
466465
467 pub fn prep_unlinkat(466 pub fn prep_unlinkat(
468 sqe: *linux.io_uring_sqe,467 sqe: *linux.io_uring_sqe,
469 dir_fd: os.fd_t,468 dir_fd: linux.fd_t,
470 path: [*:0]const u8,469 path: [*:0]const u8,
471 flags: u32,470 flags: u32,
472 ) void {471 ) void {
...@@ -476,9 +475,9 @@ pub const io_uring_sqe = extern struct {...@@ -476,9 +475,9 @@ pub const io_uring_sqe = extern struct {
476475
477 pub fn prep_mkdirat(476 pub fn prep_mkdirat(
478 sqe: *linux.io_uring_sqe,477 sqe: *linux.io_uring_sqe,
479 dir_fd: os.fd_t,478 dir_fd: linux.fd_t,
480 path: [*:0]const u8,479 path: [*:0]const u8,
481 mode: os.mode_t,480 mode: linux.mode_t,
482 ) void {481 ) void {
483 sqe.prep_rw(.MKDIRAT, dir_fd, @intFromPtr(path), mode, 0);482 sqe.prep_rw(.MKDIRAT, dir_fd, @intFromPtr(path), mode, 0);
484 }483 }
...@@ -486,7 +485,7 @@ pub const io_uring_sqe = extern struct {...@@ -486,7 +485,7 @@ pub const io_uring_sqe = extern struct {
486 pub fn prep_symlinkat(485 pub fn prep_symlinkat(
487 sqe: *linux.io_uring_sqe,486 sqe: *linux.io_uring_sqe,
488 target: [*:0]const u8,487 target: [*:0]const u8,
489 new_dir_fd: os.fd_t,488 new_dir_fd: linux.fd_t,
490 link_path: [*:0]const u8,489 link_path: [*:0]const u8,
491 ) void {490 ) void {
492 sqe.prep_rw(491 sqe.prep_rw(
...@@ -500,9 +499,9 @@ pub const io_uring_sqe = extern struct {...@@ -500,9 +499,9 @@ pub const io_uring_sqe = extern struct {
500499
501 pub fn prep_linkat(500 pub fn prep_linkat(
502 sqe: *linux.io_uring_sqe,501 sqe: *linux.io_uring_sqe,
503 old_dir_fd: os.fd_t,502 old_dir_fd: linux.fd_t,
504 old_path: [*:0]const u8,503 old_path: [*:0]const u8,
505 new_dir_fd: os.fd_t,504 new_dir_fd: linux.fd_t,
506 new_path: [*:0]const u8,505 new_path: [*:0]const u8,
507 flags: u32,506 flags: u32,
508 ) void {507 ) void {
...@@ -541,9 +540,9 @@ pub const io_uring_sqe = extern struct {...@@ -541,9 +540,9 @@ pub const io_uring_sqe = extern struct {
541540
542 pub fn prep_multishot_accept(541 pub fn prep_multishot_accept(
543 sqe: *linux.io_uring_sqe,542 sqe: *linux.io_uring_sqe,
544 fd: os.fd_t,543 fd: linux.fd_t,
545 addr: ?*os.sockaddr,544 addr: ?*linux.sockaddr,
546 addrlen: ?*os.socklen_t,545 addrlen: ?*linux.socklen_t,
547 flags: u32,546 flags: u32,
548 ) void {547 ) void {
549 prep_accept(sqe, fd, addr, addrlen, flags);548 prep_accept(sqe, fd, addr, addrlen, flags);
lib/std/os/linux/mips.zig+2-2
...@@ -3,8 +3,8 @@ const maxInt = std.math.maxInt;...@@ -3,8 +3,8 @@ const maxInt = std.math.maxInt;
3const linux = std.os.linux;3const linux = std.os.linux;
4const SYS = linux.SYS;4const SYS = linux.SYS;
5const socklen_t = linux.socklen_t;5const socklen_t = linux.socklen_t;
6const iovec = std.os.iovec;6const iovec = std.posix.iovec;
7const iovec_const = std.os.iovec_const;7const iovec_const = std.posix.iovec_const;
8const uid_t = linux.uid_t;8const uid_t = linux.uid_t;
9const gid_t = linux.gid_t;9const gid_t = linux.gid_t;
10const pid_t = linux.pid_t;10const pid_t = linux.pid_t;
lib/std/os/linux/mips64.zig+2-2
...@@ -3,8 +3,8 @@ const maxInt = std.math.maxInt;...@@ -3,8 +3,8 @@ const maxInt = std.math.maxInt;
3const linux = std.os.linux;3const linux = std.os.linux;
4const SYS = linux.SYS;4const SYS = linux.SYS;
5const socklen_t = linux.socklen_t;5const socklen_t = linux.socklen_t;
6const iovec = std.os.iovec;6const iovec = std.posix.iovec;
7const iovec_const = std.os.iovec_const;7const iovec_const = std.posix.iovec_const;
8const uid_t = linux.uid_t;8const uid_t = linux.uid_t;
9const gid_t = linux.gid_t;9const gid_t = linux.gid_t;
10const pid_t = linux.pid_t;10const pid_t = linux.pid_t;
lib/std/os/linux/powerpc.zig+2-2
...@@ -3,8 +3,8 @@ const maxInt = std.math.maxInt;...@@ -3,8 +3,8 @@ const maxInt = std.math.maxInt;
3const linux = std.os.linux;3const linux = std.os.linux;
4const SYS = linux.SYS;4const SYS = linux.SYS;
5const socklen_t = linux.socklen_t;5const socklen_t = linux.socklen_t;
6const iovec = std.os.iovec;6const iovec = std.posix.iovec;
7const iovec_const = std.os.iovec_const;7const iovec_const = std.posix.iovec_const;
8const uid_t = linux.uid_t;8const uid_t = linux.uid_t;
9const gid_t = linux.gid_t;9const gid_t = linux.gid_t;
10const pid_t = linux.pid_t;10const pid_t = linux.pid_t;
lib/std/os/linux/powerpc64.zig+2-2
...@@ -3,8 +3,8 @@ const maxInt = std.math.maxInt;...@@ -3,8 +3,8 @@ const maxInt = std.math.maxInt;
3const linux = std.os.linux;3const linux = std.os.linux;
4const SYS = linux.SYS;4const SYS = linux.SYS;
5const socklen_t = linux.socklen_t;5const socklen_t = linux.socklen_t;
6const iovec = std.os.iovec;6const iovec = std.posix.iovec;
7const iovec_const = std.os.iovec_const;7const iovec_const = std.posix.iovec_const;
8const uid_t = linux.uid_t;8const uid_t = linux.uid_t;
9const gid_t = linux.gid_t;9const gid_t = linux.gid_t;
10const pid_t = linux.pid_t;10const pid_t = linux.pid_t;
lib/std/os/linux/riscv64.zig+2-2
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const std = @import("../../std.zig");1const std = @import("../../std.zig");
2const iovec = std.os.iovec;2const iovec = std.posix.iovec;
3const iovec_const = std.os.iovec_const;3const iovec_const = std.posix.iovec_const;
4const linux = std.os.linux;4const linux = std.os.linux;
5const SYS = linux.SYS;5const SYS = linux.SYS;
6const uid_t = std.os.linux.uid_t;6const uid_t = std.os.linux.uid_t;
lib/std/os/linux/sparc64.zig+2-2
...@@ -10,8 +10,8 @@ const linux = std.os.linux;...@@ -10,8 +10,8 @@ const linux = std.os.linux;
10const SYS = linux.SYS;10const SYS = linux.SYS;
11const sockaddr = linux.sockaddr;11const sockaddr = linux.sockaddr;
12const socklen_t = linux.socklen_t;12const socklen_t = linux.socklen_t;
13const iovec = std.os.iovec;13const iovec = std.posix.iovec;
14const iovec_const = std.os.iovec_const;14const iovec_const = std.posix.iovec_const;
15const timespec = linux.timespec;15const timespec = linux.timespec;
1616
17pub fn syscall_pipe(fd: *[2]i32) usize {17pub fn syscall_pipe(fd: *[2]i32) usize {
lib/std/os/linux/start_pie.zig+1-1
...@@ -81,7 +81,7 @@ pub fn relocate(phdrs: []elf.Phdr) void {...@@ -81,7 +81,7 @@ pub fn relocate(phdrs: []elf.Phdr) void {
81 break :base @intFromPtr(dynv) - phdr.p_vaddr;81 break :base @intFromPtr(dynv) - phdr.p_vaddr;
82 }82 }
83 // This is not supposed to happen for well-formed binaries.83 // This is not supposed to happen for well-formed binaries.
84 std.os.abort();84 @trap();
85 };85 };
8686
87 var rel_addr: usize = 0;87 var rel_addr: usize = 0;
lib/std/os/linux/test.zig+8-8
...@@ -18,7 +18,7 @@ test "fallocate" {...@@ -18,7 +18,7 @@ test "fallocate" {
18 try expect((try file.stat()).size == 0);18 try expect((try file.stat()).size == 0);
1919
20 const len: i64 = 65536;20 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))) {
22 .SUCCESS => {},22 .SUCCESS => {},
23 .NOSYS => return error.SkipZigTest,23 .NOSYS => return error.SkipZigTest,
24 .OPNOTSUPP => return error.SkipZigTest,24 .OPNOTSUPP => return error.SkipZigTest,
...@@ -34,11 +34,11 @@ test "getpid" {...@@ -34,11 +34,11 @@ test "getpid" {
3434
35test "timer" {35test "timer" {
36 const epoll_fd = linux.epoll_create();36 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);
38 try expect(err == .SUCCESS);38 try expect(err == .SUCCESS);
3939
40 const timer_fd = linux.timerfd_create(linux.CLOCK.MONOTONIC, .{});40 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
43 const time_interval = linux.timespec{43 const time_interval = linux.timespec{
44 .tv_sec = 0,44 .tv_sec = 0,
...@@ -50,7 +50,7 @@ test "timer" {...@@ -50,7 +50,7 @@ test "timer" {
50 .it_value = time_interval,50 .it_value = time_interval,
51 };51 };
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));
54 try expect(err == .SUCCESS);54 try expect(err == .SUCCESS);
5555
56 var event = linux.epoll_event{56 var event = linux.epoll_event{
...@@ -58,13 +58,13 @@ test "timer" {...@@ -58,13 +58,13 @@ test "timer" {
58 .data = linux.epoll_data{ .ptr = 0 },58 .data = linux.epoll_data{ .ptr = 0 },
59 };59 };
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));
62 try expect(err == .SUCCESS);62 try expect(err == .SUCCESS);
6363
64 const events_one: linux.epoll_event = undefined;64 const events_one: linux.epoll_event = undefined;
65 var events = [_]linux.epoll_event{events_one} ** 8;65 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));
68 try expect(err == .SUCCESS);68 try expect(err == .SUCCESS);
69}69}
7070
...@@ -77,7 +77,7 @@ test "statx" {...@@ -77,7 +77,7 @@ test "statx" {
77 defer file.close();77 defer file.close();
7878
79 var statx_buf: linux.Statx = undefined;79 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))) {
81 .SUCCESS => {},81 .SUCCESS => {},
82 // The statx syscall was only introduced in linux 4.1182 // The statx syscall was only introduced in linux 4.11
83 .NOSYS => return error.SkipZigTest,83 .NOSYS => return error.SkipZigTest,
...@@ -85,7 +85,7 @@ test "statx" {...@@ -85,7 +85,7 @@ test "statx" {
85 }85 }
8686
87 var stat_buf: linux.Stat = undefined;87 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))) {
89 .SUCCESS => {},89 .SUCCESS => {},
90 else => unreachable,90 else => unreachable,
91 }91 }
lib/std/os/linux/tls.zig+10-9
...@@ -1,10 +1,11 @@...@@ -1,10 +1,11 @@
1const std = @import("std");1const std = @import("std");
2const os = std.os;
3const mem = std.mem;2const mem = std.mem;
4const elf = std.elf;3const elf = std.elf;
5const math = std.math;4const math = std.math;
6const assert = std.debug.assert;5const assert = std.debug.assert;
7const native_arch = @import("builtin").cpu.arch;6const native_arch = @import("builtin").cpu.arch;
7const linux = std.os.linux;
8const posix = std.posix;
89
9// This file implements the two TLS variants [1] used by ELF-based systems.10// This file implements the two TLS variants [1] used by ELF-based systems.
10//11//
...@@ -111,7 +112,7 @@ pub var tls_image: TLSImage = undefined;...@@ -111,7 +112,7 @@ pub var tls_image: TLSImage = undefined;
111pub fn setThreadPointer(addr: usize) void {112pub fn setThreadPointer(addr: usize) void {
112 switch (native_arch) {113 switch (native_arch) {
113 .x86 => {114 .x86 => {
114 var user_desc = std.os.linux.user_desc{115 var user_desc: linux.user_desc = .{
115 .entry_number = tls_image.gdt_entry_number,116 .entry_number = tls_image.gdt_entry_number,
116 .base_addr = addr,117 .base_addr = addr,
117 .limit = 0xfffff,118 .limit = 0xfffff,
...@@ -124,7 +125,7 @@ pub fn setThreadPointer(addr: usize) void {...@@ -124,7 +125,7 @@ pub fn setThreadPointer(addr: usize) void {
124 .useable = 1,125 .useable = 1,
125 },126 },
126 };127 };
127 const rc = std.os.linux.syscall1(.set_thread_area, @intFromPtr(&user_desc));128 const rc = linux.syscall1(.set_thread_area, @intFromPtr(&user_desc));
128 assert(rc == 0);129 assert(rc == 0);
129130
130 const gdt_entry_number = user_desc.entry_number;131 const gdt_entry_number = user_desc.entry_number;
...@@ -137,7 +138,7 @@ pub fn setThreadPointer(addr: usize) void {...@@ -137,7 +138,7 @@ pub fn setThreadPointer(addr: usize) void {
137 );138 );
138 },139 },
139 .x86_64 => {140 .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);
141 assert(rc == 0);142 assert(rc == 0);
142 },143 },
143 .aarch64, .aarch64_be => {144 .aarch64, .aarch64_be => {
...@@ -148,7 +149,7 @@ pub fn setThreadPointer(addr: usize) void {...@@ -148,7 +149,7 @@ pub fn setThreadPointer(addr: usize) void {
148 );149 );
149 },150 },
150 .arm, .thumb => {151 .arm, .thumb => {
151 const rc = std.os.linux.syscall1(.set_tls, addr);152 const rc = linux.syscall1(.set_tls, addr);
152 assert(rc == 0);153 assert(rc == 0);
153 },154 },
154 .riscv64 => {155 .riscv64 => {
...@@ -159,7 +160,7 @@ pub fn setThreadPointer(addr: usize) void {...@@ -159,7 +160,7 @@ pub fn setThreadPointer(addr: usize) void {
159 );160 );
160 },161 },
161 .mips, .mipsel, .mips64, .mips64el => {162 .mips, .mipsel, .mips64, .mips64el => {
162 const rc = std.os.linux.syscall1(.set_thread_area, addr);163 const rc = linux.syscall1(.set_thread_area, addr);
163 assert(rc == 0);164 assert(rc == 0);
164 },165 },
165 .powerpc, .powerpcle => {166 .powerpc, .powerpcle => {
...@@ -320,14 +321,14 @@ pub fn initStaticTLS(phdrs: []elf.Phdr) void {...@@ -320,14 +321,14 @@ pub fn initStaticTLS(phdrs: []elf.Phdr) void {
320 break :blk main_thread_tls_buffer[0..tls_image.alloc_size];321 break :blk main_thread_tls_buffer[0..tls_image.alloc_size];
321 }322 }
322323
323 const alloc_tls_area = os.mmap(324 const alloc_tls_area = posix.mmap(
324 null,325 null,
325 tls_image.alloc_size + tls_image.alloc_align - 1,326 tls_image.alloc_size + tls_image.alloc_align - 1,
326 os.PROT.READ | os.PROT.WRITE,327 posix.PROT.READ | posix.PROT.WRITE,
327 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },328 .{ .TYPE = .PRIVATE, .ANONYMOUS = true },
328 -1,329 -1,
329 0,330 0,
330 ) catch os.abort();331 ) catch posix.abort();
331332
332 // Make sure the slice is correctly aligned.333 // Make sure the slice is correctly aligned.
333 const begin_addr = @intFromPtr(alloc_tls_area.ptr);334 const begin_addr = @intFromPtr(alloc_tls_area.ptr);
lib/std/os/linux/vdso.zig+1-1
...@@ -5,7 +5,7 @@ const mem = std.mem;...@@ -5,7 +5,7 @@ const mem = std.mem;
5const maxInt = std.math.maxInt;5const maxInt = std.math.maxInt;
66
7pub fn lookup(vername: []const u8, name: []const u8) usize {7pub 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);
9 if (vdso_addr == 0) return 0;9 if (vdso_addr == 0) return 0;
1010
11 const eh = @as(*elf.Ehdr, @ptrFromInt(vdso_addr));11 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;...@@ -3,8 +3,8 @@ const maxInt = std.math.maxInt;
3const linux = std.os.linux;3const linux = std.os.linux;
4const SYS = linux.SYS;4const SYS = linux.SYS;
5const socklen_t = linux.socklen_t;5const socklen_t = linux.socklen_t;
6const iovec = std.os.iovec;6const iovec = std.posix.iovec;
7const iovec_const = std.os.iovec_const;7const iovec_const = std.posix.iovec_const;
8const uid_t = linux.uid_t;8const uid_t = linux.uid_t;
9const gid_t = linux.gid_t;9const gid_t = linux.gid_t;
10const pid_t = linux.pid_t;10const pid_t = linux.pid_t;
lib/std/os/linux/x86_64.zig+2-2
...@@ -2,8 +2,8 @@ const std = @import("../../std.zig");...@@ -2,8 +2,8 @@ const std = @import("../../std.zig");
2const maxInt = std.math.maxInt;2const maxInt = std.math.maxInt;
3const linux = std.os.linux;3const linux = std.os.linux;
4const SYS = linux.SYS;4const SYS = linux.SYS;
5const iovec = std.os.iovec;5const iovec = std.posix.iovec;
6const iovec_const = std.os.iovec_const;6const iovec_const = std.posix.iovec_const;
77
8const pid_t = linux.pid_t;8const pid_t = linux.pid_t;
9const uid_t = linux.uid_t;9const uid_t = linux.uid_t;
lib/std/os/plan9.zig+90-7
...@@ -11,13 +11,96 @@ pub const syscall_bits = switch (builtin.cpu.arch) {...@@ -11,13 +11,96 @@ pub const syscall_bits = switch (builtin.cpu.arch) {
11 .x86_64 => @import("plan9/x86_64.zig"),11 .x86_64 => @import("plan9/x86_64.zig"),
12 else => @compileError("more plan9 syscall implementations (needs more inline asm in stage2"),12 else => @compileError("more plan9 syscall implementations (needs more inline asm in stage2"),
13};13};
14pub const E = @import("plan9/errno.zig").E;14/// Ported from /sys/include/ape/errno.h
15/// Get the errno from a syscall return value, or 0 for no error.15pub const E = enum(u16) {
16pub fn getErrno(r: usize) E {16 SUCCESS = 0,
17 const signed_r = @as(isize, @bitCast(r));17 DOM = 1000,
18 const int = if (signed_r > -4096 and signed_r < 0) -signed_r else 0;18 RANGE = 1001,
19 return @as(E, @enumFromInt(int));19 PLAN9 = 1002,
20}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};
21// The max bytes that can be in the errstr buff104// The max bytes that can be in the errstr buff
22pub const ERRMAX = 128;105pub const ERRMAX = 128;
23var errstr_buf: [ERRMAX]u8 = undefined;106var 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 {...@@ -17,8 +17,8 @@ comptime {
17 // assert(@alignOf(u64) == 8);17 // assert(@alignOf(u64) == 8);
18}18}
1919
20pub const iovec_t = std.os.iovec;20pub const iovec_t = std.posix.iovec;
21pub const ciovec_t = std.os.iovec_const;21pub const ciovec_t = std.posix.iovec_const;
2222
23pub extern "wasi_snapshot_preview1" fn args_get(argv: [*][*:0]u8, argv_buf: [*]u8) errno_t;23pub extern "wasi_snapshot_preview1" fn args_get(argv: [*][*:0]u8, argv_buf: [*]u8) errno_t;
24pub extern "wasi_snapshot_preview1" fn args_sizes_get(argc: *usize, argv_buf_size: *usize) errno_t;24pub 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;...@@ -11,6 +11,7 @@ const assert = std.debug.assert;
11const math = std.math;11const math = std.math;
12const maxInt = std.math.maxInt;12const maxInt = std.math.maxInt;
13const native_arch = builtin.cpu.arch;13const native_arch = builtin.cpu.arch;
14const UnexpectedError = std.posix.UnexpectedError;
1415
15test {16test {
16 if (builtin.os.tag == .windows) {17 if (builtin.os.tag == .windows) {
...@@ -547,7 +548,7 @@ pub const GetQueuedCompletionStatusError = error{...@@ -547,7 +548,7 @@ pub const GetQueuedCompletionStatusError = error{
547 Cancelled,548 Cancelled,
548 EOF,549 EOF,
549 Timeout,550 Timeout,
550} || std.os.UnexpectedError;551} || UnexpectedError;
551552
552pub fn GetQueuedCompletionStatusEx(553pub fn GetQueuedCompletionStatusEx(
553 completion_port: HANDLE,554 completion_port: HANDLE,
...@@ -1701,7 +1702,7 @@ pub fn VirtualProtectEx(handle: HANDLE, addr: ?LPVOID, size: SIZE_T, new_prot: D...@@ -1701,7 +1702,7 @@ pub fn VirtualProtectEx(handle: HANDLE, addr: ?LPVOID, size: SIZE_T, new_prot: D
1701 .SUCCESS => return old_prot,1702 .SUCCESS => return old_prot,
1702 .INVALID_ADDRESS => return error.InvalidAddress,1703 .INVALID_ADDRESS => return error.InvalidAddress,
1703 // TODO: map errors1704 // TODO: map errors
1704 else => |rc| return std.os.windows.unexpectedStatus(rc),1705 else => |rc| return unexpectedStatus(rc),
1705 }1706 }
1706}1707}
17071708
...@@ -1946,7 +1947,7 @@ pub fn SetFileTime(...@@ -1946,7 +1947,7 @@ pub fn SetFileTime(
1946pub const LockFileError = error{1947pub const LockFileError = error{
1947 SystemResources,1948 SystemResources,
1948 WouldBlock,1949 WouldBlock,
1949} || std.os.UnexpectedError;1950} || UnexpectedError;
19501951
1951pub fn LockFile(1952pub fn LockFile(
1952 FileHandle: HANDLE,1953 FileHandle: HANDLE,
...@@ -1983,7 +1984,7 @@ pub fn LockFile(...@@ -1983,7 +1984,7 @@ pub fn LockFile(
19831984
1984pub const UnlockFileError = error{1985pub const UnlockFileError = error{
1985 RangeNotLocked,1986 RangeNotLocked,
1986} || std.os.UnexpectedError;1987} || UnexpectedError;
19871988
1988pub fn UnlockFile(1989pub fn UnlockFile(
1989 FileHandle: HANDLE,1990 FileHandle: HANDLE,
...@@ -2672,8 +2673,8 @@ pub fn loadWinsockExtensionFunction(comptime T: type, sock: ws2_32.SOCKET, guid:...@@ -2672,8 +2673,8 @@ pub fn loadWinsockExtensionFunction(comptime T: type, sock: ws2_32.SOCKET, guid:
26722673
2673/// Call this when you made a windows DLL call or something that does SetLastError2674/// Call this when you made a windows DLL call or something that does SetLastError
2674/// and you get an unexpected error.2675/// and you get an unexpected error.
2675pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError {2676pub fn unexpectedError(err: Win32Error) UnexpectedError {
2676 if (std.os.unexpected_error_tracing) {2677 if (std.posix.unexpected_error_tracing) {
2677 // 614 is the length of the longest windows error description2678 // 614 is the length of the longest windows error description
2678 var buf_wstr: [614]WCHAR = undefined;2679 var buf_wstr: [614]WCHAR = undefined;
2679 const len = kernel32.FormatMessageW(2680 const len = kernel32.FormatMessageW(
...@@ -2694,14 +2695,14 @@ pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError {...@@ -2694,14 +2695,14 @@ pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError {
2694 return error.Unexpected;2695 return error.Unexpected;
2695}2696}
26962697
2697pub fn unexpectedWSAError(err: ws2_32.WinsockError) std.os.UnexpectedError {2698pub fn unexpectedWSAError(err: ws2_32.WinsockError) UnexpectedError {
2698 return unexpectedError(@as(Win32Error, @enumFromInt(@intFromEnum(err))));2699 return unexpectedError(@as(Win32Error, @enumFromInt(@intFromEnum(err))));
2699}2700}
27002701
2701/// Call this when you made a windows NtDll call2702/// Call this when you made a windows NtDll call
2702/// and you get an unexpected status.2703/// and you get an unexpected status.
2703pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError {2704pub fn unexpectedStatus(status: NTSTATUS) UnexpectedError {
2704 if (std.os.unexpected_error_tracing) {2705 if (std.posix.unexpected_error_tracing) {
2705 std.debug.print("error.Unexpected NTSTATUS=0x{x}\n", .{@intFromEnum(status)});2706 std.debug.print("error.Unexpected NTSTATUS=0x{x}\n", .{@intFromEnum(status)});
2706 std.debug.dumpCurrentStackTrace(@returnAddress());2707 std.debug.dumpCurrentStackTrace(@returnAddress());
2707 }2708 }
...@@ -4246,7 +4247,7 @@ pub const KNONVOLATILE_CONTEXT_POINTERS = switch (native_arch) {...@@ -4246,7 +4247,7 @@ pub const KNONVOLATILE_CONTEXT_POINTERS = switch (native_arch) {
42464247
4247pub const EXCEPTION_POINTERS = extern struct {4248pub const EXCEPTION_POINTERS = extern struct {
4248 ExceptionRecord: *EXCEPTION_RECORD,4249 ExceptionRecord: *EXCEPTION_RECORD,
4249 ContextRecord: *std.os.windows.CONTEXT,4250 ContextRecord: *CONTEXT,
4250};4251};
42514252
4252pub const VECTORED_EXCEPTION_HANDLER = *const fn (ExceptionInfo: *EXCEPTION_POINTERS) callconv(WINAPI) c_long;4253pub 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" {...@@ -245,7 +245,7 @@ test "loadWinsockExtensionFunction" {
245 const LPFN_CONNECTEX = *const fn (245 const LPFN_CONNECTEX = *const fn (
246 Socket: windows.ws2_32.SOCKET,246 Socket: windows.ws2_32.SOCKET,
247 SockAddr: *const windows.ws2_32.sockaddr,247 SockAddr: *const windows.ws2_32.sockaddr,
248 SockLen: std.os.socklen_t,248 SockLen: std.posix.socklen_t,
249 SendBuf: ?*const anyopaque,249 SendBuf: ?*const anyopaque,
250 SendBufLen: windows.DWORD,250 SendBufLen: windows.DWORD,
251 BytesSent: *windows.DWORD,251 BytesSent: *windows.DWORD,
...@@ -254,7 +254,7 @@ test "loadWinsockExtensionFunction" {...@@ -254,7 +254,7 @@ test "loadWinsockExtensionFunction" {
254254
255 _ = windows.loadWinsockExtensionFunction(255 _ = windows.loadWinsockExtensionFunction(
256 LPFN_CONNECTEX,256 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),
258 windows.ws2_32.WSAID_CONNECTEX,258 windows.ws2_32.WSAID_CONNECTEX,
259 ) catch |err| switch (err) {259 ) catch |err| switch (err) {
260 error.OperationNotSupported => unreachable,260 error.OperationNotSupported => unreachable,
lib/std/pdb.zig-1
...@@ -2,7 +2,6 @@ const std = @import("std.zig");...@@ -2,7 +2,6 @@ const std = @import("std.zig");
2const io = std.io;2const io = std.io;
3const math = std.math;3const math = std.math;
4const mem = std.mem;4const mem = std.mem;
5const os = std.os;
6const coff = std.coff;5const coff = std.coff;
7const fs = std.fs;6const fs = std.fs;
8const File = std.fs.File;7const File = std.fs.File;
lib/std/posix.zig created+7330
...@@ -0,0 +1,7330 @@
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 std.os.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 std.os.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
4303pub const FStatAtError = FStatError || error{
4304 NameTooLong,
4305 FileNotFound,
4306 SymLinkLoop,
4307 /// WASI-only; file paths must be valid UTF-8.
4308 InvalidUtf8,
4309};
4310
4311/// Similar to `fstat`, but returns stat of a resource pointed to by `pathname`
4312/// which is relative to `dirfd` handle.
4313/// On WASI, `pathname` should be encoded as valid UTF-8.
4314/// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding.
4315/// See also `fstatatZ` and `std.os.fstatat_wasi`.
4316pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
4317 if (native_os == .wasi and !builtin.link_libc) {
4318 const filestat = try std.os.fstatat_wasi(dirfd, pathname, .{
4319 .SYMLINK_FOLLOW = (flags & AT.SYMLINK_NOFOLLOW) == 0,
4320 });
4321 return Stat.fromFilestat(filestat);
4322 } else if (native_os == .windows) {
4323 @compileError("fstatat is not yet implemented on Windows");
4324 } else {
4325 const pathname_c = try toPosixPath(pathname);
4326 return fstatatZ(dirfd, &pathname_c, flags);
4327 }
4328}
4329
4330/// Same as `fstatat` but `pathname` is null-terminated.
4331/// See also `fstatat`.
4332pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat {
4333 if (native_os == .wasi and !builtin.link_libc) {
4334 const filestat = try std.os.fstatat_wasi(dirfd, mem.sliceTo(pathname, 0), .{
4335 .SYMLINK_FOLLOW = (flags & AT.SYMLINK_NOFOLLOW) == 0,
4336 });
4337 return Stat.fromFilestat(filestat);
4338 }
4339
4340 const fstatat_sym = if (lfs64_abi) system.fstatat64 else system.fstatat;
4341 var stat = mem.zeroes(Stat);
4342 switch (errno(fstatat_sym(dirfd, pathname, &stat, flags))) {
4343 .SUCCESS => return stat,
4344 .INVAL => unreachable,
4345 .BADF => unreachable, // Always a race condition.
4346 .NOMEM => return error.SystemResources,
4347 .ACCES => return error.AccessDenied,
4348 .PERM => return error.AccessDenied,
4349 .FAULT => unreachable,
4350 .NAMETOOLONG => return error.NameTooLong,
4351 .LOOP => return error.SymLinkLoop,
4352 .NOENT => return error.FileNotFound,
4353 .NOTDIR => return error.FileNotFound,
4354 .ILSEQ => |err| if (native_os == .wasi)
4355 return error.InvalidUtf8
4356 else
4357 return unexpectedErrno(err),
4358 else => |err| return unexpectedErrno(err),
4359 }
4360}
4361
4362pub const KQueueError = error{
4363 /// The per-process limit on the number of open file descriptors has been reached.
4364 ProcessFdQuotaExceeded,
4365
4366 /// The system-wide limit on the total number of open files has been reached.
4367 SystemFdQuotaExceeded,
4368} || UnexpectedError;
4369
4370pub fn kqueue() KQueueError!i32 {
4371 const rc = system.kqueue();
4372 switch (errno(rc)) {
4373 .SUCCESS => return @intCast(rc),
4374 .MFILE => return error.ProcessFdQuotaExceeded,
4375 .NFILE => return error.SystemFdQuotaExceeded,
4376 else => |err| return unexpectedErrno(err),
4377 }
4378}
4379
4380pub const KEventError = error{
4381 /// The process does not have permission to register a filter.
4382 AccessDenied,
4383
4384 /// The event could not be found to be modified or deleted.
4385 EventNotFound,
4386
4387 /// No memory was available to register the event.
4388 SystemResources,
4389
4390 /// The specified process to attach to does not exist.
4391 ProcessNotFound,
4392
4393 /// changelist or eventlist had too many items on it.
4394 /// TODO remove this possibility
4395 Overflow,
4396};
4397
4398pub fn kevent(
4399 kq: i32,
4400 changelist: []const Kevent,
4401 eventlist: []Kevent,
4402 timeout: ?*const timespec,
4403) KEventError!usize {
4404 while (true) {
4405 const rc = system.kevent(
4406 kq,
4407 changelist.ptr,
4408 cast(c_int, changelist.len) orelse return error.Overflow,
4409 eventlist.ptr,
4410 cast(c_int, eventlist.len) orelse return error.Overflow,
4411 timeout,
4412 );
4413 switch (errno(rc)) {
4414 .SUCCESS => return @intCast(rc),
4415 .ACCES => return error.AccessDenied,
4416 .FAULT => unreachable,
4417 .BADF => unreachable, // Always a race condition.
4418 .INTR => continue,
4419 .INVAL => unreachable,
4420 .NOENT => return error.EventNotFound,
4421 .NOMEM => return error.SystemResources,
4422 .SRCH => return error.ProcessNotFound,
4423 else => unreachable,
4424 }
4425 }
4426}
4427
4428pub const INotifyInitError = error{
4429 ProcessFdQuotaExceeded,
4430 SystemFdQuotaExceeded,
4431 SystemResources,
4432} || UnexpectedError;
4433
4434/// initialize an inotify instance
4435pub fn inotify_init1(flags: u32) INotifyInitError!i32 {
4436 const rc = system.inotify_init1(flags);
4437 switch (errno(rc)) {
4438 .SUCCESS => return @intCast(rc),
4439 .INVAL => unreachable,
4440 .MFILE => return error.ProcessFdQuotaExceeded,
4441 .NFILE => return error.SystemFdQuotaExceeded,
4442 .NOMEM => return error.SystemResources,
4443 else => |err| return unexpectedErrno(err),
4444 }
4445}
4446
4447pub const INotifyAddWatchError = error{
4448 AccessDenied,
4449 NameTooLong,
4450 FileNotFound,
4451 SystemResources,
4452 UserResourceLimitReached,
4453 NotDir,
4454 WatchAlreadyExists,
4455} || UnexpectedError;
4456
4457/// add a watch to an initialized inotify instance
4458pub fn inotify_add_watch(inotify_fd: i32, pathname: []const u8, mask: u32) INotifyAddWatchError!i32 {
4459 const pathname_c = try toPosixPath(pathname);
4460 return inotify_add_watchZ(inotify_fd, &pathname_c, mask);
4461}
4462
4463/// Same as `inotify_add_watch` except pathname is null-terminated.
4464pub fn inotify_add_watchZ(inotify_fd: i32, pathname: [*:0]const u8, mask: u32) INotifyAddWatchError!i32 {
4465 const rc = system.inotify_add_watch(inotify_fd, pathname, mask);
4466 switch (errno(rc)) {
4467 .SUCCESS => return @intCast(rc),
4468 .ACCES => return error.AccessDenied,
4469 .BADF => unreachable,
4470 .FAULT => unreachable,
4471 .INVAL => unreachable,
4472 .NAMETOOLONG => return error.NameTooLong,
4473 .NOENT => return error.FileNotFound,
4474 .NOMEM => return error.SystemResources,
4475 .NOSPC => return error.UserResourceLimitReached,
4476 .NOTDIR => return error.NotDir,
4477 .EXIST => return error.WatchAlreadyExists,
4478 else => |err| return unexpectedErrno(err),
4479 }
4480}
4481
4482/// remove an existing watch from an inotify instance
4483pub fn inotify_rm_watch(inotify_fd: i32, wd: i32) void {
4484 switch (errno(system.inotify_rm_watch(inotify_fd, wd))) {
4485 .SUCCESS => return,
4486 .BADF => unreachable,
4487 .INVAL => unreachable,
4488 else => unreachable,
4489 }
4490}
4491
4492pub const FanotifyInitError = error{
4493 ProcessFdQuotaExceeded,
4494 SystemFdQuotaExceeded,
4495 SystemResources,
4496 OperationNotSupported,
4497 PermissionDenied,
4498} || UnexpectedError;
4499
4500pub fn fanotify_init(flags: u32, event_f_flags: u32) FanotifyInitError!i32 {
4501 const rc = system.fanotify_init(flags, event_f_flags);
4502 switch (errno(rc)) {
4503 .SUCCESS => return @intCast(rc),
4504 .INVAL => unreachable,
4505 .MFILE => return error.ProcessFdQuotaExceeded,
4506 .NFILE => return error.SystemFdQuotaExceeded,
4507 .NOMEM => return error.SystemResources,
4508 .NOSYS => return error.OperationNotSupported,
4509 .PERM => return error.PermissionDenied,
4510 else => |err| return unexpectedErrno(err),
4511 }
4512}
4513
4514pub const FanotifyMarkError = error{
4515 MarkAlreadyExists,
4516 IsDir,
4517 NotAssociatedWithFileSystem,
4518 FileNotFound,
4519 SystemResources,
4520 UserMarkQuotaExceeded,
4521 NotImplemented,
4522 NotDir,
4523 OperationNotSupported,
4524 PermissionDenied,
4525 NotSameFileSystem,
4526 NameTooLong,
4527} || UnexpectedError;
4528
4529pub fn fanotify_mark(fanotify_fd: i32, flags: u32, mask: u64, dirfd: i32, pathname: ?[]const u8) FanotifyMarkError!void {
4530 if (pathname) |path| {
4531 const path_c = try toPosixPath(path);
4532 return fanotify_markZ(fanotify_fd, flags, mask, dirfd, &path_c);
4533 }
4534
4535 return fanotify_markZ(fanotify_fd, flags, mask, dirfd, null);
4536}
4537
4538pub fn fanotify_markZ(fanotify_fd: i32, flags: u32, mask: u64, dirfd: i32, pathname: ?[*:0]const u8) FanotifyMarkError!void {
4539 const rc = system.fanotify_mark(fanotify_fd, flags, mask, dirfd, pathname);
4540 switch (errno(rc)) {
4541 .SUCCESS => return,
4542 .BADF => unreachable,
4543 .EXIST => return error.MarkAlreadyExists,
4544 .INVAL => unreachable,
4545 .ISDIR => return error.IsDir,
4546 .NODEV => return error.NotAssociatedWithFileSystem,
4547 .NOENT => return error.FileNotFound,
4548 .NOMEM => return error.SystemResources,
4549 .NOSPC => return error.UserMarkQuotaExceeded,
4550 .NOSYS => return error.NotImplemented,
4551 .NOTDIR => return error.NotDir,
4552 .OPNOTSUPP => return error.OperationNotSupported,
4553 .PERM => return error.PermissionDenied,
4554 .XDEV => return error.NotSameFileSystem,
4555 else => |err| return unexpectedErrno(err),
4556 }
4557}
4558
4559pub const MProtectError = error{
4560 /// The memory cannot be given the specified access. This can happen, for example, if you
4561 /// mmap(2) a file to which you have read-only access, then ask mprotect() to mark it
4562 /// PROT_WRITE.
4563 AccessDenied,
4564
4565 /// Changing the protection of a memory region would result in the total number of map‐
4566 /// pings with distinct attributes (e.g., read versus read/write protection) exceeding the
4567 /// allowed maximum. (For example, making the protection of a range PROT_READ in the mid‐
4568 /// dle of a region currently protected as PROT_READ|PROT_WRITE would result in three map‐
4569 /// pings: two read/write mappings at each end and a read-only mapping in the middle.)
4570 OutOfMemory,
4571} || UnexpectedError;
4572
4573/// `memory.len` must be page-aligned.
4574pub fn mprotect(memory: []align(mem.page_size) u8, protection: u32) MProtectError!void {
4575 assert(mem.isAligned(memory.len, mem.page_size));
4576 if (native_os == .windows) {
4577 const win_prot: windows.DWORD = switch (@as(u3, @truncate(protection))) {
4578 0b000 => windows.PAGE_NOACCESS,
4579 0b001 => windows.PAGE_READONLY,
4580 0b010 => unreachable, // +w -r not allowed
4581 0b011 => windows.PAGE_READWRITE,
4582 0b100 => windows.PAGE_EXECUTE,
4583 0b101 => windows.PAGE_EXECUTE_READ,
4584 0b110 => unreachable, // +w -r not allowed
4585 0b111 => windows.PAGE_EXECUTE_READWRITE,
4586 };
4587 var old: windows.DWORD = undefined;
4588 windows.VirtualProtect(memory.ptr, memory.len, win_prot, &old) catch |err| switch (err) {
4589 error.InvalidAddress => return error.AccessDenied,
4590 error.Unexpected => return error.Unexpected,
4591 };
4592 } else {
4593 switch (errno(system.mprotect(memory.ptr, memory.len, protection))) {
4594 .SUCCESS => return,
4595 .INVAL => unreachable,
4596 .ACCES => return error.AccessDenied,
4597 .NOMEM => return error.OutOfMemory,
4598 else => |err| return unexpectedErrno(err),
4599 }
4600 }
4601}
4602
4603pub const ForkError = error{SystemResources} || UnexpectedError;
4604
4605pub fn fork() ForkError!pid_t {
4606 const rc = system.fork();
4607 switch (errno(rc)) {
4608 .SUCCESS => return @intCast(rc),
4609 .AGAIN => return error.SystemResources,
4610 .NOMEM => return error.SystemResources,
4611 else => |err| return unexpectedErrno(err),
4612 }
4613}
4614
4615pub const MMapError = error{
4616 /// The underlying filesystem of the specified file does not support memory mapping.
4617 MemoryMappingNotSupported,
4618
4619 /// A file descriptor refers to a non-regular file. Or a file mapping was requested,
4620 /// but the file descriptor is not open for reading. Or `MAP.SHARED` was requested
4621 /// and `PROT_WRITE` is set, but the file descriptor is not open in `RDWR` mode.
4622 /// Or `PROT_WRITE` is set, but the file is append-only.
4623 AccessDenied,
4624
4625 /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on
4626 /// a filesystem that was mounted no-exec.
4627 PermissionDenied,
4628 LockedMemoryLimitExceeded,
4629 ProcessFdQuotaExceeded,
4630 SystemFdQuotaExceeded,
4631 OutOfMemory,
4632} || UnexpectedError;
4633
4634/// Map files or devices into memory.
4635/// `length` does not need to be aligned.
4636/// Use of a mapped region can result in these signals:
4637/// * SIGSEGV - Attempted write into a region mapped as read-only.
4638/// * SIGBUS - Attempted access to a portion of the buffer that does not correspond to the file
4639pub fn mmap(
4640 ptr: ?[*]align(mem.page_size) u8,
4641 length: usize,
4642 prot: u32,
4643 flags: system.MAP,
4644 fd: fd_t,
4645 offset: u64,
4646) MMapError![]align(mem.page_size) u8 {
4647 const mmap_sym = if (lfs64_abi) system.mmap64 else system.mmap;
4648 const rc = mmap_sym(ptr, length, prot, @bitCast(flags), fd, @bitCast(offset));
4649 const err: E = if (builtin.link_libc) blk: {
4650 if (rc != std.c.MAP_FAILED) return @as([*]align(mem.page_size) u8, @ptrCast(@alignCast(rc)))[0..length];
4651 break :blk @enumFromInt(system._errno().*);
4652 } else blk: {
4653 const err = errno(rc);
4654 if (err == .SUCCESS) return @as([*]align(mem.page_size) u8, @ptrFromInt(rc))[0..length];
4655 break :blk err;
4656 };
4657 switch (err) {
4658 .SUCCESS => unreachable,
4659 .TXTBSY => return error.AccessDenied,
4660 .ACCES => return error.AccessDenied,
4661 .PERM => return error.PermissionDenied,
4662 .AGAIN => return error.LockedMemoryLimitExceeded,
4663 .BADF => unreachable, // Always a race condition.
4664 .OVERFLOW => unreachable, // The number of pages used for length + offset would overflow.
4665 .NODEV => return error.MemoryMappingNotSupported,
4666 .INVAL => unreachable, // Invalid parameters to mmap()
4667 .MFILE => return error.ProcessFdQuotaExceeded,
4668 .NFILE => return error.SystemFdQuotaExceeded,
4669 .NOMEM => return error.OutOfMemory,
4670 else => return unexpectedErrno(err),
4671 }
4672}
4673
4674/// Deletes the mappings for the specified address range, causing
4675/// further references to addresses within the range to generate invalid memory references.
4676/// Note that while POSIX allows unmapping a region in the middle of an existing mapping,
4677/// Zig's munmap function does not, for two reasons:
4678/// * It violates the Zig principle that resource deallocation must succeed.
4679/// * The Windows function, VirtualFree, has this restriction.
4680pub fn munmap(memory: []align(mem.page_size) const u8) void {
4681 switch (errno(system.munmap(memory.ptr, memory.len))) {
4682 .SUCCESS => return,
4683 .INVAL => unreachable, // Invalid parameters.
4684 .NOMEM => unreachable, // Attempted to unmap a region in the middle of an existing mapping.
4685 else => unreachable,
4686 }
4687}
4688
4689pub const MSyncError = error{
4690 UnmappedMemory,
4691} || UnexpectedError;
4692
4693pub fn msync(memory: []align(mem.page_size) u8, flags: i32) MSyncError!void {
4694 switch (errno(system.msync(memory.ptr, memory.len, flags))) {
4695 .SUCCESS => return,
4696 .NOMEM => return error.UnmappedMemory, // Unsuccessful, provided pointer does not point mapped memory
4697 .INVAL => unreachable, // Invalid parameters.
4698 else => unreachable,
4699 }
4700}
4701
4702pub const AccessError = error{
4703 PermissionDenied,
4704 FileNotFound,
4705 NameTooLong,
4706 InputOutput,
4707 SystemResources,
4708 BadPathName,
4709 FileBusy,
4710 SymLinkLoop,
4711 ReadOnlyFileSystem,
4712 /// WASI-only; file paths must be valid UTF-8.
4713 InvalidUtf8,
4714 /// Windows-only; file paths provided by the user must be valid WTF-8.
4715 /// https://simonsapin.github.io/wtf-8/
4716 InvalidWtf8,
4717} || UnexpectedError;
4718
4719/// check user's permissions for a file
4720///
4721/// * On Windows, asserts `path` is valid [WTF-8](https://simonsapin.github.io/wtf-8/).
4722/// * On WASI, invalid UTF-8 passed to `path` causes `error.InvalidUtf8`.
4723/// * On other platforms, `path` is an opaque sequence of bytes with no particular encoding.
4724///
4725/// On Windows, `mode` is ignored. This is a POSIX API that is only partially supported by
4726/// Windows. See `fs` for the cross-platform file system API.
4727pub fn access(path: []const u8, mode: u32) AccessError!void {
4728 if (native_os == .windows) {
4729 const path_w = windows.sliceToPrefixedFileW(null, path) catch |err| switch (err) {
4730 error.AccessDenied => return error.PermissionDenied,
4731 else => |e| return e,
4732 };
4733 _ = try windows.GetFileAttributesW(path_w.span().ptr);
4734 return;
4735 } else if (native_os == .wasi and !builtin.link_libc) {
4736 return faccessat(wasi.AT.FDCWD, path, mode, 0);
4737 }
4738 const path_c = try toPosixPath(path);
4739 return accessZ(&path_c, mode);
4740}
4741
4742/// Same as `access` except `path` is null-terminated.
4743pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void {
4744 if (native_os == .windows) {
4745 const path_w = windows.cStrToPrefixedFileW(null, path) catch |err| switch (err) {
4746 error.AccessDenied => return error.PermissionDenied,
4747 else => |e| return e,
4748 };
4749 _ = try windows.GetFileAttributesW(path_w.span().ptr);
4750 return;
4751 } else if (native_os == .wasi and !builtin.link_libc) {
4752 return access(mem.sliceTo(path, 0), mode);
4753 }
4754 switch (errno(system.access(path, mode))) {
4755 .SUCCESS => return,
4756 .ACCES => return error.PermissionDenied,
4757 .ROFS => return error.ReadOnlyFileSystem,
4758 .LOOP => return error.SymLinkLoop,
4759 .TXTBSY => return error.FileBusy,
4760 .NOTDIR => return error.FileNotFound,
4761 .NOENT => return error.FileNotFound,
4762 .NAMETOOLONG => return error.NameTooLong,
4763 .INVAL => unreachable,
4764 .FAULT => unreachable,
4765 .IO => return error.InputOutput,
4766 .NOMEM => return error.SystemResources,
4767 .ILSEQ => |err| if (native_os == .wasi)
4768 return error.InvalidUtf8
4769 else
4770 return unexpectedErrno(err),
4771 else => |err| return unexpectedErrno(err),
4772 }
4773}
4774
4775/// Check user's permissions for a file, based on an open directory handle.
4776///
4777/// * On Windows, asserts `path` is valid [WTF-8](https://simonsapin.github.io/wtf-8/).
4778/// * On WASI, invalid UTF-8 passed to `path` causes `error.InvalidUtf8`.
4779/// * On other platforms, `path` is an opaque sequence of bytes with no particular encoding.
4780///
4781/// On Windows, `mode` is ignored. This is a POSIX API that is only partially supported by
4782/// Windows. See `fs` for the cross-platform file system API.
4783pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void {
4784 if (native_os == .windows) {
4785 const path_w = try windows.sliceToPrefixedFileW(dirfd, path);
4786 return faccessatW(dirfd, path_w.span().ptr);
4787 } else if (native_os == .wasi and !builtin.link_libc) {
4788 const resolved: RelativePathWasi = .{ .dir_fd = dirfd, .relative_path = path };
4789
4790 const st = blk: {
4791 break :blk std.os.fstatat_wasi(dirfd, path, .{
4792 .SYMLINK_FOLLOW = (flags & AT.SYMLINK_NOFOLLOW) == 0,
4793 });
4794 } catch |err| switch (err) {
4795 error.AccessDenied => return error.PermissionDenied,
4796 else => |e| return e,
4797 };
4798
4799 if (mode != F_OK) {
4800 var directory: wasi.fdstat_t = undefined;
4801 if (wasi.fd_fdstat_get(resolved.dir_fd, &directory) != .SUCCESS) {
4802 return error.PermissionDenied;
4803 }
4804
4805 var rights: wasi.rights_t = .{};
4806 if (mode & R_OK != 0) {
4807 if (st.filetype == .DIRECTORY) {
4808 rights.FD_READDIR = true;
4809 } else {
4810 rights.FD_READ = true;
4811 }
4812 }
4813 if (mode & W_OK != 0) {
4814 rights.FD_WRITE = true;
4815 }
4816 // No validation for X_OK
4817
4818 // https://github.com/ziglang/zig/issues/18882
4819 const rights_int: u64 = @bitCast(rights);
4820 const inheriting_int: u64 = @bitCast(directory.fs_rights_inheriting);
4821 if ((rights_int & inheriting_int) != rights_int) {
4822 return error.PermissionDenied;
4823 }
4824 }
4825 return;
4826 }
4827 const path_c = try toPosixPath(path);
4828 return faccessatZ(dirfd, &path_c, mode, flags);
4829}
4830
4831/// Same as `faccessat` except the path parameter is null-terminated.
4832pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void {
4833 if (native_os == .windows) {
4834 const path_w = try windows.cStrToPrefixedFileW(dirfd, path);
4835 return faccessatW(dirfd, path_w.span().ptr);
4836 } else if (native_os == .wasi and !builtin.link_libc) {
4837 return faccessat(dirfd, mem.sliceTo(path, 0), mode, flags);
4838 }
4839 switch (errno(system.faccessat(dirfd, path, mode, flags))) {
4840 .SUCCESS => return,
4841 .ACCES => return error.PermissionDenied,
4842 .ROFS => return error.ReadOnlyFileSystem,
4843 .LOOP => return error.SymLinkLoop,
4844 .TXTBSY => return error.FileBusy,
4845 .NOTDIR => return error.FileNotFound,
4846 .NOENT => return error.FileNotFound,
4847 .NAMETOOLONG => return error.NameTooLong,
4848 .INVAL => unreachable,
4849 .FAULT => unreachable,
4850 .IO => return error.InputOutput,
4851 .NOMEM => return error.SystemResources,
4852 .ILSEQ => |err| if (native_os == .wasi)
4853 return error.InvalidUtf8
4854 else
4855 return unexpectedErrno(err),
4856 else => |err| return unexpectedErrno(err),
4857 }
4858}
4859
4860/// Same as `faccessat` except asserts the target is Windows and the path parameter
4861/// is NtDll-prefixed, null-terminated, WTF-16 encoded.
4862pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16) AccessError!void {
4863 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
4864 return;
4865 }
4866 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
4867 return;
4868 }
4869
4870 const path_len_bytes = cast(u16, mem.sliceTo(sub_path_w, 0).len * 2) orelse return error.NameTooLong;
4871 var nt_name = windows.UNICODE_STRING{
4872 .Length = path_len_bytes,
4873 .MaximumLength = path_len_bytes,
4874 .Buffer = @constCast(sub_path_w),
4875 };
4876 var attr = windows.OBJECT_ATTRIBUTES{
4877 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
4878 .RootDirectory = if (fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd,
4879 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
4880 .ObjectName = &nt_name,
4881 .SecurityDescriptor = null,
4882 .SecurityQualityOfService = null,
4883 };
4884 var basic_info: windows.FILE_BASIC_INFORMATION = undefined;
4885 switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) {
4886 .SUCCESS => return,
4887 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
4888 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
4889 .OBJECT_NAME_INVALID => unreachable,
4890 .INVALID_PARAMETER => unreachable,
4891 .ACCESS_DENIED => return error.PermissionDenied,
4892 .OBJECT_PATH_SYNTAX_BAD => unreachable,
4893 else => |rc| return windows.unexpectedStatus(rc),
4894 }
4895}
4896
4897pub const PipeError = error{
4898 SystemFdQuotaExceeded,
4899 ProcessFdQuotaExceeded,
4900} || UnexpectedError;
4901
4902/// Creates a unidirectional data channel that can be used for interprocess communication.
4903pub fn pipe() PipeError![2]fd_t {
4904 var fds: [2]fd_t = undefined;
4905 switch (errno(system.pipe(&fds))) {
4906 .SUCCESS => return fds,
4907 .INVAL => unreachable, // Invalid parameters to pipe()
4908 .FAULT => unreachable, // Invalid fds pointer
4909 .NFILE => return error.SystemFdQuotaExceeded,
4910 .MFILE => return error.ProcessFdQuotaExceeded,
4911 else => |err| return unexpectedErrno(err),
4912 }
4913}
4914
4915pub fn pipe2(flags: O) PipeError![2]fd_t {
4916 // https://github.com/ziglang/zig/issues/19352
4917 if (@hasDecl(system, "pipe2")) {
4918 var fds: [2]fd_t = undefined;
4919 switch (errno(system.pipe2(&fds, flags))) {
4920 .SUCCESS => return fds,
4921 .INVAL => unreachable, // Invalid flags
4922 .FAULT => unreachable, // Invalid fds pointer
4923 .NFILE => return error.SystemFdQuotaExceeded,
4924 .MFILE => return error.ProcessFdQuotaExceeded,
4925 else => |err| return unexpectedErrno(err),
4926 }
4927 }
4928
4929 const fds: [2]fd_t = try pipe();
4930 errdefer {
4931 close(fds[0]);
4932 close(fds[1]);
4933 }
4934
4935 // https://github.com/ziglang/zig/issues/18882
4936 if (@as(u32, @bitCast(flags)) == 0)
4937 return fds;
4938
4939 // CLOEXEC is special, it's a file descriptor flag and must be set using
4940 // F.SETFD.
4941 if (flags.CLOEXEC) {
4942 for (fds) |fd| {
4943 switch (errno(system.fcntl(fd, F.SETFD, @as(u32, FD_CLOEXEC)))) {
4944 .SUCCESS => {},
4945 .INVAL => unreachable, // Invalid flags
4946 .BADF => unreachable, // Always a race condition
4947 else => |err| return unexpectedErrno(err),
4948 }
4949 }
4950 }
4951
4952 const new_flags: u32 = f: {
4953 var new_flags = flags;
4954 new_flags.CLOEXEC = false;
4955 break :f @bitCast(new_flags);
4956 };
4957 // Set every other flag affecting the file status using F.SETFL.
4958 if (new_flags != 0) {
4959 for (fds) |fd| {
4960 switch (errno(system.fcntl(fd, F.SETFL, new_flags))) {
4961 .SUCCESS => {},
4962 .INVAL => unreachable, // Invalid flags
4963 .BADF => unreachable, // Always a race condition
4964 else => |err| return unexpectedErrno(err),
4965 }
4966 }
4967 }
4968
4969 return fds;
4970}
4971
4972pub const SysCtlError = error{
4973 PermissionDenied,
4974 SystemResources,
4975 NameTooLong,
4976 UnknownName,
4977} || UnexpectedError;
4978
4979pub fn sysctl(
4980 name: []const c_int,
4981 oldp: ?*anyopaque,
4982 oldlenp: ?*usize,
4983 newp: ?*anyopaque,
4984 newlen: usize,
4985) SysCtlError!void {
4986 if (native_os == .wasi) {
4987 @panic("unsupported"); // TODO should be compile error, not panic
4988 }
4989 if (native_os == .haiku) {
4990 @panic("unsupported"); // TODO should be compile error, not panic
4991 }
4992
4993 const name_len = cast(c_uint, name.len) orelse return error.NameTooLong;
4994 switch (errno(system.sysctl(name.ptr, name_len, oldp, oldlenp, newp, newlen))) {
4995 .SUCCESS => return,
4996 .FAULT => unreachable,
4997 .PERM => return error.PermissionDenied,
4998 .NOMEM => return error.SystemResources,
4999 .NOENT => return error.UnknownName,
5000 else => |err| return unexpectedErrno(err),
5001 }
5002}
5003
5004pub fn sysctlbynameZ(
5005 name: [*:0]const u8,
5006 oldp: ?*anyopaque,
5007 oldlenp: ?*usize,
5008 newp: ?*anyopaque,
5009 newlen: usize,
5010) SysCtlError!void {
5011 if (native_os == .wasi) {
5012 @panic("unsupported"); // TODO should be compile error, not panic
5013 }
5014 if (native_os == .haiku) {
5015 @panic("unsupported"); // TODO should be compile error, not panic
5016 }
5017
5018 switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) {
5019 .SUCCESS => return,
5020 .FAULT => unreachable,
5021 .PERM => return error.PermissionDenied,
5022 .NOMEM => return error.SystemResources,
5023 .NOENT => return error.UnknownName,
5024 else => |err| return unexpectedErrno(err),
5025 }
5026}
5027
5028pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void {
5029 switch (errno(system.gettimeofday(tv, tz))) {
5030 .SUCCESS => return,
5031 .INVAL => unreachable,
5032 else => unreachable,
5033 }
5034}
5035
5036pub const SeekError = error{
5037 Unseekable,
5038
5039 /// In WASI, this error may occur when the file descriptor does
5040 /// not hold the required rights to seek on it.
5041 AccessDenied,
5042} || UnexpectedError;
5043
5044/// Repositions read/write file offset relative to the beginning.
5045pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
5046 if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
5047 var result: u64 = undefined;
5048 switch (errno(system.llseek(fd, offset, &result, SEEK.SET))) {
5049 .SUCCESS => return,
5050 .BADF => unreachable, // always a race condition
5051 .INVAL => return error.Unseekable,
5052 .OVERFLOW => return error.Unseekable,
5053 .SPIPE => return error.Unseekable,
5054 .NXIO => return error.Unseekable,
5055 else => |err| return unexpectedErrno(err),
5056 }
5057 }
5058 if (native_os == .windows) {
5059 return windows.SetFilePointerEx_BEGIN(fd, offset);
5060 }
5061 if (native_os == .wasi and !builtin.link_libc) {
5062 var new_offset: wasi.filesize_t = undefined;
5063 switch (wasi.fd_seek(fd, @bitCast(offset), .SET, &new_offset)) {
5064 .SUCCESS => return,
5065 .BADF => unreachable, // always a race condition
5066 .INVAL => return error.Unseekable,
5067 .OVERFLOW => return error.Unseekable,
5068 .SPIPE => return error.Unseekable,
5069 .NXIO => return error.Unseekable,
5070 .NOTCAPABLE => return error.AccessDenied,
5071 else => |err| return unexpectedErrno(err),
5072 }
5073 }
5074
5075 const lseek_sym = if (lfs64_abi) system.lseek64 else system.lseek;
5076 switch (errno(lseek_sym(fd, @bitCast(offset), SEEK.SET))) {
5077 .SUCCESS => return,
5078 .BADF => unreachable, // always a race condition
5079 .INVAL => return error.Unseekable,
5080 .OVERFLOW => return error.Unseekable,
5081 .SPIPE => return error.Unseekable,
5082 .NXIO => return error.Unseekable,
5083 else => |err| return unexpectedErrno(err),
5084 }
5085}
5086
5087/// Repositions read/write file offset relative to the current offset.
5088pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
5089 if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
5090 var result: u64 = undefined;
5091 switch (errno(system.llseek(fd, @bitCast(offset), &result, SEEK.CUR))) {
5092 .SUCCESS => return,
5093 .BADF => unreachable, // always a race condition
5094 .INVAL => return error.Unseekable,
5095 .OVERFLOW => return error.Unseekable,
5096 .SPIPE => return error.Unseekable,
5097 .NXIO => return error.Unseekable,
5098 else => |err| return unexpectedErrno(err),
5099 }
5100 }
5101 if (native_os == .windows) {
5102 return windows.SetFilePointerEx_CURRENT(fd, offset);
5103 }
5104 if (native_os == .wasi and !builtin.link_libc) {
5105 var new_offset: wasi.filesize_t = undefined;
5106 switch (wasi.fd_seek(fd, offset, .CUR, &new_offset)) {
5107 .SUCCESS => return,
5108 .BADF => unreachable, // always a race condition
5109 .INVAL => return error.Unseekable,
5110 .OVERFLOW => return error.Unseekable,
5111 .SPIPE => return error.Unseekable,
5112 .NXIO => return error.Unseekable,
5113 .NOTCAPABLE => return error.AccessDenied,
5114 else => |err| return unexpectedErrno(err),
5115 }
5116 }
5117 const lseek_sym = if (lfs64_abi) system.lseek64 else system.lseek;
5118 switch (errno(lseek_sym(fd, @bitCast(offset), SEEK.CUR))) {
5119 .SUCCESS => return,
5120 .BADF => unreachable, // always a race condition
5121 .INVAL => return error.Unseekable,
5122 .OVERFLOW => return error.Unseekable,
5123 .SPIPE => return error.Unseekable,
5124 .NXIO => return error.Unseekable,
5125 else => |err| return unexpectedErrno(err),
5126 }
5127}
5128
5129/// Repositions read/write file offset relative to the end.
5130pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
5131 if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
5132 var result: u64 = undefined;
5133 switch (errno(system.llseek(fd, @bitCast(offset), &result, SEEK.END))) {
5134 .SUCCESS => return,
5135 .BADF => unreachable, // always a race condition
5136 .INVAL => return error.Unseekable,
5137 .OVERFLOW => return error.Unseekable,
5138 .SPIPE => return error.Unseekable,
5139 .NXIO => return error.Unseekable,
5140 else => |err| return unexpectedErrno(err),
5141 }
5142 }
5143 if (native_os == .windows) {
5144 return windows.SetFilePointerEx_END(fd, offset);
5145 }
5146 if (native_os == .wasi and !builtin.link_libc) {
5147 var new_offset: wasi.filesize_t = undefined;
5148 switch (wasi.fd_seek(fd, offset, .END, &new_offset)) {
5149 .SUCCESS => return,
5150 .BADF => unreachable, // always a race condition
5151 .INVAL => return error.Unseekable,
5152 .OVERFLOW => return error.Unseekable,
5153 .SPIPE => return error.Unseekable,
5154 .NXIO => return error.Unseekable,
5155 .NOTCAPABLE => return error.AccessDenied,
5156 else => |err| return unexpectedErrno(err),
5157 }
5158 }
5159 const lseek_sym = if (lfs64_abi) system.lseek64 else system.lseek;
5160 switch (errno(lseek_sym(fd, @bitCast(offset), SEEK.END))) {
5161 .SUCCESS => return,
5162 .BADF => unreachable, // always a race condition
5163 .INVAL => return error.Unseekable,
5164 .OVERFLOW => return error.Unseekable,
5165 .SPIPE => return error.Unseekable,
5166 .NXIO => return error.Unseekable,
5167 else => |err| return unexpectedErrno(err),
5168 }
5169}
5170
5171/// Returns the read/write file offset relative to the beginning.
5172pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
5173 if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
5174 var result: u64 = undefined;
5175 switch (errno(system.llseek(fd, 0, &result, SEEK.CUR))) {
5176 .SUCCESS => return result,
5177 .BADF => unreachable, // always a race condition
5178 .INVAL => return error.Unseekable,
5179 .OVERFLOW => return error.Unseekable,
5180 .SPIPE => return error.Unseekable,
5181 .NXIO => return error.Unseekable,
5182 else => |err| return unexpectedErrno(err),
5183 }
5184 }
5185 if (native_os == .windows) {
5186 return windows.SetFilePointerEx_CURRENT_get(fd);
5187 }
5188 if (native_os == .wasi and !builtin.link_libc) {
5189 var new_offset: wasi.filesize_t = undefined;
5190 switch (wasi.fd_seek(fd, 0, .CUR, &new_offset)) {
5191 .SUCCESS => return new_offset,
5192 .BADF => unreachable, // always a race condition
5193 .INVAL => return error.Unseekable,
5194 .OVERFLOW => return error.Unseekable,
5195 .SPIPE => return error.Unseekable,
5196 .NXIO => return error.Unseekable,
5197 .NOTCAPABLE => return error.AccessDenied,
5198 else => |err| return unexpectedErrno(err),
5199 }
5200 }
5201 const lseek_sym = if (lfs64_abi) system.lseek64 else system.lseek;
5202 const rc = lseek_sym(fd, 0, SEEK.CUR);
5203 switch (errno(rc)) {
5204 .SUCCESS => return @bitCast(rc),
5205 .BADF => unreachable, // always a race condition
5206 .INVAL => return error.Unseekable,
5207 .OVERFLOW => return error.Unseekable,
5208 .SPIPE => return error.Unseekable,
5209 .NXIO => return error.Unseekable,
5210 else => |err| return unexpectedErrno(err),
5211 }
5212}
5213
5214pub const FcntlError = error{
5215 PermissionDenied,
5216 FileBusy,
5217 ProcessFdQuotaExceeded,
5218 Locked,
5219 DeadLock,
5220 LockedRegionLimitExceeded,
5221} || UnexpectedError;
5222
5223pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
5224 while (true) {
5225 const rc = system.fcntl(fd, cmd, arg);
5226 switch (errno(rc)) {
5227 .SUCCESS => return @intCast(rc),
5228 .INTR => continue,
5229 .AGAIN, .ACCES => return error.Locked,
5230 .BADF => unreachable,
5231 .BUSY => return error.FileBusy,
5232 .INVAL => unreachable, // invalid parameters
5233 .PERM => return error.PermissionDenied,
5234 .MFILE => return error.ProcessFdQuotaExceeded,
5235 .NOTDIR => unreachable, // invalid parameter
5236 .DEADLK => return error.DeadLock,
5237 .NOLCK => return error.LockedRegionLimitExceeded,
5238 else => |err| return unexpectedErrno(err),
5239 }
5240 }
5241}
5242
5243pub const FlockError = error{
5244 WouldBlock,
5245
5246 /// The kernel ran out of memory for allocating file locks
5247 SystemResources,
5248
5249 /// The underlying filesystem does not support file locks
5250 FileLocksNotSupported,
5251} || UnexpectedError;
5252
5253/// Depending on the operating system `flock` may or may not interact with
5254/// `fcntl` locks made by other processes.
5255pub fn flock(fd: fd_t, operation: i32) FlockError!void {
5256 while (true) {
5257 const rc = system.flock(fd, operation);
5258 switch (errno(rc)) {
5259 .SUCCESS => return,
5260 .BADF => unreachable,
5261 .INTR => continue,
5262 .INVAL => unreachable, // invalid parameters
5263 .NOLCK => return error.SystemResources,
5264 .AGAIN => return error.WouldBlock, // TODO: integrate with async instead of just returning an error
5265 .OPNOTSUPP => return error.FileLocksNotSupported,
5266 else => |err| return unexpectedErrno(err),
5267 }
5268 }
5269}
5270
5271pub const RealPathError = error{
5272 FileNotFound,
5273 AccessDenied,
5274 NameTooLong,
5275 NotSupported,
5276 NotDir,
5277 SymLinkLoop,
5278 InputOutput,
5279 FileTooBig,
5280 IsDir,
5281 ProcessFdQuotaExceeded,
5282 SystemFdQuotaExceeded,
5283 NoDevice,
5284 SystemResources,
5285 NoSpaceLeft,
5286 FileSystem,
5287 BadPathName,
5288 DeviceBusy,
5289
5290 SharingViolation,
5291 PipeBusy,
5292
5293 /// Windows-only; file paths provided by the user must be valid WTF-8.
5294 /// https://simonsapin.github.io/wtf-8/
5295 InvalidWtf8,
5296
5297 /// On Windows, `\\server` or `\\server\share` was not found.
5298 NetworkNotFound,
5299
5300 PathAlreadyExists,
5301
5302 /// On Windows, antivirus software is enabled by default. It can be
5303 /// disabled, but Windows Update sometimes ignores the user's preference
5304 /// and re-enables it. When enabled, antivirus software on Windows
5305 /// intercepts file system operations and makes them significantly slower
5306 /// in addition to possibly failing with this error code.
5307 AntivirusInterference,
5308
5309 /// On Windows, the volume does not contain a recognized file system. File
5310 /// system drivers might not be loaded, or the volume may be corrupt.
5311 UnrecognizedVolume,
5312} || UnexpectedError;
5313
5314/// Return the canonicalized absolute pathname.
5315///
5316/// Expands all symbolic links and resolves references to `.`, `..`, and
5317/// extra `/` characters in `pathname`.
5318///
5319/// On Windows, `pathname` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
5320///
5321/// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding.
5322///
5323/// The return value is a slice of `out_buffer`, but not necessarily from the beginning.
5324///
5325/// See also `realpathZ` and `realpathW`.
5326///
5327/// * On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
5328/// * On other platforms, the result is an opaque sequence of bytes with no particular encoding.
5329///
5330/// Calling this function is usually a bug.
5331pub fn realpath(pathname: []const u8, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
5332 if (native_os == .windows) {
5333 const pathname_w = try windows.sliceToPrefixedFileW(null, pathname);
5334 return realpathW(pathname_w.span(), out_buffer);
5335 } else if (native_os == .wasi and !builtin.link_libc) {
5336 @compileError("WASI does not support os.realpath");
5337 }
5338 const pathname_c = try toPosixPath(pathname);
5339 return realpathZ(&pathname_c, out_buffer);
5340}
5341
5342/// Same as `realpath` except `pathname` is null-terminated.
5343///
5344/// Calling this function is usually a bug.
5345pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
5346 if (native_os == .windows) {
5347 const pathname_w = try windows.cStrToPrefixedFileW(null, pathname);
5348 return realpathW(pathname_w.span(), out_buffer);
5349 } else if (native_os == .wasi and !builtin.link_libc) {
5350 return realpath(mem.sliceTo(pathname, 0), out_buffer);
5351 }
5352 if (!builtin.link_libc) {
5353 const flags: O = switch (native_os) {
5354 .linux => .{
5355 .NONBLOCK = true,
5356 .CLOEXEC = true,
5357 .PATH = true,
5358 },
5359 else => .{
5360 .NONBLOCK = true,
5361 .CLOEXEC = true,
5362 },
5363 };
5364 const fd = openZ(pathname, flags, 0) catch |err| switch (err) {
5365 error.FileLocksNotSupported => unreachable,
5366 error.WouldBlock => unreachable,
5367 error.FileBusy => unreachable, // not asking for write permissions
5368 error.InvalidUtf8 => unreachable, // WASI-only
5369 else => |e| return e,
5370 };
5371 defer close(fd);
5372
5373 return std.os.getFdPath(fd, out_buffer);
5374 }
5375 const result_path = std.c.realpath(pathname, out_buffer) orelse switch (@as(E, @enumFromInt(std.c._errno().*))) {
5376 .SUCCESS => unreachable,
5377 .INVAL => unreachable,
5378 .BADF => unreachable,
5379 .FAULT => unreachable,
5380 .ACCES => return error.AccessDenied,
5381 .NOENT => return error.FileNotFound,
5382 .OPNOTSUPP => return error.NotSupported,
5383 .NOTDIR => return error.NotDir,
5384 .NAMETOOLONG => return error.NameTooLong,
5385 .LOOP => return error.SymLinkLoop,
5386 .IO => return error.InputOutput,
5387 else => |err| return unexpectedErrno(err),
5388 };
5389 return mem.sliceTo(result_path, 0);
5390}
5391
5392/// Same as `realpath` except `pathname` is WTF16LE-encoded.
5393///
5394/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
5395///
5396/// Calling this function is usually a bug.
5397pub fn realpathW(pathname: []const u16, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
5398 const w = windows;
5399
5400 const dir = fs.cwd().fd;
5401 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
5402 const share_access = w.FILE_SHARE_READ;
5403 const creation = w.FILE_OPEN;
5404 const h_file = blk: {
5405 const res = w.OpenFile(pathname, .{
5406 .dir = dir,
5407 .access_mask = access_mask,
5408 .share_access = share_access,
5409 .creation = creation,
5410 .filter = .any,
5411 }) catch |err| switch (err) {
5412 error.WouldBlock => unreachable,
5413 else => |e| return e,
5414 };
5415 break :blk res;
5416 };
5417 defer w.CloseHandle(h_file);
5418
5419 return std.os.getFdPath(h_file, out_buffer);
5420}
5421
5422/// Spurious wakeups are possible and no precision of timing is guaranteed.
5423pub fn nanosleep(seconds: u64, nanoseconds: u64) void {
5424 var req = timespec{
5425 .tv_sec = cast(isize, seconds) orelse maxInt(isize),
5426 .tv_nsec = cast(isize, nanoseconds) orelse maxInt(isize),
5427 };
5428 var rem: timespec = undefined;
5429 while (true) {
5430 switch (errno(system.nanosleep(&req, &rem))) {
5431 .FAULT => unreachable,
5432 .INVAL => {
5433 // Sometimes Darwin returns EINVAL for no reason.
5434 // We treat it as a spurious wakeup.
5435 return;
5436 },
5437 .INTR => {
5438 req = rem;
5439 continue;
5440 },
5441 // This prong handles success as well as unexpected errors.
5442 else => return,
5443 }
5444 }
5445}
5446
5447pub fn dl_iterate_phdr(
5448 context: anytype,
5449 comptime Error: type,
5450 comptime callback: fn (info: *dl_phdr_info, size: usize, context: @TypeOf(context)) Error!void,
5451) Error!void {
5452 const Context = @TypeOf(context);
5453 const elf = std.elf;
5454 const dl = @import("dynamic_library.zig");
5455
5456 switch (builtin.object_format) {
5457 .elf, .c => {},
5458 else => @compileError("dl_iterate_phdr is not available for this target"),
5459 }
5460
5461 if (builtin.link_libc) {
5462 switch (system.dl_iterate_phdr(struct {
5463 fn callbackC(info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.C) c_int {
5464 const context_ptr: *const Context = @ptrCast(@alignCast(data));
5465 callback(info, size, context_ptr.*) catch |err| return @intFromError(err);
5466 return 0;
5467 }
5468 }.callbackC, @ptrCast(@constCast(&context)))) {
5469 0 => return,
5470 else => |err| return @as(Error, @errorCast(@errorFromInt(@as(std.meta.Int(.unsigned, @bitSizeOf(anyerror)), @intCast(err))))),
5471 }
5472 }
5473
5474 const elf_base = std.process.getBaseAddress();
5475 const ehdr: *elf.Ehdr = @ptrFromInt(elf_base);
5476 // Make sure the base address points to an ELF image.
5477 assert(mem.eql(u8, ehdr.e_ident[0..4], elf.MAGIC));
5478 const n_phdr = ehdr.e_phnum;
5479 const phdrs = (@as([*]elf.Phdr, @ptrFromInt(elf_base + ehdr.e_phoff)))[0..n_phdr];
5480
5481 var it = dl.linkmap_iterator(phdrs) catch unreachable;
5482
5483 // The executable has no dynamic link segment, create a single entry for
5484 // the whole ELF image.
5485 if (it.end()) {
5486 // Find the base address for the ELF image, if this is a PIE the value
5487 // is non-zero.
5488 const base_address = for (phdrs) |*phdr| {
5489 if (phdr.p_type == elf.PT_PHDR) {
5490 break @intFromPtr(phdrs.ptr) - phdr.p_vaddr;
5491 // We could try computing the difference between _DYNAMIC and
5492 // the p_vaddr of the PT_DYNAMIC section, but using the phdr is
5493 // good enough (Is it?).
5494 }
5495 } else unreachable;
5496
5497 var info = dl_phdr_info{
5498 .dlpi_addr = base_address,
5499 .dlpi_name = "/proc/self/exe",
5500 .dlpi_phdr = phdrs.ptr,
5501 .dlpi_phnum = ehdr.e_phnum,
5502 };
5503
5504 return callback(&info, @sizeOf(dl_phdr_info), context);
5505 }
5506
5507 // Last return value from the callback function.
5508 while (it.next()) |entry| {
5509 var dlpi_phdr: [*]elf.Phdr = undefined;
5510 var dlpi_phnum: u16 = undefined;
5511
5512 if (entry.l_addr != 0) {
5513 const elf_header: *elf.Ehdr = @ptrFromInt(entry.l_addr);
5514 dlpi_phdr = @ptrFromInt(entry.l_addr + elf_header.e_phoff);
5515 dlpi_phnum = elf_header.e_phnum;
5516 } else {
5517 // This is the running ELF image
5518 dlpi_phdr = @ptrFromInt(elf_base + ehdr.e_phoff);
5519 dlpi_phnum = ehdr.e_phnum;
5520 }
5521
5522 var info = dl_phdr_info{
5523 .dlpi_addr = entry.l_addr,
5524 .dlpi_name = entry.l_name,
5525 .dlpi_phdr = dlpi_phdr,
5526 .dlpi_phnum = dlpi_phnum,
5527 };
5528
5529 try callback(&info, @sizeOf(dl_phdr_info), context);
5530 }
5531}
5532
5533pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError;
5534
5535/// TODO: change this to return the timespec as a return value
5536/// TODO: look into making clk_id an enum
5537pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
5538 if (native_os == .wasi and !builtin.link_libc) {
5539 var ts: timestamp_t = undefined;
5540 switch (system.clock_time_get(@bitCast(clk_id), 1, &ts)) {
5541 .SUCCESS => {
5542 tp.* = .{
5543 .tv_sec = @intCast(ts / std.time.ns_per_s),
5544 .tv_nsec = @intCast(ts % std.time.ns_per_s),
5545 };
5546 },
5547 .INVAL => return error.UnsupportedClock,
5548 else => |err| return unexpectedErrno(err),
5549 }
5550 return;
5551 }
5552 if (native_os == .windows) {
5553 if (clk_id == CLOCK.REALTIME) {
5554 var ft: windows.FILETIME = undefined;
5555 windows.kernel32.GetSystemTimeAsFileTime(&ft);
5556 // FileTime has a granularity of 100 nanoseconds and uses the NTFS/Windows epoch.
5557 const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
5558 const ft_per_s = std.time.ns_per_s / 100;
5559 tp.* = .{
5560 .tv_sec = @as(i64, @intCast(ft64 / ft_per_s)) + std.time.epoch.windows,
5561 .tv_nsec = @as(c_long, @intCast(ft64 % ft_per_s)) * 100,
5562 };
5563 return;
5564 } else {
5565 // TODO POSIX implementation of CLOCK.MONOTONIC on Windows.
5566 return error.UnsupportedClock;
5567 }
5568 }
5569
5570 switch (errno(system.clock_gettime(clk_id, tp))) {
5571 .SUCCESS => return,
5572 .FAULT => unreachable,
5573 .INVAL => return error.UnsupportedClock,
5574 else => |err| return unexpectedErrno(err),
5575 }
5576}
5577
5578pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void {
5579 if (native_os == .wasi and !builtin.link_libc) {
5580 var ts: timestamp_t = undefined;
5581 switch (system.clock_res_get(@bitCast(clk_id), &ts)) {
5582 .SUCCESS => res.* = .{
5583 .tv_sec = @intCast(ts / std.time.ns_per_s),
5584 .tv_nsec = @intCast(ts % std.time.ns_per_s),
5585 },
5586 .INVAL => return error.UnsupportedClock,
5587 else => |err| return unexpectedErrno(err),
5588 }
5589 return;
5590 }
5591
5592 switch (errno(system.clock_getres(clk_id, res))) {
5593 .SUCCESS => return,
5594 .FAULT => unreachable,
5595 .INVAL => return error.UnsupportedClock,
5596 else => |err| return unexpectedErrno(err),
5597 }
5598}
5599
5600pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError;
5601
5602pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t {
5603 var set: cpu_set_t = undefined;
5604 switch (errno(system.sched_getaffinity(pid, @sizeOf(cpu_set_t), &set))) {
5605 .SUCCESS => return set,
5606 .FAULT => unreachable,
5607 .INVAL => unreachable,
5608 .SRCH => unreachable,
5609 .PERM => return error.PermissionDenied,
5610 else => |err| return unexpectedErrno(err),
5611 }
5612}
5613
5614pub const SigaltstackError = error{
5615 /// The supplied stack size was less than MINSIGSTKSZ.
5616 SizeTooSmall,
5617
5618 /// Attempted to change the signal stack while it was active.
5619 PermissionDenied,
5620} || UnexpectedError;
5621
5622pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void {
5623 switch (errno(system.sigaltstack(ss, old_ss))) {
5624 .SUCCESS => return,
5625 .FAULT => unreachable,
5626 .INVAL => unreachable,
5627 .NOMEM => return error.SizeTooSmall,
5628 .PERM => return error.PermissionDenied,
5629 else => |err| return unexpectedErrno(err),
5630 }
5631}
5632
5633/// Examine and change a signal action.
5634pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) error{OperationNotSupported}!void {
5635 switch (errno(system.sigaction(sig, act, oact))) {
5636 .SUCCESS => return,
5637 .INVAL, .NOSYS => return error.OperationNotSupported,
5638 else => unreachable,
5639 }
5640}
5641
5642/// Sets the thread signal mask.
5643pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*sigset_t) void {
5644 switch (errno(system.sigprocmask(@bitCast(flags), set, oldset))) {
5645 .SUCCESS => return,
5646 .FAULT => unreachable,
5647 .INVAL => unreachable,
5648 else => unreachable,
5649 }
5650}
5651
5652pub const FutimensError = error{
5653 /// times is NULL, or both tv_nsec values are UTIME_NOW, and either:
5654 /// * the effective user ID of the caller does not match the owner
5655 /// of the file, the caller does not have write access to the
5656 /// file, and the caller is not privileged (Linux: does not have
5657 /// either the CAP_FOWNER or the CAP_DAC_OVERRIDE capability);
5658 /// or,
5659 /// * the file is marked immutable (see chattr(1)).
5660 AccessDenied,
5661
5662 /// The caller attempted to change one or both timestamps to a value
5663 /// other than the current time, or to change one of the timestamps
5664 /// to the current time while leaving the other timestamp unchanged,
5665 /// (i.e., times is not NULL, neither tv_nsec field is UTIME_NOW,
5666 /// and neither tv_nsec field is UTIME_OMIT) and either:
5667 /// * the caller's effective user ID does not match the owner of
5668 /// file, and the caller is not privileged (Linux: does not have
5669 /// the CAP_FOWNER capability); or,
5670 /// * the file is marked append-only or immutable (see chattr(1)).
5671 PermissionDenied,
5672
5673 ReadOnlyFileSystem,
5674} || UnexpectedError;
5675
5676pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void {
5677 if (native_os == .wasi and !builtin.link_libc) {
5678 // TODO WASI encodes `wasi.fstflags` to signify magic values
5679 // similar to UTIME_NOW and UTIME_OMIT. Currently, we ignore
5680 // this here, but we should really handle it somehow.
5681 const atim = times[0].toTimestamp();
5682 const mtim = times[1].toTimestamp();
5683 switch (wasi.fd_filestat_set_times(fd, atim, mtim, .{
5684 .ATIM = true,
5685 .MTIM = true,
5686 })) {
5687 .SUCCESS => return,
5688 .ACCES => return error.AccessDenied,
5689 .PERM => return error.PermissionDenied,
5690 .BADF => unreachable, // always a race condition
5691 .FAULT => unreachable,
5692 .INVAL => unreachable,
5693 .ROFS => return error.ReadOnlyFileSystem,
5694 else => |err| return unexpectedErrno(err),
5695 }
5696 }
5697
5698 switch (errno(system.futimens(fd, times))) {
5699 .SUCCESS => return,
5700 .ACCES => return error.AccessDenied,
5701 .PERM => return error.PermissionDenied,
5702 .BADF => unreachable, // always a race condition
5703 .FAULT => unreachable,
5704 .INVAL => unreachable,
5705 .ROFS => return error.ReadOnlyFileSystem,
5706 else => |err| return unexpectedErrno(err),
5707 }
5708}
5709
5710pub const GetHostNameError = error{PermissionDenied} || UnexpectedError;
5711
5712pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
5713 if (builtin.link_libc) {
5714 switch (errno(system.gethostname(name_buffer, name_buffer.len))) {
5715 .SUCCESS => return mem.sliceTo(name_buffer, 0),
5716 .FAULT => unreachable,
5717 .NAMETOOLONG => unreachable, // HOST_NAME_MAX prevents this
5718 .PERM => return error.PermissionDenied,
5719 else => |err| return unexpectedErrno(err),
5720 }
5721 }
5722 if (native_os == .linux) {
5723 const uts = uname();
5724 const hostname = mem.sliceTo(&uts.nodename, 0);
5725 const result = name_buffer[0..hostname.len];
5726 @memcpy(result, hostname);
5727 return result;
5728 }
5729
5730 @compileError("TODO implement gethostname for this OS");
5731}
5732
5733pub fn uname() utsname {
5734 var uts: utsname = undefined;
5735 switch (errno(system.uname(&uts))) {
5736 .SUCCESS => return uts,
5737 .FAULT => unreachable,
5738 else => unreachable,
5739 }
5740}
5741
5742pub fn res_mkquery(
5743 op: u4,
5744 dname: []const u8,
5745 class: u8,
5746 ty: u8,
5747 data: []const u8,
5748 newrr: ?[*]const u8,
5749 buf: []u8,
5750) usize {
5751 _ = data;
5752 _ = newrr;
5753 // This implementation is ported from musl libc.
5754 // A more idiomatic "ziggy" implementation would be welcome.
5755 var name = dname;
5756 if (mem.endsWith(u8, name, ".")) name.len -= 1;
5757 assert(name.len <= 253);
5758 const n = 17 + name.len + @intFromBool(name.len != 0);
5759
5760 // Construct query template - ID will be filled later
5761 var q: [280]u8 = undefined;
5762 @memset(q[0..n], 0);
5763 q[2] = @as(u8, op) * 8 + 1;
5764 q[5] = 1;
5765 @memcpy(q[13..][0..name.len], name);
5766 var i: usize = 13;
5767 var j: usize = undefined;
5768 while (q[i] != 0) : (i = j + 1) {
5769 j = i;
5770 while (q[j] != 0 and q[j] != '.') : (j += 1) {}
5771 // TODO determine the circumstances for this and whether or
5772 // not this should be an error.
5773 if (j - i - 1 > 62) unreachable;
5774 q[i - 1] = @intCast(j - i);
5775 }
5776 q[i + 1] = ty;
5777 q[i + 3] = class;
5778
5779 // Make a reasonably unpredictable id
5780 var ts: timespec = undefined;
5781 clock_gettime(CLOCK.REALTIME, &ts) catch {};
5782 const UInt = std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(ts.tv_nsec)));
5783 const unsec: UInt = @bitCast(ts.tv_nsec);
5784 const id: u32 = @truncate(unsec + unsec / 65536);
5785 q[0] = @truncate(id / 256);
5786 q[1] = @truncate(id);
5787
5788 @memcpy(buf[0..n], q[0..n]);
5789 return n;
5790}
5791
5792pub const SendError = error{
5793 /// (For UNIX domain sockets, which are identified by pathname) Write permission is denied
5794 /// on the destination socket file, or search permission is denied for one of the
5795 /// directories the path prefix. (See path_resolution(7).)
5796 /// (For UDP sockets) An attempt was made to send to a network/broadcast address as though
5797 /// it was a unicast address.
5798 AccessDenied,
5799
5800 /// The socket is marked nonblocking and the requested operation would block, and
5801 /// there is no global event loop configured.
5802 /// It's also possible to get this error under the following condition:
5803 /// (Internet domain datagram sockets) The socket referred to by sockfd had not previously
5804 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it was
5805 /// determined that all port numbers in the ephemeral port range are currently in use. See
5806 /// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
5807 WouldBlock,
5808
5809 /// Another Fast Open is already in progress.
5810 FastOpenAlreadyInProgress,
5811
5812 /// Connection reset by peer.
5813 ConnectionResetByPeer,
5814
5815 /// The socket type requires that message be sent atomically, and the size of the message
5816 /// to be sent made this impossible. The message is not transmitted.
5817 MessageTooBig,
5818
5819 /// The output queue for a network interface was full. This generally indicates that the
5820 /// interface has stopped sending, but may be caused by transient congestion. (Normally,
5821 /// this does not occur in Linux. Packets are just silently dropped when a device queue
5822 /// overflows.)
5823 /// This is also caused when there is not enough kernel memory available.
5824 SystemResources,
5825
5826 /// The local end has been shut down on a connection oriented socket. In this case, the
5827 /// process will also receive a SIGPIPE unless MSG.NOSIGNAL is set.
5828 BrokenPipe,
5829
5830 FileDescriptorNotASocket,
5831
5832 /// Network is unreachable.
5833 NetworkUnreachable,
5834
5835 /// The local network interface used to reach the destination is down.
5836 NetworkSubsystemFailed,
5837} || UnexpectedError;
5838
5839pub const SendMsgError = SendError || error{
5840 /// The passed address didn't have the correct address family in its sa_family field.
5841 AddressFamilyNotSupported,
5842
5843 /// Returned when socket is AF.UNIX and the given path has a symlink loop.
5844 SymLinkLoop,
5845
5846 /// Returned when socket is AF.UNIX and the given path length exceeds `max_path_bytes` bytes.
5847 NameTooLong,
5848
5849 /// Returned when socket is AF.UNIX and the given path does not point to an existing file.
5850 FileNotFound,
5851 NotDir,
5852
5853 /// The socket is not connected (connection-oriented sockets only).
5854 SocketNotConnected,
5855 AddressNotAvailable,
5856};
5857
5858pub fn sendmsg(
5859 /// The file descriptor of the sending socket.
5860 sockfd: socket_t,
5861 /// Message header and iovecs
5862 msg: *const msghdr_const,
5863 flags: u32,
5864) SendMsgError!usize {
5865 while (true) {
5866 const rc = system.sendmsg(sockfd, msg, flags);
5867 if (native_os == .windows) {
5868 if (rc == windows.ws2_32.SOCKET_ERROR) {
5869 switch (windows.ws2_32.WSAGetLastError()) {
5870 .WSAEACCES => return error.AccessDenied,
5871 .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
5872 .WSAECONNRESET => return error.ConnectionResetByPeer,
5873 .WSAEMSGSIZE => return error.MessageTooBig,
5874 .WSAENOBUFS => return error.SystemResources,
5875 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
5876 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
5877 .WSAEDESTADDRREQ => unreachable, // A destination address is required.
5878 .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.
5879 .WSAEHOSTUNREACH => return error.NetworkUnreachable,
5880 // TODO: WSAEINPROGRESS, WSAEINTR
5881 .WSAEINVAL => unreachable,
5882 .WSAENETDOWN => return error.NetworkSubsystemFailed,
5883 .WSAENETRESET => return error.ConnectionResetByPeer,
5884 .WSAENETUNREACH => return error.NetworkUnreachable,
5885 .WSAENOTCONN => return error.SocketNotConnected,
5886 .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.
5887 .WSAEWOULDBLOCK => return error.WouldBlock,
5888 .WSANOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function.
5889 else => |err| return windows.unexpectedWSAError(err),
5890 }
5891 } else {
5892 return @intCast(rc);
5893 }
5894 } else {
5895 switch (errno(rc)) {
5896 .SUCCESS => return @intCast(rc),
5897
5898 .ACCES => return error.AccessDenied,
5899 .AGAIN => return error.WouldBlock,
5900 .ALREADY => return error.FastOpenAlreadyInProgress,
5901 .BADF => unreachable, // always a race condition
5902 .CONNRESET => return error.ConnectionResetByPeer,
5903 .DESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
5904 .FAULT => unreachable, // An invalid user space address was specified for an argument.
5905 .INTR => continue,
5906 .INVAL => unreachable, // Invalid argument passed.
5907 .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
5908 .MSGSIZE => return error.MessageTooBig,
5909 .NOBUFS => return error.SystemResources,
5910 .NOMEM => return error.SystemResources,
5911 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
5912 .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
5913 .PIPE => return error.BrokenPipe,
5914 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
5915 .LOOP => return error.SymLinkLoop,
5916 .NAMETOOLONG => return error.NameTooLong,
5917 .NOENT => return error.FileNotFound,
5918 .NOTDIR => return error.NotDir,
5919 .HOSTUNREACH => return error.NetworkUnreachable,
5920 .NETUNREACH => return error.NetworkUnreachable,
5921 .NOTCONN => return error.SocketNotConnected,
5922 .NETDOWN => return error.NetworkSubsystemFailed,
5923 else => |err| return unexpectedErrno(err),
5924 }
5925 }
5926 }
5927}
5928
5929pub const SendToError = SendMsgError || error{
5930 /// The destination address is not reachable by the bound address.
5931 UnreachableAddress,
5932};
5933
5934/// Transmit a message to another socket.
5935///
5936/// The `sendto` call may be used only when the socket is in a connected state (so that the intended
5937/// recipient is known). The following call
5938///
5939/// send(sockfd, buf, len, flags);
5940///
5941/// is equivalent to
5942///
5943/// sendto(sockfd, buf, len, flags, NULL, 0);
5944///
5945/// If sendto() is used on a connection-mode (`SOCK.STREAM`, `SOCK.SEQPACKET`) socket, the arguments
5946/// `dest_addr` and `addrlen` are asserted to be `null` and `0` respectively, and asserted
5947/// that the socket was actually connected.
5948/// Otherwise, the address of the target is given by `dest_addr` with `addrlen` specifying its size.
5949///
5950/// If the message is too long to pass atomically through the underlying protocol,
5951/// `SendError.MessageTooBig` is returned, and the message is not transmitted.
5952///
5953/// There is no indication of failure to deliver.
5954///
5955/// When the message does not fit into the send buffer of the socket, `sendto` normally blocks,
5956/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail
5957/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is
5958/// possible to send more data.
5959pub fn sendto(
5960 /// The file descriptor of the sending socket.
5961 sockfd: socket_t,
5962 /// Message to send.
5963 buf: []const u8,
5964 flags: u32,
5965 dest_addr: ?*const sockaddr,
5966 addrlen: socklen_t,
5967) SendToError!usize {
5968 if (native_os == .windows) {
5969 switch (windows.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen)) {
5970 windows.ws2_32.SOCKET_ERROR => switch (windows.ws2_32.WSAGetLastError()) {
5971 .WSAEACCES => return error.AccessDenied,
5972 .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
5973 .WSAECONNRESET => return error.ConnectionResetByPeer,
5974 .WSAEMSGSIZE => return error.MessageTooBig,
5975 .WSAENOBUFS => return error.SystemResources,
5976 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
5977 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
5978 .WSAEDESTADDRREQ => unreachable, // A destination address is required.
5979 .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.
5980 .WSAEHOSTUNREACH => return error.NetworkUnreachable,
5981 // TODO: WSAEINPROGRESS, WSAEINTR
5982 .WSAEINVAL => unreachable,
5983 .WSAENETDOWN => return error.NetworkSubsystemFailed,
5984 .WSAENETRESET => return error.ConnectionResetByPeer,
5985 .WSAENETUNREACH => return error.NetworkUnreachable,
5986 .WSAENOTCONN => return error.SocketNotConnected,
5987 .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.
5988 .WSAEWOULDBLOCK => return error.WouldBlock,
5989 .WSANOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function.
5990 else => |err| return windows.unexpectedWSAError(err),
5991 },
5992 else => |rc| return @intCast(rc),
5993 }
5994 }
5995 while (true) {
5996 const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);
5997 switch (errno(rc)) {
5998 .SUCCESS => return @intCast(rc),
5999
6000 .ACCES => return error.AccessDenied,
6001 .AGAIN => return error.WouldBlock,
6002 .ALREADY => return error.FastOpenAlreadyInProgress,
6003 .BADF => unreachable, // always a race condition
6004 .CONNRESET => return error.ConnectionResetByPeer,
6005 .DESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
6006 .FAULT => unreachable, // An invalid user space address was specified for an argument.
6007 .INTR => continue,
6008 .INVAL => return error.UnreachableAddress,
6009 .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
6010 .MSGSIZE => return error.MessageTooBig,
6011 .NOBUFS => return error.SystemResources,
6012 .NOMEM => return error.SystemResources,
6013 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
6014 .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
6015 .PIPE => return error.BrokenPipe,
6016 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
6017 .LOOP => return error.SymLinkLoop,
6018 .NAMETOOLONG => return error.NameTooLong,
6019 .NOENT => return error.FileNotFound,
6020 .NOTDIR => return error.NotDir,
6021 .HOSTUNREACH => return error.NetworkUnreachable,
6022 .NETUNREACH => return error.NetworkUnreachable,
6023 .NOTCONN => return error.SocketNotConnected,
6024 .NETDOWN => return error.NetworkSubsystemFailed,
6025 else => |err| return unexpectedErrno(err),
6026 }
6027 }
6028}
6029
6030/// Transmit a message to another socket.
6031///
6032/// The `send` call may be used only when the socket is in a connected state (so that the intended
6033/// recipient is known). The only difference between `send` and `write` is the presence of
6034/// flags. With a zero flags argument, `send` is equivalent to `write`. Also, the following
6035/// call
6036///
6037/// send(sockfd, buf, len, flags);
6038///
6039/// is equivalent to
6040///
6041/// sendto(sockfd, buf, len, flags, NULL, 0);
6042///
6043/// There is no indication of failure to deliver.
6044///
6045/// When the message does not fit into the send buffer of the socket, `send` normally blocks,
6046/// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail
6047/// with `SendError.WouldBlock`. The `select` call may be used to determine when it is
6048/// possible to send more data.
6049pub fn send(
6050 /// The file descriptor of the sending socket.
6051 sockfd: socket_t,
6052 buf: []const u8,
6053 flags: u32,
6054) SendError!usize {
6055 return sendto(sockfd, buf, flags, null, 0) catch |err| switch (err) {
6056 error.AddressFamilyNotSupported => unreachable,
6057 error.SymLinkLoop => unreachable,
6058 error.NameTooLong => unreachable,
6059 error.FileNotFound => unreachable,
6060 error.NotDir => unreachable,
6061 error.NetworkUnreachable => unreachable,
6062 error.AddressNotAvailable => unreachable,
6063 error.SocketNotConnected => unreachable,
6064 error.UnreachableAddress => unreachable,
6065 else => |e| return e,
6066 };
6067}
6068
6069pub const SendFileError = PReadError || WriteError || SendError;
6070
6071/// Transfer data between file descriptors, with optional headers and trailers.
6072///
6073/// Returns the number of bytes written, which can be zero.
6074///
6075/// The `sendfile` call copies `in_len` bytes from one file descriptor to another. When possible,
6076/// this is done within the operating system kernel, which can provide better performance
6077/// characteristics than transferring data from kernel to user space and back, such as with
6078/// `read` and `write` calls. When `in_len` is `0`, it means to copy until the end of the input file has been
6079/// reached. Note, however, that partial writes are still possible in this case.
6080///
6081/// `in_fd` must be a file descriptor opened for reading, and `out_fd` must be a file descriptor
6082/// opened for writing. They may be any kind of file descriptor; however, if `in_fd` is not a regular
6083/// file system file, it may cause this function to fall back to calling `read` and `write`, in which case
6084/// atomicity guarantees no longer apply.
6085///
6086/// Copying begins reading at `in_offset`. The input file descriptor seek position is ignored and not updated.
6087/// If the output file descriptor has a seek position, it is updated as bytes are written. When
6088/// `in_offset` is past the end of the input file, it successfully reads 0 bytes.
6089///
6090/// `flags` has different meanings per operating system; refer to the respective man pages.
6091///
6092/// These systems support atomically sending everything, including headers and trailers:
6093/// * macOS
6094/// * FreeBSD
6095///
6096/// These systems support in-kernel data copying, but headers and trailers are not sent atomically:
6097/// * Linux
6098///
6099/// Other systems fall back to calling `read` / `write`.
6100///
6101/// Linux has a limit on how many bytes may be transferred in one `sendfile` call, which is `0x7ffff000`
6102/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
6103/// well as stuffing the errno codes into the last `4096` values. This is noted on the `sendfile` man page.
6104/// The limit on Darwin is `0x7fffffff`, trying to write more than that returns EINVAL.
6105/// The corresponding POSIX limit on this is `maxInt(isize)`.
6106pub fn sendfile(
6107 out_fd: fd_t,
6108 in_fd: fd_t,
6109 in_offset: u64,
6110 in_len: u64,
6111 headers: []const iovec_const,
6112 trailers: []const iovec_const,
6113 flags: u32,
6114) SendFileError!usize {
6115 var header_done = false;
6116 var total_written: usize = 0;
6117
6118 // Prevents EOVERFLOW.
6119 const size_t = std.meta.Int(.unsigned, @typeInfo(usize).Int.bits - 1);
6120 const max_count = switch (native_os) {
6121 .linux => 0x7ffff000,
6122 .macos, .ios, .watchos, .tvos => maxInt(i32),
6123 else => maxInt(size_t),
6124 };
6125
6126 switch (native_os) {
6127 .linux => sf: {
6128 // sendfile() first appeared in Linux 2.2, glibc 2.1.
6129 const call_sf = comptime if (builtin.link_libc)
6130 std.c.versionCheck(.{ .major = 2, .minor = 1, .patch = 0 })
6131 else
6132 builtin.os.version_range.linux.range.max.order(.{ .major = 2, .minor = 2, .patch = 0 }) != .lt;
6133 if (!call_sf) break :sf;
6134
6135 if (headers.len != 0) {
6136 const amt = try writev(out_fd, headers);
6137 total_written += amt;
6138 if (amt < count_iovec_bytes(headers)) return total_written;
6139 header_done = true;
6140 }
6141
6142 // Here we match BSD behavior, making a zero count value send as many bytes as possible.
6143 const adjusted_count = if (in_len == 0) max_count else @min(in_len, max_count);
6144
6145 const sendfile_sym = if (lfs64_abi) system.sendfile64 else system.sendfile;
6146 while (true) {
6147 var offset: off_t = @bitCast(in_offset);
6148 const rc = sendfile_sym(out_fd, in_fd, &offset, adjusted_count);
6149 switch (errno(rc)) {
6150 .SUCCESS => {
6151 const amt: usize = @bitCast(rc);
6152 total_written += amt;
6153 if (in_len == 0 and amt == 0) {
6154 // We have detected EOF from `in_fd`.
6155 break;
6156 } else if (amt < in_len) {
6157 return total_written;
6158 } else {
6159 break;
6160 }
6161 },
6162
6163 .BADF => unreachable, // Always a race condition.
6164 .FAULT => unreachable, // Segmentation fault.
6165 .OVERFLOW => unreachable, // We avoid passing too large of a `count`.
6166 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
6167
6168 .INVAL, .NOSYS => {
6169 // EINVAL could be any of the following situations:
6170 // * Descriptor is not valid or locked
6171 // * an mmap(2)-like operation is not available for in_fd
6172 // * count is negative
6173 // * out_fd has the APPEND flag set
6174 // Because of the "mmap(2)-like operation" possibility, we fall back to doing read/write
6175 // manually, the same as ENOSYS.
6176 break :sf;
6177 },
6178 .AGAIN => return error.WouldBlock,
6179 .IO => return error.InputOutput,
6180 .PIPE => return error.BrokenPipe,
6181 .NOMEM => return error.SystemResources,
6182 .NXIO => return error.Unseekable,
6183 .SPIPE => return error.Unseekable,
6184 else => |err| {
6185 unexpectedErrno(err) catch {};
6186 break :sf;
6187 },
6188 }
6189 }
6190
6191 if (trailers.len != 0) {
6192 total_written += try writev(out_fd, trailers);
6193 }
6194
6195 return total_written;
6196 },
6197 .freebsd => sf: {
6198 var hdtr_data: std.c.sf_hdtr = undefined;
6199 var hdtr: ?*std.c.sf_hdtr = null;
6200 if (headers.len != 0 or trailers.len != 0) {
6201 // Here we carefully avoid `@intCast` by returning partial writes when
6202 // too many io vectors are provided.
6203 const hdr_cnt = cast(u31, headers.len) orelse maxInt(u31);
6204 if (headers.len > hdr_cnt) return writev(out_fd, headers);
6205
6206 const trl_cnt = cast(u31, trailers.len) orelse maxInt(u31);
6207
6208 hdtr_data = std.c.sf_hdtr{
6209 .headers = headers.ptr,
6210 .hdr_cnt = hdr_cnt,
6211 .trailers = trailers.ptr,
6212 .trl_cnt = trl_cnt,
6213 };
6214 hdtr = &hdtr_data;
6215 }
6216
6217 while (true) {
6218 var sbytes: off_t = undefined;
6219 const err = errno(system.sendfile(in_fd, out_fd, @bitCast(in_offset), @min(in_len, max_count), hdtr, &sbytes, flags));
6220 const amt: usize = @bitCast(sbytes);
6221 switch (err) {
6222 .SUCCESS => return amt,
6223
6224 .BADF => unreachable, // Always a race condition.
6225 .FAULT => unreachable, // Segmentation fault.
6226 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
6227
6228 .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => {
6229 // EINVAL could be any of the following situations:
6230 // * The fd argument is not a regular file.
6231 // * The s argument is not a SOCK.STREAM type socket.
6232 // * The offset argument is negative.
6233 // Because of some of these possibilities, we fall back to doing read/write
6234 // manually, the same as ENOSYS.
6235 break :sf;
6236 },
6237
6238 .INTR => if (amt != 0) return amt else continue,
6239
6240 .AGAIN => if (amt != 0) {
6241 return amt;
6242 } else {
6243 return error.WouldBlock;
6244 },
6245
6246 .BUSY => if (amt != 0) {
6247 return amt;
6248 } else {
6249 return error.WouldBlock;
6250 },
6251
6252 .IO => return error.InputOutput,
6253 .NOBUFS => return error.SystemResources,
6254 .PIPE => return error.BrokenPipe,
6255
6256 else => {
6257 unexpectedErrno(err) catch {};
6258 if (amt != 0) {
6259 return amt;
6260 } else {
6261 break :sf;
6262 }
6263 },
6264 }
6265 }
6266 },
6267 .macos, .ios, .tvos, .watchos => sf: {
6268 var hdtr_data: std.c.sf_hdtr = undefined;
6269 var hdtr: ?*std.c.sf_hdtr = null;
6270 if (headers.len != 0 or trailers.len != 0) {
6271 // Here we carefully avoid `@intCast` by returning partial writes when
6272 // too many io vectors are provided.
6273 const hdr_cnt = cast(u31, headers.len) orelse maxInt(u31);
6274 if (headers.len > hdr_cnt) return writev(out_fd, headers);
6275
6276 const trl_cnt = cast(u31, trailers.len) orelse maxInt(u31);
6277
6278 hdtr_data = std.c.sf_hdtr{
6279 .headers = headers.ptr,
6280 .hdr_cnt = hdr_cnt,
6281 .trailers = trailers.ptr,
6282 .trl_cnt = trl_cnt,
6283 };
6284 hdtr = &hdtr_data;
6285 }
6286
6287 while (true) {
6288 var sbytes: off_t = @min(in_len, max_count);
6289 const err = errno(system.sendfile(in_fd, out_fd, @bitCast(in_offset), &sbytes, hdtr, flags));
6290 const amt: usize = @bitCast(sbytes);
6291 switch (err) {
6292 .SUCCESS => return amt,
6293
6294 .BADF => unreachable, // Always a race condition.
6295 .FAULT => unreachable, // Segmentation fault.
6296 .INVAL => unreachable,
6297 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
6298
6299 .OPNOTSUPP, .NOTSOCK, .NOSYS => break :sf,
6300
6301 .INTR => if (amt != 0) return amt else continue,
6302
6303 .AGAIN => if (amt != 0) {
6304 return amt;
6305 } else {
6306 return error.WouldBlock;
6307 },
6308
6309 .IO => return error.InputOutput,
6310 .PIPE => return error.BrokenPipe,
6311
6312 else => {
6313 unexpectedErrno(err) catch {};
6314 if (amt != 0) {
6315 return amt;
6316 } else {
6317 break :sf;
6318 }
6319 },
6320 }
6321 }
6322 },
6323 else => {}, // fall back to read/write
6324 }
6325
6326 if (headers.len != 0 and !header_done) {
6327 const amt = try writev(out_fd, headers);
6328 total_written += amt;
6329 if (amt < count_iovec_bytes(headers)) return total_written;
6330 }
6331
6332 rw: {
6333 var buf: [8 * 4096]u8 = undefined;
6334 // Here we match BSD behavior, making a zero count value send as many bytes as possible.
6335 const adjusted_count = if (in_len == 0) buf.len else @min(buf.len, in_len);
6336 const amt_read = try pread(in_fd, buf[0..adjusted_count], in_offset);
6337 if (amt_read == 0) {
6338 if (in_len == 0) {
6339 // We have detected EOF from `in_fd`.
6340 break :rw;
6341 } else {
6342 return total_written;
6343 }
6344 }
6345 const amt_written = try write(out_fd, buf[0..amt_read]);
6346 total_written += amt_written;
6347 if (amt_written < in_len or in_len == 0) return total_written;
6348 }
6349
6350 if (trailers.len != 0) {
6351 total_written += try writev(out_fd, trailers);
6352 }
6353
6354 return total_written;
6355}
6356
6357fn count_iovec_bytes(iovs: []const iovec_const) usize {
6358 var count: usize = 0;
6359 for (iovs) |iov| {
6360 count += iov.iov_len;
6361 }
6362 return count;
6363}
6364
6365pub const CopyFileRangeError = error{
6366 FileTooBig,
6367 InputOutput,
6368 /// `fd_in` is not open for reading; or `fd_out` is not open for writing;
6369 /// or the `APPEND` flag is set for `fd_out`.
6370 FilesOpenedWithWrongFlags,
6371 IsDir,
6372 OutOfMemory,
6373 NoSpaceLeft,
6374 Unseekable,
6375 PermissionDenied,
6376 SwapFile,
6377 CorruptedData,
6378} || PReadError || PWriteError || UnexpectedError;
6379
6380/// Transfer data between file descriptors at specified offsets.
6381///
6382/// Returns the number of bytes written, which can less than requested.
6383///
6384/// The `copy_file_range` call copies `len` bytes from one file descriptor to another. When possible,
6385/// this is done within the operating system kernel, which can provide better performance
6386/// characteristics than transferring data from kernel to user space and back, such as with
6387/// `pread` and `pwrite` calls.
6388///
6389/// `fd_in` must be a file descriptor opened for reading, and `fd_out` must be a file descriptor
6390/// opened for writing. They may be any kind of file descriptor; however, if `fd_in` is not a regular
6391/// file system file, it may cause this function to fall back to calling `pread` and `pwrite`, in which case
6392/// atomicity guarantees no longer apply.
6393///
6394/// If `fd_in` and `fd_out` are the same, source and target ranges must not overlap.
6395/// The file descriptor seek positions are ignored and not updated.
6396/// When `off_in` is past the end of the input file, it successfully reads 0 bytes.
6397///
6398/// `flags` has different meanings per operating system; refer to the respective man pages.
6399///
6400/// These systems support in-kernel data copying:
6401/// * Linux 4.5 (cross-filesystem 5.3)
6402/// * FreeBSD 13.0
6403///
6404/// Other systems fall back to calling `pread` / `pwrite`.
6405///
6406/// Maximum offsets on Linux and FreeBSD are `maxInt(i64)`.
6407pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len: usize, flags: u32) CopyFileRangeError!usize {
6408 const global = struct {
6409 var has_copy_file_range = true;
6410 };
6411
6412 if ((comptime builtin.os.isAtLeast(.freebsd, .{ .major = 13, .minor = 0, .patch = 0 }) orelse false) or
6413 ((comptime builtin.os.isAtLeast(.linux, .{ .major = 4, .minor = 5, .patch = 0 }) orelse false and
6414 std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 })) and
6415 @atomicLoad(bool, &global.has_copy_file_range, .monotonic)))
6416 {
6417 var off_in_copy: i64 = @bitCast(off_in);
6418 var off_out_copy: i64 = @bitCast(off_out);
6419
6420 while (true) {
6421 const rc = system.copy_file_range(fd_in, &off_in_copy, fd_out, &off_out_copy, len, flags);
6422 if (native_os == .freebsd) {
6423 switch (errno(rc)) {
6424 .SUCCESS => return @intCast(rc),
6425 .BADF => return error.FilesOpenedWithWrongFlags,
6426 .FBIG => return error.FileTooBig,
6427 .IO => return error.InputOutput,
6428 .ISDIR => return error.IsDir,
6429 .NOSPC => return error.NoSpaceLeft,
6430 .INVAL => break, // these may not be regular files, try fallback
6431 .INTEGRITY => return error.CorruptedData,
6432 .INTR => continue,
6433 else => |err| return unexpectedErrno(err),
6434 }
6435 } else { // assume linux
6436 switch (errno(rc)) {
6437 .SUCCESS => return @intCast(rc),
6438 .BADF => return error.FilesOpenedWithWrongFlags,
6439 .FBIG => return error.FileTooBig,
6440 .IO => return error.InputOutput,
6441 .ISDIR => return error.IsDir,
6442 .NOSPC => return error.NoSpaceLeft,
6443 .INVAL => break, // these may not be regular files, try fallback
6444 .NOMEM => return error.OutOfMemory,
6445 .OVERFLOW => return error.Unseekable,
6446 .PERM => return error.PermissionDenied,
6447 .TXTBSY => return error.SwapFile,
6448 .XDEV => break, // support for cross-filesystem copy added in Linux 5.3, use fallback
6449 .NOSYS => {
6450 @atomicStore(bool, &global.has_copy_file_range, false, .monotonic);
6451 break;
6452 },
6453 else => |err| return unexpectedErrno(err),
6454 }
6455 }
6456 }
6457 }
6458
6459 var buf: [8 * 4096]u8 = undefined;
6460 const amt_read = try pread(fd_in, buf[0..@min(buf.len, len)], off_in);
6461 if (amt_read == 0) return 0;
6462 return pwrite(fd_out, buf[0..amt_read], off_out);
6463}
6464
6465pub const PollError = error{
6466 /// The network subsystem has failed.
6467 NetworkSubsystemFailed,
6468
6469 /// The kernel had no space to allocate file descriptor tables.
6470 SystemResources,
6471} || UnexpectedError;
6472
6473pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
6474 while (true) {
6475 const fds_count = cast(nfds_t, fds.len) orelse return error.SystemResources;
6476 const rc = system.poll(fds.ptr, fds_count, timeout);
6477 if (native_os == .windows) {
6478 if (rc == windows.ws2_32.SOCKET_ERROR) {
6479 switch (windows.ws2_32.WSAGetLastError()) {
6480 .WSANOTINITIALISED => unreachable,
6481 .WSAENETDOWN => return error.NetworkSubsystemFailed,
6482 .WSAENOBUFS => return error.SystemResources,
6483 // TODO: handle more errors
6484 else => |err| return windows.unexpectedWSAError(err),
6485 }
6486 } else {
6487 return @intCast(rc);
6488 }
6489 } else {
6490 switch (errno(rc)) {
6491 .SUCCESS => return @intCast(rc),
6492 .FAULT => unreachable,
6493 .INTR => continue,
6494 .INVAL => unreachable,
6495 .NOMEM => return error.SystemResources,
6496 else => |err| return unexpectedErrno(err),
6497 }
6498 }
6499 unreachable;
6500 }
6501}
6502
6503pub const PPollError = error{
6504 /// The operation was interrupted by a delivery of a signal before it could complete.
6505 SignalInterrupt,
6506
6507 /// The kernel had no space to allocate file descriptor tables.
6508 SystemResources,
6509} || UnexpectedError;
6510
6511pub fn ppoll(fds: []pollfd, timeout: ?*const timespec, mask: ?*const sigset_t) PPollError!usize {
6512 var ts: timespec = undefined;
6513 var ts_ptr: ?*timespec = null;
6514 if (timeout) |timeout_ns| {
6515 ts_ptr = &ts;
6516 ts = timeout_ns.*;
6517 }
6518 const fds_count = cast(nfds_t, fds.len) orelse return error.SystemResources;
6519 const rc = system.ppoll(fds.ptr, fds_count, ts_ptr, mask);
6520 switch (errno(rc)) {
6521 .SUCCESS => return @intCast(rc),
6522 .FAULT => unreachable,
6523 .INTR => return error.SignalInterrupt,
6524 .INVAL => unreachable,
6525 .NOMEM => return error.SystemResources,
6526 else => |err| return unexpectedErrno(err),
6527 }
6528}
6529
6530pub const RecvFromError = error{
6531 /// The socket is marked nonblocking and the requested operation would block, and
6532 /// there is no global event loop configured.
6533 WouldBlock,
6534
6535 /// A remote host refused to allow the network connection, typically because it is not
6536 /// running the requested service.
6537 ConnectionRefused,
6538
6539 /// Could not allocate kernel memory.
6540 SystemResources,
6541
6542 ConnectionResetByPeer,
6543 ConnectionTimedOut,
6544
6545 /// The socket has not been bound.
6546 SocketNotBound,
6547
6548 /// The UDP message was too big for the buffer and part of it has been discarded
6549 MessageTooBig,
6550
6551 /// The network subsystem has failed.
6552 NetworkSubsystemFailed,
6553
6554 /// The socket is not connected (connection-oriented sockets only).
6555 SocketNotConnected,
6556} || UnexpectedError;
6557
6558pub fn recv(sock: socket_t, buf: []u8, flags: u32) RecvFromError!usize {
6559 return recvfrom(sock, buf, flags, null, null);
6560}
6561
6562/// If `sockfd` is opened in non blocking mode, the function will
6563/// return error.WouldBlock when EAGAIN is received.
6564pub fn recvfrom(
6565 sockfd: socket_t,
6566 buf: []u8,
6567 flags: u32,
6568 src_addr: ?*sockaddr,
6569 addrlen: ?*socklen_t,
6570) RecvFromError!usize {
6571 while (true) {
6572 const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen);
6573 if (native_os == .windows) {
6574 if (rc == windows.ws2_32.SOCKET_ERROR) {
6575 switch (windows.ws2_32.WSAGetLastError()) {
6576 .WSANOTINITIALISED => unreachable,
6577 .WSAECONNRESET => return error.ConnectionResetByPeer,
6578 .WSAEINVAL => return error.SocketNotBound,
6579 .WSAEMSGSIZE => return error.MessageTooBig,
6580 .WSAENETDOWN => return error.NetworkSubsystemFailed,
6581 .WSAENOTCONN => return error.SocketNotConnected,
6582 .WSAEWOULDBLOCK => return error.WouldBlock,
6583 .WSAETIMEDOUT => return error.ConnectionTimedOut,
6584 // TODO: handle more errors
6585 else => |err| return windows.unexpectedWSAError(err),
6586 }
6587 } else {
6588 return @intCast(rc);
6589 }
6590 } else {
6591 switch (errno(rc)) {
6592 .SUCCESS => return @intCast(rc),
6593 .BADF => unreachable, // always a race condition
6594 .FAULT => unreachable,
6595 .INVAL => unreachable,
6596 .NOTCONN => return error.SocketNotConnected,
6597 .NOTSOCK => unreachable,
6598 .INTR => continue,
6599 .AGAIN => return error.WouldBlock,
6600 .NOMEM => return error.SystemResources,
6601 .CONNREFUSED => return error.ConnectionRefused,
6602 .CONNRESET => return error.ConnectionResetByPeer,
6603 .TIMEDOUT => return error.ConnectionTimedOut,
6604 else => |err| return unexpectedErrno(err),
6605 }
6606 }
6607 }
6608}
6609
6610pub const DnExpandError = error{InvalidDnsPacket};
6611
6612pub fn dn_expand(
6613 msg: []const u8,
6614 comp_dn: []const u8,
6615 exp_dn: []u8,
6616) DnExpandError!usize {
6617 // This implementation is ported from musl libc.
6618 // A more idiomatic "ziggy" implementation would be welcome.
6619 var p = comp_dn.ptr;
6620 var len: usize = maxInt(usize);
6621 const end = msg.ptr + msg.len;
6622 if (p == end or exp_dn.len == 0) return error.InvalidDnsPacket;
6623 var dest = exp_dn.ptr;
6624 const dend = dest + @min(exp_dn.len, 254);
6625 // detect reference loop using an iteration counter
6626 var i: usize = 0;
6627 while (i < msg.len) : (i += 2) {
6628 // loop invariants: p<end, dest<dend
6629 if ((p[0] & 0xc0) != 0) {
6630 if (p + 1 == end) return error.InvalidDnsPacket;
6631 const j = @as(usize, p[0] & 0x3f) << 8 | p[1];
6632 if (len == maxInt(usize)) len = @intFromPtr(p) + 2 - @intFromPtr(comp_dn.ptr);
6633 if (j >= msg.len) return error.InvalidDnsPacket;
6634 p = msg.ptr + j;
6635 } else if (p[0] != 0) {
6636 if (dest != exp_dn.ptr) {
6637 dest[0] = '.';
6638 dest += 1;
6639 }
6640 var j = p[0];
6641 p += 1;
6642 if (j >= @intFromPtr(end) - @intFromPtr(p) or j >= @intFromPtr(dend) - @intFromPtr(dest)) {
6643 return error.InvalidDnsPacket;
6644 }
6645 while (j != 0) {
6646 j -= 1;
6647 dest[0] = p[0];
6648 dest += 1;
6649 p += 1;
6650 }
6651 } else {
6652 dest[0] = 0;
6653 if (len == maxInt(usize)) len = @intFromPtr(p) + 1 - @intFromPtr(comp_dn.ptr);
6654 return len;
6655 }
6656 }
6657 return error.InvalidDnsPacket;
6658}
6659
6660pub const SetSockOptError = error{
6661 /// The socket is already connected, and a specified option cannot be set while the socket is connected.
6662 AlreadyConnected,
6663
6664 /// The option is not supported by the protocol.
6665 InvalidProtocolOption,
6666
6667 /// The send and receive timeout values are too big to fit into the timeout fields in the socket structure.
6668 TimeoutTooBig,
6669
6670 /// Insufficient resources are available in the system to complete the call.
6671 SystemResources,
6672
6673 // Setting the socket option requires more elevated permissions.
6674 PermissionDenied,
6675
6676 NetworkSubsystemFailed,
6677 FileDescriptorNotASocket,
6678 SocketNotBound,
6679 NoDevice,
6680} || UnexpectedError;
6681
6682/// Set a socket's options.
6683pub fn setsockopt(fd: socket_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void {
6684 if (native_os == .windows) {
6685 const rc = windows.ws2_32.setsockopt(fd, @intCast(level), @intCast(optname), opt.ptr, @intCast(opt.len));
6686 if (rc == windows.ws2_32.SOCKET_ERROR) {
6687 switch (windows.ws2_32.WSAGetLastError()) {
6688 .WSANOTINITIALISED => unreachable,
6689 .WSAENETDOWN => return error.NetworkSubsystemFailed,
6690 .WSAEFAULT => unreachable,
6691 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
6692 .WSAEINVAL => return error.SocketNotBound,
6693 else => |err| return windows.unexpectedWSAError(err),
6694 }
6695 }
6696 return;
6697 } else {
6698 switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(opt.len)))) {
6699 .SUCCESS => {},
6700 .BADF => unreachable, // always a race condition
6701 .NOTSOCK => unreachable, // always a race condition
6702 .INVAL => unreachable,
6703 .FAULT => unreachable,
6704 .DOM => return error.TimeoutTooBig,
6705 .ISCONN => return error.AlreadyConnected,
6706 .NOPROTOOPT => return error.InvalidProtocolOption,
6707 .NOMEM => return error.SystemResources,
6708 .NOBUFS => return error.SystemResources,
6709 .PERM => return error.PermissionDenied,
6710 .NODEV => return error.NoDevice,
6711 else => |err| return unexpectedErrno(err),
6712 }
6713 }
6714}
6715
6716pub const MemFdCreateError = error{
6717 SystemFdQuotaExceeded,
6718 ProcessFdQuotaExceeded,
6719 OutOfMemory,
6720 /// Either the name provided exceeded `NAME_MAX`, or invalid flags were passed.
6721 NameTooLong,
6722
6723 /// memfd_create is available in Linux 3.17 and later. This error is returned
6724 /// for older kernel versions.
6725 SystemOutdated,
6726} || UnexpectedError;
6727
6728pub fn memfd_createZ(name: [*:0]const u8, flags: u32) MemFdCreateError!fd_t {
6729 switch (native_os) {
6730 .linux => {
6731 // memfd_create is available only in glibc versions starting with 2.27.
6732 const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 });
6733 const sys = if (use_c) std.c else linux;
6734 const rc = sys.memfd_create(name, flags);
6735 switch (errno(rc)) {
6736 .SUCCESS => return @intCast(rc),
6737 .FAULT => unreachable, // name has invalid memory
6738 .INVAL => return error.NameTooLong, // or, program has a bug and flags are faulty
6739 .NFILE => return error.SystemFdQuotaExceeded,
6740 .MFILE => return error.ProcessFdQuotaExceeded,
6741 .NOMEM => return error.OutOfMemory,
6742 .NOSYS => return error.SystemOutdated,
6743 else => |err| return unexpectedErrno(err),
6744 }
6745 },
6746 .freebsd => {
6747 if (comptime builtin.os.version_range.semver.max.order(.{ .major = 13, .minor = 0, .patch = 0 }) == .lt)
6748 @compileError("memfd_create is unavailable on FreeBSD < 13.0");
6749 const rc = system.memfd_create(name, flags);
6750 switch (errno(rc)) {
6751 .SUCCESS => return rc,
6752 .BADF => unreachable, // name argument NULL
6753 .INVAL => unreachable, // name too long or invalid/unsupported flags.
6754 .MFILE => return error.ProcessFdQuotaExceeded,
6755 .NFILE => return error.SystemFdQuotaExceeded,
6756 .NOSYS => return error.SystemOutdated,
6757 else => |err| return unexpectedErrno(err),
6758 }
6759 },
6760 else => @compileError("target OS does not support memfd_create()"),
6761 }
6762}
6763
6764pub fn memfd_create(name: []const u8, flags: u32) MemFdCreateError!fd_t {
6765 var buffer: [NAME_MAX - "memfd:".len - 1:0]u8 = undefined;
6766 if (name.len > buffer.len) return error.NameTooLong;
6767 @memcpy(buffer[0..name.len], name);
6768 buffer[name.len] = 0;
6769 return memfd_createZ(&buffer, flags);
6770}
6771
6772pub fn getrusage(who: i32) rusage {
6773 var result: rusage = undefined;
6774 const rc = system.getrusage(who, &result);
6775 switch (errno(rc)) {
6776 .SUCCESS => return result,
6777 .INVAL => unreachable,
6778 .FAULT => unreachable,
6779 else => unreachable,
6780 }
6781}
6782
6783pub const TIOCError = error{NotATerminal};
6784
6785pub const TermiosGetError = TIOCError || UnexpectedError;
6786
6787pub fn tcgetattr(handle: fd_t) TermiosGetError!termios {
6788 while (true) {
6789 var term: termios = undefined;
6790 switch (errno(system.tcgetattr(handle, &term))) {
6791 .SUCCESS => return term,
6792 .INTR => continue,
6793 .BADF => unreachable,
6794 .NOTTY => return error.NotATerminal,
6795 else => |err| return unexpectedErrno(err),
6796 }
6797 }
6798}
6799
6800pub const TermiosSetError = TermiosGetError || error{ProcessOrphaned};
6801
6802pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) TermiosSetError!void {
6803 while (true) {
6804 switch (errno(system.tcsetattr(handle, optional_action, &termios_p))) {
6805 .SUCCESS => return,
6806 .BADF => unreachable,
6807 .INTR => continue,
6808 .INVAL => unreachable,
6809 .NOTTY => return error.NotATerminal,
6810 .IO => return error.ProcessOrphaned,
6811 else => |err| return unexpectedErrno(err),
6812 }
6813 }
6814}
6815
6816pub const TermioGetPgrpError = TIOCError || UnexpectedError;
6817
6818/// Returns the process group ID for the TTY associated with the given handle.
6819pub fn tcgetpgrp(handle: fd_t) TermioGetPgrpError!pid_t {
6820 while (true) {
6821 var pgrp: pid_t = undefined;
6822 switch (errno(system.tcgetpgrp(handle, &pgrp))) {
6823 .SUCCESS => return pgrp,
6824 .BADF => unreachable,
6825 .INVAL => unreachable,
6826 .INTR => continue,
6827 .NOTTY => return error.NotATerminal,
6828 else => |err| return unexpectedErrno(err),
6829 }
6830 }
6831}
6832
6833pub const TermioSetPgrpError = TermioGetPgrpError || error{NotAPgrpMember};
6834
6835/// Sets the controlling process group ID for given TTY.
6836/// handle must be valid fd_t to a TTY associated with calling process.
6837/// pgrp must be a valid process group, and the calling process must be a member
6838/// of that group.
6839pub fn tcsetpgrp(handle: fd_t, pgrp: pid_t) TermioSetPgrpError!void {
6840 while (true) {
6841 switch (errno(system.tcsetpgrp(handle, &pgrp))) {
6842 .SUCCESS => return,
6843 .BADF => unreachable,
6844 .INVAL => unreachable,
6845 .INTR => continue,
6846 .NOTTY => return error.NotATerminal,
6847 .PERM => return TermioSetPgrpError.NotAPgrpMember,
6848 else => |err| return unexpectedErrno(err),
6849 }
6850 }
6851}
6852
6853pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t {
6854 const rc = system.signalfd(fd, mask, flags);
6855 switch (errno(rc)) {
6856 .SUCCESS => return @intCast(rc),
6857 .BADF, .INVAL => unreachable,
6858 .NFILE => return error.SystemFdQuotaExceeded,
6859 .NOMEM => return error.SystemResources,
6860 .MFILE => return error.ProcessResources,
6861 .NODEV => return error.InodeMountFail,
6862 .NOSYS => return error.SystemOutdated,
6863 else => |err| return unexpectedErrno(err),
6864 }
6865}
6866
6867pub const SyncError = error{
6868 InputOutput,
6869 NoSpaceLeft,
6870 DiskQuota,
6871 AccessDenied,
6872} || UnexpectedError;
6873
6874/// Write all pending file contents and metadata modifications to all filesystems.
6875pub fn sync() void {
6876 system.sync();
6877}
6878
6879/// Write all pending file contents and metadata modifications to the filesystem which contains the specified file.
6880pub fn syncfs(fd: fd_t) SyncError!void {
6881 const rc = system.syncfs(fd);
6882 switch (errno(rc)) {
6883 .SUCCESS => return,
6884 .BADF, .INVAL, .ROFS => unreachable,
6885 .IO => return error.InputOutput,
6886 .NOSPC => return error.NoSpaceLeft,
6887 .DQUOT => return error.DiskQuota,
6888 else => |err| return unexpectedErrno(err),
6889 }
6890}
6891
6892/// Write all pending file contents and metadata modifications for the specified file descriptor to the underlying filesystem.
6893pub fn fsync(fd: fd_t) SyncError!void {
6894 if (native_os == .windows) {
6895 if (windows.kernel32.FlushFileBuffers(fd) != 0)
6896 return;
6897 switch (windows.kernel32.GetLastError()) {
6898 .SUCCESS => return,
6899 .INVALID_HANDLE => unreachable,
6900 .ACCESS_DENIED => return error.AccessDenied, // a sync was performed but the system couldn't update the access time
6901 .UNEXP_NET_ERR => return error.InputOutput,
6902 else => return error.InputOutput,
6903 }
6904 }
6905 const rc = system.fsync(fd);
6906 switch (errno(rc)) {
6907 .SUCCESS => return,
6908 .BADF, .INVAL, .ROFS => unreachable,
6909 .IO => return error.InputOutput,
6910 .NOSPC => return error.NoSpaceLeft,
6911 .DQUOT => return error.DiskQuota,
6912 else => |err| return unexpectedErrno(err),
6913 }
6914}
6915
6916/// Write all pending file contents for the specified file descriptor to the underlying filesystem, but not necessarily the metadata.
6917pub fn fdatasync(fd: fd_t) SyncError!void {
6918 if (native_os == .windows) {
6919 return fsync(fd) catch |err| switch (err) {
6920 SyncError.AccessDenied => return, // fdatasync doesn't promise that the access time was synced
6921 else => return err,
6922 };
6923 }
6924 const rc = system.fdatasync(fd);
6925 switch (errno(rc)) {
6926 .SUCCESS => return,
6927 .BADF, .INVAL, .ROFS => unreachable,
6928 .IO => return error.InputOutput,
6929 .NOSPC => return error.NoSpaceLeft,
6930 .DQUOT => return error.DiskQuota,
6931 else => |err| return unexpectedErrno(err),
6932 }
6933}
6934
6935pub const PrctlError = error{
6936 /// Can only occur with PR_SET_SECCOMP/SECCOMP_MODE_FILTER or
6937 /// PR_SET_MM/PR_SET_MM_EXE_FILE
6938 AccessDenied,
6939 /// Can only occur with PR_SET_MM/PR_SET_MM_EXE_FILE
6940 InvalidFileDescriptor,
6941 InvalidAddress,
6942 /// Can only occur with PR_SET_SPECULATION_CTRL, PR_MPX_ENABLE_MANAGEMENT,
6943 /// or PR_MPX_DISABLE_MANAGEMENT
6944 UnsupportedFeature,
6945 /// Can only occur with PR_SET_FP_MODE
6946 OperationNotSupported,
6947 PermissionDenied,
6948} || UnexpectedError;
6949
6950pub fn prctl(option: PR, args: anytype) PrctlError!u31 {
6951 if (@typeInfo(@TypeOf(args)) != .Struct)
6952 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
6953 if (args.len > 4)
6954 @compileError("prctl takes a maximum of 4 optional arguments");
6955
6956 var buf: [4]usize = undefined;
6957 {
6958 comptime var i = 0;
6959 inline while (i < args.len) : (i += 1) buf[i] = args[i];
6960 }
6961
6962 const rc = system.prctl(@intFromEnum(option), buf[0], buf[1], buf[2], buf[3]);
6963 switch (errno(rc)) {
6964 .SUCCESS => return @intCast(rc),
6965 .ACCES => return error.AccessDenied,
6966 .BADF => return error.InvalidFileDescriptor,
6967 .FAULT => return error.InvalidAddress,
6968 .INVAL => unreachable,
6969 .NODEV, .NXIO => return error.UnsupportedFeature,
6970 .OPNOTSUPP => return error.OperationNotSupported,
6971 .PERM, .BUSY => return error.PermissionDenied,
6972 .RANGE => unreachable,
6973 else => |err| return unexpectedErrno(err),
6974 }
6975}
6976
6977pub const GetrlimitError = UnexpectedError;
6978
6979pub fn getrlimit(resource: rlimit_resource) GetrlimitError!rlimit {
6980 const getrlimit_sym = if (lfs64_abi) system.getrlimit64 else system.getrlimit;
6981
6982 var limits: rlimit = undefined;
6983 switch (errno(getrlimit_sym(resource, &limits))) {
6984 .SUCCESS => return limits,
6985 .FAULT => unreachable, // bogus pointer
6986 .INVAL => unreachable,
6987 else => |err| return unexpectedErrno(err),
6988 }
6989}
6990
6991pub const SetrlimitError = error{ PermissionDenied, LimitTooBig } || UnexpectedError;
6992
6993pub fn setrlimit(resource: rlimit_resource, limits: rlimit) SetrlimitError!void {
6994 const setrlimit_sym = if (lfs64_abi) system.setrlimit64 else system.setrlimit;
6995
6996 switch (errno(setrlimit_sym(resource, &limits))) {
6997 .SUCCESS => return,
6998 .FAULT => unreachable, // bogus pointer
6999 .INVAL => return error.LimitTooBig, // this could also mean "invalid resource", but that would be unreachable
7000 .PERM => return error.PermissionDenied,
7001 else => |err| return unexpectedErrno(err),
7002 }
7003}
7004
7005pub const MincoreError = error{
7006 /// A kernel resource was temporarily unavailable.
7007 SystemResources,
7008 /// vec points to an invalid address.
7009 InvalidAddress,
7010 /// addr is not page-aligned.
7011 InvalidSyscall,
7012 /// One of the following:
7013 /// * length is greater than user space TASK_SIZE - addr
7014 /// * addr + length contains unmapped memory
7015 OutOfMemory,
7016 /// The mincore syscall is not available on this version and configuration
7017 /// of this UNIX-like kernel.
7018 MincoreUnavailable,
7019} || UnexpectedError;
7020
7021/// Determine whether pages are resident in memory.
7022pub fn mincore(ptr: [*]align(mem.page_size) u8, length: usize, vec: [*]u8) MincoreError!void {
7023 return switch (errno(system.mincore(ptr, length, vec))) {
7024 .SUCCESS => {},
7025 .AGAIN => error.SystemResources,
7026 .FAULT => error.InvalidAddress,
7027 .INVAL => error.InvalidSyscall,
7028 .NOMEM => error.OutOfMemory,
7029 .NOSYS => error.MincoreUnavailable,
7030 else => |err| unexpectedErrno(err),
7031 };
7032}
7033
7034pub const MadviseError = error{
7035 /// advice is MADV.REMOVE, but the specified address range is not a shared writable mapping.
7036 AccessDenied,
7037 /// advice is MADV.HWPOISON, but the caller does not have the CAP_SYS_ADMIN capability.
7038 PermissionDenied,
7039 /// A kernel resource was temporarily unavailable.
7040 SystemResources,
7041 /// One of the following:
7042 /// * addr is not page-aligned or length is negative
7043 /// * advice is not valid
7044 /// * advice is MADV.DONTNEED or MADV.REMOVE and the specified address range
7045 /// includes locked, Huge TLB pages, or VM_PFNMAP pages.
7046 /// * advice is MADV.MERGEABLE or MADV.UNMERGEABLE, but the kernel was not
7047 /// configured with CONFIG_KSM.
7048 /// * advice is MADV.FREE or MADV.WIPEONFORK but the specified address range
7049 /// includes file, Huge TLB, MAP.SHARED, or VM_PFNMAP ranges.
7050 InvalidSyscall,
7051 /// (for MADV.WILLNEED) Paging in this area would exceed the process's
7052 /// maximum resident set size.
7053 WouldExceedMaximumResidentSetSize,
7054 /// One of the following:
7055 /// * (for MADV.WILLNEED) Not enough memory: paging in failed.
7056 /// * Addresses in the specified range are not currently mapped, or
7057 /// are outside the address space of the process.
7058 OutOfMemory,
7059 /// The madvise syscall is not available on this version and configuration
7060 /// of the Linux kernel.
7061 MadviseUnavailable,
7062 /// The operating system returned an undocumented error code.
7063 Unexpected,
7064};
7065
7066/// Give advice about use of memory.
7067/// This syscall is optional and is sometimes configured to be disabled.
7068pub fn madvise(ptr: [*]align(mem.page_size) u8, length: usize, advice: u32) MadviseError!void {
7069 switch (errno(system.madvise(ptr, length, advice))) {
7070 .SUCCESS => return,
7071 .ACCES => return error.AccessDenied,
7072 .AGAIN => return error.SystemResources,
7073 .BADF => unreachable, // The map exists, but the area maps something that isn't a file.
7074 .INVAL => return error.InvalidSyscall,
7075 .IO => return error.WouldExceedMaximumResidentSetSize,
7076 .NOMEM => return error.OutOfMemory,
7077 .NOSYS => return error.MadviseUnavailable,
7078 else => |err| return unexpectedErrno(err),
7079 }
7080}
7081
7082pub const PerfEventOpenError = error{
7083 /// Returned if the perf_event_attr size value is too small (smaller
7084 /// than PERF_ATTR_SIZE_VER0), too big (larger than the page size),
7085 /// or larger than the kernel supports and the extra bytes are not
7086 /// zero. When E2BIG is returned, the perf_event_attr size field is
7087 /// overwritten by the kernel to be the size of the structure it was
7088 /// expecting.
7089 TooBig,
7090 /// Returned when the requested event requires CAP_SYS_ADMIN permis‐
7091 /// sions (or a more permissive perf_event paranoid setting). Some
7092 /// common cases where an unprivileged process may encounter this
7093 /// error: attaching to a process owned by a different user; moni‐
7094 /// toring all processes on a given CPU (i.e., specifying the pid
7095 /// argument as -1); and not setting exclude_kernel when the para‐
7096 /// noid setting requires it.
7097 /// Also:
7098 /// Returned on many (but not all) architectures when an unsupported
7099 /// exclude_hv, exclude_idle, exclude_user, or exclude_kernel set‐
7100 /// ting is specified.
7101 /// It can also happen, as with EACCES, when the requested event re‐
7102 /// quires CAP_SYS_ADMIN permissions (or a more permissive
7103 /// perf_event paranoid setting). This includes setting a break‐
7104 /// point on a kernel address, and (since Linux 3.13) setting a ker‐
7105 /// nel function-trace tracepoint.
7106 PermissionDenied,
7107 /// Returned if another event already has exclusive access to the
7108 /// PMU.
7109 DeviceBusy,
7110 /// Each opened event uses one file descriptor. If a large number
7111 /// of events are opened, the per-process limit on the number of
7112 /// open file descriptors will be reached, and no more events can be
7113 /// created.
7114 ProcessResources,
7115 EventRequiresUnsupportedCpuFeature,
7116 /// Returned if you try to add more breakpoint
7117 /// events than supported by the hardware.
7118 TooManyBreakpoints,
7119 /// Returned if PERF_SAMPLE_STACK_USER is set in sample_type and it
7120 /// is not supported by hardware.
7121 SampleStackNotSupported,
7122 /// Returned if an event requiring a specific hardware feature is
7123 /// requested but there is no hardware support. This includes re‐
7124 /// questing low-skid events if not supported, branch tracing if it
7125 /// is not available, sampling if no PMU interrupt is available, and
7126 /// branch stacks for software events.
7127 EventNotSupported,
7128 /// Returned if PERF_SAMPLE_CALLCHAIN is requested and sam‐
7129 /// ple_max_stack is larger than the maximum specified in
7130 /// /proc/sys/kernel/perf_event_max_stack.
7131 SampleMaxStackOverflow,
7132 /// Returned if attempting to attach to a process that does not exist.
7133 ProcessNotFound,
7134} || UnexpectedError;
7135
7136pub fn perf_event_open(
7137 attr: *linux.perf_event_attr,
7138 pid: pid_t,
7139 cpu: i32,
7140 group_fd: fd_t,
7141 flags: usize,
7142) PerfEventOpenError!fd_t {
7143 const rc = linux.perf_event_open(attr, pid, cpu, group_fd, flags);
7144 switch (errno(rc)) {
7145 .SUCCESS => return @intCast(rc),
7146 .@"2BIG" => return error.TooBig,
7147 .ACCES => return error.PermissionDenied,
7148 .BADF => unreachable, // group_fd file descriptor is not valid.
7149 .BUSY => return error.DeviceBusy,
7150 .FAULT => unreachable, // Segmentation fault.
7151 .INVAL => unreachable, // Bad attr settings.
7152 .INTR => unreachable, // Mixed perf and ftrace handling for a uprobe.
7153 .MFILE => return error.ProcessResources,
7154 .NODEV => return error.EventRequiresUnsupportedCpuFeature,
7155 .NOENT => unreachable, // Invalid type setting.
7156 .NOSPC => return error.TooManyBreakpoints,
7157 .NOSYS => return error.SampleStackNotSupported,
7158 .OPNOTSUPP => return error.EventNotSupported,
7159 .OVERFLOW => return error.SampleMaxStackOverflow,
7160 .PERM => return error.PermissionDenied,
7161 .SRCH => return error.ProcessNotFound,
7162 else => |err| return unexpectedErrno(err),
7163 }
7164}
7165
7166pub const TimerFdCreateError = error{
7167 AccessDenied,
7168 ProcessFdQuotaExceeded,
7169 SystemFdQuotaExceeded,
7170 NoDevice,
7171 SystemResources,
7172} || UnexpectedError;
7173
7174pub const TimerFdGetError = error{InvalidHandle} || UnexpectedError;
7175pub const TimerFdSetError = TimerFdGetError || error{Canceled};
7176
7177pub fn timerfd_create(clokid: i32, flags: linux.TFD) TimerFdCreateError!fd_t {
7178 const rc = linux.timerfd_create(clokid, flags);
7179 return switch (errno(rc)) {
7180 .SUCCESS => @intCast(rc),
7181 .INVAL => unreachable,
7182 .MFILE => return error.ProcessFdQuotaExceeded,
7183 .NFILE => return error.SystemFdQuotaExceeded,
7184 .NODEV => return error.NoDevice,
7185 .NOMEM => return error.SystemResources,
7186 .PERM => return error.AccessDenied,
7187 else => |err| return unexpectedErrno(err),
7188 };
7189}
7190
7191pub fn timerfd_settime(
7192 fd: i32,
7193 flags: linux.TFD.TIMER,
7194 new_value: *const linux.itimerspec,
7195 old_value: ?*linux.itimerspec,
7196) TimerFdSetError!void {
7197 const rc = linux.timerfd_settime(fd, flags, new_value, old_value);
7198 return switch (errno(rc)) {
7199 .SUCCESS => {},
7200 .BADF => error.InvalidHandle,
7201 .FAULT => unreachable,
7202 .INVAL => unreachable,
7203 .CANCELED => error.Canceled,
7204 else => |err| return unexpectedErrno(err),
7205 };
7206}
7207
7208pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec {
7209 var curr_value: linux.itimerspec = undefined;
7210 const rc = linux.timerfd_gettime(fd, &curr_value);
7211 return switch (errno(rc)) {
7212 .SUCCESS => return curr_value,
7213 .BADF => error.InvalidHandle,
7214 .FAULT => unreachable,
7215 .INVAL => unreachable,
7216 else => |err| return unexpectedErrno(err),
7217 };
7218}
7219
7220pub const PtraceError = error{
7221 DeviceBusy,
7222 InputOutput,
7223 ProcessNotFound,
7224 PermissionDenied,
7225} || UnexpectedError;
7226
7227pub fn ptrace(request: u32, pid: pid_t, addr: usize, signal: usize) PtraceError!void {
7228 if (native_os == .windows or native_os == .wasi)
7229 @compileError("Unsupported OS");
7230
7231 return switch (native_os) {
7232 .linux => switch (errno(linux.ptrace(request, pid, addr, signal, 0))) {
7233 .SUCCESS => {},
7234 .SRCH => error.ProcessNotFound,
7235 .FAULT => unreachable,
7236 .INVAL => unreachable,
7237 .IO => return error.InputOutput,
7238 .PERM => error.PermissionDenied,
7239 .BUSY => error.DeviceBusy,
7240 else => |err| return unexpectedErrno(err),
7241 },
7242
7243 .macos, .ios, .tvos, .watchos => switch (errno(std.c.ptrace(
7244 @intCast(request),
7245 pid,
7246 @ptrFromInt(addr),
7247 @intCast(signal),
7248 ))) {
7249 .SUCCESS => {},
7250 .SRCH => error.ProcessNotFound,
7251 .INVAL => unreachable,
7252 .PERM => error.PermissionDenied,
7253 .BUSY => error.DeviceBusy,
7254 else => |err| return unexpectedErrno(err),
7255 },
7256
7257 else => switch (errno(system.ptrace(request, pid, addr, signal))) {
7258 .SUCCESS => {},
7259 .SRCH => error.ProcessNotFound,
7260 .INVAL => unreachable,
7261 .PERM => error.PermissionDenied,
7262 .BUSY => error.DeviceBusy,
7263 else => |err| return unexpectedErrno(err),
7264 },
7265 };
7266}
7267
7268pub const IoCtl_SIOCGIFINDEX_Error = error{
7269 FileSystem,
7270 InterfaceNotFound,
7271} || UnexpectedError;
7272
7273pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void {
7274 while (true) {
7275 switch (errno(system.ioctl(fd, SIOCGIFINDEX, @intFromPtr(ifr)))) {
7276 .SUCCESS => return,
7277 .INVAL => unreachable, // Bad parameters.
7278 .NOTTY => unreachable,
7279 .NXIO => unreachable,
7280 .BADF => unreachable, // Always a race condition.
7281 .FAULT => unreachable, // Bad pointer parameter.
7282 .INTR => continue,
7283 .IO => return error.FileSystem,
7284 .NODEV => return error.InterfaceNotFound,
7285 else => |err| return unexpectedErrno(err),
7286 }
7287 }
7288}
7289
7290const lfs64_abi = native_os == .linux and builtin.link_libc and builtin.abi.isGnu();
7291
7292/// Whether or not `error.Unexpected` will print its value and a stack trace.
7293///
7294/// If this happens the fix is to add the error code to the corresponding
7295/// switch expression, possibly introduce a new error in the error set, and
7296/// send a patch to Zig.
7297pub const unexpected_error_tracing = builtin.zig_backend == .stage2_llvm and builtin.mode == .Debug;
7298
7299pub const UnexpectedError = error{
7300 /// The Operating System returned an undocumented error code.
7301 ///
7302 /// This error is in theory not possible, but it would be better
7303 /// to handle this error than to invoke undefined behavior.
7304 ///
7305 /// When this error code is observed, it usually means the Zig Standard
7306 /// Library needs a small patch to add the error code to the error set for
7307 /// the respective function.
7308 Unexpected,
7309};
7310
7311/// Call this when you made a syscall or something that sets errno
7312/// and you get an unexpected error.
7313pub fn unexpectedErrno(err: E) UnexpectedError {
7314 if (unexpected_error_tracing) {
7315 std.debug.print("unexpected errno: {d}\n", .{@intFromEnum(err)});
7316 std.debug.dumpCurrentStackTrace(null);
7317 }
7318 return error.Unexpected;
7319}
7320
7321/// Used to convert a slice to a null terminated slice on the stack.
7322pub fn toPosixPath(file_path: []const u8) error{NameTooLong}![PATH_MAX - 1:0]u8 {
7323 if (std.debug.runtime_safety) assert(mem.indexOfScalar(u8, file_path, 0) == null);
7324 var path_with_null: [PATH_MAX - 1:0]u8 = undefined;
7325 // >= rather than > to make room for the null byte
7326 if (file_path.len >= PATH_MAX) return error.NameTooLong;
7327 @memcpy(path_with_null[0..file_path.len], file_path);
7328 path_with_null[file_path.len] = 0;
7329 return path_with_null;
7330}
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 @@...@@ -1,6 +1,5 @@
1const std = @import("std.zig");1const std = @import("std.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const os = std.os;
4const fs = std.fs;3const fs = std.fs;
5const mem = std.mem;4const mem = std.mem;
6const math = std.math;5const math = std.math;
...@@ -8,20 +7,27 @@ const Allocator = mem.Allocator;...@@ -8,20 +7,27 @@ const Allocator = mem.Allocator;
8const assert = std.debug.assert;7const assert = std.debug.assert;
9const testing = std.testing;8const testing = std.testing;
10const child_process = @import("child_process.zig");9const child_process = @import("child_process.zig");
10const native_os = builtin.os.tag;
11const posix = std.posix;
12const windows = std.os.windows;
1113
12pub const Child = child_process.ChildProcess;14pub const Child = child_process.ChildProcess;
13pub const abort = os.abort;15pub const abort = posix.abort;
14pub const exit = os.exit;16pub const exit = posix.exit;
15pub const changeCurDir = os.chdir;17pub const changeCurDir = posix.chdir;
16pub const changeCurDirC = os.chdirC;18pub const changeCurDirC = posix.chdirC;
19
20pub const GetCwdError = posix.GetCwdError;
1721
18/// The result is a slice of `out_buffer`, from index `0`.22/// The result is a slice of `out_buffer`, from index `0`.
19/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).23/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
20/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.24/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
21pub fn getCwd(out_buffer: []u8) ![]u8 {25pub fn getCwd(out_buffer: []u8) ![]u8 {
22 return os.getcwd(out_buffer);26 return posix.getcwd(out_buffer);
23}27}
2428
29pub const GetCwdAllocError = Allocator.Error || posix.GetCwdError;
30
25/// Caller must free the returned memory.31/// Caller must free the returned memory.
26/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).32/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
27/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.33/// 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 {...@@ -34,7 +40,7 @@ pub fn getCwdAlloc(allocator: Allocator) ![]u8 {
3440
35 var current_buf: []u8 = &stack_buf;41 var current_buf: []u8 = &stack_buf;
36 while (true) {42 while (true) {
37 if (os.getcwd(current_buf)) |slice| {43 if (posix.getcwd(current_buf)) |slice| {
38 return allocator.dupe(u8, slice);44 return allocator.dupe(u8, slice);
39 } else |err| switch (err) {45 } else |err| switch (err) {
40 error.NameTooLong => {46 error.NameTooLong => {
...@@ -51,7 +57,7 @@ pub fn getCwdAlloc(allocator: Allocator) ![]u8 {...@@ -51,7 +57,7 @@ pub fn getCwdAlloc(allocator: Allocator) ![]u8 {
51}57}
5258
53test getCwdAlloc {59test getCwdAlloc {
54 if (builtin.os.tag == .wasi) return error.SkipZigTest;60 if (native_os == .wasi) return error.SkipZigTest;
5561
56 const cwd = try getCwdAlloc(testing.allocator);62 const cwd = try getCwdAlloc(testing.allocator);
57 testing.allocator.free(cwd);63 testing.allocator.free(cwd);
...@@ -72,13 +78,13 @@ pub const EnvMap = struct {...@@ -72,13 +78,13 @@ pub const EnvMap = struct {
72 pub const EnvNameHashContext = struct {78 pub const EnvNameHashContext = struct {
73 fn upcase(c: u21) u21 {79 fn upcase(c: u21) u21 {
74 if (c <= std.math.maxInt(u16))80 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)));
76 return c;82 return c;
77 }83 }
7884
79 pub fn hash(self: @This(), s: []const u8) u64 {85 pub fn hash(self: @This(), s: []const u8) u64 {
80 _ = self;86 _ = self;
81 if (builtin.os.tag == .windows) {87 if (native_os == .windows) {
82 var h = std.hash.Wyhash.init(0);88 var h = std.hash.Wyhash.init(0);
83 var it = std.unicode.Wtf8View.initUnchecked(s).iterator();89 var it = std.unicode.Wtf8View.initUnchecked(s).iterator();
84 while (it.nextCodepoint()) |cp| {90 while (it.nextCodepoint()) |cp| {
...@@ -96,7 +102,7 @@ pub const EnvMap = struct {...@@ -96,7 +102,7 @@ pub const EnvMap = struct {
96102
97 pub fn eql(self: @This(), a: []const u8, b: []const u8) bool {103 pub fn eql(self: @This(), a: []const u8, b: []const u8) bool {
98 _ = self;104 _ = self;
99 if (builtin.os.tag == .windows) {105 if (native_os == .windows) {
100 var it_a = std.unicode.Wtf8View.initUnchecked(a).iterator();106 var it_a = std.unicode.Wtf8View.initUnchecked(a).iterator();
101 var it_b = std.unicode.Wtf8View.initUnchecked(b).iterator();107 var it_b = std.unicode.Wtf8View.initUnchecked(b).iterator();
102 while (true) {108 while (true) {
...@@ -228,7 +234,7 @@ test "EnvMap" {...@@ -228,7 +234,7 @@ test "EnvMap" {
228 try testing.expectEqual(@as(EnvMap.Size, 2), env.count());234 try testing.expectEqual(@as(EnvMap.Size, 2), env.count());
229235
230 // case insensitivity on Windows only236 // case insensitivity on Windows only
231 if (builtin.os.tag == .windows) {237 if (native_os == .windows) {
232 try testing.expectEqualStrings("1", env.get("something_New_aNd_LONGER").?);238 try testing.expectEqualStrings("1", env.get("something_New_aNd_LONGER").?);
233 } else {239 } else {
234 try testing.expect(null == env.get("something_New_aNd_LONGER"));240 try testing.expect(null == env.get("something_New_aNd_LONGER"));
...@@ -248,7 +254,7 @@ test "EnvMap" {...@@ -248,7 +254,7 @@ test "EnvMap" {
248254
249 try testing.expectEqual(@as(EnvMap.Size, 1), env.count());255 try testing.expectEqual(@as(EnvMap.Size, 1), env.count());
250256
251 if (builtin.os.tag == .windows) {257 if (native_os == .windows) {
252 // test Unicode case-insensitivity on Windows258 // test Unicode case-insensitivity on Windows
253 try env.put("КИРиллИЦА", "something else");259 try env.put("КИРиллИЦА", "something else");
254 try testing.expectEqualStrings("something else", env.get("кириллица").?);260 try testing.expectEqualStrings("something else", env.get("кириллица").?);
...@@ -279,8 +285,8 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap {...@@ -279,8 +285,8 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap {
279 var result = EnvMap.init(allocator);285 var result = EnvMap.init(allocator);
280 errdefer result.deinit();286 errdefer result.deinit();
281287
282 if (builtin.os.tag == .windows) {288 if (native_os == .windows) {
283 const ptr = os.windows.peb().ProcessParameters.Environment;289 const ptr = windows.peb().ProcessParameters.Environment;
284290
285 var i: usize = 0;291 var i: usize = 0;
286 while (ptr[i] != 0) {292 while (ptr[i] != 0) {
...@@ -310,13 +316,13 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap {...@@ -310,13 +316,13 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap {
310 try result.putMove(key, value);316 try result.putMove(key, value);
311 }317 }
312 return result;318 return result;
313 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {319 } else if (native_os == .wasi and !builtin.link_libc) {
314 var environ_count: usize = undefined;320 var environ_count: usize = undefined;
315 var environ_buf_size: usize = undefined;321 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);
318 if (environ_sizes_get_ret != .SUCCESS) {324 if (environ_sizes_get_ret != .SUCCESS) {
319 return os.unexpectedErrno(environ_sizes_get_ret);325 return posix.unexpectedErrno(environ_sizes_get_ret);
320 }326 }
321327
322 if (environ_count == 0) {328 if (environ_count == 0) {
...@@ -328,9 +334,9 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap {...@@ -328,9 +334,9 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap {
328 const environ_buf = try allocator.alloc(u8, environ_buf_size);334 const environ_buf = try allocator.alloc(u8, environ_buf_size);
329 defer allocator.free(environ_buf);335 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);
332 if (environ_get_ret != .SUCCESS) {338 if (environ_get_ret != .SUCCESS) {
333 return os.unexpectedErrno(environ_get_ret);339 return posix.unexpectedErrno(environ_get_ret);
334 }340 }
335341
336 for (environ) |env| {342 for (environ) |env| {
...@@ -356,7 +362,7 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap {...@@ -356,7 +362,7 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap {
356 }362 }
357 return result;363 return result;
358 } else {364 } else {
359 for (os.environ) |line| {365 for (std.os.environ) |line| {
360 var line_i: usize = 0;366 var line_i: usize = 0;
361 while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}367 while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
362 const key = line[0..line_i];368 const key = line[0..line_i];
...@@ -391,37 +397,37 @@ pub const GetEnvVarOwnedError = error{...@@ -391,37 +397,37 @@ pub const GetEnvVarOwnedError = error{
391/// On Windows, the value is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).397/// On Windows, the value is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
392/// On other platforms, the value is an opaque sequence of bytes with no particular encoding.398/// On other platforms, the value is an opaque sequence of bytes with no particular encoding.
393pub fn getEnvVarOwned(allocator: Allocator, key: []const u8) GetEnvVarOwnedError![]u8 {399pub fn getEnvVarOwned(allocator: Allocator, key: []const u8) GetEnvVarOwnedError![]u8 {
394 if (builtin.os.tag == .windows) {400 if (native_os == .windows) {
395 const result_w = blk: {401 const result_w = blk: {
396 var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator);402 var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator);
397 const stack_allocator = stack_alloc.get();403 const stack_allocator = stack_alloc.get();
398 const key_w = try std.unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key);404 const key_w = try std.unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key);
399 defer stack_allocator.free(key_w);405 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;
402 };408 };
403 // wtf16LeToWtf8Alloc can only fail with OutOfMemory409 // wtf16LeToWtf8Alloc can only fail with OutOfMemory
404 return std.unicode.wtf16LeToWtf8Alloc(allocator, result_w);410 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) {
406 var envmap = getEnvMap(allocator) catch return error.OutOfMemory;412 var envmap = getEnvMap(allocator) catch return error.OutOfMemory;
407 defer envmap.deinit();413 defer envmap.deinit();
408 const val = envmap.get(key) orelse return error.EnvironmentVariableNotFound;414 const val = envmap.get(key) orelse return error.EnvironmentVariableNotFound;
409 return allocator.dupe(u8, val);415 return allocator.dupe(u8, val);
410 } else {416 } else {
411 const result = os.getenv(key) orelse return error.EnvironmentVariableNotFound;417 const result = posix.getenv(key) orelse return error.EnvironmentVariableNotFound;
412 return allocator.dupe(u8, result);418 return allocator.dupe(u8, result);
413 }419 }
414}420}
415421
416/// On Windows, `key` must be valid UTF-8.422/// On Windows, `key` must be valid UTF-8.
417pub fn hasEnvVarConstant(comptime key: []const u8) bool {423pub fn hasEnvVarConstant(comptime key: []const u8) bool {
418 if (builtin.os.tag == .windows) {424 if (native_os == .windows) {
419 const key_w = comptime std.unicode.utf8ToUtf16LeStringLiteral(key);425 const key_w = comptime std.unicode.utf8ToUtf16LeStringLiteral(key);
420 return std.os.getenvW(key_w) != null;426 return getenvW(key_w) != null;
421 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {427 } else if (native_os == .wasi and !builtin.link_libc) {
422 @compileError("hasEnvVarConstant is not supported for WASI without libc");428 @compileError("hasEnvVarConstant is not supported for WASI without libc");
423 } else {429 } else {
424 return os.getenv(key) != null;430 return posix.getenv(key) != null;
425 }431 }
426}432}
427433
...@@ -436,19 +442,63 @@ pub const HasEnvVarError = error{...@@ -436,19 +442,63 @@ pub const HasEnvVarError = error{
436/// On Windows, if `key` is not valid [WTF-8](https://simonsapin.github.io/wtf-8/),442/// On Windows, if `key` is not valid [WTF-8](https://simonsapin.github.io/wtf-8/),
437/// then `error.InvalidWtf8` is returned.443/// then `error.InvalidWtf8` is returned.
438pub fn hasEnvVar(allocator: Allocator, key: []const u8) HasEnvVarError!bool {444pub fn hasEnvVar(allocator: Allocator, key: []const u8) HasEnvVarError!bool {
439 if (builtin.os.tag == .windows) {445 if (native_os == .windows) {
440 var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator);446 var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator);
441 const stack_allocator = stack_alloc.get();447 const stack_allocator = stack_alloc.get();
442 const key_w = try std.unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key);448 const key_w = try std.unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key);
443 defer stack_allocator.free(key_w);449 defer stack_allocator.free(key_w);
444 return std.os.getenvW(key_w) != null;450 return getenvW(key_w) != null;
445 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {451 } else if (native_os == .wasi and !builtin.link_libc) {
446 var envmap = getEnvMap(allocator) catch return error.OutOfMemory;452 var envmap = getEnvMap(allocator) catch return error.OutOfMemory;
447 defer envmap.deinit();453 defer envmap.deinit();
448 return envmap.getPtr(key) != null;454 return envmap.getPtr(key) != null;
449 } else {455 } 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
451 }500 }
501 return null;
452}502}
453503
454test getEnvVarOwned {504test getEnvVarOwned {
...@@ -459,7 +509,7 @@ test getEnvVarOwned {...@@ -459,7 +509,7 @@ test getEnvVarOwned {
459}509}
460510
461test hasEnvVarConstant {511test 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
464 try testing.expect(!hasEnvVarConstant("BADENV"));514 try testing.expect(!hasEnvVarConstant("BADENV"));
465}515}
...@@ -478,14 +528,14 @@ pub const ArgIteratorPosix = struct {...@@ -478,14 +528,14 @@ pub const ArgIteratorPosix = struct {
478 pub fn init() ArgIteratorPosix {528 pub fn init() ArgIteratorPosix {
479 return ArgIteratorPosix{529 return ArgIteratorPosix{
480 .index = 0,530 .index = 0,
481 .count = os.argv.len,531 .count = std.os.argv.len,
482 };532 };
483 }533 }
484534
485 pub fn next(self: *ArgIteratorPosix) ?[:0]const u8 {535 pub fn next(self: *ArgIteratorPosix) ?[:0]const u8 {
486 if (self.index == self.count) return null;536 if (self.index == self.count) return null;
487537
488 const s = os.argv[self.index];538 const s = std.os.argv[self.index];
489 self.index += 1;539 self.index += 1;
490 return mem.sliceTo(s, 0);540 return mem.sliceTo(s, 0);
491 }541 }
...@@ -503,7 +553,7 @@ pub const ArgIteratorWasi = struct {...@@ -503,7 +553,7 @@ pub const ArgIteratorWasi = struct {
503 index: usize,553 index: usize,
504 args: [][:0]u8,554 args: [][:0]u8,
505555
506 pub const InitError = error{OutOfMemory} || os.UnexpectedError;556 pub const InitError = error{OutOfMemory} || posix.UnexpectedError;
507557
508 /// You must call deinit to free the internal buffer of the558 /// You must call deinit to free the internal buffer of the
509 /// iterator after you are done.559 /// iterator after you are done.
...@@ -517,13 +567,12 @@ pub const ArgIteratorWasi = struct {...@@ -517,13 +567,12 @@ pub const ArgIteratorWasi = struct {
517 }567 }
518568
519 fn internalInit(allocator: Allocator) InitError![][:0]u8 {569 fn internalInit(allocator: Allocator) InitError![][:0]u8 {
520 const w = os.wasi;
521 var count: usize = undefined;570 var count: usize = undefined;
522 var buf_size: usize = undefined;571 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)) {
525 .SUCCESS => {},574 .SUCCESS => {},
526 else => |err| return os.unexpectedErrno(err),575 else => |err| return posix.unexpectedErrno(err),
527 }576 }
528577
529 if (count == 0) {578 if (count == 0) {
...@@ -535,9 +584,9 @@ pub const ArgIteratorWasi = struct {...@@ -535,9 +584,9 @@ pub const ArgIteratorWasi = struct {
535584
536 const argv_buf = try allocator.alloc(u8, buf_size);585 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)) {
539 .SUCCESS => {},588 .SUCCESS => {},
540 else => |err| return os.unexpectedErrno(err),589 else => |err| return posix.unexpectedErrno(err),
541 }590 }
542591
543 var result_args = try allocator.alloc([:0]u8, count);592 var result_args = try allocator.alloc([:0]u8, count);
...@@ -1007,7 +1056,7 @@ pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type {...@@ -1007,7 +1056,7 @@ pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type {
10071056
1008/// Cross-platform command line argument iterator.1057/// Cross-platform command line argument iterator.
1009pub const ArgIterator = struct {1058pub const ArgIterator = struct {
1010 const InnerType = switch (builtin.os.tag) {1059 const InnerType = switch (native_os) {
1011 .windows => ArgIteratorWindows,1060 .windows => ArgIteratorWindows,
1012 .wasi => if (builtin.link_libc) ArgIteratorPosix else ArgIteratorWasi,1061 .wasi => if (builtin.link_libc) ArgIteratorPosix else ArgIteratorWasi,
1013 else => ArgIteratorPosix,1062 else => ArgIteratorPosix,
...@@ -1018,10 +1067,10 @@ pub const ArgIterator = struct {...@@ -1018,10 +1067,10 @@ pub const ArgIterator = struct {
1018 /// Initialize the args iterator. Consider using initWithAllocator() instead1067 /// Initialize the args iterator. Consider using initWithAllocator() instead
1019 /// for cross-platform compatibility.1068 /// for cross-platform compatibility.
1020 pub fn init() ArgIterator {1069 pub fn init() ArgIterator {
1021 if (builtin.os.tag == .wasi) {1070 if (native_os == .wasi) {
1022 @compileError("In WASI, use initWithAllocator instead.");1071 @compileError("In WASI, use initWithAllocator instead.");
1023 }1072 }
1024 if (builtin.os.tag == .windows) {1073 if (native_os == .windows) {
1025 @compileError("In Windows, use initWithAllocator instead.");1074 @compileError("In Windows, use initWithAllocator instead.");
1026 }1075 }
10271076
...@@ -1032,11 +1081,11 @@ pub const ArgIterator = struct {...@@ -1032,11 +1081,11 @@ pub const ArgIterator = struct {
10321081
1033 /// You must deinitialize iterator's internal buffers by calling `deinit` when done.1082 /// You must deinitialize iterator's internal buffers by calling `deinit` when done.
1034 pub fn initWithAllocator(allocator: Allocator) InitError!ArgIterator {1083 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) {
1036 return ArgIterator{ .inner = try InnerType.init(allocator) };1085 return ArgIterator{ .inner = try InnerType.init(allocator) };
1037 }1086 }
1038 if (builtin.os.tag == .windows) {1087 if (native_os == .windows) {
1039 const cmd_line_w = os.windows.kernel32.GetCommandLineW();1088 const cmd_line_w = windows.kernel32.GetCommandLineW();
1040 return ArgIterator{ .inner = try InnerType.init(allocator, cmd_line_w) };1089 return ArgIterator{ .inner = try InnerType.init(allocator, cmd_line_w) };
1041 }1090 }
10421091
...@@ -1061,11 +1110,11 @@ pub const ArgIterator = struct {...@@ -1061,11 +1110,11 @@ pub const ArgIterator = struct {
1061 /// was created with `initWithAllocator` function.1110 /// was created with `initWithAllocator` function.
1062 pub fn deinit(self: *ArgIterator) void {1111 pub fn deinit(self: *ArgIterator) void {
1063 // Unless we're targeting WASI or Windows, this is a no-op.1112 // 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) {
1065 self.inner.deinit();1114 self.inner.deinit();
1066 }1115 }
10671116
1068 if (builtin.os.tag == .windows) {1117 if (native_os == .windows) {
1069 self.inner.deinit();1118 self.inner.deinit();
1070 }1119 }
1071 }1120 }
...@@ -1334,13 +1383,13 @@ fn testResponseFileCmdLine(input_cmd_line: []const u8, expected_args: []const []...@@ -1334,13 +1383,13 @@ fn testResponseFileCmdLine(input_cmd_line: []const u8, expected_args: []const []
1334}1383}
13351384
1336pub const UserInfo = struct {1385pub const UserInfo = struct {
1337 uid: os.uid_t,1386 uid: posix.uid_t,
1338 gid: os.gid_t,1387 gid: posix.gid_t,
1339};1388};
13401389
1341/// POSIX function which gets a uid from username.1390/// POSIX function which gets a uid from username.
1342pub fn getUserInfo(name: []const u8) !UserInfo {1391pub fn getUserInfo(name: []const u8) !UserInfo {
1343 return switch (builtin.os.tag) {1392 return switch (native_os) {
1344 .linux,1393 .linux,
1345 .macos,1394 .macos,
1346 .watchos,1395 .watchos,
...@@ -1376,8 +1425,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {...@@ -1376,8 +1425,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
1376 var buf: [std.mem.page_size]u8 = undefined;1425 var buf: [std.mem.page_size]u8 = undefined;
1377 var name_index: usize = 0;1426 var name_index: usize = 0;
1378 var state = State.Start;1427 var state = State.Start;
1379 var uid: os.uid_t = 0;1428 var uid: posix.uid_t = 0;
1380 var gid: os.gid_t = 0;1429 var gid: posix.gid_t = 0;
13811430
1382 while (true) {1431 while (true) {
1383 const amt_read = try reader.read(buf[0..]);1432 const amt_read = try reader.read(buf[0..]);
...@@ -1462,36 +1511,36 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {...@@ -1462,36 +1511,36 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
1462}1511}
14631512
1464pub fn getBaseAddress() usize {1513pub fn getBaseAddress() usize {
1465 switch (builtin.os.tag) {1514 switch (native_os) {
1466 .linux => {1515 .linux => {
1467 const base = os.system.getauxval(std.elf.AT_BASE);1516 const base = std.os.linux.getauxval(std.elf.AT_BASE);
1468 if (base != 0) {1517 if (base != 0) {
1469 return base;1518 return base;
1470 }1519 }
1471 const phdr = os.system.getauxval(std.elf.AT_PHDR);1520 const phdr = std.os.linux.getauxval(std.elf.AT_PHDR);
1472 return phdr - @sizeOf(std.elf.Ehdr);1521 return phdr - @sizeOf(std.elf.Ehdr);
1473 },1522 },
1474 .macos, .freebsd, .netbsd => {1523 .macos, .freebsd, .netbsd => {
1475 return @intFromPtr(&std.c._mh_execute_header);1524 return @intFromPtr(&std.c._mh_execute_header);
1476 },1525 },
1477 .windows => return @intFromPtr(os.windows.kernel32.GetModuleHandleW(null)),1526 .windows => return @intFromPtr(windows.kernel32.GetModuleHandleW(null)),
1478 else => @compileError("Unsupported OS"),1527 else => @compileError("Unsupported OS"),
1479 }1528 }
1480}1529}
14811530
1482/// Tells whether calling the `execv` or `execve` functions will be a compile error.1531/// 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) {
1484 .windows, .haiku, .wasi => false,1533 .windows, .haiku, .wasi => false,
1485 else => true,1534 else => true,
1486};1535};
14871536
1488/// Tells whether spawning child processes is supported (e.g. via ChildProcess)1537/// 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) {
1490 .wasi, .watchos, .tvos => false,1539 .wasi, .watchos, .tvos => false,
1491 else => true,1540 else => true,
1492};1541};
14931542
1494pub const ExecvError = std.os.ExecveError || error{OutOfMemory};1543pub const ExecvError = std.posix.ExecveError || error{OutOfMemory};
14951544
1496/// Replaces the current process image with the executed process.1545/// Replaces the current process image with the executed process.
1497/// This function must allocate memory to add a null terminating bytes on path and each arg.1546/// 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};...@@ -1500,7 +1549,7 @@ pub const ExecvError = std.os.ExecveError || error{OutOfMemory};
1500/// `argv[0]` is the executable path.1549/// `argv[0]` is the executable path.
1501/// This function also uses the PATH environment variable to get the full path to the executable.1550/// This function also uses the PATH environment variable to get the full path to the executable.
1502/// Due to the heap-allocation, it is illegal to call this function in a fork() child.1551/// 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.
1504pub fn execv(allocator: Allocator, argv: []const []const u8) ExecvError {1553pub fn execv(allocator: Allocator, argv: []const []const u8) ExecvError {
1505 return execve(allocator, argv, null);1554 return execve(allocator, argv, null);
1506}1555}
...@@ -1512,7 +1561,7 @@ pub fn execv(allocator: Allocator, argv: []const []const u8) ExecvError {...@@ -1512,7 +1561,7 @@ pub fn execv(allocator: Allocator, argv: []const []const u8) ExecvError {
1512/// `argv[0]` is the executable path.1561/// `argv[0]` is the executable path.
1513/// This function also uses the PATH environment variable to get the full path to the executable.1562/// This function also uses the PATH environment variable to get the full path to the executable.
1514/// Due to the heap-allocation, it is illegal to call this function in a fork() child.1563/// 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.
1516pub fn execve(1565pub fn execve(
1517 allocator: Allocator,1566 allocator: Allocator,
1518 argv: []const []const u8,1567 argv: []const []const u8,
...@@ -1536,14 +1585,14 @@ pub fn execve(...@@ -1536,14 +1585,14 @@ pub fn execve(
1536 } else if (builtin.output_mode == .Exe) {1585 } else if (builtin.output_mode == .Exe) {
1537 // Then we have Zig start code and this works.1586 // Then we have Zig start code and this works.
1538 // TODO type-safety for null-termination of `os.environ`.1587 // 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));
1540 } else {1589 } else {
1541 // TODO come up with a solution for this.1590 // TODO come up with a solution for this.
1542 @compileError("missing std lib enhancement: std.process.execv implementation has no way to collect the environment variables to forward to the child process");1591 @compileError("missing std lib enhancement: std.process.execv implementation has no way to collect the environment variables to forward to the child process");
1543 }1592 }
1544 };1593 };
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);
1547}1596}
15481597
1549pub const TotalSystemMemoryError = error{1598pub const TotalSystemMemoryError = error{
...@@ -1555,14 +1604,14 @@ pub const TotalSystemMemoryError = error{...@@ -1555,14 +1604,14 @@ pub const TotalSystemMemoryError = error{
1555/// and Linux's /proc/meminfo reporting more memory when1604/// and Linux's /proc/meminfo reporting more memory when
1556/// using QEMU user mode emulation.1605/// using QEMU user mode emulation.
1557pub fn totalSystemMemory() TotalSystemMemoryError!u64 {1606pub fn totalSystemMemory() TotalSystemMemoryError!u64 {
1558 switch (builtin.os.tag) {1607 switch (native_os) {
1559 .linux => {1608 .linux => {
1560 return totalSystemMemoryLinux() catch return error.UnknownTotalSystemMemory;1609 return totalSystemMemoryLinux() catch return error.UnknownTotalSystemMemory;
1561 },1610 },
1562 .freebsd => {1611 .freebsd => {
1563 var physmem: c_ulong = undefined;1612 var physmem: c_ulong = undefined;
1564 var len: usize = @sizeOf(c_ulong);1613 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) {
1566 error.NameTooLong, error.UnknownName => unreachable,1615 error.NameTooLong, error.UnknownName => unreachable,
1567 else => return error.UnknownTotalSystemMemory,1616 else => return error.UnknownTotalSystemMemory,
1568 };1617 };
...@@ -1570,12 +1619,12 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 {...@@ -1570,12 +1619,12 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 {
1570 },1619 },
1571 .openbsd => {1620 .openbsd => {
1572 const mib: [2]c_int = [_]c_int{1621 const mib: [2]c_int = [_]c_int{
1573 std.os.CTL.HW,1622 posix.CTL.HW,
1574 std.os.HW.PHYSMEM64,1623 posix.HW.PHYSMEM64,
1575 };1624 };
1576 var physmem: i64 = undefined;1625 var physmem: i64 = undefined;
1577 var len: usize = @sizeOf(@TypeOf(physmem));1626 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) {
1579 error.NameTooLong => unreachable, // constant, known good value1628 error.NameTooLong => unreachable, // constant, known good value
1580 error.PermissionDenied => unreachable, // only when setting values,1629 error.PermissionDenied => unreachable, // only when setting values,
1581 error.SystemResources => unreachable, // memory already on the stack1630 error.SystemResources => unreachable, // memory already on the stack
...@@ -1586,11 +1635,11 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 {...@@ -1586,11 +1635,11 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 {
1586 return @as(u64, @bitCast(physmem));1635 return @as(u64, @bitCast(physmem));
1587 },1636 },
1588 .windows => {1637 .windows => {
1589 var sbi: std.os.windows.SYSTEM_BASIC_INFORMATION = undefined;1638 var sbi: windows.SYSTEM_BASIC_INFORMATION = undefined;
1590 const rc = std.os.windows.ntdll.NtQuerySystemInformation(1639 const rc = windows.ntdll.NtQuerySystemInformation(
1591 .SystemBasicInformation,1640 .SystemBasicInformation,
1592 &sbi,1641 &sbi,
1593 @sizeOf(std.os.windows.SYSTEM_BASIC_INFORMATION),1642 @sizeOf(windows.SYSTEM_BASIC_INFORMATION),
1594 null,1643 null,
1595 );1644 );
1596 if (rc != .SUCCESS) {1645 if (rc != .SUCCESS) {
lib/std/start.zig+40-5
...@@ -407,7 +407,7 @@ fn posixCallMainAndExit() callconv(.C) noreturn {...@@ -407,7 +407,7 @@ fn posixCallMainAndExit() callconv(.C) noreturn {
407 // FIXME: Make __aeabi_read_tp call the kernel helper kuser_get_tls407 // FIXME: Make __aeabi_read_tp call the kernel helper kuser_get_tls
408 // For the time being use a simple abort instead of a @panic call to408 // For the time being use a simple abort instead of a @panic call to
409 // keep the binary bloat under control.409 // keep the binary bloat under control.
410 std.os.abort();410 std.posix.abort();
411 }411 }
412 }412 }
413413
...@@ -422,7 +422,7 @@ fn posixCallMainAndExit() callconv(.C) noreturn {...@@ -422,7 +422,7 @@ fn posixCallMainAndExit() callconv(.C) noreturn {
422 expandStackSize(phdrs);422 expandStackSize(phdrs);
423 }423 }
424424
425 std.os.exit(callMainWithArgs(argc, argv, envp));425 std.posix.exit(callMainWithArgs(argc, argv, envp));
426}426}
427427
428fn expandStackSize(phdrs: []elf.Phdr) void {428fn expandStackSize(phdrs: []elf.Phdr) void {
...@@ -432,13 +432,13 @@ fn expandStackSize(phdrs: []elf.Phdr) void {...@@ -432,13 +432,13 @@ fn expandStackSize(phdrs: []elf.Phdr) void {
432 assert(phdr.p_memsz % std.mem.page_size == 0);432 assert(phdr.p_memsz % std.mem.page_size == 0);
433433
434 // Silently fail if we are unable to get limits.434 // 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
437 // Clamp to limits.max .437 // Clamp to limits.max .
438 const wanted_stack_size = @min(phdr.p_memsz, limits.max);438 const wanted_stack_size = @min(phdr.p_memsz, limits.max);
439439
440 if (wanted_stack_size > limits.cur) {440 if (wanted_stack_size > limits.cur) {
441 std.os.setrlimit(.STACK, .{441 std.posix.setrlimit(.STACK, .{
442 .cur = wanted_stack_size,442 .cur = wanted_stack_size,
443 .max = limits.max,443 .max = limits.max,
444 }) catch {444 }) catch {
...@@ -464,7 +464,7 @@ inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 {...@@ -464,7 +464,7 @@ inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 {
464 std.os.environ = envp;464 std.os.environ = envp;
465465
466 std.debug.maybeEnableSegfaultHandler();466 std.debug.maybeEnableSegfaultHandler();
467 std.os.maybeIgnoreSigpipe();467 maybeIgnoreSigpipe();
468468
469 return callMain();469 return callMain();
470}470}
...@@ -563,3 +563,38 @@ pub fn call_wWinMain() std.os.windows.INT {...@@ -563,3 +563,38 @@ pub fn call_wWinMain() std.os.windows.INT {
563 // second parameter hPrevInstance, MSDN: "This parameter is always NULL"563 // second parameter hPrevInstance, MSDN: "This parameter is always NULL"
564 return root.wWinMain(hInstance, null, lpCmdLine, nCmdShow);564 return root.wWinMain(hInstance, null, lpCmdLine, nCmdShow);
565}565}
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");...@@ -85,12 +85,11 @@ pub const math = @import("math.zig");
85pub const mem = @import("mem.zig");85pub const mem = @import("mem.zig");
86pub const meta = @import("meta.zig");86pub const meta = @import("meta.zig");
87pub const net = @import("net.zig");87pub const net = @import("net.zig");
88pub const posix = @import("os.zig");
89/// Non-portable Operating System-specific API.
90pub const os = @import("os.zig");88pub const os = @import("os.zig");
91pub const once = @import("once.zig").once;89pub const once = @import("once.zig").once;
92pub const packed_int_array = @import("packed_int_array.zig");90pub const packed_int_array = @import("packed_int_array.zig");
93pub const pdb = @import("pdb.zig");91pub const pdb = @import("pdb.zig");
92pub const posix = @import("posix.zig");
94pub const process = @import("process.zig");93pub const process = @import("process.zig");
95/// Deprecated: use `Random` instead.94/// Deprecated: use `Random` instead.
96pub const rand = Random;95pub const rand = Random;
...@@ -170,3 +169,7 @@ comptime {...@@ -170,3 +169,7 @@ comptime {
170test {169test {
171 testing.refAllDecls(@This());170 testing.refAllDecls(@This());
172}171}
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");...@@ -2,8 +2,9 @@ const std = @import("std.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const testing = std.testing;4const testing = std.testing;
5const os = std.os;
6const math = std.math;5const math = std.math;
6const windows = std.os.windows;
7const posix = std.posix;
78
8pub const epoch = @import("time/epoch.zig");9pub const epoch = @import("time/epoch.zig");
910
...@@ -11,8 +12,8 @@ pub const epoch = @import("time/epoch.zig");...@@ -11,8 +12,8 @@ pub const epoch = @import("time/epoch.zig");
11pub fn sleep(nanoseconds: u64) void {12pub fn sleep(nanoseconds: u64) void {
12 if (builtin.os.tag == .windows) {13 if (builtin.os.tag == .windows) {
13 const big_ms_from_ns = nanoseconds / ns_per_ms;14 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 const ms = math.cast(windows.DWORD, big_ms_from_ns) orelse math.maxInt(windows.DWORD);
15 os.windows.kernel32.Sleep(ms);16 windows.kernel32.Sleep(ms);
16 return;17 return;
17 }18 }
1819
...@@ -40,7 +41,7 @@ pub fn sleep(nanoseconds: u64) void {...@@ -40,7 +41,7 @@ pub fn sleep(nanoseconds: u64) void {
40 }41 }
4142
42 if (builtin.os.tag == .uefi) {43 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.?;
44 const us_from_ns = nanoseconds / ns_per_us;45 const us_from_ns = nanoseconds / ns_per_us;
45 const us = math.cast(usize, us_from_ns) orelse math.maxInt(usize);46 const us = math.cast(usize, us_from_ns) orelse math.maxInt(usize);
46 _ = boot_services.stall(us);47 _ = boot_services.stall(us);
...@@ -49,7 +50,7 @@ pub fn sleep(nanoseconds: u64) void {...@@ -49,7 +50,7 @@ pub fn sleep(nanoseconds: u64) void {
4950
50 const s = nanoseconds / ns_per_s;51 const s = nanoseconds / ns_per_s;
51 const ns = nanoseconds % ns_per_s;52 const ns = nanoseconds % ns_per_s;
52 std.os.nanosleep(s, ns);53 posix.nanosleep(s, ns);
53}54}
5455
55test "sleep" {56test "sleep" {
...@@ -60,7 +61,7 @@ test "sleep" {...@@ -60,7 +61,7 @@ test "sleep" {
60/// Precision of timing depends on the hardware and operating system.61/// Precision of timing depends on the hardware and operating system.
61/// The return value is signed because it is possible to have a date that is62/// The return value is signed because it is possible to have a date that is
62/// before the epoch.63/// before the epoch.
63/// See `std.os.clock_gettime` for a POSIX timestamp.64/// See `posix.clock_gettime` for a POSIX timestamp.
64pub fn timestamp() i64 {65pub fn timestamp() i64 {
65 return @divFloor(milliTimestamp(), ms_per_s);66 return @divFloor(milliTimestamp(), ms_per_s);
66}67}
...@@ -69,7 +70,7 @@ pub fn timestamp() i64 {...@@ -69,7 +70,7 @@ pub fn timestamp() i64 {
69/// Precision of timing depends on the hardware and operating system.70/// Precision of timing depends on the hardware and operating system.
70/// The return value is signed because it is possible to have a date that is71/// The return value is signed because it is possible to have a date that is
71/// before the epoch.72/// before the epoch.
72/// See `std.os.clock_gettime` for a POSIX timestamp.73/// See `posix.clock_gettime` for a POSIX timestamp.
73pub fn milliTimestamp() i64 {74pub fn milliTimestamp() i64 {
74 return @as(i64, @intCast(@divFloor(nanoTimestamp(), ns_per_ms)));75 return @as(i64, @intCast(@divFloor(nanoTimestamp(), ns_per_ms)));
75}76}
...@@ -78,7 +79,7 @@ pub fn milliTimestamp() i64 {...@@ -78,7 +79,7 @@ pub fn milliTimestamp() i64 {
78/// Precision of timing depends on the hardware and operating system.79/// Precision of timing depends on the hardware and operating system.
79/// The return value is signed because it is possible to have a date that is80/// The return value is signed because it is possible to have a date that is
80/// before the epoch.81/// before the epoch.
81/// See `std.os.clock_gettime` for a POSIX timestamp.82/// See `posix.clock_gettime` for a POSIX timestamp.
82pub fn microTimestamp() i64 {83pub fn microTimestamp() i64 {
83 return @as(i64, @intCast(@divFloor(nanoTimestamp(), ns_per_us)));84 return @as(i64, @intCast(@divFloor(nanoTimestamp(), ns_per_us)));
84}85}
...@@ -88,21 +89,21 @@ pub fn microTimestamp() i64 {...@@ -88,21 +89,21 @@ pub fn microTimestamp() i64 {
88/// On Windows this has a maximum granularity of 100 nanoseconds.89/// On Windows this has a maximum granularity of 100 nanoseconds.
89/// The return value is signed because it is possible to have a date that is90/// The return value is signed because it is possible to have a date that is
90/// before the epoch.91/// before the epoch.
91/// See `std.os.clock_gettime` for a POSIX timestamp.92/// See `posix.clock_gettime` for a POSIX timestamp.
92pub fn nanoTimestamp() i128 {93pub fn nanoTimestamp() i128 {
93 switch (builtin.os.tag) {94 switch (builtin.os.tag) {
94 .windows => {95 .windows => {
95 // FileTime has a granularity of 100 nanoseconds and uses the NTFS/Windows epoch,96 // FileTime has a granularity of 100 nanoseconds and uses the NTFS/Windows epoch,
96 // which is 1601-01-01.97 // which is 1601-01-01.
97 const epoch_adj = epoch.windows * (ns_per_s / 100);98 const epoch_adj = epoch.windows * (ns_per_s / 100);
98 var ft: os.windows.FILETIME = undefined;99 var ft: windows.FILETIME = undefined;
99 os.windows.kernel32.GetSystemTimeAsFileTime(&ft);100 windows.kernel32.GetSystemTimeAsFileTime(&ft);
100 const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;101 const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
101 return @as(i128, @as(i64, @bitCast(ft64)) + epoch_adj) * 100;102 return @as(i128, @as(i64, @bitCast(ft64)) + epoch_adj) * 100;
102 },103 },
103 .wasi => {104 .wasi => {
104 var ns: os.wasi.timestamp_t = undefined;105 var ns: std.os.wasi.timestamp_t = undefined;
105 const err = os.wasi.clock_time_get(.REALTIME, 1, &ns);106 const err = std.os.wasi.clock_time_get(.REALTIME, 1, &ns);
106 assert(err == .SUCCESS);107 assert(err == .SUCCESS);
107 return ns;108 return ns;
108 },109 },
...@@ -113,8 +114,8 @@ pub fn nanoTimestamp() i128 {...@@ -113,8 +114,8 @@ pub fn nanoTimestamp() i128 {
113 return value.toEpoch();114 return value.toEpoch();
114 },115 },
115 else => {116 else => {
116 var ts: os.timespec = undefined;117 var ts: posix.timespec = undefined;
117 os.clock_gettime(os.CLOCK.REALTIME, &ts) catch |err| switch (err) {118 posix.clock_gettime(posix.CLOCK.REALTIME, &ts) catch |err| switch (err) {
118 error.UnsupportedClock, error.Unexpected => return 0, // "Precision of timing depends on hardware and OS".119 error.UnsupportedClock, error.Unexpected => return 0, // "Precision of timing depends on hardware and OS".
119 };120 };
120 return (@as(i128, ts.tv_sec) * ns_per_s) + ts.tv_nsec;121 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;...@@ -172,7 +173,7 @@ pub const s_per_week = s_per_day * 7;
172/// It also tries to be monotonic, but this is not a guarantee due to OS/hardware bugs.173/// It also tries to be monotonic, but this is not a guarantee due to OS/hardware bugs.
173/// If you need monotonic readings for elapsed time, consider `Timer` instead.174/// If you need monotonic readings for elapsed time, consider `Timer` instead.
174pub const Instant = struct {175pub const Instant = struct {
175 timestamp: if (is_posix) os.timespec else u64,176 timestamp: if (is_posix) posix.timespec else u64,
176177
177 // true if we should use clock_gettime()178 // true if we should use clock_gettime()
178 const is_posix = switch (builtin.os.tag) {179 const is_posix = switch (builtin.os.tag) {
...@@ -188,11 +189,11 @@ pub const Instant = struct {...@@ -188,11 +189,11 @@ pub const Instant = struct {
188 const clock_id = switch (builtin.os.tag) {189 const clock_id = switch (builtin.os.tag) {
189 .windows => {190 .windows => {
190 // QPC on windows doesn't fail on >= XP/2000 and includes time suspended.191 // 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() };
192 },193 },
193 .wasi => {194 .wasi => {
194 var ns: os.wasi.timestamp_t = undefined;195 var ns: std.os.wasi.timestamp_t = undefined;
195 const rc = os.wasi.clock_time_get(.MONOTONIC, 1, &ns);196 const rc = std.os.wasi.clock_time_get(.MONOTONIC, 1, &ns);
196 if (rc != .SUCCESS) return error.Unsupported;197 if (rc != .SUCCESS) return error.Unsupported;
197 return .{ .timestamp = ns };198 return .{ .timestamp = ns };
198 },199 },
...@@ -204,21 +205,21 @@ pub const Instant = struct {...@@ -204,21 +205,21 @@ pub const Instant = struct {
204 },205 },
205 // On darwin, use UPTIME_RAW instead of MONOTONIC as it ticks while206 // On darwin, use UPTIME_RAW instead of MONOTONIC as it ticks while
206 // suspended.207 // suspended.
207 .macos, .ios, .tvos, .watchos => os.CLOCK.UPTIME_RAW,208 .macos, .ios, .tvos, .watchos => posix.CLOCK.UPTIME_RAW,
208 // On freebsd derivatives, use MONOTONIC_FAST as currently there's209 // On freebsd derivatives, use MONOTONIC_FAST as currently there's
209 // no precision tradeoff.210 // no precision tradeoff.
210 .freebsd, .dragonfly => os.CLOCK.MONOTONIC_FAST,211 .freebsd, .dragonfly => posix.CLOCK.MONOTONIC_FAST,
211 // On linux, use BOOTTIME instead of MONOTONIC as it ticks while212 // On linux, use BOOTTIME instead of MONOTONIC as it ticks while
212 // suspended.213 // suspended.
213 .linux => os.CLOCK.BOOTTIME,214 .linux => posix.CLOCK.BOOTTIME,
214 // On other posix systems, MONOTONIC is generally the fastest and215 // On other posix systems, MONOTONIC is generally the fastest and
215 // ticks while suspended.216 // ticks while suspended.
216 else => os.CLOCK.MONOTONIC,217 else => posix.CLOCK.MONOTONIC,
217 };218 };
218219
219 var ts: os.timespec = undefined;220 var ts: posix.timespec = undefined;
220 os.clock_gettime(clock_id, &ts) catch return error.Unsupported;221 posix.clock_gettime(clock_id, &ts) catch return error.Unsupported;
221 return Instant{ .timestamp = ts };222 return .{ .timestamp = ts };
222 }223 }
223224
224 /// Quickly compares two instances between each other.225 /// Quickly compares two instances between each other.
...@@ -245,7 +246,7 @@ pub const Instant = struct {...@@ -245,7 +246,7 @@ pub const Instant = struct {
245 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data246 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
246 // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm247 // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
247 const qpc = self.timestamp - earlier.timestamp;248 const qpc = self.timestamp - earlier.timestamp;
248 const qpf = os.windows.QueryPerformanceFrequency();249 const qpf = windows.QueryPerformanceFrequency();
249250
250 // 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it.251 // 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it.
251 // https://github.com/microsoft/STL/blob/785143a0c73f030238ef618890fd4d6ae2b3a3a0/stl/inc/chrono#L694-L701252 // 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 {...@@ -1002,7 +1002,7 @@ pub const EnvVar = enum {
1002 }1002 }
10031003
1004 pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 {1004 pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 {
1005 return std.os.getenvZ(@tagName(ev));1005 return std.posix.getenvZ(@tagName(ev));
1006 }1006 }
1007};1007};
10081008
lib/std/zig/Server.zig+1-1
...@@ -146,7 +146,7 @@ pub fn serveMessage(...@@ -146,7 +146,7 @@ pub fn serveMessage(
146 header: OutMessage.Header,146 header: OutMessage.Header,
147 bufs: []const []const u8,147 bufs: []const []const u8,
148) !void {148) !void {
149 var iovecs: [10]std.os.iovec_const = undefined;149 var iovecs: [10]std.posix.iovec_const = undefined;
150 const header_le = bswap(header);150 const header_le = bswap(header);
151 iovecs[0] = .{151 iovecs[0] = .{
152 .iov_base = @as([*]const u8, @ptrCast(&header_le)),152 .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 {...@@ -168,7 +168,7 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {
168 if (query.os_tag == null) {168 if (query.os_tag == null) {
169 switch (builtin.target.os.tag) {169 switch (builtin.target.os.tag) {
170 .linux => {170 .linux => {
171 const uts = std.os.uname();171 const uts = posix.uname();
172 const release = mem.sliceTo(&uts.release, 0);172 const release = mem.sliceTo(&uts.release, 0);
173 // The release field sometimes has a weird format,173 // The release field sometimes has a weird format,
174 // `Version.parse` will attempt to find some meaningful interpretation.174 // `Version.parse` will attempt to find some meaningful interpretation.
...@@ -181,7 +181,7 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {...@@ -181,7 +181,7 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {
181 }181 }
182 },182 },
183 .solaris, .illumos => {183 .solaris, .illumos => {
184 const uts = std.os.uname();184 const uts = posix.uname();
185 const release = mem.sliceTo(&uts.release, 0);185 const release = mem.sliceTo(&uts.release, 0);
186 if (std.SemanticVersion.parse(release)) |ver| {186 if (std.SemanticVersion.parse(release)) |ver| {
187 os.version_range.semver.min = ver;187 os.version_range.semver.min = ver;
...@@ -206,7 +206,7 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {...@@ -206,7 +206,7 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {
206 var value: u32 = undefined;206 var value: u32 = undefined;
207 var len: usize = @sizeOf(@TypeOf(value));207 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) {
210 error.NameTooLong => unreachable, // constant, known good value210 error.NameTooLong => unreachable, // constant, known good value
211 error.PermissionDenied => unreachable, // only when setting values,211 error.PermissionDenied => unreachable, // only when setting values,
212 error.SystemResources => unreachable, // memory already on the stack212 error.SystemResources => unreachable, // memory already on the stack
...@@ -257,15 +257,15 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {...@@ -257,15 +257,15 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target {
257 },257 },
258 .openbsd => {258 .openbsd => {
259 const mib: [2]c_int = [_]c_int{259 const mib: [2]c_int = [_]c_int{
260 std.os.CTL.KERN,260 posix.CTL.KERN,
261 std.os.KERN.OSRELEASE,261 posix.KERN.OSRELEASE,
262 };262 };
263 var buf: [64]u8 = undefined;263 var buf: [64]u8 = undefined;
264 // consider that sysctl result includes null-termination264 // consider that sysctl result includes null-termination
265 // reserve 1 byte to ensure we never overflow when appending ".0"265 // reserve 1 byte to ensure we never overflow when appending ".0"
266 var len: usize = buf.len - 1;266 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) {
269 error.NameTooLong => unreachable, // constant, known good value269 error.NameTooLong => unreachable, // constant, known good value
270 error.PermissionDenied => unreachable, // only when setting values,270 error.PermissionDenied => unreachable, // only when setting values,
271 error.SystemResources => unreachable, // memory already on the stack271 error.SystemResources => unreachable, // memory already on the stack
...@@ -636,8 +636,8 @@ pub fn abiAndDynamicLinkerFromFile(...@@ -636,8 +636,8 @@ pub fn abiAndDynamicLinkerFromFile(
636636
637 // So far, no luck. Next we try to see if the information is637 // So far, no luck. Next we try to see if the information is
638 // present in the symlink data for the dynamic linker path.638 // present in the symlink data for the dynamic linker path.
639 var link_buf: [std.os.PATH_MAX]u8 = undefined;639 var link_buf: [posix.PATH_MAX]u8 = undefined;
640 const link_name = std.os.readlink(dl_path, &link_buf) catch |err| switch (err) {640 const link_name = posix.readlink(dl_path, &link_buf) catch |err| switch (err) {
641 error.NameTooLong => unreachable,641 error.NameTooLong => unreachable,
642 error.InvalidUtf8 => unreachable, // WASI only642 error.InvalidUtf8 => unreachable, // WASI only
643 error.InvalidWtf8 => unreachable, // Windows only643 error.InvalidWtf8 => unreachable, // Windows only
...@@ -670,7 +670,7 @@ pub fn abiAndDynamicLinkerFromFile(...@@ -670,7 +670,7 @@ pub fn abiAndDynamicLinkerFromFile(
670670
671 // Nothing worked so far. Finally we fall back to hard-coded search paths.671 // Nothing worked so far. Finally we fall back to hard-coded search paths.
672 // Some distros such as Debian keep their libc.so.6 in `/lib/$triple/`.672 // 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;
674 var index: usize = 0;674 var index: usize = 0;
675 const prefix = "/lib/";675 const prefix = "/lib/";
676 const cpu_arch = @tagName(result.cpu.arch);676 const cpu_arch = @tagName(result.cpu.arch);
...@@ -1138,6 +1138,7 @@ const fs = std.fs;...@@ -1138,6 +1138,7 @@ const fs = std.fs;
1138const assert = std.debug.assert;1138const assert = std.debug.assert;
1139const Target = std.Target;1139const Target = std.Target;
1140const native_endian = builtin.cpu.arch.endian();1140const native_endian = builtin.cpu.arch.endian();
1141const posix = std.posix;
11411142
1142test {1143test {
1143 _ = NativePaths;1144 _ = NativePaths;
lib/std/zig/system/NativePaths.zig+3-3
...@@ -137,21 +137,21 @@ pub fn detect(arena: Allocator, native_target: std.Target) !NativePaths {...@@ -137,21 +137,21 @@ pub fn detect(arena: Allocator, native_target: std.Target) !NativePaths {
137 // variables to search for headers and libraries.137 // variables to search for headers and libraries.
138 // We use os.getenv here since this part won't be executed on138 // We use os.getenv here since this part won't be executed on
139 // windows, to get rid of unnecessary error handling.139 // 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| {
141 var it = mem.tokenizeScalar(u8, c_include_path, ':');141 var it = mem.tokenizeScalar(u8, c_include_path, ':');
142 while (it.next()) |dir| {142 while (it.next()) |dir| {
143 try self.addIncludeDir(dir);143 try self.addIncludeDir(dir);
144 }144 }
145 }145 }
146146
147 if (std.os.getenv("CPLUS_INCLUDE_PATH")) |cplus_include_path| {147 if (std.posix.getenv("CPLUS_INCLUDE_PATH")) |cplus_include_path| {
148 var it = mem.tokenizeScalar(u8, cplus_include_path, ':');148 var it = mem.tokenizeScalar(u8, cplus_include_path, ':');
149 while (it.next()) |dir| {149 while (it.next()) |dir| {
150 try self.addIncludeDir(dir);150 try self.addIncludeDir(dir);
151 }151 }
152 }152 }
153153
154 if (std.os.getenv("LIBRARY_PATH")) |library_path| {154 if (std.posix.getenv("LIBRARY_PATH")) |library_path| {
155 var it = mem.tokenizeScalar(u8, library_path, ':');155 var it = mem.tokenizeScalar(u8, library_path, ':');
156 while (it.next()) |dir| {156 while (it.next()) |dir| {
157 try self.addLibDir(dir);157 try self.addLibDir(dir);
lib/std/zig/system/darwin/macos.zig+1-2
...@@ -3,7 +3,6 @@ const builtin = @import("builtin");...@@ -3,7 +3,6 @@ const builtin = @import("builtin");
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const mem = std.mem;4const mem = std.mem;
5const testing = std.testing;5const testing = std.testing;
6const os = std.os;
76
8const Target = std.Target;7const Target = std.Target;
98
...@@ -397,7 +396,7 @@ test "detect" {...@@ -397,7 +396,7 @@ test "detect" {
397pub fn detectNativeCpuAndFeatures() ?Target.Cpu {396pub fn detectNativeCpuAndFeatures() ?Target.Cpu {
398 var cpu_family: std.c.CPUFAMILY = undefined;397 var cpu_family: std.c.CPUFAMILY = undefined;
399 var len: usize = @sizeOf(std.c.CPUFAMILY);398 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) {
401 error.NameTooLong => unreachable, // constant, known good value400 error.NameTooLong => unreachable, // constant, known good value
402 error.PermissionDenied => unreachable, // only when setting values,401 error.PermissionDenied => unreachable, // only when setting values,
403 error.SystemResources => unreachable, // memory already on the stack402 error.SystemResources => unreachable, // memory already on the stack
src/Compilation.zig+4-4
...@@ -1137,7 +1137,7 @@ fn addModuleTableToCacheHash(...@@ -1137,7 +1137,7 @@ fn addModuleTableToCacheHash(
1137 root_mod: *Package.Module,1137 root_mod: *Package.Module,
1138 main_mod: *Package.Module,1138 main_mod: *Package.Module,
1139 hash_type: union(enum) { path_bytes, files: *Cache.Manifest },1139 hash_type: union(enum) { path_bytes, files: *Cache.Manifest },
1140) (error{OutOfMemory} || std.os.GetCwdError)!void {1140) (error{OutOfMemory} || std.process.GetCwdError)!void {
1141 var seen_table: std.AutoArrayHashMapUnmanaged(*Package.Module, void) = .{};1141 var seen_table: std.AutoArrayHashMapUnmanaged(*Package.Module, void) = .{};
1142 defer seen_table.deinit(gpa);1142 defer seen_table.deinit(gpa);
11431143
...@@ -2741,7 +2741,7 @@ const Header = extern struct {...@@ -2741,7 +2741,7 @@ const Header = extern struct {
2741/// saved, such as the target and most CLI flags. A cache hit will only occur2741/// saved, such as the target and most CLI flags. A cache hit will only occur
2742/// when subsequent compiler invocations use the same set of flags.2742/// when subsequent compiler invocations use the same set of flags.
2743pub fn saveState(comp: *Compilation) !void {2743pub 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;
2745 var bufs_len: usize = 0;2745 var bufs_len: usize = 0;
27462746
2747 const lf = comp.bin_file orelse return;2747 const lf = comp.bin_file orelse return;
...@@ -2808,7 +2808,7 @@ pub fn saveState(comp: *Compilation) !void {...@@ -2808,7 +2808,7 @@ pub fn saveState(comp: *Compilation) !void {
2808 try af.finish();2808 try af.finish();
2809}2809}
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 {
2812 const i = bufs_len.*;2812 const i = bufs_len.*;
2813 bufs_len.* = i + 1;2813 bufs_len.* = i + 1;
2814 bufs_list[i] = .{2814 bufs_list[i] = .{
...@@ -3791,7 +3791,7 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void {...@@ -3791,7 +3791,7 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void {
3791 break :p padding_buffer[0..n];3791 break :p padding_buffer[0..n];
3792 };3792 };
37933793
3794 var header_and_trailer: [2]std.os.iovec_const = .{3794 var header_and_trailer: [2]std.posix.iovec_const = .{
3795 .{ .iov_base = header_bytes.ptr, .iov_len = header_bytes.len },3795 .{ .iov_base = header_bytes.ptr, .iov_len = header_bytes.len },
3796 .{ .iov_base = padding.ptr, .iov_len = padding.len },3796 .{ .iov_base = padding.ptr, .iov_len = padding.len },
3797 };3797 };
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.posix.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.posix.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.posix.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.posix.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.posix.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 {...@@ -2396,7 +2396,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
2396 .stat_inode = stat.inode,2396 .stat_inode = stat.inode,
2397 .stat_mtime = stat.mtime,2397 .stat_mtime = stat.mtime,
2398 };2398 };
2399 var iovecs = [_]std.os.iovec_const{2399 var iovecs = [_]std.posix.iovec_const{
2400 .{2400 .{
2401 .iov_base = @as([*]const u8, @ptrCast(&header)),2401 .iov_base = @as([*]const u8, @ptrCast(&header)),
2402 .iov_len = @sizeOf(Zir.Header),2402 .iov_len = @sizeOf(Zir.Header),
...@@ -2484,7 +2484,7 @@ fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.File)...@@ -2484,7 +2484,7 @@ fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.File)
2484 else2484 else
2485 @as([*]u8, @ptrCast(zir.instructions.items(.data).ptr));2485 @as([*]u8, @ptrCast(zir.instructions.items(.data).ptr));
24862486
2487 var iovecs = [_]std.os.iovec{2487 var iovecs = [_]std.posix.iovec{
2488 .{2488 .{
2489 .iov_base = @as([*]u8, @ptrCast(zir.instructions.items(.tag).ptr)),2489 .iov_base = @as([*]u8, @ptrCast(zir.instructions.items(.tag).ptr)),
2490 .iov_len = header.instructions_len,2490 .iov_len = header.instructions_len,
src/Package/Fetch.zig+10-9
...@@ -482,7 +482,7 @@ fn runResource(...@@ -482,7 +482,7 @@ fn runResource(
482 // Compute the package hash based on the remaining files in the temporary482 // Compute the package hash based on the remaining files in the temporary
483 // directory.483 // 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) {
486 // https://github.com/ziglang/zig/issues/17095486 // https://github.com/ziglang/zig/issues/17095
487 tmp_directory.handle.close();487 tmp_directory.handle.close();
488 tmp_directory.handle = cache_root.handle.makeOpenPath(tmp_dir_sub_path, .{488 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 {...@@ -1153,11 +1153,7 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!void {
1153 std.tar.pipeToFileSystem(out_dir, reader, .{1153 std.tar.pipeToFileSystem(out_dir, reader, .{
1154 .diagnostics = &diagnostics,1154 .diagnostics = &diagnostics,
1155 .strip_components = 1,1155 .strip_components = 1,
1156 // TODO: we would like to set this to executable_bit_only, but two1156 // https://github.com/ziglang/zig/issues/17463
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).
1161 .mode_mode = .ignore,1157 .mode_mode = .ignore,
1162 .exclude_empty_directories = true,1158 .exclude_empty_directories = true,
1163 }) catch |err| return f.fail(f.location_tok, try eb.printString(1159 }) 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...@@ -1542,6 +1538,8 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void
1542 .file => {1538 .file => {
1543 var file = try dir.openFile(hashed_file.fs_path, .{});1539 var file = try dir.openFile(hashed_file.fs_path, .{});
1544 defer file.close();1540 defer file.close();
1541 // When implementing https://github.com/ziglang/zig/issues/17463
1542 // this will change to hard-coded `false`.
1545 hasher.update(&.{ 0, @intFromBool(try isExecutable(file)) });1543 hasher.update(&.{ 0, @intFromBool(try isExecutable(file)) });
1546 while (true) {1544 while (true) {
1547 const bytes_read = try file.read(&buf);1545 const bytes_read = try file.read(&buf);
...@@ -1568,15 +1566,17 @@ fn deleteFileFallible(dir: fs.Dir, deleted_file: *DeletedFile) DeletedFile.Error...@@ -1568,15 +1566,17 @@ fn deleteFileFallible(dir: fs.Dir, deleted_file: *DeletedFile) DeletedFile.Error
1568}1566}
15691567
1570fn isExecutable(file: fs.File) !bool {1568fn isExecutable(file: fs.File) !bool {
1571 if (builtin.os.tag == .windows) {1569 // When implementing https://github.com/ziglang/zig/issues/17463
1572 // TODO check the ACL on Windows.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) {
1573 // Until this is implemented, this could be a false negative on1573 // Until this is implemented, this could be a false negative on
1574 // Windows, which is why we do not yet set executable_bit_only above1574 // Windows, which is why we do not yet set executable_bit_only above
1575 // when unpacking the tarball.1575 // when unpacking the tarball.
1576 return false;1576 return false;
1577 } else {1577 } else {
1578 const stat = try file.stat();1578 const stat = try file.stat();
1579 return (stat.mode & std.os.S.IXUSR) != 0;1579 return (stat.mode & std.posix.S.IXUSR) != 0;
1580 }1580 }
1581}1581}
15821582
...@@ -1694,6 +1694,7 @@ const git = @import("Fetch/git.zig");...@@ -1694,6 +1694,7 @@ const git = @import("Fetch/git.zig");
1694const Package = @import("../Package.zig");1694const Package = @import("../Package.zig");
1695const Manifest = Package.Manifest;1695const Manifest = Package.Manifest;
1696const ErrorBundle = std.zig.ErrorBundle;1696const ErrorBundle = std.zig.ErrorBundle;
1697const native_os = builtin.os.tag;
16971698
1698test {1699test {
1699 _ = Filter;1700 _ = Filter;
src/crash_report.zig+21-20
...@@ -2,9 +2,10 @@ const std = @import("std");...@@ -2,9 +2,10 @@ const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const build_options = @import("build_options");3const build_options = @import("build_options");
4const debug = std.debug;4const debug = std.debug;
5const os = std.os;
6const io = std.io;5const io = std.io;
7const print_zir = @import("print_zir.zig");6const print_zir = @import("print_zir.zig");
7const windows = std.os.windows;
8const posix = std.posix;
8const native_os = builtin.os.tag;9const native_os = builtin.os.tag;
910
10const Module = @import("Module.zig");11const Module = @import("Module.zig");
...@@ -156,14 +157,14 @@ pub fn attachSegfaultHandler() void {...@@ -156,14 +157,14 @@ pub fn attachSegfaultHandler() void {
156 if (!debug.have_segfault_handling_support) {157 if (!debug.have_segfault_handling_support) {
157 @compileError("segfault handler not supported for this target");158 @compileError("segfault handler not supported for this target");
158 }159 }
159 if (builtin.os.tag == .windows) {160 if (native_os == .windows) {
160 _ = os.windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);161 _ = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
161 return;162 return;
162 }163 }
163 var act = os.Sigaction{164 var act: posix.Sigaction = .{
164 .handler = .{ .sigaction = handleSegfaultPosix },165 .handler = .{ .sigaction = handleSegfaultPosix },
165 .mask = os.empty_sigset,166 .mask = posix.empty_sigset,
166 .flags = (os.SA.SIGINFO | os.SA.RESTART | os.SA.RESETHAND),167 .flags = (posix.SA.SIGINFO | posix.SA.RESTART | posix.SA.RESETHAND),
167 };168 };
168169
169 debug.updateSegfaultHandler(&act) catch {170 debug.updateSegfaultHandler(&act) catch {
...@@ -171,11 +172,11 @@ pub fn attachSegfaultHandler() void {...@@ -171,11 +172,11 @@ pub fn attachSegfaultHandler() void {
171 };172 };
172}173}
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 {
175 // TODO: use alarm() here to prevent infinite loops176 // TODO: use alarm() here to prevent infinite loops
176 PanicSwitch.preDispatch();177 PanicSwitch.preDispatch();
177178
178 const addr = switch (builtin.os.tag) {179 const addr = switch (native_os) {
179 .linux => @intFromPtr(info.fields.sigfault.addr),180 .linux => @intFromPtr(info.fields.sigfault.addr),
180 .freebsd, .macos => @intFromPtr(info.addr),181 .freebsd, .macos => @intFromPtr(info.addr),
181 .netbsd => @intFromPtr(info.info.reason.fault.addr),182 .netbsd => @intFromPtr(info.info.reason.fault.addr),
...@@ -186,9 +187,9 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any...@@ -186,9 +187,9 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any
186187
187 var err_buffer: [128]u8 = undefined;188 var err_buffer: [128]u8 = undefined;
188 const error_msg = switch (sig) {189 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 posix.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 posix.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",192 posix.SIG.BUS => std.fmt.bufPrint(&err_buffer, "Bus error at address 0x{x}", .{addr}) catch "Bus error",
192 else => std.fmt.bufPrint(&err_buffer, "Unknown error (signal {}) at address 0x{x}", .{ sig, addr }) catch "Unknown error",193 else => std.fmt.bufPrint(&err_buffer, "Unknown error (signal {}) at address 0x{x}", .{ sig, addr }) catch "Unknown error",
193 };194 };
194195
...@@ -210,20 +211,20 @@ const WindowsSegfaultMessage = union(enum) {...@@ -210,20 +211,20 @@ const WindowsSegfaultMessage = union(enum) {
210 illegal_instruction: void,211 illegal_instruction: void,
211};212};
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 {
214 switch (info.ExceptionRecord.ExceptionCode) {215 switch (info.ExceptionRecord.ExceptionCode) {
215 os.windows.EXCEPTION_DATATYPE_MISALIGNMENT => handleSegfaultWindowsExtra(info, .{ .literal = "Unaligned Memory Access" }),216 windows.EXCEPTION_DATATYPE_MISALIGNMENT => handleSegfaultWindowsExtra(info, .{ .literal = "Unaligned Memory Access" }),
216 os.windows.EXCEPTION_ACCESS_VIOLATION => handleSegfaultWindowsExtra(info, .segfault),217 windows.EXCEPTION_ACCESS_VIOLATION => handleSegfaultWindowsExtra(info, .segfault),
217 os.windows.EXCEPTION_ILLEGAL_INSTRUCTION => handleSegfaultWindowsExtra(info, .illegal_instruction),218 windows.EXCEPTION_ILLEGAL_INSTRUCTION => handleSegfaultWindowsExtra(info, .illegal_instruction),
218 os.windows.EXCEPTION_STACK_OVERFLOW => handleSegfaultWindowsExtra(info, .{ .literal = "Stack Overflow" }),219 windows.EXCEPTION_STACK_OVERFLOW => handleSegfaultWindowsExtra(info, .{ .literal = "Stack Overflow" }),
219 else => return os.windows.EXCEPTION_CONTINUE_SEARCH,220 else => return windows.EXCEPTION_CONTINUE_SEARCH,
220 }221 }
221}222}
222223
223fn handleSegfaultWindowsExtra(info: *os.windows.EXCEPTION_POINTERS, comptime msg: WindowsSegfaultMessage) noreturn {224fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, comptime msg: WindowsSegfaultMessage) noreturn {
224 PanicSwitch.preDispatch();225 PanicSwitch.preDispatch();
225226
226 const stack_ctx = if (@hasDecl(os.windows, "CONTEXT"))227 const stack_ctx = if (@hasDecl(windows, "CONTEXT"))
227 StackContext{ .exception = info.ContextRecord }228 StackContext{ .exception = info.ContextRecord }
228 else ctx: {229 else ctx: {
229 const addr = @intFromPtr(info.ExceptionRecord.ExceptionAddress);230 const addr = @intFromPtr(info.ExceptionRecord.ExceptionAddress);
...@@ -488,7 +489,7 @@ const PanicSwitch = struct {...@@ -488,7 +489,7 @@ const PanicSwitch = struct {
488 }489 }
489490
490 noinline fn abort() noreturn {491 noinline fn abort() noreturn {
491 os.abort();492 std.process.abort();
492 }493 }
493494
494 inline fn goTo(comptime func: anytype, args: anytype) noreturn {495 inline fn goTo(comptime func: anytype, args: anytype) noreturn {
src/link.zig+2-2
...@@ -254,7 +254,7 @@ pub const File = struct {...@@ -254,7 +254,7 @@ pub const File = struct {
254 try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{});254 try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{});
255 try emit.directory.handle.rename(tmp_sub_path, emit.sub_path);255 try emit.directory.handle.rename(tmp_sub_path, emit.sub_path);
256 switch (builtin.os.tag) {256 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| {
258 log.warn("ptrace failure: {s}", .{@errorName(err)});258 log.warn("ptrace failure: {s}", .{@errorName(err)});
259 },259 },
260 .macos => base.cast(MachO).?.ptraceAttach(pid) catch |err| {260 .macos => base.cast(MachO).?.ptraceAttach(pid) catch |err| {
...@@ -305,7 +305,7 @@ pub const File = struct {...@@ -305,7 +305,7 @@ pub const File = struct {
305305
306 if (base.child_pid) |pid| {306 if (base.child_pid) |pid| {
307 switch (builtin.os.tag) {307 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| {
309 log.warn("ptrace failure: {s}", .{@errorName(err)});309 log.warn("ptrace failure: {s}", .{@errorName(err)});
310 },310 },
311 else => return error.HotSwapUnavailableOnHostOperatingSystem,311 else => return error.HotSwapUnavailableOnHostOperatingSystem,
src/link/C.zig+2-2
...@@ -518,7 +518,7 @@ const Flush = struct {...@@ -518,7 +518,7 @@ const Flush = struct {
518 asm_buf: std.ArrayListUnmanaged(u8) = .{},518 asm_buf: std.ArrayListUnmanaged(u8) = .{},
519519
520 /// We collect a list of buffers to write, and write them all at once with pwritev 😎520 /// 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) = .{},
522 /// Keeps track of the total bytes of `all_buffers`.522 /// Keeps track of the total bytes of `all_buffers`.
523 file_size: u64 = 0,523 file_size: u64 = 0,
524524
...@@ -752,7 +752,7 @@ pub fn flushEmitH(module: *Module) !void {...@@ -752,7 +752,7 @@ pub fn flushEmitH(module: *Module) !void {
752752
753 // We collect a list of buffers to write, and write them all at once with pwritev 😎753 // We collect a list of buffers to write, and write them all at once with pwritev 😎
754 const num_buffers = emit_h.decl_table.count() + 1;754 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);
756 defer all_buffers.deinit();756 defer all_buffers.deinit();
757757
758 var file_size: u64 = zig_h.len;758 var file_size: u64 = zig_h.len;
src/link/Dwarf.zig+4-4
...@@ -2118,7 +2118,7 @@ fn pwriteDbgLineNops(...@@ -2118,7 +2118,7 @@ fn pwriteDbgLineNops(
21182118
2119 const page_of_nops = [1]u8{DW.LNS.negate_stmt} ** 4096;2119 const page_of_nops = [1]u8{DW.LNS.negate_stmt} ** 4096;
2120 const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 };2120 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;
2122 var vec_index: usize = 0;2122 var vec_index: usize = 0;
2123 {2123 {
2124 var padding_left = prev_padding_size;2124 var padding_left = prev_padding_size;
...@@ -2235,7 +2235,7 @@ fn pwriteDbgInfoNops(...@@ -2235,7 +2235,7 @@ fn pwriteDbgInfoNops(
2235 defer tracy.end();2235 defer tracy.end();
22362236
2237 const page_of_nops = [1]u8{@intFromEnum(AbbrevCode.padding)} ** 4096;2237 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;
2239 var vec_index: usize = 0;2239 var vec_index: usize = 0;
2240 {2240 {
2241 var padding_left = prev_padding_size;2241 var padding_left = prev_padding_size;
...@@ -2807,10 +2807,10 @@ fn genIncludeDirsAndFileNames(self: *Dwarf, arena: Allocator) !struct {...@@ -2807,10 +2807,10 @@ fn genIncludeDirsAndFileNames(self: *Dwarf, arena: Allocator) !struct {
2807 const full_path = try dif.mod.root.joinString(arena, dif.sub_file_path);2807 const full_path = try dif.mod.root.joinString(arena, dif.sub_file_path);
2808 const dir_path = std.fs.path.dirname(full_path) orelse ".";2808 const dir_path = std.fs.path.dirname(full_path) orelse ".";
2809 const sub_file_path = std.fs.path.basename(full_path);2809 const sub_file_path = std.fs.path.basename(full_path);
2810 // TODO re-investigate if realpath is needed here2810 // https://github.com/ziglang/zig/issues/19353
2811 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;2811 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
2812 const resolved = if (!std.fs.path.isAbsolute(dir_path))2812 const resolved = if (!std.fs.path.isAbsolute(dir_path))
2813 std.os.realpath(dir_path, &buffer) catch dir_path2813 std.posix.realpath(dir_path, &buffer) catch dir_path
2814 else2814 else
2815 dir_path;2815 dir_path;
28162816
src/link/Elf.zig+8-8
...@@ -422,7 +422,7 @@ pub fn createEmpty(...@@ -422,7 +422,7 @@ pub fn createEmpty(
422 const index: File.Index = @intCast(try self.files.addOne(gpa));422 const index: File.Index = @intCast(try self.files.addOne(gpa));
423 self.files.set(index, .{ .zig_object = .{423 self.files.set(index, .{ .zig_object = .{
424 .index = index,424 .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(
426 zcu.main_mod.root_src_path,426 zcu.main_mod.root_src_path,
427 )}),427 )}),
428 } });428 } });
...@@ -1673,7 +1673,7 @@ pub const ParseError = error{...@@ -1673,7 +1673,7 @@ pub const ParseError = error{
1673 NotSupported,1673 NotSupported,
1674 InvalidCharacter,1674 InvalidCharacter,
1675 UnknownFileType,1675 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
1678pub fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void {1678pub fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
1679 const tracy = trace(@src());1679 const tracy = trace(@src());
...@@ -1703,7 +1703,7 @@ fn parseObject(self: *Elf, path: []const u8) ParseError!void {...@@ -1703,7 +1703,7 @@ fn parseObject(self: *Elf, path: []const u8) ParseError!void {
1703 defer tracy.end();1703 defer tracy.end();
17041704
1705 const gpa = self.base.comp.gpa;1705 const gpa = self.base.comp.gpa;
1706 const handle = try std.fs.cwd().openFile(path, .{});1706 const handle = try fs.cwd().openFile(path, .{});
1707 const fh = try self.addFileHandle(handle);1707 const fh = try self.addFileHandle(handle);
17081708
1709 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));1709 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 {...@@ -1723,7 +1723,7 @@ fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
1723 defer tracy.end();1723 defer tracy.end();
17241724
1725 const gpa = self.base.comp.gpa;1725 const gpa = self.base.comp.gpa;
1726 const handle = try std.fs.cwd().openFile(path, .{});1726 const handle = try fs.cwd().openFile(path, .{});
1727 const fh = try self.addFileHandle(handle);1727 const fh = try self.addFileHandle(handle);
17281728
1729 var archive = Archive{};1729 var archive = Archive{};
...@@ -1749,7 +1749,7 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void {...@@ -1749,7 +1749,7 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void {
1749 defer tracy.end();1749 defer tracy.end();
17501750
1751 const gpa = self.base.comp.gpa;1751 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, .{});
1753 defer handle.close();1753 defer handle.close();
17541754
1755 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));1755 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
...@@ -1770,7 +1770,7 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {...@@ -1770,7 +1770,7 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
1770 defer tracy.end();1770 defer tracy.end();
17711771
1772 const gpa = self.base.comp.gpa;1772 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, .{});
1774 defer in_file.close();1774 defer in_file.close();
1775 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));1775 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
1776 defer gpa.free(data);1776 defer gpa.free(data);
...@@ -5468,7 +5468,7 @@ pub fn file(self: *Elf, index: File.Index) ?File {...@@ -5468,7 +5468,7 @@ pub fn file(self: *Elf, index: File.Index) ?File {
5468 };5468 };
5469}5469}
54705470
5471pub fn addFileHandle(self: *Elf, handle: std.fs.File) !File.HandleIndex {5471pub fn addFileHandle(self: *Elf, handle: fs.File) !File.HandleIndex {
5472 const gpa = self.base.comp.gpa;5472 const gpa = self.base.comp.gpa;
5473 const index: File.HandleIndex = @intCast(self.file_handles.items.len);5473 const index: File.HandleIndex = @intCast(self.file_handles.items.len);
5474 const fh = try self.file_handles.addOne(gpa);5474 const fh = try self.file_handles.addOne(gpa);
...@@ -6045,7 +6045,7 @@ fn fmtDumpState(...@@ -6045,7 +6045,7 @@ fn fmtDumpState(
6045}6045}
60466046
6047/// Caller owns the memory.6047/// 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 {
6049 const buffer = try allocator.alloc(u8, math.cast(usize, size) orelse return error.Overflow);6049 const buffer = try allocator.alloc(u8, math.cast(usize, size) orelse return error.Overflow);
6050 errdefer allocator.free(buffer);6050 errdefer allocator.free(buffer);
6051 const amt = try handle.preadAll(buffer, offset);6051 const amt = try handle.preadAll(buffer, offset);
src/link/Elf/ZigObject.zig+3-3
...@@ -965,16 +965,16 @@ fn updateDeclCode(...@@ -965,16 +965,16 @@ fn updateDeclCode(
965 if (elf_file.base.child_pid) |pid| {965 if (elf_file.base.child_pid) |pid| {
966 switch (builtin.os.tag) {966 switch (builtin.os.tag) {
967 .linux => {967 .linux => {
968 var code_vec: [1]std.os.iovec_const = .{.{968 var code_vec: [1]std.posix.iovec_const = .{.{
969 .iov_base = code.ptr,969 .iov_base = code.ptr,
970 .iov_len = code.len,970 .iov_len = code.len,
971 }};971 }};
972 var remote_vec: [1]std.os.iovec_const = .{.{972 var remote_vec: [1]std.posix.iovec_const = .{.{
973 .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(sym.address(.{}, elf_file))))),973 .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(sym.address(.{}, elf_file))))),
974 .iov_len = code.len,974 .iov_len = code.len,
975 }};975 }};
976 const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0);976 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)) {
978 .SUCCESS => assert(rc == code.len),978 .SUCCESS => assert(rc == code.len),
979 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),979 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
980 }980 }
src/link/Elf/synthetic_sections.zig+3-3
...@@ -317,16 +317,16 @@ pub const ZigGotSection = struct {...@@ -317,16 +317,16 @@ pub const ZigGotSection = struct {
317 if (elf_file.base.child_pid) |pid| {317 if (elf_file.base.child_pid) |pid| {
318 switch (builtin.os.tag) {318 switch (builtin.os.tag) {
319 .linux => {319 .linux => {
320 var local_vec: [1]std.os.iovec_const = .{.{320 var local_vec: [1]std.posix.iovec_const = .{.{
321 .iov_base = &buf,321 .iov_base = &buf,
322 .iov_len = buf.len,322 .iov_len = buf.len,
323 }};323 }};
324 var remote_vec: [1]std.os.iovec_const = .{.{324 var remote_vec: [1]std.posix.iovec_const = .{.{
325 .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(vaddr)))),325 .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(vaddr)))),
326 .iov_len = buf.len,326 .iov_len = buf.len,
327 }};327 }};
328 const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0);328 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)) {
330 .SUCCESS => assert(rc == buf.len),330 .SUCCESS => assert(rc == buf.len),
331 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),331 else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}),
332 }332 }
src/link/MachO.zig+31-31
...@@ -258,7 +258,7 @@ pub fn createEmpty(...@@ -258,7 +258,7 @@ pub fn createEmpty(
258 const index: File.Index = @intCast(try self.files.addOne(gpa));258 const index: File.Index = @intCast(try self.files.addOne(gpa));
259 self.files.set(index, .{ .zig_object = .{259 self.files.set(index, .{ .zig_object = .{
260 .index = index,260 .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(
262 zcu.main_mod.root_src_path,262 zcu.main_mod.root_src_path,
263 )}),263 )}),
264 } });264 } });
...@@ -843,7 +843,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {...@@ -843,7 +843,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void {
843 }843 }
844844
845 for (self.frameworks) |framework| {845 for (self.frameworks) |framework| {
846 const name = std.fs.path.stem(framework.path);846 const name = fs.path.stem(framework.path);
847 const arg = if (framework.needed)847 const arg = if (framework.needed)
848 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name})848 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name})
849 else if (framework.weak)849 else if (framework.weak)
...@@ -917,7 +917,7 @@ pub const ParseError = error{...@@ -917,7 +917,7 @@ pub const ParseError = error{
917 NotSupported,917 NotSupported,
918 Unhandled,918 Unhandled,
919 UnknownFileType,919 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
922pub fn parsePositional(self: *MachO, path: []const u8, must_link: bool) ParseError!void {922pub fn parsePositional(self: *MachO, path: []const u8, must_link: bool) ParseError!void {
923 const tracy = trace(@src());923 const tracy = trace(@src());
...@@ -956,7 +956,7 @@ fn parseObject(self: *MachO, path: []const u8) ParseError!void {...@@ -956,7 +956,7 @@ fn parseObject(self: *MachO, path: []const u8) ParseError!void {
956 defer tracy.end();956 defer tracy.end();
957957
958 const gpa = self.base.comp.gpa;958 const gpa = self.base.comp.gpa;
959 const file = try std.fs.cwd().openFile(path, .{});959 const file = try fs.cwd().openFile(path, .{});
960 const handle = try self.addFileHandle(file);960 const handle = try self.addFileHandle(file);
961 const mtime: u64 = mtime: {961 const mtime: u64 = mtime: {
962 const stat = file.stat() catch break :mtime 0;962 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...@@ -992,7 +992,7 @@ fn parseArchive(self: *MachO, lib: SystemLib, must_link: bool, fat_arch: ?fat.Ar
992992
993 const gpa = self.base.comp.gpa;993 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, .{});
996 const handle = try self.addFileHandle(file);996 const handle = try self.addFileHandle(file);
997997
998 var archive = Archive{};998 var archive = Archive{};
...@@ -1029,7 +1029,7 @@ fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch)...@@ -1029,7 +1029,7 @@ fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch)
10291029
1030 const gpa = self.base.comp.gpa;1030 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, .{});
1033 defer file.close();1033 defer file.close();
10341034
1035 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));1035 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...@@ -1054,7 +1054,7 @@ fn parseTbd(self: *MachO, lib: SystemLib, explicit: bool) ParseError!File.Index
1054 defer tracy.end();1054 defer tracy.end();
10551055
1056 const gpa = self.base.comp.gpa;1056 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, .{});
1058 defer file.close();1058 defer file.close();
10591059
1060 var lib_stub = LibStub.loadFromFile(gpa, file) catch return error.MalformedTbd; // TODO actually handle different errors1060 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...@@ -1080,10 +1080,10 @@ fn parseTbd(self: *MachO, lib: SystemLib, explicit: bool) ParseError!File.Index
1080/// image unless overriden by -no_implicit_dylibs.1080/// image unless overriden by -no_implicit_dylibs.
1081fn isHoisted(self: *MachO, install_name: []const u8) bool {1081fn isHoisted(self: *MachO, install_name: []const u8) bool {
1082 if (self.no_implicit_dylibs) return true;1082 if (self.no_implicit_dylibs) return true;
1083 if (std.fs.path.dirname(install_name)) |dirname| {1083 if (fs.path.dirname(install_name)) |dirname| {
1084 if (mem.startsWith(u8, dirname, "/usr/lib")) return true;1084 if (mem.startsWith(u8, dirname, "/usr/lib")) return true;
1085 if (eatPrefix(dirname, "/System/Library/Frameworks/")) |path| {1085 if (eatPrefix(dirname, "/System/Library/Frameworks/")) |path| {
1086 const basename = std.fs.path.basename(install_name);1086 const basename = fs.path.basename(install_name);
1087 if (mem.indexOfScalar(u8, path, '.')) |index| {1087 if (mem.indexOfScalar(u8, path, '.')) |index| {
1088 if (mem.eql(u8, basename, path[0..index])) return true;1088 if (mem.eql(u8, basename, path[0..index])) return true;
1089 }1089 }
...@@ -1105,7 +1105,7 @@ fn accessLibPath(...@@ -1105,7 +1105,7 @@ fn accessLibPath(
1105 test_path.clearRetainingCapacity();1105 test_path.clearRetainingCapacity();
1106 try test_path.writer().print("{s}" ++ sep ++ "lib{s}{s}", .{ search_dir, name, ext });1106 try test_path.writer().print("{s}" ++ sep ++ "lib{s}{s}", .{ search_dir, name, ext });
1107 try checked_paths.append(try arena.dupe(u8, test_path.items));1107 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) {
1109 error.FileNotFound => continue,1109 error.FileNotFound => continue,
1110 else => |e| return e,1110 else => |e| return e,
1111 };1111 };
...@@ -1133,7 +1133,7 @@ fn accessFrameworkPath(...@@ -1133,7 +1133,7 @@ fn accessFrameworkPath(
1133 ext,1133 ext,
1134 });1134 });
1135 try checked_paths.append(try arena.dupe(u8, test_path.items));1135 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) {
1137 error.FileNotFound => continue,1137 error.FileNotFound => continue,
1138 else => |e| return e,1138 else => |e| return e,
1139 };1139 };
...@@ -1181,7 +1181,7 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1181,7 +1181,7 @@ fn parseDependentDylibs(self: *MachO) !void {
11811181
1182 const full_path = full_path: {1182 const full_path = full_path: {
1183 {1183 {
1184 const stem = std.fs.path.stem(id.name);1184 const stem = fs.path.stem(id.name);
11851185
1186 // Framework1186 // Framework
1187 for (framework_dirs) |dir| {1187 for (framework_dirs) |dir| {
...@@ -1197,18 +1197,18 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1197,18 +1197,18 @@ fn parseDependentDylibs(self: *MachO) !void {
1197 }1197 }
1198 }1198 }
11991199
1200 if (std.fs.path.isAbsolute(id.name)) {1200 if (fs.path.isAbsolute(id.name)) {
1201 const existing_ext = std.fs.path.extension(id.name);1201 const existing_ext = fs.path.extension(id.name);
1202 const path = if (existing_ext.len > 0) id.name[0 .. id.name.len - existing_ext.len] else id.name;1202 const path = if (existing_ext.len > 0) id.name[0 .. id.name.len - existing_ext.len] else id.name;
1203 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {1203 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
1204 test_path.clearRetainingCapacity();1204 test_path.clearRetainingCapacity();
1205 if (self.base.comp.sysroot) |root| {1205 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 });
1207 } else {1207 } else {
1208 try test_path.writer().print("{s}{s}", .{ path, ext });1208 try test_path.writer().print("{s}{s}", .{ path, ext });
1209 }1209 }
1210 try checked_paths.append(try arena.dupe(u8, test_path.items));1210 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) {
1212 error.FileNotFound => continue,1212 error.FileNotFound => continue,
1213 else => |e| return e,1213 else => |e| return e,
1214 };1214 };
...@@ -1220,10 +1220,10 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1220,10 +1220,10 @@ fn parseDependentDylibs(self: *MachO) !void {
1220 const dylib = self.getFile(dylib_index).?.dylib;1220 const dylib = self.getFile(dylib_index).?.dylib;
1221 for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| {1221 for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| {
1222 const prefix = eatPrefix(rpath, "@loader_path/") orelse rpath;1222 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 });
1224 try checked_paths.append(rel_path);1224 try checked_paths.append(rel_path);
1225 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;1225 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
1226 const full_path = std.fs.realpath(rel_path, &buffer) catch continue;1226 const full_path = fs.realpath(rel_path, &buffer) catch continue;
1227 break :full_path try arena.dupe(u8, full_path);1227 break :full_path try arena.dupe(u8, full_path);
1228 }1228 }
1229 } else if (eatPrefix(id.name, "@loader_path/")) |_| {1229 } else if (eatPrefix(id.name, "@loader_path/")) |_| {
...@@ -1235,8 +1235,8 @@ fn parseDependentDylibs(self: *MachO) !void {...@@ -1235,8 +1235,8 @@ fn parseDependentDylibs(self: *MachO) !void {
1235 }1235 }
12361236
1237 try checked_paths.append(try arena.dupe(u8, id.name));1237 try checked_paths.append(try arena.dupe(u8, id.name));
1238 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;1238 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
1239 if (std.fs.realpath(id.name, &buffer)) |full_path| {1239 if (fs.realpath(id.name, &buffer)) |full_path| {
1240 break :full_path try arena.dupe(u8, full_path);1240 break :full_path try arena.dupe(u8, full_path);
1241 } else |_| {1241 } else |_| {
1242 try self.reportMissingDependencyError(1242 try self.reportMissingDependencyError(
...@@ -3651,7 +3651,7 @@ pub fn getTarget(self: MachO) std.Target {...@@ -3651,7 +3651,7 @@ pub fn getTarget(self: MachO) std.Target {
3651/// into a new inode, remove the original file, and rename the copy to match3651/// into a new inode, remove the original file, and rename the copy to match
3652/// the original file. This is super messy, but there doesn't seem any other3652/// the original file. This is super messy, but there doesn't seem any other
3653/// way to please the XNU.3653/// 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 {
3655 if (comptime builtin.target.isDarwin() and builtin.target.cpu.arch == .aarch64) {3655 if (comptime builtin.target.isDarwin() and builtin.target.cpu.arch == .aarch64) {
3656 try dir.copyFile(sub_path, dir, sub_path, .{});3656 try dir.copyFile(sub_path, dir, sub_path, .{});
3657 }3657 }
...@@ -3839,7 +3839,7 @@ pub fn getInternalObject(self: *MachO) ?*InternalObject {...@@ -3839,7 +3839,7 @@ pub fn getInternalObject(self: *MachO) ?*InternalObject {
3839 return self.getFile(index).?.internal;3839 return self.getFile(index).?.internal;
3840}3840}
38413841
3842pub fn addFileHandle(self: *MachO, file: std.fs.File) !File.HandleIndex {3842pub fn addFileHandle(self: *MachO, file: fs.File) !File.HandleIndex {
3843 const gpa = self.base.comp.gpa;3843 const gpa = self.base.comp.gpa;
3844 const index: File.HandleIndex = @intCast(self.file_handles.items.len);3844 const index: File.HandleIndex = @intCast(self.file_handles.items.len);
3845 const fh = try self.file_handles.addOne(gpa);3845 const fh = try self.file_handles.addOne(gpa);
...@@ -4136,10 +4136,10 @@ pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols {...@@ -4136,10 +4136,10 @@ pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols {
4136 return null;4136 return null;
4137}4137}
41384138
4139pub fn ptraceAttach(self: *MachO, pid: std.os.pid_t) !void {4139pub fn ptraceAttach(self: *MachO, pid: std.posix.pid_t) !void {
4140 if (!is_hot_update_compatible) return;4140 if (!is_hot_update_compatible) return;
41414141
4142 const mach_task = try std.os.darwin.machTaskForPid(pid);4142 const mach_task = try std.c.machTaskForPid(pid);
4143 log.debug("Mach task for pid {d}: {any}", .{ pid, mach_task });4143 log.debug("Mach task for pid {d}: {any}", .{ pid, mach_task });
4144 self.hot_state.mach_task = mach_task;4144 self.hot_state.mach_task = mach_task;
41454145
...@@ -4149,7 +4149,7 @@ pub fn ptraceAttach(self: *MachO, pid: std.os.pid_t) !void {...@@ -4149,7 +4149,7 @@ pub fn ptraceAttach(self: *MachO, pid: std.os.pid_t) !void {
4149 // try std.os.ptrace(std.os.darwin.PT.ATTACHEXC, pid, 0, 0);4149 // try std.os.ptrace(std.os.darwin.PT.ATTACHEXC, pid, 0, 0);
4150}4150}
41514151
4152pub fn ptraceDetach(self: *MachO, pid: std.os.pid_t) !void {4152pub fn ptraceDetach(self: *MachO, pid: std.posix.pid_t) !void {
4153 if (!is_hot_update_compatible) return;4153 if (!is_hot_update_compatible) return;
41544154
4155 _ = pid;4155 _ = pid;
...@@ -4330,7 +4330,7 @@ const Section = struct {...@@ -4330,7 +4330,7 @@ const Section = struct {
4330};4330};
43314331
4332const HotUpdateState = struct {4332const HotUpdateState = struct {
4333 mach_task: ?std.os.darwin.MachTask = null,4333 mach_task: ?std.c.MachTask = null,
4334};4334};
43354335
4336pub const DynamicRelocs = struct {4336pub const DynamicRelocs = struct {
...@@ -4530,7 +4530,7 @@ fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersi...@@ -4530,7 +4530,7 @@ fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersi
45304530
4531 const sdk_dir = switch (sdk_layout) {4531 const sdk_dir = switch (sdk_layout) {
4532 .sdk => comp.sysroot.?,4532 .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,
4534 };4534 };
4535 if (readSdkVersionFromSettings(arena, sdk_dir)) |ver| {4535 if (readSdkVersionFromSettings(arena, sdk_dir)) |ver| {
4536 return parseSdkVersion(ver);4536 return parseSdkVersion(ver);
...@@ -4541,7 +4541,7 @@ fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersi...@@ -4541,7 +4541,7 @@ fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersi
4541 }4541 }
45424542
4543 // infer from pathname4543 // infer from pathname
4544 const stem = std.fs.path.stem(sdk_dir);4544 const stem = fs.path.stem(sdk_dir);
4545 const start = for (stem, 0..) |c, i| {4545 const start = for (stem, 0..) |c, i| {
4546 if (std.ascii.isDigit(c)) break i;4546 if (std.ascii.isDigit(c)) break i;
4547 } else stem.len;4547 } else stem.len;
...@@ -4556,8 +4556,8 @@ fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersi...@@ -4556,8 +4556,8 @@ fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersi
4556// Use property `MinimalDisplayName` to determine version.4556// Use property `MinimalDisplayName` to determine version.
4557// The file/property is also available with vendored libc.4557// The file/property is also available with vendored libc.
4558fn readSdkVersionFromSettings(arena: Allocator, dir: []const u8) ![]const u8 {4558fn readSdkVersionFromSettings(arena: Allocator, dir: []const u8) ![]const u8 {
4559 const sdk_path = try std.fs.path.join(arena, &.{ dir, "SDKSettings.json" });4559 const sdk_path = try fs.path.join(arena, &.{ dir, "SDKSettings.json" });
4560 const contents = try std.fs.cwd().readFileAlloc(arena, sdk_path, std.math.maxInt(u16));4560 const contents = try fs.cwd().readFileAlloc(arena, sdk_path, std.math.maxInt(u16));
4561 const parsed = try std.json.parseFromSlice(std.json.Value, arena, contents, .{});4561 const parsed = try std.json.parseFromSlice(std.json.Value, arena, contents, .{});
4562 if (parsed.value.object.get("MinimalDisplayName")) |ver| return ver.string;4562 if (parsed.value.object.get("MinimalDisplayName")) |ver| return ver.string;
4563 return error.SdkVersionFailure;4563 return error.SdkVersionFailure;
src/link/Plan9.zig+2-2
...@@ -368,7 +368,7 @@ fn putFn(self: *Plan9, decl_index: InternPool.DeclIndex, out: FnDeclOutput) !voi...@@ -368,7 +368,7 @@ fn putFn(self: *Plan9, decl_index: InternPool.DeclIndex, out: FnDeclOutput) !voi
368 // getting the full file path368 // getting the full file path
369 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;369 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
370 const full_path = try std.fs.path.join(arena, &.{370 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),
372 file.mod.root.sub_path,372 file.mod.root.sub_path,
373 file.sub_file_path,373 file.sub_file_path,
374 });374 });
...@@ -722,7 +722,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, prog_node: *std.Progress.Node...@@ -722,7 +722,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, prog_node: *std.Progress.Node
722 defer gpa.free(got_table);722 defer gpa.free(got_table);
723723
724 // + 4 for header, got, symbols, linecountinfo724 // + 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());
726 defer gpa.free(iovecs);726 defer gpa.free(iovecs);
727727
728 const file = self.base.file.?;728 const file = self.base.file.?;
src/link/Wasm.zig+2-2
...@@ -3046,7 +3046,7 @@ fn writeToFile(...@@ -3046,7 +3046,7 @@ fn writeToFile(
3046 }3046 }
30473047
3048 // finally, write the entire binary into the file.3048 // finally, write the entire binary into the file.
3049 var iovec = [_]std.os.iovec_const{.{3049 var iovec = [_]std.posix.iovec_const{.{
3050 .iov_base = binary_bytes.items.ptr,3050 .iov_base = binary_bytes.items.ptr,
3051 .iov_len = binary_bytes.items.len,3051 .iov_len = binary_bytes.items.len,
3052 }};3052 }};
...@@ -3709,7 +3709,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) !vo...@@ -3709,7 +3709,7 @@ fn linkWithLLD(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) !vo
3709 // report a nice error here with the file path if it fails instead of3709 // report a nice error here with the file path if it fails instead of
3710 // just returning the error code.3710 // just returning the error code.
3711 // chmod does not interact with umask, so we use a conservative -rwxr--r-- here.3711 // 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) {
3713 error.OperationNotSupported => unreachable, // Not a symlink.3713 error.OperationNotSupported => unreachable, // Not a symlink.
3714 else => |e| return e,3714 else => |e| return e,
3715 };3715 };
src/link/Wasm/Archive.zig+1-1
...@@ -193,7 +193,7 @@ pub fn parseObject(archive: Archive, wasm_file: *const Wasm, file_offset: u32) !...@@ -193,7 +193,7 @@ pub fn parseObject(archive: Archive, wasm_file: *const Wasm, file_offset: u32) !
193 const object_name = try archive.parseName(header);193 const object_name = try archive.parseName(header);
194 const name = name: {194 const name = name: {
195 var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;195 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);
197 break :name try std.fmt.allocPrint(gpa, "{s}({s})", .{ path, object_name });197 break :name try std.fmt.allocPrint(gpa, "{s}({s})", .{ path, object_name });
198 };198 };
199 defer gpa.free(name);199 defer gpa.free(name);
src/main.zig+31-26
...@@ -12,6 +12,7 @@ const Color = std.zig.Color;...@@ -12,6 +12,7 @@ const Color = std.zig.Color;
12const warn = std.log.warn;12const warn = std.log.warn;
13const ThreadPool = std.Thread.Pool;13const ThreadPool = std.Thread.Pool;
14const cleanExit = std.process.cleanExit;14const cleanExit = std.process.cleanExit;
15const native_os = builtin.os.tag;
1516
16const tracy = @import("tracy.zig");17const tracy = @import("tracy.zig");
17const Compilation = @import("Compilation.zig");18const Compilation = @import("Compilation.zig");
...@@ -48,7 +49,7 @@ pub const panic = crash_report.panic;...@@ -48,7 +49,7 @@ pub const panic = crash_report.panic;
48var wasi_preopens: fs.wasi.Preopens = undefined;49var wasi_preopens: fs.wasi.Preopens = undefined;
49pub fn wasi_cwd() std.os.wasi.fd_t {50pub fn wasi_cwd() std.os.wasi.fd_t {
50 // Expect the first preopen to be current working directory.51 // Expect the first preopen to be current working directory.
51 const cwd_fd: std.os.fd_t = 3;52 const cwd_fd: std.posix.fd_t = 3;
52 assert(mem.eql(u8, wasi_preopens.names[cwd_fd], "."));53 assert(mem.eql(u8, wasi_preopens.names[cwd_fd], "."));
53 return cwd_fd;54 return cwd_fd;
54}55}
...@@ -158,9 +159,9 @@ var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{...@@ -158,9 +159,9 @@ var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{
158pub fn main() anyerror!void {159pub fn main() anyerror!void {
159 crash_report.initialize();160 crash_report.initialize();
160161
161 const use_gpa = (build_options.force_gpa or !builtin.link_libc) and builtin.os.tag != .wasi;162 const use_gpa = (build_options.force_gpa or !builtin.link_libc) and native_os != .wasi;
162 const gpa = gpa: {163 const gpa = gpa: {
163 if (builtin.os.tag == .wasi) {164 if (native_os == .wasi) {
164 break :gpa std.heap.wasm_allocator;165 break :gpa std.heap.wasm_allocator;
165 }166 }
166 if (use_gpa) {167 if (use_gpa) {
...@@ -187,7 +188,7 @@ pub fn main() anyerror!void {...@@ -187,7 +188,7 @@ pub fn main() anyerror!void {
187 return mainArgs(gpa_tracy.allocator(), arena, args);188 return mainArgs(gpa_tracy.allocator(), arena, args);
188 }189 }
189190
190 if (builtin.os.tag == .wasi) {191 if (native_os == .wasi) {
191 wasi_preopens = try fs.wasi.preopensAlloc(arena);192 wasi_preopens = try fs.wasi.preopensAlloc(arena);
192 }193 }
193194
...@@ -222,7 +223,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -222,7 +223,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
222 fatal("expected command argument", .{});223 fatal("expected command argument", .{});
223 }224 }
224225
225 if (process.can_execv and std.os.getenvZ("ZIG_IS_DETECTING_LIBC_PATHS") != null) {226 if (process.can_execv and std.posix.getenvZ("ZIG_IS_DETECTING_LIBC_PATHS") != null) {
226 // In this case we have accidentally invoked ourselves as "the system C compiler"227 // In this case we have accidentally invoked ourselves as "the system C compiler"
227 // to figure out where libc is installed. This is essentially infinite recursion228 // to figure out where libc is installed. This is essentially infinite recursion
228 // via child process execution due to the CC environment variable pointing to Zig.229 // via child process execution due to the CC environment variable pointing to Zig.
...@@ -813,9 +814,9 @@ fn buildOutputType(...@@ -813,9 +814,9 @@ fn buildOutputType(
813 var no_builtin = false;814 var no_builtin = false;
814 var listen: Listen = .none;815 var listen: Listen = .none;
815 var debug_compile_errors = false;816 var debug_compile_errors = false;
816 var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and817 var verbose_link = (native_os != .wasi or builtin.link_libc) and
817 EnvVar.ZIG_VERBOSE_LINK.isSet();818 EnvVar.ZIG_VERBOSE_LINK.isSet();
818 var verbose_cc = (builtin.os.tag != .wasi or builtin.link_libc) and819 var verbose_cc = (native_os != .wasi or builtin.link_libc) and
819 EnvVar.ZIG_VERBOSE_CC.isSet();820 EnvVar.ZIG_VERBOSE_CC.isSet();
820 var verbose_air = false;821 var verbose_air = false;
821 var verbose_intern_pool = false;822 var verbose_intern_pool = false;
...@@ -991,7 +992,7 @@ fn buildOutputType(...@@ -991,7 +992,7 @@ fn buildOutputType(
991 // if it exists, default the color setting to .off992 // if it exists, default the color setting to .off
992 // explicit --color arguments will still override this setting.993 // explicit --color arguments will still override this setting.
993 // Disable color on WASI per https://github.com/WebAssembly/WASI/issues/162994 // Disable color on WASI per https://github.com/WebAssembly/WASI/issues/162
994 var color: Color = if (builtin.os.tag == .wasi or EnvVar.NO_COLOR.isSet()) .off else .auto;995 var color: Color = if (native_os == .wasi or EnvVar.NO_COLOR.isSet()) .off else .auto;
995996
996 switch (arg_mode) {997 switch (arg_mode) {
997 .build, .translate_c, .zig_test, .run => {998 .build, .translate_c, .zig_test, .run => {
...@@ -2684,7 +2685,7 @@ fn buildOutputType(...@@ -2684,7 +2685,7 @@ fn buildOutputType(
2684 fatal("unable to open zig lib directory '{s}': {s}", .{ lib_dir, @errorName(err) });2685 fatal("unable to open zig lib directory '{s}': {s}", .{ lib_dir, @errorName(err) });
2685 },2686 },
2686 };2687 };
2687 } else if (builtin.os.tag == .wasi) {2688 } else if (native_os == .wasi) {
2688 break :d getWasiPreopen("/lib");2689 break :d getWasiPreopen("/lib");
2689 } else if (self_exe_path) |p| {2690 } else if (self_exe_path) |p| {
2690 break :d introspect.findZigLibDirFromSelfExe(arena, p) catch |err| {2691 break :d introspect.findZigLibDirFromSelfExe(arena, p) catch |err| {
...@@ -2703,7 +2704,7 @@ fn buildOutputType(...@@ -2703,7 +2704,7 @@ fn buildOutputType(
2703 .path = p,2704 .path = p,
2704 };2705 };
2705 }2706 }
2706 if (builtin.os.tag == .wasi) {2707 if (native_os == .wasi) {
2707 break :l getWasiPreopen("/cache");2708 break :l getWasiPreopen("/cache");
2708 }2709 }
2709 const p = try introspect.resolveGlobalCacheDir(arena);2710 const p = try introspect.resolveGlobalCacheDir(arena);
...@@ -4368,7 +4369,7 @@ fn runOrTest(...@@ -4368,7 +4369,7 @@ fn runOrTest(
4368 }4369 }
4369 } else {4370 } else {
4370 const cmd = try std.mem.join(arena, " ", argv.items);4371 const cmd = try std.mem.join(arena, " ", argv.items);
4371 fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });4372 fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(native_os), cmd });
4372 }4373 }
4373}4374}
43744375
...@@ -4434,16 +4435,16 @@ fn runOrTestHotSwap(...@@ -4434,16 +4435,16 @@ fn runOrTestHotSwap(
44344435
4435 switch (builtin.target.os.tag) {4436 switch (builtin.target.os.tag) {
4436 .macos, .ios, .tvos, .watchos => {4437 .macos, .ios, .tvos, .watchos => {
4437 const PosixSpawn = std.os.darwin.PosixSpawn;4438 const PosixSpawn = @import("DarwinPosixSpawn.zig");
44384439
4439 var attr = try PosixSpawn.Attr.init();4440 var attr = try PosixSpawn.Attr.init();
4440 defer attr.deinit();4441 defer attr.deinit();
44414442
4442 // ASLR is probably a good default for better debugging experience/programming4443 // ASLR is probably a good default for better debugging experience/programming
4443 // with hot-code updates in mind. However, we can also make it work with ASLR on.4444 // 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 const flags: u16 = std.c.POSIX_SPAWN.SETSIGDEF |
4445 std.os.darwin.POSIX_SPAWN.SETSIGMASK |4446 std.c.POSIX_SPAWN.SETSIGMASK |
4446 std.os.darwin.POSIX_SPAWN.DISABLE_ASLR;4447 std.c.POSIX_SPAWN.DISABLE_ASLR;
4447 try attr.set(flags);4448 try attr.set(flags);
44484449
4449 var arena_allocator = std.heap.ArenaAllocator.init(gpa);4450 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
...@@ -4747,9 +4748,9 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -4747,9 +4748,9 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
4747 var child_argv = std.ArrayList([]const u8).init(arena);4748 var child_argv = std.ArrayList([]const u8).init(arena);
4748 var reference_trace: ?u32 = null;4749 var reference_trace: ?u32 = null;
4749 var debug_compile_errors = false;4750 var debug_compile_errors = false;
4750 var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and4751 var verbose_link = (native_os != .wasi or builtin.link_libc) and
4751 EnvVar.ZIG_VERBOSE_LINK.isSet();4752 EnvVar.ZIG_VERBOSE_LINK.isSet();
4752 var verbose_cc = (builtin.os.tag != .wasi or builtin.link_libc) and4753 var verbose_cc = (native_os != .wasi or builtin.link_libc) and
4753 EnvVar.ZIG_VERBOSE_CC.isSet();4754 EnvVar.ZIG_VERBOSE_CC.isSet();
4754 var verbose_air = false;4755 var verbose_air = false;
4755 var verbose_intern_pool = false;4756 var verbose_intern_pool = false;
...@@ -4894,7 +4895,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -4894,7 +4895,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
4894 }4895 }
4895 }4896 }
48964897
4897 const work_around_btrfs_bug = builtin.os.tag == .linux and4898 const work_around_btrfs_bug = native_os == .linux and
4898 EnvVar.ZIG_BTRFS_WORKAROUND.isSet();4899 EnvVar.ZIG_BTRFS_WORKAROUND.isSet();
4899 const color: Color = .auto;4900 const color: Color = .auto;
49004901
...@@ -5311,7 +5312,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5311,7 +5312,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5311 }5312 }
5312 } else {5313 } else {
5313 const cmd = try std.mem.join(arena, " ", child_argv.items);5314 const cmd = try std.mem.join(arena, " ", child_argv.items);
5314 fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });5315 fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(native_os), cmd });
5315 }5316 }
5316 }5317 }
5317}5318}
...@@ -5543,7 +5544,7 @@ fn jitCmd(...@@ -5543,7 +5544,7 @@ fn jitCmd(
5543 if (!process.can_spawn) {5544 if (!process.can_spawn) {
5544 const cmd = try std.mem.join(arena, " ", child_argv.items);5545 const cmd = try std.mem.join(arena, " ", child_argv.items);
5545 fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{5546 fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{
5546 @tagName(builtin.os.tag), cmd,5547 @tagName(native_os), cmd,
5547 });5548 });
5548 }5549 }
55495550
...@@ -5655,7 +5656,7 @@ pub fn lldMain(...@@ -5655,7 +5656,7 @@ pub fn lldMain(
5655 var count: usize = 0;5656 var count: usize = 0;
5656 };5657 };
5657 if (CallCounter.count == 1) { // Issue the warning on the first repeat call5658 if (CallCounter.count == 1) { // Issue the warning on the first repeat call
5658 warn("invoking LLD for the second time within the same process because the host OS ({s}) does not support spawning child processes. This sometimes activates LLD bugs", .{@tagName(builtin.os.tag)});5659 warn("invoking LLD for the second time within the same process because the host OS ({s}) does not support spawning child processes. This sometimes activates LLD bugs", .{@tagName(native_os)});
5659 }5660 }
5660 CallCounter.count += 1;5661 CallCounter.count += 1;
56615662
...@@ -5985,17 +5986,21 @@ fn parseCodeModel(arg: []const u8) std.builtin.CodeModel {...@@ -5985,17 +5986,21 @@ fn parseCodeModel(arg: []const u8) std.builtin.CodeModel {
5985/// garbage collector to run concurrently to zig processes, and to allow multiple5986/// garbage collector to run concurrently to zig processes, and to allow multiple
5986/// zig processes to run concurrently with each other, without clobbering each other.5987/// zig processes to run concurrently with each other, without clobbering each other.
5987fn gimmeMoreOfThoseSweetSweetFileDescriptors() void {5988fn gimmeMoreOfThoseSweetSweetFileDescriptors() void {
5988 if (!@hasDecl(std.os.system, "rlimit")) return;5989 const have_rlimit = switch (native_os) {
5989 const posix = std.os;5990 .windows, .wasi => false,
5991 else => true,
5992 };
5993 if (!have_rlimit) return;
5994 const posix = std.posix;
59905995
5991 var lim = posix.getrlimit(.NOFILE) catch return; // Oh well; we tried.5996 var lim = posix.getrlimit(.NOFILE) catch return; // Oh well; we tried.
5992 if (comptime builtin.target.isDarwin()) {5997 if (native_os.isDarwin()) {
5993 // On Darwin, `NOFILE` is bounded by a hardcoded value `OPEN_MAX`.5998 // On Darwin, `NOFILE` is bounded by a hardcoded value `OPEN_MAX`.
5994 // According to the man pages for setrlimit():5999 // According to the man pages for setrlimit():
5995 // setrlimit() now returns with errno set to EINVAL in places that historically succeeded.6000 // setrlimit() now returns with errno set to EINVAL in places that historically succeeded.
5996 // It no longer accepts "rlim_cur = RLIM.INFINITY" for RLIM.NOFILE.6001 // It no longer accepts "rlim_cur = RLIM.INFINITY" for RLIM.NOFILE.
5997 // Use "rlim_cur = min(OPEN_MAX, rlim_max)".6002 // Use "rlim_cur = min(OPEN_MAX, rlim_max)".
5998 lim.max = @min(std.os.darwin.OPEN_MAX, lim.max);6003 lim.max = @min(std.c.OPEN_MAX, lim.max);
5999 }6004 }
6000 if (lim.cur == lim.max) return;6005 if (lim.cur == lim.max) return;
60016006
...@@ -6766,7 +6771,7 @@ fn cmdFetch(...@@ -6766,7 +6771,7 @@ fn cmdFetch(
6766 args: []const []const u8,6771 args: []const []const u8,
6767) !void {6772) !void {
6768 const color: Color = .auto;6773 const color: Color = .auto;
6769 const work_around_btrfs_bug = builtin.os.tag == .linux and6774 const work_around_btrfs_bug = native_os == .linux and
6770 EnvVar.ZIG_BTRFS_WORKAROUND.isSet();6775 EnvVar.ZIG_BTRFS_WORKAROUND.isSet();
6771 var opt_path_or_url: ?[]const u8 = null;6776 var opt_path_or_url: ?[]const u8 = null;
6772 var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);6777 var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
test/cases/arithmetic_operations.0.zig+1-1
...@@ -8,7 +8,7 @@ pub fn main() void {...@@ -8,7 +8,7 @@ pub fn main() void {
8fn print(a: u32, b: u32) void {8fn print(a: u32, b: u32) void {
9 const str = "123456789";9 const str = "123456789";
10 const len = a + b;10 const len = a + b;
11 _ = std.os.write(1, str[0..len]) catch {};11 _ = std.posix.write(1, str[0..len]) catch {};
12}12}
1313
14// run14// run
test/cases/arithmetic_operations.1.zig+1-1
...@@ -8,7 +8,7 @@ pub fn main() void {...@@ -8,7 +8,7 @@ pub fn main() void {
8fn print(a: u32, b: u32) void {8fn print(a: u32, b: u32) void {
9 const str = "123456789";9 const str = "123456789";
10 const len = a - b;10 const len = a - b;
11 _ = std.os.write(1, str[0..len]) catch {};11 _ = std.posix.write(1, str[0..len]) catch {};
12}12}
1313
14// run14// run
test/cases/arithmetic_operations.2.zig+1-1
...@@ -8,7 +8,7 @@ pub fn main() void {...@@ -8,7 +8,7 @@ pub fn main() void {
8fn print(a: u32, b: u32) void {8fn print(a: u32, b: u32) void {
9 const str = "123456789";9 const str = "123456789";
10 const len = a & b;10 const len = a & b;
11 _ = std.os.write(1, str[0..len]) catch {};11 _ = std.posix.write(1, str[0..len]) catch {};
12}12}
1313
14// run14// run
test/cases/arithmetic_operations.3.zig+1-1
...@@ -8,7 +8,7 @@ pub fn main() void {...@@ -8,7 +8,7 @@ pub fn main() void {
8fn print(a: u32, b: u32) void {8fn print(a: u32, b: u32) void {
9 const str = "123456789";9 const str = "123456789";
10 const len = a | b;10 const len = a | b;
11 _ = std.os.write(1, str[0..len]) catch {};11 _ = std.posix.write(1, str[0..len]) catch {};
12}12}
1313
14// run14// run
test/cases/arithmetic_operations.4.zig+1-1
...@@ -8,7 +8,7 @@ pub fn main() void {...@@ -8,7 +8,7 @@ pub fn main() void {
8fn print(a: u32, b: u32) void {8fn print(a: u32, b: u32) void {
9 const str = "123456789";9 const str = "123456789";
10 const len = a ^ b;10 const len = a ^ b;
11 _ = std.os.write(1, str[0..len]) catch {};11 _ = std.posix.write(1, str[0..len]) catch {};
12}12}
1313
14// run14// run
test/cases/errors.0.zig+1-1
...@@ -7,7 +7,7 @@ pub fn main() void {...@@ -7,7 +7,7 @@ pub fn main() void {
7fn foo() anyerror!void {}7fn foo() anyerror!void {}
88
9fn print() void {9fn print() void {
10 _ = std.os.write(1, "Hello, World!\n") catch {};10 _ = std.posix.write(1, "Hello, World!\n") catch {};
11}11}
1212
13// run13// run
test/cases/errors.1.zig+1-1
...@@ -9,7 +9,7 @@ fn foo() anyerror!void {...@@ -9,7 +9,7 @@ fn foo() anyerror!void {
9}9}
1010
11fn print() void {11fn print() void {
12 _ = std.os.write(1, "Hello, World!\n") catch {};12 _ = std.posix.write(1, "Hello, World!\n") catch {};
13}13}
1414
15// run15// run
test/cases/function_pointers.zig+2-2
...@@ -12,11 +12,11 @@ pub fn main() void {...@@ -12,11 +12,11 @@ pub fn main() void {
12}12}
1313
14fn stopSayingThat() void {14fn stopSayingThat() void {
15 _ = std.os.write(1, "Hello, my name is Inigo Montoya; you killed my father, prepare to die.\n") catch {};15 _ = std.posix.write(1, "Hello, my name is Inigo Montoya; you killed my father, prepare to die.\n") catch {};
16}16}
1717
18fn moveEveryZig() void {18fn moveEveryZig() void {
19 _ = std.os.write(1, "All your codebase are belong to us\n") catch {};19 _ = std.posix.write(1, "All your codebase are belong to us\n") catch {};
20}20}
2121
22// run22// run
test/cases/parameters_and_return_values.0.zig+1-1
...@@ -10,7 +10,7 @@ fn id(x: u32) u32 {...@@ -10,7 +10,7 @@ fn id(x: u32) u32 {
1010
11fn print(len: u32) void {11fn print(len: u32) void {
12 const str = "Hello, World!\n";12 const str = "Hello, World!\n";
13 _ = std.os.write(1, str[0..len]) catch {};13 _ = std.posix.write(1, str[0..len]) catch {};
14}14}
1515
16// run16// run
test/cases/print_u32s.zig+2-2
...@@ -12,10 +12,10 @@ fn printNumberHex(x: u32) void {...@@ -12,10 +12,10 @@ fn printNumberHex(x: u32) void {
12 var i: u5 = 28;12 var i: u5 = 28;
13 while (true) : (i -= 4) {13 while (true) : (i -= 4) {
14 const digit = (x >> i) & 0xf;14 const digit = (x >> i) & 0xf;
15 _ = std.os.write(1, &.{digit_chars[digit]}) catch {};15 _ = std.posix.write(1, &.{digit_chars[digit]}) catch {};
16 if (i == 0) break;16 if (i == 0) break;
17 }17 }
18 _ = std.os.write(1, "\n") catch {};18 _ = std.posix.write(1, "\n") catch {};
19}19}
2020
21// run21// run
test/cases/safety/resuming a non-suspended function which has been suspended and resumed.zig +3-3
...@@ -11,21 +11,21 @@ fn foo() void {...@@ -11,21 +11,21 @@ fn foo() void {
11 }11 }
12 var f = async bar(@frame());12 var f = async bar(@frame());
13 _ = &f;13 _ = &f;
14 std.os.exit(1);14 std.process.exit(1);
15}15}
1616
17fn bar(frame: anyframe) void {17fn bar(frame: anyframe) void {
18 suspend {18 suspend {
19 resume frame;19 resume frame;
20 }20 }
21 std.os.exit(1);21 std.process.exit(1);
22}22}
2323
24var global_frame: anyframe = undefined;24var global_frame: anyframe = undefined;
25pub fn main() !void {25pub fn main() !void {
26 _ = async foo();26 _ = async foo();
27 resume global_frame;27 resume global_frame;
28 std.os.exit(1);28 std.process.exit(1);
29}29}
30// run30// run
31// backend=stage131// backend=stage1
test/cases/safety/resuming a non-suspended function which never been suspended.zig +2-2
...@@ -8,14 +8,14 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usi...@@ -8,14 +8,14 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usi
8fn foo() void {8fn foo() void {
9 var f = async bar(@frame());9 var f = async bar(@frame());
10 _ = &f;10 _ = &f;
11 std.os.exit(1);11 std.process.exit(1);
12}12}
1313
14fn bar(frame: anyframe) void {14fn bar(frame: anyframe) void {
15 suspend {15 suspend {
16 resume frame;16 resume frame;
17 }17 }
18 std.os.exit(1);18 std.process.exit(1);
19}19}
2020
21pub fn main() !void {21pub fn main() !void {
test/link/glibc_compat/glibc_runtime_check.zig+2-2
...@@ -30,11 +30,11 @@ fn checkStat() !void {...@@ -30,11 +30,11 @@ fn checkStat() !void {
30 var stat = std.mem.zeroes(std.c.Stat);30 var stat = std.mem.zeroes(std.c.Stat);
31 var result = std.c.fstatat(cwdFd, "a_file_that_definitely_does_not_exist", &stat, 0);31 var result = std.c.fstatat(cwdFd, "a_file_that_definitely_does_not_exist", &stat, 0);
32 assert(result == -1);32 assert(result == -1);
33 assert(std.c.getErrno(result) == .NOENT);33 assert(std.posix.errno(result) == .NOENT);
3434
35 result = std.c.stat("a_file_that_definitely_does_not_exist", &stat);35 result = std.c.stat("a_file_that_definitely_does_not_exist", &stat);
36 assert(result == -1);36 assert(result == -1);
37 assert(std.c.getErrno(result) == .NOENT);37 assert(std.posix.errno(result) == .NOENT);
38}38}
3939
40// PR #17607 - reallocarray not visible in headers40// PR #17607 - reallocarray not visible in headers
test/standalone/sigpipe/breakpipe.zig+7-9
...@@ -1,19 +1,17 @@...@@ -1,19 +1,17 @@
1const std = @import("std");1const std = @import("std");
2const build_options = @import("build_options");2const build_options = @import("build_options");
33
4pub usingnamespace if (build_options.keep_sigpipe) struct {4pub const std_options = .{
5 pub const std_options = .{5 .keep_sigpipe = build_options.keep_sigpipe,
6 .keep_sigpipe = true,6};
7 };
8} else struct {};
97
10pub fn main() !void {8pub fn main() !void {
11 const pipe = try std.os.pipe();9 const pipe = try std.posix.pipe();
12 std.os.close(pipe[0]);10 std.posix.close(pipe[0]);
13 _ = std.os.write(pipe[1], "a") catch |err| switch (err) {11 _ = std.posix.write(pipe[1], "a") catch |err| switch (err) {
14 error.BrokenPipe => {12 error.BrokenPipe => {
15 try std.io.getStdOut().writer().writeAll("BrokenPipe\n");13 try std.io.getStdOut().writer().writeAll("BrokenPipe\n");
16 std.os.exit(123);14 std.posix.exit(123);
17 },15 },
18 else => |e| return e,16 else => |e| return e,
19 };17 };
test/standalone/sigpipe/build.zig+6-6
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const os = std.os;2const posix = std.posix;
33
4pub fn build(b: *std.build.Builder) !void {4pub fn build(b: *std.build.Builder) !void {
5 const test_step = b.step("test", "Test it");5 const test_step = b.step("test", "Test it");
...@@ -16,12 +16,12 @@ pub fn build(b: *std.build.Builder) !void {...@@ -16,12 +16,12 @@ pub fn build(b: *std.build.Builder) !void {
16 // This test runs "breakpipe" as a child process and that process16 // This test runs "breakpipe" as a child process and that process
17 // depends on inheriting a SIGPIPE disposition of "default".17 // depends on inheriting a SIGPIPE disposition of "default".
18 {18 {
19 const act = os.Sigaction{19 const act = posix.Sigaction{
20 .handler = .{ .handler = os.SIG.DFL },20 .handler = .{ .handler = posix.SIG.DFL },
21 .mask = os.empty_sigset,21 .mask = posix.empty_sigset,
22 .flags = 0,22 .flags = 0,
23 };23 };
24 try os.sigaction(os.SIG.PIPE, &act, null);24 try posix.sigaction(posix.SIG.PIPE, &act, null);
25 }25 }
2626
27 for ([_]bool{ false, true }) |keep_sigpipe| {27 for ([_]bool{ false, true }) |keep_sigpipe| {
...@@ -34,7 +34,7 @@ pub fn build(b: *std.build.Builder) !void {...@@ -34,7 +34,7 @@ pub fn build(b: *std.build.Builder) !void {
34 exe.addOptions("build_options", options);34 exe.addOptions("build_options", options);
35 const run = b.addRunArtifact(exe);35 const run = b.addRunArtifact(exe);
36 if (keep_sigpipe) {36 if (keep_sigpipe) {
37 run.addCheck(.{ .expect_term = .{ .Signal = std.os.SIG.PIPE } });37 run.addCheck(.{ .expect_term = .{ .Signal = std.posix.SIG.PIPE } });
38 } else {38 } else {
39 run.addCheck(.{ .expect_stdout_exact = "BrokenPipe\n" });39 run.addCheck(.{ .expect_stdout_exact = "BrokenPipe\n" });
40 run.addCheck(.{ .expect_term = .{ .Exited = 123 } });40 run.addCheck(.{ .expect_term = .{ .Exited = 123 } });
test/standalone/windows_spawn/main.zig+4-4
...@@ -25,13 +25,13 @@ pub fn main() anyerror!void {...@@ -25,13 +25,13 @@ pub fn main() anyerror!void {
25 defer allocator.free(tmp_relative_path);25 defer allocator.free(tmp_relative_path);
2626
27 // Clear PATH27 // Clear PATH
28 std.debug.assert(std.os.windows.kernel32.SetEnvironmentVariableW(28 std.debug.assert(windows.kernel32.SetEnvironmentVariableW(
29 utf16Literal("PATH"),29 utf16Literal("PATH"),
30 null,30 null,
31 ) == windows.TRUE);31 ) == windows.TRUE);
3232
33 // Set PATHEXT to something predictable33 // Set PATHEXT to something predictable
34 std.debug.assert(std.os.windows.kernel32.SetEnvironmentVariableW(34 std.debug.assert(windows.kernel32.SetEnvironmentVariableW(
35 utf16Literal("PATHEXT"),35 utf16Literal("PATHEXT"),
36 utf16Literal(".COM;.EXE;.BAT;.CMD;.JS"),36 utf16Literal(".COM;.EXE;.BAT;.CMD;.JS"),
37 ) == windows.TRUE);37 ) == windows.TRUE);
...@@ -39,7 +39,7 @@ pub fn main() anyerror!void {...@@ -39,7 +39,7 @@ pub fn main() anyerror!void {
39 // No PATH, so it should fail to find anything not in the cwd39 // No PATH, so it should fail to find anything not in the cwd
40 try testExecError(error.FileNotFound, allocator, "something_missing");40 try testExecError(error.FileNotFound, allocator, "something_missing");
4141
42 std.debug.assert(std.os.windows.kernel32.SetEnvironmentVariableW(42 std.debug.assert(windows.kernel32.SetEnvironmentVariableW(
43 utf16Literal("PATH"),43 utf16Literal("PATH"),
44 tmp_absolute_path_w,44 tmp_absolute_path_w,
45 ) == windows.TRUE);45 ) == windows.TRUE);
...@@ -120,7 +120,7 @@ pub fn main() anyerror!void {...@@ -120,7 +120,7 @@ pub fn main() anyerror!void {
120 const something_subdir_abs_path = try std.mem.concatWithSentinel(allocator, u16, &.{ tmp_absolute_path_w, utf16Literal("\\something") }, 0);120 const something_subdir_abs_path = try std.mem.concatWithSentinel(allocator, u16, &.{ tmp_absolute_path_w, utf16Literal("\\something") }, 0);
121 defer allocator.free(something_subdir_abs_path);121 defer allocator.free(something_subdir_abs_path);
122122
123 std.debug.assert(std.os.windows.kernel32.SetEnvironmentVariableW(123 std.debug.assert(windows.kernel32.SetEnvironmentVariableW(
124 utf16Literal("PATH"),124 utf16Literal("PATH"),
125 something_subdir_abs_path,125 something_subdir_abs_path,
126 ) == windows.TRUE);126 ) == windows.TRUE);