| author | |
| committer | |
| log | 3a836b4800337567735b2619075d8bb4e9f21c4d |
| tree | ab80d1a4f48e7401ad04dfb7855ad654e3761f63 |
| parent | 7057bffc14602add697eb566b83934b7ad3fd81c |
| parent | 8c94950c2496a5fa69f35b0f06f221e2c12339cf |
| signature |
extract std.posix from std.os115 files changed, 11531 insertions(+), 11594 deletions(-)
CMakeLists.txt-1| ... | ... | @@ -286,7 +286,6 @@ set(ZIG_STAGE2_SOURCES |
| 286 | 286 | "${CMAKE_SOURCE_DIR}/lib/std/multi_array_list.zig" |
| 287 | 287 | "${CMAKE_SOURCE_DIR}/lib/std/os.zig" |
| 288 | 288 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig" |
| 289 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux/errno/generic.zig" | |
| 290 | 289 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux/x86_64.zig" |
| 291 | 290 | "${CMAKE_SOURCE_DIR}/lib/std/os/linux.zig" |
| 292 | 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 | 882 | return path; |
| 883 | 883 | } else |_| { |
| 884 | 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 | } |
| 888 | 888 |
lib/compiler/objcopy.zig+1-1| ... | ... | @@ -1285,7 +1285,7 @@ const ElfFileHelper = struct { |
| 1285 | 1285 | for (consolidated.items) |cmd| { |
| 1286 | 1286 | switch (cmd) { |
| 1287 | 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 | 1289 | try out_file.pwritevAll(&iovec, data.out_offset); |
| 1290 | 1290 | }, |
| 1291 | 1291 | .copy_range => |range| { |
lib/compiler/resinator/main.zig+11-11| ... | ... | @@ -26,7 +26,7 @@ pub fn main() !void { |
| 26 | 26 | |
| 27 | 27 | if (args.len < 2) { |
| 28 | 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 | 31 | const zig_lib_dir = args[1]; |
| 32 | 32 | var cli_args = args[2..]; |
| ... | ... | @@ -62,7 +62,7 @@ pub fn main() !void { |
| 62 | 62 | var options = cli.parse(allocator, cli_args, &cli_diagnostics) catch |err| switch (err) { |
| 63 | 63 | error.ParseError => { |
| 64 | 64 | try error_handler.emitCliDiagnostics(allocator, cli_args, &cli_diagnostics); |
| 65 | std.os.exit(1); | |
| 65 | std.process.exit(1); | |
| 66 | 66 | }, |
| 67 | 67 | else => |e| return e, |
| 68 | 68 | }; |
| ... | ... | @@ -117,7 +117,7 @@ pub fn main() !void { |
| 117 | 117 | }, |
| 118 | 118 | } |
| 119 | 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 | }; |
| 123 | 123 | |
| ... | ... | @@ -153,16 +153,16 @@ pub fn main() !void { |
| 153 | 153 | preprocess.preprocess(&comp, preprocessed_buf.writer(), argv.items, maybe_dependencies_list) catch |err| switch (err) { |
| 154 | 154 | error.GeneratedSourceError => { |
| 155 | 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 | 158 | // ArgError can occur if e.g. the .rc file is not found |
| 159 | 159 | error.ArgError, error.PreprocessError => { |
| 160 | 160 | try error_handler.emitAroDiagnostics(allocator, "failed during preprocessing:", &comp); |
| 161 | std.os.exit(1); | |
| 161 | std.process.exit(1); | |
| 162 | 162 | }, |
| 163 | 163 | error.StreamTooLong => { |
| 164 | 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 | 167 | error.OutOfMemory => |e| return e, |
| 168 | 168 | }; |
| ... | ... | @@ -171,7 +171,7 @@ pub fn main() !void { |
| 171 | 171 | } else { |
| 172 | 172 | break :full_input std.fs.cwd().readFileAlloc(allocator, options.input_filename, std.math.maxInt(usize)) catch |err| { |
| 173 | 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 | 191 | const final_input = removeComments(mapping_results.result, mapping_results.result, &mapping_results.mappings) catch |err| switch (err) { |
| 192 | 192 | error.InvalidSourceMappingCollapse => { |
| 193 | 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 | 196 | else => |e| return e, |
| 197 | 197 | }; |
| 198 | 198 | |
| 199 | 199 | var output_file = std.fs.cwd().createFile(options.output_filename, .{}) catch |err| { |
| 200 | 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 | 203 | var output_file_closed = false; |
| 204 | 204 | defer if (!output_file_closed) output_file.close(); |
| ... | ... | @@ -231,7 +231,7 @@ pub fn main() !void { |
| 231 | 231 | output_file_closed = true; |
| 232 | 232 | // Failing to delete is not really a big deal, so swallow any errors |
| 233 | 233 | std.fs.cwd().deleteFile(options.output_filename) catch {}; |
| 234 | std.os.exit(1); | |
| 234 | std.process.exit(1); | |
| 235 | 235 | }, |
| 236 | 236 | else => |e| return e, |
| 237 | 237 | }; |
| ... | ... | @@ -247,7 +247,7 @@ pub fn main() !void { |
| 247 | 247 | if (options.depfile_path) |depfile_path| { |
| 248 | 248 | var depfile = std.fs.cwd().createFile(depfile_path, .{}) catch |err| { |
| 249 | 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 | 252 | defer depfile.close(); |
| 253 | 253 |
lib/std/Random/benchmark.zig+3-3| ... | ... | @@ -144,7 +144,7 @@ pub fn main() !void { |
| 144 | 144 | i += 1; |
| 145 | 145 | if (i == args.len) { |
| 146 | 146 | usage(); |
| 147 | std.os.exit(1); | |
| 147 | std.process.exit(1); | |
| 148 | 148 | } |
| 149 | 149 | |
| 150 | 150 | filter = args[i]; |
| ... | ... | @@ -152,7 +152,7 @@ pub fn main() !void { |
| 152 | 152 | i += 1; |
| 153 | 153 | if (i == args.len) { |
| 154 | 154 | usage(); |
| 155 | std.os.exit(1); | |
| 155 | std.process.exit(1); | |
| 156 | 156 | } |
| 157 | 157 | |
| 158 | 158 | const c = try std.fmt.parseUnsigned(usize, args[i], 10); |
| ... | ... | @@ -170,7 +170,7 @@ pub fn main() !void { |
| 170 | 170 | return; |
| 171 | 171 | } else { |
| 172 | 172 | usage(); |
| 173 | std.os.exit(1); | |
| 173 | std.process.exit(1); | |
| 174 | 174 | } |
| 175 | 175 | } |
| 176 | 176 |
lib/std/Thread.zig+72-72| ... | ... | @@ -5,9 +5,11 @@ |
| 5 | 5 | const std = @import("std.zig"); |
| 6 | 6 | const builtin = @import("builtin"); |
| 7 | 7 | const math = std.math; |
| 8 | const os = std.os; | |
| 9 | 8 | const assert = std.debug.assert; |
| 10 | 9 | const target = builtin.target; |
| 10 | const native_os = builtin.os.tag; | |
| 11 | const posix = std.posix; | |
| 12 | const windows = std.os.windows; | |
| 11 | 13 | |
| 12 | 14 | pub const Futex = @import("Thread/Futex.zig"); |
| 13 | 15 | pub const ResetEvent = @import("Thread/ResetEvent.zig"); |
| ... | ... | @@ -18,23 +20,23 @@ pub const RwLock = @import("Thread/RwLock.zig"); |
| 18 | 20 | pub const Pool = @import("Thread/Pool.zig"); |
| 19 | 21 | pub const WaitGroup = @import("Thread/WaitGroup.zig"); |
| 20 | 22 | |
| 21 | pub const use_pthreads = target.os.tag != .windows and target.os.tag != .wasi and builtin.link_libc; | |
| 23 | pub const use_pthreads = native_os != .windows and native_os != .wasi and builtin.link_libc; | |
| 22 | 24 | |
| 23 | 25 | const Thread = @This(); |
| 24 | const Impl = if (target.os.tag == .windows) | |
| 26 | const Impl = if (native_os == .windows) | |
| 25 | 27 | WindowsThreadImpl |
| 26 | 28 | else if (use_pthreads) |
| 27 | 29 | PosixThreadImpl |
| 28 | else if (target.os.tag == .linux) | |
| 30 | else if (native_os == .linux) | |
| 29 | 31 | LinuxThreadImpl |
| 30 | else if (target.os.tag == .wasi) | |
| 32 | else if (native_os == .wasi) | |
| 31 | 33 | WasiThreadImpl |
| 32 | 34 | else |
| 33 | 35 | UnsupportedImpl; |
| 34 | 36 | |
| 35 | 37 | impl: Impl, |
| 36 | 38 | |
| 37 | pub const max_name_len = switch (target.os.tag) { | |
| 39 | pub const max_name_len = switch (native_os) { | |
| 38 | 40 | .linux => 15, |
| 39 | 41 | .windows => 31, |
| 40 | 42 | .macos, .ios, .watchos, .tvos => 63, |
| ... | ... | @@ -50,7 +52,7 @@ pub const SetNameError = error{ |
| 50 | 52 | NameTooLong, |
| 51 | 53 | Unsupported, |
| 52 | 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; | |
| 54 | 56 | |
| 55 | 57 | pub fn setName(self: Thread, name: []const u8) SetNameError!void { |
| 56 | 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 | 64 | break :blk name_buf[0..name.len :0]; |
| 63 | 65 | }; |
| 64 | 66 | |
| 65 | switch (target.os.tag) { | |
| 67 | switch (native_os) { | |
| 66 | 68 | .linux => if (use_pthreads) { |
| 67 | 69 | if (self.getHandle() == std.c.pthread_self()) { |
| 68 | 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)}); | |
| 70 | switch (@as(os.E, @enumFromInt(err))) { | |
| 71 | const err = try posix.prctl(.SET_NAME, .{@intFromPtr(name_with_terminator.ptr)}); | |
| 72 | switch (@as(posix.E, @enumFromInt(err))) { | |
| 71 | 73 | .SUCCESS => return, |
| 72 | else => |e| return os.unexpectedErrno(e), | |
| 74 | else => |e| return posix.unexpectedErrno(e), | |
| 73 | 75 | } |
| 74 | 76 | } else { |
| 75 | 77 | const err = std.c.pthread_setname_np(self.getHandle(), name_with_terminator.ptr); |
| 76 | 78 | switch (err) { |
| 77 | 79 | .SUCCESS => return, |
| 78 | 80 | .RANGE => unreachable, |
| 79 | else => |e| return os.unexpectedErrno(e), | |
| 81 | else => |e| return posix.unexpectedErrno(e), | |
| 80 | 82 | } |
| 81 | 83 | } |
| 82 | 84 | } else { |
| ... | ... | @@ -95,21 +97,21 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void { |
| 95 | 97 | const byte_len = math.cast(c_ushort, len * 2) orelse return error.NameTooLong; |
| 96 | 98 | |
| 97 | 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 | 101 | .Length = byte_len, |
| 100 | 102 | .MaximumLength = byte_len, |
| 101 | 103 | .Buffer = &buf, |
| 102 | 104 | }; |
| 103 | 105 | |
| 104 | switch (os.windows.ntdll.NtSetInformationThread( | |
| 106 | switch (windows.ntdll.NtSetInformationThread( | |
| 105 | 107 | self.getHandle(), |
| 106 | 108 | .ThreadNameInformation, |
| 107 | 109 | &unicode_string, |
| 108 | @sizeOf(os.windows.UNICODE_STRING), | |
| 110 | @sizeOf(windows.UNICODE_STRING), | |
| 109 | 111 | )) { |
| 110 | 112 | .SUCCESS => return, |
| 111 | 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 | 117 | .macos, .ios, .watchos, .tvos => if (use_pthreads) { |
| ... | ... | @@ -119,7 +121,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void { |
| 119 | 121 | const err = std.c.pthread_setname_np(name_with_terminator.ptr); |
| 120 | 122 | switch (err) { |
| 121 | 123 | .SUCCESS => return, |
| 122 | else => |e| return os.unexpectedErrno(e), | |
| 124 | else => |e| return posix.unexpectedErrno(e), | |
| 123 | 125 | } |
| 124 | 126 | }, |
| 125 | 127 | .netbsd, .solaris, .illumos => if (use_pthreads) { |
| ... | ... | @@ -129,7 +131,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void { |
| 129 | 131 | .INVAL => unreachable, |
| 130 | 132 | .SRCH => unreachable, |
| 131 | 133 | .NOMEM => unreachable, |
| 132 | else => |e| return os.unexpectedErrno(e), | |
| 134 | else => |e| return posix.unexpectedErrno(e), | |
| 133 | 135 | } |
| 134 | 136 | }, |
| 135 | 137 | .freebsd, .openbsd => if (use_pthreads) { |
| ... | ... | @@ -148,7 +150,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void { |
| 148 | 150 | .FAULT => unreachable, |
| 149 | 151 | .NAMETOOLONG => unreachable, // already checked |
| 150 | 152 | .SRCH => unreachable, |
| 151 | else => |e| return os.unexpectedErrno(e), | |
| 153 | else => |e| return posix.unexpectedErrno(e), | |
| 152 | 154 | } |
| 153 | 155 | }, |
| 154 | 156 | else => {}, |
| ... | ... | @@ -159,7 +161,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void { |
| 159 | 161 | pub const GetNameError = error{ |
| 160 | 162 | Unsupported, |
| 161 | 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; | |
| 163 | 165 | |
| 164 | 166 | /// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). |
| 165 | 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 | 169 | buffer_ptr[max_name_len] = 0; |
| 168 | 170 | var buffer: [:0]u8 = buffer_ptr; |
| 169 | 171 | |
| 170 | switch (target.os.tag) { | |
| 172 | switch (native_os) { | |
| 171 | 173 | .linux => if (use_pthreads) { |
| 172 | 174 | if (self.getHandle() == std.c.pthread_self()) { |
| 173 | 175 | // Get the name of the calling thread (no thread id required). |
| 174 | const err = try os.prctl(.GET_NAME, .{@intFromPtr(buffer.ptr)}); | |
| 175 | switch (@as(os.E, @enumFromInt(err))) { | |
| 176 | const err = try posix.prctl(.GET_NAME, .{@intFromPtr(buffer.ptr)}); | |
| 177 | switch (@as(posix.E, @enumFromInt(err))) { | |
| 176 | 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 | 181 | } else { |
| 180 | 182 | const err = std.c.pthread_getname_np(self.getHandle(), buffer.ptr, max_name_len + 1); |
| 181 | 183 | switch (err) { |
| 182 | 184 | .SUCCESS => return std.mem.sliceTo(buffer, 0), |
| 183 | 185 | .RANGE => unreachable, |
| 184 | else => |e| return os.unexpectedErrno(e), | |
| 186 | else => |e| return posix.unexpectedErrno(e), | |
| 185 | 187 | } |
| 186 | 188 | } |
| 187 | 189 | } else { |
| ... | ... | @@ -196,10 +198,10 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co |
| 196 | 198 | return if (data_len >= 1) buffer[0 .. data_len - 1] else null; |
| 197 | 199 | }, |
| 198 | 200 | .windows => { |
| 199 | const buf_capacity = @sizeOf(os.windows.UNICODE_STRING) + (@sizeOf(u16) * max_name_len); | |
| 200 | var buf: [buf_capacity]u8 align(@alignOf(os.windows.UNICODE_STRING)) = undefined; | |
| 201 | const buf_capacity = @sizeOf(windows.UNICODE_STRING) + (@sizeOf(u16) * max_name_len); | |
| 202 | var buf: [buf_capacity]u8 align(@alignOf(windows.UNICODE_STRING)) = undefined; | |
| 201 | 203 | |
| 202 | switch (os.windows.ntdll.NtQueryInformationThread( | |
| 204 | switch (windows.ntdll.NtQueryInformationThread( | |
| 203 | 205 | self.getHandle(), |
| 204 | 206 | .ThreadNameInformation, |
| 205 | 207 | &buf, |
| ... | ... | @@ -207,12 +209,12 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co |
| 207 | 209 | null, |
| 208 | 210 | )) { |
| 209 | 211 | .SUCCESS => { |
| 210 | const string = @as(*const os.windows.UNICODE_STRING, @ptrCast(&buf)); | |
| 212 | const string = @as(*const windows.UNICODE_STRING, @ptrCast(&buf)); | |
| 211 | 213 | const len = std.unicode.wtf16LeToWtf8(buffer, string.Buffer.?[0 .. string.Length / 2]); |
| 212 | 214 | return if (len > 0) buffer[0..len] else null; |
| 213 | 215 | }, |
| 214 | 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 | 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 | 222 | switch (err) { |
| 221 | 223 | .SUCCESS => return std.mem.sliceTo(buffer, 0), |
| 222 | 224 | .SRCH => unreachable, |
| 223 | else => |e| return os.unexpectedErrno(e), | |
| 225 | else => |e| return posix.unexpectedErrno(e), | |
| 224 | 226 | } |
| 225 | 227 | }, |
| 226 | 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 | 231 | .SUCCESS => return std.mem.sliceTo(buffer, 0), |
| 230 | 232 | .INVAL => unreachable, |
| 231 | 233 | .SRCH => unreachable, |
| 232 | else => |e| return os.unexpectedErrno(e), | |
| 234 | else => |e| return posix.unexpectedErrno(e), | |
| 233 | 235 | } |
| 234 | 236 | }, |
| 235 | 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 | 248 | .INVAL => unreachable, |
| 247 | 249 | .FAULT => unreachable, |
| 248 | 250 | .SRCH => unreachable, |
| 249 | else => |e| return os.unexpectedErrno(e), | |
| 251 | else => |e| return posix.unexpectedErrno(e), | |
| 250 | 252 | } |
| 251 | 253 | }, |
| 252 | 254 | else => {}, |
| ... | ... | @@ -255,7 +257,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co |
| 255 | 257 | } |
| 256 | 258 | |
| 257 | 259 | /// Represents an ID per thread guaranteed to be unique only within a process. |
| 258 | pub const Id = switch (target.os.tag) { | |
| 260 | pub const Id = switch (native_os) { | |
| 259 | 261 | .linux, |
| 260 | 262 | .dragonfly, |
| 261 | 263 | .netbsd, |
| ... | ... | @@ -265,7 +267,7 @@ pub const Id = switch (target.os.tag) { |
| 265 | 267 | .wasi, |
| 266 | 268 | => u32, |
| 267 | 269 | .macos, .ios, .watchos, .tvos => u64, |
| 268 | .windows => os.windows.DWORD, | |
| 270 | .windows => windows.DWORD, | |
| 269 | 271 | else => usize, |
| 270 | 272 | }; |
| 271 | 273 | |
| ... | ... | @@ -368,13 +370,13 @@ pub const YieldError = error{ |
| 368 | 370 | |
| 369 | 371 | /// Yields the current thread potentially allowing other threads to run. |
| 370 | 372 | pub fn yield() YieldError!void { |
| 371 | if (builtin.os.tag == .windows) { | |
| 373 | if (native_os == .windows) { | |
| 372 | 374 | // The return value has to do with how many other threads there are; it is not |
| 373 | 375 | // an error condition on Windows. |
| 374 | _ = os.windows.kernel32.SwitchToThread(); | |
| 376 | _ = windows.kernel32.SwitchToThread(); | |
| 375 | 377 | return; |
| 376 | 378 | } |
| 377 | switch (os.errno(os.system.sched_yield())) { | |
| 379 | switch (posix.errno(posix.system.sched_yield())) { | |
| 378 | 380 | .SUCCESS => return, |
| 379 | 381 | .NOSYS => return error.SystemCannotYield, |
| 380 | 382 | else => return error.SystemCannotYield, |
| ... | ... | @@ -390,7 +392,7 @@ const Completion = std.atomic.Value(enum(u8) { |
| 390 | 392 | |
| 391 | 393 | /// Used by the Thread implementations to call the spawned function with the arguments. |
| 392 | 394 | fn callFn(comptime f: anytype, args: anytype) switch (Impl) { |
| 393 | WindowsThreadImpl => std.os.windows.DWORD, | |
| 395 | WindowsThreadImpl => windows.DWORD, | |
| 394 | 396 | LinuxThreadImpl => u8, |
| 395 | 397 | PosixThreadImpl => ?*anyopaque, |
| 396 | 398 | else => unreachable, |
| ... | ... | @@ -470,13 +472,11 @@ const UnsupportedImpl = struct { |
| 470 | 472 | |
| 471 | 473 | fn unsupported(unused: anytype) noreturn { |
| 472 | 474 | _ = unused; |
| 473 | @compileError("Unsupported operating system " ++ @tagName(target.os.tag)); | |
| 475 | @compileError("Unsupported operating system " ++ @tagName(native_os)); | |
| 474 | 476 | } |
| 475 | 477 | }; |
| 476 | 478 | |
| 477 | 479 | const WindowsThreadImpl = struct { |
| 478 | const windows = os.windows; | |
| 479 | ||
| 480 | 480 | pub const ThreadHandle = windows.HANDLE; |
| 481 | 481 | |
| 482 | 482 | fn getCurrentId() windows.DWORD { |
| ... | ... | @@ -584,7 +584,7 @@ const PosixThreadImpl = struct { |
| 584 | 584 | pub const ThreadHandle = c.pthread_t; |
| 585 | 585 | |
| 586 | 586 | fn getCurrentId() Id { |
| 587 | switch (target.os.tag) { | |
| 587 | switch (native_os) { | |
| 588 | 588 | .linux => { |
| 589 | 589 | return LinuxThreadImpl.getCurrentId(); |
| 590 | 590 | }, |
| ... | ... | @@ -616,15 +616,15 @@ const PosixThreadImpl = struct { |
| 616 | 616 | } |
| 617 | 617 | |
| 618 | 618 | fn getCpuCount() !usize { |
| 619 | switch (target.os.tag) { | |
| 619 | switch (native_os) { | |
| 620 | 620 | .linux => { |
| 621 | 621 | return LinuxThreadImpl.getCpuCount(); |
| 622 | 622 | }, |
| 623 | 623 | .openbsd => { |
| 624 | 624 | var count: c_int = undefined; |
| 625 | 625 | var count_size: usize = @sizeOf(c_int); |
| 626 | const mib = [_]c_int{ os.CTL.HW, os.system.HW.NCPUONLINE }; | |
| 627 | os.sysctl(&mib, &count, &count_size, null, 0) catch |err| switch (err) { | |
| 626 | const mib = [_]c_int{ std.c.CTL.HW, std.c.HW.NCPUONLINE }; | |
| 627 | std.c.sysctl(&mib, &count, &count_size, null, 0) catch |err| switch (err) { | |
| 628 | 628 | error.NameTooLong, error.UnknownName => unreachable, |
| 629 | 629 | else => |e| return e, |
| 630 | 630 | }; |
| ... | ... | @@ -634,25 +634,25 @@ const PosixThreadImpl = struct { |
| 634 | 634 | // The "proper" way to get the cpu count would be to query |
| 635 | 635 | // /dev/kstat via ioctls, and traverse a linked list for each |
| 636 | 636 | // cpu. |
| 637 | const rc = c.sysconf(os._SC.NPROCESSORS_ONLN); | |
| 638 | return switch (os.errno(rc)) { | |
| 637 | const rc = c.sysconf(std.c._SC.NPROCESSORS_ONLN); | |
| 638 | return switch (posix.errno(rc)) { | |
| 639 | 639 | .SUCCESS => @as(usize, @intCast(rc)), |
| 640 | else => |err| os.unexpectedErrno(err), | |
| 640 | else => |err| posix.unexpectedErrno(err), | |
| 641 | 641 | }; |
| 642 | 642 | }, |
| 643 | 643 | .haiku => { |
| 644 | var system_info: os.system.system_info = undefined; | |
| 645 | const rc = os.system.get_system_info(&system_info); // always returns B_OK | |
| 646 | return switch (os.errno(rc)) { | |
| 644 | var system_info: std.c.system_info = undefined; | |
| 645 | const rc = std.c.get_system_info(&system_info); // always returns B_OK | |
| 646 | return switch (posix.errno(rc)) { | |
| 647 | 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 | 651 | else => { |
| 652 | 652 | var count: c_int = undefined; |
| 653 | 653 | var count_len: usize = @sizeOf(c_int); |
| 654 | 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 | 656 | error.NameTooLong, error.UnknownName => unreachable, |
| 657 | 657 | else => |e| return e, |
| 658 | 658 | }; |
| ... | ... | @@ -699,7 +699,7 @@ const PosixThreadImpl = struct { |
| 699 | 699 | .AGAIN => return error.SystemResources, |
| 700 | 700 | .PERM => unreachable, |
| 701 | 701 | .INVAL => unreachable, |
| 702 | else => |err| return os.unexpectedErrno(err), | |
| 702 | else => |err| return posix.unexpectedErrno(err), | |
| 703 | 703 | } |
| 704 | 704 | } |
| 705 | 705 | |
| ... | ... | @@ -1013,7 +1013,7 @@ const WasiThreadImpl = struct { |
| 1013 | 1013 | }; |
| 1014 | 1014 | |
| 1015 | 1015 | const LinuxThreadImpl = struct { |
| 1016 | const linux = os.linux; | |
| 1016 | const linux = std.os.linux; | |
| 1017 | 1017 | |
| 1018 | 1018 | pub const ThreadHandle = i32; |
| 1019 | 1019 | |
| ... | ... | @@ -1028,9 +1028,9 @@ const LinuxThreadImpl = struct { |
| 1028 | 1028 | } |
| 1029 | 1029 | |
| 1030 | 1030 | fn getCpuCount() !usize { |
| 1031 | const cpu_set = try os.sched_getaffinity(0); | |
| 1031 | const cpu_set = try posix.sched_getaffinity(0); | |
| 1032 | 1032 | // 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 | } |
| 1035 | 1035 | |
| 1036 | 1036 | thread: *ThreadCompletion, |
| ... | ... | @@ -1228,10 +1228,10 @@ const LinuxThreadImpl = struct { |
| 1228 | 1228 | // map all memory needed without read/write permissions |
| 1229 | 1229 | // to avoid committing the whole region right away |
| 1230 | 1230 | // anonymous mapping ensures file descriptor limits are not exceeded |
| 1231 | const mapped = os.mmap( | |
| 1231 | const mapped = posix.mmap( | |
| 1232 | 1232 | null, |
| 1233 | 1233 | map_bytes, |
| 1234 | os.PROT.NONE, | |
| 1234 | posix.PROT.NONE, | |
| 1235 | 1235 | .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, |
| 1236 | 1236 | -1, |
| 1237 | 1237 | 0, |
| ... | ... | @@ -1244,24 +1244,24 @@ const LinuxThreadImpl = struct { |
| 1244 | 1244 | else => |e| return e, |
| 1245 | 1245 | }; |
| 1246 | 1246 | assert(mapped.len >= map_bytes); |
| 1247 | errdefer os.munmap(mapped); | |
| 1247 | errdefer posix.munmap(mapped); | |
| 1248 | 1248 | |
| 1249 | 1249 | // map everything but the guard page as read/write |
| 1250 | os.mprotect( | |
| 1250 | posix.mprotect( | |
| 1251 | 1251 | @alignCast(mapped[guard_offset..]), |
| 1252 | os.PROT.READ | os.PROT.WRITE, | |
| 1252 | posix.PROT.READ | posix.PROT.WRITE, | |
| 1253 | 1253 | ) catch |err| switch (err) { |
| 1254 | 1254 | error.AccessDenied => unreachable, |
| 1255 | 1255 | else => |e| return e, |
| 1256 | 1256 | }; |
| 1257 | 1257 | |
| 1258 | 1258 | // Prepare the TLS segment and prepare a user_desc struct when needed on x86 |
| 1259 | var tls_ptr = os.linux.tls.prepareTLS(mapped[tls_offset..]); | |
| 1260 | var user_desc: if (target.cpu.arch == .x86) os.linux.user_desc else void = undefined; | |
| 1259 | var tls_ptr = linux.tls.prepareTLS(mapped[tls_offset..]); | |
| 1260 | var user_desc: if (target.cpu.arch == .x86) linux.user_desc else void = undefined; | |
| 1261 | 1261 | if (target.cpu.arch == .x86) { |
| 1262 | 1262 | defer tls_ptr = @intFromPtr(&user_desc); |
| 1263 | 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 | 1265 | .base_addr = tls_ptr, |
| 1266 | 1266 | .limit = 0xfffff, |
| 1267 | 1267 | .flags = .{ |
| ... | ... | @@ -1286,7 +1286,7 @@ const LinuxThreadImpl = struct { |
| 1286 | 1286 | linux.CLONE.PARENT_SETTID | linux.CLONE.CHILD_CLEARTID | |
| 1287 | 1287 | linux.CLONE.SIGHAND | linux.CLONE.SYSVSEM | linux.CLONE.SETTLS; |
| 1288 | 1288 | |
| 1289 | switch (linux.getErrno(linux.clone( | |
| 1289 | switch (linux.E.init(linux.clone( | |
| 1290 | 1290 | Instance.entryFn, |
| 1291 | 1291 | @intFromPtr(&mapped[stack_offset]), |
| 1292 | 1292 | flags, |
| ... | ... | @@ -1302,7 +1302,7 @@ const LinuxThreadImpl = struct { |
| 1302 | 1302 | .NOSPC => unreachable, |
| 1303 | 1303 | .PERM => unreachable, |
| 1304 | 1304 | .USERS => unreachable, |
| 1305 | else => |err| return os.unexpectedErrno(err), | |
| 1305 | else => |err| return posix.unexpectedErrno(err), | |
| 1306 | 1306 | } |
| 1307 | 1307 | } |
| 1308 | 1308 | |
| ... | ... | @@ -1319,7 +1319,7 @@ const LinuxThreadImpl = struct { |
| 1319 | 1319 | } |
| 1320 | 1320 | |
| 1321 | 1321 | fn join(self: Impl) void { |
| 1322 | defer os.munmap(self.thread.mapped); | |
| 1322 | defer posix.munmap(self.thread.mapped); | |
| 1323 | 1323 | |
| 1324 | 1324 | var spin: u8 = 10; |
| 1325 | 1325 | while (true) { |
| ... | ... | @@ -1334,7 +1334,7 @@ const LinuxThreadImpl = struct { |
| 1334 | 1334 | continue; |
| 1335 | 1335 | } |
| 1336 | 1336 | |
| 1337 | switch (linux.getErrno(linux.futex_wait( | |
| 1337 | switch (linux.E.init(linux.futex_wait( | |
| 1338 | 1338 | &self.thread.child_tid.raw, |
| 1339 | 1339 | linux.FUTEX.WAIT, |
| 1340 | 1340 | tid, |
| ... | ... | @@ -1383,7 +1383,7 @@ test "setName, getName" { |
| 1383 | 1383 | // Wait for the main thread to have set the thread field in the context. |
| 1384 | 1384 | ctx.start_wait_event.wait(); |
| 1385 | 1385 | |
| 1386 | switch (target.os.tag) { | |
| 1386 | switch (native_os) { | |
| 1387 | 1387 | .windows => testThreadName(&ctx.thread) catch |err| switch (err) { |
| 1388 | 1388 | error.Unsupported => return error.SkipZigTest, |
| 1389 | 1389 | else => return err, |
| ... | ... | @@ -1406,7 +1406,7 @@ test "setName, getName" { |
| 1406 | 1406 | context.start_wait_event.set(); |
| 1407 | 1407 | context.test_done_event.wait(); |
| 1408 | 1408 | |
| 1409 | switch (target.os.tag) { | |
| 1409 | switch (native_os) { | |
| 1410 | 1410 | .macos, .ios, .watchos, .tvos => { |
| 1411 | 1411 | const res = thread.setName("foobar"); |
| 1412 | 1412 | try std.testing.expectError(error.Unsupported, res); |
lib/std/Thread/Futex.zig+71-64| ... | ... | @@ -1,13 +1,20 @@ |
| 1 | //! Futex is a mechanism used to block (`wait`) and unblock (`wake`) threads using a 32bit memory address as hints. | |
| 2 | //! Blocking a thread is acknowledged only if the 32bit memory address is equal to a given value. | |
| 3 | //! This check helps avoid block/unblock deadlocks which occur if a `wake()` happens before a `wait()`. | |
| 4 | //! Using Futex, other Thread synchronization primitives can be built which efficiently wait for cross-thread events or signals. | |
| 1 | //! A mechanism used to block (`wait`) and unblock (`wake`) threads using a | |
| 2 | //! 32bit memory address as hints. | |
| 3 | //! | |
| 4 | //! Blocking a thread is acknowledged only if the 32bit memory address is equal | |
| 5 | //! to a given value. This check helps avoid block/unblock deadlocks which | |
| 6 | //! occur if a `wake()` happens before a `wait()`. | |
| 7 | //! | |
| 8 | //! Using Futex, other Thread synchronization primitives can be built which | |
| 9 | //! efficiently wait for cross-thread events or signals. | |
| 5 | 10 | |
| 6 | 11 | const std = @import("../std.zig"); |
| 7 | 12 | const builtin = @import("builtin"); |
| 8 | 13 | const Futex = @This(); |
| 14 | const windows = std.os.windows; | |
| 15 | const linux = std.os.linux; | |
| 16 | const c = std.c; | |
| 9 | 17 | |
| 10 | const os = std.os; | |
| 11 | 18 | const assert = std.debug.assert; |
| 12 | 19 | const testing = std.testing; |
| 13 | 20 | const atomic = std.atomic; |
| ... | ... | @@ -124,18 +131,18 @@ const SingleThreadedImpl = struct { |
| 124 | 131 | // as it's generally already a linked target and is autoloaded into all processes anyway. |
| 125 | 132 | const WindowsImpl = struct { |
| 126 | 133 | fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void { |
| 127 | var timeout_value: os.windows.LARGE_INTEGER = undefined; | |
| 128 | var timeout_ptr: ?*const os.windows.LARGE_INTEGER = null; | |
| 134 | var timeout_value: windows.LARGE_INTEGER = undefined; | |
| 135 | var timeout_ptr: ?*const windows.LARGE_INTEGER = null; | |
| 129 | 136 | |
| 130 | 137 | // NTDLL functions work with time in units of 100 nanoseconds. |
| 131 | 138 | // Positive values are absolute deadlines while negative values are relative durations. |
| 132 | 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 | 141 | timeout_value = -timeout_value; |
| 135 | 142 | timeout_ptr = &timeout_value; |
| 136 | 143 | } |
| 137 | 144 | |
| 138 | const rc = os.windows.ntdll.RtlWaitOnAddress( | |
| 145 | const rc = windows.ntdll.RtlWaitOnAddress( | |
| 139 | 146 | ptr, |
| 140 | 147 | &expect, |
| 141 | 148 | @sizeOf(@TypeOf(expect)), |
| ... | ... | @@ -157,8 +164,8 @@ const WindowsImpl = struct { |
| 157 | 164 | assert(max_waiters != 0); |
| 158 | 165 | |
| 159 | 166 | switch (max_waiters) { |
| 160 | 1 => os.windows.ntdll.RtlWakeAddressSingle(address), | |
| 161 | else => os.windows.ntdll.RtlWakeAddressAll(address), | |
| 167 | 1 => windows.ntdll.RtlWakeAddressSingle(address), | |
| 168 | else => windows.ntdll.RtlWakeAddressAll(address), | |
| 162 | 169 | } |
| 163 | 170 | } |
| 164 | 171 | }; |
| ... | ... | @@ -189,10 +196,10 @@ const DarwinImpl = struct { |
| 189 | 196 | var timeout_overflowed = false; |
| 190 | 197 | |
| 191 | 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 | 200 | const status = blk: { |
| 194 | 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 | } |
| 197 | 204 | |
| 198 | 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 | 207 | break :overflow std.math.maxInt(u32); |
| 201 | 208 | }; |
| 202 | 209 | |
| 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 | }; |
| 205 | 212 | |
| 206 | 213 | if (status >= 0) return; |
| 207 | switch (@as(std.os.E, @enumFromInt(-status))) { | |
| 214 | switch (@as(c.E, @enumFromInt(-status))) { | |
| 208 | 215 | // Wait was interrupted by the OS or other spurious signalling. |
| 209 | 216 | .INTR => {}, |
| 210 | 217 | // Address of the futex was paged out. This is unlikely, but possible in theory, and |
| ... | ... | @@ -221,17 +228,17 @@ const DarwinImpl = struct { |
| 221 | 228 | } |
| 222 | 229 | |
| 223 | 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 | 232 | if (max_waiters > 1) { |
| 226 | flags |= os.darwin.ULF_WAKE_ALL; | |
| 233 | flags |= c.ULF_WAKE_ALL; | |
| 227 | 234 | } |
| 228 | 235 | |
| 229 | 236 | while (true) { |
| 230 | 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); | |
| 232 | 239 | |
| 233 | 240 | if (status >= 0) return; |
| 234 | switch (@as(std.os.E, @enumFromInt(-status))) { | |
| 241 | switch (@as(c.E, @enumFromInt(-status))) { | |
| 235 | 242 | .INTR => continue, // spurious wake() |
| 236 | 243 | .FAULT => unreachable, // __ulock_wake doesn't generate EFAULT according to darwin pthread_cond_t |
| 237 | 244 | .NOENT => return, // nothing was woken up |
| ... | ... | @@ -245,20 +252,20 @@ const DarwinImpl = struct { |
| 245 | 252 | // https://man7.org/linux/man-pages/man2/futex.2.html |
| 246 | 253 | const LinuxImpl = struct { |
| 247 | 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 | 256 | if (timeout) |timeout_ns| { |
| 250 | 257 | ts.tv_sec = @as(@TypeOf(ts.tv_sec), @intCast(timeout_ns / std.time.ns_per_s)); |
| 251 | 258 | ts.tv_nsec = @as(@TypeOf(ts.tv_nsec), @intCast(timeout_ns % std.time.ns_per_s)); |
| 252 | 259 | } |
| 253 | 260 | |
| 254 | const rc = os.linux.futex_wait( | |
| 261 | const rc = linux.futex_wait( | |
| 255 | 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 | 264 | @as(i32, @bitCast(expect)), |
| 258 | 265 | if (timeout != null) &ts else null, |
| 259 | 266 | ); |
| 260 | 267 | |
| 261 | switch (os.linux.getErrno(rc)) { | |
| 268 | switch (linux.E.init(rc)) { | |
| 262 | 269 | .SUCCESS => {}, // notified by `wake()` |
| 263 | 270 | .INTR => {}, // spurious wakeup |
| 264 | 271 | .AGAIN => {}, // ptr.* != expect |
| ... | ... | @@ -273,13 +280,13 @@ const LinuxImpl = struct { |
| 273 | 280 | } |
| 274 | 281 | |
| 275 | 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 | 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 | 286 | std.math.cast(i32, max_waiters) orelse std.math.maxInt(i32), |
| 280 | 287 | ); |
| 281 | 288 | |
| 282 | switch (os.linux.getErrno(rc)) { | |
| 289 | switch (linux.E.init(rc)) { | |
| 283 | 290 | .SUCCESS => {}, // successful wake up |
| 284 | 291 | .INVAL => {}, // invalid futex_wait() on ptr done elsewhere |
| 285 | 292 | .FAULT => {}, // pointer became invalid while doing the wake |
| ... | ... | @@ -292,28 +299,28 @@ const LinuxImpl = struct { |
| 292 | 299 | const FreebsdImpl = struct { |
| 293 | 300 | fn wait(ptr: *const atomic.Value(u32), expect: u32, timeout: ?u64) error{Timeout}!void { |
| 294 | 301 | var tm_size: usize = 0; |
| 295 | var tm: os.freebsd._umtx_time = undefined; | |
| 296 | var tm_ptr: ?*const os.freebsd._umtx_time = null; | |
| 302 | var tm: c._umtx_time = undefined; | |
| 303 | var tm_ptr: ?*const c._umtx_time = null; | |
| 297 | 304 | |
| 298 | 305 | if (timeout) |timeout_ns| { |
| 299 | 306 | tm_ptr = &tm; |
| 300 | 307 | tm_size = @sizeOf(@TypeOf(tm)); |
| 301 | 308 | |
| 302 | 309 | tm._flags = 0; // use relative time not UMTX_ABSTIME |
| 303 | tm._clockid = os.CLOCK.MONOTONIC; | |
| 310 | tm._clockid = c.CLOCK.MONOTONIC; | |
| 304 | 311 | tm._timeout.tv_sec = @as(@TypeOf(tm._timeout.tv_sec), @intCast(timeout_ns / std.time.ns_per_s)); |
| 305 | 312 | tm._timeout.tv_nsec = @as(@TypeOf(tm._timeout.tv_nsec), @intCast(timeout_ns % std.time.ns_per_s)); |
| 306 | 313 | } |
| 307 | 314 | |
| 308 | const rc = os.freebsd._umtx_op( | |
| 315 | const rc = c._umtx_op( | |
| 309 | 316 | @intFromPtr(&ptr.raw), |
| 310 | @intFromEnum(os.freebsd.UMTX_OP.WAIT_UINT_PRIVATE), | |
| 317 | @intFromEnum(c.UMTX_OP.WAIT_UINT_PRIVATE), | |
| 311 | 318 | @as(c_ulong, expect), |
| 312 | 319 | tm_size, |
| 313 | 320 | @intFromPtr(tm_ptr), |
| 314 | 321 | ); |
| 315 | 322 | |
| 316 | switch (os.errno(rc)) { | |
| 323 | switch (std.posix.errno(rc)) { | |
| 317 | 324 | .SUCCESS => {}, |
| 318 | 325 | .FAULT => unreachable, // one of the args points to invalid memory |
| 319 | 326 | .INVAL => unreachable, // arguments should be correct |
| ... | ... | @@ -327,15 +334,15 @@ const FreebsdImpl = struct { |
| 327 | 334 | } |
| 328 | 335 | |
| 329 | 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 | 338 | @intFromPtr(&ptr.raw), |
| 332 | @intFromEnum(os.freebsd.UMTX_OP.WAKE_PRIVATE), | |
| 339 | @intFromEnum(c.UMTX_OP.WAKE_PRIVATE), | |
| 333 | 340 | @as(c_ulong, max_waiters), |
| 334 | 341 | 0, // there is no timeout struct |
| 335 | 342 | 0, // there is no timeout struct pointer |
| 336 | 343 | ); |
| 337 | 344 | |
| 338 | switch (os.errno(rc)) { | |
| 345 | switch (std.posix.errno(rc)) { | |
| 339 | 346 | .SUCCESS => {}, |
| 340 | 347 | .FAULT => {}, // it's ok if the ptr doesn't point to valid memory |
| 341 | 348 | .INVAL => unreachable, // arguments should be correct |
| ... | ... | @@ -347,21 +354,21 @@ const FreebsdImpl = struct { |
| 347 | 354 | // https://man.openbsd.org/futex.2 |
| 348 | 355 | const OpenbsdImpl = struct { |
| 349 | 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 | 358 | if (timeout) |timeout_ns| { |
| 352 | 359 | ts.tv_sec = @as(@TypeOf(ts.tv_sec), @intCast(timeout_ns / std.time.ns_per_s)); |
| 353 | 360 | ts.tv_nsec = @as(@TypeOf(ts.tv_nsec), @intCast(timeout_ns % std.time.ns_per_s)); |
| 354 | 361 | } |
| 355 | 362 | |
| 356 | const rc = os.openbsd.futex( | |
| 363 | const rc = c.futex( | |
| 357 | 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 | 366 | @as(c_int, @bitCast(expect)), |
| 360 | 367 | if (timeout != null) &ts else null, |
| 361 | 368 | null, // FUTEX_WAIT takes no requeue address |
| 362 | 369 | ); |
| 363 | 370 | |
| 364 | switch (os.errno(rc)) { | |
| 371 | switch (std.posix.errno(rc)) { | |
| 365 | 372 | .SUCCESS => {}, // woken up by wake |
| 366 | 373 | .NOSYS => unreachable, // the futex operation shouldn't be invalid |
| 367 | 374 | .FAULT => unreachable, // ptr was invalid |
| ... | ... | @@ -378,9 +385,9 @@ const OpenbsdImpl = struct { |
| 378 | 385 | } |
| 379 | 386 | |
| 380 | 387 | fn wake(ptr: *const atomic.Value(u32), max_waiters: u32) void { |
| 381 | const rc = os.openbsd.futex( | |
| 388 | const rc = c.futex( | |
| 382 | 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 | 391 | std.math.cast(c_int, max_waiters) orelse std.math.maxInt(c_int), |
| 385 | 392 | null, // FUTEX_WAKE takes no timeout ptr |
| 386 | 393 | null, // FUTEX_WAKE takes no requeue address |
| ... | ... | @@ -415,9 +422,9 @@ const DragonflyImpl = struct { |
| 415 | 422 | |
| 416 | 423 | const value = @as(c_int, @bitCast(expect)); |
| 417 | 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); | |
| 419 | 426 | |
| 420 | switch (os.errno(rc)) { | |
| 427 | switch (std.posix.errno(rc)) { | |
| 421 | 428 | .SUCCESS => {}, |
| 422 | 429 | .BUSY => {}, // ptr != expect |
| 423 | 430 | .AGAIN => { // maybe timed out, or paged out, or hit 2s kernel refresh |
| ... | ... | @@ -444,7 +451,7 @@ const DragonflyImpl = struct { |
| 444 | 451 | // > umtx_wakeup() will generally return 0 unless the address is bad. |
| 445 | 452 | // We are fine with the address being bad (e.g. for Semaphore.post() where Semaphore.wait() frees the Semaphore) |
| 446 | 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 | }; |
| 450 | 457 | |
| ... | ... | @@ -496,8 +503,8 @@ const WasmImpl = struct { |
| 496 | 503 | /// https://go.dev/src/runtime/sema.go |
| 497 | 504 | const PosixImpl = struct { |
| 498 | 505 | const Event = struct { |
| 499 | cond: std.c.pthread_cond_t, | |
| 500 | mutex: std.c.pthread_mutex_t, | |
| 506 | cond: c.pthread_cond_t, | |
| 507 | mutex: c.pthread_mutex_t, | |
| 501 | 508 | state: enum { empty, waiting, notified }, |
| 502 | 509 | |
| 503 | 510 | fn init(self: *Event) void { |
| ... | ... | @@ -509,18 +516,18 @@ const PosixImpl = struct { |
| 509 | 516 | |
| 510 | 517 | fn deinit(self: *Event) void { |
| 511 | 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 | 520 | assert(rc == .SUCCESS or rc == .INVAL); |
| 514 | 521 | |
| 515 | const rm = std.c.pthread_mutex_destroy(&self.mutex); | |
| 522 | const rm = c.pthread_mutex_destroy(&self.mutex); | |
| 516 | 523 | assert(rm == .SUCCESS or rm == .INVAL); |
| 517 | 524 | |
| 518 | 525 | self.* = undefined; |
| 519 | 526 | } |
| 520 | 527 | |
| 521 | 528 | fn wait(self: *Event, timeout: ?u64) error{Timeout}!void { |
| 522 | assert(std.c.pthread_mutex_lock(&self.mutex) == .SUCCESS); | |
| 523 | defer assert(std.c.pthread_mutex_unlock(&self.mutex) == .SUCCESS); | |
| 529 | assert(c.pthread_mutex_lock(&self.mutex) == .SUCCESS); | |
| 530 | defer assert(c.pthread_mutex_unlock(&self.mutex) == .SUCCESS); | |
| 524 | 531 | |
| 525 | 532 | // Early return if the event was already set. |
| 526 | 533 | if (self.state == .notified) { |
| ... | ... | @@ -530,9 +537,9 @@ const PosixImpl = struct { |
| 530 | 537 | // Compute the absolute timeout if one was specified. |
| 531 | 538 | // POSIX requires that REALTIME is used by default for the pthread timedwait functions. |
| 532 | 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 | 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 | 543 | ts.tv_sec +|= @as(@TypeOf(ts.tv_sec), @intCast(timeout_ns / std.time.ns_per_s)); |
| 537 | 544 | ts.tv_nsec += @as(@TypeOf(ts.tv_nsec), @intCast(timeout_ns % std.time.ns_per_s)); |
| 538 | 545 | |
| ... | ... | @@ -549,8 +556,8 @@ const PosixImpl = struct { |
| 549 | 556 | while (true) { |
| 550 | 557 | // Block using either pthread_cond_wait or pthread_cond_timewait if there's an absolute timeout. |
| 551 | 558 | const rc = blk: { |
| 552 | if (timeout == null) break :blk std.c.pthread_cond_wait(&self.cond, &self.mutex); | |
| 553 | break :blk std.c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts); | |
| 559 | if (timeout == null) break :blk c.pthread_cond_wait(&self.cond, &self.mutex); | |
| 560 | break :blk c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts); | |
| 554 | 561 | }; |
| 555 | 562 | |
| 556 | 563 | // After waking up, check if the event was set. |
| ... | ... | @@ -574,8 +581,8 @@ const PosixImpl = struct { |
| 574 | 581 | } |
| 575 | 582 | |
| 576 | 583 | fn set(self: *Event) void { |
| 577 | assert(std.c.pthread_mutex_lock(&self.mutex) == .SUCCESS); | |
| 578 | defer assert(std.c.pthread_mutex_unlock(&self.mutex) == .SUCCESS); | |
| 584 | assert(c.pthread_mutex_lock(&self.mutex) == .SUCCESS); | |
| 585 | defer assert(c.pthread_mutex_unlock(&self.mutex) == .SUCCESS); | |
| 579 | 586 | |
| 580 | 587 | // Make sure that multiple calls to set() were not done on the same Event. |
| 581 | 588 | const old_state = self.state; |
| ... | ... | @@ -586,7 +593,7 @@ const PosixImpl = struct { |
| 586 | 593 | // the condition variable once it observes the new state, potentially causing a UAF if done unlocked. |
| 587 | 594 | self.state = .notified; |
| 588 | 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 | 739 | }; |
| 733 | 740 | |
| 734 | 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 | 743 | pending: atomic.Value(usize) = atomic.Value(usize).init(0), |
| 737 | 744 | treap: Treap = .{}, |
| 738 | 745 | |
| ... | ... | @@ -798,8 +805,8 @@ const PosixImpl = struct { |
| 798 | 805 | |
| 799 | 806 | var waiter: Waiter = undefined; |
| 800 | 807 | { |
| 801 | assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS); | |
| 802 | defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS); | |
| 808 | assert(c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS); | |
| 809 | defer assert(c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS); | |
| 803 | 810 | |
| 804 | 811 | cancelled = ptr.load(.monotonic) != expect; |
| 805 | 812 | if (cancelled) { |
| ... | ... | @@ -821,8 +828,8 @@ const PosixImpl = struct { |
| 821 | 828 | // If we return early without waiting, the waiter on the stack would be invalidated and the wake() thread risks a UAF. |
| 822 | 829 | defer if (!cancelled) waiter.event.wait(null) catch unreachable; |
| 823 | 830 | |
| 824 | assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS); | |
| 825 | defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS); | |
| 831 | assert(c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS); | |
| 832 | defer assert(c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS); | |
| 826 | 833 | |
| 827 | 834 | cancelled = WaitQueue.tryRemove(&bucket.treap, address, &waiter); |
| 828 | 835 | if (cancelled) { |
| ... | ... | @@ -871,8 +878,8 @@ const PosixImpl = struct { |
| 871 | 878 | } |
| 872 | 879 | }; |
| 873 | 880 | |
| 874 | assert(std.c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS); | |
| 875 | defer assert(std.c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS); | |
| 881 | assert(c.pthread_mutex_lock(&bucket.mutex) == .SUCCESS); | |
| 882 | defer assert(c.pthread_mutex_unlock(&bucket.mutex) == .SUCCESS); | |
| 876 | 883 | |
| 877 | 884 | // Another pending check again to avoid the WaitQueue lookup if not necessary. |
| 878 | 885 | if (bucket.pending.load(.monotonic) > 0) { |
lib/std/Thread/Mutex.zig+12-9| ... | ... | @@ -23,7 +23,6 @@ const std = @import("../std.zig"); |
| 23 | 23 | const builtin = @import("builtin"); |
| 24 | 24 | const Mutex = @This(); |
| 25 | 25 | |
| 26 | const os = std.os; | |
| 27 | 26 | const assert = std.debug.assert; |
| 28 | 27 | const testing = std.testing; |
| 29 | 28 | const Thread = std.Thread; |
| ... | ... | @@ -117,36 +116,40 @@ const SingleThreadedImpl = struct { |
| 117 | 116 | // SRWLOCK on windows is almost always faster than Futex solution. |
| 118 | 117 | // It also implements an efficient Condition with requeue support for us. |
| 119 | 118 | const WindowsImpl = struct { |
| 120 | srwlock: os.windows.SRWLOCK = .{}, | |
| 119 | srwlock: windows.SRWLOCK = .{}, | |
| 121 | 120 | |
| 122 | 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 | } |
| 125 | 124 | |
| 126 | 125 | fn lock(self: *@This()) void { |
| 127 | os.windows.kernel32.AcquireSRWLockExclusive(&self.srwlock); | |
| 126 | windows.kernel32.AcquireSRWLockExclusive(&self.srwlock); | |
| 128 | 127 | } |
| 129 | 128 | |
| 130 | 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 | }; |
| 134 | 135 | |
| 135 | 136 | // os_unfair_lock on darwin supports priority inheritance and is generally faster than Futex solutions. |
| 136 | 137 | const DarwinImpl = struct { |
| 137 | oul: os.darwin.os_unfair_lock = .{}, | |
| 138 | oul: c.os_unfair_lock = .{}, | |
| 138 | 139 | |
| 139 | 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 | } |
| 142 | 143 | |
| 143 | 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 | } |
| 146 | 147 | |
| 147 | 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 | }; |
| 151 | 154 | |
| 152 | 155 | const 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 | 782 | }, |
| 783 | 783 | .wasi => { |
| 784 | 784 | std.debug.print("{s}", .{msg}); |
| 785 | std.os.abort(); | |
| 785 | std.posix.abort(); | |
| 786 | 786 | }, |
| 787 | 787 | .uefi => { |
| 788 | 788 | const uefi = std.os.uefi; |
| ... | ... | @@ -830,9 +830,9 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace, ret_addr |
| 830 | 830 | } |
| 831 | 831 | |
| 832 | 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 | 836 | .plan9 => { |
| 837 | 837 | var status: [std.os.plan9.ERRMAX]u8 = undefined; |
| 838 | 838 | const len = @min(msg.len, status.len - 1); |
lib/std/c.zig+25-32| ... | ... | @@ -2,12 +2,13 @@ const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const c = @This(); |
| 4 | 4 | const page_size = std.mem.page_size; |
| 5 | const iovec = std.os.iovec; | |
| 6 | const iovec_const = std.os.iovec_const; | |
| 5 | const iovec = std.posix.iovec; | |
| 6 | const iovec_const = std.posix.iovec_const; | |
| 7 | 7 | const wasi = @import("c/wasi.zig"); |
| 8 | 8 | const native_abi = builtin.abi; |
| 9 | 9 | const native_arch = builtin.cpu.arch; |
| 10 | 10 | const native_os = builtin.os.tag; |
| 11 | const linux = std.os.linux; | |
| 11 | 12 | |
| 12 | 13 | /// If not linking libc, returns false. |
| 13 | 14 | /// If linking musl libc, returns true. |
| ... | ... | @@ -208,7 +209,7 @@ pub const pthread_rwlock_t = switch (native_os) { |
| 208 | 209 | }; |
| 209 | 210 | |
| 210 | 211 | pub const AT = switch (native_os) { |
| 211 | .linux => std.os.linux.AT, | |
| 212 | .linux => linux.AT, | |
| 212 | 213 | .windows => struct { |
| 213 | 214 | /// Remove directory instead of unlinking file |
| 214 | 215 | pub const REMOVEDIR = 0x200; |
| ... | ... | @@ -326,9 +327,9 @@ pub const AT = switch (native_os) { |
| 326 | 327 | }; |
| 327 | 328 | |
| 328 | 329 | pub const O = switch (native_os) { |
| 329 | .linux => std.os.linux.O, | |
| 330 | .linux => linux.O, | |
| 330 | 331 | .emscripten => packed struct(u32) { |
| 331 | ACCMODE: std.os.ACCMODE = .RDONLY, | |
| 332 | ACCMODE: std.posix.ACCMODE = .RDONLY, | |
| 332 | 333 | _2: u4 = 0, |
| 333 | 334 | CREAT: bool = false, |
| 334 | 335 | EXCL: bool = false, |
| ... | ... | @@ -369,7 +370,7 @@ pub const O = switch (native_os) { |
| 369 | 370 | _: u3 = 0, |
| 370 | 371 | }, |
| 371 | 372 | .solaris, .illumos => packed struct(u32) { |
| 372 | ACCMODE: std.os.ACCMODE = .RDONLY, | |
| 373 | ACCMODE: std.posix.ACCMODE = .RDONLY, | |
| 373 | 374 | NDELAY: bool = false, |
| 374 | 375 | APPEND: bool = false, |
| 375 | 376 | SYNC: bool = false, |
| ... | ... | @@ -396,7 +397,7 @@ pub const O = switch (native_os) { |
| 396 | 397 | _: u6 = 0, |
| 397 | 398 | }, |
| 398 | 399 | .netbsd => packed struct(u32) { |
| 399 | ACCMODE: std.os.ACCMODE = .RDONLY, | |
| 400 | ACCMODE: std.posix.ACCMODE = .RDONLY, | |
| 400 | 401 | NONBLOCK: bool = false, |
| 401 | 402 | APPEND: bool = false, |
| 402 | 403 | SHLOCK: bool = false, |
| ... | ... | @@ -420,7 +421,7 @@ pub const O = switch (native_os) { |
| 420 | 421 | _: u8 = 0, |
| 421 | 422 | }, |
| 422 | 423 | .openbsd => packed struct(u32) { |
| 423 | ACCMODE: std.os.ACCMODE = .RDONLY, | |
| 424 | ACCMODE: std.posix.ACCMODE = .RDONLY, | |
| 424 | 425 | NONBLOCK: bool = false, |
| 425 | 426 | APPEND: bool = false, |
| 426 | 427 | SHLOCK: bool = false, |
| ... | ... | @@ -438,7 +439,7 @@ pub const O = switch (native_os) { |
| 438 | 439 | _: u14 = 0, |
| 439 | 440 | }, |
| 440 | 441 | .haiku => packed struct(u32) { |
| 441 | ACCMODE: std.os.ACCMODE = .RDONLY, | |
| 442 | ACCMODE: std.posix.ACCMODE = .RDONLY, | |
| 442 | 443 | _2: u4 = 0, |
| 443 | 444 | CLOEXEC: bool = false, |
| 444 | 445 | NONBLOCK: bool = false, |
| ... | ... | @@ -458,7 +459,7 @@ pub const O = switch (native_os) { |
| 458 | 459 | _: u10 = 0, |
| 459 | 460 | }, |
| 460 | 461 | .macos, .ios, .tvos, .watchos => packed struct(u32) { |
| 461 | ACCMODE: std.os.ACCMODE = .RDONLY, | |
| 462 | ACCMODE: std.posix.ACCMODE = .RDONLY, | |
| 462 | 463 | NONBLOCK: bool = false, |
| 463 | 464 | APPEND: bool = false, |
| 464 | 465 | SHLOCK: bool = false, |
| ... | ... | @@ -485,7 +486,7 @@ pub const O = switch (native_os) { |
| 485 | 486 | POPUP: bool = false, |
| 486 | 487 | }, |
| 487 | 488 | .dragonfly => packed struct(u32) { |
| 488 | ACCMODE: std.os.ACCMODE = .RDONLY, | |
| 489 | ACCMODE: std.posix.ACCMODE = .RDONLY, | |
| 489 | 490 | NONBLOCK: bool = false, |
| 490 | 491 | APPEND: bool = false, |
| 491 | 492 | SHLOCK: bool = false, |
| ... | ... | @@ -511,7 +512,7 @@ pub const O = switch (native_os) { |
| 511 | 512 | _: u4 = 0, |
| 512 | 513 | }, |
| 513 | 514 | .freebsd => packed struct(u32) { |
| 514 | ACCMODE: std.os.ACCMODE = .RDONLY, | |
| 515 | ACCMODE: std.posix.ACCMODE = .RDONLY, | |
| 515 | 516 | NONBLOCK: bool = false, |
| 516 | 517 | APPEND: bool = false, |
| 517 | 518 | SHLOCK: bool = false, |
| ... | ... | @@ -538,7 +539,7 @@ pub const O = switch (native_os) { |
| 538 | 539 | }; |
| 539 | 540 | |
| 540 | 541 | pub const MAP = switch (native_os) { |
| 541 | .linux => std.os.linux.MAP, | |
| 542 | .linux => linux.MAP, | |
| 542 | 543 | .emscripten => packed struct(u32) { |
| 543 | 544 | TYPE: enum(u4) { |
| 544 | 545 | SHARED = 0x01, |
| ... | ... | @@ -683,7 +684,7 @@ pub const cc_t = u8; |
| 683 | 684 | |
| 684 | 685 | /// Indices into the `cc` array in the `termios` struct. |
| 685 | 686 | pub const V = switch (native_os) { |
| 686 | .linux => std.os.linux.V, | |
| 687 | .linux => linux.V, | |
| 687 | 688 | .macos, .ios, .tvos, .watchos, .netbsd, .openbsd => enum { |
| 688 | 689 | EOF, |
| 689 | 690 | EOL, |
| ... | ... | @@ -782,7 +783,7 @@ pub const V = switch (native_os) { |
| 782 | 783 | }; |
| 783 | 784 | |
| 784 | 785 | pub const NCCS = switch (native_os) { |
| 785 | .linux => std.os.linux.NCCS, | |
| 786 | .linux => linux.NCCS, | |
| 786 | 787 | .macos, .ios, .tvos, .watchos, .freebsd, .kfreebsd, .netbsd, .openbsd, .dragonfly => 20, |
| 787 | 788 | .haiku => 11, |
| 788 | 789 | .solaris, .illumos => 19, |
| ... | ... | @@ -791,7 +792,7 @@ pub const NCCS = switch (native_os) { |
| 791 | 792 | }; |
| 792 | 793 | |
| 793 | 794 | pub const termios = switch (native_os) { |
| 794 | .linux => std.os.linux.termios, | |
| 795 | .linux => linux.termios, | |
| 795 | 796 | .macos, .ios, .tvos, .watchos => extern struct { |
| 796 | 797 | iflag: tc_iflag_t, |
| 797 | 798 | oflag: tc_oflag_t, |
| ... | ... | @@ -841,7 +842,7 @@ pub const termios = switch (native_os) { |
| 841 | 842 | }; |
| 842 | 843 | |
| 843 | 844 | pub const tc_iflag_t = switch (native_os) { |
| 844 | .linux => std.os.linux.tc_iflag_t, | |
| 845 | .linux => linux.tc_iflag_t, | |
| 845 | 846 | .macos, .ios, .tvos, .watchos => packed struct(u64) { |
| 846 | 847 | IGNBRK: bool = false, |
| 847 | 848 | BRKINT: bool = false, |
| ... | ... | @@ -951,7 +952,7 @@ pub const tc_iflag_t = switch (native_os) { |
| 951 | 952 | }; |
| 952 | 953 | |
| 953 | 954 | pub const tc_oflag_t = switch (native_os) { |
| 954 | .linux => std.os.linux.tc_oflag_t, | |
| 955 | .linux => linux.tc_oflag_t, | |
| 955 | 956 | .macos, .ios, .tvos, .watchos => packed struct(u64) { |
| 956 | 957 | OPOST: bool = false, |
| 957 | 958 | ONLCR: bool = false, |
| ... | ... | @@ -1042,13 +1043,13 @@ pub const tc_oflag_t = switch (native_os) { |
| 1042 | 1043 | }; |
| 1043 | 1044 | |
| 1044 | 1045 | pub const CSIZE = switch (native_os) { |
| 1045 | .linux => std.os.linux.CSIZE, | |
| 1046 | .linux => linux.CSIZE, | |
| 1046 | 1047 | .haiku => enum(u1) { CS7, CS8 }, |
| 1047 | 1048 | else => enum(u2) { CS5, CS6, CS7, CS8 }, |
| 1048 | 1049 | }; |
| 1049 | 1050 | |
| 1050 | 1051 | pub const tc_cflag_t = switch (native_os) { |
| 1051 | .linux => std.os.linux.tc_cflag_t, | |
| 1052 | .linux => linux.tc_cflag_t, | |
| 1052 | 1053 | .macos, .ios, .tvos, .watchos => packed struct(u64) { |
| 1053 | 1054 | CIGNORE: bool = false, |
| 1054 | 1055 | _1: u5 = 0, |
| ... | ... | @@ -1184,7 +1185,7 @@ pub const tc_cflag_t = switch (native_os) { |
| 1184 | 1185 | }; |
| 1185 | 1186 | |
| 1186 | 1187 | pub const tc_lflag_t = switch (native_os) { |
| 1187 | .linux => std.os.linux.tc_lflag_t, | |
| 1188 | .linux => linux.tc_lflag_t, | |
| 1188 | 1189 | .macos, .ios, .tvos, .watchos => packed struct(u64) { |
| 1189 | 1190 | ECHOKE: bool = false, |
| 1190 | 1191 | ECHOE: bool = false, |
| ... | ... | @@ -1310,7 +1311,7 @@ pub const tc_lflag_t = switch (native_os) { |
| 1310 | 1311 | }; |
| 1311 | 1312 | |
| 1312 | 1313 | pub const speed_t = switch (native_os) { |
| 1313 | .linux => std.os.linux.speed_t, | |
| 1314 | .linux => linux.speed_t, | |
| 1314 | 1315 | .macos, .ios, .tvos, .watchos, .openbsd => enum(u64) { |
| 1315 | 1316 | B0 = 0, |
| 1316 | 1317 | B50 = 50, |
| ... | ... | @@ -1605,14 +1606,6 @@ pub const stat = switch (native_os) { |
| 1605 | 1606 | else => private.stat, |
| 1606 | 1607 | }; |
| 1607 | 1608 | |
| 1608 | pub fn getErrno(rc: anytype) c.E { | |
| 1609 | if (rc == -1) { | |
| 1610 | return @enumFromInt(c._errno().*); | |
| 1611 | } else { | |
| 1612 | return .SUCCESS; | |
| 1613 | } | |
| 1614 | } | |
| 1615 | ||
| 1616 | 1609 | pub extern "c" var environ: [*:null]?[*:0]u8; |
| 1617 | 1610 | |
| 1618 | 1611 | pub 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 | 1898 | pub const getcontext = if (builtin.target.isAndroid()) |
| 1906 | 1899 | @compileError("android bionic libc does not implement getcontext") |
| 1907 | 1900 | else if (native_os == .linux and builtin.target.isMusl()) |
| 1908 | std.os.linux.getcontext | |
| 1901 | linux.getcontext | |
| 1909 | 1902 | else |
| 1910 | 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 | 1905 | }.getcontext; |
| 1913 | 1906 | |
| 1914 | 1907 | pub 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 | 4 | const macho = std.macho; |
| 5 | 5 | const native_arch = builtin.target.cpu.arch; |
| 6 | 6 | const maxInt = std.math.maxInt; |
| 7 | const iovec_const = std.os.iovec_const; | |
| 7 | const iovec_const = std.posix.iovec_const; | |
| 8 | 8 | |
| 9 | 9 | pub const aarch64 = @import("darwin/aarch64.zig"); |
| 10 | 10 | pub const x86_64 = @import("darwin/x86_64.zig"); |
| ... | ... | @@ -2826,237 +2826,12 @@ pub extern "c" fn posix_spawnp( |
| 2826 | 2826 | env: [*:null]?[*:0]const u8, |
| 2827 | 2827 | ) c_int; |
| 2828 | 2828 | |
| 2829 | pub 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 | ||
| 3054 | 2829 | pub fn getKernError(err: kern_return_t) KernE { |
| 3055 | 2830 | return @as(KernE, @enumFromInt(@as(u32, @truncate(@as(usize, @intCast(err)))))); |
| 3056 | 2831 | } |
| 3057 | 2832 | |
| 3058 | pub fn unexpectedKernError(err: KernE) std.os.UnexpectedError { | |
| 3059 | if (std.os.unexpected_error_tracing) { | |
| 2833 | pub fn unexpectedKernError(err: KernE) std.posix.UnexpectedError { | |
| 2834 | if (std.posix.unexpected_error_tracing) { | |
| 3060 | 2835 | std.debug.print("unexpected error: {d}\n", .{@intFromEnum(err)}); |
| 3061 | 2836 | std.debug.dumpCurrentStackTrace(null); |
| 3062 | 2837 | } |
| ... | ... | @@ -3067,7 +2842,7 @@ pub const MachError = error{ |
| 3067 | 2842 | /// Not enough permissions held to perform the requested kernel |
| 3068 | 2843 | /// call. |
| 3069 | 2844 | PermissionDenied, |
| 3070 | } || std.os.UnexpectedError; | |
| 2845 | } || std.posix.UnexpectedError; | |
| 3071 | 2846 | |
| 3072 | 2847 | pub const MachTask = extern struct { |
| 3073 | 2848 | port: mach_port_name_t, |
| ... | ... | @@ -3076,8 +2851,8 @@ pub const MachTask = extern struct { |
| 3076 | 2851 | return self.port != TASK_NULL; |
| 3077 | 2852 | } |
| 3078 | 2853 | |
| 3079 | pub fn pidForTask(self: MachTask) MachError!std.os.pid_t { | |
| 3080 | var pid: std.os.pid_t = undefined; | |
| 2854 | pub fn pidForTask(self: MachTask) MachError!std.c.pid_t { | |
| 2855 | var pid: std.c.pid_t = undefined; | |
| 3081 | 2856 | switch (getKernError(pid_for_task(self.port, &pid))) { |
| 3082 | 2857 | .SUCCESS => return pid, |
| 3083 | 2858 | .FAILURE => return error.PermissionDenied, |
| ... | ... | @@ -3517,7 +3292,7 @@ pub const MachThread = extern struct { |
| 3517 | 3292 | } |
| 3518 | 3293 | }; |
| 3519 | 3294 | |
| 3520 | pub fn machTaskForPid(pid: std.os.pid_t) MachError!MachTask { | |
| 3295 | pub fn machTaskForPid(pid: std.c.pid_t) MachError!MachTask { | |
| 3521 | 3296 | var port: mach_port_name_t = undefined; |
| 3522 | 3297 | switch (getKernError(task_for_pid(mach_task_self(), pid, &port))) { |
| 3523 | 3298 | .SUCCESS => {}, |
lib/std/c/dragonfly.zig+1-1| ... | ... | @@ -2,7 +2,7 @@ const builtin = @import("builtin"); |
| 2 | 2 | const std = @import("../std.zig"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const maxInt = std.math.maxInt; |
| 5 | const iovec = std.os.iovec; | |
| 5 | const iovec = std.posix.iovec; | |
| 6 | 6 | |
| 7 | 7 | extern "c" threadlocal var errno: c_int; |
| 8 | 8 | pub fn _errno() *c_int { |
lib/std/c/freebsd.zig+2-2| ... | ... | @@ -2,8 +2,8 @@ const std = @import("../std.zig"); |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | const builtin = @import("builtin"); |
| 4 | 4 | const maxInt = std.math.maxInt; |
| 5 | const iovec = std.os.iovec; | |
| 6 | const iovec_const = std.os.iovec_const; | |
| 5 | const iovec = std.posix.iovec; | |
| 6 | const iovec_const = std.posix.iovec_const; | |
| 7 | 7 | |
| 8 | 8 | extern "c" fn __error() *c_int; |
| 9 | 9 | pub const _errno = __error; |
lib/std/c/haiku.zig+2-2| ... | ... | @@ -2,8 +2,8 @@ const std = @import("../std.zig"); |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | const builtin = @import("builtin"); |
| 4 | 4 | const maxInt = std.math.maxInt; |
| 5 | const iovec = std.os.iovec; | |
| 6 | const iovec_const = std.os.iovec_const; | |
| 5 | const iovec = std.posix.iovec; | |
| 6 | const iovec_const = std.posix.iovec_const; | |
| 7 | 7 | |
| 8 | 8 | extern "c" fn _errnop() *c_int; |
| 9 | 9 |
lib/std/c/linux.zig+2-2| ... | ... | @@ -3,8 +3,8 @@ const builtin = @import("builtin"); |
| 3 | 3 | const native_abi = builtin.abi; |
| 4 | 4 | const native_arch = builtin.cpu.arch; |
| 5 | 5 | const linux = std.os.linux; |
| 6 | const iovec = std.os.iovec; | |
| 7 | const iovec_const = std.os.iovec_const; | |
| 6 | const iovec = std.posix.iovec; | |
| 7 | const iovec_const = std.posix.iovec_const; | |
| 8 | 8 | const FILE = std.c.FILE; |
| 9 | 9 | |
| 10 | 10 | pub const AF = linux.AF; |
lib/std/c/netbsd.zig+2-2| ... | ... | @@ -2,8 +2,8 @@ const std = @import("../std.zig"); |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | const builtin = @import("builtin"); |
| 4 | 4 | const maxInt = std.math.maxInt; |
| 5 | const iovec = std.os.iovec; | |
| 6 | const iovec_const = std.os.iovec_const; | |
| 5 | const iovec = std.posix.iovec; | |
| 6 | const iovec_const = std.posix.iovec_const; | |
| 7 | 7 | const timezone = std.c.timezone; |
| 8 | 8 | const rusage = std.c.rusage; |
| 9 | 9 |
lib/std/c/openbsd.zig+2-2| ... | ... | @@ -2,8 +2,8 @@ const std = @import("../std.zig"); |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | const maxInt = std.math.maxInt; |
| 4 | 4 | const builtin = @import("builtin"); |
| 5 | const iovec = std.os.iovec; | |
| 6 | const iovec_const = std.os.iovec_const; | |
| 5 | const iovec = std.posix.iovec; | |
| 6 | const iovec_const = std.posix.iovec_const; | |
| 7 | 7 | |
| 8 | 8 | extern "c" fn __errno() *c_int; |
| 9 | 9 | pub const _errno = __errno; |
lib/std/c/solaris.zig+2-2| ... | ... | @@ -2,8 +2,8 @@ const std = @import("../std.zig"); |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | const builtin = @import("builtin"); |
| 4 | 4 | const maxInt = std.math.maxInt; |
| 5 | const iovec = std.os.iovec; | |
| 6 | const iovec_const = std.os.iovec_const; | |
| 5 | const iovec = std.posix.iovec; | |
| 6 | const iovec_const = std.posix.iovec_const; | |
| 7 | 7 | const timezone = std.c.timezone; |
| 8 | 8 | |
| 9 | 9 | extern "c" fn ___errno() *c_int; |
lib/std/c/wasi.zig+2| ... | ... | @@ -8,6 +8,8 @@ pub fn _errno() *c_int { |
| 8 | 8 | return &errno; |
| 9 | 9 | } |
| 10 | 10 | |
| 11 | pub const PATH_MAX = 4096; | |
| 12 | ||
| 11 | 13 | pub const mode_t = u32; |
| 12 | 14 | pub const time_t = i64; |
| 13 | 15 |
lib/std/child_process.zig+91-90| ... | ... | @@ -3,30 +3,31 @@ const builtin = @import("builtin"); |
| 3 | 3 | const unicode = std.unicode; |
| 4 | 4 | const io = std.io; |
| 5 | 5 | const fs = std.fs; |
| 6 | const os = std.os; | |
| 7 | 6 | const process = std.process; |
| 8 | 7 | const File = std.fs.File; |
| 9 | const windows = os.windows; | |
| 10 | const linux = os.linux; | |
| 8 | const windows = std.os.windows; | |
| 9 | const linux = std.os.linux; | |
| 10 | const posix = std.posix; | |
| 11 | 11 | const mem = std.mem; |
| 12 | 12 | const math = std.math; |
| 13 | 13 | const debug = std.debug; |
| 14 | 14 | const EnvMap = process.EnvMap; |
| 15 | 15 | const maxInt = std.math.maxInt; |
| 16 | 16 | const assert = std.debug.assert; |
| 17 | const native_os = builtin.os.tag; | |
| 17 | 18 | |
| 18 | 19 | pub const ChildProcess = struct { |
| 19 | pub const Id = switch (builtin.os.tag) { | |
| 20 | pub const Id = switch (native_os) { | |
| 20 | 21 | .windows => windows.HANDLE, |
| 21 | 22 | .wasi => void, |
| 22 | else => os.pid_t, | |
| 23 | else => posix.pid_t, | |
| 23 | 24 | }; |
| 24 | 25 | |
| 25 | 26 | /// Available after calling `spawn()`. This becomes `undefined` after calling `wait()`. |
| 26 | 27 | /// On Windows this is the hProcess. |
| 27 | 28 | /// On POSIX this is the pid. |
| 28 | 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, | |
| 30 | 31 | |
| 31 | 32 | allocator: mem.Allocator, |
| 32 | 33 | |
| ... | ... | @@ -46,10 +47,10 @@ pub const ChildProcess = struct { |
| 46 | 47 | stderr_behavior: StdIo, |
| 47 | 48 | |
| 48 | 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, | |
| 50 | 51 | |
| 51 | 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, | |
| 53 | 54 | |
| 54 | 55 | /// Set to change the current working directory when spawning the child process. |
| 55 | 56 | cwd: ?[]const u8, |
| ... | ... | @@ -58,7 +59,7 @@ pub const ChildProcess = struct { |
| 58 | 59 | /// Once that is done, `cwd` will be deprecated in favor of this field. |
| 59 | 60 | cwd_dir: ?fs.Dir = null, |
| 60 | 61 | |
| 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, | |
| 62 | 63 | |
| 63 | 64 | expand_arg0: Arg0Expand, |
| 64 | 65 | |
| ... | ... | @@ -87,7 +88,7 @@ pub const ChildProcess = struct { |
| 87 | 88 | /// Returns the peak resident set size of the child process, in bytes, |
| 88 | 89 | /// if available. |
| 89 | 90 | pub inline fn getMaxRss(rus: ResourceUsageStatistics) ?usize { |
| 90 | switch (builtin.os.tag) { | |
| 91 | switch (native_os) { | |
| 91 | 92 | .linux => { |
| 92 | 93 | if (rus.rusage) |ru| { |
| 93 | 94 | return @as(usize, @intCast(ru.maxrss)) * 1024; |
| ... | ... | @@ -114,14 +115,14 @@ pub const ChildProcess = struct { |
| 114 | 115 | } |
| 115 | 116 | } |
| 116 | 117 | |
| 117 | const rusage_init = switch (builtin.os.tag) { | |
| 118 | .linux, .macos, .ios => @as(?std.os.rusage, null), | |
| 118 | const rusage_init = switch (native_os) { | |
| 119 | .linux, .macos, .ios => @as(?posix.rusage, null), | |
| 119 | 120 | .windows => @as(?windows.VM_COUNTERS, null), |
| 120 | 121 | else => {}, |
| 121 | 122 | }; |
| 122 | 123 | }; |
| 123 | 124 | |
| 124 | pub const Arg0Expand = os.Arg0Expand; | |
| 125 | pub const Arg0Expand = posix.Arg0Expand; | |
| 125 | 126 | |
| 126 | 127 | pub const SpawnError = error{ |
| 127 | 128 | OutOfMemory, |
| ... | ... | @@ -136,9 +137,9 @@ pub const ChildProcess = struct { |
| 136 | 137 | /// Windows-only. `cwd` was provided, but the path did not exist when spawning the child process. |
| 137 | 138 | CurrentWorkingDirectoryUnlinked, |
| 138 | 139 | } || |
| 139 | os.ExecveError || | |
| 140 | os.SetIdError || | |
| 141 | os.ChangeCurDirError || | |
| 140 | posix.ExecveError || | |
| 141 | posix.SetIdError || | |
| 142 | posix.ChangeCurDirError || | |
| 142 | 143 | windows.CreateProcessError || |
| 143 | 144 | windows.GetProcessMemoryInfoError || |
| 144 | 145 | windows.WaitForSingleObjectError; |
| ... | ... | @@ -168,8 +169,8 @@ pub const ChildProcess = struct { |
| 168 | 169 | .term = null, |
| 169 | 170 | .env_map = null, |
| 170 | 171 | .cwd = null, |
| 171 | .uid = if (builtin.os.tag == .windows or builtin.os.tag == .wasi) {} else null, | |
| 172 | .gid = if (builtin.os.tag == .windows or builtin.os.tag == .wasi) {} else null, | |
| 172 | .uid = if (native_os == .windows or native_os == .wasi) {} else null, | |
| 173 | .gid = if (native_os == .windows or native_os == .wasi) {} else null, | |
| 173 | 174 | .stdin = null, |
| 174 | 175 | .stdout = null, |
| 175 | 176 | .stderr = null, |
| ... | ... | @@ -193,7 +194,7 @@ pub const ChildProcess = struct { |
| 193 | 194 | @compileError("the target operating system cannot spawn processes"); |
| 194 | 195 | } |
| 195 | 196 | |
| 196 | if (builtin.os.tag == .windows) { | |
| 197 | if (native_os == .windows) { | |
| 197 | 198 | return self.spawnWindows(); |
| 198 | 199 | } else { |
| 199 | 200 | return self.spawnPosix(); |
| ... | ... | @@ -207,7 +208,7 @@ pub const ChildProcess = struct { |
| 207 | 208 | |
| 208 | 209 | /// Forcibly terminates child process and then cleans up all resources. |
| 209 | 210 | pub fn kill(self: *ChildProcess) !Term { |
| 210 | if (builtin.os.tag == .windows) { | |
| 211 | if (native_os == .windows) { | |
| 211 | 212 | return self.killWindows(1); |
| 212 | 213 | } else { |
| 213 | 214 | return self.killPosix(); |
| ... | ... | @@ -241,7 +242,7 @@ pub const ChildProcess = struct { |
| 241 | 242 | self.cleanupStreams(); |
| 242 | 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 | 246 | error.ProcessNotFound => return error.AlreadyTerminated, |
| 246 | 247 | else => return err, |
| 247 | 248 | }; |
| ... | ... | @@ -251,7 +252,7 @@ pub const ChildProcess = struct { |
| 251 | 252 | |
| 252 | 253 | /// Blocks until child process terminates and then cleans up all resources. |
| 253 | 254 | pub fn wait(self: *ChildProcess) !Term { |
| 254 | const term = if (builtin.os.tag == .windows) | |
| 255 | const term = if (native_os == .windows) | |
| 255 | 256 | try self.waitWindows() |
| 256 | 257 | else |
| 257 | 258 | try self.waitPosix(); |
| ... | ... | @@ -318,7 +319,7 @@ pub const ChildProcess = struct { |
| 318 | 319 | stderr.* = fifoToOwnedArrayList(poller.fifo(.stderr)); |
| 319 | 320 | } |
| 320 | 321 | |
| 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 | 323 | StdoutStreamTooLong, |
| 323 | 324 | StderrStreamTooLong, |
| 324 | 325 | }; |
| ... | ... | @@ -396,19 +397,19 @@ pub const ChildProcess = struct { |
| 396 | 397 | self.resource_usage_statistics.rusage = try windows.GetProcessMemoryInfo(self.id); |
| 397 | 398 | } |
| 398 | 399 | |
| 399 | os.close(self.id); | |
| 400 | os.close(self.thread_handle); | |
| 400 | posix.close(self.id); | |
| 401 | posix.close(self.thread_handle); | |
| 401 | 402 | self.cleanupStreams(); |
| 402 | 403 | return result; |
| 403 | 404 | } |
| 404 | 405 | |
| 405 | 406 | fn waitUnwrapped(self: *ChildProcess) !void { |
| 406 | const res: os.WaitPidResult = res: { | |
| 407 | const res: posix.WaitPidResult = res: { | |
| 407 | 408 | if (self.request_resource_usage_statistics) { |
| 408 | switch (builtin.os.tag) { | |
| 409 | switch (native_os) { | |
| 409 | 410 | .linux, .macos, .ios => { |
| 410 | var ru: std.os.rusage = undefined; | |
| 411 | const res = os.wait4(self.id, 0, &ru); | |
| 411 | var ru: posix.rusage = undefined; | |
| 412 | const res = posix.wait4(self.id, 0, &ru); | |
| 412 | 413 | self.resource_usage_statistics.rusage = ru; |
| 413 | 414 | break :res res; |
| 414 | 415 | }, |
| ... | ... | @@ -416,7 +417,7 @@ pub const ChildProcess = struct { |
| 416 | 417 | } |
| 417 | 418 | } |
| 418 | 419 | |
| 419 | break :res os.waitpid(self.id, 0); | |
| 420 | break :res posix.waitpid(self.id, 0); | |
| 420 | 421 | }; |
| 421 | 422 | const status = res.status; |
| 422 | 423 | self.cleanupStreams(); |
| ... | ... | @@ -446,20 +447,20 @@ pub const ChildProcess = struct { |
| 446 | 447 | if (self.err_pipe) |err_pipe| { |
| 447 | 448 | defer destroyPipe(err_pipe); |
| 448 | 449 | |
| 449 | if (builtin.os.tag == .linux) { | |
| 450 | var fd = [1]std.os.pollfd{std.os.pollfd{ | |
| 450 | if (native_os == .linux) { | |
| 451 | var fd = [1]posix.pollfd{posix.pollfd{ | |
| 451 | 452 | .fd = err_pipe[0], |
| 452 | .events = std.os.POLL.IN, | |
| 453 | .events = posix.POLL.IN, | |
| 453 | 454 | .revents = undefined, |
| 454 | 455 | }}; |
| 455 | 456 | |
| 456 | 457 | // Check if the eventfd buffer stores a non-zero value by polling |
| 457 | 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; | |
| 459 | 460 | |
| 460 | 461 | // According to eventfd(2) the descriptor is readable if the counter |
| 461 | 462 | // 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 | 464 | const err_int = try readIntFd(err_pipe[0]); |
| 464 | 465 | return @as(SpawnError, @errorCast(@errorFromInt(err_int))); |
| 465 | 466 | } |
| ... | ... | @@ -483,36 +484,36 @@ pub const ChildProcess = struct { |
| 483 | 484 | } |
| 484 | 485 | |
| 485 | 486 | fn statusToTerm(status: u32) Term { |
| 486 | return if (os.W.IFEXITED(status)) | |
| 487 | Term{ .Exited = os.W.EXITSTATUS(status) } | |
| 488 | else if (os.W.IFSIGNALED(status)) | |
| 489 | Term{ .Signal = os.W.TERMSIG(status) } | |
| 490 | else if (os.W.IFSTOPPED(status)) | |
| 491 | Term{ .Stopped = os.W.STOPSIG(status) } | |
| 487 | return if (posix.W.IFEXITED(status)) | |
| 488 | Term{ .Exited = posix.W.EXITSTATUS(status) } | |
| 489 | else if (posix.W.IFSIGNALED(status)) | |
| 490 | Term{ .Signal = posix.W.TERMSIG(status) } | |
| 491 | else if (posix.W.IFSTOPPED(status)) | |
| 492 | Term{ .Stopped = posix.W.STOPSIG(status) } | |
| 492 | 493 | else |
| 493 | 494 | Term{ .Unknown = status }; |
| 494 | 495 | } |
| 495 | 496 | |
| 496 | 497 | fn spawnPosix(self: *ChildProcess) SpawnError!void { |
| 497 | const pipe_flags: os.O = .{}; | |
| 498 | const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined; | |
| 498 | const pipe_flags: posix.O = .{}; | |
| 499 | const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try posix.pipe2(pipe_flags) else undefined; | |
| 499 | 500 | errdefer if (self.stdin_behavior == StdIo.Pipe) { |
| 500 | 501 | destroyPipe(stdin_pipe); |
| 501 | 502 | }; |
| 502 | 503 | |
| 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 | 505 | errdefer if (self.stdout_behavior == StdIo.Pipe) { |
| 505 | 506 | destroyPipe(stdout_pipe); |
| 506 | 507 | }; |
| 507 | 508 | |
| 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 | 510 | errdefer if (self.stderr_behavior == StdIo.Pipe) { |
| 510 | 511 | destroyPipe(stderr_pipe); |
| 511 | 512 | }; |
| 512 | 513 | |
| 513 | 514 | const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); |
| 514 | 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 | 517 | error.PathAlreadyExists => unreachable, |
| 517 | 518 | error.NoSpaceLeft => unreachable, |
| 518 | 519 | error.FileTooBig => unreachable, |
| ... | ... | @@ -526,7 +527,7 @@ pub const ChildProcess = struct { |
| 526 | 527 | else |
| 527 | 528 | undefined; |
| 528 | 529 | defer { |
| 529 | if (any_ignore) os.close(dev_null_fd); | |
| 530 | if (any_ignore) posix.close(dev_null_fd); | |
| 530 | 531 | } |
| 531 | 532 | |
| 532 | 533 | var arena_allocator = std.heap.ArenaAllocator.init(self.allocator); |
| ... | ... | @@ -554,7 +555,7 @@ pub const ChildProcess = struct { |
| 554 | 555 | } else if (builtin.output_mode == .Exe) { |
| 555 | 556 | // Then we have Zig start code and this works. |
| 556 | 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 | 559 | } else { |
| 559 | 560 | // TODO come up with a solution for this. |
| 560 | 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 | 565 | // This pipe is used to communicate errors between the time of fork |
| 565 | 566 | // and execve from the child process to the parent process. |
| 566 | 567 | const err_pipe = blk: { |
| 567 | if (builtin.os.tag == .linux) { | |
| 568 | const fd = try os.eventfd(0, linux.EFD.CLOEXEC); | |
| 568 | if (native_os == .linux) { | |
| 569 | const fd = try posix.eventfd(0, linux.EFD.CLOEXEC); | |
| 569 | 570 | // There's no distinction between the readable and the writeable |
| 570 | 571 | // end with eventfd |
| 571 | break :blk [2]os.fd_t{ fd, fd }; | |
| 572 | break :blk [2]posix.fd_t{ fd, fd }; | |
| 572 | 573 | } else { |
| 573 | break :blk try os.pipe2(.{ .CLOEXEC = true }); | |
| 574 | break :blk try posix.pipe2(.{ .CLOEXEC = true }); | |
| 574 | 575 | } |
| 575 | 576 | }; |
| 576 | 577 | errdefer destroyPipe(err_pipe); |
| 577 | 578 | |
| 578 | const pid_result = try os.fork(); | |
| 579 | const pid_result = try posix.fork(); | |
| 579 | 580 | if (pid_result == 0) { |
| 580 | 581 | // we are the child |
| 581 | setUpChildIo(self.stdin_behavior, stdin_pipe[0], os.STDIN_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); | |
| 582 | setUpChildIo(self.stdout_behavior, stdout_pipe[1], os.STDOUT_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); | |
| 583 | setUpChildIo(self.stderr_behavior, stderr_pipe[1], os.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); | |
| 582 | setUpChildIo(self.stdin_behavior, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); | |
| 583 | setUpChildIo(self.stdout_behavior, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); | |
| 584 | setUpChildIo(self.stderr_behavior, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkChildErrReport(err_pipe[1], err); | |
| 584 | 585 | |
| 585 | 586 | if (self.stdin_behavior == .Pipe) { |
| 586 | os.close(stdin_pipe[0]); | |
| 587 | os.close(stdin_pipe[1]); | |
| 587 | posix.close(stdin_pipe[0]); | |
| 588 | posix.close(stdin_pipe[1]); | |
| 588 | 589 | } |
| 589 | 590 | if (self.stdout_behavior == .Pipe) { |
| 590 | os.close(stdout_pipe[0]); | |
| 591 | os.close(stdout_pipe[1]); | |
| 591 | posix.close(stdout_pipe[0]); | |
| 592 | posix.close(stdout_pipe[1]); | |
| 592 | 593 | } |
| 593 | 594 | if (self.stderr_behavior == .Pipe) { |
| 594 | os.close(stderr_pipe[0]); | |
| 595 | os.close(stderr_pipe[1]); | |
| 595 | posix.close(stderr_pipe[0]); | |
| 596 | posix.close(stderr_pipe[1]); | |
| 596 | 597 | } |
| 597 | 598 | |
| 598 | 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 | 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 | } |
| 603 | 604 | |
| 604 | 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 | } |
| 607 | 608 | |
| 608 | 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 | } |
| 611 | 612 | |
| 612 | 613 | const err = switch (self.expand_arg0) { |
| 613 | .expand => os.execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_buf.ptr, envp), | |
| 614 | .no_expand => os.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp), | |
| 614 | .expand => posix.execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_buf.ptr, envp), | |
| 615 | .no_expand => posix.execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_buf.ptr, envp), | |
| 615 | 616 | }; |
| 616 | 617 | forkChildErrReport(err_pipe[1], err); |
| 617 | 618 | } |
| 618 | 619 | |
| 619 | 620 | // we are the parent |
| 620 | const pid = @as(i32, @intCast(pid_result)); | |
| 621 | const pid: i32 = @intCast(pid_result); | |
| 621 | 622 | if (self.stdin_behavior == StdIo.Pipe) { |
| 622 | 623 | self.stdin = File{ .handle = stdin_pipe[1] }; |
| 623 | 624 | } else { |
| ... | ... | @@ -639,13 +640,13 @@ pub const ChildProcess = struct { |
| 639 | 640 | self.term = null; |
| 640 | 641 | |
| 641 | 642 | if (self.stdin_behavior == StdIo.Pipe) { |
| 642 | os.close(stdin_pipe[0]); | |
| 643 | posix.close(stdin_pipe[0]); | |
| 643 | 644 | } |
| 644 | 645 | if (self.stdout_behavior == StdIo.Pipe) { |
| 645 | os.close(stdout_pipe[1]); | |
| 646 | posix.close(stdout_pipe[1]); | |
| 646 | 647 | } |
| 647 | 648 | if (self.stderr_behavior == StdIo.Pipe) { |
| 648 | os.close(stderr_pipe[1]); | |
| 649 | posix.close(stderr_pipe[1]); | |
| 649 | 650 | } |
| 650 | 651 | } |
| 651 | 652 | |
| ... | ... | @@ -679,7 +680,7 @@ pub const ChildProcess = struct { |
| 679 | 680 | else |
| 680 | 681 | undefined; |
| 681 | 682 | defer { |
| 682 | if (any_ignore) os.close(nul_handle); | |
| 683 | if (any_ignore) posix.close(nul_handle); | |
| 683 | 684 | } |
| 684 | 685 | |
| 685 | 686 | var g_hChildStd_IN_Rd: ?windows.HANDLE = null; |
| ... | ... | @@ -821,8 +822,8 @@ pub const ChildProcess = struct { |
| 821 | 822 | defer self.allocator.free(cmd_line_w); |
| 822 | 823 | |
| 823 | 824 | run: { |
| 824 | const PATH: [:0]const u16 = std.os.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse &[_:0]u16{}; | |
| 825 | const PATHEXT: [:0]const u16 = std.os.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATHEXT")) orelse &[_:0]u16{}; | |
| 825 | const PATH: [:0]const u16 = std.process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse &[_:0]u16{}; | |
| 826 | const PATHEXT: [:0]const u16 = std.process.getenvW(unicode.utf8ToUtf16LeStringLiteral("PATHEXT")) orelse &[_:0]u16{}; | |
| 826 | 827 | |
| 827 | 828 | var app_buf = std.ArrayListUnmanaged(u16){}; |
| 828 | 829 | defer app_buf.deinit(self.allocator); |
| ... | ... | @@ -905,22 +906,22 @@ pub const ChildProcess = struct { |
| 905 | 906 | self.term = null; |
| 906 | 907 | |
| 907 | 908 | if (self.stdin_behavior == StdIo.Pipe) { |
| 908 | os.close(g_hChildStd_IN_Rd.?); | |
| 909 | posix.close(g_hChildStd_IN_Rd.?); | |
| 909 | 910 | } |
| 910 | 911 | if (self.stderr_behavior == StdIo.Pipe) { |
| 911 | os.close(g_hChildStd_ERR_Wr.?); | |
| 912 | posix.close(g_hChildStd_ERR_Wr.?); | |
| 912 | 913 | } |
| 913 | 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 | } |
| 917 | 918 | |
| 918 | 919 | fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void { |
| 919 | 920 | switch (stdio) { |
| 920 | .Pipe => try os.dup2(pipe_fd, std_fileno), | |
| 921 | .Close => os.close(std_fileno), | |
| 921 | .Pipe => try posix.dup2(pipe_fd, std_fileno), | |
| 922 | .Close => posix.close(std_fileno), | |
| 922 | 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 | 988 | |
| 988 | 989 | // This 2048 is arbitrary, we just want it to be large enough to get multiple FILE_DIRECTORY_INFORMATION entries |
| 989 | 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 | 992 | const file_info_maximum_single_entry_size = @sizeOf(windows.FILE_DIRECTORY_INFORMATION) + (windows.NAME_MAX * 2); |
| 992 | 993 | if (file_information_buf.len < file_info_maximum_single_entry_size) { |
| 993 | 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 | 1392 | } |
| 1392 | 1393 | |
| 1393 | 1394 | fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void { |
| 1394 | if (rd) |h| os.close(h); | |
| 1395 | if (wr) |h| os.close(h); | |
| 1395 | if (rd) |h| posix.close(h); | |
| 1396 | if (wr) |h| posix.close(h); | |
| 1396 | 1397 | } |
| 1397 | 1398 | |
| 1398 | 1399 | fn 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 | 1444 | else => |err| return windows.unexpectedError(err), |
| 1444 | 1445 | } |
| 1445 | 1446 | } |
| 1446 | errdefer os.close(read_handle); | |
| 1447 | errdefer posix.close(read_handle); | |
| 1447 | 1448 | |
| 1448 | 1449 | var sattr_copy = sattr.*; |
| 1449 | 1450 | const write_handle = windows.kernel32.CreateFileW( |
| ... | ... | @@ -1460,7 +1461,7 @@ fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *cons |
| 1460 | 1461 | else => |err| return windows.unexpectedError(err), |
| 1461 | 1462 | } |
| 1462 | 1463 | } |
| 1463 | errdefer os.close(write_handle); | |
| 1464 | errdefer posix.close(write_handle); | |
| 1464 | 1465 | |
| 1465 | 1466 | try windows.SetHandleInformation(read_handle, windows.HANDLE_FLAG_INHERIT, 0); |
| 1466 | 1467 | |
| ... | ... | @@ -1468,9 +1469,9 @@ fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *cons |
| 1468 | 1469 | wr.* = write_handle; |
| 1469 | 1470 | } |
| 1470 | 1471 | |
| 1471 | fn destroyPipe(pipe: [2]os.fd_t) void { | |
| 1472 | os.close(pipe[0]); | |
| 1473 | if (pipe[0] != pipe[1]) os.close(pipe[1]); | |
| 1472 | fn destroyPipe(pipe: [2]posix.fd_t) void { | |
| 1473 | posix.close(pipe[0]); | |
| 1474 | if (pipe[0] != pipe[1]) posix.close(pipe[1]); | |
| 1474 | 1475 | } |
| 1475 | 1476 | |
| 1476 | 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 | 1486 | // The _exit(2) function does nothing but make the exit syscall, unlike exit(3) |
| 1486 | 1487 | std.c._exit(1); |
| 1487 | 1488 | } |
| 1488 | os.exit(1); | |
| 1489 | posix.exit(1); | |
| 1489 | 1490 | } |
| 1490 | 1491 | |
| 1491 | 1492 | const 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 | 125 | cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len); |
| 126 | 126 | } |
| 127 | 127 | |
| 128 | const RescanWindowsError = Allocator.Error || ParseCertError || std.os.UnexpectedError || error{FileNotFound}; | |
| 128 | const RescanWindowsError = Allocator.Error || ParseCertError || std.posix.UnexpectedError || error{FileNotFound}; | |
| 129 | 129 | |
| 130 | 130 | fn rescanWindows(cb: *Bundle, gpa: Allocator) RescanWindowsError!void { |
| 131 | 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 | 42 | |
| 43 | 43 | const table_header = try reader.readStructEndian(TableHeader, .big); |
| 44 | 44 | |
| 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 | 46 | continue; |
| 47 | 47 | } |
| 48 | 48 |
lib/std/crypto/tlcsprng.zig+9-8| ... | ... | @@ -6,7 +6,8 @@ |
| 6 | 6 | const std = @import("std"); |
| 7 | 7 | const builtin = @import("builtin"); |
| 8 | 8 | const mem = std.mem; |
| 9 | const os = std.os; | |
| 9 | const native_os = builtin.os.tag; | |
| 10 | const posix = std.posix; | |
| 10 | 11 | |
| 11 | 12 | /// We use this as a layer of indirection because global const pointers cannot |
| 12 | 13 | /// point to thread-local variables. |
| ... | ... | @@ -15,7 +16,7 @@ pub const interface = std.Random{ |
| 15 | 16 | .fillFn = tlsCsprngFill, |
| 16 | 17 | }; |
| 17 | 18 | |
| 18 | const os_has_fork = switch (builtin.os.tag) { | |
| 19 | const os_has_fork = switch (native_os) { | |
| 19 | 20 | .dragonfly, |
| 20 | 21 | .freebsd, |
| 21 | 22 | .ios, |
| ... | ... | @@ -41,7 +42,7 @@ const maybe_have_wipe_on_fork = builtin.os.isAtLeast(.linux, .{ |
| 41 | 42 | .minor = 14, |
| 42 | 43 | .patch = 0, |
| 43 | 44 | }) orelse true; |
| 44 | const is_haiku = builtin.os.tag == .haiku; | |
| 45 | const is_haiku = native_os == .haiku; | |
| 45 | 46 | |
| 46 | 47 | const Rng = std.Random.DefaultCsprng; |
| 47 | 48 | |
| ... | ... | @@ -79,10 +80,10 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void { |
| 79 | 80 | if (want_fork_safety and maybe_have_wipe_on_fork or is_haiku) { |
| 80 | 81 | // Allocate a per-process page, madvise operates with page |
| 81 | 82 | // granularity. |
| 82 | wipe_mem = os.mmap( | |
| 83 | wipe_mem = posix.mmap( | |
| 83 | 84 | null, |
| 84 | 85 | @sizeOf(Context), |
| 85 | os.PROT.READ | os.PROT.WRITE, | |
| 86 | posix.PROT.READ | posix.PROT.WRITE, | |
| 86 | 87 | .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, |
| 87 | 88 | -1, |
| 88 | 89 | 0, |
| ... | ... | @@ -115,11 +116,11 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void { |
| 115 | 116 | // Qemu user-mode emulation ignores any valid/invalid madvise |
| 116 | 117 | // hint and returns success. Check if this is the case by |
| 117 | 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 | 120 | break :wof; |
| 120 | 121 | } else |_| {} |
| 121 | 122 | |
| 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 | 124 | return initAndFill(buffer); |
| 124 | 125 | } else |_| {} |
| 125 | 126 | } |
| ... | ... | @@ -164,7 +165,7 @@ fn fillWithCsprng(buffer: []u8) void { |
| 164 | 165 | } |
| 165 | 166 | |
| 166 | 167 | pub 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 | } |
| 169 | 170 | |
| 170 | 171 | fn initAndFill(buffer: []u8) void { |
lib/std/crypto/tls/Client.zig+14-14| ... | ... | @@ -62,7 +62,7 @@ pub const StreamInterface = struct { |
| 62 | 62 | /// The `iovecs` parameter is mutable because so that function may to |
| 63 | 63 | /// mutate the fields in order to handle partial reads from the underlying |
| 64 | 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 | 66 | _ = .{ this, iovecs }; |
| 67 | 67 | @panic("unimplemented"); |
| 68 | 68 | } |
| ... | ... | @@ -72,7 +72,7 @@ pub const StreamInterface = struct { |
| 72 | 72 | |
| 73 | 73 | /// Returns the number of bytes read, which may be less than the buffer |
| 74 | 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 | 76 | _ = .{ this, iovecs }; |
| 77 | 77 | @panic("unimplemented"); |
| 78 | 78 | } |
| ... | ... | @@ -81,7 +81,7 @@ pub const StreamInterface = struct { |
| 81 | 81 | /// space provided, indicating end-of-stream. |
| 82 | 82 | /// The `iovecs` parameter is mutable in case this function needs to mutate |
| 83 | 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 | 85 | // This can be implemented in terms of writev, or specialized if desired. |
| 86 | 86 | _ = .{ this, iovecs }; |
| 87 | 87 | @panic("unimplemented"); |
| ... | ... | @@ -215,7 +215,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In |
| 215 | 215 | } ++ int2(@intCast(out_handshake.len + host_len)) ++ out_handshake; |
| 216 | 216 | |
| 217 | 217 | { |
| 218 | var iovecs = [_]std.os.iovec_const{ | |
| 218 | var iovecs = [_]std.posix.iovec_const{ | |
| 219 | 219 | .{ |
| 220 | 220 | .iov_base = &plaintext_header, |
| 221 | 221 | .iov_len = plaintext_header.len, |
| ... | ... | @@ -677,7 +677,7 @@ pub fn init(stream: anytype, ca_bundle: Certificate.Bundle, host: []const u8) In |
| 677 | 677 | P.AEAD.encrypt(ciphertext, auth_tag, &out_cleartext, ad, nonce, p.client_handshake_key); |
| 678 | 678 | |
| 679 | 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 | 681 | .iov_base = &both_msgs, |
| 682 | 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 | 755 | /// TLS session, or a truncation attack. |
| 756 | 756 | pub fn writeEnd(c: *Client, stream: anytype, bytes: []const u8, end: bool) !usize { |
| 757 | 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 | 759 | var prepared = prepareCiphertextRecord(c, &iovecs_buf, &ciphertext_buf, bytes, .application_data); |
| 760 | 760 | if (end) { |
| 761 | 761 | prepared.iovec_end += prepareCiphertextRecord( |
| ... | ... | @@ -796,7 +796,7 @@ pub fn writeEnd(c: *Client, stream: anytype, bytes: []const u8, end: bool) !usiz |
| 796 | 796 | |
| 797 | 797 | fn prepareCiphertextRecord( |
| 798 | 798 | c: *Client, |
| 799 | iovecs: []std.os.iovec_const, | |
| 799 | iovecs: []std.posix.iovec_const, | |
| 800 | 800 | ciphertext_buf: []u8, |
| 801 | 801 | bytes: []const u8, |
| 802 | 802 | inner_content_type: tls.ContentType, |
| ... | ... | @@ -885,7 +885,7 @@ pub fn eof(c: Client) bool { |
| 885 | 885 | /// If the number read is less than `len` it means the stream reached the end. |
| 886 | 886 | /// Reaching the end of the stream is not an error condition. |
| 887 | 887 | pub 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 | 889 | return readvAtLeast(c, stream, &iovecs, len); |
| 890 | 890 | } |
| 891 | 891 | |
| ... | ... | @@ -908,7 +908,7 @@ pub fn readAll(c: *Client, stream: anytype, buffer: []u8) !usize { |
| 908 | 908 | /// stream is not an error condition. |
| 909 | 909 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 910 | 910 | /// order to handle partial reads from the underlying stream layer. |
| 911 | pub fn readv(c: *Client, stream: anytype, iovecs: []std.os.iovec) !usize { | |
| 911 | pub fn readv(c: *Client, stream: anytype, iovecs: []std.posix.iovec) !usize { | |
| 912 | 912 | return readvAtLeast(c, stream, iovecs, 1); |
| 913 | 913 | } |
| 914 | 914 | |
| ... | ... | @@ -919,7 +919,7 @@ pub fn readv(c: *Client, stream: anytype, iovecs: []std.os.iovec) !usize { |
| 919 | 919 | /// Reaching the end of the stream is not an error condition. |
| 920 | 920 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 921 | 921 | /// order to handle partial reads from the underlying stream layer. |
| 922 | pub fn readvAtLeast(c: *Client, stream: anytype, iovecs: []std.os.iovec, len: usize) !usize { | |
| 922 | pub fn readvAtLeast(c: *Client, stream: anytype, iovecs: []std.posix.iovec, len: usize) !usize { | |
| 923 | 923 | if (c.eof()) return 0; |
| 924 | 924 | |
| 925 | 925 | var off_i: usize = 0; |
| ... | ... | @@ -945,7 +945,7 @@ pub fn readvAtLeast(c: *Client, stream: anytype, iovecs: []std.os.iovec, len: us |
| 945 | 945 | /// function asserts that `eof()` is `false`. |
| 946 | 946 | /// See `readv` for a higher level function that has the same, familiar API as |
| 947 | 947 | /// other read functions, such as `std.fs.File.read`. |
| 948 | pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.os.iovec) !usize { | |
| 948 | pub fn readvAdvanced(c: *Client, stream: anytype, iovecs: []const std.posix.iovec) !usize { | |
| 949 | 949 | var vp: VecPut = .{ .iovecs = iovecs }; |
| 950 | 950 | |
| 951 | 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 | 998 | c.partial_cleartext_idx = 0; |
| 999 | 999 | const first_iov = c.partially_read_buffer[c.partial_ciphertext_end..]; |
| 1000 | 1000 | |
| 1001 | var ask_iovecs_buf: [2]std.os.iovec = .{ | |
| 1001 | var ask_iovecs_buf: [2]std.posix.iovec = .{ | |
| 1002 | 1002 | .{ |
| 1003 | 1003 | .iov_base = first_iov.ptr, |
| 1004 | 1004 | .iov_len = first_iov.len, |
| ... | ... | @@ -1352,7 +1352,7 @@ fn SchemeEddsa(comptime scheme: tls.SignatureScheme) type { |
| 1352 | 1352 | |
| 1353 | 1353 | /// Abstraction for sending multiple byte buffers to a slice of iovecs. |
| 1354 | 1354 | const VecPut = struct { |
| 1355 | iovecs: []const std.os.iovec, | |
| 1355 | iovecs: []const std.posix.iovec, | |
| 1356 | 1356 | idx: usize = 0, |
| 1357 | 1357 | off: usize = 0, |
| 1358 | 1358 | total: usize = 0, |
| ... | ... | @@ -1413,7 +1413,7 @@ const VecPut = struct { |
| 1413 | 1413 | }; |
| 1414 | 1414 | |
| 1415 | 1415 | /// Limit iovecs to a specific byte size. |
| 1416 | fn limitVecs(iovecs: []std.os.iovec, len: usize) []std.os.iovec { | |
| 1416 | fn limitVecs(iovecs: []std.posix.iovec, len: usize) []std.posix.iovec { | |
| 1417 | 1417 | var bytes_left: usize = len; |
| 1418 | 1418 | for (iovecs, 0..) |*iovec, vec_i| { |
| 1419 | 1419 | if (bytes_left <= iovec.iov_len) { |
lib/std/debug.zig+57-60| ... | ... | @@ -3,7 +3,7 @@ const builtin = @import("builtin"); |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const mem = std.mem; |
| 5 | 5 | const io = std.io; |
| 6 | const os = std.os; | |
| 6 | const posix = std.posix; | |
| 7 | 7 | const fs = std.fs; |
| 8 | 8 | const testing = std.testing; |
| 9 | 9 | const elf = std.elf; |
| ... | ... | @@ -34,7 +34,7 @@ pub const sys_can_stack_trace = switch (builtin.cpu.arch) { |
| 34 | 34 | // "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address". |
| 35 | 35 | .wasm32, |
| 36 | 36 | .wasm64, |
| 37 | => builtin.os.tag == .emscripten, | |
| 37 | => native_os == .emscripten, | |
| 38 | 38 | |
| 39 | 39 | // `@returnAddress()` is unsupported in LLVM 13. |
| 40 | 40 | .bpfel, |
| ... | ... | @@ -192,8 +192,8 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { |
| 192 | 192 | } |
| 193 | 193 | } |
| 194 | 194 | |
| 195 | pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and | |
| 196 | (builtin.os.tag != .linux or switch (builtin.cpu.arch) { | |
| 195 | pub const have_ucontext = @hasDecl(posix.system, "ucontext_t") and | |
| 196 | (native_os != .linux or switch (builtin.cpu.arch) { | |
| 197 | 197 | .mips, .mipsel, .mips64, .mips64el, .riscv64 => false, |
| 198 | 198 | else => true, |
| 199 | 199 | }); |
| ... | ... | @@ -203,9 +203,9 @@ pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and |
| 203 | 203 | /// use internal pointers within this structure. To make a copy, use `copyContext`. |
| 204 | 204 | pub const ThreadContext = blk: { |
| 205 | 205 | if (native_os == .windows) { |
| 206 | break :blk std.os.windows.CONTEXT; | |
| 206 | break :blk windows.CONTEXT; | |
| 207 | 207 | } else if (have_ucontext) { |
| 208 | break :blk os.ucontext_t; | |
| 208 | break :blk posix.ucontext_t; | |
| 209 | 209 | } else { |
| 210 | 210 | break :blk void; |
| 211 | 211 | } |
| ... | ... | @@ -228,9 +228,9 @@ pub fn relocateContext(context: *ThreadContext) void { |
| 228 | 228 | }; |
| 229 | 229 | } |
| 230 | 230 | |
| 231 | pub const have_getcontext = @hasDecl(os.system, "getcontext") and | |
| 232 | builtin.os.tag != .openbsd and | |
| 233 | (builtin.os.tag != .linux or switch (builtin.cpu.arch) { | |
| 231 | pub const have_getcontext = @hasDecl(posix.system, "getcontext") and | |
| 232 | native_os != .openbsd and | |
| 233 | (native_os != .linux or switch (builtin.cpu.arch) { | |
| 234 | 234 | .x86, |
| 235 | 235 | .x86_64, |
| 236 | 236 | => true, |
| ... | ... | @@ -249,7 +249,7 @@ pub inline fn getContext(context: *ThreadContext) bool { |
| 249 | 249 | return true; |
| 250 | 250 | } |
| 251 | 251 | |
| 252 | const result = have_getcontext and os.system.getcontext(context) == 0; | |
| 252 | const result = have_getcontext and posix.system.getcontext(context) == 0; | |
| 253 | 253 | if (native_os == .macos) { |
| 254 | 254 | assert(context.mcsize == @sizeOf(std.c.mcontext_t)); |
| 255 | 255 | |
| ... | ... | @@ -470,12 +470,12 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize |
| 470 | 470 | |
| 471 | 471 | const stderr = io.getStdErr().writer(); |
| 472 | 472 | if (builtin.single_threaded) { |
| 473 | stderr.print("panic: ", .{}) catch os.abort(); | |
| 473 | stderr.print("panic: ", .{}) catch posix.abort(); | |
| 474 | 474 | } else { |
| 475 | 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 | 479 | if (trace) |t| { |
| 480 | 480 | dumpStackTrace(t.*); |
| 481 | 481 | } |
| ... | ... | @@ -491,14 +491,14 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize |
| 491 | 491 | // we're still holding the mutex but that's fine as we're going to |
| 492 | 492 | // call abort() |
| 493 | 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 | 496 | else => { |
| 497 | 497 | // Panicked while printing "Panicked during a panic." |
| 498 | 498 | }, |
| 499 | 499 | }; |
| 500 | 500 | |
| 501 | os.abort(); | |
| 501 | posix.abort(); | |
| 502 | 502 | } |
| 503 | 503 | |
| 504 | 504 | /// Must be called only after adding 1 to `panicking`. There are three callsites. |
| ... | ... | @@ -584,7 +584,7 @@ pub const StackIterator = struct { |
| 584 | 584 | }; |
| 585 | 585 | } |
| 586 | 586 | |
| 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 | 588 | // The implementation of DWARF unwinding on aarch64-macos is not complete. However, Apple mandates that |
| 589 | 589 | // the frame pointer register is always used, so on this platform we can safely use the FP-based unwinder. |
| 590 | 590 | if (comptime builtin.target.isDarwin() and native_arch == .aarch64) { |
| ... | ... | @@ -668,12 +668,11 @@ pub const StackIterator = struct { |
| 668 | 668 | const aligned_memory = @as([*]align(mem.page_size) u8, @ptrFromInt(aligned_address))[0..mem.page_size]; |
| 669 | 669 | |
| 670 | 670 | if (native_os == .windows) { |
| 671 | const w = os.windows; | |
| 672 | var memory_info: w.MEMORY_BASIC_INFORMATION = undefined; | |
| 671 | var memory_info: windows.MEMORY_BASIC_INFORMATION = undefined; | |
| 673 | 672 | |
| 674 | 673 | // The only error this function can throw is ERROR_INVALID_PARAMETER. |
| 675 | 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 | 676 | return false; |
| 678 | 677 | }; |
| 679 | 678 | |
| ... | ... | @@ -683,17 +682,15 @@ pub const StackIterator = struct { |
| 683 | 682 | } |
| 684 | 683 | |
| 685 | 684 | // 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 | 686 | return false; |
| 688 | 687 | } |
| 689 | 688 | |
| 690 | 689 | return true; |
| 691 | } else if (@hasDecl(os.system, "msync") and native_os != .wasi and native_os != .emscripten) { | |
| 692 | os.msync(aligned_memory, os.MSF.ASYNC) catch |err| { | |
| 690 | } else if (@hasDecl(posix.system, "msync") and native_os != .wasi and native_os != .emscripten) { | |
| 691 | posix.msync(aligned_memory, posix.MSF.ASYNC) catch |err| { | |
| 693 | 692 | switch (err) { |
| 694 | os.MSyncError.UnmappedMemory => { | |
| 695 | return false; | |
| 696 | }, | |
| 693 | error.UnmappedMemory => return false, | |
| 697 | 694 | else => unreachable, |
| 698 | 695 | } |
| 699 | 696 | }; |
| ... | ... | @@ -1296,7 +1293,7 @@ pub fn readElfDebugInfo( |
| 1296 | 1293 | } |
| 1297 | 1294 | |
| 1298 | 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; | |
| 1300 | 1297 | |
| 1301 | 1298 | // <global debug directory>/<absolute folder of current binary>/<gnu_debuglink> |
| 1302 | 1299 | for (global_debug_directories) |global_directory| { |
| ... | ... | @@ -1651,15 +1648,15 @@ fn mapWholeFile(file: File) ![]align(mem.page_size) const u8 { |
| 1651 | 1648 | defer file.close(); |
| 1652 | 1649 | |
| 1653 | 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 | 1652 | null, |
| 1656 | 1653 | file_len, |
| 1657 | os.PROT.READ, | |
| 1654 | posix.PROT.READ, | |
| 1658 | 1655 | .{ .TYPE = .SHARED }, |
| 1659 | 1656 | file.handle, |
| 1660 | 1657 | 0, |
| 1661 | 1658 | ); |
| 1662 | errdefer os.munmap(mapped_mem); | |
| 1659 | errdefer posix.munmap(mapped_mem); | |
| 1663 | 1660 | |
| 1664 | 1661 | return mapped_mem; |
| 1665 | 1662 | } |
| ... | ... | @@ -1997,8 +1994,8 @@ pub const DebugInfo = struct { |
| 1997 | 1994 | } = .{ .address = address }; |
| 1998 | 1995 | const CtxTy = @TypeOf(ctx); |
| 1999 | 1996 | |
| 2000 | if (os.dl_iterate_phdr(&ctx, error{Found}, struct { | |
| 2001 | fn callback(info: *os.dl_phdr_info, size: usize, context: *CtxTy) !void { | |
| 1997 | if (posix.dl_iterate_phdr(&ctx, error{Found}, struct { | |
| 1998 | fn callback(info: *posix.dl_phdr_info, size: usize, context: *CtxTy) !void { | |
| 2002 | 1999 | _ = size; |
| 2003 | 2000 | if (context.address < info.dlpi_addr) return; |
| 2004 | 2001 | const phdrs = info.dlpi_phdr[0..info.dlpi_phnum]; |
| ... | ... | @@ -2036,8 +2033,8 @@ pub const DebugInfo = struct { |
| 2036 | 2033 | } = .{ .address = address }; |
| 2037 | 2034 | const CtxTy = @TypeOf(ctx); |
| 2038 | 2035 | |
| 2039 | if (os.dl_iterate_phdr(&ctx, error{Found}, struct { | |
| 2040 | fn callback(info: *os.dl_phdr_info, size: usize, context: *CtxTy) !void { | |
| 2036 | if (posix.dl_iterate_phdr(&ctx, error{Found}, struct { | |
| 2037 | fn callback(info: *posix.dl_phdr_info, size: usize, context: *CtxTy) !void { | |
| 2041 | 2038 | _ = size; |
| 2042 | 2039 | // The base address is too high |
| 2043 | 2040 | if (context.address < info.dlpi_addr) |
| ... | ... | @@ -2159,7 +2156,7 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 2159 | 2156 | } |
| 2160 | 2157 | self.ofiles.deinit(); |
| 2161 | 2158 | allocator.free(self.symbols); |
| 2162 | os.munmap(self.mapped_memory); | |
| 2159 | posix.munmap(self.mapped_memory); | |
| 2163 | 2160 | } |
| 2164 | 2161 | |
| 2165 | 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 | 2430 | |
| 2434 | 2431 | pub fn deinit(self: *@This(), allocator: mem.Allocator) void { |
| 2435 | 2432 | self.dwarf.deinit(allocator); |
| 2436 | os.munmap(self.mapped_memory); | |
| 2437 | if (self.external_mapped_memory) |m| os.munmap(m); | |
| 2433 | posix.munmap(self.mapped_memory); | |
| 2434 | if (self.external_mapped_memory) |m| posix.munmap(m); | |
| 2438 | 2435 | } |
| 2439 | 2436 | |
| 2440 | 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 | 2511 | .windows, |
| 2515 | 2512 | => true, |
| 2516 | 2513 | |
| 2517 | .freebsd, .openbsd => @hasDecl(os.system, "ucontext_t"), | |
| 2514 | .freebsd, .openbsd => @hasDecl(std.c, "ucontext_t"), | |
| 2518 | 2515 | else => false, |
| 2519 | 2516 | }; |
| 2520 | 2517 | |
| ... | ... | @@ -2529,11 +2526,11 @@ pub fn maybeEnableSegfaultHandler() void { |
| 2529 | 2526 | |
| 2530 | 2527 | var windows_segfault_handle: ?windows.HANDLE = null; |
| 2531 | 2528 | |
| 2532 | pub fn updateSegfaultHandler(act: ?*const os.Sigaction) error{OperationNotSupported}!void { | |
| 2533 | try os.sigaction(os.SIG.SEGV, act, null); | |
| 2534 | try os.sigaction(os.SIG.ILL, act, null); | |
| 2535 | try os.sigaction(os.SIG.BUS, act, null); | |
| 2536 | try os.sigaction(os.SIG.FPE, act, null); | |
| 2529 | pub fn updateSegfaultHandler(act: ?*const posix.Sigaction) error{OperationNotSupported}!void { | |
| 2530 | try posix.sigaction(posix.SIG.SEGV, act, null); | |
| 2531 | try posix.sigaction(posix.SIG.ILL, act, null); | |
| 2532 | try posix.sigaction(posix.SIG.BUS, act, null); | |
| 2533 | try posix.sigaction(posix.SIG.FPE, act, null); | |
| 2537 | 2534 | } |
| 2538 | 2535 | |
| 2539 | 2536 | /// Attaches a global SIGSEGV handler which calls `@panic("segmentation fault");` |
| ... | ... | @@ -2545,10 +2542,10 @@ pub fn attachSegfaultHandler() void { |
| 2545 | 2542 | windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows); |
| 2546 | 2543 | return; |
| 2547 | 2544 | } |
| 2548 | var act = os.Sigaction{ | |
| 2545 | var act = posix.Sigaction{ | |
| 2549 | 2546 | .handler = .{ .sigaction = handleSegfaultPosix }, |
| 2550 | .mask = os.empty_sigset, | |
| 2551 | .flags = (os.SA.SIGINFO | os.SA.RESTART | os.SA.RESETHAND), | |
| 2547 | .mask = posix.empty_sigset, | |
| 2548 | .flags = (posix.SA.SIGINFO | posix.SA.RESTART | posix.SA.RESETHAND), | |
| 2552 | 2549 | }; |
| 2553 | 2550 | |
| 2554 | 2551 | updateSegfaultHandler(&act) catch { |
| ... | ... | @@ -2564,16 +2561,16 @@ fn resetSegfaultHandler() void { |
| 2564 | 2561 | } |
| 2565 | 2562 | return; |
| 2566 | 2563 | } |
| 2567 | var act = os.Sigaction{ | |
| 2568 | .handler = .{ .handler = os.SIG.DFL }, | |
| 2569 | .mask = os.empty_sigset, | |
| 2564 | var act = posix.Sigaction{ | |
| 2565 | .handler = .{ .handler = posix.SIG.DFL }, | |
| 2566 | .mask = posix.empty_sigset, | |
| 2570 | 2567 | .flags = 0, |
| 2571 | 2568 | }; |
| 2572 | 2569 | // To avoid a double-panic, do nothing if an error happens here. |
| 2573 | 2570 | updateSegfaultHandler(&act) catch {}; |
| 2574 | 2571 | } |
| 2575 | 2572 | |
| 2576 | fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const anyopaque) callconv(.C) noreturn { | |
| 2573 | fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*const anyopaque) callconv(.C) noreturn { | |
| 2577 | 2574 | // Reset to the default handler so that if a segfault happens in this handler it will crash |
| 2578 | 2575 | // the process. Also when this handler returns, the original instruction will be repeated |
| 2579 | 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 | 2609 | // We cannot allow the signal handler to return because when it runs the original instruction |
| 2613 | 2610 | // again, the memory may be mapped and undefined behavior would occur rather than repeating |
| 2614 | 2611 | // the segfault. So we simply abort here. |
| 2615 | os.abort(); | |
| 2612 | posix.abort(); | |
| 2616 | 2613 | } |
| 2617 | 2614 | |
| 2618 | 2615 | fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*const anyopaque) void { |
| 2619 | 2616 | const stderr = io.getStdErr().writer(); |
| 2620 | 2617 | _ = switch (sig) { |
| 2621 | os.SIG.SEGV => if (native_arch == .x86_64 and native_os == .linux and code == 128) // SI_KERNEL | |
| 2618 | posix.SIG.SEGV => if (native_arch == .x86_64 and native_os == .linux and code == 128) // SI_KERNEL | |
| 2622 | 2619 | // x86_64 doesn't have a full 64-bit virtual address space. |
| 2623 | 2620 | // Addresses outside of that address space are non-canonical |
| 2624 | 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 | 2626 | stderr.print("General protection exception (no address available)\n", .{}) |
| 2630 | 2627 | else |
| 2631 | 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}), | |
| 2633 | os.SIG.BUS => stderr.print("Bus error at address 0x{x}\n", .{addr}), | |
| 2634 | os.SIG.FPE => stderr.print("Arithmetic exception at address 0x{x}\n", .{addr}), | |
| 2629 | posix.SIG.ILL => stderr.print("Illegal instruction at address 0x{x}\n", .{addr}), | |
| 2630 | posix.SIG.BUS => stderr.print("Bus error at address 0x{x}\n", .{addr}), | |
| 2631 | posix.SIG.FPE => stderr.print("Arithmetic exception at address 0x{x}\n", .{addr}), | |
| 2635 | 2632 | else => unreachable, |
| 2636 | } catch os.abort(); | |
| 2633 | } catch posix.abort(); | |
| 2637 | 2634 | |
| 2638 | 2635 | switch (native_arch) { |
| 2639 | 2636 | .x86, |
| ... | ... | @@ -2641,7 +2638,7 @@ fn dumpSegfaultInfoPosix(sig: i32, code: i32, addr: usize, ctx_ptr: ?*const anyo |
| 2641 | 2638 | .arm, |
| 2642 | 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 | 2642 | dumpStackTraceFromBase(ctx); |
| 2646 | 2643 | }, |
| 2647 | 2644 | else => {}, |
| ... | ... | @@ -2684,7 +2681,7 @@ fn handleSegfaultWindowsExtra( |
| 2684 | 2681 | dumpSegfaultInfoWindows(info, msg, label); |
| 2685 | 2682 | }, |
| 2686 | 2683 | }; |
| 2687 | os.abort(); | |
| 2684 | posix.abort(); | |
| 2688 | 2685 | } else { |
| 2689 | 2686 | switch (msg) { |
| 2690 | 2687 | 0 => panicImpl(null, exception_address, "{s}", label.?), |
| ... | ... | @@ -2707,7 +2704,7 @@ fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[ |
| 2707 | 2704 | 1 => stderr.print("Segmentation fault at address 0x{x}\n", .{info.ExceptionRecord.ExceptionInformation[1]}), |
| 2708 | 2705 | 2 => stderr.print("Illegal instruction at address 0x{x}\n", .{info.ContextRecord.getRegs().ip}), |
| 2709 | 2706 | else => unreachable, |
| 2710 | } catch os.abort(); | |
| 2707 | } catch posix.abort(); | |
| 2711 | 2708 | |
| 2712 | 2709 | dumpStackTraceFromBase(info.ContextRecord); |
| 2713 | 2710 | } |
| ... | ... | @@ -2722,9 +2719,9 @@ pub fn dumpStackPointerAddr(prefix: []const u8) void { |
| 2722 | 2719 | test "manage resources correctly" { |
| 2723 | 2720 | if (builtin.strip_debug_info) return error.SkipZigTest; |
| 2724 | 2721 | |
| 2725 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 2722 | if (native_os == .wasi) return error.SkipZigTest; | |
| 2726 | 2723 | |
| 2727 | if (builtin.os.tag == .windows) { | |
| 2724 | if (native_os == .windows) { | |
| 2728 | 2725 | // https://github.com/ziglang/zig/issues/13963 |
| 2729 | 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 | 2188 | /// This function is to make it handy to comment out the return and make it |
| 2189 | 2189 | /// into a crash when working on this file. |
| 2190 | 2190 | fn badDwarf() error{InvalidDebugInfo} { |
| 2191 | //std.os.abort(); // can be handy to uncomment when working on this file | |
| 2191 | //if (true) @panic("badDwarf"); // can be handy to uncomment when working on this file | |
| 2192 | 2192 | return error.InvalidDebugInfo; |
| 2193 | 2193 | } |
| 2194 | 2194 | |
| 2195 | 2195 | fn missingDwarf() error{MissingDebugInfo} { |
| 2196 | //std.os.abort(); // can be handy to uncomment when working on this file | |
| 2196 | //if (true) @panic("missingDwarf"); // can be handy to uncomment when working on this file | |
| 2197 | 2197 | return error.MissingDebugInfo; |
| 2198 | 2198 | } |
| 2199 | 2199 |
lib/std/dwarf/abi.zig+43-42| ... | ... | @@ -1,7 +1,8 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | 2 | const std = @import("../std.zig"); |
| 3 | const os = std.os; | |
| 4 | 3 | const mem = std.mem; |
| 4 | const native_os = builtin.os.tag; | |
| 5 | const posix = std.posix; | |
| 5 | 6 | |
| 6 | 7 | pub fn supportsUnwinding(target: std.Target) bool { |
| 7 | 8 | return switch (target.cpu.arch) { |
| ... | ... | @@ -138,7 +139,7 @@ pub fn regBytes( |
| 138 | 139 | reg_number: u8, |
| 139 | 140 | reg_context: ?RegisterContext, |
| 140 | 141 | ) AbiError!RegBytesReturnType(@TypeOf(thread_context_ptr)) { |
| 141 | if (builtin.os.tag == .windows) { | |
| 142 | if (native_os == .windows) { | |
| 142 | 143 | return switch (builtin.cpu.arch) { |
| 143 | 144 | .x86 => switch (reg_number) { |
| 144 | 145 | 0 => mem.asBytes(&thread_context_ptr.Eax), |
| ... | ... | @@ -193,61 +194,61 @@ pub fn regBytes( |
| 193 | 194 | |
| 194 | 195 | const ucontext_ptr = thread_context_ptr; |
| 195 | 196 | return switch (builtin.cpu.arch) { |
| 196 | .x86 => switch (builtin.os.tag) { | |
| 197 | .x86 => switch (native_os) { | |
| 197 | 198 | .linux, .netbsd, .solaris, .illumos => switch (reg_number) { |
| 198 | 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EAX]), | |
| 199 | 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]), | |
| 200 | 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]), | |
| 201 | 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBX]), | |
| 199 | 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EAX]), | |
| 200 | 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ECX]), | |
| 201 | 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EDX]), | |
| 202 | 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBX]), | |
| 202 | 203 | 4...5 => if (reg_context) |r| bytes: { |
| 203 | 204 | if (reg_number == 4) { |
| 204 | 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 | 207 | else |
| 207 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESP]); | |
| 208 | mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESP]); | |
| 208 | 209 | } else { |
| 209 | 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 | 212 | else |
| 212 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP]); | |
| 213 | mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EBP]); | |
| 213 | 214 | } |
| 214 | 215 | } else error.RegisterContextRequired, |
| 215 | 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESI]), | |
| 216 | 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDI]), | |
| 217 | 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EIP]), | |
| 218 | 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EFL]), | |
| 219 | 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.CS]), | |
| 220 | 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.SS]), | |
| 221 | 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.DS]), | |
| 222 | 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ES]), | |
| 223 | 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]), | |
| 224 | 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]), | |
| 216 | 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ESI]), | |
| 217 | 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EDI]), | |
| 218 | 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EIP]), | |
| 219 | 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.EFL]), | |
| 220 | 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.CS]), | |
| 221 | 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.SS]), | |
| 222 | 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.DS]), | |
| 223 | 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.ES]), | |
| 224 | 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.FS]), | |
| 225 | 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.GS]), | |
| 225 | 226 | 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs |
| 226 | 227 | 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs |
| 227 | 228 | else => error.InvalidRegister, |
| 228 | 229 | }, |
| 229 | 230 | else => error.UnimplementedOs, |
| 230 | 231 | }, |
| 231 | .x86_64 => switch (builtin.os.tag) { | |
| 232 | .x86_64 => switch (native_os) { | |
| 232 | 233 | .linux, .solaris, .illumos => switch (reg_number) { |
| 233 | 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RAX]), | |
| 234 | 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RDX]), | |
| 235 | 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RCX]), | |
| 236 | 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RBX]), | |
| 237 | 4 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RSI]), | |
| 238 | 5 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RDI]), | |
| 239 | 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RBP]), | |
| 240 | 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RSP]), | |
| 241 | 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R8]), | |
| 242 | 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R9]), | |
| 243 | 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R10]), | |
| 244 | 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R11]), | |
| 245 | 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R12]), | |
| 246 | 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R13]), | |
| 247 | 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R14]), | |
| 248 | 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.R15]), | |
| 249 | 16 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.RIP]), | |
| 250 | 17...32 => |i| if (builtin.os.tag.isSolarish()) | |
| 234 | 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RAX]), | |
| 235 | 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDX]), | |
| 236 | 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RCX]), | |
| 237 | 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RBX]), | |
| 238 | 4 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RSI]), | |
| 239 | 5 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RDI]), | |
| 240 | 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RBP]), | |
| 241 | 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RSP]), | |
| 242 | 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R8]), | |
| 243 | 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R9]), | |
| 244 | 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R10]), | |
| 245 | 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R11]), | |
| 246 | 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R12]), | |
| 247 | 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R13]), | |
| 248 | 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R14]), | |
| 249 | 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.R15]), | |
| 250 | 16 => mem.asBytes(&ucontext_ptr.mcontext.gregs[posix.REG.RIP]), | |
| 251 | 17...32 => |i| if (native_os.isSolarish()) | |
| 251 | 252 | mem.asBytes(&ucontext_ptr.mcontext.fpregs.chip_state.xmm[i - 17]) |
| 252 | 253 | else |
| 253 | 254 | mem.asBytes(&ucontext_ptr.mcontext.fpregs.xmm[i - 17]), |
| ... | ... | @@ -317,7 +318,7 @@ pub fn regBytes( |
| 317 | 318 | }, |
| 318 | 319 | else => error.UnimplementedOs, |
| 319 | 320 | }, |
| 320 | .arm => switch (builtin.os.tag) { | |
| 321 | .arm => switch (native_os) { | |
| 321 | 322 | .linux => switch (reg_number) { |
| 322 | 323 | 0 => mem.asBytes(&ucontext_ptr.mcontext.arm_r0), |
| 323 | 324 | 1 => mem.asBytes(&ucontext_ptr.mcontext.arm_r1), |
| ... | ... | @@ -340,7 +341,7 @@ pub fn regBytes( |
| 340 | 341 | }, |
| 341 | 342 | else => error.UnimplementedOs, |
| 342 | 343 | }, |
| 343 | .aarch64 => switch (builtin.os.tag) { | |
| 344 | .aarch64 => switch (native_os) { | |
| 344 | 345 | .macos, .ios => switch (reg_number) { |
| 345 | 346 | 0...28 => mem.asBytes(&ucontext_ptr.mcontext.ss.regs[reg_number]), |
| 346 | 347 | 29 => mem.asBytes(&ucontext_ptr.mcontext.ss.fp), |
lib/std/dynamic_library.zig+30-30| ... | ... | @@ -1,16 +1,16 @@ |
| 1 | 1 | const std = @import("std.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const mem = std.mem; |
| 4 | const os = std.os; | |
| 5 | 4 | const testing = std.testing; |
| 6 | 5 | const elf = std.elf; |
| 7 | 6 | const windows = std.os.windows; |
| 8 | const system = std.os.system; | |
| 7 | const native_os = builtin.os.tag; | |
| 8 | const posix = std.posix; | |
| 9 | 9 | |
| 10 | 10 | /// Cross-platform dynamic library loading and symbol lookup. |
| 11 | 11 | /// Platform-specific functionality is available through the `inner` field. |
| 12 | 12 | pub const DynLib = struct { |
| 13 | const InnerType = switch (builtin.os.tag) { | |
| 13 | const InnerType = switch (native_os) { | |
| 14 | 14 | .linux => if (!builtin.link_libc or builtin.abi == .musl and builtin.link_mode == .static) |
| 15 | 15 | ElfDynLib |
| 16 | 16 | else |
| ... | ... | @@ -125,7 +125,7 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) error{InvalidExe}!LinkMap.Iterator { |
| 125 | 125 | pub const ElfDynLib = struct { |
| 126 | 126 | strings: [*:0]u8, |
| 127 | 127 | syms: [*]elf.Sym, |
| 128 | hashtab: [*]os.Elf_Symndx, | |
| 128 | hashtab: [*]posix.Elf_Symndx, | |
| 129 | 129 | versym: ?[*]u16, |
| 130 | 130 | verdef: ?*elf.Verdef, |
| 131 | 131 | memory: []align(mem.page_size) u8, |
| ... | ... | @@ -138,27 +138,27 @@ pub const ElfDynLib = struct { |
| 138 | 138 | ElfStringSectionNotFound, |
| 139 | 139 | ElfSymSectionNotFound, |
| 140 | 140 | ElfHashTableNotFound, |
| 141 | } || os.OpenError || os.MMapError; | |
| 141 | } || posix.OpenError || posix.MMapError; | |
| 142 | 142 | |
| 143 | 143 | /// Trusts the file. Malicious file will be able to execute arbitrary code. |
| 144 | 144 | pub fn open(path: []const u8) Error!ElfDynLib { |
| 145 | const fd = try os.open(path, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | |
| 146 | defer os.close(fd); | |
| 145 | const fd = try posix.open(path, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | |
| 146 | defer posix.close(fd); | |
| 147 | 147 | |
| 148 | const stat = try os.fstat(fd); | |
| 148 | const stat = try posix.fstat(fd); | |
| 149 | 149 | const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig; |
| 150 | 150 | |
| 151 | 151 | // This one is to read the ELF info. We do more mmapping later |
| 152 | 152 | // corresponding to the actual LOAD sections. |
| 153 | const file_bytes = try os.mmap( | |
| 153 | const file_bytes = try posix.mmap( | |
| 154 | 154 | null, |
| 155 | 155 | mem.alignForward(usize, size, mem.page_size), |
| 156 | os.PROT.READ, | |
| 156 | posix.PROT.READ, | |
| 157 | 157 | .{ .TYPE = .PRIVATE }, |
| 158 | 158 | fd, |
| 159 | 159 | 0, |
| 160 | 160 | ); |
| 161 | defer os.munmap(file_bytes); | |
| 161 | defer posix.munmap(file_bytes); | |
| 162 | 162 | |
| 163 | 163 | const eh = @as(*elf.Ehdr, @ptrCast(file_bytes.ptr)); |
| 164 | 164 | if (!mem.eql(u8, eh.e_ident[0..4], elf.MAGIC)) return error.NotElfFile; |
| ... | ... | @@ -188,15 +188,15 @@ pub const ElfDynLib = struct { |
| 188 | 188 | const dynv = maybe_dynv orelse return error.MissingDynamicLinkingInformation; |
| 189 | 189 | |
| 190 | 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 | 192 | null, |
| 193 | 193 | virt_addr_end, |
| 194 | os.PROT.NONE, | |
| 194 | posix.PROT.NONE, | |
| 195 | 195 | .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, |
| 196 | 196 | -1, |
| 197 | 197 | 0, |
| 198 | 198 | ); |
| 199 | errdefer os.munmap(all_loaded_mem); | |
| 199 | errdefer posix.munmap(all_loaded_mem); | |
| 200 | 200 | |
| 201 | 201 | const base = @intFromPtr(all_loaded_mem.ptr); |
| 202 | 202 | |
| ... | ... | @@ -220,7 +220,7 @@ pub const ElfDynLib = struct { |
| 220 | 220 | const prot = elfToMmapProt(ph.p_flags); |
| 221 | 221 | if ((ph.p_flags & elf.PF_W) == 0) { |
| 222 | 222 | // If it does not need write access, it can be mapped from the fd. |
| 223 | _ = try os.mmap( | |
| 223 | _ = try posix.mmap( | |
| 224 | 224 | ptr, |
| 225 | 225 | extended_memsz, |
| 226 | 226 | prot, |
| ... | ... | @@ -229,7 +229,7 @@ pub const ElfDynLib = struct { |
| 229 | 229 | ph.p_offset - extra_bytes, |
| 230 | 230 | ); |
| 231 | 231 | } else { |
| 232 | const sect_mem = try os.mmap( | |
| 232 | const sect_mem = try posix.mmap( | |
| 233 | 233 | ptr, |
| 234 | 234 | extended_memsz, |
| 235 | 235 | prot, |
| ... | ... | @@ -247,7 +247,7 @@ pub const ElfDynLib = struct { |
| 247 | 247 | |
| 248 | 248 | var maybe_strings: ?[*:0]u8 = null; |
| 249 | 249 | var maybe_syms: ?[*]elf.Sym = null; |
| 250 | var maybe_hashtab: ?[*]os.Elf_Symndx = null; | |
| 250 | var maybe_hashtab: ?[*]posix.Elf_Symndx = null; | |
| 251 | 251 | var maybe_versym: ?[*]u16 = null; |
| 252 | 252 | var maybe_verdef: ?*elf.Verdef = null; |
| 253 | 253 | |
| ... | ... | @@ -258,7 +258,7 @@ pub const ElfDynLib = struct { |
| 258 | 258 | switch (dynv[i]) { |
| 259 | 259 | elf.DT_STRTAB => maybe_strings = @as([*:0]u8, @ptrFromInt(p)), |
| 260 | 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 | 262 | elf.DT_VERSYM => maybe_versym = @as([*]u16, @ptrFromInt(p)), |
| 263 | 263 | elf.DT_VERDEF => maybe_verdef = @as(*elf.Verdef, @ptrFromInt(p)), |
| 264 | 264 | else => {}, |
| ... | ... | @@ -283,7 +283,7 @@ pub const ElfDynLib = struct { |
| 283 | 283 | |
| 284 | 284 | /// Trusts the file |
| 285 | 285 | pub fn close(self: *ElfDynLib) void { |
| 286 | os.munmap(self.memory); | |
| 286 | posix.munmap(self.memory); | |
| 287 | 287 | self.* = undefined; |
| 288 | 288 | } |
| 289 | 289 | |
| ... | ... | @@ -320,10 +320,10 @@ pub const ElfDynLib = struct { |
| 320 | 320 | } |
| 321 | 321 | |
| 322 | 322 | fn elfToMmapProt(elf_prot: u64) u32 { |
| 323 | var result: u32 = os.PROT.NONE; | |
| 324 | if ((elf_prot & elf.PF_R) != 0) result |= os.PROT.READ; | |
| 325 | if ((elf_prot & elf.PF_W) != 0) result |= os.PROT.WRITE; | |
| 326 | if ((elf_prot & elf.PF_X) != 0) result |= os.PROT.EXEC; | |
| 323 | var result: u32 = posix.PROT.NONE; | |
| 324 | if ((elf_prot & elf.PF_R) != 0) result |= posix.PROT.READ; | |
| 325 | if ((elf_prot & elf.PF_W) != 0) result |= posix.PROT.WRITE; | |
| 326 | if ((elf_prot & elf.PF_X) != 0) result |= posix.PROT.EXEC; | |
| 327 | 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 | 343 | } |
| 344 | 344 | |
| 345 | 345 | test "ElfDynLib" { |
| 346 | if (builtin.os.tag != .linux) { | |
| 346 | if (native_os != .linux) { | |
| 347 | 347 | return error.SkipZigTest; |
| 348 | 348 | } |
| 349 | 349 | |
| ... | ... | @@ -419,20 +419,20 @@ pub const DlDynLib = struct { |
| 419 | 419 | handle: *anyopaque, |
| 420 | 420 | |
| 421 | 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 | 423 | return openZ(&path_c); |
| 424 | 424 | } |
| 425 | 425 | |
| 426 | 426 | pub fn openZ(path_c: [*:0]const u8) Error!DlDynLib { |
| 427 | 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 | 429 | return error.FileNotFound; |
| 430 | 430 | }, |
| 431 | 431 | }; |
| 432 | 432 | } |
| 433 | 433 | |
| 434 | 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 | 436 | .SUCCESS => return, |
| 437 | 437 | else => unreachable, |
| 438 | 438 | } |
| ... | ... | @@ -442,7 +442,7 @@ pub const DlDynLib = struct { |
| 442 | 442 | pub fn lookup(self: *DlDynLib, comptime T: type, name: [:0]const u8) ?T { |
| 443 | 443 | // dlsym (and other dl-functions) secretly take shadow parameter - return address on stack |
| 444 | 444 | // 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 | 446 | return @as(T, @ptrCast(@alignCast(symbol))); |
| 447 | 447 | } else { |
| 448 | 448 | return null; |
| ... | ... | @@ -453,12 +453,12 @@ pub const DlDynLib = struct { |
| 453 | 453 | /// Returns human readable string describing most recent error than occurred from `lookup` |
| 454 | 454 | /// or `null` if no error has occurred since initialization or when `getError` was last called. |
| 455 | 455 | pub fn getError() ?[:0]const u8 { |
| 456 | return mem.span(system.dlerror()); | |
| 456 | return mem.span(std.c.dlerror()); | |
| 457 | 457 | } |
| 458 | 458 | }; |
| 459 | 459 | |
| 460 | 460 | test "dynamic_library" { |
| 461 | const libname = switch (builtin.os.tag) { | |
| 461 | const libname = switch (native_os) { | |
| 462 | 462 | .linux, .freebsd, .openbsd, .solaris, .illumos => "invalid_so.so", |
| 463 | 463 | .windows => "invalid_dll.dll", |
| 464 | 464 | .macos, .tvos, .watchos, .ios => "invalid_dylib.dylib", |
lib/std/fs.zig+89-82| ... | ... | @@ -3,21 +3,23 @@ |
| 3 | 3 | const std = @import("std.zig"); |
| 4 | 4 | const builtin = @import("builtin"); |
| 5 | 5 | const root = @import("root"); |
| 6 | const os = std.os; | |
| 7 | 6 | const mem = std.mem; |
| 8 | 7 | const base64 = std.base64; |
| 9 | 8 | const crypto = std.crypto; |
| 10 | 9 | const Allocator = std.mem.Allocator; |
| 11 | 10 | const assert = std.debug.assert; |
| 11 | const native_os = builtin.os.tag; | |
| 12 | const posix = std.posix; | |
| 13 | const windows = std.os.windows; | |
| 12 | 14 | |
| 13 | const is_darwin = builtin.os.tag.isDarwin(); | |
| 15 | const is_darwin = native_os.isDarwin(); | |
| 14 | 16 | |
| 15 | 17 | pub const AtomicFile = @import("fs/AtomicFile.zig"); |
| 16 | 18 | pub const Dir = @import("fs/Dir.zig"); |
| 17 | 19 | pub const File = @import("fs/File.zig"); |
| 18 | 20 | pub const path = @import("fs/path.zig"); |
| 19 | 21 | |
| 20 | pub const has_executable_bit = switch (builtin.os.tag) { | |
| 22 | pub const has_executable_bit = switch (native_os) { | |
| 21 | 23 | .windows, .wasi => false, |
| 22 | 24 | else => true, |
| 23 | 25 | }; |
| ... | ... | @@ -26,36 +28,41 @@ pub const wasi = @import("fs/wasi.zig"); |
| 26 | 28 | |
| 27 | 29 | // TODO audit these APIs with respect to Dir and absolute paths |
| 28 | 30 | |
| 29 | pub const realpath = os.realpath; | |
| 30 | pub const realpathZ = os.realpathZ; | |
| 31 | pub const realpathW = os.realpathW; | |
| 31 | pub const realpath = posix.realpath; | |
| 32 | pub const realpathZ = posix.realpathZ; | |
| 33 | pub const realpathW = posix.realpathW; | |
| 32 | 34 | |
| 33 | 35 | pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir; |
| 34 | 36 | pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError; |
| 35 | 37 | |
| 36 | /// This represents the maximum size of a `[]u8` file path that the | |
| 37 | /// operating system will accept. Paths, including those returned from file | |
| 38 | /// system operations, may be longer than this length, but such paths cannot | |
| 39 | /// be successfully passed back in other file system operations. However, | |
| 40 | /// all path components returned by file system operations are assumed to | |
| 41 | /// fit into a `u8` array of this length. | |
| 38 | /// Deprecated: use `max_path_bytes`. | |
| 39 | pub const MAX_PATH_BYTES = max_path_bytes; | |
| 40 | ||
| 41 | /// The maximum length of a file path that the operating system will accept. | |
| 42 | /// | |
| 43 | /// Paths, including those returned from file system operations, may be longer | |
| 44 | /// than this length, but such paths cannot be successfully passed back in | |
| 45 | /// other file system operations. However, all path components returned by file | |
| 46 | /// system operations are assumed to fit into a `u8` array of this length. | |
| 47 | /// | |
| 42 | 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/). | |
| 44 | /// On WASI, `[]u8` file paths are encoded as valid UTF-8. | |
| 45 | /// On other platforms, `[]u8` file paths are opaque sequences of bytes with no particular encoding. | |
| 46 | pub const MAX_PATH_BYTES = switch (builtin.os.tag) { | |
| 47 | .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .haiku, .solaris, .illumos, .plan9, .emscripten => os.PATH_MAX, | |
| 49 | /// | |
| 50 | /// * On Windows, `[]u8` file paths are encoded as | |
| 51 | /// [WTF-8](https://simonsapin.github.io/wtf-8/). | |
| 52 | /// * On WASI, `[]u8` file paths are encoded as valid UTF-8. | |
| 53 | /// * On other platforms, `[]u8` file paths are opaque sequences of bytes with | |
| 54 | /// no particular encoding. | |
| 55 | pub 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 | 57 | // Each WTF-16LE code unit may be expanded to 3 WTF-8 bytes. |
| 49 | 58 | // If it would require 4 WTF-8 bytes, then there would be a surrogate |
| 50 | 59 | // pair in the WTF-16LE, and we (over)account 3 bytes for it that way. |
| 51 | 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, | |
| 53 | // TODO work out what a reasonable value we should use here | |
| 54 | .wasi => 4096, | |
| 61 | .windows => windows.PATH_MAX_WIDE * 3 + 1, | |
| 55 | 62 | else => if (@hasDecl(root, "os") and @hasDecl(root.os, "PATH_MAX")) |
| 56 | 63 | root.os.PATH_MAX |
| 57 | 64 | else |
| 58 | @compileError("PATH_MAX not implemented for " ++ @tagName(builtin.os.tag)), | |
| 65 | @compileError("PATH_MAX not implemented for " ++ @tagName(native_os)), | |
| 59 | 66 | }; |
| 60 | 67 | |
| 61 | 68 | /// 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 | 73 | /// On Windows, `[]u8` file name components are encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). |
| 67 | 74 | /// On WASI, file name components are encoded as valid UTF-8. |
| 68 | 75 | /// On other platforms, `[]u8` components are an opaque sequence of bytes with no particular encoding. |
| 69 | pub const MAX_NAME_BYTES = switch (builtin.os.tag) { | |
| 70 | .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .illumos => os.NAME_MAX, | |
| 76 | pub const MAX_NAME_BYTES = switch (native_os) { | |
| 77 | .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .illumos => posix.NAME_MAX, | |
| 71 | 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 | 80 | // Each WTF-16LE character may be expanded to 3 WTF-8 bytes. |
| 74 | 81 | // If it would require 4 WTF-8 bytes, then there would be a surrogate |
| 75 | 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 | 84 | // For WASI, the MAX_NAME will depend on the host OS, so it needs to be |
| 78 | 85 | // as large as the largest MAX_NAME_BYTES (Windows) in order to work on any host OS. |
| 79 | 86 | // TODO determine if this is a reasonable approach |
| 80 | .wasi => os.windows.NAME_MAX * 3, | |
| 87 | .wasi => windows.NAME_MAX * 3, | |
| 81 | 88 | else => if (@hasDecl(root, "os") and @hasDecl(root.os, "NAME_MAX")) |
| 82 | 89 | root.os.NAME_MAX |
| 83 | 90 | else |
| 84 | @compileError("NAME_MAX not implemented for " ++ @tagName(builtin.os.tag)), | |
| 91 | @compileError("NAME_MAX not implemented for " ++ @tagName(native_os)), | |
| 85 | 92 | }; |
| 86 | 93 | |
| 87 | 94 | pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*; |
| ... | ... | @@ -167,19 +174,19 @@ pub fn copyFileAbsolute( |
| 167 | 174 | /// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. |
| 168 | 175 | pub fn makeDirAbsolute(absolute_path: []const u8) !void { |
| 169 | 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 | } |
| 172 | 179 | |
| 173 | 180 | /// Same as `makeDirAbsolute` except the parameter is null-terminated. |
| 174 | 181 | pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void { |
| 175 | 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 | } |
| 178 | 185 | |
| 179 | 186 | /// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 LE-encoded string. |
| 180 | 187 | pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void { |
| 181 | 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 | } |
| 184 | 191 | |
| 185 | 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 | 195 | /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. |
| 189 | 196 | pub fn deleteDirAbsolute(dir_path: []const u8) !void { |
| 190 | 197 | assert(path.isAbsolute(dir_path)); |
| 191 | return os.rmdir(dir_path); | |
| 198 | return posix.rmdir(dir_path); | |
| 192 | 199 | } |
| 193 | 200 | |
| 194 | 201 | /// Same as `deleteDirAbsolute` except the path parameter is null-terminated. |
| 195 | 202 | pub fn deleteDirAbsoluteZ(dir_path: [*:0]const u8) !void { |
| 196 | 203 | assert(path.isAbsoluteZ(dir_path)); |
| 197 | return os.rmdirZ(dir_path); | |
| 204 | return posix.rmdirZ(dir_path); | |
| 198 | 205 | } |
| 199 | 206 | |
| 200 | 207 | /// Same as `deleteDirAbsolute` except the path parameter is WTF-16 and target OS is assumed Windows. |
| 201 | 208 | pub fn deleteDirAbsoluteW(dir_path: [*:0]const u16) !void { |
| 202 | 209 | assert(path.isAbsoluteWindowsW(dir_path)); |
| 203 | return os.rmdirW(dir_path); | |
| 210 | return posix.rmdirW(dir_path); | |
| 204 | 211 | } |
| 205 | 212 | |
| 206 | 213 | /// Same as `Dir.rename` except the paths are absolute. |
| ... | ... | @@ -210,49 +217,49 @@ pub fn deleteDirAbsoluteW(dir_path: [*:0]const u16) !void { |
| 210 | 217 | pub fn renameAbsolute(old_path: []const u8, new_path: []const u8) !void { |
| 211 | 218 | assert(path.isAbsolute(old_path)); |
| 212 | 219 | assert(path.isAbsolute(new_path)); |
| 213 | return os.rename(old_path, new_path); | |
| 220 | return posix.rename(old_path, new_path); | |
| 214 | 221 | } |
| 215 | 222 | |
| 216 | 223 | /// Same as `renameAbsolute` except the path parameters are null-terminated. |
| 217 | 224 | pub fn renameAbsoluteZ(old_path: [*:0]const u8, new_path: [*:0]const u8) !void { |
| 218 | 225 | assert(path.isAbsoluteZ(old_path)); |
| 219 | 226 | assert(path.isAbsoluteZ(new_path)); |
| 220 | return os.renameZ(old_path, new_path); | |
| 227 | return posix.renameZ(old_path, new_path); | |
| 221 | 228 | } |
| 222 | 229 | |
| 223 | 230 | /// Same as `renameAbsolute` except the path parameters are WTF-16 and target OS is assumed Windows. |
| 224 | 231 | pub fn renameAbsoluteW(old_path: [*:0]const u16, new_path: [*:0]const u16) !void { |
| 225 | 232 | assert(path.isAbsoluteWindowsW(old_path)); |
| 226 | 233 | assert(path.isAbsoluteWindowsW(new_path)); |
| 227 | return os.renameW(old_path, new_path); | |
| 234 | return posix.renameW(old_path, new_path); | |
| 228 | 235 | } |
| 229 | 236 | |
| 230 | 237 | /// Same as `Dir.rename`, except `new_sub_path` is relative to `new_dir` |
| 231 | 238 | pub 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 | } |
| 234 | 241 | |
| 235 | 242 | /// Same as `rename` except the parameters are null-terminated. |
| 236 | 243 | pub 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 | } |
| 239 | 246 | |
| 240 | 247 | /// Same as `rename` except the parameters are WTF16LE, NT prefixed. |
| 241 | 248 | /// This function is Windows-only. |
| 242 | 249 | pub 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 | } |
| 245 | 252 | |
| 246 | 253 | /// Returns a handle to the current working directory. It is not opened with iteration capability. |
| 247 | 254 | /// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior. |
| 248 | 255 | /// On POSIX targets, this function is comptime-callable. |
| 249 | 256 | pub fn cwd() Dir { |
| 250 | if (builtin.os.tag == .windows) { | |
| 251 | return .{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle }; | |
| 252 | } else if (builtin.os.tag == .wasi) { | |
| 257 | if (native_os == .windows) { | |
| 258 | return .{ .fd = windows.peb().ProcessParameters.CurrentDirectory.Handle }; | |
| 259 | } else if (native_os == .wasi) { | |
| 253 | 260 | return .{ .fd = std.options.wasiCwd() }; |
| 254 | 261 | } else { |
| 255 | return .{ .fd = os.AT.FDCWD }; | |
| 262 | return .{ .fd = posix.AT.FDCWD }; | |
| 256 | 263 | } |
| 257 | 264 | } |
| 258 | 265 | |
| ... | ... | @@ -412,20 +419,20 @@ pub fn deleteTreeAbsolute(absolute_path: []const u8) !void { |
| 412 | 419 | /// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding. |
| 413 | 420 | pub fn readLinkAbsolute(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { |
| 414 | 421 | assert(path.isAbsolute(pathname)); |
| 415 | return os.readlink(pathname, buffer); | |
| 422 | return posix.readlink(pathname, buffer); | |
| 416 | 423 | } |
| 417 | 424 | |
| 418 | 425 | /// Windows-only. Same as `readlinkW`, except the path parameter is null-terminated, WTF16 |
| 419 | 426 | /// encoded. |
| 420 | 427 | pub fn readlinkAbsoluteW(pathname_w: [*:0]const u16, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { |
| 421 | 428 | assert(path.isAbsoluteWindowsW(pathname_w)); |
| 422 | return os.readlinkW(pathname_w, buffer); | |
| 429 | return posix.readlinkW(pathname_w, buffer); | |
| 423 | 430 | } |
| 424 | 431 | |
| 425 | 432 | /// Same as `readLink`, except the path parameter is null-terminated. |
| 426 | 433 | pub fn readLinkAbsoluteZ(pathname_c: [*:0]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { |
| 427 | 434 | assert(path.isAbsoluteZ(pathname_c)); |
| 428 | return os.readlinkZ(pathname_c, buffer); | |
| 435 | return posix.readlinkZ(pathname_c, buffer); | |
| 429 | 436 | } |
| 430 | 437 | |
| 431 | 438 | /// Creates a symbolic link named `sym_link_path` which contains the string `target_path`. |
| ... | ... | @@ -443,12 +450,12 @@ pub fn symLinkAbsolute( |
| 443 | 450 | ) !void { |
| 444 | 451 | assert(path.isAbsolute(target_path)); |
| 445 | 452 | assert(path.isAbsolute(sym_link_path)); |
| 446 | if (builtin.os.tag == .windows) { | |
| 447 | const target_path_w = try os.windows.sliceToPrefixedFileW(null, target_path); | |
| 448 | const sym_link_path_w = try os.windows.sliceToPrefixedFileW(null, sym_link_path); | |
| 449 | return os.windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory); | |
| 453 | if (native_os == .windows) { | |
| 454 | const target_path_w = try windows.sliceToPrefixedFileW(null, target_path); | |
| 455 | const sym_link_path_w = try windows.sliceToPrefixedFileW(null, sym_link_path); | |
| 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 | } |
| 453 | 460 | |
| 454 | 461 | /// Windows-only. Same as `symLinkAbsolute` except the parameters are null-terminated, WTF16 LE encoded. |
| ... | ... | @@ -462,7 +469,7 @@ pub fn symLinkAbsoluteW( |
| 462 | 469 | ) !void { |
| 463 | 470 | assert(path.isAbsoluteWindowsWTF16(target_path_w)); |
| 464 | 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 | } |
| 467 | 474 | |
| 468 | 475 | /// Same as `symLinkAbsolute` except the parameters are null-terminated pointers. |
| ... | ... | @@ -474,27 +481,27 @@ pub fn symLinkAbsoluteZ( |
| 474 | 481 | ) !void { |
| 475 | 482 | assert(path.isAbsoluteZ(target_path_c)); |
| 476 | 483 | assert(path.isAbsoluteZ(sym_link_path_c)); |
| 477 | if (builtin.os.tag == .windows) { | |
| 478 | const target_path_w = try os.windows.cStrToPrefixedFileW(null, target_path_c); | |
| 479 | const sym_link_path_w = try os.windows.cStrToPrefixedFileW(null, sym_link_path_c); | |
| 480 | return os.windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory); | |
| 484 | if (native_os == .windows) { | |
| 485 | const target_path_w = try windows.cStrToPrefixedFileW(null, target_path_c); | |
| 486 | const sym_link_path_w = try windows.cStrToPrefixedFileW(null, sym_link_path_c); | |
| 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 | } |
| 484 | 491 | |
| 485 | pub const OpenSelfExeError = os.OpenError || SelfExePathError || os.FlockError; | |
| 492 | pub const OpenSelfExeError = posix.OpenError || SelfExePathError || posix.FlockError; | |
| 486 | 493 | |
| 487 | 494 | pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File { |
| 488 | if (builtin.os.tag == .linux) { | |
| 495 | if (native_os == .linux) { | |
| 489 | 496 | return openFileAbsoluteZ("/proc/self/exe", flags); |
| 490 | 497 | } |
| 491 | if (builtin.os.tag == .windows) { | |
| 498 | if (native_os == .windows) { | |
| 492 | 499 | // If ImagePathName is a symlink, then it will contain the path of the symlink, |
| 493 | 500 | // not the path that the symlink points to. However, because we are opening |
| 494 | 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 | 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 | 505 | return cwd().openFileW(prefixed_path_w.span(), flags); |
| 499 | 506 | } |
| 500 | 507 | // 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 | 512 | return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, flags); |
| 506 | 513 | } |
| 507 | 514 | |
| 508 | // This is os.ReadLinkError || os.RealPathError with impossible errors excluded | |
| 515 | // This is `posix.ReadLinkError || posix.RealPathError` with impossible errors excluded | |
| 509 | 516 | pub const SelfExePathError = error{ |
| 510 | 517 | FileNotFound, |
| 511 | 518 | AccessDenied, |
| ... | ... | @@ -542,7 +549,7 @@ pub const SelfExePathError = error{ |
| 542 | 549 | /// On Windows, the volume does not contain a recognized file system. File |
| 543 | 550 | /// system drivers might not be loaded, or the volume may be corrupt. |
| 544 | 551 | UnrecognizedVolume, |
| 545 | } || os.SysCtlError; | |
| 552 | } || posix.SysCtlError; | |
| 546 | 553 | |
| 547 | 554 | /// `selfExePath` except allocates the result on the heap. |
| 548 | 555 | /// Caller owns returned memory. |
| ... | ... | @@ -580,7 +587,7 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { |
| 580 | 587 | if (rc != 0) return error.NameTooLong; |
| 581 | 588 | |
| 582 | 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 | 591 | error.InvalidWtf8 => unreachable, // Windows-only |
| 585 | 592 | error.NetworkNotFound => unreachable, // Windows-only |
| 586 | 593 | else => |e| return e, |
| ... | ... | @@ -590,15 +597,15 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { |
| 590 | 597 | @memcpy(result, real_path); |
| 591 | 598 | return result; |
| 592 | 599 | } |
| 593 | switch (builtin.os.tag) { | |
| 594 | .linux => return os.readlinkZ("/proc/self/exe", out_buffer) catch |err| switch (err) { | |
| 600 | switch (native_os) { | |
| 601 | .linux => return posix.readlinkZ("/proc/self/exe", out_buffer) catch |err| switch (err) { | |
| 595 | 602 | error.InvalidUtf8 => unreachable, // WASI-only |
| 596 | 603 | error.InvalidWtf8 => unreachable, // Windows-only |
| 597 | 604 | error.UnsupportedReparsePointType => unreachable, // Windows-only |
| 598 | 605 | error.NetworkNotFound => unreachable, // Windows-only |
| 599 | 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 | 609 | error.InvalidUtf8 => unreachable, // WASI-only |
| 603 | 610 | error.InvalidWtf8 => unreachable, // Windows-only |
| 604 | 611 | error.UnsupportedReparsePointType => unreachable, // Windows-only |
| ... | ... | @@ -606,29 +613,29 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { |
| 606 | 613 | else => |e| return e, |
| 607 | 614 | }, |
| 608 | 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 | 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 | 619 | // TODO could this slice from 0 to out_len instead? |
| 613 | 620 | return mem.sliceTo(out_buffer, 0); |
| 614 | 621 | }, |
| 615 | 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 | 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 | 626 | // TODO could this slice from 0 to out_len instead? |
| 620 | 627 | return mem.sliceTo(out_buffer, 0); |
| 621 | 628 | }, |
| 622 | 629 | .openbsd, .haiku => { |
| 623 | 630 | // 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 | 632 | return error.FileNotFound; |
| 626 | 633 | |
| 627 | const argv0 = mem.span(os.argv[0]); | |
| 634 | const argv0 = mem.span(std.os.argv[0]); | |
| 628 | 635 | if (mem.indexOf(u8, argv0, "/") != null) { |
| 629 | 636 | // argv[0] is a path (relative or absolute): use realpath(3) directly |
| 630 | 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 | 639 | error.InvalidWtf8 => unreachable, // Windows-only |
| 633 | 640 | error.NetworkNotFound => unreachable, // Windows-only |
| 634 | 641 | else => |e| return e, |
| ... | ... | @@ -640,17 +647,17 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { |
| 640 | 647 | return result; |
| 641 | 648 | } else if (argv0.len != 0) { |
| 642 | 649 | // 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 | 651 | var path_it = mem.tokenizeScalar(u8, PATH, path.delimiter); |
| 645 | 652 | while (path_it.next()) |a_path| { |
| 646 | 653 | var resolved_path_buf: [MAX_PATH_BYTES - 1:0]u8 = undefined; |
| 647 | 654 | const resolved_path = std.fmt.bufPrintZ(&resolved_path_buf, "{s}/{s}", .{ |
| 648 | 655 | a_path, |
| 649 | os.argv[0], | |
| 656 | std.os.argv[0], | |
| 650 | 657 | }) catch continue; |
| 651 | 658 | |
| 652 | 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 | 661 | // found a file, and hope it is the right file |
| 655 | 662 | if (real_path.len > out_buffer.len) |
| 656 | 663 | return error.NameTooLong; |
| ... | ... | @@ -663,13 +670,13 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { |
| 663 | 670 | return error.FileNotFound; |
| 664 | 671 | }, |
| 665 | 672 | .windows => { |
| 666 | const image_path_unicode_string = &os.windows.peb().ProcessParameters.ImagePathName; | |
| 673 | const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName; | |
| 667 | 674 | const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0]; |
| 668 | 675 | |
| 669 | 676 | // If ImagePathName is a symlink, then it will contain the path of the |
| 670 | 677 | // symlink, not the path that the symlink points to. We want the path |
| 671 | 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 | 680 | return std.fs.cwd().realpathW(pathname_w.span(), out_buffer) catch |err| switch (err) { |
| 674 | 681 | error.InvalidWtf8 => unreachable, |
| 675 | 682 | else => |e| return e, |
| ... | ... | @@ -718,11 +725,11 @@ pub fn realpathAlloc(allocator: Allocator, pathname: []const u8) ![]u8 { |
| 718 | 725 | // paths. musl supports passing NULL but restricts the output to PATH_MAX |
| 719 | 726 | // anyway. |
| 720 | 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 | } |
| 723 | 730 | |
| 724 | 731 | test { |
| 725 | if (builtin.os.tag != .wasi) { | |
| 732 | if (native_os != .wasi) { | |
| 726 | 733 | _ = &makeDirAbsolute; |
| 727 | 734 | _ = &makeDirAbsoluteZ; |
| 728 | 735 | _ = &copyFileAbsolute; |
lib/std/fs/AtomicFile.zig+1-2| ... | ... | @@ -85,5 +85,4 @@ const File = std.fs.File; |
| 85 | 85 | const Dir = std.fs.Dir; |
| 86 | 86 | const fs = std.fs; |
| 87 | 87 | const assert = std.debug.assert; |
| 88 | // https://github.com/ziglang/zig/issues/5019 | |
| 89 | const posix = std.os; | |
| 88 | const posix = std.posix; |
lib/std/fs/Dir.zig+76-81| ... | ... | @@ -18,7 +18,7 @@ const IteratorError = error{ |
| 18 | 18 | InvalidUtf8, |
| 19 | 19 | } || posix.UnexpectedError; |
| 20 | 20 | |
| 21 | pub const Iterator = switch (builtin.os.tag) { | |
| 21 | pub const Iterator = switch (native_os) { | |
| 22 | 22 | .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris, .illumos => struct { |
| 23 | 23 | dir: Dir, |
| 24 | 24 | seek: i64, |
| ... | ... | @@ -34,7 +34,7 @@ pub const Iterator = switch (builtin.os.tag) { |
| 34 | 34 | /// Memory such as file names referenced in this returned entry becomes invalid |
| 35 | 35 | /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized. |
| 36 | 36 | pub fn next(self: *Self) Error!?Entry { |
| 37 | switch (builtin.os.tag) { | |
| 37 | switch (native_os) { | |
| 38 | 38 | .macos, .ios => return self.nextDarwin(), |
| 39 | 39 | .freebsd, .netbsd, .dragonfly, .openbsd => return self.nextBsd(), |
| 40 | 40 | .solaris, .illumos => return self.nextSolaris(), |
| ... | ... | @@ -183,7 +183,7 @@ pub const Iterator = switch (builtin.os.tag) { |
| 183 | 183 | |
| 184 | 184 | const name = @as([*]u8, @ptrCast(&bsd_entry.name))[0..bsd_entry.namlen]; |
| 185 | 185 | |
| 186 | const skip_zero_fileno = switch (builtin.os.tag) { | |
| 186 | const skip_zero_fileno = switch (native_os) { | |
| 187 | 187 | // fileno=0 is used to mark invalid entries or deleted files. |
| 188 | 188 | .openbsd, .netbsd => true, |
| 189 | 189 | else => false, |
| ... | ... | @@ -315,7 +315,7 @@ pub const Iterator = switch (builtin.os.tag) { |
| 315 | 315 | dir: Dir, |
| 316 | 316 | // The if guard is solely there to prevent compile errors from missing `linux.dirent64` |
| 317 | 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 | 319 | index: usize, |
| 320 | 320 | end_index: usize, |
| 321 | 321 | first_iter: bool, |
| ... | ... | @@ -348,7 +348,7 @@ pub const Iterator = switch (builtin.os.tag) { |
| 348 | 348 | self.first_iter = false; |
| 349 | 349 | } |
| 350 | 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 | 352 | .SUCCESS => {}, |
| 353 | 353 | .BADF => unreachable, // Dir is invalid or was opened without iteration ability |
| 354 | 354 | .FAULT => unreachable, |
| ... | ... | @@ -398,7 +398,7 @@ pub const Iterator = switch (builtin.os.tag) { |
| 398 | 398 | }, |
| 399 | 399 | .windows => struct { |
| 400 | 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 | 402 | index: usize, |
| 403 | 403 | end_index: usize, |
| 404 | 404 | first_iter: bool, |
| ... | ... | @@ -411,8 +411,8 @@ pub const Iterator = switch (builtin.os.tag) { |
| 411 | 411 | /// Memory such as file names referenced in this returned entry becomes invalid |
| 412 | 412 | /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized. |
| 413 | 413 | pub fn next(self: *Self) Error!?Entry { |
| 414 | const w = windows; | |
| 414 | 415 | while (true) { |
| 415 | const w = std.os.windows; | |
| 416 | 416 | if (self.index >= self.end_index) { |
| 417 | 417 | var io: w.IO_STATUS_BLOCK = undefined; |
| 418 | 418 | const rc = w.ntdll.NtQueryDirectoryFile( |
| ... | ... | @@ -582,7 +582,7 @@ pub fn iterateAssumeFirstIteration(self: Dir) Iterator { |
| 582 | 582 | } |
| 583 | 583 | |
| 584 | 584 | fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator { |
| 585 | switch (builtin.os.tag) { | |
| 585 | switch (native_os) { | |
| 586 | 586 | .macos, |
| 587 | 587 | .ios, |
| 588 | 588 | .freebsd, |
| ... | ... | @@ -770,11 +770,11 @@ pub fn close(self: *Dir) void { |
| 770 | 770 | /// On WASI, `sub_path` should be encoded as valid UTF-8. |
| 771 | 771 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. |
| 772 | 772 | pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { |
| 773 | if (builtin.os.tag == .windows) { | |
| 774 | const path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path); | |
| 773 | if (native_os == .windows) { | |
| 774 | const path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path); | |
| 775 | 775 | return self.openFileW(path_w.span(), flags); |
| 776 | 776 | } |
| 777 | if (builtin.os.tag == .wasi) { | |
| 777 | if (native_os == .wasi) { | |
| 778 | 778 | var base: std.os.wasi.rights_t = .{}; |
| 779 | 779 | if (flags.isRead()) { |
| 780 | 780 | base.FD_READ = true; |
| ... | ... | @@ -803,9 +803,9 @@ pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.Ope |
| 803 | 803 | |
| 804 | 804 | /// Same as `openFile` but the path parameter is null-terminated. |
| 805 | 805 | pub 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 | 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 | 809 | return self.openFileW(path_w.span(), flags); |
| 810 | 810 | }, |
| 811 | 811 | .wasi => { |
| ... | ... | @@ -884,7 +884,7 @@ pub fn openFileZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File |
| 884 | 884 | /// Same as `openFile` but Windows-only and the path parameter is |
| 885 | 885 | /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. |
| 886 | 886 | pub 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 | 888 | const file: File = .{ |
| 889 | 889 | .handle = try w.OpenFile(sub_path_w, .{ |
| 890 | 890 | .dir = self.fd, |
| ... | ... | @@ -925,11 +925,11 @@ pub fn openFileW(self: Dir, sub_path_w: []const u16, flags: File.OpenFlags) File |
| 925 | 925 | /// On WASI, `sub_path` should be encoded as valid UTF-8. |
| 926 | 926 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. |
| 927 | 927 | pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { |
| 928 | if (builtin.os.tag == .windows) { | |
| 929 | const path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path); | |
| 928 | if (native_os == .windows) { | |
| 929 | const path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path); | |
| 930 | 930 | return self.createFileW(path_w.span(), flags); |
| 931 | 931 | } |
| 932 | if (builtin.os.tag == .wasi) { | |
| 932 | if (native_os == .wasi) { | |
| 933 | 933 | return .{ |
| 934 | 934 | .handle = try posix.openatWasi(self.fd, sub_path, .{}, .{ |
| 935 | 935 | .CREAT = true, |
| ... | ... | @@ -957,9 +957,9 @@ pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File |
| 957 | 957 | |
| 958 | 958 | /// Same as `createFile` but the path parameter is null-terminated. |
| 959 | 959 | pub 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 | 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 | 963 | return self.createFileW(path_w.span(), flags); |
| 964 | 964 | }, |
| 965 | 965 | .wasi => { |
| ... | ... | @@ -968,7 +968,7 @@ pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags |
| 968 | 968 | else => {}, |
| 969 | 969 | } |
| 970 | 970 | |
| 971 | var os_flags: std.os.O = .{ | |
| 971 | var os_flags: posix.O = .{ | |
| 972 | 972 | .ACCMODE = if (flags.read) .RDWR else .WRONLY, |
| 973 | 973 | .CREAT = true, |
| 974 | 974 | .TRUNC = flags.truncate, |
| ... | ... | @@ -1032,7 +1032,7 @@ pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags |
| 1032 | 1032 | /// Same as `createFile` but Windows-only and the path parameter is |
| 1033 | 1033 | /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. |
| 1034 | 1034 | pub 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 | 1036 | const read_flag = if (flags.read) @as(u32, w.GENERIC_READ) else 0; |
| 1037 | 1037 | const file: File = .{ |
| 1038 | 1038 | .handle = try w.OpenFile(sub_path_w, .{ |
| ... | ... | @@ -1145,7 +1145,7 @@ pub fn makePath(self: Dir, sub_path: []const u8) !void { |
| 1145 | 1145 | /// have been modified regardless. |
| 1146 | 1146 | /// `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). |
| 1147 | 1147 | fn 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 | 1149 | var it = try fs.path.componentIterator(sub_path); |
| 1150 | 1150 | // If there are no components in the path, then create a dummy component with the full path. |
| 1151 | 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 | 1180 | /// On WASI, `sub_path` should be encoded as valid UTF-8. |
| 1181 | 1181 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. |
| 1182 | 1182 | pub 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 | 1184 | .windows => { |
| 1185 | const w = std.os.windows; | |
| 1185 | const w = windows; | |
| 1186 | 1186 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | |
| 1187 | 1187 | w.SYNCHRONIZE | w.FILE_TRAVERSE | |
| 1188 | 1188 | (if (open_dir_options.iterate) w.FILE_LIST_DIRECTORY else @as(u32, 0)); |
| ... | ... | @@ -1215,11 +1215,11 @@ pub const RealPathError = posix.RealPathError; |
| 1215 | 1215 | /// Currently supported hosts are: Linux, macOS, and Windows. |
| 1216 | 1216 | /// See also `Dir.realpathZ`, `Dir.realpathW`, and `Dir.realpathAlloc`. |
| 1217 | 1217 | pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError![]u8 { |
| 1218 | if (builtin.os.tag == .wasi) { | |
| 1218 | if (native_os == .wasi) { | |
| 1219 | 1219 | @compileError("realpath is not available on WASI"); |
| 1220 | 1220 | } |
| 1221 | if (builtin.os.tag == .windows) { | |
| 1222 | const pathname_w = try std.os.windows.sliceToPrefixedFileW(self.fd, pathname); | |
| 1221 | if (native_os == .windows) { | |
| 1222 | const pathname_w = try windows.sliceToPrefixedFileW(self.fd, pathname); | |
| 1223 | 1223 | return self.realpathW(pathname_w.span(), out_buffer); |
| 1224 | 1224 | } |
| 1225 | 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 | 1229 | /// Same as `Dir.realpath` except `pathname` is null-terminated. |
| 1230 | 1230 | /// See also `Dir.realpath`, `realpathZ`. |
| 1231 | 1231 | pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathError![]u8 { |
| 1232 | if (builtin.os.tag == .windows) { | |
| 1233 | const pathname_w = try posix.windows.cStrToPrefixedFileW(self.fd, pathname); | |
| 1232 | if (native_os == .windows) { | |
| 1233 | const pathname_w = try windows.cStrToPrefixedFileW(self.fd, pathname); | |
| 1234 | 1234 | return self.realpathW(pathname_w.span(), out_buffer); |
| 1235 | 1235 | } |
| 1236 | 1236 | |
| 1237 | const flags: posix.O = switch (builtin.os.tag) { | |
| 1237 | const flags: posix.O = switch (native_os) { | |
| 1238 | 1238 | .linux => .{ |
| 1239 | 1239 | .NONBLOCK = true, |
| 1240 | 1240 | .CLOEXEC = true, |
| ... | ... | @@ -1255,14 +1255,8 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathE |
| 1255 | 1255 | }; |
| 1256 | 1256 | defer posix.close(fd); |
| 1257 | 1257 | |
| 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 | 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); | |
| 1266 | 1260 | |
| 1267 | 1261 | if (out_path.len > out_buffer.len) { |
| 1268 | 1262 | return error.NameTooLong; |
| ... | ... | @@ -1277,7 +1271,7 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathE |
| 1277 | 1271 | /// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). |
| 1278 | 1272 | /// See also `Dir.realpath`, `realpathW`. |
| 1279 | 1273 | pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) RealPathError![]u8 { |
| 1280 | const w = std.os.windows; | |
| 1274 | const w = windows; | |
| 1281 | 1275 | |
| 1282 | 1276 | const access_mask = w.GENERIC_READ | w.SYNCHRONIZE; |
| 1283 | 1277 | const share_access = w.FILE_SHARE_READ; |
| ... | ... | @@ -1331,16 +1325,16 @@ pub fn realpathAlloc(self: Dir, allocator: Allocator, pathname: []const u8) Real |
| 1331 | 1325 | /// Not all targets support this. For example, WASI does not have the concept |
| 1332 | 1326 | /// of a current working directory. |
| 1333 | 1327 | pub fn setAsCwd(self: Dir) !void { |
| 1334 | if (builtin.os.tag == .wasi) { | |
| 1328 | if (native_os == .wasi) { | |
| 1335 | 1329 | @compileError("changing cwd is not currently possible in WASI"); |
| 1336 | 1330 | } |
| 1337 | if (builtin.os.tag == .windows) { | |
| 1338 | var dir_path_buffer: [std.os.windows.PATH_MAX_WIDE]u16 = undefined; | |
| 1339 | const dir_path = try std.os.windows.GetFinalPathNameByHandle(self.fd, .{}, &dir_path_buffer); | |
| 1331 | if (native_os == .windows) { | |
| 1332 | var dir_path_buffer: [windows.PATH_MAX_WIDE]u16 = undefined; | |
| 1333 | const dir_path = try windows.GetFinalPathNameByHandle(self.fd, .{}, &dir_path_buffer); | |
| 1340 | 1334 | if (builtin.link_libc) { |
| 1341 | 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 | 1339 | try posix.fchdir(self.fd); |
| 1346 | 1340 | } |
| ... | ... | @@ -1368,9 +1362,9 @@ pub const OpenDirOptions = struct { |
| 1368 | 1362 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. |
| 1369 | 1363 | /// Asserts that the path parameter has no null bytes. |
| 1370 | 1364 | pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir { |
| 1371 | switch (builtin.os.tag) { | |
| 1365 | switch (native_os) { | |
| 1372 | 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 | 1368 | return self.openDirW(sub_path_w.span().ptr, args); |
| 1375 | 1369 | }, |
| 1376 | 1370 | .wasi => { |
| ... | ... | @@ -1427,9 +1421,9 @@ pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError! |
| 1427 | 1421 | |
| 1428 | 1422 | /// Same as `openDir` except the parameter is null-terminated. |
| 1429 | 1423 | pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir { |
| 1430 | switch (builtin.os.tag) { | |
| 1424 | switch (native_os) { | |
| 1431 | 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 | 1427 | return self.openDirW(sub_path_w.span().ptr, args); |
| 1434 | 1428 | }, |
| 1435 | 1429 | .wasi => { |
| ... | ... | @@ -1453,7 +1447,7 @@ pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) Open |
| 1453 | 1447 | /// Same as `openDir` except the path parameter is WTF-16 LE encoded, NT-prefixed. |
| 1454 | 1448 | /// This function asserts the target OS is Windows. |
| 1455 | 1449 | pub 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 | 1451 | // TODO remove some of these flags if args.access_sub_paths is false |
| 1458 | 1452 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | |
| 1459 | 1453 | w.SYNCHRONIZE | w.FILE_TRAVERSE; |
| ... | ... | @@ -1487,7 +1481,7 @@ const MakeOpenDirAccessMaskWOptions = struct { |
| 1487 | 1481 | }; |
| 1488 | 1482 | |
| 1489 | 1483 | fn 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; | |
| 1491 | 1485 | |
| 1492 | 1486 | var result = Dir{ |
| 1493 | 1487 | .fd = undefined, |
| ... | ... | @@ -1545,10 +1539,10 @@ pub const DeleteFileError = posix.UnlinkError; |
| 1545 | 1539 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. |
| 1546 | 1540 | /// Asserts that the path parameter has no null bytes. |
| 1547 | 1541 | pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void { |
| 1548 | if (builtin.os.tag == .windows) { | |
| 1549 | const sub_path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path); | |
| 1542 | if (native_os == .windows) { | |
| 1543 | const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path); | |
| 1550 | 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 | 1546 | posix.unlinkat(self.fd, sub_path, 0) catch |err| switch (err) { |
| 1553 | 1547 | error.DirNotEmpty => unreachable, // not passing AT.REMOVEDIR |
| 1554 | 1548 | else => |e| return e, |
| ... | ... | @@ -1563,7 +1557,7 @@ pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void { |
| 1563 | 1557 | pub fn deleteFileZ(self: Dir, sub_path_c: [*:0]const u8) DeleteFileError!void { |
| 1564 | 1558 | posix.unlinkatZ(self.fd, sub_path_c, 0) catch |err| switch (err) { |
| 1565 | 1559 | error.DirNotEmpty => unreachable, // not passing AT.REMOVEDIR |
| 1566 | error.AccessDenied => |e| switch (builtin.os.tag) { | |
| 1560 | error.AccessDenied => |e| switch (native_os) { | |
| 1567 | 1561 | // non-Linux POSIX systems return EPERM when trying to delete a directory, so |
| 1568 | 1562 | // we need to handle that case specifically and translate the error |
| 1569 | 1563 | .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris, .illumos => { |
| ... | ... | @@ -1615,10 +1609,10 @@ pub const DeleteDirError = error{ |
| 1615 | 1609 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. |
| 1616 | 1610 | /// Asserts that the path parameter has no null bytes. |
| 1617 | 1611 | pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void { |
| 1618 | if (builtin.os.tag == .windows) { | |
| 1619 | const sub_path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path); | |
| 1612 | if (native_os == .windows) { | |
| 1613 | const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path); | |
| 1620 | 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 | 1616 | posix.unlinkat(self.fd, sub_path, posix.AT.REMOVEDIR) catch |err| switch (err) { |
| 1623 | 1617 | error.IsDir => unreachable, // not possible since we pass AT.REMOVEDIR |
| 1624 | 1618 | else => |e| return e, |
| ... | ... | @@ -1691,15 +1685,15 @@ pub fn symLink( |
| 1691 | 1685 | sym_link_path: []const u8, |
| 1692 | 1686 | flags: SymLinkFlags, |
| 1693 | 1687 | ) !void { |
| 1694 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | |
| 1688 | if (native_os == .wasi and !builtin.link_libc) { | |
| 1695 | 1689 | return self.symLinkWasi(target_path, sym_link_path, flags); |
| 1696 | 1690 | } |
| 1697 | if (builtin.os.tag == .windows) { | |
| 1691 | if (native_os == .windows) { | |
| 1698 | 1692 | // Target path does not use sliceToPrefixedFileW because certain paths |
| 1699 | 1693 | // are handled differently when creating a symlink than they would be |
| 1700 | 1694 | // when converting to an NT namespaced path. CreateSymbolicLink in |
| 1701 | 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 | 1697 | target_path_w.len = try std.unicode.wtf8ToWtf16Le(&target_path_w.data, target_path); |
| 1704 | 1698 | target_path_w.data[target_path_w.len] = 0; |
| 1705 | 1699 | // However, we need to canonicalize any path separators to `\`, since if |
| ... | ... | @@ -1711,7 +1705,7 @@ pub fn symLink( |
| 1711 | 1705 | mem.nativeToLittle(u16, '\\'), |
| 1712 | 1706 | ); |
| 1713 | 1707 | |
| 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 | 1709 | return self.symLinkW(target_path_w.span(), sym_link_path_w.span(), flags); |
| 1716 | 1710 | } |
| 1717 | 1711 | const target_path_c = try posix.toPosixPath(target_path); |
| ... | ... | @@ -1736,9 +1730,9 @@ pub fn symLinkZ( |
| 1736 | 1730 | sym_link_path_c: [*:0]const u8, |
| 1737 | 1731 | flags: SymLinkFlags, |
| 1738 | 1732 | ) !void { |
| 1739 | if (builtin.os.tag == .windows) { | |
| 1740 | const target_path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, target_path_c); | |
| 1741 | const sym_link_path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, sym_link_path_c); | |
| 1733 | if (native_os == .windows) { | |
| 1734 | const target_path_w = try windows.cStrToPrefixedFileW(self.fd, target_path_c); | |
| 1735 | const sym_link_path_w = try windows.cStrToPrefixedFileW(self.fd, sym_link_path_c); | |
| 1742 | 1736 | return self.symLinkW(target_path_w.span(), sym_link_path_w.span(), flags); |
| 1743 | 1737 | } |
| 1744 | 1738 | return posix.symlinkatZ(target_path_c, self.fd, sym_link_path_c); |
| ... | ... | @@ -1756,7 +1750,7 @@ pub fn symLinkW( |
| 1756 | 1750 | sym_link_path_w: []const u16, |
| 1757 | 1751 | flags: SymLinkFlags, |
| 1758 | 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 | } |
| 1761 | 1755 | |
| 1762 | 1756 | pub const ReadLinkError = posix.ReadLinkError; |
| ... | ... | @@ -1768,11 +1762,11 @@ pub const ReadLinkError = posix.ReadLinkError; |
| 1768 | 1762 | /// On WASI, `sub_path` should be encoded as valid UTF-8. |
| 1769 | 1763 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. |
| 1770 | 1764 | pub 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 | 1766 | return self.readLinkWasi(sub_path, buffer); |
| 1773 | 1767 | } |
| 1774 | if (builtin.os.tag == .windows) { | |
| 1775 | const sub_path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path); | |
| 1768 | if (native_os == .windows) { | |
| 1769 | const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path); | |
| 1776 | 1770 | return self.readLinkW(sub_path_w.span(), buffer); |
| 1777 | 1771 | } |
| 1778 | 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 | 1780 | |
| 1787 | 1781 | /// Same as `readLink`, except the `sub_path_c` parameter is null-terminated. |
| 1788 | 1782 | pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 { |
| 1789 | if (builtin.os.tag == .windows) { | |
| 1790 | const sub_path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, sub_path_c); | |
| 1783 | if (native_os == .windows) { | |
| 1784 | const sub_path_w = try windows.cStrToPrefixedFileW(self.fd, sub_path_c); | |
| 1791 | 1785 | return self.readLinkW(sub_path_w.span(), buffer); |
| 1792 | 1786 | } |
| 1793 | 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 | 1790 | /// Windows-only. Same as `readLink` except the pathname parameter |
| 1797 | 1791 | /// is WTF16 LE encoded. |
| 1798 | 1792 | pub 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 | } |
| 1801 | 1795 | |
| 1802 | 1796 | /// Read all of file contents using a preallocated buffer. |
| ... | ... | @@ -2319,8 +2313,8 @@ pub const AccessError = posix.AccessError; |
| 2319 | 2313 | /// For example, instead of testing if a file exists and then opening it, just |
| 2320 | 2314 | /// open it and handle the error for file not found. |
| 2321 | 2315 | pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void { |
| 2322 | if (builtin.os.tag == .windows) { | |
| 2323 | const sub_path_w = std.os.windows.sliceToPrefixedFileW(self.fd, sub_path) catch |err| switch (err) { | |
| 2316 | if (native_os == .windows) { | |
| 2317 | const sub_path_w = windows.sliceToPrefixedFileW(self.fd, sub_path) catch |err| switch (err) { | |
| 2324 | 2318 | error.AccessDenied => return error.PermissionDenied, |
| 2325 | 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 | 2326 | |
| 2333 | 2327 | /// Same as `access` except the path parameter is null-terminated. |
| 2334 | 2328 | pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void { |
| 2335 | if (builtin.os.tag == .windows) { | |
| 2336 | const sub_path_w = std.os.windows.cStrToPrefixedFileW(self.fd, sub_path) catch |err| switch (err) { | |
| 2329 | if (native_os == .windows) { | |
| 2330 | const sub_path_w = windows.cStrToPrefixedFileW(self.fd, sub_path) catch |err| switch (err) { | |
| 2337 | 2331 | error.AccessDenied => return error.PermissionDenied, |
| 2338 | 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 | 2349 | /// TODO currently this ignores `flags`. |
| 2356 | 2350 | pub fn accessW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) AccessError!void { |
| 2357 | 2351 | _ = flags; |
| 2358 | return posix.faccessatW(self.fd, sub_path_w, 0, 0); | |
| 2352 | return posix.faccessatW(self.fd, sub_path_w); | |
| 2359 | 2353 | } |
| 2360 | 2354 | |
| 2361 | 2355 | pub 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 | 2467 | } |
| 2474 | 2468 | } |
| 2475 | 2469 | |
| 2476 | if (builtin.os.tag == .linux) { | |
| 2470 | if (native_os == .linux) { | |
| 2477 | 2471 | // Try copy_file_range first as that works at the FS level and is the |
| 2478 | 2472 | // most efficient method (if available). |
| 2479 | 2473 | var offset: u64 = 0; |
| ... | ... | @@ -2555,13 +2549,13 @@ pub const StatFileError = File.OpenError || File.StatError || posix.FStatAtError |
| 2555 | 2549 | /// On WASI, `sub_path` should be encoded as valid UTF-8. |
| 2556 | 2550 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. |
| 2557 | 2551 | pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat { |
| 2558 | if (builtin.os.tag == .windows) { | |
| 2552 | if (native_os == .windows) { | |
| 2559 | 2553 | var file = try self.openFile(sub_path, .{}); |
| 2560 | 2554 | defer file.close(); |
| 2561 | 2555 | return file.stat(); |
| 2562 | 2556 | } |
| 2563 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | |
| 2564 | const st = try posix.fstatat_wasi(self.fd, sub_path, .{ .SYMLINK_FOLLOW = true }); | |
| 2557 | if (native_os == .wasi and !builtin.link_libc) { | |
| 2558 | const st = try std.os.fstatat_wasi(self.fd, sub_path, .{ .SYMLINK_FOLLOW = true }); | |
| 2565 | 2559 | return Stat.fromWasi(st); |
| 2566 | 2560 | } |
| 2567 | 2561 | const st = try posix.fstatat(self.fd, sub_path, 0); |
| ... | ... | @@ -2617,9 +2611,10 @@ const builtin = @import("builtin"); |
| 2617 | 2611 | const std = @import("../std.zig"); |
| 2618 | 2612 | const File = std.fs.File; |
| 2619 | 2613 | const AtomicFile = std.fs.AtomicFile; |
| 2620 | // https://github.com/ziglang/zig/issues/5019 | |
| 2621 | const posix = std.os; | |
| 2614 | const posix = std.posix; | |
| 2622 | 2615 | const mem = std.mem; |
| 2623 | 2616 | const fs = std.fs; |
| 2624 | 2617 | const Allocator = std.mem.Allocator; |
| 2625 | 2618 | const assert = std.debug.assert; |
| 2619 | const windows = std.os.windows; | |
| 2620 | const native_os = builtin.os.tag; |
lib/std/fs/File.zig+56-5| ... | ... | @@ -193,6 +193,58 @@ pub fn isTty(self: File) bool { |
| 193 | 193 | return posix.isatty(self.handle); |
| 194 | 194 | } |
| 195 | 195 | |
| 196 | pub 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 | 248 | /// Test whether ANSI escape codes will be treated as such. |
| 197 | 249 | pub fn supportsAnsiEscapeCodes(self: File) bool { |
| 198 | 250 | if (builtin.os.tag == .windows) { |
| ... | ... | @@ -201,7 +253,7 @@ pub fn supportsAnsiEscapeCodes(self: File) bool { |
| 201 | 253 | if (console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return true; |
| 202 | 254 | } |
| 203 | 255 | |
| 204 | return posix.isCygwinPty(self.handle); | |
| 256 | return self.isCygwinPty(); | |
| 205 | 257 | } |
| 206 | 258 | if (builtin.os.tag == .wasi) { |
| 207 | 259 | // WASI sanitizes stdout when fd is a tty so ANSI escape codes |
| ... | ... | @@ -405,7 +457,7 @@ pub fn stat(self: File) StatError!Stat { |
| 405 | 457 | } |
| 406 | 458 | |
| 407 | 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 | 461 | return Stat.fromWasi(st); |
| 410 | 462 | } |
| 411 | 463 | |
| ... | ... | @@ -952,7 +1004,7 @@ pub fn metadata(self: File) MetadataError!Metadata { |
| 952 | 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 | 1008 | else => .{ .stat = try posix.fstat(self.handle) }, |
| 957 | 1009 | }, |
| 958 | 1010 | }; |
| ... | ... | @@ -1634,8 +1686,7 @@ const File = @This(); |
| 1634 | 1686 | const std = @import("../std.zig"); |
| 1635 | 1687 | const builtin = @import("builtin"); |
| 1636 | 1688 | const Allocator = std.mem.Allocator; |
| 1637 | // https://github.com/ziglang/zig/issues/5019 | |
| 1638 | const posix = std.os; | |
| 1689 | const posix = std.posix; | |
| 1639 | 1690 | const io = std.io; |
| 1640 | 1691 | const math = std.math; |
| 1641 | 1692 | const assert = std.debug.assert; |
lib/std/fs/get_app_data_dir.zig+8-7| ... | ... | @@ -3,7 +3,8 @@ const builtin = @import("builtin"); |
| 3 | 3 | const unicode = std.unicode; |
| 4 | 4 | const mem = std.mem; |
| 5 | 5 | const fs = std.fs; |
| 6 | const os = std.os; | |
| 6 | const native_os = builtin.os.tag; | |
| 7 | const posix = std.posix; | |
| 7 | 8 | |
| 8 | 9 | pub const GetAppDataDirError = error{ |
| 9 | 10 | OutOfMemory, |
| ... | ... | @@ -13,7 +14,7 @@ pub const GetAppDataDirError = error{ |
| 13 | 14 | /// Caller owns returned memory. |
| 14 | 15 | /// TODO determine if we can remove the allocator requirement |
| 15 | 16 | pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 { |
| 16 | switch (builtin.os.tag) { | |
| 17 | switch (native_os) { | |
| 17 | 18 | .windows => { |
| 18 | 19 | const local_app_data_dir = std.process.getEnvVarOwned(allocator, "LOCALAPPDATA") catch |err| switch (err) { |
| 19 | 20 | error.OutOfMemory => |e| return e, |
| ... | ... | @@ -23,18 +24,18 @@ pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDi |
| 23 | 24 | return fs.path.join(allocator, &[_][]const u8{ local_app_data_dir, appname }); |
| 24 | 25 | }, |
| 25 | 26 | .macos => { |
| 26 | const home_dir = os.getenv("HOME") orelse { | |
| 27 | const home_dir = posix.getenv("HOME") orelse { | |
| 27 | 28 | // TODO look in /etc/passwd |
| 28 | 29 | return error.AppDataDirUnavailable; |
| 29 | 30 | }; |
| 30 | 31 | return fs.path.join(allocator, &[_][]const u8{ home_dir, "Library", "Application Support", appname }); |
| 31 | 32 | }, |
| 32 | 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 | 35 | return fs.path.join(allocator, &[_][]const u8{ xdg, appname }); |
| 35 | 36 | } |
| 36 | 37 | |
| 37 | const home_dir = os.getenv("HOME") orelse { | |
| 38 | const home_dir = posix.getenv("HOME") orelse { | |
| 38 | 39 | // TODO look in /etc/passwd |
| 39 | 40 | return error.AppDataDirUnavailable; |
| 40 | 41 | }; |
| ... | ... | @@ -48,7 +49,7 @@ pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDi |
| 48 | 49 | } |
| 49 | 50 | // TODO look into directory_which |
| 50 | 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 | 53 | const settings_dir = try allocator.dupeZ(u8, mem.sliceTo(dir_path_ptr, 0)); |
| 53 | 54 | defer allocator.free(settings_dir); |
| 54 | 55 | switch (rc) { |
| ... | ... | @@ -61,7 +62,7 @@ pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDi |
| 61 | 62 | } |
| 62 | 63 | |
| 63 | 64 | test "getAppDataDir" { |
| 64 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 65 | if (native_os == .wasi) return error.SkipZigTest; | |
| 65 | 66 | |
| 66 | 67 | // We can't actually validate the result |
| 67 | 68 | const dir = getAppDataDir(std.testing.allocator, "zig") catch return; |
lib/std/fs/test.zig+71-71| ... | ... | @@ -1,10 +1,12 @@ |
| 1 | 1 | const std = @import("../std.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const testing = std.testing; |
| 4 | const os = std.os; | |
| 5 | 4 | const fs = std.fs; |
| 6 | 5 | const mem = std.mem; |
| 7 | 6 | const wasi = std.os.wasi; |
| 7 | const native_os = builtin.os.tag; | |
| 8 | const windows = std.os.windows; | |
| 9 | const posix = std.posix; | |
| 8 | 10 | |
| 9 | 11 | const ArenaAllocator = std.heap.ArenaAllocator; |
| 10 | 12 | const Dir = std.fs.Dir; |
| ... | ... | @@ -25,7 +27,7 @@ const PathType = enum { |
| 25 | 27 | }; |
| 26 | 28 | } |
| 27 | 29 | |
| 28 | pub const TransformError = std.os.RealPathError || error{OutOfMemory}; | |
| 30 | pub const TransformError = posix.RealPathError || error{OutOfMemory}; | |
| 29 | 31 | pub const TransformFn = fn (allocator: mem.Allocator, dir: Dir, relative_path: [:0]const u8) TransformError![:0]const u8; |
| 30 | 32 | |
| 31 | 33 | pub fn getTransformFn(comptime path_type: PathType) TransformFn { |
| ... | ... | @@ -42,7 +44,7 @@ const PathType = enum { |
| 42 | 44 | // The final path may not actually exist which would cause realpath to fail. |
| 43 | 45 | // So instead, we get the path of the dir and join it with the relative path. |
| 44 | 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 | 48 | return fs.path.joinZ(allocator, &.{ dir_path, relative_path }); |
| 47 | 49 | } |
| 48 | 50 | }.transform, |
| ... | ... | @@ -51,8 +53,8 @@ const PathType = enum { |
| 51 | 53 | // Any drive absolute path (C:\foo) can be converted into a UNC path by |
| 52 | 54 | // using '127.0.0.1' as the server name and '<drive letter>$' as the share name. |
| 53 | 55 | var fd_path_buf: [fs.MAX_PATH_BYTES]u8 = undefined; |
| 54 | const dir_path = try os.getFdPath(dir.fd, &fd_path_buf); | |
| 55 | const windows_path_type = std.os.windows.getUnprefixedPathType(u8, dir_path); | |
| 56 | const dir_path = try std.os.getFdPath(dir.fd, &fd_path_buf); | |
| 57 | const windows_path_type = windows.getUnprefixedPathType(u8, dir_path); | |
| 56 | 58 | switch (windows_path_type) { |
| 57 | 59 | .unc_absolute => return fs.path.joinZ(allocator, &.{ dir_path, relative_path }), |
| 58 | 60 | .drive_absolute => { |
| ... | ... | @@ -102,7 +104,7 @@ const TestContext = struct { |
| 102 | 104 | pub fn transformPath(self: *TestContext, relative_path: [:0]const u8) ![:0]const u8 { |
| 103 | 105 | const allocator = self.arena.allocator(); |
| 104 | 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 | 108 | const transformed_sep_path = try allocator.dupeZ(u8, transformed_path); |
| 107 | 109 | std.mem.replaceScalar(u8, transformed_sep_path, switch (self.path_sep) { |
| 108 | 110 | '/' => '\\', |
| ... | ... | @@ -119,7 +121,7 @@ const TestContext = struct { |
| 119 | 121 | /// If path separators are replaced, then the result is allocated by the |
| 120 | 122 | /// TestContext's arena and will be free'd during `TestContext.deinit`. |
| 121 | 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 | 125 | const allocator = self.arena.allocator(); |
| 124 | 126 | const transformed_sep_path = try allocator.dupeZ(u8, path); |
| 125 | 127 | std.mem.replaceScalar(u8, transformed_sep_path, '/', '\\'); |
| ... | ... | @@ -157,7 +159,7 @@ fn testWithPathTypeIfSupported(comptime path_type: PathType, comptime path_sep: |
| 157 | 159 | fn setupSymlink(dir: Dir, target: []const u8, link: []const u8, flags: SymLinkFlags) !void { |
| 158 | 160 | return dir.symLink(target, link, flags) catch |err| switch (err) { |
| 159 | 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 | 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 | 168 | // AccessDenied, then make the test failure silent (it is not a Zig failure). |
| 167 | 169 | fn setupSymlinkAbsolute(target: []const u8, link: []const u8, flags: SymLinkFlags) !void { |
| 168 | 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 | 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 | 234 | |
| 233 | 235 | var symlink = switch (builtin.target.os.tag) { |
| 234 | 236 | .windows => windows_symlink: { |
| 235 | const w = std.os.windows; | |
| 236 | ||
| 237 | const sub_path_w = try std.os.windows.cStrToPrefixedFileW(ctx.dir.fd, "symlink"); | |
| 237 | const sub_path_w = try windows.cStrToPrefixedFileW(ctx.dir.fd, "symlink"); | |
| 238 | 238 | |
| 239 | 239 | var result = Dir{ |
| 240 | 240 | .fd = undefined, |
| 241 | 241 | }; |
| 242 | 242 | |
| 243 | 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 | 245 | .Length = path_len_bytes, |
| 246 | 246 | .MaximumLength = path_len_bytes, |
| 247 | 247 | .Buffer = @constCast(&sub_path_w.data), |
| 248 | 248 | }; |
| 249 | var attr = w.OBJECT_ATTRIBUTES{ | |
| 250 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | |
| 249 | var attr = windows.OBJECT_ATTRIBUTES{ | |
| 250 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | |
| 251 | 251 | .RootDirectory = if (fs.path.isAbsoluteWindowsW(sub_path_w.span())) null else ctx.dir.fd, |
| 252 | 252 | .Attributes = 0, |
| 253 | 253 | .ObjectName = &nt_name, |
| 254 | 254 | .SecurityDescriptor = null, |
| 255 | 255 | .SecurityQualityOfService = null, |
| 256 | 256 | }; |
| 257 | var io: w.IO_STATUS_BLOCK = undefined; | |
| 258 | const rc = w.ntdll.NtCreateFile( | |
| 257 | var io: windows.IO_STATUS_BLOCK = undefined; | |
| 258 | const rc = windows.ntdll.NtCreateFile( | |
| 259 | 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 | 261 | &attr, |
| 262 | 262 | &io, |
| 263 | 263 | null, |
| 264 | w.FILE_ATTRIBUTE_NORMAL, | |
| 265 | w.FILE_SHARE_READ | w.FILE_SHARE_WRITE, | |
| 266 | w.FILE_OPEN, | |
| 264 | windows.FILE_ATTRIBUTE_NORMAL, | |
| 265 | windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE, | |
| 266 | windows.FILE_OPEN, | |
| 267 | 267 | // 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 | 269 | null, |
| 270 | 270 | 0, |
| 271 | 271 | ); |
| 272 | 272 | |
| 273 | 273 | switch (rc) { |
| 274 | 274 | .SUCCESS => break :windows_symlink result, |
| 275 | else => return w.unexpectedStatus(rc), | |
| 275 | else => return windows.unexpectedStatus(rc), | |
| 276 | 276 | } |
| 277 | 277 | }, |
| 278 | 278 | .linux => linux_symlink: { |
| 279 | const sub_path_c = try os.toPosixPath("symlink"); | |
| 279 | const sub_path_c = try posix.toPosixPath("symlink"); | |
| 280 | 280 | // the O_NOFOLLOW | O_PATH combination can obtain a fd to a symlink |
| 281 | 281 | // note that if O_DIRECTORY is set, then this will error with ENOTDIR |
| 282 | const flags: os.O = .{ | |
| 282 | const flags: posix.O = .{ | |
| 283 | 283 | .NOFOLLOW = true, |
| 284 | 284 | .PATH = true, |
| 285 | 285 | .ACCMODE = .RDONLY, |
| 286 | 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 | 289 | break :linux_symlink Dir{ .fd = fd }; |
| 290 | 290 | }, |
| 291 | 291 | else => unreachable, |
| ... | ... | @@ -315,7 +315,7 @@ test "openDir" { |
| 315 | 315 | } |
| 316 | 316 | |
| 317 | 317 | test "accessAbsolute" { |
| 318 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 318 | if (native_os == .wasi) return error.SkipZigTest; | |
| 319 | 319 | |
| 320 | 320 | var tmp = tmpDir(.{}); |
| 321 | 321 | defer tmp.cleanup(); |
| ... | ... | @@ -333,7 +333,7 @@ test "accessAbsolute" { |
| 333 | 333 | } |
| 334 | 334 | |
| 335 | 335 | test "openDirAbsolute" { |
| 336 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 336 | if (native_os == .wasi) return error.SkipZigTest; | |
| 337 | 337 | |
| 338 | 338 | var tmp = tmpDir(.{}); |
| 339 | 339 | defer tmp.cleanup(); |
| ... | ... | @@ -361,14 +361,14 @@ test "openDirAbsolute" { |
| 361 | 361 | } |
| 362 | 362 | |
| 363 | 363 | test "openDir cwd parent '..'" { |
| 364 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 364 | if (native_os == .wasi) return error.SkipZigTest; | |
| 365 | 365 | |
| 366 | 366 | var dir = try fs.cwd().openDir("..", .{}); |
| 367 | 367 | defer dir.close(); |
| 368 | 368 | } |
| 369 | 369 | |
| 370 | 370 | test "openDir non-cwd parent '..'" { |
| 371 | switch (builtin.os.tag) { | |
| 371 | switch (native_os) { | |
| 372 | 372 | .wasi, .netbsd, .openbsd => return error.SkipZigTest, |
| 373 | 373 | else => {}, |
| 374 | 374 | } |
| ... | ... | @@ -392,7 +392,7 @@ test "openDir non-cwd parent '..'" { |
| 392 | 392 | } |
| 393 | 393 | |
| 394 | 394 | test "readLinkAbsolute" { |
| 395 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 395 | if (native_os == .wasi) return error.SkipZigTest; | |
| 396 | 396 | |
| 397 | 397 | var tmp = tmpDir(.{}); |
| 398 | 398 | defer tmp.cleanup(); |
| ... | ... | @@ -587,7 +587,7 @@ test "Dir.Iterator but dir is deleted during iteration" { |
| 587 | 587 | try std.testing.expect(entry == null); |
| 588 | 588 | |
| 589 | 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 | 591 | try std.testing.expectError(error.DirNotFound, iterator.nextLinux()); |
| 592 | 592 | } |
| 593 | 593 | } |
| ... | ... | @@ -744,7 +744,7 @@ test "directory operations on files" { |
| 744 | 744 | |
| 745 | 745 | test "file operations on directories" { |
| 746 | 746 | // 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; | |
| 748 | 748 | |
| 749 | 749 | try testWithAllSupportedPathTypes(struct { |
| 750 | 750 | fn impl(ctx: *TestContext) !void { |
| ... | ... | @@ -754,7 +754,7 @@ test "file operations on directories" { |
| 754 | 754 | |
| 755 | 755 | try testing.expectError(error.IsDir, ctx.dir.createFile(test_dir_name, .{})); |
| 756 | 756 | try testing.expectError(error.IsDir, ctx.dir.deleteFile(test_dir_name)); |
| 757 | switch (builtin.os.tag) { | |
| 757 | switch (native_os) { | |
| 758 | 758 | // no error when reading a directory. |
| 759 | 759 | .dragonfly, .netbsd => {}, |
| 760 | 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 | 895 | |
| 896 | 896 | test "Dir.rename directory onto empty dir" { |
| 897 | 897 | // 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; | |
| 899 | 899 | |
| 900 | 900 | try testWithAllSupportedPathTypes(struct { |
| 901 | 901 | fn impl(ctx: *TestContext) !void { |
| ... | ... | @@ -916,7 +916,7 @@ test "Dir.rename directory onto empty dir" { |
| 916 | 916 | |
| 917 | 917 | test "Dir.rename directory onto non-empty dir" { |
| 918 | 918 | // 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; | |
| 920 | 920 | |
| 921 | 921 | try testWithAllSupportedPathTypes(struct { |
| 922 | 922 | fn impl(ctx: *TestContext) !void { |
| ... | ... | @@ -942,7 +942,7 @@ test "Dir.rename directory onto non-empty dir" { |
| 942 | 942 | |
| 943 | 943 | test "Dir.rename file <-> dir" { |
| 944 | 944 | // 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; | |
| 946 | 946 | |
| 947 | 947 | try testWithAllSupportedPathTypes(struct { |
| 948 | 948 | fn impl(ctx: *TestContext) !void { |
| ... | ... | @@ -979,7 +979,7 @@ test "rename" { |
| 979 | 979 | } |
| 980 | 980 | |
| 981 | 981 | test "renameAbsolute" { |
| 982 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 982 | if (native_os == .wasi) return error.SkipZigTest; | |
| 983 | 983 | |
| 984 | 984 | var tmp_dir = tmpDir(.{}); |
| 985 | 985 | defer tmp_dir.cleanup(); |
| ... | ... | @@ -1032,14 +1032,14 @@ test "renameAbsolute" { |
| 1032 | 1032 | } |
| 1033 | 1033 | |
| 1034 | 1034 | test "openSelfExe" { |
| 1035 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 1035 | if (native_os == .wasi) return error.SkipZigTest; | |
| 1036 | 1036 | |
| 1037 | 1037 | const self_exe_file = try std.fs.openSelfExe(.{}); |
| 1038 | 1038 | self_exe_file.close(); |
| 1039 | 1039 | } |
| 1040 | 1040 | |
| 1041 | 1041 | test "selfExePath" { |
| 1042 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 1042 | if (native_os == .wasi) return error.SkipZigTest; | |
| 1043 | 1043 | |
| 1044 | 1044 | var buf: [fs.MAX_PATH_BYTES]u8 = undefined; |
| 1045 | 1045 | const buf_self_exe_path = try std.fs.selfExePath(&buf); |
| ... | ... | @@ -1120,7 +1120,7 @@ test "makePath, put some files in it, deleteTreeMinStackSize" { |
| 1120 | 1120 | } |
| 1121 | 1121 | |
| 1122 | 1122 | test "makePath in a directory that no longer exists" { |
| 1123 | if (builtin.os.tag == .windows) return error.SkipZigTest; // Windows returns FileBusy if attempting to remove an open dir | |
| 1123 | if (native_os == .windows) return error.SkipZigTest; // Windows returns FileBusy if attempting to remove an open dir | |
| 1124 | 1124 | |
| 1125 | 1125 | var tmp = tmpDir(.{}); |
| 1126 | 1126 | defer tmp.cleanup(); |
| ... | ... | @@ -1182,7 +1182,7 @@ test "makepath relative walks" { |
| 1182 | 1182 | try tmp.dir.makePath(relPath); |
| 1183 | 1183 | |
| 1184 | 1184 | // How .. is handled is different on Windows than non-Windows |
| 1185 | switch (builtin.os.tag) { | |
| 1185 | switch (native_os) { | |
| 1186 | 1186 | .windows => { |
| 1187 | 1187 | // On Windows, .. is resolved before passing the path to NtCreateFile, |
| 1188 | 1188 | // meaning everything except `first/C` drops out. |
| ... | ... | @@ -1248,12 +1248,12 @@ test "max file name component lengths" { |
| 1248 | 1248 | var tmp = tmpDir(.{ .iterate = true }); |
| 1249 | 1249 | defer tmp.cleanup(); |
| 1250 | 1250 | |
| 1251 | if (builtin.os.tag == .windows) { | |
| 1251 | if (native_os == .windows) { | |
| 1252 | 1252 | // U+FFFF is the character with the largest code point that is encoded as a single |
| 1253 | 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 | 1255 | try testFilenameLimits(tmp.dir, &maxed_windows_filename); |
| 1256 | } else if (builtin.os.tag == .wasi) { | |
| 1256 | } else if (native_os == .wasi) { | |
| 1257 | 1257 | // On WASI, the maxed filename depends on the host OS, so in order for this test to |
| 1258 | 1258 | // work on any host, we need to use a length that will work for all platforms |
| 1259 | 1259 | // (i.e. the minimum MAX_NAME_BYTES of all supported platforms). |
| ... | ... | @@ -1274,7 +1274,7 @@ test "writev, readv" { |
| 1274 | 1274 | |
| 1275 | 1275 | var buf1: [line1.len]u8 = undefined; |
| 1276 | 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 | 1279 | .iov_base = line1, |
| 1280 | 1280 | .iov_len = line1.len, |
| ... | ... | @@ -1284,7 +1284,7 @@ test "writev, readv" { |
| 1284 | 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 | 1289 | .iov_base = &buf2, |
| 1290 | 1290 | .iov_len = buf2.len, |
| ... | ... | @@ -1316,7 +1316,7 @@ test "pwritev, preadv" { |
| 1316 | 1316 | |
| 1317 | 1317 | var buf1: [line1.len]u8 = undefined; |
| 1318 | 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 | 1321 | .iov_base = line1, |
| 1322 | 1322 | .iov_len = line1.len, |
| ... | ... | @@ -1326,7 +1326,7 @@ test "pwritev, preadv" { |
| 1326 | 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 | 1331 | .iov_base = &buf2, |
| 1332 | 1332 | .iov_len = buf2.len, |
| ... | ... | @@ -1376,7 +1376,7 @@ test "sendfile" { |
| 1376 | 1376 | |
| 1377 | 1377 | const line1 = "line1\n"; |
| 1378 | 1378 | const line2 = "second line\n"; |
| 1379 | var vecs = [_]std.os.iovec_const{ | |
| 1379 | var vecs = [_]posix.iovec_const{ | |
| 1380 | 1380 | .{ |
| 1381 | 1381 | .iov_base = line1, |
| 1382 | 1382 | .iov_len = line1.len, |
| ... | ... | @@ -1399,7 +1399,7 @@ test "sendfile" { |
| 1399 | 1399 | const header2 = "second header\n"; |
| 1400 | 1400 | const trailer1 = "trailer1\n"; |
| 1401 | 1401 | const trailer2 = "second trailer\n"; |
| 1402 | var hdtr = [_]std.os.iovec_const{ | |
| 1402 | var hdtr = [_]posix.iovec_const{ | |
| 1403 | 1403 | .{ |
| 1404 | 1404 | .iov_base = header1, |
| 1405 | 1405 | .iov_len = header1.len, |
| ... | ... | @@ -1510,7 +1510,7 @@ test "AtomicFile" { |
| 1510 | 1510 | } |
| 1511 | 1511 | |
| 1512 | 1512 | test "open file with exclusive nonblocking lock twice" { |
| 1513 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 1513 | if (native_os == .wasi) return error.SkipZigTest; | |
| 1514 | 1514 | |
| 1515 | 1515 | try testWithAllSupportedPathTypes(struct { |
| 1516 | 1516 | fn impl(ctx: *TestContext) !void { |
| ... | ... | @@ -1526,7 +1526,7 @@ test "open file with exclusive nonblocking lock twice" { |
| 1526 | 1526 | } |
| 1527 | 1527 | |
| 1528 | 1528 | test "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; | |
| 1530 | 1530 | |
| 1531 | 1531 | try testWithAllSupportedPathTypes(struct { |
| 1532 | 1532 | fn impl(ctx: *TestContext) !void { |
| ... | ... | @@ -1542,7 +1542,7 @@ test "open file with shared and exclusive nonblocking lock" { |
| 1542 | 1542 | } |
| 1543 | 1543 | |
| 1544 | 1544 | test "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; | |
| 1546 | 1546 | |
| 1547 | 1547 | try testWithAllSupportedPathTypes(struct { |
| 1548 | 1548 | fn impl(ctx: *TestContext) !void { |
| ... | ... | @@ -1601,7 +1601,7 @@ test "open file with exclusive lock twice, make sure second lock waits" { |
| 1601 | 1601 | } |
| 1602 | 1602 | |
| 1603 | 1603 | test "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; | |
| 1605 | 1605 | |
| 1606 | 1606 | var random_bytes: [12]u8 = undefined; |
| 1607 | 1607 | std.crypto.random.bytes(&random_bytes); |
| ... | ... | @@ -1634,7 +1634,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" { |
| 1634 | 1634 | } |
| 1635 | 1635 | |
| 1636 | 1636 | test "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; | |
| 1638 | 1638 | |
| 1639 | 1639 | var tmp = tmpDir(.{ .iterate = true }); |
| 1640 | 1640 | defer tmp.cleanup(); |
| ... | ... | @@ -1687,7 +1687,7 @@ test "walker" { |
| 1687 | 1687 | } |
| 1688 | 1688 | |
| 1689 | 1689 | test "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; | |
| 1691 | 1691 | |
| 1692 | 1692 | var tmp = tmpDir(.{ .iterate = true }); |
| 1693 | 1693 | defer tmp.cleanup(); |
| ... | ... | @@ -1710,9 +1710,9 @@ test "walker without fully iterating" { |
| 1710 | 1710 | } |
| 1711 | 1711 | |
| 1712 | 1712 | test "'.' 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; | |
| 1714 | 1714 | |
| 1715 | if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64) { | |
| 1715 | if (native_os == .windows and builtin.cpu.arch == .aarch64) { | |
| 1716 | 1716 | // https://github.com/ziglang/zig/issues/17134 |
| 1717 | 1717 | return error.SkipZigTest; |
| 1718 | 1718 | } |
| ... | ... | @@ -1750,7 +1750,7 @@ test "'.' and '..' in fs.Dir functions" { |
| 1750 | 1750 | } |
| 1751 | 1751 | |
| 1752 | 1752 | test "'.' and '..' in absolute functions" { |
| 1753 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 1753 | if (native_os == .wasi) return error.SkipZigTest; | |
| 1754 | 1754 | |
| 1755 | 1755 | var tmp = tmpDir(.{}); |
| 1756 | 1756 | defer tmp.cleanup(); |
| ... | ... | @@ -1794,7 +1794,7 @@ test "'.' and '..' in absolute functions" { |
| 1794 | 1794 | } |
| 1795 | 1795 | |
| 1796 | 1796 | test "chmod" { |
| 1797 | if (builtin.os.tag == .windows or builtin.os.tag == .wasi) | |
| 1797 | if (native_os == .windows or native_os == .wasi) | |
| 1798 | 1798 | return error.SkipZigTest; |
| 1799 | 1799 | |
| 1800 | 1800 | var tmp = tmpDir(.{}); |
| ... | ... | @@ -1816,7 +1816,7 @@ test "chmod" { |
| 1816 | 1816 | } |
| 1817 | 1817 | |
| 1818 | 1818 | test "chown" { |
| 1819 | if (builtin.os.tag == .windows or builtin.os.tag == .wasi) | |
| 1819 | if (native_os == .windows or native_os == .wasi) | |
| 1820 | 1820 | return error.SkipZigTest; |
| 1821 | 1821 | |
| 1822 | 1822 | var tmp = tmpDir(.{}); |
| ... | ... | @@ -1849,7 +1849,7 @@ test "File.Metadata" { |
| 1849 | 1849 | } |
| 1850 | 1850 | |
| 1851 | 1851 | test "File.Permissions" { |
| 1852 | if (builtin.os.tag == .wasi) | |
| 1852 | if (native_os == .wasi) | |
| 1853 | 1853 | return error.SkipZigTest; |
| 1854 | 1854 | |
| 1855 | 1855 | var tmp = tmpDir(.{}); |
| ... | ... | @@ -1875,7 +1875,7 @@ test "File.Permissions" { |
| 1875 | 1875 | } |
| 1876 | 1876 | |
| 1877 | 1877 | test "File.PermissionsUnix" { |
| 1878 | if (builtin.os.tag == .windows or builtin.os.tag == .wasi) | |
| 1878 | if (native_os == .windows or native_os == .wasi) | |
| 1879 | 1879 | return error.SkipZigTest; |
| 1880 | 1880 | |
| 1881 | 1881 | var tmp = tmpDir(.{}); |
| ... | ... | @@ -1910,7 +1910,7 @@ test "File.PermissionsUnix" { |
| 1910 | 1910 | } |
| 1911 | 1911 | |
| 1912 | 1912 | test "delete a read-only file on windows" { |
| 1913 | if (builtin.os.tag != .windows) | |
| 1913 | if (native_os != .windows) | |
| 1914 | 1914 | return error.SkipZigTest; |
| 1915 | 1915 | |
| 1916 | 1916 | var tmp = testing.tmpDir(.{}); |
| ... | ... | @@ -1941,7 +1941,7 @@ test "delete a read-only file on windows" { |
| 1941 | 1941 | } |
| 1942 | 1942 | |
| 1943 | 1943 | test "delete a setAsCwd directory on Windows" { |
| 1944 | if (builtin.os.tag != .windows) return error.SkipZigTest; | |
| 1944 | if (native_os != .windows) return error.SkipZigTest; | |
| 1945 | 1945 | |
| 1946 | 1946 | var tmp = tmpDir(.{}); |
| 1947 | 1947 | // Set tmp dir as current working directory. |
| ... | ... | @@ -1956,7 +1956,7 @@ test "delete a setAsCwd directory on Windows" { |
| 1956 | 1956 | } |
| 1957 | 1957 | |
| 1958 | 1958 | test "invalid UTF-8/WTF-8 paths" { |
| 1959 | const expected_err = switch (builtin.os.tag) { | |
| 1959 | const expected_err = switch (native_os) { | |
| 1960 | 1960 | .wasi => error.InvalidUtf8, |
| 1961 | 1961 | .windows => error.InvalidWtf8, |
| 1962 | 1962 | else => return error.SkipZigTest, |
| ... | ... | @@ -1993,13 +1993,13 @@ test "invalid UTF-8/WTF-8 paths" { |
| 1993 | 1993 | |
| 1994 | 1994 | try testing.expectError(expected_err, ctx.dir.symLink(invalid_path, invalid_path, .{})); |
| 1995 | 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 | 1997 | try testing.expectError(expected_err, ctx.dir.symLinkWasi(invalid_path, invalid_path, .{})); |
| 1998 | 1998 | } |
| 1999 | 1999 | |
| 2000 | 2000 | try testing.expectError(expected_err, ctx.dir.readLink(invalid_path, &[_]u8{})); |
| 2001 | 2001 | try testing.expectError(expected_err, ctx.dir.readLinkZ(invalid_path, &[_]u8{})); |
| 2002 | if (builtin.os.tag == .wasi) { | |
| 2002 | if (native_os == .wasi) { | |
| 2003 | 2003 | try testing.expectError(expected_err, ctx.dir.readLinkWasi(invalid_path, &[_]u8{})); |
| 2004 | 2004 | } |
| 2005 | 2005 | |
| ... | ... | @@ -2023,7 +2023,7 @@ test "invalid UTF-8/WTF-8 paths" { |
| 2023 | 2023 | |
| 2024 | 2024 | try testing.expectError(expected_err, ctx.dir.statFile(invalid_path)); |
| 2025 | 2025 | |
| 2026 | if (builtin.os.tag != .wasi) { | |
| 2026 | if (native_os != .wasi) { | |
| 2027 | 2027 | try testing.expectError(expected_err, ctx.dir.realpath(invalid_path, &[_]u8{})); |
| 2028 | 2028 | try testing.expectError(expected_err, ctx.dir.realpathZ(invalid_path, &[_]u8{})); |
| 2029 | 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 | 2032 | try testing.expectError(expected_err, fs.rename(ctx.dir, invalid_path, ctx.dir, invalid_path)); |
| 2033 | 2033 | try testing.expectError(expected_err, fs.renameZ(ctx.dir, invalid_path, ctx.dir, invalid_path)); |
| 2034 | 2034 | |
| 2035 | if (builtin.os.tag != .wasi and ctx.path_type != .relative) { | |
| 2035 | if (native_os != .wasi and ctx.path_type != .relative) { | |
| 2036 | 2036 | try testing.expectError(expected_err, fs.updateFileAbsolute(invalid_path, invalid_path, .{})); |
| 2037 | 2037 | try testing.expectError(expected_err, fs.copyFileAbsolute(invalid_path, invalid_path, .{})); |
| 2038 | 2038 | try testing.expectError(expected_err, fs.makeDirAbsolute(invalid_path)); |
lib/std/fs/wasi.zig+2-3| ... | ... | @@ -1,6 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const os = std.os; | |
| 4 | 3 | const mem = std.mem; |
| 5 | 4 | const math = std.math; |
| 6 | 5 | const fs = std.fs; |
| ... | ... | @@ -14,10 +13,10 @@ pub const Preopens = struct { |
| 14 | 13 | // Indexed by file descriptor number. |
| 15 | 14 | names: []const []const u8, |
| 16 | 15 | |
| 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 | 17 | for (p.names, 0..) |elem_name, i| { |
| 19 | 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 | 22 | return null; |
lib/std/hash/benchmark.zig+6-6| ... | ... | @@ -367,7 +367,7 @@ pub fn main() !void { |
| 367 | 367 | i += 1; |
| 368 | 368 | if (i == args.len) { |
| 369 | 369 | usage(); |
| 370 | std.os.exit(1); | |
| 370 | std.process.exit(1); | |
| 371 | 371 | } |
| 372 | 372 | |
| 373 | 373 | seed = try std.fmt.parseUnsigned(u32, args[i], 10); |
| ... | ... | @@ -376,7 +376,7 @@ pub fn main() !void { |
| 376 | 376 | i += 1; |
| 377 | 377 | if (i == args.len) { |
| 378 | 378 | usage(); |
| 379 | std.os.exit(1); | |
| 379 | std.process.exit(1); | |
| 380 | 380 | } |
| 381 | 381 | |
| 382 | 382 | filter = args[i]; |
| ... | ... | @@ -384,7 +384,7 @@ pub fn main() !void { |
| 384 | 384 | i += 1; |
| 385 | 385 | if (i == args.len) { |
| 386 | 386 | usage(); |
| 387 | std.os.exit(1); | |
| 387 | std.process.exit(1); | |
| 388 | 388 | } |
| 389 | 389 | |
| 390 | 390 | const c = try std.fmt.parseUnsigned(usize, args[i], 10); |
| ... | ... | @@ -393,13 +393,13 @@ pub fn main() !void { |
| 393 | 393 | i += 1; |
| 394 | 394 | if (i == args.len) { |
| 395 | 395 | usage(); |
| 396 | std.os.exit(1); | |
| 396 | std.process.exit(1); | |
| 397 | 397 | } |
| 398 | 398 | |
| 399 | 399 | key_size = try std.fmt.parseUnsigned(usize, args[i], 10); |
| 400 | 400 | if (key_size.? > block_size) { |
| 401 | 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 | 404 | } else if (std.mem.eql(u8, args[i], "--iterative-only")) { |
| 405 | 405 | test_iterative_only = true; |
| ... | ... | @@ -410,7 +410,7 @@ pub fn main() !void { |
| 410 | 410 | return; |
| 411 | 411 | } else { |
| 412 | 412 | usage(); |
| 413 | std.os.exit(1); | |
| 413 | std.process.exit(1); | |
| 414 | 414 | } |
| 415 | 415 | } |
| 416 | 416 |
lib/std/heap.zig+10-10| ... | ... | @@ -4,9 +4,9 @@ const root = @import("root"); |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | 5 | const testing = std.testing; |
| 6 | 6 | const mem = std.mem; |
| 7 | const os = std.os; | |
| 8 | 7 | const c = std.c; |
| 9 | 8 | const Allocator = std.mem.Allocator; |
| 9 | const windows = std.os.windows; | |
| 10 | 10 | |
| 11 | 11 | pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator; |
| 12 | 12 | pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator; |
| ... | ... | @@ -263,7 +263,7 @@ pub const HeapAllocator = switch (builtin.os.tag) { |
| 263 | 263 | .windows => struct { |
| 264 | 264 | heap_handle: ?HeapHandle, |
| 265 | 265 | |
| 266 | const HeapHandle = os.windows.HANDLE; | |
| 266 | const HeapHandle = windows.HANDLE; | |
| 267 | 267 | |
| 268 | 268 | pub fn init() HeapAllocator { |
| 269 | 269 | return HeapAllocator{ |
| ... | ... | @@ -284,7 +284,7 @@ pub const HeapAllocator = switch (builtin.os.tag) { |
| 284 | 284 | |
| 285 | 285 | pub fn deinit(self: *HeapAllocator) void { |
| 286 | 286 | if (self.heap_handle) |heap_handle| { |
| 287 | os.windows.HeapDestroy(heap_handle); | |
| 287 | windows.HeapDestroy(heap_handle); | |
| 288 | 288 | } |
| 289 | 289 | } |
| 290 | 290 | |
| ... | ... | @@ -305,13 +305,13 @@ pub const HeapAllocator = switch (builtin.os.tag) { |
| 305 | 305 | const amt = n + ptr_align - 1 + @sizeOf(usize); |
| 306 | 306 | const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, .seq_cst); |
| 307 | 307 | const heap_handle = optional_heap_handle orelse blk: { |
| 308 | const options = if (builtin.single_threaded) os.windows.HEAP_NO_SERIALIZE else 0; | |
| 309 | const hh = os.windows.kernel32.HeapCreate(options, amt, 0) orelse return null; | |
| 308 | const options = if (builtin.single_threaded) windows.HEAP_NO_SERIALIZE else 0; | |
| 309 | const hh = windows.kernel32.HeapCreate(options, amt, 0) orelse return null; | |
| 310 | 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 | 312 | 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 | 315 | const root_addr = @intFromPtr(ptr); |
| 316 | 316 | const aligned_addr = mem.alignForward(usize, root_addr, ptr_align); |
| 317 | 317 | const buf = @as([*]u8, @ptrFromInt(aligned_addr))[0..n]; |
| ... | ... | @@ -333,9 +333,9 @@ pub const HeapAllocator = switch (builtin.os.tag) { |
| 333 | 333 | const root_addr = getRecordPtr(buf).*; |
| 334 | 334 | const align_offset = @intFromPtr(buf.ptr) - root_addr; |
| 335 | 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 | 337 | self.heap_handle.?, |
| 338 | os.windows.HEAP_REALLOC_IN_PLACE_ONLY, | |
| 338 | windows.HEAP_REALLOC_IN_PLACE_ONLY, | |
| 339 | 339 | @as(*anyopaque, @ptrFromInt(root_addr)), |
| 340 | 340 | amt, |
| 341 | 341 | ) orelse return false; |
| ... | ... | @@ -353,7 +353,7 @@ pub const HeapAllocator = switch (builtin.os.tag) { |
| 353 | 353 | _ = log2_buf_align; |
| 354 | 354 | _ = return_address; |
| 355 | 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 | 359 | else => @compileError("Unsupported OS"), |
lib/std/heap/PageAllocator.zig+16-16| ... | ... | @@ -2,9 +2,11 @@ const std = @import("../std.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const Allocator = std.mem.Allocator; |
| 4 | 4 | const mem = std.mem; |
| 5 | const os = std.os; | |
| 6 | 5 | const maxInt = std.math.maxInt; |
| 7 | 6 | const assert = std.debug.assert; |
| 7 | const native_os = builtin.os.tag; | |
| 8 | const windows = std.os.windows; | |
| 9 | const posix = std.posix; | |
| 8 | 10 | |
| 9 | 11 | pub const vtable = Allocator.VTable{ |
| 10 | 12 | .alloc = alloc, |
| ... | ... | @@ -19,22 +21,21 @@ fn alloc(_: *anyopaque, n: usize, log2_align: u8, ra: usize) ?[*]u8 { |
| 19 | 21 | if (n > maxInt(usize) - (mem.page_size - 1)) return null; |
| 20 | 22 | const aligned_len = mem.alignForward(usize, n, mem.page_size); |
| 21 | 23 | |
| 22 | if (builtin.os.tag == .windows) { | |
| 23 | const w = os.windows; | |
| 24 | const addr = w.VirtualAlloc( | |
| 24 | if (native_os == .windows) { | |
| 25 | const addr = windows.VirtualAlloc( | |
| 25 | 26 | null, |
| 26 | 27 | aligned_len, |
| 27 | w.MEM_COMMIT | w.MEM_RESERVE, | |
| 28 | w.PAGE_READWRITE, | |
| 28 | windows.MEM_COMMIT | windows.MEM_RESERVE, | |
| 29 | windows.PAGE_READWRITE, | |
| 29 | 30 | ) catch return null; |
| 30 | 31 | return @ptrCast(addr); |
| 31 | 32 | } |
| 32 | 33 | |
| 33 | 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 | 36 | hint, |
| 36 | 37 | aligned_len, |
| 37 | os.PROT.READ | os.PROT.WRITE, | |
| 38 | posix.PROT.READ | posix.PROT.WRITE, | |
| 38 | 39 | .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, |
| 39 | 40 | -1, |
| 40 | 41 | 0, |
| ... | ... | @@ -56,8 +57,7 @@ fn resize( |
| 56 | 57 | _ = return_address; |
| 57 | 58 | const new_size_aligned = mem.alignForward(usize, new_size, mem.page_size); |
| 58 | 59 | |
| 59 | if (builtin.os.tag == .windows) { | |
| 60 | const w = os.windows; | |
| 60 | if (native_os == .windows) { | |
| 61 | 61 | if (new_size <= buf_unaligned.len) { |
| 62 | 62 | const base_addr = @intFromPtr(buf_unaligned.ptr); |
| 63 | 63 | const old_addr_end = base_addr + buf_unaligned.len; |
| ... | ... | @@ -65,10 +65,10 @@ fn resize( |
| 65 | 65 | if (old_addr_end > new_addr_end) { |
| 66 | 66 | // For shrinking that is not releasing, we will only |
| 67 | 67 | // decommit the pages not needed anymore. |
| 68 | w.VirtualFree( | |
| 68 | windows.VirtualFree( | |
| 69 | 69 | @as(*anyopaque, @ptrFromInt(new_addr_end)), |
| 70 | 70 | old_addr_end - new_addr_end, |
| 71 | w.MEM_DECOMMIT, | |
| 71 | windows.MEM_DECOMMIT, | |
| 72 | 72 | ); |
| 73 | 73 | } |
| 74 | 74 | return true; |
| ... | ... | @@ -87,7 +87,7 @@ fn resize( |
| 87 | 87 | if (new_size_aligned < buf_aligned_len) { |
| 88 | 88 | const ptr = buf_unaligned.ptr + new_size_aligned; |
| 89 | 89 | // 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 | 91 | return true; |
| 92 | 92 | } |
| 93 | 93 | |
| ... | ... | @@ -100,10 +100,10 @@ fn free(_: *anyopaque, slice: []u8, log2_buf_align: u8, return_address: usize) v |
| 100 | 100 | _ = log2_buf_align; |
| 101 | 101 | _ = return_address; |
| 102 | 102 | |
| 103 | if (builtin.os.tag == .windows) { | |
| 104 | os.windows.VirtualFree(slice.ptr, 0, os.windows.MEM_RELEASE); | |
| 103 | if (native_os == .windows) { | |
| 104 | windows.VirtualFree(slice.ptr, 0, windows.MEM_RELEASE); | |
| 105 | 105 | } else { |
| 106 | 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 | 220 | |
| 221 | 221 | pub const Protocol = enum { plain, tls }; |
| 222 | 222 | |
| 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 | 224 | return conn.tls_client.readv(conn.stream, buffers) catch |err| { |
| 225 | 225 | // https://github.com/ziglang/zig/issues/2473 |
| 226 | 226 | if (mem.startsWith(u8, @errorName(err), "TlsAlert")) return error.TlsAlert; |
| ... | ... | @@ -234,7 +234,7 @@ pub const Connection = struct { |
| 234 | 234 | }; |
| 235 | 235 | } |
| 236 | 236 | |
| 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 | 238 | if (conn.protocol == .tls) { |
| 239 | 239 | if (disable_tls) unreachable; |
| 240 | 240 | |
| ... | ... | @@ -252,7 +252,7 @@ pub const Connection = struct { |
| 252 | 252 | pub fn fill(conn: *Connection) ReadError!void { |
| 253 | 253 | if (conn.read_end != conn.read_start) return; |
| 254 | 254 | |
| 255 | var iovecs = [1]std.os.iovec{ | |
| 255 | var iovecs = [1]std.posix.iovec{ | |
| 256 | 256 | .{ .iov_base = &conn.read_buf, .iov_len = conn.read_buf.len }, |
| 257 | 257 | }; |
| 258 | 258 | const nread = try conn.readvDirect(&iovecs); |
| ... | ... | @@ -288,7 +288,7 @@ pub const Connection = struct { |
| 288 | 288 | return available_read; |
| 289 | 289 | } |
| 290 | 290 | |
| 291 | var iovecs = [2]std.os.iovec{ | |
| 291 | var iovecs = [2]std.posix.iovec{ | |
| 292 | 292 | .{ .iov_base = buffer.ptr, .iov_len = buffer.len }, |
| 293 | 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 | 1387 | return &conn.data; |
| 1388 | 1388 | } |
| 1389 | 1389 | |
| 1390 | pub const ConnectUnixError = Allocator.Error || std.os.SocketError || error{NameTooLong} || std.os.ConnectError; | |
| 1390 | pub const ConnectUnixError = Allocator.Error || std.posix.SocketError || error{NameTooLong} || std.posix.ConnectError; | |
| 1391 | 1391 | |
| 1392 | 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 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const root = @import("root"); |
| 4 | 4 | const c = std.c; |
| 5 | const is_windows = builtin.os.tag == .windows; | |
| 6 | const windows = std.os.windows; | |
| 7 | const posix = std.posix; | |
| 5 | 8 | |
| 6 | 9 | const math = std.math; |
| 7 | 10 | const assert = std.debug.assert; |
| 8 | const os = std.os; | |
| 9 | 11 | const fs = std.fs; |
| 10 | 12 | const mem = std.mem; |
| 11 | 13 | const meta = std.meta; |
| 12 | 14 | const File = std.fs.File; |
| 13 | 15 | const Allocator = std.mem.Allocator; |
| 14 | 16 | |
| 15 | fn getStdOutHandle() os.fd_t { | |
| 16 | if (builtin.os.tag == .windows) { | |
| 17 | fn getStdOutHandle() posix.fd_t { | |
| 18 | if (is_windows) { | |
| 17 | 19 | if (builtin.zig_backend == .stage2_aarch64) { |
| 18 | 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 | } |
| 23 | 25 | |
| 24 | 26 | if (@hasDecl(root, "os") and @hasDecl(root.os, "io") and @hasDecl(root.os.io, "getStdOutHandle")) { |
| 25 | 27 | return root.os.io.getStdOutHandle(); |
| 26 | 28 | } |
| 27 | 29 | |
| 28 | return os.STDOUT_FILENO; | |
| 30 | return posix.STDOUT_FILENO; | |
| 29 | 31 | } |
| 30 | 32 | |
| 31 | 33 | pub fn getStdOut() File { |
| 32 | return File{ .handle = getStdOutHandle() }; | |
| 34 | return .{ .handle = getStdOutHandle() }; | |
| 33 | 35 | } |
| 34 | 36 | |
| 35 | fn getStdErrHandle() os.fd_t { | |
| 36 | if (builtin.os.tag == .windows) { | |
| 37 | fn getStdErrHandle() posix.fd_t { | |
| 38 | if (is_windows) { | |
| 37 | 39 | if (builtin.zig_backend == .stage2_aarch64) { |
| 38 | 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 | } |
| 43 | 45 | |
| 44 | 46 | if (@hasDecl(root, "os") and @hasDecl(root.os, "io") and @hasDecl(root.os.io, "getStdErrHandle")) { |
| 45 | 47 | return root.os.io.getStdErrHandle(); |
| 46 | 48 | } |
| 47 | 49 | |
| 48 | return os.STDERR_FILENO; | |
| 50 | return posix.STDERR_FILENO; | |
| 49 | 51 | } |
| 50 | 52 | |
| 51 | 53 | pub fn getStdErr() File { |
| 52 | return File{ .handle = getStdErrHandle() }; | |
| 54 | return .{ .handle = getStdErrHandle() }; | |
| 53 | 55 | } |
| 54 | 56 | |
| 55 | fn getStdInHandle() os.fd_t { | |
| 56 | if (builtin.os.tag == .windows) { | |
| 57 | fn getStdInHandle() posix.fd_t { | |
| 58 | if (is_windows) { | |
| 57 | 59 | if (builtin.zig_backend == .stage2_aarch64) { |
| 58 | 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 | } |
| 63 | 65 | |
| 64 | 66 | if (@hasDecl(root, "os") and @hasDecl(root.os, "io") and @hasDecl(root.os.io, "getStdInHandle")) { |
| 65 | 67 | return root.os.io.getStdInHandle(); |
| 66 | 68 | } |
| 67 | 69 | |
| 68 | return os.STDIN_FILENO; | |
| 70 | return posix.STDIN_FILENO; | |
| 69 | 71 | } |
| 70 | 72 | |
| 71 | 73 | pub fn getStdIn() File { |
| 72 | return File{ .handle = getStdInHandle() }; | |
| 74 | return .{ .handle = getStdInHandle() }; | |
| 73 | 75 | } |
| 74 | 76 | |
| 75 | 77 | pub fn GenericReader( |
| ... | ... | @@ -434,10 +436,10 @@ pub fn poll( |
| 434 | 436 | const enum_fields = @typeInfo(StreamEnum).Enum.fields; |
| 435 | 437 | var result: Poller(StreamEnum) = undefined; |
| 436 | 438 | |
| 437 | if (builtin.os.tag == .windows) result.windows = .{ | |
| 439 | if (is_windows) result.windows = .{ | |
| 438 | 440 | .first_read_done = false, |
| 439 | .overlapped = [1]os.windows.OVERLAPPED{ | |
| 440 | mem.zeroes(os.windows.OVERLAPPED), | |
| 441 | .overlapped = [1]windows.OVERLAPPED{ | |
| 442 | mem.zeroes(windows.OVERLAPPED), | |
| 441 | 443 | } ** enum_fields.len, |
| 442 | 444 | .active = .{ |
| 443 | 445 | .count = 0, |
| ... | ... | @@ -453,12 +455,12 @@ pub fn poll( |
| 453 | 455 | .head = 0, |
| 454 | 456 | .count = 0, |
| 455 | 457 | }; |
| 456 | if (builtin.os.tag == .windows) { | |
| 458 | if (is_windows) { | |
| 457 | 459 | result.windows.active.handles_buf[i] = @field(files, enum_fields[i].name).handle; |
| 458 | 460 | } else { |
| 459 | 461 | result.poll_fds[i] = .{ |
| 460 | 462 | .fd = @field(files, enum_fields[i].name).handle, |
| 461 | .events = os.POLL.IN, | |
| 463 | .events = posix.POLL.IN, | |
| 462 | 464 | .revents = undefined, |
| 463 | 465 | }; |
| 464 | 466 | } |
| ... | ... | @@ -471,16 +473,16 @@ pub const PollFifo = std.fifo.LinearFifo(u8, .Dynamic); |
| 471 | 473 | pub fn Poller(comptime StreamEnum: type) type { |
| 472 | 474 | return struct { |
| 473 | 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; | |
| 475 | 477 | |
| 476 | 478 | fifos: [enum_fields.len]PollFifo, |
| 477 | 479 | poll_fds: [enum_fields.len]PollFd, |
| 478 | windows: if (builtin.os.tag == .windows) struct { | |
| 480 | windows: if (is_windows) struct { | |
| 479 | 481 | first_read_done: bool, |
| 480 | overlapped: [enum_fields.len]os.windows.OVERLAPPED, | |
| 482 | overlapped: [enum_fields.len]windows.OVERLAPPED, | |
| 481 | 483 | active: struct { |
| 482 | 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 | 486 | stream_map: [enum_fields.len]StreamEnum, |
| 485 | 487 | |
| 486 | 488 | pub fn removeAt(self: *@This(), index: u32) void { |
| ... | ... | @@ -497,10 +499,10 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 497 | 499 | const Self = @This(); |
| 498 | 500 | |
| 499 | 501 | pub fn deinit(self: *Self) void { |
| 500 | if (builtin.os.tag == .windows) { | |
| 502 | if (is_windows) { | |
| 501 | 503 | // cancel any pending IO to prevent clobbering OVERLAPPED value |
| 502 | 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 | 508 | inline for (&self.fifos) |*q| q.deinit(); |
| ... | ... | @@ -508,7 +510,7 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 508 | 510 | } |
| 509 | 511 | |
| 510 | 512 | pub fn poll(self: *Self) !bool { |
| 511 | if (builtin.os.tag == .windows) { | |
| 513 | if (is_windows) { | |
| 512 | 514 | return pollWindows(self, null); |
| 513 | 515 | } else { |
| 514 | 516 | return pollPosix(self, null); |
| ... | ... | @@ -516,7 +518,7 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 516 | 518 | } |
| 517 | 519 | |
| 518 | 520 | pub fn pollTimeout(self: *Self, nanoseconds: u64) !bool { |
| 519 | if (builtin.os.tag == .windows) { | |
| 521 | if (is_windows) { | |
| 520 | 522 | return pollWindows(self, nanoseconds); |
| 521 | 523 | } else { |
| 522 | 524 | return pollPosix(self, nanoseconds); |
| ... | ... | @@ -554,39 +556,39 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 554 | 556 | while (true) { |
| 555 | 557 | if (self.windows.active.count == 0) return false; |
| 556 | 558 | |
| 557 | const status = os.windows.kernel32.WaitForMultipleObjects( | |
| 559 | const status = windows.kernel32.WaitForMultipleObjects( | |
| 558 | 560 | self.windows.active.count, |
| 559 | 561 | &self.windows.active.handles_buf, |
| 560 | 562 | 0, |
| 561 | 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 | 565 | else |
| 564 | os.windows.INFINITE, | |
| 566 | windows.INFINITE, | |
| 565 | 567 | ); |
| 566 | if (status == os.windows.WAIT_FAILED) | |
| 567 | return os.windows.unexpectedError(os.windows.kernel32.GetLastError()); | |
| 568 | if (status == os.windows.WAIT_TIMEOUT) | |
| 568 | if (status == windows.WAIT_FAILED) | |
| 569 | return windows.unexpectedError(windows.kernel32.GetLastError()); | |
| 570 | if (status == windows.WAIT_TIMEOUT) | |
| 569 | 571 | return true; |
| 570 | 572 | |
| 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 | 574 | unreachable; |
| 573 | 575 | |
| 574 | const active_idx = status - os.windows.WAIT_OBJECT_0; | |
| 576 | const active_idx = status - windows.WAIT_OBJECT_0; | |
| 575 | 577 | |
| 576 | 578 | const handle = self.windows.active.handles_buf[active_idx]; |
| 577 | 579 | const stream_idx = @intFromEnum(self.windows.active.stream_map[active_idx]); |
| 578 | 580 | var read_bytes: u32 = undefined; |
| 579 | if (0 == os.windows.kernel32.GetOverlappedResult( | |
| 581 | if (0 == windows.kernel32.GetOverlappedResult( | |
| 580 | 582 | handle, |
| 581 | 583 | &self.windows.overlapped[stream_idx], |
| 582 | 584 | &read_bytes, |
| 583 | 585 | 0, |
| 584 | )) switch (os.windows.kernel32.GetLastError()) { | |
| 586 | )) switch (windows.kernel32.GetLastError()) { | |
| 585 | 587 | .BROKEN_PIPE => { |
| 586 | 588 | self.windows.active.removeAt(active_idx); |
| 587 | 589 | continue; |
| 588 | 590 | }, |
| 589 | else => |err| return os.windows.unexpectedError(err), | |
| 591 | else => |err| return windows.unexpectedError(err), | |
| 590 | 592 | }; |
| 591 | 593 | |
| 592 | 594 | self.fifos[stream_idx].update(read_bytes); |
| ... | ... | @@ -611,9 +613,9 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 611 | 613 | // allocate grows exponentially. |
| 612 | 614 | const bump_amt = 512; |
| 613 | 615 | |
| 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; | |
| 615 | 617 | |
| 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 | 619 | std.math.cast(i32, ns / std.time.ns_per_ms) orelse std.math.maxInt(i32) |
| 618 | 620 | else |
| 619 | 621 | -1); |
| ... | ... | @@ -629,9 +631,9 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 629 | 631 | // conditions. |
| 630 | 632 | // It's still possible to read after a POLL.HUP is received, |
| 631 | 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 | 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 | 637 | q.update(amt); |
| 636 | 638 | if (amt == 0) { |
| 637 | 639 | // Remove the fd when the EOF condition is met. |
| ... | ... | @@ -652,19 +654,19 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 652 | 654 | } |
| 653 | 655 | |
| 654 | 656 | fn windowsAsyncRead( |
| 655 | handle: os.windows.HANDLE, | |
| 656 | overlapped: *os.windows.OVERLAPPED, | |
| 657 | handle: windows.HANDLE, | |
| 658 | overlapped: *windows.OVERLAPPED, | |
| 657 | 659 | fifo: *PollFifo, |
| 658 | 660 | bump_amt: usize, |
| 659 | 661 | ) !enum { pending, closed } { |
| 660 | 662 | while (true) { |
| 661 | 663 | const buf = try fifo.writableWithSize(bump_amt); |
| 662 | 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); | |
| 664 | if (read_result == 0) return switch (os.windows.kernel32.GetLastError()) { | |
| 665 | const read_result = windows.kernel32.ReadFile(handle, buf.ptr, math.cast(u32, buf.len) orelse math.maxInt(u32), &read_bytes, overlapped); | |
| 666 | if (read_result == 0) return switch (windows.kernel32.GetLastError()) { | |
| 665 | 667 | .IO_PENDING => .pending, |
| 666 | 668 | .BROKEN_PIPE => .closed, |
| 667 | else => |err| os.windows.unexpectedError(err), | |
| 669 | else => |err| windows.unexpectedError(err), | |
| 668 | 670 | }; |
| 669 | 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 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const io = std.io; |
| 4 | 4 | const testing = std.testing; |
| 5 | const os = std.os; | |
| 6 | 5 | |
| 7 | 6 | pub const CWriter = io.Writer(*std.c.FILE, std.fs.File.WriteError, cWriterWrite); |
| 8 | 7 | |
| ... | ... | @@ -13,7 +12,7 @@ pub fn cWriter(c_file: *std.c.FILE) CWriter { |
| 13 | 12 | fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!usize { |
| 14 | 13 | const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, c_file); |
| 15 | 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 | 16 | .SUCCESS => unreachable, |
| 18 | 17 | .INVAL => unreachable, |
| 19 | 18 | .FAULT => unreachable, |
| ... | ... | @@ -26,11 +25,11 @@ fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!u |
| 26 | 25 | .NOSPC => return error.NoSpaceLeft, |
| 27 | 26 | .PERM => return error.AccessDenied, |
| 28 | 27 | .PIPE => return error.BrokenPipe, |
| 29 | else => |err| return os.unexpectedErrno(err), | |
| 28 | else => |err| return std.posix.unexpectedErrno(err), | |
| 30 | 29 | } |
| 31 | 30 | } |
| 32 | 31 | |
| 33 | test "C Writer" { | |
| 32 | test cWriter { | |
| 34 | 33 | if (!builtin.link_libc or builtin.os.tag == .wasi) return error.SkipZigTest; |
| 35 | 34 | |
| 36 | 35 | const filename = "tmp_io_test_file.txt"; |
lib/std/net.zig+167-166| ... | ... | @@ -5,15 +5,16 @@ const builtin = @import("builtin"); |
| 5 | 5 | const assert = std.debug.assert; |
| 6 | 6 | const net = @This(); |
| 7 | 7 | const mem = std.mem; |
| 8 | const os = std.os; | |
| 9 | 8 | const posix = std.posix; |
| 10 | 9 | const fs = std.fs; |
| 11 | 10 | const io = std.io; |
| 12 | 11 | const native_endian = builtin.target.cpu.arch.endian(); |
| 12 | const native_os = builtin.os.tag; | |
| 13 | const windows = std.os.windows; | |
| 13 | 14 | |
| 14 | 15 | // Windows 10 added support for unix sockets in build 17063, redstone 4 is the |
| 15 | 16 | // first release to support them. |
| 16 | pub const has_unix_sockets = switch (builtin.os.tag) { | |
| 17 | pub const has_unix_sockets = switch (native_os) { | |
| 17 | 18 | .windows => builtin.os.version_range.windows.isAtLeast(.win10_rs4) orelse false, |
| 18 | 19 | else => true, |
| 19 | 20 | }; |
| ... | ... | @@ -28,14 +29,14 @@ pub const IPParseError = error{ |
| 28 | 29 | pub const IPv4ParseError = IPParseError || error{NonCanonical}; |
| 29 | 30 | |
| 30 | 31 | pub const IPv6ParseError = IPParseError || error{InvalidIpv4Mapping}; |
| 31 | pub const IPv6InterfaceError = os.SocketError || os.IoCtl_SIOCGIFINDEX_Error || error{NameTooLong}; | |
| 32 | pub const IPv6InterfaceError = posix.SocketError || posix.IoCtl_SIOCGIFINDEX_Error || error{NameTooLong}; | |
| 32 | 33 | pub const IPv6ResolveError = IPv6ParseError || IPv6InterfaceError; |
| 33 | 34 | |
| 34 | 35 | pub const Address = extern union { |
| 35 | any: os.sockaddr, | |
| 36 | any: posix.sockaddr, | |
| 36 | 37 | in: Ip4Address, |
| 37 | 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, | |
| 39 | 40 | |
| 40 | 41 | /// Parse the given IP address string into an Address value. |
| 41 | 42 | /// It is recommended to use `resolveIp` instead, to handle |
| ... | ... | @@ -85,38 +86,38 @@ pub const Address = extern union { |
| 85 | 86 | return error.InvalidIPAddressFormat; |
| 86 | 87 | } |
| 87 | 88 | |
| 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 | 90 | switch (family) { |
| 90 | os.AF.INET => return parseIp4(name, port), | |
| 91 | os.AF.INET6 => return parseIp6(name, port), | |
| 92 | os.AF.UNSPEC => return parseIp(name, port), | |
| 91 | posix.AF.INET => return parseIp4(name, port), | |
| 92 | posix.AF.INET6 => return parseIp6(name, port), | |
| 93 | posix.AF.UNSPEC => return parseIp(name, port), | |
| 93 | 94 | else => unreachable, |
| 94 | 95 | } |
| 95 | 96 | } |
| 96 | 97 | |
| 97 | 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 | } |
| 100 | 101 | |
| 101 | 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 | } |
| 104 | 105 | |
| 105 | 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 | } |
| 108 | 109 | |
| 109 | 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 | } |
| 112 | 113 | |
| 113 | 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 | } |
| 116 | 117 | |
| 117 | 118 | pub fn initUnix(path: []const u8) !Address { |
| 118 | var sock_addr = os.sockaddr.un{ | |
| 119 | .family = os.AF.UNIX, | |
| 119 | var sock_addr = posix.sockaddr.un{ | |
| 120 | .family = posix.AF.UNIX, | |
| 120 | 121 | .path = undefined, |
| 121 | 122 | }; |
| 122 | 123 | |
| ... | ... | @@ -133,8 +134,8 @@ pub const Address = extern union { |
| 133 | 134 | /// Asserts that the address is ip4 or ip6. |
| 134 | 135 | pub fn getPort(self: Address) u16 { |
| 135 | 136 | return switch (self.any.family) { |
| 136 | os.AF.INET => self.in.getPort(), | |
| 137 | os.AF.INET6 => self.in6.getPort(), | |
| 137 | posix.AF.INET => self.in.getPort(), | |
| 138 | posix.AF.INET6 => self.in6.getPort(), | |
| 138 | 139 | else => unreachable, |
| 139 | 140 | }; |
| 140 | 141 | } |
| ... | ... | @@ -143,8 +144,8 @@ pub const Address = extern union { |
| 143 | 144 | /// Asserts that the address is ip4 or ip6. |
| 144 | 145 | pub fn setPort(self: *Address, port: u16) void { |
| 145 | 146 | switch (self.any.family) { |
| 146 | os.AF.INET => self.in.setPort(port), | |
| 147 | os.AF.INET6 => self.in6.setPort(port), | |
| 147 | posix.AF.INET => self.in.setPort(port), | |
| 148 | posix.AF.INET6 => self.in6.setPort(port), | |
| 148 | 149 | else => unreachable, |
| 149 | 150 | } |
| 150 | 151 | } |
| ... | ... | @@ -152,10 +153,10 @@ pub const Address = extern union { |
| 152 | 153 | /// Asserts that `addr` is an IP address. |
| 153 | 154 | /// This function will read past the end of the pointer, with a size depending |
| 154 | 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 | 157 | switch (addr.family) { |
| 157 | os.AF.INET => return Address{ .in = Ip4Address{ .sa = @as(*const os.sockaddr.in, @ptrCast(addr)).* } }, | |
| 158 | os.AF.INET6 => return Address{ .in6 = Ip6Address{ .sa = @as(*const os.sockaddr.in6, @ptrCast(addr)).* } }, | |
| 158 | posix.AF.INET => return Address{ .in = Ip4Address{ .sa = @as(*const posix.sockaddr.in, @ptrCast(addr)).* } }, | |
| 159 | posix.AF.INET6 => return Address{ .in6 = Ip6Address{ .sa = @as(*const posix.sockaddr.in6, @ptrCast(addr)).* } }, | |
| 159 | 160 | else => unreachable, |
| 160 | 161 | } |
| 161 | 162 | } |
| ... | ... | @@ -168,9 +169,9 @@ pub const Address = extern union { |
| 168 | 169 | ) !void { |
| 169 | 170 | if (fmt.len != 0) std.fmt.invalidFmtError(fmt, self); |
| 170 | 171 | switch (self.any.family) { |
| 171 | os.AF.INET => try self.in.format(fmt, options, out_stream), | |
| 172 | os.AF.INET6 => try self.in6.format(fmt, options, out_stream), | |
| 173 | os.AF.UNIX => { | |
| 172 | posix.AF.INET => try self.in.format(fmt, options, out_stream), | |
| 173 | posix.AF.INET6 => try self.in6.format(fmt, options, out_stream), | |
| 174 | posix.AF.UNIX => { | |
| 174 | 175 | if (!has_unix_sockets) { |
| 175 | 176 | unreachable; |
| 176 | 177 | } |
| ... | ... | @@ -187,11 +188,11 @@ pub const Address = extern union { |
| 187 | 188 | return mem.eql(u8, a_bytes, b_bytes); |
| 188 | 189 | } |
| 189 | 190 | |
| 190 | pub fn getOsSockLen(self: Address) os.socklen_t { | |
| 191 | pub fn getOsSockLen(self: Address) posix.socklen_t { | |
| 191 | 192 | switch (self.any.family) { |
| 192 | os.AF.INET => return self.in.getOsSockLen(), | |
| 193 | os.AF.INET6 => return self.in6.getOsSockLen(), | |
| 194 | os.AF.UNIX => { | |
| 193 | posix.AF.INET => return self.in.getOsSockLen(), | |
| 194 | posix.AF.INET6 => return self.in6.getOsSockLen(), | |
| 195 | posix.AF.UNIX => { | |
| 195 | 196 | if (!has_unix_sockets) { |
| 196 | 197 | unreachable; |
| 197 | 198 | } |
| ... | ... | @@ -204,7 +205,7 @@ pub const Address = extern union { |
| 204 | 205 | // provide the full buffer size (e.g. getsockname, getpeername, recvfrom, accept). |
| 205 | 206 | // |
| 206 | 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 | }, |
| 209 | 210 | |
| 210 | 211 | else => unreachable, |
| ... | ... | @@ -247,7 +248,7 @@ pub const Address = extern union { |
| 247 | 248 | posix.SO.REUSEADDR, |
| 248 | 249 | &mem.toBytes(@as(c_int, 1)), |
| 249 | 250 | ); |
| 250 | switch (builtin.os.tag) { | |
| 251 | switch (native_os) { | |
| 251 | 252 | .windows => {}, |
| 252 | 253 | else => try posix.setsockopt( |
| 253 | 254 | sockfd, |
| ... | ... | @@ -267,7 +268,7 @@ pub const Address = extern union { |
| 267 | 268 | }; |
| 268 | 269 | |
| 269 | 270 | pub const Ip4Address = extern struct { |
| 270 | sa: os.sockaddr.in, | |
| 271 | sa: posix.sockaddr.in, | |
| 271 | 272 | |
| 272 | 273 | pub fn parse(buf: []const u8, port: u16) IPv4ParseError!Ip4Address { |
| 273 | 274 | var result = Ip4Address{ |
| ... | ... | @@ -330,7 +331,7 @@ pub const Ip4Address = extern struct { |
| 330 | 331 | |
| 331 | 332 | pub fn init(addr: [4]u8, port: u16) Ip4Address { |
| 332 | 333 | return Ip4Address{ |
| 333 | .sa = os.sockaddr.in{ | |
| 334 | .sa = posix.sockaddr.in{ | |
| 334 | 335 | .port = mem.nativeToBig(u16, port), |
| 335 | 336 | .addr = @as(*align(1) const u32, @ptrCast(&addr)).*, |
| 336 | 337 | }, |
| ... | ... | @@ -367,21 +368,21 @@ pub const Ip4Address = extern struct { |
| 367 | 368 | }); |
| 368 | 369 | } |
| 369 | 370 | |
| 370 | pub fn getOsSockLen(self: Ip4Address) os.socklen_t { | |
| 371 | pub fn getOsSockLen(self: Ip4Address) posix.socklen_t { | |
| 371 | 372 | _ = self; |
| 372 | return @sizeOf(os.sockaddr.in); | |
| 373 | return @sizeOf(posix.sockaddr.in); | |
| 373 | 374 | } |
| 374 | 375 | }; |
| 375 | 376 | |
| 376 | 377 | pub const Ip6Address = extern struct { |
| 377 | sa: os.sockaddr.in6, | |
| 378 | sa: posix.sockaddr.in6, | |
| 378 | 379 | |
| 379 | 380 | /// Parse a given IPv6 address string into an Address. |
| 380 | 381 | /// Assumes the Scope ID of the address is fully numeric. |
| 381 | 382 | /// For non-numeric addresses, see `resolveIp6`. |
| 382 | 383 | pub fn parse(buf: []const u8, port: u16) IPv6ParseError!Ip6Address { |
| 383 | 384 | var result = Ip6Address{ |
| 384 | .sa = os.sockaddr.in6{ | |
| 385 | .sa = posix.sockaddr.in6{ | |
| 385 | 386 | .scope_id = 0, |
| 386 | 387 | .port = mem.nativeToBig(u16, port), |
| 387 | 388 | .flowinfo = 0, |
| ... | ... | @@ -499,7 +500,7 @@ pub const Ip6Address = extern struct { |
| 499 | 500 | pub fn resolve(buf: []const u8, port: u16) IPv6ResolveError!Ip6Address { |
| 500 | 501 | // TODO: Unify the implementations of resolveIp6 and parseIp6. |
| 501 | 502 | var result = Ip6Address{ |
| 502 | .sa = os.sockaddr.in6{ | |
| 503 | .sa = posix.sockaddr.in6{ | |
| 503 | 504 | .scope_id = 0, |
| 504 | 505 | .port = mem.nativeToBig(u16, port), |
| 505 | 506 | .flowinfo = 0, |
| ... | ... | @@ -516,7 +517,7 @@ pub const Ip6Address = extern struct { |
| 516 | 517 | var abbrv = false; |
| 517 | 518 | |
| 518 | 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 | 521 | var scope_id_index: usize = 0; |
| 521 | 522 | |
| 522 | 523 | for (buf, 0..) |c, i| { |
| ... | ... | @@ -632,7 +633,7 @@ pub const Ip6Address = extern struct { |
| 632 | 633 | |
| 633 | 634 | pub fn init(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Ip6Address { |
| 634 | 635 | return Ip6Address{ |
| 635 | .sa = os.sockaddr.in6{ | |
| 636 | .sa = posix.sockaddr.in6{ | |
| 636 | 637 | .addr = addr, |
| 637 | 638 | .port = mem.nativeToBig(u16, port), |
| 638 | 639 | .flowinfo = flowinfo, |
| ... | ... | @@ -702,51 +703,51 @@ pub const Ip6Address = extern struct { |
| 702 | 703 | try std.fmt.format(out_stream, "]:{}", .{port}); |
| 703 | 704 | } |
| 704 | 705 | |
| 705 | pub fn getOsSockLen(self: Ip6Address) os.socklen_t { | |
| 706 | pub fn getOsSockLen(self: Ip6Address) posix.socklen_t { | |
| 706 | 707 | _ = self; |
| 707 | return @sizeOf(os.sockaddr.in6); | |
| 708 | return @sizeOf(posix.sockaddr.in6); | |
| 708 | 709 | } |
| 709 | 710 | }; |
| 710 | 711 | |
| 711 | 712 | pub fn connectUnixSocket(path: []const u8) !Stream { |
| 712 | 713 | const opt_non_block = 0; |
| 713 | const sockfd = try os.socket( | |
| 714 | os.AF.UNIX, | |
| 715 | os.SOCK.STREAM | os.SOCK.CLOEXEC | opt_non_block, | |
| 714 | const sockfd = try posix.socket( | |
| 715 | posix.AF.UNIX, | |
| 716 | posix.SOCK.STREAM | posix.SOCK.CLOEXEC | opt_non_block, | |
| 716 | 717 | 0, |
| 717 | 718 | ); |
| 718 | 719 | errdefer Stream.close(.{ .handle = sockfd }); |
| 719 | 720 | |
| 720 | 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()); | |
| 722 | 723 | |
| 723 | return Stream{ .handle = sockfd }; | |
| 724 | return .{ .handle = sockfd }; | |
| 724 | 725 | } |
| 725 | 726 | |
| 726 | 727 | fn if_nametoindex(name: []const u8) IPv6InterfaceError!u32 { |
| 727 | if (builtin.target.os.tag == .linux) { | |
| 728 | var ifr: os.ifreq = undefined; | |
| 729 | const sockfd = try os.socket(os.AF.UNIX, os.SOCK.DGRAM | os.SOCK.CLOEXEC, 0); | |
| 728 | if (native_os == .linux) { | |
| 729 | var ifr: posix.ifreq = undefined; | |
| 730 | const sockfd = try posix.socket(posix.AF.UNIX, posix.SOCK.DGRAM | posix.SOCK.CLOEXEC, 0); | |
| 730 | 731 | defer Stream.close(.{ .handle = sockfd }); |
| 731 | 732 | |
| 732 | 733 | @memcpy(ifr.ifrn.name[0..name.len], name); |
| 733 | 734 | ifr.ifrn.name[name.len] = 0; |
| 734 | 735 | |
| 735 | 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); | |
| 737 | 738 | |
| 738 | return @as(u32, @bitCast(ifr.ifru.ivalue)); | |
| 739 | return @bitCast(ifr.ifru.ivalue); | |
| 739 | 740 | } |
| 740 | 741 | |
| 741 | if (comptime builtin.target.os.tag.isDarwin()) { | |
| 742 | if (name.len >= os.IFNAMESIZE) | |
| 742 | if (native_os.isDarwin()) { | |
| 743 | if (name.len >= posix.IFNAMESIZE) | |
| 743 | 744 | return error.NameTooLong; |
| 744 | 745 | |
| 745 | var if_name: [os.IFNAMESIZE:0]u8 = undefined; | |
| 746 | var if_name: [posix.IFNAMESIZE:0]u8 = undefined; | |
| 746 | 747 | @memcpy(if_name[0..name.len], name); |
| 747 | 748 | if_name[name.len] = 0; |
| 748 | 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 | 751 | if (index == 0) |
| 751 | 752 | return error.InterfaceNotFound; |
| 752 | 753 | return @as(u32, @bitCast(index)); |
| ... | ... | @@ -786,24 +787,24 @@ pub fn tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) T |
| 786 | 787 | else => return err, |
| 787 | 788 | }; |
| 788 | 789 | } |
| 789 | return std.os.ConnectError.ConnectionRefused; | |
| 790 | return posix.ConnectError.ConnectionRefused; | |
| 790 | 791 | } |
| 791 | 792 | |
| 792 | pub const TcpConnectToAddressError = std.os.SocketError || std.os.ConnectError; | |
| 793 | pub const TcpConnectToAddressError = posix.SocketError || posix.ConnectError; | |
| 793 | 794 | |
| 794 | 795 | pub fn tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream { |
| 795 | 796 | const nonblock = 0; |
| 796 | const sock_flags = os.SOCK.STREAM | nonblock | | |
| 797 | (if (builtin.target.os.tag == .windows) 0 else os.SOCK.CLOEXEC); | |
| 798 | const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO.TCP); | |
| 797 | const sock_flags = posix.SOCK.STREAM | nonblock | | |
| 798 | (if (native_os == .windows) 0 else posix.SOCK.CLOEXEC); | |
| 799 | const sockfd = try posix.socket(address.any.family, sock_flags, posix.IPPROTO.TCP); | |
| 799 | 800 | errdefer Stream.close(.{ .handle = sockfd }); |
| 800 | 801 | |
| 801 | try os.connect(sockfd, &address.any, address.getOsSockLen()); | |
| 802 | try posix.connect(sockfd, &address.any, address.getOsSockLen()); | |
| 802 | 803 | |
| 803 | 804 | return Stream{ .handle = sockfd }; |
| 804 | 805 | } |
| 805 | 806 | |
| 806 | const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || std.os.SocketError || std.os.BindError || std.os.SetSockOptError || error{ | |
| 807 | const GetAddressListError = std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError || posix.SocketError || posix.BindError || posix.SetSockOptError || error{ | |
| 807 | 808 | // TODO: break this up into error sets from the various underlying functions |
| 808 | 809 | |
| 809 | 810 | TemporaryNameServerFailure, |
| ... | ... | @@ -844,30 +845,30 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get |
| 844 | 845 | const arena = result.arena.allocator(); |
| 845 | 846 | errdefer result.deinit(); |
| 846 | 847 | |
| 847 | if (builtin.target.os.tag == .windows) { | |
| 848 | if (native_os == .windows) { | |
| 848 | 849 | const name_c = try allocator.dupeZ(u8, name); |
| 849 | 850 | defer allocator.free(name_c); |
| 850 | 851 | |
| 851 | 852 | const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port}); |
| 852 | 853 | defer allocator.free(port_c); |
| 853 | 854 | |
| 854 | const ws2_32 = os.windows.ws2_32; | |
| 855 | const hints = os.addrinfo{ | |
| 855 | const ws2_32 = windows.ws2_32; | |
| 856 | const hints = posix.addrinfo{ | |
| 856 | 857 | .flags = ws2_32.AI.NUMERICSERV, |
| 857 | .family = os.AF.UNSPEC, | |
| 858 | .socktype = os.SOCK.STREAM, | |
| 859 | .protocol = os.IPPROTO.TCP, | |
| 858 | .family = posix.AF.UNSPEC, | |
| 859 | .socktype = posix.SOCK.STREAM, | |
| 860 | .protocol = posix.IPPROTO.TCP, | |
| 860 | 861 | .canonname = null, |
| 861 | 862 | .addr = null, |
| 862 | 863 | .addrlen = 0, |
| 863 | 864 | .next = null, |
| 864 | 865 | }; |
| 865 | var res: ?*os.addrinfo = null; | |
| 866 | var res: ?*posix.addrinfo = null; | |
| 866 | 867 | var first = true; |
| 867 | 868 | while (true) { |
| 868 | 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 | @as(os.windows.ws2_32.WinsockError, @enumFromInt(0)) => break, | |
| 870 | switch (@as(windows.ws2_32.WinsockError, @enumFromInt(@as(u16, @intCast(rc))))) { | |
| 871 | @as(windows.ws2_32.WinsockError, @enumFromInt(0)) => break, | |
| 871 | 872 | .WSATRY_AGAIN => return error.TemporaryNameServerFailure, |
| 872 | 873 | .WSANO_RECOVERY => return error.NameServerFailure, |
| 873 | 874 | .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported, |
| ... | ... | @@ -879,10 +880,10 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get |
| 879 | 880 | .WSANOTINITIALISED => { |
| 880 | 881 | if (!first) return error.Unexpected; |
| 881 | 882 | first = false; |
| 882 | try os.windows.callWSAStartup(); | |
| 883 | try windows.callWSAStartup(); | |
| 883 | 884 | continue; |
| 884 | 885 | }, |
| 885 | else => |err| return os.windows.unexpectedWSAError(err), | |
| 886 | else => |err| return windows.unexpectedWSAError(err), | |
| 886 | 887 | } |
| 887 | 888 | } |
| 888 | 889 | defer ws2_32.freeaddrinfo(res); |
| ... | ... | @@ -923,18 +924,18 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get |
| 923 | 924 | const port_c = try std.fmt.allocPrintZ(allocator, "{}", .{port}); |
| 924 | 925 | defer allocator.free(port_c); |
| 925 | 926 | |
| 926 | const sys = if (builtin.target.os.tag == .windows) os.windows.ws2_32 else os.system; | |
| 927 | const hints = os.addrinfo{ | |
| 927 | const sys = if (native_os == .windows) windows.ws2_32 else posix.system; | |
| 928 | const hints = posix.addrinfo{ | |
| 928 | 929 | .flags = sys.AI.NUMERICSERV, |
| 929 | .family = os.AF.UNSPEC, | |
| 930 | .socktype = os.SOCK.STREAM, | |
| 931 | .protocol = os.IPPROTO.TCP, | |
| 930 | .family = posix.AF.UNSPEC, | |
| 931 | .socktype = posix.SOCK.STREAM, | |
| 932 | .protocol = posix.IPPROTO.TCP, | |
| 932 | 933 | .canonname = null, |
| 933 | 934 | .addr = null, |
| 934 | 935 | .addrlen = 0, |
| 935 | 936 | .next = null, |
| 936 | 937 | }; |
| 937 | var res: ?*os.addrinfo = null; | |
| 938 | var res: ?*posix.addrinfo = null; | |
| 938 | 939 | switch (sys.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) { |
| 939 | 940 | @as(sys.EAI, @enumFromInt(0)) => {}, |
| 940 | 941 | .ADDRFAMILY => return error.HostLacksNetworkAddresses, |
| ... | ... | @@ -947,8 +948,8 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get |
| 947 | 948 | .NONAME => return error.UnknownHostName, |
| 948 | 949 | .SERVICE => return error.ServiceUnavailable, |
| 949 | 950 | .SOCKTYPE => unreachable, // Invalid socket type requested in hints |
| 950 | .SYSTEM => switch (os.errno(-1)) { | |
| 951 | else => |e| return os.unexpectedErrno(e), | |
| 951 | .SYSTEM => switch (posix.errno(-1)) { | |
| 952 | else => |e| return posix.unexpectedErrno(e), | |
| 952 | 953 | }, |
| 953 | 954 | else => unreachable, |
| 954 | 955 | } |
| ... | ... | @@ -983,9 +984,9 @@ pub fn getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) Get |
| 983 | 984 | return result; |
| 984 | 985 | } |
| 985 | 986 | |
| 986 | if (builtin.target.os.tag == .linux) { | |
| 987 | if (native_os == .linux) { | |
| 987 | 988 | const flags = std.c.AI.NUMERICSERV; |
| 988 | const family = os.AF.UNSPEC; | |
| 989 | const family = posix.AF.UNSPEC; | |
| 989 | 990 | var lookup_addrs = std.ArrayList(LookupAddr).init(allocator); |
| 990 | 991 | defer lookup_addrs.deinit(); |
| 991 | 992 | |
| ... | ... | @@ -1026,7 +1027,7 @@ fn linuxLookupName( |
| 1026 | 1027 | addrs: *std.ArrayList(LookupAddr), |
| 1027 | 1028 | canon: *std.ArrayList(u8), |
| 1028 | 1029 | opt_name: ?[]const u8, |
| 1029 | family: os.sa_family_t, | |
| 1030 | family: posix.sa_family_t, | |
| 1030 | 1031 | flags: u32, |
| 1031 | 1032 | port: u16, |
| 1032 | 1033 | ) !void { |
| ... | ... | @@ -1066,9 +1067,9 @@ fn linuxLookupName( |
| 1066 | 1067 | |
| 1067 | 1068 | // No further processing is needed if there are fewer than 2 |
| 1068 | 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 | 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 | 1073 | } else true; |
| 1073 | 1074 | if (all_ip4) return; |
| 1074 | 1075 | |
| ... | ... | @@ -1081,42 +1082,42 @@ fn linuxLookupName( |
| 1081 | 1082 | // A more idiomatic "ziggy" implementation would be welcome. |
| 1082 | 1083 | for (addrs.items, 0..) |*addr, i| { |
| 1083 | 1084 | var key: i32 = 0; |
| 1084 | var sa6: os.sockaddr.in6 = undefined; | |
| 1085 | @memset(@as([*]u8, @ptrCast(&sa6))[0..@sizeOf(os.sockaddr.in6)], 0); | |
| 1086 | var da6 = os.sockaddr.in6{ | |
| 1087 | .family = os.AF.INET6, | |
| 1085 | var sa6: posix.sockaddr.in6 = undefined; | |
| 1086 | @memset(@as([*]u8, @ptrCast(&sa6))[0..@sizeOf(posix.sockaddr.in6)], 0); | |
| 1087 | var da6 = posix.sockaddr.in6{ | |
| 1088 | .family = posix.AF.INET6, | |
| 1088 | 1089 | .scope_id = addr.addr.in6.sa.scope_id, |
| 1089 | 1090 | .port = 65535, |
| 1090 | 1091 | .flowinfo = 0, |
| 1091 | 1092 | .addr = [1]u8{0} ** 16, |
| 1092 | 1093 | }; |
| 1093 | var sa4: os.sockaddr.in = undefined; | |
| 1094 | @memset(@as([*]u8, @ptrCast(&sa4))[0..@sizeOf(os.sockaddr.in)], 0); | |
| 1095 | var da4 = os.sockaddr.in{ | |
| 1096 | .family = os.AF.INET, | |
| 1094 | var sa4: posix.sockaddr.in = undefined; | |
| 1095 | @memset(@as([*]u8, @ptrCast(&sa4))[0..@sizeOf(posix.sockaddr.in)], 0); | |
| 1096 | var da4 = posix.sockaddr.in{ | |
| 1097 | .family = posix.AF.INET, | |
| 1097 | 1098 | .port = 65535, |
| 1098 | 1099 | .addr = 0, |
| 1099 | 1100 | .zero = [1]u8{0} ** 8, |
| 1100 | 1101 | }; |
| 1101 | var sa: *align(4) os.sockaddr = undefined; | |
| 1102 | var da: *align(4) os.sockaddr = undefined; | |
| 1103 | var salen: os.socklen_t = undefined; | |
| 1104 | var dalen: os.socklen_t = undefined; | |
| 1105 | if (addr.addr.any.family == os.AF.INET6) { | |
| 1102 | var sa: *align(4) posix.sockaddr = undefined; | |
| 1103 | var da: *align(4) posix.sockaddr = undefined; | |
| 1104 | var salen: posix.socklen_t = undefined; | |
| 1105 | var dalen: posix.socklen_t = undefined; | |
| 1106 | if (addr.addr.any.family == posix.AF.INET6) { | |
| 1106 | 1107 | da6.addr = addr.addr.in6.sa.addr; |
| 1107 | 1108 | da = @ptrCast(&da6); |
| 1108 | dalen = @sizeOf(os.sockaddr.in6); | |
| 1109 | dalen = @sizeOf(posix.sockaddr.in6); | |
| 1109 | 1110 | sa = @ptrCast(&sa6); |
| 1110 | salen = @sizeOf(os.sockaddr.in6); | |
| 1111 | salen = @sizeOf(posix.sockaddr.in6); | |
| 1111 | 1112 | } else { |
| 1112 | 1113 | sa6.addr[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*; |
| 1113 | 1114 | da6.addr[0..12].* = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff".*; |
| 1114 | 1115 | mem.writeInt(u32, da6.addr[12..], addr.addr.in.sa.addr, native_endian); |
| 1115 | 1116 | da4.addr = addr.addr.in.sa.addr; |
| 1116 | 1117 | da = @ptrCast(&da4); |
| 1117 | dalen = @sizeOf(os.sockaddr.in); | |
| 1118 | dalen = @sizeOf(posix.sockaddr.in); | |
| 1118 | 1119 | sa = @ptrCast(&sa4); |
| 1119 | salen = @sizeOf(os.sockaddr.in); | |
| 1120 | salen = @sizeOf(posix.sockaddr.in); | |
| 1120 | 1121 | } |
| 1121 | 1122 | const dpolicy = policyOf(da6.addr); |
| 1122 | 1123 | const dscope: i32 = scopeOf(da6.addr); |
| ... | ... | @@ -1124,13 +1125,13 @@ fn linuxLookupName( |
| 1124 | 1125 | const dprec: i32 = dpolicy.prec; |
| 1125 | 1126 | const MAXADDRS = 3; |
| 1126 | 1127 | var prefixlen: i32 = 0; |
| 1127 | const sock_flags = os.SOCK.DGRAM | os.SOCK.CLOEXEC; | |
| 1128 | if (os.socket(addr.addr.any.family, sock_flags, os.IPPROTO.UDP)) |fd| syscalls: { | |
| 1128 | const sock_flags = posix.SOCK.DGRAM | posix.SOCK.CLOEXEC; | |
| 1129 | if (posix.socket(addr.addr.any.family, sock_flags, posix.IPPROTO.UDP)) |fd| syscalls: { | |
| 1129 | 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 | 1132 | key |= DAS_USABLE; |
| 1132 | os.getsockname(fd, sa, &salen) catch break :syscalls; | |
| 1133 | if (addr.addr.any.family == os.AF.INET) { | |
| 1133 | posix.getsockname(fd, sa, &salen) catch break :syscalls; | |
| 1134 | if (addr.addr.any.family == posix.AF.INET) { | |
| 1134 | 1135 | mem.writeInt(u32, sa6.addr[12..16], sa4.addr, native_endian); |
| 1135 | 1136 | } |
| 1136 | 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 | 1268 | |
| 1268 | 1269 | fn linuxLookupNameFromNull( |
| 1269 | 1270 | addrs: *std.ArrayList(LookupAddr), |
| 1270 | family: os.sa_family_t, | |
| 1271 | family: posix.sa_family_t, | |
| 1271 | 1272 | flags: u32, |
| 1272 | 1273 | port: u16, |
| 1273 | 1274 | ) !void { |
| 1274 | 1275 | if ((flags & std.c.AI.PASSIVE) != 0) { |
| 1275 | if (family != os.AF.INET6) { | |
| 1276 | if (family != posix.AF.INET6) { | |
| 1276 | 1277 | (try addrs.addOne()).* = LookupAddr{ |
| 1277 | 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 | 1282 | (try addrs.addOne()).* = LookupAddr{ |
| 1282 | 1283 | .addr = Address.initIp6([1]u8{0} ** 16, port, 0, 0), |
| 1283 | 1284 | }; |
| 1284 | 1285 | } |
| 1285 | 1286 | } else { |
| 1286 | if (family != os.AF.INET6) { | |
| 1287 | if (family != posix.AF.INET6) { | |
| 1287 | 1288 | (try addrs.addOne()).* = LookupAddr{ |
| 1288 | 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 | 1293 | (try addrs.addOne()).* = LookupAddr{ |
| 1293 | 1294 | .addr = Address.initIp6(([1]u8{0} ** 15) ++ [1]u8{1}, port, 0, 0), |
| 1294 | 1295 | }; |
| ... | ... | @@ -1300,7 +1301,7 @@ fn linuxLookupNameFromHosts( |
| 1300 | 1301 | addrs: *std.ArrayList(LookupAddr), |
| 1301 | 1302 | canon: *std.ArrayList(u8), |
| 1302 | 1303 | name: []const u8, |
| 1303 | family: os.sa_family_t, | |
| 1304 | family: posix.sa_family_t, | |
| 1304 | 1305 | port: u16, |
| 1305 | 1306 | ) !void { |
| 1306 | 1307 | const file = fs.openFileAbsoluteZ("/etc/hosts", .{}) catch |err| switch (err) { |
| ... | ... | @@ -1374,7 +1375,7 @@ fn linuxLookupNameFromDnsSearch( |
| 1374 | 1375 | addrs: *std.ArrayList(LookupAddr), |
| 1375 | 1376 | canon: *std.ArrayList(u8), |
| 1376 | 1377 | name: []const u8, |
| 1377 | family: os.sa_family_t, | |
| 1378 | family: posix.sa_family_t, | |
| 1378 | 1379 | port: u16, |
| 1379 | 1380 | ) !void { |
| 1380 | 1381 | var rc: ResolvConf = undefined; |
| ... | ... | @@ -1429,7 +1430,7 @@ fn linuxLookupNameFromDns( |
| 1429 | 1430 | addrs: *std.ArrayList(LookupAddr), |
| 1430 | 1431 | canon: *std.ArrayList(u8), |
| 1431 | 1432 | name: []const u8, |
| 1432 | family: os.sa_family_t, | |
| 1433 | family: posix.sa_family_t, | |
| 1433 | 1434 | rc: ResolvConf, |
| 1434 | 1435 | port: u16, |
| 1435 | 1436 | ) !void { |
| ... | ... | @@ -1439,12 +1440,12 @@ fn linuxLookupNameFromDns( |
| 1439 | 1440 | .port = port, |
| 1440 | 1441 | }; |
| 1441 | 1442 | const AfRr = struct { |
| 1442 | af: os.sa_family_t, | |
| 1443 | af: posix.sa_family_t, | |
| 1443 | 1444 | rr: u8, |
| 1444 | 1445 | }; |
| 1445 | 1446 | const afrrs = [_]AfRr{ |
| 1446 | AfRr{ .af = os.AF.INET6, .rr = os.RR.A }, | |
| 1447 | AfRr{ .af = os.AF.INET, .rr = os.RR.AAAA }, | |
| 1447 | AfRr{ .af = posix.AF.INET6, .rr = posix.RR.A }, | |
| 1448 | AfRr{ .af = posix.AF.INET, .rr = posix.RR.AAAA }, | |
| 1448 | 1449 | }; |
| 1449 | 1450 | var qbuf: [2][280]u8 = undefined; |
| 1450 | 1451 | var abuf: [2][512]u8 = undefined; |
| ... | ... | @@ -1454,7 +1455,7 @@ fn linuxLookupNameFromDns( |
| 1454 | 1455 | |
| 1455 | 1456 | for (afrrs) |afrr| { |
| 1456 | 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 | 1459 | qp[nq] = qbuf[nq][0..len]; |
| 1459 | 1460 | nq += 1; |
| 1460 | 1461 | } |
| ... | ... | @@ -1582,8 +1583,8 @@ fn resMSendRc( |
| 1582 | 1583 | const timeout = 1000 * rc.timeout; |
| 1583 | 1584 | const attempts = rc.attempts; |
| 1584 | 1585 | |
| 1585 | var sl: os.socklen_t = @sizeOf(os.sockaddr.in); | |
| 1586 | var family: os.sa_family_t = os.AF.INET; | |
| 1586 | var sl: posix.socklen_t = @sizeOf(posix.sockaddr.in); | |
| 1587 | var family: posix.sa_family_t = posix.AF.INET; | |
| 1587 | 1588 | |
| 1588 | 1589 | var ns_list = std.ArrayList(Address).init(rc.ns.allocator); |
| 1589 | 1590 | defer ns_list.deinit(); |
| ... | ... | @@ -1594,18 +1595,18 @@ fn resMSendRc( |
| 1594 | 1595 | for (rc.ns.items, 0..) |iplit, i| { |
| 1595 | 1596 | ns[i] = iplit.addr; |
| 1596 | 1597 | assert(ns[i].getPort() == 53); |
| 1597 | if (iplit.addr.any.family != os.AF.INET) { | |
| 1598 | family = os.AF.INET6; | |
| 1598 | if (iplit.addr.any.family != posix.AF.INET) { | |
| 1599 | family = posix.AF.INET6; | |
| 1599 | 1600 | } |
| 1600 | 1601 | } |
| 1601 | 1602 | |
| 1602 | const flags = os.SOCK.DGRAM | os.SOCK.CLOEXEC | os.SOCK.NONBLOCK; | |
| 1603 | const fd = os.socket(family, flags, 0) catch |err| switch (err) { | |
| 1603 | const flags = posix.SOCK.DGRAM | posix.SOCK.CLOEXEC | posix.SOCK.NONBLOCK; | |
| 1604 | const fd = posix.socket(family, flags, 0) catch |err| switch (err) { | |
| 1604 | 1605 | error.AddressFamilyNotSupported => blk: { |
| 1605 | 1606 | // Handle case where system lacks IPv6 support |
| 1606 | if (family == os.AF.INET6) { | |
| 1607 | family = os.AF.INET; | |
| 1608 | break :blk try os.socket(os.AF.INET, flags, 0); | |
| 1607 | if (family == posix.AF.INET6) { | |
| 1608 | family = posix.AF.INET; | |
| 1609 | break :blk try posix.socket(posix.AF.INET, flags, 0); | |
| 1609 | 1610 | } |
| 1610 | 1611 | return err; |
| 1611 | 1612 | }, |
| ... | ... | @@ -1618,33 +1619,33 @@ fn resMSendRc( |
| 1618 | 1619 | // packet which is up to the caller to interpret. |
| 1619 | 1620 | |
| 1620 | 1621 | // Convert any IPv4 addresses in a mixed environment to v4-mapped |
| 1621 | if (family == os.AF.INET6) { | |
| 1622 | try os.setsockopt( | |
| 1622 | if (family == posix.AF.INET6) { | |
| 1623 | try posix.setsockopt( | |
| 1623 | 1624 | fd, |
| 1624 | os.SOL.IPV6, | |
| 1625 | os.linux.IPV6.V6ONLY, | |
| 1625 | posix.SOL.IPV6, | |
| 1626 | std.os.linux.IPV6.V6ONLY, | |
| 1626 | 1627 | &mem.toBytes(@as(c_int, 0)), |
| 1627 | 1628 | ); |
| 1628 | 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 | 1631 | mem.writeInt(u32, ns[i].in6.sa.addr[12..], ns[i].in.sa.addr, native_endian); |
| 1631 | 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 | 1634 | ns[i].in6.sa.flowinfo = 0; |
| 1634 | 1635 | ns[i].in6.sa.scope_id = 0; |
| 1635 | 1636 | } |
| 1636 | sl = @sizeOf(os.sockaddr.in6); | |
| 1637 | sl = @sizeOf(posix.sockaddr.in6); | |
| 1637 | 1638 | } |
| 1638 | 1639 | |
| 1639 | 1640 | // Get local address and open/bind a socket |
| 1640 | 1641 | var sa: Address = undefined; |
| 1641 | 1642 | @memset(@as([*]u8, @ptrCast(&sa))[0..@sizeOf(Address)], 0); |
| 1642 | 1643 | sa.any.family = family; |
| 1643 | try os.bind(fd, &sa.any, sl); | |
| 1644 | try posix.bind(fd, &sa.any, sl); | |
| 1644 | 1645 | |
| 1645 | var pfd = [1]os.pollfd{os.pollfd{ | |
| 1646 | var pfd = [1]posix.pollfd{posix.pollfd{ | |
| 1646 | 1647 | .fd = fd, |
| 1647 | .events = os.POLL.IN, | |
| 1648 | .events = posix.POLL.IN, | |
| 1648 | 1649 | .revents = undefined, |
| 1649 | 1650 | }}; |
| 1650 | 1651 | const retry_interval = timeout / attempts; |
| ... | ... | @@ -1663,7 +1664,7 @@ fn resMSendRc( |
| 1663 | 1664 | if (answers[i].len == 0) { |
| 1664 | 1665 | var j: usize = 0; |
| 1665 | 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 | 1674 | |
| 1674 | 1675 | // Wait for a response, or until time to retry |
| 1675 | 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 | 1678 | if (nevents == 0) continue; |
| 1678 | 1679 | |
| 1679 | 1680 | while (true) { |
| 1680 | 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; | |
| 1682 | 1683 | |
| 1683 | 1684 | // Ignore non-identifiable packets |
| 1684 | 1685 | if (rlen < 4) continue; |
| ... | ... | @@ -1704,7 +1705,7 @@ fn resMSendRc( |
| 1704 | 1705 | 0, 3 => {}, |
| 1705 | 1706 | 2 => if (servfail_retry != 0) { |
| 1706 | 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 | 1710 | else => continue, |
| 1710 | 1711 | } |
| ... | ... | @@ -1758,24 +1759,24 @@ fn dnsParse( |
| 1758 | 1759 | |
| 1759 | 1760 | fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) !void { |
| 1760 | 1761 | switch (rr) { |
| 1761 | os.RR.A => { | |
| 1762 | posix.RR.A => { | |
| 1762 | 1763 | if (data.len != 4) return error.InvalidDnsARecord; |
| 1763 | 1764 | const new_addr = try ctx.addrs.addOne(); |
| 1764 | 1765 | new_addr.* = LookupAddr{ |
| 1765 | 1766 | .addr = Address.initIp4(data[0..4].*, ctx.port), |
| 1766 | 1767 | }; |
| 1767 | 1768 | }, |
| 1768 | os.RR.AAAA => { | |
| 1769 | posix.RR.AAAA => { | |
| 1769 | 1770 | if (data.len != 16) return error.InvalidDnsAAAARecord; |
| 1770 | 1771 | const new_addr = try ctx.addrs.addOne(); |
| 1771 | 1772 | new_addr.* = LookupAddr{ |
| 1772 | 1773 | .addr = Address.initIp6(data[0..16].*, ctx.port, 0, 0), |
| 1773 | 1774 | }; |
| 1774 | 1775 | }, |
| 1775 | os.RR.CNAME => { | |
| 1776 | posix.RR.CNAME => { | |
| 1776 | 1777 | var tmp: [256]u8 = undefined; |
| 1777 | 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 | 1780 | const canon_name = mem.sliceTo(&tmp, 0); |
| 1780 | 1781 | if (isValidHostName(canon_name)) { |
| 1781 | 1782 | ctx.canon.items.len = 0; |
| ... | ... | @@ -1792,14 +1793,14 @@ pub const Stream = struct { |
| 1792 | 1793 | handle: posix.socket_t, |
| 1793 | 1794 | |
| 1794 | 1795 | pub fn close(s: Stream) void { |
| 1795 | switch (builtin.os.tag) { | |
| 1796 | .windows => std.os.windows.closesocket(s.handle) catch unreachable, | |
| 1796 | switch (native_os) { | |
| 1797 | .windows => windows.closesocket(s.handle) catch unreachable, | |
| 1797 | 1798 | else => posix.close(s.handle), |
| 1798 | 1799 | } |
| 1799 | 1800 | } |
| 1800 | 1801 | |
| 1801 | pub const ReadError = os.ReadError; | |
| 1802 | pub const WriteError = os.WriteError; | |
| 1802 | pub const ReadError = posix.ReadError; | |
| 1803 | pub const WriteError = posix.WriteError; | |
| 1803 | 1804 | |
| 1804 | 1805 | pub const Reader = io.Reader(Stream, ReadError, read); |
| 1805 | 1806 | pub const Writer = io.Writer(Stream, WriteError, write); |
| ... | ... | @@ -1813,22 +1814,22 @@ pub const Stream = struct { |
| 1813 | 1814 | } |
| 1814 | 1815 | |
| 1815 | 1816 | pub fn read(self: Stream, buffer: []u8) ReadError!usize { |
| 1816 | if (builtin.os.tag == .windows) { | |
| 1817 | return os.windows.ReadFile(self.handle, buffer, null); | |
| 1817 | if (native_os == .windows) { | |
| 1818 | return windows.ReadFile(self.handle, buffer, null); | |
| 1818 | 1819 | } |
| 1819 | 1820 | |
| 1820 | return os.read(self.handle, buffer); | |
| 1821 | return posix.read(self.handle, buffer); | |
| 1821 | 1822 | } |
| 1822 | 1823 | |
| 1823 | pub fn readv(s: Stream, iovecs: []const os.iovec) ReadError!usize { | |
| 1824 | if (builtin.os.tag == .windows) { | |
| 1824 | pub fn readv(s: Stream, iovecs: []const posix.iovec) ReadError!usize { | |
| 1825 | if (native_os == .windows) { | |
| 1825 | 1826 | // TODO improve this to use ReadFileScatter |
| 1826 | 1827 | if (iovecs.len == 0) return @as(usize, 0); |
| 1827 | 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 | } |
| 1830 | 1831 | |
| 1831 | return os.readv(s.handle, iovecs); | |
| 1832 | return posix.readv(s.handle, iovecs); | |
| 1832 | 1833 | } |
| 1833 | 1834 | |
| 1834 | 1835 | /// Returns the number of bytes read. If the number read is smaller than |
| ... | ... | @@ -1858,11 +1859,11 @@ pub const Stream = struct { |
| 1858 | 1859 | /// file system thread instead of non-blocking. It needs to be reworked to properly |
| 1859 | 1860 | /// use non-blocking I/O. |
| 1860 | 1861 | pub fn write(self: Stream, buffer: []const u8) WriteError!usize { |
| 1861 | if (builtin.os.tag == .windows) { | |
| 1862 | return os.windows.WriteFile(self.handle, buffer, null); | |
| 1862 | if (native_os == .windows) { | |
| 1863 | return windows.WriteFile(self.handle, buffer, null); | |
| 1863 | 1864 | } |
| 1864 | 1865 | |
| 1865 | return os.write(self.handle, buffer); | |
| 1866 | return posix.write(self.handle, buffer); | |
| 1866 | 1867 | } |
| 1867 | 1868 | |
| 1868 | 1869 | pub fn writeAll(self: Stream, bytes: []const u8) WriteError!void { |
| ... | ... | @@ -1874,15 +1875,15 @@ pub const Stream = struct { |
| 1874 | 1875 | |
| 1875 | 1876 | /// See https://github.com/ziglang/zig/issues/7699 |
| 1876 | 1877 | /// See equivalent function: `std.fs.File.writev`. |
| 1877 | pub fn writev(self: Stream, iovecs: []const os.iovec_const) WriteError!usize { | |
| 1878 | return os.writev(self.handle, iovecs); | |
| 1878 | pub fn writev(self: Stream, iovecs: []const posix.iovec_const) WriteError!usize { | |
| 1879 | return posix.writev(self.handle, iovecs); | |
| 1879 | 1880 | } |
| 1880 | 1881 | |
| 1881 | 1882 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 1882 | 1883 | /// order to handle partial writes from the underlying OS layer. |
| 1883 | 1884 | /// See https://github.com/ziglang/zig/issues/7699 |
| 1884 | 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 | 1887 | if (iovecs.len == 0) return; |
| 1887 | 1888 | |
| 1888 | 1889 | var i: usize = 0; |
lib/std/os.zig+183-7607| ... | ... | @@ -11,8 +11,6 @@ |
| 11 | 11 | //! On Linux libc can be side-stepped by using `std.os.linux` directly. |
| 12 | 12 | //! * For Windows, this file represents the API that libc would provide for |
| 13 | 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. | |
| 16 | 14 | |
| 17 | 15 | const root = @import("root"); |
| 18 | 16 | const std = @import("std.zig"); |
| ... | ... | @@ -24,15 +22,8 @@ const elf = std.elf; |
| 24 | 22 | const fs = std.fs; |
| 25 | 23 | const dl = @import("dynamic_library.zig"); |
| 26 | 24 | const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES; |
| 25 | const posix = std.posix; | |
| 27 | 26 | |
| 28 | pub const darwin = std.c; | |
| 29 | pub const dragonfly = std.c; | |
| 30 | pub const freebsd = std.c; | |
| 31 | pub const haiku = std.c; | |
| 32 | pub const netbsd = std.c; | |
| 33 | pub const openbsd = std.c; | |
| 34 | pub const solaris = std.c; | |
| 35 | pub const illumos = std.c; | |
| 36 | 27 | pub const linux = @import("os/linux.zig"); |
| 37 | 28 | pub const plan9 = @import("os/plan9.zig"); |
| 38 | 29 | pub const uefi = @import("os/uefi.zig"); |
| ... | ... | @@ -40,214 +31,15 @@ pub const wasi = @import("os/wasi.zig"); |
| 40 | 31 | pub const emscripten = @import("os/emscripten.zig"); |
| 41 | 32 | pub const windows = @import("os/windows.zig"); |
| 42 | 33 | |
| 43 | comptime { | |
| 44 | assert(@import("std") == std); // std lib tests require --zig-lib-dir | |
| 45 | } | |
| 46 | ||
| 47 | 34 | test { |
| 48 | _ = darwin; | |
| 49 | 35 | _ = linux; |
| 50 | 36 | if (builtin.os.tag == .uefi) { |
| 51 | 37 | _ = uefi; |
| 52 | 38 | } |
| 53 | 39 | _ = wasi; |
| 54 | 40 | _ = windows; |
| 55 | ||
| 56 | _ = @import("os/test.zig"); | |
| 57 | 41 | } |
| 58 | 42 | |
| 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. | |
| 62 | pub const system = if (@hasDecl(root, "os") and @hasDecl(root.os, "system") and root.os != @This()) | |
| 63 | root.os.system | |
| 64 | else if (use_libc) | |
| 65 | std.c | |
| 66 | else 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. | |
| 74 | const use_libc = builtin.link_libc or switch (builtin.os.tag) { | |
| 75 | .windows, .wasi => true, | |
| 76 | else => false, | |
| 77 | }; | |
| 78 | ||
| 79 | pub const AF = system.AF; | |
| 80 | pub const AF_SUN = system.AF_SUN; | |
| 81 | pub const ARCH = system.ARCH; | |
| 82 | pub const AT = system.AT; | |
| 83 | pub const AT_SUN = system.AT_SUN; | |
| 84 | pub const CLOCK = system.CLOCK; | |
| 85 | pub const CPU_COUNT = system.CPU_COUNT; | |
| 86 | pub const CTL = system.CTL; | |
| 87 | pub const DT = system.DT; | |
| 88 | pub const E = system.E; | |
| 89 | pub const Elf_Symndx = system.Elf_Symndx; | |
| 90 | pub const F = system.F; | |
| 91 | pub const FD_CLOEXEC = system.FD_CLOEXEC; | |
| 92 | pub const Flock = system.Flock; | |
| 93 | pub const HOST_NAME_MAX = system.HOST_NAME_MAX; | |
| 94 | pub const HW = system.HW; | |
| 95 | pub const IFNAMESIZE = system.IFNAMESIZE; | |
| 96 | pub const IOV_MAX = system.IOV_MAX; | |
| 97 | pub const IPPROTO = system.IPPROTO; | |
| 98 | pub const KERN = system.KERN; | |
| 99 | pub const Kevent = system.Kevent; | |
| 100 | pub const LOCK = system.LOCK; | |
| 101 | pub const MADV = system.MADV; | |
| 102 | pub const MAP = system.MAP; | |
| 103 | pub const MSF = system.MSF; | |
| 104 | pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN; | |
| 105 | pub const MFD = system.MFD; | |
| 106 | pub const MMAP2_UNIT = system.MMAP2_UNIT; | |
| 107 | pub const MSG = system.MSG; | |
| 108 | pub const NAME_MAX = system.NAME_MAX; | |
| 109 | pub const O = system.O; | |
| 110 | pub const PATH_MAX = system.PATH_MAX; | |
| 111 | pub const POLL = system.POLL; | |
| 112 | pub const POSIX_FADV = system.POSIX_FADV; | |
| 113 | pub const PR = system.PR; | |
| 114 | pub const PROT = system.PROT; | |
| 115 | pub const REG = system.REG; | |
| 116 | pub const RLIM = system.RLIM; | |
| 117 | pub const RR = system.RR; | |
| 118 | pub const S = system.S; | |
| 119 | pub const SA = system.SA; | |
| 120 | pub const SC = system.SC; | |
| 121 | pub const _SC = system._SC; | |
| 122 | pub const SEEK = system.SEEK; | |
| 123 | pub const SHUT = system.SHUT; | |
| 124 | pub const SIG = system.SIG; | |
| 125 | pub const SIOCGIFINDEX = system.SIOCGIFINDEX; | |
| 126 | pub const SO = system.SO; | |
| 127 | pub const SOCK = system.SOCK; | |
| 128 | pub const SOL = system.SOL; | |
| 129 | pub const STDERR_FILENO = system.STDERR_FILENO; | |
| 130 | pub const STDIN_FILENO = system.STDIN_FILENO; | |
| 131 | pub const STDOUT_FILENO = system.STDOUT_FILENO; | |
| 132 | pub const SYS = system.SYS; | |
| 133 | pub const Sigaction = system.Sigaction; | |
| 134 | pub const Stat = system.Stat; | |
| 135 | pub const T = system.T; | |
| 136 | pub const TCSA = system.TCSA; | |
| 137 | pub const TCP = system.TCP; | |
| 138 | pub const VDSO = system.VDSO; | |
| 139 | pub const W = system.W; | |
| 140 | pub const addrinfo = system.addrinfo; | |
| 141 | pub const blkcnt_t = system.blkcnt_t; | |
| 142 | pub const blksize_t = system.blksize_t; | |
| 143 | pub const clock_t = system.clock_t; | |
| 144 | pub const cpu_set_t = system.cpu_set_t; | |
| 145 | pub const dev_t = system.dev_t; | |
| 146 | pub const dl_phdr_info = system.dl_phdr_info; | |
| 147 | pub const empty_sigset = system.empty_sigset; | |
| 148 | pub const filled_sigset = system.filled_sigset; | |
| 149 | pub const fd_t = system.fd_t; | |
| 150 | pub const gid_t = system.gid_t; | |
| 151 | pub const ifreq = system.ifreq; | |
| 152 | pub const ino_t = system.ino_t; | |
| 153 | pub const mcontext_t = system.mcontext_t; | |
| 154 | pub const mode_t = system.mode_t; | |
| 155 | pub const msghdr = system.msghdr; | |
| 156 | pub const msghdr_const = system.msghdr_const; | |
| 157 | pub const nfds_t = system.nfds_t; | |
| 158 | pub const nlink_t = system.nlink_t; | |
| 159 | pub const off_t = system.off_t; | |
| 160 | pub const pid_t = system.pid_t; | |
| 161 | pub const pollfd = system.pollfd; | |
| 162 | pub const port_t = system.port_t; | |
| 163 | pub const port_event = system.port_event; | |
| 164 | pub const port_notify = system.port_notify; | |
| 165 | pub const file_obj = system.file_obj; | |
| 166 | pub const rlim_t = system.rlim_t; | |
| 167 | pub const rlimit = system.rlimit; | |
| 168 | pub const rlimit_resource = system.rlimit_resource; | |
| 169 | pub const rusage = system.rusage; | |
| 170 | pub const sa_family_t = system.sa_family_t; | |
| 171 | pub const siginfo_t = system.siginfo_t; | |
| 172 | pub const sigset_t = system.sigset_t; | |
| 173 | pub const sockaddr = system.sockaddr; | |
| 174 | pub const socklen_t = system.socklen_t; | |
| 175 | pub const stack_t = system.stack_t; | |
| 176 | pub const time_t = system.time_t; | |
| 177 | pub const timespec = system.timespec; | |
| 178 | pub const timestamp_t = system.timestamp_t; | |
| 179 | pub const timeval = system.timeval; | |
| 180 | pub const timezone = system.timezone; | |
| 181 | pub const ucontext_t = system.ucontext_t; | |
| 182 | pub const uid_t = system.uid_t; | |
| 183 | pub const user_desc = system.user_desc; | |
| 184 | pub const utsname = system.utsname; | |
| 185 | pub const winsize = system.winsize; | |
| 186 | ||
| 187 | pub const termios = system.termios; | |
| 188 | pub const CSIZE = system.CSIZE; | |
| 189 | pub const NCCS = system.NCCS; | |
| 190 | pub const cc_t = system.cc_t; | |
| 191 | pub const V = system.V; | |
| 192 | pub const speed_t = system.speed_t; | |
| 193 | pub const tc_iflag_t = system.tc_iflag_t; | |
| 194 | pub const tc_oflag_t = system.tc_oflag_t; | |
| 195 | pub const tc_cflag_t = system.tc_cflag_t; | |
| 196 | pub const tc_lflag_t = system.tc_lflag_t; | |
| 197 | ||
| 198 | pub const F_OK = system.F_OK; | |
| 199 | pub const R_OK = system.R_OK; | |
| 200 | pub const W_OK = system.W_OK; | |
| 201 | pub const X_OK = system.X_OK; | |
| 202 | ||
| 203 | pub const iovec = extern struct { | |
| 204 | iov_base: [*]u8, | |
| 205 | iov_len: usize, | |
| 206 | }; | |
| 207 | ||
| 208 | pub const iovec_const = extern struct { | |
| 209 | iov_base: [*]const u8, | |
| 210 | iov_len: usize, | |
| 211 | }; | |
| 212 | ||
| 213 | pub const ACCMODE = enum(u2) { | |
| 214 | RDONLY = 0, | |
| 215 | WRONLY = 1, | |
| 216 | RDWR = 2, | |
| 217 | }; | |
| 218 | ||
| 219 | pub 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. | |
| 242 | pub 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 | ||
| 249 | pub const socket_t = if (builtin.os.tag == .windows) windows.ws2_32.SOCKET else fd_t; | |
| 250 | ||
| 251 | 43 | /// See also `getenv`. Populated by startup code before main(). |
| 252 | 44 | /// TODO this is a footgun because the value will be undefined when using `zig build-lib`. |
| 253 | 45 | /// 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 | 54 | else => undefined, |
| 263 | 55 | }; |
| 264 | 56 | |
| 265 | pub const have_sigpipe_support = @hasDecl(@This(), "SIG") and @hasDecl(SIG, "PIPE"); | |
| 266 | ||
| 267 | fn noopSigHandler(_: c_int) callconv(.C) void {} | |
| 268 | ||
| 269 | /// On default executed by posix startup code before main(), if SIGPIPE is supported. | |
| 270 | pub 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. | |
| 290 | pub 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. | |
| 297 | pub 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); | |
| 57 | /// Call from Windows-specific code if you already have a WTF-16LE encoded, null terminated string. | |
| 58 | /// Otherwise use `access` or `accessZ`. | |
| 59 | pub fn accessW(path: [*:0]const u16) windows.GetFileAttributesError!void { | |
| 60 | const ret = try windows.GetFileAttributesW(path); | |
| 61 | if (ret != windows.INVALID_FILE_ATTRIBUTES) { | |
| 303 | 62 | return; |
| 304 | 63 | } |
| 305 | if (builtin.target.isDarwin()) { | |
| 306 | // This avoids the EINTR problem. | |
| 307 | switch (darwin.getErrno(darwin.@"close$NOCANCEL"(fd))) { | |
| 308 | .BADF => unreachable, // Always a race condition. | |
| 309 | else => return, | |
| 310 | } | |
| 311 | } | |
| 312 | switch (errno(system.close(fd))) { | |
| 313 | .BADF => unreachable, // Always a race condition. | |
| 314 | .INTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425 | |
| 315 | else => return, | |
| 64 | switch (windows.kernel32.GetLastError()) { | |
| 65 | .FILE_NOT_FOUND => return error.FileNotFound, | |
| 66 | .PATH_NOT_FOUND => return error.FileNotFound, | |
| 67 | .ACCESS_DENIED => return error.PermissionDenied, | |
| 68 | else => |err| return windows.unexpectedError(err), | |
| 316 | 69 | } |
| 317 | 70 | } |
| 318 | 71 | |
| 319 | pub const FChmodError = error{ | |
| 320 | AccessDenied, | |
| 321 | InputOutput, | |
| 322 | SymLinkLoop, | |
| 323 | FileNotFound, | |
| 324 | SystemResources, | |
| 325 | ReadOnlyFileSystem, | |
| 326 | } || UnexpectedError; | |
| 327 | ||
| 328 | /// Changes the mode of the file referred to by the file descriptor. | |
| 329 | /// The process must have the correct privileges in order to do this | |
| 330 | /// successfully, or must have the effective user ID matching the owner | |
| 331 | /// of the file. | |
| 332 | pub 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); | |
| 72 | pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool { | |
| 73 | return switch (os.tag) { | |
| 74 | .windows, | |
| 75 | .macos, | |
| 76 | .ios, | |
| 77 | .watchos, | |
| 78 | .tvos, | |
| 79 | .linux, | |
| 80 | .solaris, | |
| 81 | .illumos, | |
| 82 | .freebsd, | |
| 83 | => true, | |
| 337 | 84 | |
| 338 | switch (system.getErrno(res)) { | |
| 339 | .SUCCESS => return, | |
| 340 | .INTR => continue, | |
| 341 | .BADF => unreachable, | |
| 342 | .FAULT => unreachable, | |
| 343 | .INVAL => unreachable, | |
| 344 | .ACCES => return error.AccessDenied, | |
| 345 | .IO => return error.InputOutput, | |
| 346 | .LOOP => return error.SymLinkLoop, | |
| 347 | .NOENT => return error.FileNotFound, | |
| 348 | .NOMEM => return error.SystemResources, | |
| 349 | .NOTDIR => return error.FileNotFound, | |
| 350 | .PERM => return error.AccessDenied, | |
| 351 | .ROFS => return error.ReadOnlyFileSystem, | |
| 352 | else => |err| return unexpectedErrno(err), | |
| 353 | } | |
| 354 | } | |
| 85 | .dragonfly => os.version_range.semver.max.order(.{ .major = 6, .minor = 0, .patch = 0 }) != .lt, | |
| 86 | .netbsd => os.version_range.semver.max.order(.{ .major = 10, .minor = 0, .patch = 0 }) != .lt, | |
| 87 | else => false, | |
| 88 | }; | |
| 355 | 89 | } |
| 356 | 90 | |
| 357 | const FChmodAtError = FChmodError || error{ | |
| 358 | /// A component of `path` exceeded `NAME_MAX`, or the entire path exceeded | |
| 359 | /// `PATH_MAX`. | |
| 360 | NameTooLong, | |
| 361 | /// `path` resolves to a symbolic link, and `AT.SYMLINK_NOFOLLOW` was set | |
| 362 | /// in `flags`. This error only occurs on Linux, where changing the mode of | |
| 363 | /// a symbolic link has no meaning and can cause undefined behaviour on | |
| 364 | /// certain filesystems. | |
| 365 | /// | |
| 366 | /// The procfs fallback was used but procfs was not mounted. | |
| 367 | OperationNotSupported, | |
| 368 | /// The procfs fallback was used but the process exceeded its open file | |
| 369 | /// limit. | |
| 370 | ProcessFdQuotaExceeded, | |
| 371 | /// The procfs fallback was used but the system exceeded it open file limit. | |
| 372 | SystemFdQuotaExceeded, | |
| 373 | }; | |
| 374 | ||
| 375 | var 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. | |
| 91 | /// Return canonical path of handle `fd`. | |
| 381 | 92 | /// |
| 382 | /// On Linux the `fchmodat2` syscall will be used if available, otherwise a | |
| 383 | /// workaround using procfs will be employed. Changing the mode of a symbolic | |
| 384 | /// link with `AT.SYMLINK_NOFOLLOW` set will also return | |
| 385 | /// `OperationNotSupported`, as: | |
| 93 | /// This function is very host-specific and is not universally supported by all hosts. | |
| 94 | /// For example, while it generally works on Linux, macOS, FreeBSD or Windows, it is | |
| 95 | /// unsupported on WASI. | |
| 386 | 96 | /// |
| 387 | /// 1. Permissions on the link are ignored when resolving its target. | |
| 388 | /// 2. This operation has been known to invoke undefined behaviour across | |
| 389 | /// different filesystems[1]. | |
| 97 | /// * On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). | |
| 98 | /// * On other platforms, the result is an opaque sequence of bytes with no particular encoding. | |
| 390 | 99 | /// |
| 391 | /// [1]: https://sourceware.org/legacy-ml/libc-alpha/2020-02/msg00467.html. | |
| 392 | pub inline fn fchmodat(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void { | |
| 393 | if (!std.fs.has_executable_bit) @compileError("fchmodat unsupported by target OS"); | |
| 394 | ||
| 395 | // No special handling for linux is needed if we can use the libc fallback | |
| 396 | // or `flags` is empty. Glibc only added the fallback in 2.32. | |
| 397 | const skip_fchmodat_fallback = builtin.os.tag != .linux or | |
| 398 | std.c.versionCheck(.{ .major = 2, .minor = 32, .patch = 0 }) or | |
| 399 | flags == 0; | |
| 400 | ||
| 401 | // This function is marked inline so that when flags is comptime-known, | |
| 402 | // skip_fchmodat_fallback will be comptime-known true. | |
| 403 | if (skip_fchmodat_fallback) | |
| 404 | return fchmodat1(dirfd, path, mode, flags); | |
| 405 | ||
| 406 | return fchmodat2(dirfd, path, mode, flags); | |
| 407 | } | |
| 408 | ||
| 409 | fn 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 | ||
| 436 | fn 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 | } | |
| 100 | /// Calling this function is usually a bug. | |
| 101 | pub fn getFdPath(fd: std.posix.fd_t, out_buffer: *[MAX_PATH_BYTES]u8) std.posix.RealPathError![]u8 { | |
| 102 | if (!comptime isGetFdPathSupportedOnTarget(builtin.os)) { | |
| 103 | @compileError("querying for canonical path of a handle is unsupported on this host"); | |
| 529 | 104 | } |
| 530 | } | |
| 531 | ||
| 532 | pub 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. | |
| 546 | pub fn fchown(fd: fd_t, owner: ?uid_t, group: ?gid_t) FChownError!void { | |
| 547 | 105 | switch (builtin.os.tag) { |
| 548 | .windows, .wasi => @compileError("Unsupported OS"), | |
| 549 | else => {}, | |
| 550 | } | |
| 551 | ||
| 552 | while (true) { | |
| 553 | const res = system.fchown(fd, owner orelse ~@as(uid_t, 0), group orelse ~@as(gid_t, 0)); | |
| 554 | ||
| 555 | switch (system.getErrno(res)) { | |
| 556 | .SUCCESS => return, | |
| 557 | .INTR => continue, | |
| 558 | .BADF => unreachable, // Can be reached if the fd refers to a directory opened without `OpenDirOptions{ .iterate = true }` | |
| 559 | ||
| 560 | .FAULT => unreachable, | |
| 561 | .INVAL => unreachable, | |
| 562 | .ACCES => return error.AccessDenied, | |
| 563 | .IO => return error.InputOutput, | |
| 564 | .LOOP => return error.SymLinkLoop, | |
| 565 | .NOENT => return error.FileNotFound, | |
| 566 | .NOMEM => return error.SystemResources, | |
| 567 | .NOTDIR => return error.FileNotFound, | |
| 568 | .PERM => return error.AccessDenied, | |
| 569 | .ROFS => return error.ReadOnlyFileSystem, | |
| 570 | else => |err| return unexpectedErrno(err), | |
| 571 | } | |
| 572 | } | |
| 573 | } | |
| 574 | ||
| 575 | pub const RebootError = error{ | |
| 576 | PermissionDenied, | |
| 577 | } || UnexpectedError; | |
| 578 | ||
| 579 | pub const RebootCommand = switch (builtin.os.tag) { | |
| 580 | .linux => union(linux.LINUX_REBOOT.CMD) { | |
| 581 | RESTART: void, | |
| 582 | HALT: void, | |
| 583 | CAD_ON: void, | |
| 584 | CAD_OFF: void, | |
| 585 | POWER_OFF: void, | |
| 586 | RESTART2: [*:0]const u8, | |
| 587 | SW_SUSPEND: void, | |
| 588 | KEXEC: void, | |
| 589 | }, | |
| 590 | else => @compileError("Unsupported OS"), | |
| 591 | }; | |
| 106 | .windows => { | |
| 107 | var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined; | |
| 108 | const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]); | |
| 592 | 109 | |
| 593 | pub fn reboot(cmd: RebootCommand) RebootError!void { | |
| 594 | switch (builtin.os.tag) { | |
| 595 | .linux => { | |
| 596 | switch (system.getErrno(linux.reboot( | |
| 597 | .MAGIC1, | |
| 598 | .MAGIC2, | |
| 599 | cmd, | |
| 600 | switch (cmd) { | |
| 601 | .RESTART2 => |s| s, | |
| 602 | else => null, | |
| 603 | }, | |
| 604 | ))) { | |
| 110 | const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice); | |
| 111 | return out_buffer[0..end_index]; | |
| 112 | }, | |
| 113 | .macos, .ios, .watchos, .tvos => { | |
| 114 | // On macOS, we can use F.GETPATH fcntl command to query the OS for | |
| 115 | // the path to the file descriptor. | |
| 116 | @memset(out_buffer[0..MAX_PATH_BYTES], 0); | |
| 117 | switch (posix.errno(posix.system.fcntl(fd, posix.F.GETPATH, out_buffer))) { | |
| 605 | 118 | .SUCCESS => {}, |
| 606 | .PERM => return error.PermissionDenied, | |
| 607 | else => |err| return std.os.unexpectedErrno(err), | |
| 608 | } | |
| 609 | switch (cmd) { | |
| 610 | .CAD_OFF => {}, | |
| 611 | .CAD_ON => {}, | |
| 612 | .SW_SUSPEND => {}, | |
| 613 | ||
| 614 | .HALT => unreachable, | |
| 615 | .KEXEC => unreachable, | |
| 616 | .POWER_OFF => unreachable, | |
| 617 | .RESTART => unreachable, | |
| 618 | .RESTART2 => unreachable, | |
| 119 | .BADF => return error.FileNotFound, | |
| 120 | .NOSPC => return error.NameTooLong, | |
| 121 | // TODO man pages for fcntl on macOS don't really tell you what | |
| 122 | // errno values to expect when command is F.GETPATH... | |
| 123 | else => |err| return posix.unexpectedErrno(err), | |
| 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"), | |
| 622 | } | |
| 623 | } | |
| 624 | ||
| 625 | pub const GetRandomError = OpenError; | |
| 128 | .linux => { | |
| 129 | var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined; | |
| 130 | const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/fd/{d}", .{fd}) catch unreachable; | |
| 626 | 131 | |
| 627 | /// Obtain a series of random bytes. These bytes can be used to seed user-space | |
| 628 | /// random number generators or for cryptographic purposes. | |
| 629 | /// When linking against libc, this calls the | |
| 630 | /// appropriate OS-specific library call. Otherwise it uses the zig standard | |
| 631 | /// library implementation. | |
| 632 | pub fn getrandom(buffer: []u8) GetRandomError!void { | |
| 633 | if (builtin.os.tag == .windows) { | |
| 634 | return windows.RtlGenRandom(buffer); | |
| 635 | } | |
| 636 | if (builtin.os.tag == .linux or builtin.os.tag == .freebsd) { | |
| 637 | var buf = buffer; | |
| 638 | const use_c = builtin.os.tag != .linux or | |
| 639 | std.c.versionCheck(std.SemanticVersion{ .major = 2, .minor = 25, .patch = 0 }); | |
| 132 | const target = posix.readlinkZ(proc_path, out_buffer) catch |err| { | |
| 133 | switch (err) { | |
| 134 | error.NotLink => unreachable, | |
| 135 | error.BadPathName => unreachable, | |
| 136 | error.InvalidUtf8 => unreachable, // WASI-only | |
| 137 | error.InvalidWtf8 => unreachable, // Windows-only | |
| 138 | error.UnsupportedReparsePointType => unreachable, // Windows-only | |
| 139 | error.NetworkNotFound => unreachable, // Windows-only | |
| 140 | else => |e| return e, | |
| 141 | } | |
| 142 | }; | |
| 143 | return target; | |
| 144 | }, | |
| 145 | .solaris, .illumos => { | |
| 146 | var procfs_buf: ["/proc/self/path/-2147483648\x00".len]u8 = undefined; | |
| 147 | const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/path/{d}", .{fd}) catch unreachable; | |
| 640 | 148 | |
| 641 | while (buf.len != 0) { | |
| 642 | const num_read: usize, const err = if (use_c) res: { | |
| 643 | const rc = std.c.getrandom(buf.ptr, buf.len, 0); | |
| 644 | break :res .{ | |
| 645 | @bitCast(rc), | |
| 646 | std.c.getErrno(rc), | |
| 149 | const target = posix.readlinkZ(proc_path, out_buffer) catch |err| switch (err) { | |
| 150 | error.UnsupportedReparsePointType => unreachable, | |
| 151 | error.NotLink => unreachable, | |
| 152 | else => |e| return e, | |
| 153 | }; | |
| 154 | return target; | |
| 155 | }, | |
| 156 | .freebsd => { | |
| 157 | if (comptime builtin.os.isAtLeast(.freebsd, .{ .major = 13, .minor = 0, .patch = 0 }) orelse false) { | |
| 158 | var kfile: std.c.kinfo_file = undefined; | |
| 159 | kfile.structsize = std.c.KINFO_FILE_SIZE; | |
| 160 | switch (posix.errno(std.c.fcntl(fd, std.c.F.KINFO, @intFromPtr(&kfile)))) { | |
| 161 | .SUCCESS => {}, | |
| 162 | .BADF => return error.FileNotFound, | |
| 163 | else => |err| return posix.unexpectedErrno(err), | |
| 164 | } | |
| 165 | const len = mem.indexOfScalar(u8, &kfile.path, 0) orelse MAX_PATH_BYTES; | |
| 166 | if (len == 0) return error.NameTooLong; | |
| 167 | const result = out_buffer[0..len]; | |
| 168 | @memcpy(result, kfile.path[0..len]); | |
| 169 | return result; | |
| 170 | } else { | |
| 171 | // This fallback implementation reimplements libutil's `kinfo_getfile()`. | |
| 172 | // The motivation is to avoid linking -lutil when building zig or general | |
| 173 | // user executables. | |
| 174 | var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC, posix.KERN.PROC_FILEDESC, std.c.getpid() }; | |
| 175 | var len: usize = undefined; | |
| 176 | posix.sysctl(&mib, null, &len, null, 0) catch |err| switch (err) { | |
| 177 | error.PermissionDenied => unreachable, | |
| 178 | error.SystemResources => return error.SystemResources, | |
| 179 | error.NameTooLong => unreachable, | |
| 180 | error.UnknownName => unreachable, | |
| 181 | else => return error.Unexpected, | |
| 647 | 182 | }; |
| 648 | } else res: { | |
| 649 | const rc = linux.getrandom(buf.ptr, buf.len, 0); | |
| 650 | break :res .{ | |
| 651 | rc, | |
| 652 | linux.getErrno(rc), | |
| 183 | len = len * 4 / 3; | |
| 184 | const buf = std.heap.c_allocator.alloc(u8, len) catch return error.SystemResources; | |
| 185 | defer std.heap.c_allocator.free(buf); | |
| 186 | len = buf.len; | |
| 187 | posix.sysctl(&mib, &buf[0], &len, null, 0) catch |err| switch (err) { | |
| 188 | error.PermissionDenied => unreachable, | |
| 189 | error.SystemResources => return error.SystemResources, | |
| 190 | error.NameTooLong => unreachable, | |
| 191 | error.UnknownName => unreachable, | |
| 192 | else => return error.Unexpected, | |
| 653 | 193 | }; |
| 654 | }; | |
| 655 | ||
| 656 | switch (err) { | |
| 657 | .SUCCESS => buf = buf[num_read..], | |
| 658 | .INVAL => unreachable, | |
| 659 | .FAULT => unreachable, | |
| 660 | .INTR => continue, | |
| 661 | .NOSYS => return getRandomBytesDevURandom(buf), | |
| 662 | else => return unexpectedErrno(err), | |
| 194 | var i: usize = 0; | |
| 195 | while (i < len) { | |
| 196 | const kf: *align(1) std.c.kinfo_file = @ptrCast(&buf[i]); | |
| 197 | if (kf.fd == fd) { | |
| 198 | len = mem.indexOfScalar(u8, &kf.path, 0) orelse MAX_PATH_BYTES; | |
| 199 | if (len == 0) return error.NameTooLong; | |
| 200 | const result = out_buffer[0..len]; | |
| 201 | @memcpy(result, kf.path[0..len]); | |
| 202 | return result; | |
| 203 | } | |
| 204 | i += @intCast(kf.structsize); | |
| 205 | } | |
| 206 | return error.FileNotFound; | |
| 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)) { | |
| 680 | .SUCCESS => return, | |
| 681 | else => |err| return unexpectedErrno(err), | |
| 209 | .dragonfly => { | |
| 210 | @memset(out_buffer[0..MAX_PATH_BYTES], 0); | |
| 211 | switch (posix.errno(std.c.fcntl(fd, posix.F.GETPATH, out_buffer))) { | |
| 212 | .SUCCESS => {}, | |
| 213 | .BADF => return error.FileNotFound, | |
| 214 | .RANGE => return error.NameTooLong, | |
| 215 | else => |err| return posix.unexpectedErrno(err), | |
| 216 | } | |
| 217 | const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse MAX_PATH_BYTES; | |
| 218 | return out_buffer[0..len]; | |
| 219 | }, | |
| 220 | .netbsd => { | |
| 221 | @memset(out_buffer[0..MAX_PATH_BYTES], 0); | |
| 222 | switch (posix.errno(std.c.fcntl(fd, posix.F.GETPATH, out_buffer))) { | |
| 223 | .SUCCESS => {}, | |
| 224 | .ACCES => return error.AccessDenied, | |
| 225 | .BADF => return error.FileNotFound, | |
| 226 | .NOENT => return error.FileNotFound, | |
| 227 | .NOMEM => return error.SystemResources, | |
| 228 | .RANGE => return error.NameTooLong, | |
| 229 | else => |err| return posix.unexpectedErrno(err), | |
| 230 | } | |
| 231 | const len = mem.indexOfScalar(u8, out_buffer[0..], 0) orelse MAX_PATH_BYTES; | |
| 232 | return out_buffer[0..len]; | |
| 682 | 233 | }, |
| 683 | else => return getRandomBytesDevURandom(buffer), | |
| 234 | else => unreachable, // made unreachable by isGetFdPathSupportedOnTarget above | |
| 684 | 235 | } |
| 685 | 236 | } |
| 686 | 237 | |
| 687 | fn getRandomBytesDevURandom(buf: []u8) !void { | |
| 688 | const fd = try openZ("/dev/urandom", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | |
| 689 | defer close(fd); | |
| 690 | ||
| 691 | const st = try fstat(fd); | |
| 692 | if (!S.ISCHR(st.mode)) { | |
| 693 | return error.NoDevice; | |
| 238 | /// WASI-only. Same as `fstatat` but targeting WASI. | |
| 239 | /// `pathname` should be encoded as valid UTF-8. | |
| 240 | /// See also `fstatat`. | |
| 241 | pub fn fstatat_wasi(dirfd: posix.fd_t, pathname: []const u8, flags: wasi.lookupflags_t) posix.FStatAtError!wasi.filestat_t { | |
| 242 | var stat: wasi.filestat_t = undefined; | |
| 243 | switch (wasi.path_filestat_get(dirfd, flags, pathname.ptr, pathname.len, &stat)) { | |
| 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 | } |
| 700 | 258 | |
| 701 | /// Causes abnormal process termination. | |
| 702 | /// If linking against libc, this calls the abort() libc function. Otherwise | |
| 703 | /// it raises SIGABRT followed by SIGKILL and finally lo | |
| 704 | /// Invokes the current signal handler for SIGABRT, if any. | |
| 705 | pub fn abort() noreturn { | |
| 706 | @setCold(true); | |
| 707 | // MSVCRT abort() sometimes opens a popup window which is undesirable, so | |
| 708 | // even when linking libc on Windows we use our own abort implementation. | |
| 709 | // See https://github.com/ziglang/zig/issues/2071 for more details. | |
| 710 | if (builtin.os.tag == .windows) { | |
| 711 | if (builtin.mode == .Debug) { | |
| 712 | @breakpoint(); | |
| 713 | } | |
| 714 | windows.kernel32.ExitProcess(3); | |
| 715 | } | |
| 716 | if (!builtin.link_libc and builtin.os.tag == .linux) { | |
| 717 | // The Linux man page says that the libc abort() function | |
| 718 | // "first unblocks the SIGABRT signal", but this is a footgun | |
| 719 | // for user-defined signal handlers that want to restore some state in | |
| 720 | // some program sections and crash in others. | |
| 721 | // So, the user-installed SIGABRT handler is run, if present. | |
| 722 | raise(SIG.ABRT) catch {}; | |
| 723 | ||
| 724 | // Disable all signal handlers. | |
| 725 | sigprocmask(SIG.BLOCK, &linux.all_mask, null); | |
| 726 | ||
| 727 | // Only one thread may proceed to the rest of abort(). | |
| 728 | if (!builtin.single_threaded) { | |
| 729 | const global = struct { | |
| 730 | var abort_entered: bool = false; | |
| 731 | }; | |
| 732 | while (@cmpxchgWeak(bool, &global.abort_entered, false, true, .seq_cst, .seq_cst)) |_| {} | |
| 733 | } | |
| 734 | ||
| 735 | // Install default handler so that the tkill below will terminate. | |
| 736 | const sigact = Sigaction{ | |
| 737 | .handler = .{ .handler = SIG.DFL }, | |
| 738 | .mask = empty_sigset, | |
| 739 | .flags = 0, | |
| 740 | }; | |
| 741 | sigaction(SIG.ABRT, &sigact, null) catch |err| switch (err) { | |
| 742 | error.OperationNotSupported => unreachable, | |
| 743 | }; | |
| 744 | ||
| 745 | _ = linux.tkill(linux.gettid(), SIG.ABRT); | |
| 746 | ||
| 747 | const sigabrtmask: linux.sigset_t = [_]u32{0} ** 31 ++ [_]u32{1 << (SIG.ABRT - 1)}; | |
| 748 | sigprocmask(SIG.UNBLOCK, &sigabrtmask, null); | |
| 749 | ||
| 750 | // Beyond this point should be unreachable. | |
| 751 | @as(*allowzero volatile u8, @ptrFromInt(0)).* = 0; | |
| 752 | raise(SIG.KILL) catch {}; | |
| 753 | exit(127); // Pid 1 might not be signalled in some containers. | |
| 754 | } | |
| 755 | switch (builtin.os.tag) { | |
| 756 | .uefi, .wasi, .emscripten, .cuda, .amdhsa => @trap(), | |
| 757 | else => system.abort(), | |
| 259 | pub fn fstat_wasi(fd: posix.fd_t) posix.FStatError!wasi.filestat_t { | |
| 260 | var stat: wasi.filestat_t = undefined; | |
| 261 | switch (wasi.fd_filestat_get(fd, &stat)) { | |
| 262 | .SUCCESS => return stat, | |
| 263 | .INVAL => unreachable, | |
| 264 | .BADF => unreachable, // Always a race condition. | |
| 265 | .NOMEM => return error.SystemResources, | |
| 266 | .ACCES => return error.AccessDenied, | |
| 267 | .NOTCAPABLE => return error.AccessDenied, | |
| 268 | else => |err| return posix.unexpectedErrno(err), | |
| 758 | 269 | } |
| 759 | 270 | } |
| 760 | ||
| 761 | pub const RaiseError = UnexpectedError; | |
| 762 | ||
| 763 | pub 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 | ||
| 791 | pub const KillError = error{ ProcessNotFound, PermissionDenied } || UnexpectedError; | |
| 792 | ||
| 793 | pub 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. | |
| 804 | pub 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 | ||
| 829 | pub 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)`. | |
| 859 | pub 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. | |
| 930 | pub 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 | ||
| 979 | pub 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)`. | |
| 995 | pub 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 | ||
| 1061 | pub 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 | ||
| 1071 | pub 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. | |
| 1137 | pub 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 | ||
| 1198 | pub 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)`. | |
| 1248 | pub 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. | |
| 1328 | pub 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 | ||
| 1379 | pub 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)`. | |
| 1403 | pub 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. | |
| 1491 | pub 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 | ||
| 1554 | pub 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`. | |
| 1625 | pub 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`. | |
| 1640 | pub 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`. | |
| 1683 | pub 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 | ||
| 1712 | pub 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` | |
| 1744 | const 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. | |
| 1753 | fn 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. | |
| 1789 | pub 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`. | |
| 1836 | pub 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 | ||
| 1877 | pub 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 | ||
| 1887 | pub 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 | ||
| 1900 | pub 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. | |
| 1915 | pub 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 | ||
| 1953 | pub 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. | |
| 1961 | pub 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`. | |
| 2013 | pub 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`. | |
| 2023 | pub 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`. | |
| 2059 | pub 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. | |
| 2073 | pub 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 | ||
| 2107 | pub const GetCwdError = error{ | |
| 2108 | NameTooLong, | |
| 2109 | CurrentWorkingDirectoryUnlinked, | |
| 2110 | } || UnexpectedError; | |
| 2111 | ||
| 2112 | /// The result is a slice of out_buffer, indexed from 0. | |
| 2113 | pub 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 | ||
| 2140 | pub 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. | |
| 2173 | pub 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`. | |
| 2186 | pub 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`. | |
| 2225 | pub 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`. | |
| 2238 | pub 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`. | |
| 2264 | pub 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 | ||
| 2294 | pub 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. | |
| 2314 | pub 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. | |
| 2345 | pub 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 | ||
| 2357 | pub 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. | |
| 2361 | pub 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. | |
| 2399 | pub 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 | ||
| 2447 | pub 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`. | |
| 2482 | pub 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. | |
| 2498 | pub 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. | |
| 2529 | pub 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 | ||
| 2536 | pub 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. | |
| 2546 | pub 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`. | |
| 2560 | pub 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. | |
| 2592 | pub 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. | |
| 2628 | pub 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 | ||
| 2633 | pub 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. | |
| 2676 | pub 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. | |
| 2691 | pub 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. | |
| 2729 | pub 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. | |
| 2738 | pub 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`. | |
| 2761 | pub 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. | |
| 2789 | pub 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. | |
| 2833 | pub 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. | |
| 2941 | pub 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 | ||
| 2953 | pub 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. | |
| 2978 | pub 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. | |
| 3012 | pub 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 | ||
| 3029 | pub 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. | |
| 3059 | pub 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. | |
| 3075 | pub 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. | |
| 3106 | pub 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 | ||
| 3123 | pub 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. | |
| 3147 | pub 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. | |
| 3167 | pub 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. | |
| 3198 | pub 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 | ||
| 3205 | pub 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. | |
| 3225 | pub 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. | |
| 3243 | pub 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. | |
| 3271 | pub 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 | ||
| 3278 | pub const FchdirError = error{ | |
| 3279 | AccessDenied, | |
| 3280 | NotDir, | |
| 3281 | FileSystem, | |
| 3282 | } || UnexpectedError; | |
| 3283 | ||
| 3284 | pub 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 | ||
| 3299 | pub 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. | |
| 3331 | pub 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`. | |
| 3346 | pub 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. | |
| 3351 | pub 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`. | |
| 3387 | pub 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`. | |
| 3401 | pub 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`. | |
| 3423 | pub 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`. | |
| 3429 | pub 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 | ||
| 3456 | pub const SetEidError = error{ | |
| 3457 | InvalidUserId, | |
| 3458 | PermissionDenied, | |
| 3459 | } || UnexpectedError; | |
| 3460 | ||
| 3461 | pub const SetIdError = error{ResourceLimitReached} || SetEidError; | |
| 3462 | ||
| 3463 | pub 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 | ||
| 3473 | pub 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 | ||
| 3482 | pub 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 | ||
| 3492 | pub 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 | ||
| 3502 | pub 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 | ||
| 3511 | pub 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. | |
| 3522 | pub 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 | ||
| 3562 | pub 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 | ||
| 3610 | pub 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 | ||
| 3638 | pub 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 | ||
| 3696 | pub 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 | ||
| 3711 | pub const ShutdownHow = enum { recv, send, both }; | |
| 3712 | ||
| 3713 | /// Shutdown socket send/receive operations | |
| 3714 | pub 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 | ||
| 3750 | pub 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 | |
| 3796 | pub 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 | ||
| 3839 | pub 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 | ||
| 3867 | pub 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 | ||
| 3898 | pub 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. | |
| 3940 | pub 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 | ||
| 4024 | pub 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 | ||
| 4038 | pub 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 | ||
| 4051 | pub 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 | ||
| 4077 | pub 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. | |
| 4097 | pub 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 | ||
| 4112 | pub const EventFdError = error{ | |
| 4113 | SystemResources, | |
| 4114 | ProcessFdQuotaExceeded, | |
| 4115 | SystemFdQuotaExceeded, | |
| 4116 | } || UnexpectedError; | |
| 4117 | ||
| 4118 | pub 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 | ||
| 4132 | pub 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 | ||
| 4145 | pub 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 | ||
| 4174 | pub 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 | ||
| 4203 | pub 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. | |
| 4253 | pub 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 | ||
| 4307 | pub 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 | ||
| 4343 | pub 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. | |
| 4350 | pub 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 | ||
| 4367 | pub 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 | ||
| 4384 | pub 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. | |
| 4393 | pub 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 | ||
| 4413 | pub 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 | ||
| 4426 | pub 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`. | |
| 4439 | pub 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`. | |
| 4456 | pub 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`. | |
| 4476 | pub 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 | ||
| 4506 | pub 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 | ||
| 4514 | pub 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 | ||
| 4524 | pub 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 | ||
| 4542 | pub 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 | ||
| 4572 | pub const INotifyInitError = error{ | |
| 4573 | ProcessFdQuotaExceeded, | |
| 4574 | SystemFdQuotaExceeded, | |
| 4575 | SystemResources, | |
| 4576 | } || UnexpectedError; | |
| 4577 | ||
| 4578 | /// initialize an inotify instance | |
| 4579 | pub 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 | ||
| 4591 | pub 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 | |
| 4602 | pub 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. | |
| 4608 | pub 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 | |
| 4627 | pub 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 | ||
| 4636 | pub const FanotifyInitError = error{ | |
| 4637 | ProcessFdQuotaExceeded, | |
| 4638 | SystemFdQuotaExceeded, | |
| 4639 | SystemResources, | |
| 4640 | OperationNotSupported, | |
| 4641 | PermissionDenied, | |
| 4642 | } || UnexpectedError; | |
| 4643 | ||
| 4644 | pub 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 | ||
| 4658 | pub 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 | ||
| 4673 | pub 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 | ||
| 4682 | pub 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 | ||
| 4703 | pub 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. | |
| 4718 | pub 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 | ||
| 4747 | pub const ForkError = error{SystemResources} || UnexpectedError; | |
| 4748 | ||
| 4749 | pub 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 | ||
| 4759 | pub 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 | |
| 4783 | pub 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. | |
| 4824 | pub 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 | ||
| 4833 | pub const MSyncError = error{ | |
| 4834 | UnmappedMemory, | |
| 4835 | } || UnexpectedError; | |
| 4836 | ||
| 4837 | pub 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 | ||
| 4846 | pub 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. | |
| 4868 | pub 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. | |
| 4884 | pub 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`. | |
| 4919 | pub 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. | |
| 4938 | pub 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. | |
| 4987 | pub 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` | |
| 5018 | pub 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 | ||
| 5055 | pub const PipeError = error{ | |
| 5056 | SystemFdQuotaExceeded, | |
| 5057 | ProcessFdQuotaExceeded, | |
| 5058 | } || UnexpectedError; | |
| 5059 | ||
| 5060 | /// Creates a unidirectional data channel that can be used for interprocess communication. | |
| 5061 | pub 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 | ||
| 5073 | pub 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 | ||
| 5129 | pub const SysCtlError = error{ | |
| 5130 | PermissionDenied, | |
| 5131 | SystemResources, | |
| 5132 | NameTooLong, | |
| 5133 | UnknownName, | |
| 5134 | } || UnexpectedError; | |
| 5135 | ||
| 5136 | pub 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 | ||
| 5161 | pub 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 | ||
| 5185 | pub 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 | ||
| 5193 | pub 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. | |
| 5202 | pub 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. | |
| 5245 | pub 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. | |
| 5287 | pub 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. | |
| 5329 | pub 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 | ||
| 5371 | pub const FcntlError = error{ | |
| 5372 | PermissionDenied, | |
| 5373 | FileBusy, | |
| 5374 | ProcessFdQuotaExceeded, | |
| 5375 | Locked, | |
| 5376 | DeadLock, | |
| 5377 | LockedRegionLimitExceeded, | |
| 5378 | } || UnexpectedError; | |
| 5379 | ||
| 5380 | pub 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 | ||
| 5400 | fn 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 | ||
| 5458 | pub 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. | |
| 5470 | pub 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 | ||
| 5486 | pub 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. | |
| 5539 | pub 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. | |
| 5552 | pub 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. | |
| 5602 | pub 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 | ||
| 5627 | pub 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. | |
| 5653 | pub 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. | |
| 5791 | pub 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 | ||
| 5815 | pub 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 | ||
| 5899 | pub 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 | |
| 5903 | pub 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 | ||
| 5944 | pub 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 | ||
| 5966 | pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError; | |
| 5967 | ||
| 5968 | pub 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 | |
| 5982 | pub 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. | |
| 5996 | pub const unexpected_error_tracing = builtin.zig_backend == .stage2_llvm and builtin.mode == .Debug; | |
| 5997 | ||
| 5998 | pub 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. | |
| 6007 | pub 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 | ||
| 6015 | pub 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 | ||
| 6023 | pub 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. | |
| 6035 | pub 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. | |
| 6044 | pub 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 | ||
| 6053 | pub 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 | ||
| 6077 | pub 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 | ||
| 6111 | pub const GetHostNameError = error{PermissionDenied} || UnexpectedError; | |
| 6112 | ||
| 6113 | pub 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 | ||
| 6134 | pub 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 | ||
| 6143 | pub 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 | ||
| 6193 | pub 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 | ||
| 6240 | pub 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 | ||
| 6259 | pub 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 | ||
| 6330 | pub 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. | |
| 6360 | pub 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. | |
| 6450 | pub 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 | ||
| 6470 | pub const SendFileError = PReadError || WriteError || SendError; | |
| 6471 | ||
| 6472 | fn 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)`. | |
| 6514 | pub 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 | ||
| 6765 | pub 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 | ||
| 6780 | var 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)`. | |
| 6808 | pub 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 | ||
| 6862 | pub 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 | ||
| 6870 | pub 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 | ||
| 6900 | pub 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 | ||
| 6908 | pub 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 | ||
| 6927 | pub 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 | ||
| 6955 | pub 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. | |
| 6961 | pub 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 | ||
| 7007 | pub const DnExpandError = error{InvalidDnsPacket}; | |
| 7008 | ||
| 7009 | pub 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 | ||
| 7057 | pub 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. | |
| 7080 | pub 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 | ||
| 7113 | pub 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 | ||
| 7123 | pub 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 | ||
| 7160 | pub const MFD_NAME_PREFIX = "memfd:"; | |
| 7161 | pub const MFD_MAX_NAME_LEN = NAME_MAX - MFD_NAME_PREFIX.len; | |
| 7162 | fn 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 | ||
| 7171 | pub 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 | ||
| 7176 | pub 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 | ||
| 7187 | pub const TIOCError = error{NotATerminal}; | |
| 7188 | ||
| 7189 | pub const TermiosGetError = TIOCError || UnexpectedError; | |
| 7190 | ||
| 7191 | pub 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 | ||
| 7204 | pub const TermiosSetError = TermiosGetError || error{ProcessOrphaned}; | |
| 7205 | ||
| 7206 | pub 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 | ||
| 7220 | pub const TermioGetPgrpError = TIOCError || UnexpectedError; | |
| 7221 | ||
| 7222 | /// Returns the process group ID for the TTY associated with the given handle. | |
| 7223 | pub 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 | ||
| 7237 | pub 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. | |
| 7243 | pub 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 | ||
| 7257 | pub const IoCtl_SIOCGIFINDEX_Error = error{ | |
| 7258 | FileSystem, | |
| 7259 | InterfaceNotFound, | |
| 7260 | } || UnexpectedError; | |
| 7261 | ||
| 7262 | pub 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 | ||
| 7279 | pub 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 | ||
| 7293 | pub 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. | |
| 7301 | pub 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. | |
| 7306 | pub 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. | |
| 7319 | pub 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. | |
| 7343 | pub 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 | ||
| 7361 | pub 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 | ||
| 7376 | pub 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 | ||
| 7403 | pub const GetrlimitError = UnexpectedError; | |
| 7404 | ||
| 7405 | pub 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 | ||
| 7417 | pub const SetrlimitError = error{ PermissionDenied, LimitTooBig } || UnexpectedError; | |
| 7418 | ||
| 7419 | pub 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 | ||
| 7431 | pub 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. | |
| 7448 | pub 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 | ||
| 7460 | pub 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. | |
| 7494 | pub 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 | ||
| 7508 | pub 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 | ||
| 7562 | pub 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 | ||
| 7592 | pub const TimerFdCreateError = error{ | |
| 7593 | AccessDenied, | |
| 7594 | ProcessFdQuotaExceeded, | |
| 7595 | SystemFdQuotaExceeded, | |
| 7596 | NoDevice, | |
| 7597 | SystemResources, | |
| 7598 | } || UnexpectedError; | |
| 7599 | ||
| 7600 | pub const TimerFdGetError = error{InvalidHandle} || UnexpectedError; | |
| 7601 | pub const TimerFdSetError = TimerFdGetError || error{Canceled}; | |
| 7602 | ||
| 7603 | pub 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 | ||
| 7617 | pub 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 | ||
| 7634 | pub 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 | ||
| 7646 | pub const PtraceError = error{ | |
| 7647 | DeviceBusy, | |
| 7648 | InputOutput, | |
| 7649 | ProcessNotFound, | |
| 7650 | PermissionDenied, | |
| 7651 | } || UnexpectedError; | |
| 7652 | ||
| 7653 | pub 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 | ||
| 7694 | const 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 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const wasi = std.os.wasi; |
| 4 | const iovec = std.os.iovec; | |
| 5 | const iovec_const = std.os.iovec_const; | |
| 4 | const iovec = std.posix.iovec; | |
| 5 | const iovec_const = std.posix.iovec_const; | |
| 6 | 6 | const c = std.c; |
| 7 | 7 | |
| 8 | 8 | pub const FILE = c.FILE; |
lib/std/os/linux.zig+610-13| ... | ... | @@ -18,9 +18,9 @@ const is_mips = native_arch.isMIPS(); |
| 18 | 18 | const is_ppc = native_arch.isPPC(); |
| 19 | 19 | const is_ppc64 = native_arch.isPPC64(); |
| 20 | 20 | const is_sparc = native_arch.isSPARC(); |
| 21 | const iovec = std.os.iovec; | |
| 22 | const iovec_const = std.os.iovec_const; | |
| 23 | const ACCMODE = std.os.ACCMODE; | |
| 21 | const iovec = std.posix.iovec; | |
| 22 | const iovec_const = std.posix.iovec_const; | |
| 23 | const ACCMODE = std.posix.ACCMODE; | |
| 24 | 24 | |
| 25 | 25 | test { |
| 26 | 26 | if (builtin.os.tag == .linux) { |
| ... | ... | @@ -451,10 +451,11 @@ fn splitValue64(val: i64) [2]u32 { |
| 451 | 451 | } |
| 452 | 452 | |
| 453 | 453 | /// Get the errno from a syscall return value, or 0 for no error. |
| 454 | pub fn getErrno(r: usize) E { | |
| 455 | const signed_r = @as(isize, @bitCast(r)); | |
| 454 | /// The public API is exposed via the `E` namespace. | |
| 455 | fn errnoFromSyscall(r: usize) E { | |
| 456 | const signed_r: isize = @bitCast(r); | |
| 456 | 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 | } |
| 459 | 460 | |
| 460 | 461 | pub fn dup(old: i32) usize { |
| ... | ... | @@ -1561,7 +1562,7 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact |
| 1561 | 1562 | .sparc, .sparc64 => syscall5(.rt_sigaction, sig, ksa_arg, oldksa_arg, @intFromPtr(ksa.restorer), mask_size), |
| 1562 | 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; | |
| 1565 | 1566 | |
| 1566 | 1567 | if (oact) |old| { |
| 1567 | 1568 | old.handler.handler = oldksa.handler; |
| ... | ... | @@ -1648,12 +1649,12 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize |
| 1648 | 1649 | if (next_unsent < i) { |
| 1649 | 1650 | const batch_size = i - next_unsent; |
| 1650 | 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 | 1653 | if (r < batch_size) return next_unsent + r; |
| 1653 | 1654 | } |
| 1654 | 1655 | // send current message as own packet |
| 1655 | 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 | 1658 | // Linux limits the total bytes sent by sendmsg to INT_MAX, so this cast is safe. |
| 1658 | 1659 | msg.msg_len = @as(u32, @intCast(r)); |
| 1659 | 1660 | next_unsent = i + 1; |
| ... | ... | @@ -1665,7 +1666,7 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize |
| 1665 | 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 | 1667 | const batch_size = kvlen - next_unsent; |
| 1667 | 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 | 1670 | return next_unsent + r; |
| 1670 | 1671 | } |
| 1671 | 1672 | return kvlen; |
| ... | ... | @@ -2263,13 +2264,609 @@ pub fn map_shadow_stack(addr: u64, size: u64, flags: u32) usize { |
| 2263 | 2264 | } |
| 2264 | 2265 | |
| 2265 | 2266 | pub const E = switch (native_arch) { |
| 2266 | .mips, .mipsel => @import("linux/errno/mips.zig").E, | |
| 2267 | .sparc, .sparcel, .sparc64 => @import("linux/errno/sparc.zig").E, | |
| 2268 | else => @import("linux/errno/generic.zig").E, | |
| 2267 | .mips, .mipsel => enum(i32) { | |
| 2268 | /// No error occurred. | |
| 2269 | SUCCESS = 0, | |
| 2270 | ||
| 2271 | PERM = 1, | |
| 2272 | NOENT = 2, | |
| 2273 | SRCH = 3, | |
| 2274 | INTR = 4, | |
| 2275 | IO = 5, | |
| 2276 | NXIO = 6, | |
| 2277 | @"2BIG" = 7, | |
| 2278 | NOEXEC = 8, | |
| 2279 | BADF = 9, | |
| 2280 | CHILD = 10, | |
| 2281 | /// Also used for WOULDBLOCK. | |
| 2282 | AGAIN = 11, | |
| 2283 | NOMEM = 12, | |
| 2284 | ACCES = 13, | |
| 2285 | FAULT = 14, | |
| 2286 | NOTBLK = 15, | |
| 2287 | BUSY = 16, | |
| 2288 | EXIST = 17, | |
| 2289 | XDEV = 18, | |
| 2290 | NODEV = 19, | |
| 2291 | NOTDIR = 20, | |
| 2292 | ISDIR = 21, | |
| 2293 | INVAL = 22, | |
| 2294 | NFILE = 23, | |
| 2295 | MFILE = 24, | |
| 2296 | NOTTY = 25, | |
| 2297 | TXTBSY = 26, | |
| 2298 | FBIG = 27, | |
| 2299 | NOSPC = 28, | |
| 2300 | SPIPE = 29, | |
| 2301 | ROFS = 30, | |
| 2302 | MLINK = 31, | |
| 2303 | PIPE = 32, | |
| 2304 | DOM = 33, | |
| 2305 | RANGE = 34, | |
| 2306 | ||
| 2307 | NOMSG = 35, | |
| 2308 | IDRM = 36, | |
| 2309 | CHRNG = 37, | |
| 2310 | L2NSYNC = 38, | |
| 2311 | L3HLT = 39, | |
| 2312 | L3RST = 40, | |
| 2313 | LNRNG = 41, | |
| 2314 | UNATCH = 42, | |
| 2315 | NOCSI = 43, | |
| 2316 | L2HLT = 44, | |
| 2317 | DEADLK = 45, | |
| 2318 | NOLCK = 46, | |
| 2319 | BADE = 50, | |
| 2320 | BADR = 51, | |
| 2321 | XFULL = 52, | |
| 2322 | NOANO = 53, | |
| 2323 | BADRQC = 54, | |
| 2324 | BADSLT = 55, | |
| 2325 | DEADLOCK = 56, | |
| 2326 | BFONT = 59, | |
| 2327 | NOSTR = 60, | |
| 2328 | NODATA = 61, | |
| 2329 | TIME = 62, | |
| 2330 | NOSR = 63, | |
| 2331 | NONET = 64, | |
| 2332 | NOPKG = 65, | |
| 2333 | REMOTE = 66, | |
| 2334 | NOLINK = 67, | |
| 2335 | ADV = 68, | |
| 2336 | SRMNT = 69, | |
| 2337 | COMM = 70, | |
| 2338 | PROTO = 71, | |
| 2339 | DOTDOT = 73, | |
| 2340 | MULTIHOP = 74, | |
| 2341 | BADMSG = 77, | |
| 2342 | NAMETOOLONG = 78, | |
| 2343 | OVERFLOW = 79, | |
| 2344 | NOTUNIQ = 80, | |
| 2345 | BADFD = 81, | |
| 2346 | REMCHG = 82, | |
| 2347 | LIBACC = 83, | |
| 2348 | LIBBAD = 84, | |
| 2349 | LIBSCN = 85, | |
| 2350 | LIBMAX = 86, | |
| 2351 | LIBEXEC = 87, | |
| 2352 | ILSEQ = 88, | |
| 2353 | NOSYS = 89, | |
| 2354 | LOOP = 90, | |
| 2355 | RESTART = 91, | |
| 2356 | STRPIPE = 92, | |
| 2357 | NOTEMPTY = 93, | |
| 2358 | USERS = 94, | |
| 2359 | NOTSOCK = 95, | |
| 2360 | DESTADDRREQ = 96, | |
| 2361 | MSGSIZE = 97, | |
| 2362 | PROTOTYPE = 98, | |
| 2363 | NOPROTOOPT = 99, | |
| 2364 | PROTONOSUPPORT = 120, | |
| 2365 | SOCKTNOSUPPORT = 121, | |
| 2366 | OPNOTSUPP = 122, | |
| 2367 | PFNOSUPPORT = 123, | |
| 2368 | AFNOSUPPORT = 124, | |
| 2369 | ADDRINUSE = 125, | |
| 2370 | ADDRNOTAVAIL = 126, | |
| 2371 | NETDOWN = 127, | |
| 2372 | NETUNREACH = 128, | |
| 2373 | NETRESET = 129, | |
| 2374 | CONNABORTED = 130, | |
| 2375 | CONNRESET = 131, | |
| 2376 | NOBUFS = 132, | |
| 2377 | ISCONN = 133, | |
| 2378 | NOTCONN = 134, | |
| 2379 | UCLEAN = 135, | |
| 2380 | NOTNAM = 137, | |
| 2381 | NAVAIL = 138, | |
| 2382 | ISNAM = 139, | |
| 2383 | REMOTEIO = 140, | |
| 2384 | SHUTDOWN = 143, | |
| 2385 | TOOMANYREFS = 144, | |
| 2386 | TIMEDOUT = 145, | |
| 2387 | CONNREFUSED = 146, | |
| 2388 | HOSTDOWN = 147, | |
| 2389 | HOSTUNREACH = 148, | |
| 2390 | ALREADY = 149, | |
| 2391 | INPROGRESS = 150, | |
| 2392 | STALE = 151, | |
| 2393 | CANCELED = 158, | |
| 2394 | NOMEDIUM = 159, | |
| 2395 | MEDIUMTYPE = 160, | |
| 2396 | NOKEY = 161, | |
| 2397 | KEYEXPIRED = 162, | |
| 2398 | KEYREVOKED = 163, | |
| 2399 | KEYREJECTED = 164, | |
| 2400 | OWNERDEAD = 165, | |
| 2401 | NOTRECOVERABLE = 166, | |
| 2402 | RFKILL = 167, | |
| 2403 | HWPOISON = 168, | |
| 2404 | DQUOT = 1133, | |
| 2405 | _, | |
| 2406 | ||
| 2407 | pub const init = errnoFromSyscall; | |
| 2408 | }, | |
| 2409 | .sparc, .sparcel, .sparc64 => enum(i32) { | |
| 2410 | /// No error occurred. | |
| 2411 | SUCCESS = 0, | |
| 2412 | ||
| 2413 | PERM = 1, | |
| 2414 | NOENT = 2, | |
| 2415 | SRCH = 3, | |
| 2416 | INTR = 4, | |
| 2417 | IO = 5, | |
| 2418 | NXIO = 6, | |
| 2419 | @"2BIG" = 7, | |
| 2420 | NOEXEC = 8, | |
| 2421 | BADF = 9, | |
| 2422 | CHILD = 10, | |
| 2423 | /// Also used for WOULDBLOCK | |
| 2424 | AGAIN = 11, | |
| 2425 | NOMEM = 12, | |
| 2426 | ACCES = 13, | |
| 2427 | FAULT = 14, | |
| 2428 | NOTBLK = 15, | |
| 2429 | BUSY = 16, | |
| 2430 | EXIST = 17, | |
| 2431 | XDEV = 18, | |
| 2432 | NODEV = 19, | |
| 2433 | NOTDIR = 20, | |
| 2434 | ISDIR = 21, | |
| 2435 | INVAL = 22, | |
| 2436 | NFILE = 23, | |
| 2437 | MFILE = 24, | |
| 2438 | NOTTY = 25, | |
| 2439 | TXTBSY = 26, | |
| 2440 | FBIG = 27, | |
| 2441 | NOSPC = 28, | |
| 2442 | SPIPE = 29, | |
| 2443 | ROFS = 30, | |
| 2444 | MLINK = 31, | |
| 2445 | PIPE = 32, | |
| 2446 | DOM = 33, | |
| 2447 | RANGE = 34, | |
| 2448 | ||
| 2449 | INPROGRESS = 36, | |
| 2450 | ALREADY = 37, | |
| 2451 | NOTSOCK = 38, | |
| 2452 | DESTADDRREQ = 39, | |
| 2453 | MSGSIZE = 40, | |
| 2454 | PROTOTYPE = 41, | |
| 2455 | NOPROTOOPT = 42, | |
| 2456 | PROTONOSUPPORT = 43, | |
| 2457 | SOCKTNOSUPPORT = 44, | |
| 2458 | /// Also used for NOTSUP | |
| 2459 | OPNOTSUPP = 45, | |
| 2460 | PFNOSUPPORT = 46, | |
| 2461 | AFNOSUPPORT = 47, | |
| 2462 | ADDRINUSE = 48, | |
| 2463 | ADDRNOTAVAIL = 49, | |
| 2464 | NETDOWN = 50, | |
| 2465 | NETUNREACH = 51, | |
| 2466 | NETRESET = 52, | |
| 2467 | CONNABORTED = 53, | |
| 2468 | CONNRESET = 54, | |
| 2469 | NOBUFS = 55, | |
| 2470 | ISCONN = 56, | |
| 2471 | NOTCONN = 57, | |
| 2472 | SHUTDOWN = 58, | |
| 2473 | TOOMANYREFS = 59, | |
| 2474 | TIMEDOUT = 60, | |
| 2475 | CONNREFUSED = 61, | |
| 2476 | LOOP = 62, | |
| 2477 | NAMETOOLONG = 63, | |
| 2478 | HOSTDOWN = 64, | |
| 2479 | HOSTUNREACH = 65, | |
| 2480 | NOTEMPTY = 66, | |
| 2481 | PROCLIM = 67, | |
| 2482 | USERS = 68, | |
| 2483 | DQUOT = 69, | |
| 2484 | STALE = 70, | |
| 2485 | REMOTE = 71, | |
| 2486 | NOSTR = 72, | |
| 2487 | TIME = 73, | |
| 2488 | NOSR = 74, | |
| 2489 | NOMSG = 75, | |
| 2490 | BADMSG = 76, | |
| 2491 | IDRM = 77, | |
| 2492 | DEADLK = 78, | |
| 2493 | NOLCK = 79, | |
| 2494 | NONET = 80, | |
| 2495 | RREMOTE = 81, | |
| 2496 | NOLINK = 82, | |
| 2497 | ADV = 83, | |
| 2498 | SRMNT = 84, | |
| 2499 | COMM = 85, | |
| 2500 | PROTO = 86, | |
| 2501 | MULTIHOP = 87, | |
| 2502 | DOTDOT = 88, | |
| 2503 | REMCHG = 89, | |
| 2504 | NOSYS = 90, | |
| 2505 | STRPIPE = 91, | |
| 2506 | OVERFLOW = 92, | |
| 2507 | BADFD = 93, | |
| 2508 | CHRNG = 94, | |
| 2509 | L2NSYNC = 95, | |
| 2510 | L3HLT = 96, | |
| 2511 | L3RST = 97, | |
| 2512 | LNRNG = 98, | |
| 2513 | UNATCH = 99, | |
| 2514 | NOCSI = 100, | |
| 2515 | L2HLT = 101, | |
| 2516 | BADE = 102, | |
| 2517 | BADR = 103, | |
| 2518 | XFULL = 104, | |
| 2519 | NOANO = 105, | |
| 2520 | BADRQC = 106, | |
| 2521 | BADSLT = 107, | |
| 2522 | DEADLOCK = 108, | |
| 2523 | BFONT = 109, | |
| 2524 | LIBEXEC = 110, | |
| 2525 | NODATA = 111, | |
| 2526 | LIBBAD = 112, | |
| 2527 | NOPKG = 113, | |
| 2528 | LIBACC = 114, | |
| 2529 | NOTUNIQ = 115, | |
| 2530 | RESTART = 116, | |
| 2531 | UCLEAN = 117, | |
| 2532 | NOTNAM = 118, | |
| 2533 | NAVAIL = 119, | |
| 2534 | ISNAM = 120, | |
| 2535 | REMOTEIO = 121, | |
| 2536 | ILSEQ = 122, | |
| 2537 | LIBMAX = 123, | |
| 2538 | LIBSCN = 124, | |
| 2539 | NOMEDIUM = 125, | |
| 2540 | MEDIUMTYPE = 126, | |
| 2541 | CANCELED = 127, | |
| 2542 | NOKEY = 128, | |
| 2543 | KEYEXPIRED = 129, | |
| 2544 | KEYREVOKED = 130, | |
| 2545 | KEYREJECTED = 131, | |
| 2546 | OWNERDEAD = 132, | |
| 2547 | NOTRECOVERABLE = 133, | |
| 2548 | RFKILL = 134, | |
| 2549 | HWPOISON = 135, | |
| 2550 | _, | |
| 2551 | ||
| 2552 | pub const init = errnoFromSyscall; | |
| 2553 | }, | |
| 2554 | else => enum(u16) { | |
| 2555 | /// No error occurred. | |
| 2556 | /// Same code used for `NSROK`. | |
| 2557 | SUCCESS = 0, | |
| 2558 | /// Operation not permitted | |
| 2559 | PERM = 1, | |
| 2560 | /// No such file or directory | |
| 2561 | NOENT = 2, | |
| 2562 | /// No such process | |
| 2563 | SRCH = 3, | |
| 2564 | /// Interrupted system call | |
| 2565 | INTR = 4, | |
| 2566 | /// I/O error | |
| 2567 | IO = 5, | |
| 2568 | /// No such device or address | |
| 2569 | NXIO = 6, | |
| 2570 | /// Arg list too long | |
| 2571 | @"2BIG" = 7, | |
| 2572 | /// Exec format error | |
| 2573 | NOEXEC = 8, | |
| 2574 | /// Bad file number | |
| 2575 | BADF = 9, | |
| 2576 | /// No child processes | |
| 2577 | CHILD = 10, | |
| 2578 | /// Try again | |
| 2579 | /// Also means: WOULDBLOCK: operation would block | |
| 2580 | AGAIN = 11, | |
| 2581 | /// Out of memory | |
| 2582 | NOMEM = 12, | |
| 2583 | /// Permission denied | |
| 2584 | ACCES = 13, | |
| 2585 | /// Bad address | |
| 2586 | FAULT = 14, | |
| 2587 | /// Block device required | |
| 2588 | NOTBLK = 15, | |
| 2589 | /// Device or resource busy | |
| 2590 | BUSY = 16, | |
| 2591 | /// File exists | |
| 2592 | EXIST = 17, | |
| 2593 | /// Cross-device link | |
| 2594 | XDEV = 18, | |
| 2595 | /// No such device | |
| 2596 | NODEV = 19, | |
| 2597 | /// Not a directory | |
| 2598 | NOTDIR = 20, | |
| 2599 | /// Is a directory | |
| 2600 | ISDIR = 21, | |
| 2601 | /// Invalid argument | |
| 2602 | INVAL = 22, | |
| 2603 | /// File table overflow | |
| 2604 | NFILE = 23, | |
| 2605 | /// Too many open files | |
| 2606 | MFILE = 24, | |
| 2607 | /// Not a typewriter | |
| 2608 | NOTTY = 25, | |
| 2609 | /// Text file busy | |
| 2610 | TXTBSY = 26, | |
| 2611 | /// File too large | |
| 2612 | FBIG = 27, | |
| 2613 | /// No space left on device | |
| 2614 | NOSPC = 28, | |
| 2615 | /// Illegal seek | |
| 2616 | SPIPE = 29, | |
| 2617 | /// Read-only file system | |
| 2618 | ROFS = 30, | |
| 2619 | /// Too many links | |
| 2620 | MLINK = 31, | |
| 2621 | /// Broken pipe | |
| 2622 | PIPE = 32, | |
| 2623 | /// Math argument out of domain of func | |
| 2624 | DOM = 33, | |
| 2625 | /// Math result not representable | |
| 2626 | RANGE = 34, | |
| 2627 | /// Resource deadlock would occur | |
| 2628 | DEADLK = 35, | |
| 2629 | /// File name too long | |
| 2630 | NAMETOOLONG = 36, | |
| 2631 | /// No record locks available | |
| 2632 | NOLCK = 37, | |
| 2633 | /// Function not implemented | |
| 2634 | NOSYS = 38, | |
| 2635 | /// Directory not empty | |
| 2636 | NOTEMPTY = 39, | |
| 2637 | /// Too many symbolic links encountered | |
| 2638 | LOOP = 40, | |
| 2639 | /// No message of desired type | |
| 2640 | NOMSG = 42, | |
| 2641 | /// Identifier removed | |
| 2642 | IDRM = 43, | |
| 2643 | /// Channel number out of range | |
| 2644 | CHRNG = 44, | |
| 2645 | /// Level 2 not synchronized | |
| 2646 | L2NSYNC = 45, | |
| 2647 | /// Level 3 halted | |
| 2648 | L3HLT = 46, | |
| 2649 | /// Level 3 reset | |
| 2650 | L3RST = 47, | |
| 2651 | /// Link number out of range | |
| 2652 | LNRNG = 48, | |
| 2653 | /// Protocol driver not attached | |
| 2654 | UNATCH = 49, | |
| 2655 | /// No CSI structure available | |
| 2656 | NOCSI = 50, | |
| 2657 | /// Level 2 halted | |
| 2658 | L2HLT = 51, | |
| 2659 | /// Invalid exchange | |
| 2660 | BADE = 52, | |
| 2661 | /// Invalid request descriptor | |
| 2662 | BADR = 53, | |
| 2663 | /// Exchange full | |
| 2664 | XFULL = 54, | |
| 2665 | /// No anode | |
| 2666 | NOANO = 55, | |
| 2667 | /// Invalid request code | |
| 2668 | BADRQC = 56, | |
| 2669 | /// Invalid slot | |
| 2670 | BADSLT = 57, | |
| 2671 | /// Bad font file format | |
| 2672 | BFONT = 59, | |
| 2673 | /// Device not a stream | |
| 2674 | NOSTR = 60, | |
| 2675 | /// No data available | |
| 2676 | NODATA = 61, | |
| 2677 | /// Timer expired | |
| 2678 | TIME = 62, | |
| 2679 | /// Out of streams resources | |
| 2680 | NOSR = 63, | |
| 2681 | /// Machine is not on the network | |
| 2682 | NONET = 64, | |
| 2683 | /// Package not installed | |
| 2684 | NOPKG = 65, | |
| 2685 | /// Object is remote | |
| 2686 | REMOTE = 66, | |
| 2687 | /// Link has been severed | |
| 2688 | NOLINK = 67, | |
| 2689 | /// Advertise error | |
| 2690 | ADV = 68, | |
| 2691 | /// Srmount error | |
| 2692 | SRMNT = 69, | |
| 2693 | /// Communication error on send | |
| 2694 | COMM = 70, | |
| 2695 | /// Protocol error | |
| 2696 | PROTO = 71, | |
| 2697 | /// Multihop attempted | |
| 2698 | MULTIHOP = 72, | |
| 2699 | /// RFS specific error | |
| 2700 | DOTDOT = 73, | |
| 2701 | /// Not a data message | |
| 2702 | BADMSG = 74, | |
| 2703 | /// Value too large for defined data type | |
| 2704 | OVERFLOW = 75, | |
| 2705 | /// Name not unique on network | |
| 2706 | NOTUNIQ = 76, | |
| 2707 | /// File descriptor in bad state | |
| 2708 | BADFD = 77, | |
| 2709 | /// Remote address changed | |
| 2710 | REMCHG = 78, | |
| 2711 | /// Can not access a needed shared library | |
| 2712 | LIBACC = 79, | |
| 2713 | /// Accessing a corrupted shared library | |
| 2714 | LIBBAD = 80, | |
| 2715 | /// .lib section in a.out corrupted | |
| 2716 | LIBSCN = 81, | |
| 2717 | /// Attempting to link in too many shared libraries | |
| 2718 | LIBMAX = 82, | |
| 2719 | /// Cannot exec a shared library directly | |
| 2720 | LIBEXEC = 83, | |
| 2721 | /// Illegal byte sequence | |
| 2722 | ILSEQ = 84, | |
| 2723 | /// Interrupted system call should be restarted | |
| 2724 | RESTART = 85, | |
| 2725 | /// Streams pipe error | |
| 2726 | STRPIPE = 86, | |
| 2727 | /// Too many users | |
| 2728 | USERS = 87, | |
| 2729 | /// Socket operation on non-socket | |
| 2730 | NOTSOCK = 88, | |
| 2731 | /// Destination address required | |
| 2732 | DESTADDRREQ = 89, | |
| 2733 | /// Message too long | |
| 2734 | MSGSIZE = 90, | |
| 2735 | /// Protocol wrong type for socket | |
| 2736 | PROTOTYPE = 91, | |
| 2737 | /// Protocol not available | |
| 2738 | NOPROTOOPT = 92, | |
| 2739 | /// Protocol not supported | |
| 2740 | PROTONOSUPPORT = 93, | |
| 2741 | /// Socket type not supported | |
| 2742 | SOCKTNOSUPPORT = 94, | |
| 2743 | /// Operation not supported on transport endpoint | |
| 2744 | /// This code also means `NOTSUP`. | |
| 2745 | OPNOTSUPP = 95, | |
| 2746 | /// Protocol family not supported | |
| 2747 | PFNOSUPPORT = 96, | |
| 2748 | /// Address family not supported by protocol | |
| 2749 | AFNOSUPPORT = 97, | |
| 2750 | /// Address already in use | |
| 2751 | ADDRINUSE = 98, | |
| 2752 | /// Cannot assign requested address | |
| 2753 | ADDRNOTAVAIL = 99, | |
| 2754 | /// Network is down | |
| 2755 | NETDOWN = 100, | |
| 2756 | /// Network is unreachable | |
| 2757 | NETUNREACH = 101, | |
| 2758 | /// Network dropped connection because of reset | |
| 2759 | NETRESET = 102, | |
| 2760 | /// Software caused connection abort | |
| 2761 | CONNABORTED = 103, | |
| 2762 | /// Connection reset by peer | |
| 2763 | CONNRESET = 104, | |
| 2764 | /// No buffer space available | |
| 2765 | NOBUFS = 105, | |
| 2766 | /// Transport endpoint is already connected | |
| 2767 | ISCONN = 106, | |
| 2768 | /// Transport endpoint is not connected | |
| 2769 | NOTCONN = 107, | |
| 2770 | /// Cannot send after transport endpoint shutdown | |
| 2771 | SHUTDOWN = 108, | |
| 2772 | /// Too many references: cannot splice | |
| 2773 | TOOMANYREFS = 109, | |
| 2774 | /// Connection timed out | |
| 2775 | TIMEDOUT = 110, | |
| 2776 | /// Connection refused | |
| 2777 | CONNREFUSED = 111, | |
| 2778 | /// Host is down | |
| 2779 | HOSTDOWN = 112, | |
| 2780 | /// No route to host | |
| 2781 | HOSTUNREACH = 113, | |
| 2782 | /// Operation already in progress | |
| 2783 | ALREADY = 114, | |
| 2784 | /// Operation now in progress | |
| 2785 | INPROGRESS = 115, | |
| 2786 | /// Stale NFS file handle | |
| 2787 | STALE = 116, | |
| 2788 | /// Structure needs cleaning | |
| 2789 | UCLEAN = 117, | |
| 2790 | /// Not a XENIX named type file | |
| 2791 | NOTNAM = 118, | |
| 2792 | /// No XENIX semaphores available | |
| 2793 | NAVAIL = 119, | |
| 2794 | /// Is a named type file | |
| 2795 | ISNAM = 120, | |
| 2796 | /// Remote I/O error | |
| 2797 | REMOTEIO = 121, | |
| 2798 | /// Quota exceeded | |
| 2799 | DQUOT = 122, | |
| 2800 | /// No medium found | |
| 2801 | NOMEDIUM = 123, | |
| 2802 | /// Wrong medium type | |
| 2803 | MEDIUMTYPE = 124, | |
| 2804 | /// Operation canceled | |
| 2805 | CANCELED = 125, | |
| 2806 | /// Required key not available | |
| 2807 | NOKEY = 126, | |
| 2808 | /// Key has expired | |
| 2809 | KEYEXPIRED = 127, | |
| 2810 | /// Key has been revoked | |
| 2811 | KEYREVOKED = 128, | |
| 2812 | /// Key was rejected by service | |
| 2813 | KEYREJECTED = 129, | |
| 2814 | // for robust mutexes | |
| 2815 | /// Owner died | |
| 2816 | OWNERDEAD = 130, | |
| 2817 | /// State not recoverable | |
| 2818 | NOTRECOVERABLE = 131, | |
| 2819 | /// Operation not possible due to RF-kill | |
| 2820 | RFKILL = 132, | |
| 2821 | /// Memory page has hardware error | |
| 2822 | HWPOISON = 133, | |
| 2823 | // nameserver query return codes | |
| 2824 | /// DNS server returned answer with no data | |
| 2825 | NSRNODATA = 160, | |
| 2826 | /// DNS server claims query was misformatted | |
| 2827 | NSRFORMERR = 161, | |
| 2828 | /// DNS server returned general failure | |
| 2829 | NSRSERVFAIL = 162, | |
| 2830 | /// Domain name not found | |
| 2831 | NSRNOTFOUND = 163, | |
| 2832 | /// DNS server does not implement requested operation | |
| 2833 | NSRNOTIMP = 164, | |
| 2834 | /// DNS server refused query | |
| 2835 | NSRREFUSED = 165, | |
| 2836 | /// Misformatted DNS query | |
| 2837 | NSRBADQUERY = 166, | |
| 2838 | /// Misformatted domain name | |
| 2839 | NSRBADNAME = 167, | |
| 2840 | /// Unsupported address family | |
| 2841 | NSRBADFAMILY = 168, | |
| 2842 | /// Misformatted DNS reply | |
| 2843 | NSRBADRESP = 169, | |
| 2844 | /// Could not contact DNS servers | |
| 2845 | NSRCONNREFUSED = 170, | |
| 2846 | /// Timeout while contacting DNS servers | |
| 2847 | NSRTIMEOUT = 171, | |
| 2848 | /// End of file | |
| 2849 | NSROF = 172, | |
| 2850 | /// Error reading file | |
| 2851 | NSRFILE = 173, | |
| 2852 | /// Out of memory | |
| 2853 | NSRNOMEM = 174, | |
| 2854 | /// Application terminated lookup | |
| 2855 | NSRDESTRUCTION = 175, | |
| 2856 | /// Domain name is too long | |
| 2857 | NSRQUERYDOMAINTOOLONG = 176, | |
| 2858 | /// Domain name is too long | |
| 2859 | NSRCNAMELOOP = 177, | |
| 2860 | ||
| 2861 | _, | |
| 2862 | ||
| 2863 | pub const init = errnoFromSyscall; | |
| 2864 | }, | |
| 2269 | 2865 | }; |
| 2270 | 2866 | |
| 2271 | 2867 | pub const pid_t = i32; |
| 2272 | 2868 | pub const fd_t = i32; |
| 2869 | pub const socket_t = i32; | |
| 2273 | 2870 | pub const uid_t = u32; |
| 2274 | 2871 | pub const gid_t = u32; |
| 2275 | 2872 | pub const clock_t = isize; |
lib/std/os/linux/IoUring.zig+251-251| ... | ... | @@ -4,12 +4,12 @@ const builtin = @import("builtin"); |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | 5 | const mem = std.mem; |
| 6 | 6 | const net = std.net; |
| 7 | const os = std.os; | |
| 8 | 7 | const posix = std.posix; |
| 9 | const linux = os.linux; | |
| 8 | const linux = std.os.linux; | |
| 10 | 9 | const testing = std.testing; |
| 10 | const is_linux = builtin.os.tag == .linux; | |
| 11 | 11 | |
| 12 | fd: os.fd_t = -1, | |
| 12 | fd: posix.fd_t = -1, | |
| 13 | 13 | sq: SubmissionQueue, |
| 14 | 14 | cq: CompletionQueue, |
| 15 | 15 | flags: u32, |
| ... | ... | @@ -45,7 +45,7 @@ pub fn init_params(entries: u16, p: *linux.io_uring_params) !IoUring { |
| 45 | 45 | assert(p.resv[2] == 0); |
| 46 | 46 | |
| 47 | 47 | const res = linux.io_uring_setup(entries, p); |
| 48 | switch (linux.getErrno(res)) { | |
| 48 | switch (linux.E.init(res)) { | |
| 49 | 49 | .SUCCESS => {}, |
| 50 | 50 | .FAULT => return error.ParamsOutsideAccessibleAddressSpace, |
| 51 | 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 | 59 | // or a container seccomp policy prohibits io_uring syscalls: |
| 60 | 60 | .PERM => return error.PermissionDenied, |
| 61 | 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 | 65 | assert(fd >= 0); |
| 66 | errdefer os.close(fd); | |
| 66 | errdefer posix.close(fd); | |
| 67 | 67 | |
| 68 | 68 | // Kernel versions 5.4 and up use only one mmap() for the submission and completion queues. |
| 69 | 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 | 121 | // The mmaps depend on the fd, so the order of these calls is important: |
| 122 | 122 | self.cq.deinit(); |
| 123 | 123 | self.sq.deinit(); |
| 124 | os.close(self.fd); | |
| 124 | posix.close(self.fd); | |
| 125 | 125 | self.fd = -1; |
| 126 | 126 | } |
| 127 | 127 | |
| ... | ... | @@ -174,7 +174,7 @@ pub fn submit_and_wait(self: *IoUring, wait_nr: u32) !u32 { |
| 174 | 174 | pub fn enter(self: *IoUring, to_submit: u32, min_complete: u32, flags: u32) !u32 { |
| 175 | 175 | assert(self.fd >= 0); |
| 176 | 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 | 178 | .SUCCESS => {}, |
| 179 | 179 | // The kernel was unable to allocate memory or ran out of resources for the request. |
| 180 | 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 | 200 | // The operation was interrupted by a delivery of a signal before it could complete. |
| 201 | 201 | // This can happen while waiting for events with IORING_ENTER_GETEVENTS: |
| 202 | 202 | .INTR => return error.SignalInterrupt, |
| 203 | else => |errno| return os.unexpectedErrno(errno), | |
| 203 | else => |errno| return posix.unexpectedErrno(errno), | |
| 204 | 204 | } |
| 205 | 205 | return @as(u32, @intCast(res)); |
| 206 | 206 | } |
| ... | ... | @@ -344,7 +344,7 @@ pub fn cq_advance(self: *IoUring, count: u32) void { |
| 344 | 344 | /// apply to the write, since the fsync may complete before the write is issued to the disk. |
| 345 | 345 | /// You should preferably use `link_with_next_sqe()` on a write's SQE to link it with an fsync, |
| 346 | 346 | /// or else insert a full write barrier using `drain_previous_sqes()` when queueing an fsync. |
| 347 | pub fn fsync(self: *IoUring, user_data: u64, fd: os.fd_t, flags: u32) !*linux.io_uring_sqe { | |
| 347 | pub fn fsync(self: *IoUring, user_data: u64, fd: posix.fd_t, flags: u32) !*linux.io_uring_sqe { | |
| 348 | 348 | const sqe = try self.get_sqe(); |
| 349 | 349 | sqe.prep_fsync(fd, flags); |
| 350 | 350 | sqe.user_data = user_data; |
| ... | ... | @@ -369,7 +369,7 @@ pub const ReadBuffer = union(enum) { |
| 369 | 369 | buffer: []u8, |
| 370 | 370 | |
| 371 | 371 | /// io_uring will read directly into these buffers using readv. |
| 372 | iovecs: []const os.iovec, | |
| 372 | iovecs: []const posix.iovec, | |
| 373 | 373 | |
| 374 | 374 | /// io_uring will select a buffer that has previously been provided with `provide_buffers`. |
| 375 | 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 | 389 | pub fn read( |
| 390 | 390 | self: *IoUring, |
| 391 | 391 | user_data: u64, |
| 392 | fd: os.fd_t, | |
| 392 | fd: posix.fd_t, | |
| 393 | 393 | buffer: ReadBuffer, |
| 394 | 394 | offset: u64, |
| 395 | 395 | ) !*linux.io_uring_sqe { |
| ... | ... | @@ -412,7 +412,7 @@ pub fn read( |
| 412 | 412 | pub fn write( |
| 413 | 413 | self: *IoUring, |
| 414 | 414 | user_data: u64, |
| 415 | fd: os.fd_t, | |
| 415 | fd: posix.fd_t, | |
| 416 | 416 | buffer: []const u8, |
| 417 | 417 | offset: u64, |
| 418 | 418 | ) !*linux.io_uring_sqe { |
| ... | ... | @@ -436,7 +436,7 @@ pub fn write( |
| 436 | 436 | /// See https://github.com/axboe/liburing/issues/291 |
| 437 | 437 | /// |
| 438 | 438 | /// Returns a pointer to the SQE so that you can further modify the SQE for advanced use cases. |
| 439 | pub 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 { | |
| 439 | pub 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 | 440 | const sqe = try self.get_sqe(); |
| 441 | 441 | sqe.prep_splice(fd_in, off_in, fd_out, off_out, len); |
| 442 | 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 | 451 | pub fn read_fixed( |
| 452 | 452 | self: *IoUring, |
| 453 | 453 | user_data: u64, |
| 454 | fd: os.fd_t, | |
| 455 | buffer: *os.iovec, | |
| 454 | fd: posix.fd_t, | |
| 455 | buffer: *posix.iovec, | |
| 456 | 456 | offset: u64, |
| 457 | 457 | buffer_index: u16, |
| 458 | 458 | ) !*linux.io_uring_sqe { |
| ... | ... | @@ -469,8 +469,8 @@ pub fn read_fixed( |
| 469 | 469 | pub fn writev( |
| 470 | 470 | self: *IoUring, |
| 471 | 471 | user_data: u64, |
| 472 | fd: os.fd_t, | |
| 473 | iovecs: []const os.iovec_const, | |
| 472 | fd: posix.fd_t, | |
| 473 | iovecs: []const posix.iovec_const, | |
| 474 | 474 | offset: u64, |
| 475 | 475 | ) !*linux.io_uring_sqe { |
| 476 | 476 | const sqe = try self.get_sqe(); |
| ... | ... | @@ -487,8 +487,8 @@ pub fn writev( |
| 487 | 487 | pub fn write_fixed( |
| 488 | 488 | self: *IoUring, |
| 489 | 489 | user_data: u64, |
| 490 | fd: os.fd_t, | |
| 491 | buffer: *os.iovec, | |
| 490 | fd: posix.fd_t, | |
| 491 | buffer: *posix.iovec, | |
| 492 | 492 | offset: u64, |
| 493 | 493 | buffer_index: u16, |
| 494 | 494 | ) !*linux.io_uring_sqe { |
| ... | ... | @@ -504,9 +504,9 @@ pub fn write_fixed( |
| 504 | 504 | pub fn accept( |
| 505 | 505 | self: *IoUring, |
| 506 | 506 | user_data: u64, |
| 507 | fd: os.fd_t, | |
| 508 | addr: ?*os.sockaddr, | |
| 509 | addrlen: ?*os.socklen_t, | |
| 507 | fd: posix.fd_t, | |
| 508 | addr: ?*posix.sockaddr, | |
| 509 | addrlen: ?*posix.socklen_t, | |
| 510 | 510 | flags: u32, |
| 511 | 511 | ) !*linux.io_uring_sqe { |
| 512 | 512 | const sqe = try self.get_sqe(); |
| ... | ... | @@ -526,9 +526,9 @@ pub fn accept( |
| 526 | 526 | pub fn accept_multishot( |
| 527 | 527 | self: *IoUring, |
| 528 | 528 | user_data: u64, |
| 529 | fd: os.fd_t, | |
| 530 | addr: ?*os.sockaddr, | |
| 531 | addrlen: ?*os.socklen_t, | |
| 529 | fd: posix.fd_t, | |
| 530 | addr: ?*posix.sockaddr, | |
| 531 | addrlen: ?*posix.socklen_t, | |
| 532 | 532 | flags: u32, |
| 533 | 533 | ) !*linux.io_uring_sqe { |
| 534 | 534 | const sqe = try self.get_sqe(); |
| ... | ... | @@ -551,9 +551,9 @@ pub fn accept_multishot( |
| 551 | 551 | pub fn accept_direct( |
| 552 | 552 | self: *IoUring, |
| 553 | 553 | user_data: u64, |
| 554 | fd: os.fd_t, | |
| 555 | addr: ?*os.sockaddr, | |
| 556 | addrlen: ?*os.socklen_t, | |
| 554 | fd: posix.fd_t, | |
| 555 | addr: ?*posix.sockaddr, | |
| 556 | addrlen: ?*posix.socklen_t, | |
| 557 | 557 | flags: u32, |
| 558 | 558 | ) !*linux.io_uring_sqe { |
| 559 | 559 | const sqe = try self.get_sqe(); |
| ... | ... | @@ -567,9 +567,9 @@ pub fn accept_direct( |
| 567 | 567 | pub fn accept_multishot_direct( |
| 568 | 568 | self: *IoUring, |
| 569 | 569 | user_data: u64, |
| 570 | fd: os.fd_t, | |
| 571 | addr: ?*os.sockaddr, | |
| 572 | addrlen: ?*os.socklen_t, | |
| 570 | fd: posix.fd_t, | |
| 571 | addr: ?*posix.sockaddr, | |
| 572 | addrlen: ?*posix.socklen_t, | |
| 573 | 573 | flags: u32, |
| 574 | 574 | ) !*linux.io_uring_sqe { |
| 575 | 575 | const sqe = try self.get_sqe(); |
| ... | ... | @@ -583,9 +583,9 @@ pub fn accept_multishot_direct( |
| 583 | 583 | pub fn connect( |
| 584 | 584 | self: *IoUring, |
| 585 | 585 | user_data: u64, |
| 586 | fd: os.fd_t, | |
| 587 | addr: *const os.sockaddr, | |
| 588 | addrlen: os.socklen_t, | |
| 586 | fd: posix.fd_t, | |
| 587 | addr: *const posix.sockaddr, | |
| 588 | addrlen: posix.socklen_t, | |
| 589 | 589 | ) !*linux.io_uring_sqe { |
| 590 | 590 | const sqe = try self.get_sqe(); |
| 591 | 591 | sqe.prep_connect(fd, addr, addrlen); |
| ... | ... | @@ -598,8 +598,8 @@ pub fn connect( |
| 598 | 598 | pub fn epoll_ctl( |
| 599 | 599 | self: *IoUring, |
| 600 | 600 | user_data: u64, |
| 601 | epfd: os.fd_t, | |
| 602 | fd: os.fd_t, | |
| 601 | epfd: posix.fd_t, | |
| 602 | fd: posix.fd_t, | |
| 603 | 603 | op: u32, |
| 604 | 604 | ev: ?*linux.epoll_event, |
| 605 | 605 | ) !*linux.io_uring_sqe { |
| ... | ... | @@ -629,7 +629,7 @@ pub const RecvBuffer = union(enum) { |
| 629 | 629 | pub fn recv( |
| 630 | 630 | self: *IoUring, |
| 631 | 631 | user_data: u64, |
| 632 | fd: os.fd_t, | |
| 632 | fd: posix.fd_t, | |
| 633 | 633 | buffer: RecvBuffer, |
| 634 | 634 | flags: u32, |
| 635 | 635 | ) !*linux.io_uring_sqe { |
| ... | ... | @@ -653,7 +653,7 @@ pub fn recv( |
| 653 | 653 | pub fn send( |
| 654 | 654 | self: *IoUring, |
| 655 | 655 | user_data: u64, |
| 656 | fd: os.fd_t, | |
| 656 | fd: posix.fd_t, | |
| 657 | 657 | buffer: []const u8, |
| 658 | 658 | flags: u32, |
| 659 | 659 | ) !*linux.io_uring_sqe { |
| ... | ... | @@ -681,7 +681,7 @@ pub fn send( |
| 681 | 681 | pub fn send_zc( |
| 682 | 682 | self: *IoUring, |
| 683 | 683 | user_data: u64, |
| 684 | fd: os.fd_t, | |
| 684 | fd: posix.fd_t, | |
| 685 | 685 | buffer: []const u8, |
| 686 | 686 | send_flags: u32, |
| 687 | 687 | zc_flags: u16, |
| ... | ... | @@ -698,7 +698,7 @@ pub fn send_zc( |
| 698 | 698 | pub fn send_zc_fixed( |
| 699 | 699 | self: *IoUring, |
| 700 | 700 | user_data: u64, |
| 701 | fd: os.fd_t, | |
| 701 | fd: posix.fd_t, | |
| 702 | 702 | buffer: []const u8, |
| 703 | 703 | send_flags: u32, |
| 704 | 704 | zc_flags: u16, |
| ... | ... | @@ -716,8 +716,8 @@ pub fn send_zc_fixed( |
| 716 | 716 | pub fn recvmsg( |
| 717 | 717 | self: *IoUring, |
| 718 | 718 | user_data: u64, |
| 719 | fd: os.fd_t, | |
| 720 | msg: *os.msghdr, | |
| 719 | fd: posix.fd_t, | |
| 720 | msg: *posix.msghdr, | |
| 721 | 721 | flags: u32, |
| 722 | 722 | ) !*linux.io_uring_sqe { |
| 723 | 723 | const sqe = try self.get_sqe(); |
| ... | ... | @@ -732,8 +732,8 @@ pub fn recvmsg( |
| 732 | 732 | pub fn sendmsg( |
| 733 | 733 | self: *IoUring, |
| 734 | 734 | user_data: u64, |
| 735 | fd: os.fd_t, | |
| 736 | msg: *const os.msghdr_const, | |
| 735 | fd: posix.fd_t, | |
| 736 | msg: *const posix.msghdr_const, | |
| 737 | 737 | flags: u32, |
| 738 | 738 | ) !*linux.io_uring_sqe { |
| 739 | 739 | const sqe = try self.get_sqe(); |
| ... | ... | @@ -748,8 +748,8 @@ pub fn sendmsg( |
| 748 | 748 | pub fn sendmsg_zc( |
| 749 | 749 | self: *IoUring, |
| 750 | 750 | user_data: u64, |
| 751 | fd: os.fd_t, | |
| 752 | msg: *const os.msghdr_const, | |
| 751 | fd: posix.fd_t, | |
| 752 | msg: *const posix.msghdr_const, | |
| 753 | 753 | flags: u32, |
| 754 | 754 | ) !*linux.io_uring_sqe { |
| 755 | 755 | const sqe = try self.get_sqe(); |
| ... | ... | @@ -764,10 +764,10 @@ pub fn sendmsg_zc( |
| 764 | 764 | pub fn openat( |
| 765 | 765 | self: *IoUring, |
| 766 | 766 | user_data: u64, |
| 767 | fd: os.fd_t, | |
| 767 | fd: posix.fd_t, | |
| 768 | 768 | path: [*:0]const u8, |
| 769 | 769 | flags: linux.O, |
| 770 | mode: os.mode_t, | |
| 770 | mode: posix.mode_t, | |
| 771 | 771 | ) !*linux.io_uring_sqe { |
| 772 | 772 | const sqe = try self.get_sqe(); |
| 773 | 773 | sqe.prep_openat(fd, path, flags, mode); |
| ... | ... | @@ -789,10 +789,10 @@ pub fn openat( |
| 789 | 789 | pub fn openat_direct( |
| 790 | 790 | self: *IoUring, |
| 791 | 791 | user_data: u64, |
| 792 | fd: os.fd_t, | |
| 792 | fd: posix.fd_t, | |
| 793 | 793 | path: [*:0]const u8, |
| 794 | 794 | flags: linux.O, |
| 795 | mode: os.mode_t, | |
| 795 | mode: posix.mode_t, | |
| 796 | 796 | file_index: u32, |
| 797 | 797 | ) !*linux.io_uring_sqe { |
| 798 | 798 | const sqe = try self.get_sqe(); |
| ... | ... | @@ -804,7 +804,7 @@ pub fn openat_direct( |
| 804 | 804 | /// Queues (but does not submit) an SQE to perform a `close(2)`. |
| 805 | 805 | /// Returns a pointer to the SQE. |
| 806 | 806 | /// Available since 5.6. |
| 807 | pub fn close(self: *IoUring, user_data: u64, fd: os.fd_t) !*linux.io_uring_sqe { | |
| 807 | pub fn close(self: *IoUring, user_data: u64, fd: posix.fd_t) !*linux.io_uring_sqe { | |
| 808 | 808 | const sqe = try self.get_sqe(); |
| 809 | 809 | sqe.prep_close(fd); |
| 810 | 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 | 836 | pub fn timeout( |
| 837 | 837 | self: *IoUring, |
| 838 | 838 | user_data: u64, |
| 839 | ts: *const os.linux.kernel_timespec, | |
| 839 | ts: *const linux.kernel_timespec, | |
| 840 | 840 | count: u32, |
| 841 | 841 | flags: u32, |
| 842 | 842 | ) !*linux.io_uring_sqe { |
| ... | ... | @@ -885,7 +885,7 @@ pub fn timeout_remove( |
| 885 | 885 | pub fn link_timeout( |
| 886 | 886 | self: *IoUring, |
| 887 | 887 | user_data: u64, |
| 888 | ts: *const os.linux.kernel_timespec, | |
| 888 | ts: *const linux.kernel_timespec, | |
| 889 | 889 | flags: u32, |
| 890 | 890 | ) !*linux.io_uring_sqe { |
| 891 | 891 | const sqe = try self.get_sqe(); |
| ... | ... | @@ -899,7 +899,7 @@ pub fn link_timeout( |
| 899 | 899 | pub fn poll_add( |
| 900 | 900 | self: *IoUring, |
| 901 | 901 | user_data: u64, |
| 902 | fd: os.fd_t, | |
| 902 | fd: posix.fd_t, | |
| 903 | 903 | poll_mask: u32, |
| 904 | 904 | ) !*linux.io_uring_sqe { |
| 905 | 905 | const sqe = try self.get_sqe(); |
| ... | ... | @@ -942,7 +942,7 @@ pub fn poll_update( |
| 942 | 942 | pub fn fallocate( |
| 943 | 943 | self: *IoUring, |
| 944 | 944 | user_data: u64, |
| 945 | fd: os.fd_t, | |
| 945 | fd: posix.fd_t, | |
| 946 | 946 | mode: i32, |
| 947 | 947 | offset: u64, |
| 948 | 948 | len: u64, |
| ... | ... | @@ -958,7 +958,7 @@ pub fn fallocate( |
| 958 | 958 | pub fn statx( |
| 959 | 959 | self: *IoUring, |
| 960 | 960 | user_data: u64, |
| 961 | fd: os.fd_t, | |
| 961 | fd: posix.fd_t, | |
| 962 | 962 | path: [:0]const u8, |
| 963 | 963 | flags: u32, |
| 964 | 964 | mask: u32, |
| ... | ... | @@ -997,7 +997,7 @@ pub fn cancel( |
| 997 | 997 | pub fn shutdown( |
| 998 | 998 | self: *IoUring, |
| 999 | 999 | user_data: u64, |
| 1000 | sockfd: os.socket_t, | |
| 1000 | sockfd: posix.socket_t, | |
| 1001 | 1001 | how: u32, |
| 1002 | 1002 | ) !*linux.io_uring_sqe { |
| 1003 | 1003 | const sqe = try self.get_sqe(); |
| ... | ... | @@ -1011,9 +1011,9 @@ pub fn shutdown( |
| 1011 | 1011 | pub fn renameat( |
| 1012 | 1012 | self: *IoUring, |
| 1013 | 1013 | user_data: u64, |
| 1014 | old_dir_fd: os.fd_t, | |
| 1014 | old_dir_fd: posix.fd_t, | |
| 1015 | 1015 | old_path: [*:0]const u8, |
| 1016 | new_dir_fd: os.fd_t, | |
| 1016 | new_dir_fd: posix.fd_t, | |
| 1017 | 1017 | new_path: [*:0]const u8, |
| 1018 | 1018 | flags: u32, |
| 1019 | 1019 | ) !*linux.io_uring_sqe { |
| ... | ... | @@ -1028,7 +1028,7 @@ pub fn renameat( |
| 1028 | 1028 | pub fn unlinkat( |
| 1029 | 1029 | self: *IoUring, |
| 1030 | 1030 | user_data: u64, |
| 1031 | dir_fd: os.fd_t, | |
| 1031 | dir_fd: posix.fd_t, | |
| 1032 | 1032 | path: [*:0]const u8, |
| 1033 | 1033 | flags: u32, |
| 1034 | 1034 | ) !*linux.io_uring_sqe { |
| ... | ... | @@ -1043,9 +1043,9 @@ pub fn unlinkat( |
| 1043 | 1043 | pub fn mkdirat( |
| 1044 | 1044 | self: *IoUring, |
| 1045 | 1045 | user_data: u64, |
| 1046 | dir_fd: os.fd_t, | |
| 1046 | dir_fd: posix.fd_t, | |
| 1047 | 1047 | path: [*:0]const u8, |
| 1048 | mode: os.mode_t, | |
| 1048 | mode: posix.mode_t, | |
| 1049 | 1049 | ) !*linux.io_uring_sqe { |
| 1050 | 1050 | const sqe = try self.get_sqe(); |
| 1051 | 1051 | sqe.prep_mkdirat(dir_fd, path, mode); |
| ... | ... | @@ -1059,7 +1059,7 @@ pub fn symlinkat( |
| 1059 | 1059 | self: *IoUring, |
| 1060 | 1060 | user_data: u64, |
| 1061 | 1061 | target: [*:0]const u8, |
| 1062 | new_dir_fd: os.fd_t, | |
| 1062 | new_dir_fd: posix.fd_t, | |
| 1063 | 1063 | link_path: [*:0]const u8, |
| 1064 | 1064 | ) !*linux.io_uring_sqe { |
| 1065 | 1065 | const sqe = try self.get_sqe(); |
| ... | ... | @@ -1073,9 +1073,9 @@ pub fn symlinkat( |
| 1073 | 1073 | pub fn linkat( |
| 1074 | 1074 | self: *IoUring, |
| 1075 | 1075 | user_data: u64, |
| 1076 | old_dir_fd: os.fd_t, | |
| 1076 | old_dir_fd: posix.fd_t, | |
| 1077 | 1077 | old_path: [*:0]const u8, |
| 1078 | new_dir_fd: os.fd_t, | |
| 1078 | new_dir_fd: posix.fd_t, | |
| 1079 | 1079 | new_path: [*:0]const u8, |
| 1080 | 1080 | flags: u32, |
| 1081 | 1081 | ) !*linux.io_uring_sqe { |
| ... | ... | @@ -1147,7 +1147,7 @@ pub fn waitid( |
| 1147 | 1147 | /// Registering file descriptors will wait for the ring to idle. |
| 1148 | 1148 | /// Files are automatically unregistered by the kernel when the ring is torn down. |
| 1149 | 1149 | /// An application need unregister only if it wants to register a new array of file descriptors. |
| 1150 | pub fn register_files(self: *IoUring, fds: []const os.fd_t) !void { | |
| 1150 | pub fn register_files(self: *IoUring, fds: []const posix.fd_t) !void { | |
| 1151 | 1151 | assert(self.fd >= 0); |
| 1152 | 1152 | const res = linux.io_uring_register( |
| 1153 | 1153 | self.fd, |
| ... | ... | @@ -1166,7 +1166,7 @@ pub fn register_files(self: *IoUring, fds: []const os.fd_t) !void { |
| 1166 | 1166 | /// * removing an existing entry (set the fd to -1) |
| 1167 | 1167 | /// * replacing an existing entry with a new fd |
| 1168 | 1168 | /// Adding new file descriptors must be done with `register_files`. |
| 1169 | pub fn register_files_update(self: *IoUring, offset: u32, fds: []const os.fd_t) !void { | |
| 1169 | pub fn register_files_update(self: *IoUring, offset: u32, fds: []const posix.fd_t) !void { | |
| 1170 | 1170 | assert(self.fd >= 0); |
| 1171 | 1171 | |
| 1172 | 1172 | const FilesUpdate = extern struct { |
| ... | ... | @@ -1192,7 +1192,7 @@ pub fn register_files_update(self: *IoUring, offset: u32, fds: []const os.fd_t) |
| 1192 | 1192 | /// Registers the file descriptor for an eventfd that will be notified of completion events on |
| 1193 | 1193 | /// an io_uring instance. |
| 1194 | 1194 | /// Only a single a eventfd can be registered at any given point in time. |
| 1195 | pub fn register_eventfd(self: *IoUring, fd: os.fd_t) !void { | |
| 1195 | pub fn register_eventfd(self: *IoUring, fd: posix.fd_t) !void { | |
| 1196 | 1196 | assert(self.fd >= 0); |
| 1197 | 1197 | const res = linux.io_uring_register( |
| 1198 | 1198 | self.fd, |
| ... | ... | @@ -1207,7 +1207,7 @@ pub fn register_eventfd(self: *IoUring, fd: os.fd_t) !void { |
| 1207 | 1207 | /// an io_uring instance. Notifications are only posted for events that complete in an async manner. |
| 1208 | 1208 | /// This means that events that complete inline while being submitted do not trigger a notification event. |
| 1209 | 1209 | /// Only a single eventfd can be registered at any given point in time. |
| 1210 | pub fn register_eventfd_async(self: *IoUring, fd: os.fd_t) !void { | |
| 1210 | pub fn register_eventfd_async(self: *IoUring, fd: posix.fd_t) !void { | |
| 1211 | 1211 | assert(self.fd >= 0); |
| 1212 | 1212 | const res = linux.io_uring_register( |
| 1213 | 1213 | self.fd, |
| ... | ... | @@ -1231,7 +1231,7 @@ pub fn unregister_eventfd(self: *IoUring) !void { |
| 1231 | 1231 | } |
| 1232 | 1232 | |
| 1233 | 1233 | /// Registers an array of buffers for use with `read_fixed` and `write_fixed`. |
| 1234 | pub fn register_buffers(self: *IoUring, buffers: []const os.iovec) !void { | |
| 1234 | pub fn register_buffers(self: *IoUring, buffers: []const posix.iovec) !void { | |
| 1235 | 1235 | assert(self.fd >= 0); |
| 1236 | 1236 | const res = linux.io_uring_register( |
| 1237 | 1237 | self.fd, |
| ... | ... | @@ -1246,15 +1246,15 @@ pub fn register_buffers(self: *IoUring, buffers: []const os.iovec) !void { |
| 1246 | 1246 | pub fn unregister_buffers(self: *IoUring) !void { |
| 1247 | 1247 | assert(self.fd >= 0); |
| 1248 | 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 | 1250 | .SUCCESS => {}, |
| 1251 | 1251 | .NXIO => return error.BuffersNotRegistered, |
| 1252 | else => |errno| return os.unexpectedErrno(errno), | |
| 1252 | else => |errno| return posix.unexpectedErrno(errno), | |
| 1253 | 1253 | } |
| 1254 | 1254 | } |
| 1255 | 1255 | |
| 1256 | 1256 | fn handle_registration_result(res: usize) !void { |
| 1257 | switch (linux.getErrno(res)) { | |
| 1257 | switch (linux.E.init(res)) { | |
| 1258 | 1258 | .SUCCESS => {}, |
| 1259 | 1259 | // One or more fds in the array are invalid, or the kernel does not support sparse sets: |
| 1260 | 1260 | .BADF => return error.FileDescriptorInvalid, |
| ... | ... | @@ -1271,7 +1271,7 @@ fn handle_registration_result(res: usize) !void { |
| 1271 | 1271 | .NOMEM => return error.SystemResources, |
| 1272 | 1272 | // Attempt to register files on a ring already registering files or being torn down: |
| 1273 | 1273 | .NXIO => return error.RingShuttingDownOrAlreadyRegisteringFiles, |
| 1274 | else => |errno| return os.unexpectedErrno(errno), | |
| 1274 | else => |errno| return posix.unexpectedErrno(errno), | |
| 1275 | 1275 | } |
| 1276 | 1276 | } |
| 1277 | 1277 | |
| ... | ... | @@ -1279,10 +1279,10 @@ fn handle_registration_result(res: usize) !void { |
| 1279 | 1279 | pub fn unregister_files(self: *IoUring) !void { |
| 1280 | 1280 | assert(self.fd >= 0); |
| 1281 | 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 | 1283 | .SUCCESS => {}, |
| 1284 | 1284 | .NXIO => return error.FilesNotRegistered, |
| 1285 | else => |errno| return os.unexpectedErrno(errno), | |
| 1285 | else => |errno| return posix.unexpectedErrno(errno), | |
| 1286 | 1286 | } |
| 1287 | 1287 | } |
| 1288 | 1288 | |
| ... | ... | @@ -1355,36 +1355,36 @@ pub const SubmissionQueue = struct { |
| 1355 | 1355 | sqe_head: u32 = 0, |
| 1356 | 1356 | sqe_tail: u32 = 0, |
| 1357 | 1357 | |
| 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 | 1359 | assert(fd >= 0); |
| 1360 | 1360 | assert((p.features & linux.IORING_FEAT_SINGLE_MMAP) != 0); |
| 1361 | 1361 | const size = @max( |
| 1362 | 1362 | p.sq_off.array + p.sq_entries * @sizeOf(u32), |
| 1363 | 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 | 1366 | null, |
| 1367 | 1367 | size, |
| 1368 | os.PROT.READ | os.PROT.WRITE, | |
| 1368 | posix.PROT.READ | posix.PROT.WRITE, | |
| 1369 | 1369 | .{ .TYPE = .SHARED, .POPULATE = true }, |
| 1370 | 1370 | fd, |
| 1371 | 1371 | linux.IORING_OFF_SQ_RING, |
| 1372 | 1372 | ); |
| 1373 | errdefer os.munmap(mmap); | |
| 1373 | errdefer posix.munmap(mmap); | |
| 1374 | 1374 | assert(mmap.len == size); |
| 1375 | 1375 | |
| 1376 | 1376 | // The motivation for the `sqes` and `array` indirection is to make it possible for the |
| 1377 | 1377 | // application to preallocate static linux.io_uring_sqe entries and then replay them when needed. |
| 1378 | 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 | 1380 | null, |
| 1381 | 1381 | size_sqes, |
| 1382 | os.PROT.READ | os.PROT.WRITE, | |
| 1382 | posix.PROT.READ | posix.PROT.WRITE, | |
| 1383 | 1383 | .{ .TYPE = .SHARED, .POPULATE = true }, |
| 1384 | 1384 | fd, |
| 1385 | 1385 | linux.IORING_OFF_SQES, |
| 1386 | 1386 | ); |
| 1387 | errdefer os.munmap(mmap_sqes); | |
| 1387 | errdefer posix.munmap(mmap_sqes); | |
| 1388 | 1388 | assert(mmap_sqes.len == size_sqes); |
| 1389 | 1389 | |
| 1390 | 1390 | const array: [*]u32 = @ptrCast(@alignCast(&mmap[p.sq_off.array])); |
| ... | ... | @@ -1406,8 +1406,8 @@ pub const SubmissionQueue = struct { |
| 1406 | 1406 | } |
| 1407 | 1407 | |
| 1408 | 1408 | pub fn deinit(self: *SubmissionQueue) void { |
| 1409 | os.munmap(self.mmap_sqes); | |
| 1410 | os.munmap(self.mmap); | |
| 1409 | posix.munmap(self.mmap_sqes); | |
| 1410 | posix.munmap(self.mmap); | |
| 1411 | 1411 | } |
| 1412 | 1412 | }; |
| 1413 | 1413 | |
| ... | ... | @@ -1418,7 +1418,7 @@ pub const CompletionQueue = struct { |
| 1418 | 1418 | overflow: *u32, |
| 1419 | 1419 | cqes: []linux.io_uring_cqe, |
| 1420 | 1420 | |
| 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 | 1422 | assert(fd >= 0); |
| 1423 | 1423 | assert((p.features & linux.IORING_FEAT_SINGLE_MMAP) != 0); |
| 1424 | 1424 | const mmap = sq.mmap; |
| ... | ... | @@ -1506,7 +1506,7 @@ pub const BufferGroup = struct { |
| 1506 | 1506 | } |
| 1507 | 1507 | |
| 1508 | 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 | 1510 | var sqe = try self.ring.get_sqe(); |
| 1511 | 1511 | sqe.prep_rw(.RECV, fd, 0, 0, 0); |
| 1512 | 1512 | sqe.rw_flags = flags; |
| ... | ... | @@ -1517,7 +1517,7 @@ pub const BufferGroup = struct { |
| 1517 | 1517 | } |
| 1518 | 1518 | |
| 1519 | 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 | 1521 | var sqe = try self.recv(user_data, fd, flags); |
| 1522 | 1522 | sqe.ioprio |= linux.IORING_RECV_MULTISHOT; |
| 1523 | 1523 | return sqe; |
| ... | ... | @@ -1559,20 +1559,20 @@ pub const BufferGroup = struct { |
| 1559 | 1559 | /// `fd` is IO_Uring.fd for which the provided buffer ring is being registered. |
| 1560 | 1560 | /// `entries` is the number of entries requested in the buffer ring, must be power of 2. |
| 1561 | 1561 | /// `group_id` is the chosen buffer group ID, unique in IO_Uring. |
| 1562 | pub fn setup_buf_ring(fd: os.fd_t, entries: u16, group_id: u16) !*align(mem.page_size) linux.io_uring_buf_ring { | |
| 1562 | pub fn setup_buf_ring(fd: posix.fd_t, entries: u16, group_id: u16) !*align(mem.page_size) linux.io_uring_buf_ring { | |
| 1563 | 1563 | if (entries == 0 or entries > 1 << 15) return error.EntriesNotInRange; |
| 1564 | 1564 | if (!std.math.isPowerOfTwo(entries)) return error.EntriesNotPowerOfTwo; |
| 1565 | 1565 | |
| 1566 | 1566 | const mmap_size = entries * @sizeOf(linux.io_uring_buf); |
| 1567 | const mmap = try os.mmap( | |
| 1567 | const mmap = try posix.mmap( | |
| 1568 | 1568 | null, |
| 1569 | 1569 | mmap_size, |
| 1570 | os.PROT.READ | os.PROT.WRITE, | |
| 1570 | posix.PROT.READ | posix.PROT.WRITE, | |
| 1571 | 1571 | .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, |
| 1572 | 1572 | -1, |
| 1573 | 1573 | 0, |
| 1574 | 1574 | ); |
| 1575 | errdefer os.munmap(mmap); | |
| 1575 | errdefer posix.munmap(mmap); | |
| 1576 | 1576 | assert(mmap.len == mmap_size); |
| 1577 | 1577 | |
| 1578 | 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 | 1580 | return br; |
| 1581 | 1581 | } |
| 1582 | 1582 | |
| 1583 | fn register_buf_ring(fd: os.fd_t, addr: u64, entries: u32, group_id: u16) !void { | |
| 1583 | fn register_buf_ring(fd: posix.fd_t, addr: u64, entries: u32, group_id: u16) !void { | |
| 1584 | 1584 | var reg = mem.zeroInit(linux.io_uring_buf_reg, .{ |
| 1585 | 1585 | .ring_addr = addr, |
| 1586 | 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 | 1595 | try handle_register_buf_ring_result(res); |
| 1596 | 1596 | } |
| 1597 | 1597 | |
| 1598 | fn unregister_buf_ring(fd: os.fd_t, group_id: u16) !void { | |
| 1598 | fn unregister_buf_ring(fd: posix.fd_t, group_id: u16) !void { | |
| 1599 | 1599 | var reg = mem.zeroInit(linux.io_uring_buf_reg, .{ |
| 1600 | 1600 | .bgid = group_id, |
| 1601 | 1601 | }); |
| ... | ... | @@ -1609,20 +1609,20 @@ fn unregister_buf_ring(fd: os.fd_t, group_id: u16) !void { |
| 1609 | 1609 | } |
| 1610 | 1610 | |
| 1611 | 1611 | fn handle_register_buf_ring_result(res: usize) !void { |
| 1612 | switch (linux.getErrno(res)) { | |
| 1612 | switch (linux.E.init(res)) { | |
| 1613 | 1613 | .SUCCESS => {}, |
| 1614 | 1614 | .INVAL => return error.ArgumentsInvalid, |
| 1615 | else => |errno| return os.unexpectedErrno(errno), | |
| 1615 | else => |errno| return posix.unexpectedErrno(errno), | |
| 1616 | 1616 | } |
| 1617 | 1617 | } |
| 1618 | 1618 | |
| 1619 | 1619 | // Unregisters a previously registered shared buffer ring, returned from io_uring_setup_buf_ring. |
| 1620 | pub fn free_buf_ring(fd: os.fd_t, br: *align(mem.page_size) linux.io_uring_buf_ring, entries: u32, group_id: u16) void { | |
| 1620 | pub 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 | 1621 | unregister_buf_ring(fd, group_id) catch {}; |
| 1622 | 1622 | var mmap: []align(mem.page_size) u8 = undefined; |
| 1623 | 1623 | mmap.ptr = @ptrCast(br); |
| 1624 | 1624 | mmap.len = entries * @sizeOf(linux.io_uring_buf); |
| 1625 | os.munmap(mmap); | |
| 1625 | posix.munmap(mmap); | |
| 1626 | 1626 | } |
| 1627 | 1627 | |
| 1628 | 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 | 1664 | } |
| 1665 | 1665 | |
| 1666 | 1666 | test "structs/offsets/entries" { |
| 1667 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 1667 | if (!is_linux) return error.SkipZigTest; | |
| 1668 | 1668 | |
| 1669 | 1669 | try testing.expectEqual(@as(usize, 120), @sizeOf(linux.io_uring_params)); |
| 1670 | 1670 | try testing.expectEqual(@as(usize, 64), @sizeOf(linux.io_uring_sqe)); |
| ... | ... | @@ -1679,7 +1679,7 @@ test "structs/offsets/entries" { |
| 1679 | 1679 | } |
| 1680 | 1680 | |
| 1681 | 1681 | test "nop" { |
| 1682 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 1682 | if (!is_linux) return error.SkipZigTest; | |
| 1683 | 1683 | |
| 1684 | 1684 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 1685 | 1685 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -1688,7 +1688,7 @@ test "nop" { |
| 1688 | 1688 | }; |
| 1689 | 1689 | defer { |
| 1690 | 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 | } |
| 1693 | 1693 | |
| 1694 | 1694 | const sqe = try ring.nop(0xaaaaaaaa); |
| ... | ... | @@ -1746,7 +1746,7 @@ test "nop" { |
| 1746 | 1746 | } |
| 1747 | 1747 | |
| 1748 | 1748 | test "readv" { |
| 1749 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 1749 | if (!is_linux) return error.SkipZigTest; | |
| 1750 | 1750 | |
| 1751 | 1751 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 1752 | 1752 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -1755,8 +1755,8 @@ test "readv" { |
| 1755 | 1755 | }; |
| 1756 | 1756 | defer ring.deinit(); |
| 1757 | 1757 | |
| 1758 | const fd = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | |
| 1759 | defer os.close(fd); | |
| 1758 | const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | |
| 1759 | defer posix.close(fd); | |
| 1760 | 1760 | |
| 1761 | 1761 | // Linux Kernel 5.4 supports IORING_REGISTER_FILES but not sparse fd sets (i.e. an fd of -1). |
| 1762 | 1762 | // Linux Kernel 5.5 adds support for sparse fd sets. |
| ... | ... | @@ -1764,13 +1764,13 @@ test "readv" { |
| 1764 | 1764 | // https://github.com/torvalds/linux/blob/v5.4/fs/io_uring.c#L3119-L3124 vs |
| 1765 | 1765 | // https://github.com/torvalds/linux/blob/v5.8/fs/io_uring.c#L6687-L6691 |
| 1766 | 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 | 1768 | const fd_index = 0; |
| 1769 | 1769 | registered_fds[fd_index] = fd; |
| 1770 | 1770 | try ring.register_files(registered_fds[0..]); |
| 1771 | 1771 | |
| 1772 | 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 | 1774 | const sqe = try ring.read(0xcccccccc, fd_index, .{ .iovecs = iovecs[0..] }, 0); |
| 1775 | 1775 | try testing.expectEqual(linux.IORING_OP.READV, sqe.opcode); |
| 1776 | 1776 | sqe.flags |= linux.IOSQE_FIXED_FILE; |
| ... | ... | @@ -1788,7 +1788,7 @@ test "readv" { |
| 1788 | 1788 | } |
| 1789 | 1789 | |
| 1790 | 1790 | test "writev/fsync/readv" { |
| 1791 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 1791 | if (!is_linux) return error.SkipZigTest; | |
| 1792 | 1792 | |
| 1793 | 1793 | var ring = IoUring.init(4, 0) catch |err| switch (err) { |
| 1794 | 1794 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -1806,12 +1806,12 @@ test "writev/fsync/readv" { |
| 1806 | 1806 | const fd = file.handle; |
| 1807 | 1807 | |
| 1808 | 1808 | const buffer_write = [_]u8{42} ** 128; |
| 1809 | const iovecs_write = [_]os.iovec_const{ | |
| 1810 | os.iovec_const{ .iov_base = &buffer_write, .iov_len = buffer_write.len }, | |
| 1809 | const iovecs_write = [_]posix.iovec_const{ | |
| 1810 | posix.iovec_const{ .iov_base = &buffer_write, .iov_len = buffer_write.len }, | |
| 1811 | 1811 | }; |
| 1812 | 1812 | var buffer_read = [_]u8{0} ** 128; |
| 1813 | var iovecs_read = [_]os.iovec{ | |
| 1814 | os.iovec{ .iov_base = &buffer_read, .iov_len = buffer_read.len }, | |
| 1813 | var iovecs_read = [_]posix.iovec{ | |
| 1814 | posix.iovec{ .iov_base = &buffer_read, .iov_len = buffer_read.len }, | |
| 1815 | 1815 | }; |
| 1816 | 1816 | |
| 1817 | 1817 | const sqe_writev = try ring.writev(0xdddddddd, fd, iovecs_write[0..], 17); |
| ... | ... | @@ -1858,7 +1858,7 @@ test "writev/fsync/readv" { |
| 1858 | 1858 | } |
| 1859 | 1859 | |
| 1860 | 1860 | test "write/read" { |
| 1861 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 1861 | if (!is_linux) return error.SkipZigTest; | |
| 1862 | 1862 | |
| 1863 | 1863 | var ring = IoUring.init(2, 0) catch |err| switch (err) { |
| 1864 | 1864 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -1905,7 +1905,7 @@ test "write/read" { |
| 1905 | 1905 | } |
| 1906 | 1906 | |
| 1907 | 1907 | test "splice/read" { |
| 1908 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 1908 | if (!is_linux) return error.SkipZigTest; | |
| 1909 | 1909 | |
| 1910 | 1910 | var ring = IoUring.init(4, 0) catch |err| switch (err) { |
| 1911 | 1911 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -1929,7 +1929,7 @@ test "splice/read" { |
| 1929 | 1929 | var buffer_read = [_]u8{98} ** 20; |
| 1930 | 1930 | _ = try file_src.write(&buffer_write); |
| 1931 | 1931 | |
| 1932 | const fds = try os.pipe(); | |
| 1932 | const fds = try posix.pipe(); | |
| 1933 | 1933 | const pipe_offset: u64 = std.math.maxInt(u64); |
| 1934 | 1934 | |
| 1935 | 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 | 1976 | } |
| 1977 | 1977 | |
| 1978 | 1978 | test "write_fixed/read_fixed" { |
| 1979 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 1979 | if (!is_linux) return error.SkipZigTest; | |
| 1980 | 1980 | |
| 1981 | 1981 | var ring = IoUring.init(2, 0) catch |err| switch (err) { |
| 1982 | 1982 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -1998,7 +1998,7 @@ test "write_fixed/read_fixed" { |
| 1998 | 1998 | @memset(&raw_buffers[0], 'z'); |
| 1999 | 1999 | raw_buffers[0][0.."foobar".len].* = "foobar".*; |
| 2000 | 2000 | |
| 2001 | var buffers = [2]os.iovec{ | |
| 2001 | var buffers = [2]posix.iovec{ | |
| 2002 | 2002 | .{ .iov_base = &raw_buffers[0], .iov_len = raw_buffers[0].len }, |
| 2003 | 2003 | .{ .iov_base = &raw_buffers[1], .iov_len = raw_buffers[1].len }, |
| 2004 | 2004 | }; |
| ... | ... | @@ -2041,7 +2041,7 @@ test "write_fixed/read_fixed" { |
| 2041 | 2041 | } |
| 2042 | 2042 | |
| 2043 | 2043 | test "openat" { |
| 2044 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2044 | if (!is_linux) return error.SkipZigTest; | |
| 2045 | 2045 | |
| 2046 | 2046 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 2047 | 2047 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2063,7 +2063,7 @@ test "openat" { |
| 2063 | 2063 | } else @intFromPtr(path); |
| 2064 | 2064 | |
| 2065 | 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 | 2067 | const sqe_openat = try ring.openat(0x33333333, tmp.dir.fd, path, flags, mode); |
| 2068 | 2068 | try testing.expectEqual(linux.io_uring_sqe{ |
| 2069 | 2069 | .opcode = .OPENAT, |
| ... | ... | @@ -2091,11 +2091,11 @@ test "openat" { |
| 2091 | 2091 | try testing.expect(cqe_openat.res > 0); |
| 2092 | 2092 | try testing.expectEqual(@as(u32, 0), cqe_openat.flags); |
| 2093 | 2093 | |
| 2094 | os.close(cqe_openat.res); | |
| 2094 | posix.close(cqe_openat.res); | |
| 2095 | 2095 | } |
| 2096 | 2096 | |
| 2097 | 2097 | test "close" { |
| 2098 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2098 | if (!is_linux) return error.SkipZigTest; | |
| 2099 | 2099 | |
| 2100 | 2100 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 2101 | 2101 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2126,7 +2126,7 @@ test "close" { |
| 2126 | 2126 | } |
| 2127 | 2127 | |
| 2128 | 2128 | test "accept/connect/send/recv" { |
| 2129 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2129 | if (!is_linux) return error.SkipZigTest; | |
| 2130 | 2130 | |
| 2131 | 2131 | var ring = IoUring.init(16, 0) catch |err| switch (err) { |
| 2132 | 2132 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2167,7 +2167,7 @@ test "accept/connect/send/recv" { |
| 2167 | 2167 | } |
| 2168 | 2168 | |
| 2169 | 2169 | test "sendmsg/recvmsg" { |
| 2170 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2170 | if (!is_linux) return error.SkipZigTest; | |
| 2171 | 2171 | |
| 2172 | 2172 | var ring = IoUring.init(2, 0) catch |err| switch (err) { |
| 2173 | 2173 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2178,24 +2178,24 @@ test "sendmsg/recvmsg" { |
| 2178 | 2178 | |
| 2179 | 2179 | var address_server = try net.Address.parseIp4("127.0.0.1", 0); |
| 2180 | 2180 | |
| 2181 | const server = try os.socket(address_server.any.family, os.SOCK.DGRAM, 0); | |
| 2182 | defer os.close(server); | |
| 2183 | try os.setsockopt(server, os.SOL.SOCKET, os.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1))); | |
| 2184 | try os.setsockopt(server, os.SOL.SOCKET, os.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); | |
| 2185 | try os.bind(server, &address_server.any, address_server.getOsSockLen()); | |
| 2181 | const server = try posix.socket(address_server.any.family, posix.SOCK.DGRAM, 0); | |
| 2182 | defer posix.close(server); | |
| 2183 | try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1))); | |
| 2184 | try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); | |
| 2185 | try posix.bind(server, &address_server.any, address_server.getOsSockLen()); | |
| 2186 | 2186 | |
| 2187 | 2187 | // set address_server to the OS-chosen IP/port. |
| 2188 | var slen: os.socklen_t = address_server.getOsSockLen(); | |
| 2189 | try os.getsockname(server, &address_server.any, &slen); | |
| 2188 | var slen: posix.socklen_t = address_server.getOsSockLen(); | |
| 2189 | try posix.getsockname(server, &address_server.any, &slen); | |
| 2190 | 2190 | |
| 2191 | const client = try os.socket(address_server.any.family, os.SOCK.DGRAM, 0); | |
| 2192 | defer os.close(client); | |
| 2191 | const client = try posix.socket(address_server.any.family, posix.SOCK.DGRAM, 0); | |
| 2192 | defer posix.close(client); | |
| 2193 | 2193 | |
| 2194 | 2194 | const buffer_send = [_]u8{42} ** 128; |
| 2195 | const iovecs_send = [_]os.iovec_const{ | |
| 2196 | os.iovec_const{ .iov_base = &buffer_send, .iov_len = buffer_send.len }, | |
| 2195 | const iovecs_send = [_]posix.iovec_const{ | |
| 2196 | posix.iovec_const{ .iov_base = &buffer_send, .iov_len = buffer_send.len }, | |
| 2197 | 2197 | }; |
| 2198 | const msg_send = os.msghdr_const{ | |
| 2198 | const msg_send: posix.msghdr_const = .{ | |
| 2199 | 2199 | .name = &address_server.any, |
| 2200 | 2200 | .namelen = address_server.getOsSockLen(), |
| 2201 | 2201 | .iov = &iovecs_send, |
| ... | ... | @@ -2210,12 +2210,12 @@ test "sendmsg/recvmsg" { |
| 2210 | 2210 | try testing.expectEqual(client, sqe_sendmsg.fd); |
| 2211 | 2211 | |
| 2212 | 2212 | var buffer_recv = [_]u8{0} ** 128; |
| 2213 | var iovecs_recv = [_]os.iovec{ | |
| 2214 | os.iovec{ .iov_base = &buffer_recv, .iov_len = buffer_recv.len }, | |
| 2213 | var iovecs_recv = [_]posix.iovec{ | |
| 2214 | posix.iovec{ .iov_base = &buffer_recv, .iov_len = buffer_recv.len }, | |
| 2215 | 2215 | }; |
| 2216 | 2216 | const addr = [_]u8{0} ** 4; |
| 2217 | 2217 | var address_recv = net.Address.initIp4(addr, 0); |
| 2218 | var msg_recv: os.msghdr = os.msghdr{ | |
| 2218 | var msg_recv: posix.msghdr = .{ | |
| 2219 | 2219 | .name = &address_recv.any, |
| 2220 | 2220 | .namelen = address_recv.getOsSockLen(), |
| 2221 | 2221 | .iov = &iovecs_recv, |
| ... | ... | @@ -2254,7 +2254,7 @@ test "sendmsg/recvmsg" { |
| 2254 | 2254 | } |
| 2255 | 2255 | |
| 2256 | 2256 | test "timeout (after a relative time)" { |
| 2257 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2257 | if (!is_linux) return error.SkipZigTest; | |
| 2258 | 2258 | |
| 2259 | 2259 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 2260 | 2260 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2265,7 +2265,7 @@ test "timeout (after a relative time)" { |
| 2265 | 2265 | |
| 2266 | 2266 | const ms = 10; |
| 2267 | 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 }; | |
| 2269 | 2269 | |
| 2270 | 2270 | const started = std.time.milliTimestamp(); |
| 2271 | 2271 | const sqe = try ring.timeout(0x55555555, &ts, 0, 0); |
| ... | ... | @@ -2285,7 +2285,7 @@ test "timeout (after a relative time)" { |
| 2285 | 2285 | } |
| 2286 | 2286 | |
| 2287 | 2287 | test "timeout (after a number of completions)" { |
| 2288 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2288 | if (!is_linux) return error.SkipZigTest; | |
| 2289 | 2289 | |
| 2290 | 2290 | var ring = IoUring.init(2, 0) catch |err| switch (err) { |
| 2291 | 2291 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2294,7 +2294,7 @@ test "timeout (after a number of completions)" { |
| 2294 | 2294 | }; |
| 2295 | 2295 | defer ring.deinit(); |
| 2296 | 2296 | |
| 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 | 2298 | const count_completions: u64 = 1; |
| 2299 | 2299 | const sqe_timeout = try ring.timeout(0x66666666, &ts, count_completions, 0); |
| 2300 | 2300 | try testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe_timeout.opcode); |
| ... | ... | @@ -2318,7 +2318,7 @@ test "timeout (after a number of completions)" { |
| 2318 | 2318 | } |
| 2319 | 2319 | |
| 2320 | 2320 | test "timeout_remove" { |
| 2321 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2321 | if (!is_linux) return error.SkipZigTest; | |
| 2322 | 2322 | |
| 2323 | 2323 | var ring = IoUring.init(2, 0) catch |err| switch (err) { |
| 2324 | 2324 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2327,7 +2327,7 @@ test "timeout_remove" { |
| 2327 | 2327 | }; |
| 2328 | 2328 | defer ring.deinit(); |
| 2329 | 2329 | |
| 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 | 2331 | const sqe_timeout = try ring.timeout(0x88888888, &ts, 0, 0); |
| 2332 | 2332 | try testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe_timeout.opcode); |
| 2333 | 2333 | try testing.expectEqual(@as(u64, 0x88888888), sqe_timeout.user_data); |
| ... | ... | @@ -2343,7 +2343,7 @@ test "timeout_remove" { |
| 2343 | 2343 | // * kernel 5.10 gives user data 0x88888888 first, 0x99999999 second |
| 2344 | 2344 | // * kernel 5.18 gives user data 0x99999999 first, 0x88888888 second |
| 2345 | 2345 | |
| 2346 | var cqes: [2]os.linux.io_uring_cqe = undefined; | |
| 2346 | var cqes: [2]linux.io_uring_cqe = undefined; | |
| 2347 | 2347 | cqes[0] = try ring.copy_cqe(); |
| 2348 | 2348 | cqes[1] = try ring.copy_cqe(); |
| 2349 | 2349 | |
| ... | ... | @@ -2378,7 +2378,7 @@ test "timeout_remove" { |
| 2378 | 2378 | } |
| 2379 | 2379 | |
| 2380 | 2380 | test "accept/connect/recv/link_timeout" { |
| 2381 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2381 | if (!is_linux) return error.SkipZigTest; | |
| 2382 | 2382 | |
| 2383 | 2383 | var ring = IoUring.init(16, 0) catch |err| switch (err) { |
| 2384 | 2384 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2395,7 +2395,7 @@ test "accept/connect/recv/link_timeout" { |
| 2395 | 2395 | const sqe_recv = try ring.recv(0xffffffff, socket_test_harness.server, .{ .buffer = buffer_recv[0..] }, 0); |
| 2396 | 2396 | sqe_recv.flags |= linux.IOSQE_IO_LINK; |
| 2397 | 2397 | |
| 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 | 2399 | _ = try ring.link_timeout(0x22222222, &ts, 0); |
| 2400 | 2400 | |
| 2401 | 2401 | const nr_wait = try ring.submit(); |
| ... | ... | @@ -2427,7 +2427,7 @@ test "accept/connect/recv/link_timeout" { |
| 2427 | 2427 | } |
| 2428 | 2428 | |
| 2429 | 2429 | test "fallocate" { |
| 2430 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2430 | if (!is_linux) return error.SkipZigTest; | |
| 2431 | 2431 | |
| 2432 | 2432 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 2433 | 2433 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2473,7 +2473,7 @@ test "fallocate" { |
| 2473 | 2473 | } |
| 2474 | 2474 | |
| 2475 | 2475 | test "statx" { |
| 2476 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2476 | if (!is_linux) return error.SkipZigTest; | |
| 2477 | 2477 | |
| 2478 | 2478 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 2479 | 2479 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2525,12 +2525,12 @@ test "statx" { |
| 2525 | 2525 | .flags = 0, |
| 2526 | 2526 | }, cqe); |
| 2527 | 2527 | |
| 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 | 2529 | try testing.expectEqual(@as(u64, 6), buf.size); |
| 2530 | 2530 | } |
| 2531 | 2531 | |
| 2532 | 2532 | test "accept/connect/recv/cancel" { |
| 2533 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2533 | if (!is_linux) return error.SkipZigTest; | |
| 2534 | 2534 | |
| 2535 | 2535 | var ring = IoUring.init(16, 0) catch |err| switch (err) { |
| 2536 | 2536 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2580,7 +2580,7 @@ test "accept/connect/recv/cancel" { |
| 2580 | 2580 | } |
| 2581 | 2581 | |
| 2582 | 2582 | test "register_files_update" { |
| 2583 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2583 | if (!is_linux) return error.SkipZigTest; | |
| 2584 | 2584 | |
| 2585 | 2585 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 2586 | 2586 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2589,10 +2589,10 @@ test "register_files_update" { |
| 2589 | 2589 | }; |
| 2590 | 2590 | defer ring.deinit(); |
| 2591 | 2591 | |
| 2592 | const fd = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | |
| 2593 | defer os.close(fd); | |
| 2592 | const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | |
| 2593 | defer posix.close(fd); | |
| 2594 | 2594 | |
| 2595 | var registered_fds = [_]os.fd_t{0} ** 2; | |
| 2595 | var registered_fds = [_]posix.fd_t{0} ** 2; | |
| 2596 | 2596 | const fd_index = 0; |
| 2597 | 2597 | const fd_index2 = 1; |
| 2598 | 2598 | registered_fds[fd_index] = fd; |
| ... | ... | @@ -2607,8 +2607,8 @@ test "register_files_update" { |
| 2607 | 2607 | // Test IORING_REGISTER_FILES_UPDATE |
| 2608 | 2608 | // Only available since Linux 5.5 |
| 2609 | 2609 | |
| 2610 | const fd2 = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | |
| 2611 | defer os.close(fd2); | |
| 2610 | const fd2 = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | |
| 2611 | defer posix.close(fd2); | |
| 2612 | 2612 | |
| 2613 | 2613 | registered_fds[fd_index] = fd2; |
| 2614 | 2614 | registered_fds[fd_index2] = -1; |
| ... | ... | @@ -2660,14 +2660,14 @@ test "register_files_update" { |
| 2660 | 2660 | |
| 2661 | 2661 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2662 | 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 | } |
| 2665 | 2665 | |
| 2666 | 2666 | try ring.unregister_files(); |
| 2667 | 2667 | } |
| 2668 | 2668 | |
| 2669 | 2669 | test "shutdown" { |
| 2670 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2670 | if (!is_linux) return error.SkipZigTest; | |
| 2671 | 2671 | |
| 2672 | 2672 | var ring = IoUring.init(16, 0) catch |err| switch (err) { |
| 2673 | 2673 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2680,17 +2680,17 @@ test "shutdown" { |
| 2680 | 2680 | |
| 2681 | 2681 | // Socket bound, expect shutdown to work |
| 2682 | 2682 | { |
| 2683 | const server = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); | |
| 2684 | defer os.close(server); | |
| 2685 | try os.setsockopt(server, os.SOL.SOCKET, os.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); | |
| 2686 | try os.bind(server, &address.any, address.getOsSockLen()); | |
| 2687 | try os.listen(server, 1); | |
| 2683 | const server = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | |
| 2684 | defer posix.close(server); | |
| 2685 | try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); | |
| 2686 | try posix.bind(server, &address.any, address.getOsSockLen()); | |
| 2687 | try posix.listen(server, 1); | |
| 2688 | 2688 | |
| 2689 | 2689 | // set address to the OS-chosen IP/port. |
| 2690 | var slen: os.socklen_t = address.getOsSockLen(); | |
| 2691 | try os.getsockname(server, &address.any, &slen); | |
| 2690 | var slen: posix.socklen_t = address.getOsSockLen(); | |
| 2691 | try posix.getsockname(server, &address.any, &slen); | |
| 2692 | 2692 | |
| 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 | 2694 | try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode); |
| 2695 | 2695 | try testing.expectEqual(@as(i32, server), shutdown_sqe.fd); |
| 2696 | 2696 | |
| ... | ... | @@ -2713,10 +2713,10 @@ test "shutdown" { |
| 2713 | 2713 | |
| 2714 | 2714 | // 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); | |
| 2717 | defer os.close(server); | |
| 2716 | const server = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | |
| 2717 | defer posix.close(server); | |
| 2718 | 2718 | |
| 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 | 2720 | else => |errno| std.debug.panic("unhandled errno: {}", .{errno}), |
| 2721 | 2721 | }; |
| 2722 | 2722 | try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode); |
| ... | ... | @@ -2726,12 +2726,12 @@ test "shutdown" { |
| 2726 | 2726 | |
| 2727 | 2727 | const cqe = try ring.copy_cqe(); |
| 2728 | 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 | } |
| 2732 | 2732 | |
| 2733 | 2733 | test "renameat" { |
| 2734 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2734 | if (!is_linux) return error.SkipZigTest; | |
| 2735 | 2735 | |
| 2736 | 2736 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 2737 | 2737 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2800,7 +2800,7 @@ test "renameat" { |
| 2800 | 2800 | } |
| 2801 | 2801 | |
| 2802 | 2802 | test "unlinkat" { |
| 2803 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2803 | if (!is_linux) return error.SkipZigTest; | |
| 2804 | 2804 | |
| 2805 | 2805 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 2806 | 2806 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2852,7 +2852,7 @@ test "unlinkat" { |
| 2852 | 2852 | } |
| 2853 | 2853 | |
| 2854 | 2854 | test "mkdirat" { |
| 2855 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2855 | if (!is_linux) return error.SkipZigTest; | |
| 2856 | 2856 | |
| 2857 | 2857 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 2858 | 2858 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2896,7 +2896,7 @@ test "mkdirat" { |
| 2896 | 2896 | } |
| 2897 | 2897 | |
| 2898 | 2898 | test "symlinkat" { |
| 2899 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2899 | if (!is_linux) return error.SkipZigTest; | |
| 2900 | 2900 | |
| 2901 | 2901 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 2902 | 2902 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -2944,7 +2944,7 @@ test "symlinkat" { |
| 2944 | 2944 | } |
| 2945 | 2945 | |
| 2946 | 2946 | test "linkat" { |
| 2947 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 2947 | if (!is_linux) return error.SkipZigTest; | |
| 2948 | 2948 | |
| 2949 | 2949 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 2950 | 2950 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -3003,7 +3003,7 @@ test "linkat" { |
| 3003 | 3003 | } |
| 3004 | 3004 | |
| 3005 | 3005 | test "provide_buffers: read" { |
| 3006 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 3006 | if (!is_linux) return error.SkipZigTest; | |
| 3007 | 3007 | |
| 3008 | 3008 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 3009 | 3009 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -3012,8 +3012,8 @@ test "provide_buffers: read" { |
| 3012 | 3012 | }; |
| 3013 | 3013 | defer ring.deinit(); |
| 3014 | 3014 | |
| 3015 | const fd = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | |
| 3016 | defer os.close(fd); | |
| 3015 | const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | |
| 3016 | defer posix.close(fd); | |
| 3017 | 3017 | |
| 3018 | 3018 | const group_id = 1337; |
| 3019 | 3019 | const buffer_id = 0; |
| ... | ... | @@ -3135,7 +3135,7 @@ test "provide_buffers: read" { |
| 3135 | 3135 | } |
| 3136 | 3136 | |
| 3137 | 3137 | test "remove_buffers" { |
| 3138 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 3138 | if (!is_linux) return error.SkipZigTest; | |
| 3139 | 3139 | |
| 3140 | 3140 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 3141 | 3141 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -3144,8 +3144,8 @@ test "remove_buffers" { |
| 3144 | 3144 | }; |
| 3145 | 3145 | defer ring.deinit(); |
| 3146 | 3146 | |
| 3147 | const fd = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | |
| 3148 | defer os.close(fd); | |
| 3147 | const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | |
| 3148 | defer posix.close(fd); | |
| 3149 | 3149 | |
| 3150 | 3150 | const group_id = 1337; |
| 3151 | 3151 | const buffer_id = 0; |
| ... | ... | @@ -3224,7 +3224,7 @@ test "remove_buffers" { |
| 3224 | 3224 | } |
| 3225 | 3225 | |
| 3226 | 3226 | test "provide_buffers: accept/connect/send/recv" { |
| 3227 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 3227 | if (!is_linux) return error.SkipZigTest; | |
| 3228 | 3228 | |
| 3229 | 3229 | var ring = IoUring.init(16, 0) catch |err| switch (err) { |
| 3230 | 3230 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -3391,9 +3391,9 @@ test "provide_buffers: accept/connect/send/recv" { |
| 3391 | 3391 | |
| 3392 | 3392 | /// Used for testing server/client interactions. |
| 3393 | 3393 | const SocketTestHarness = struct { |
| 3394 | listener: os.socket_t, | |
| 3395 | server: os.socket_t, | |
| 3396 | client: os.socket_t, | |
| 3394 | listener: posix.socket_t, | |
| 3395 | server: posix.socket_t, | |
| 3396 | client: posix.socket_t, | |
| 3397 | 3397 | |
| 3398 | 3398 | fn close(self: SocketTestHarness) void { |
| 3399 | 3399 | posix.close(self.client); |
| ... | ... | @@ -3408,12 +3408,12 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness { |
| 3408 | 3408 | errdefer posix.close(listener_socket); |
| 3409 | 3409 | |
| 3410 | 3410 | // Submit 1 accept |
| 3411 | var accept_addr: os.sockaddr = undefined; | |
| 3412 | var accept_addr_len: os.socklen_t = @sizeOf(@TypeOf(accept_addr)); | |
| 3411 | var accept_addr: posix.sockaddr = undefined; | |
| 3412 | var accept_addr_len: posix.socklen_t = @sizeOf(@TypeOf(accept_addr)); | |
| 3413 | 3413 | _ = try ring.accept(0xaaaaaaaa, listener_socket, &accept_addr, &accept_addr_len, 0); |
| 3414 | 3414 | |
| 3415 | 3415 | // 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 | 3417 | errdefer posix.close(client); |
| 3418 | 3418 | _ = try ring.connect(0xcccccccc, client, &address.any, address.getOsSockLen()); |
| 3419 | 3419 | |
| ... | ... | @@ -3451,24 +3451,24 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness { |
| 3451 | 3451 | }; |
| 3452 | 3452 | } |
| 3453 | 3453 | |
| 3454 | fn createListenerSocket(address: *net.Address) !os.socket_t { | |
| 3454 | fn createListenerSocket(address: *net.Address) !posix.socket_t { | |
| 3455 | 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 | 3457 | errdefer posix.close(listener_socket); |
| 3458 | 3458 | |
| 3459 | try os.setsockopt(listener_socket, os.SOL.SOCKET, os.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); | |
| 3460 | try os.bind(listener_socket, &address.any, address.getOsSockLen()); | |
| 3461 | try os.listen(listener_socket, kernel_backlog); | |
| 3459 | try posix.setsockopt(listener_socket, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); | |
| 3460 | try posix.bind(listener_socket, &address.any, address.getOsSockLen()); | |
| 3461 | try posix.listen(listener_socket, kernel_backlog); | |
| 3462 | 3462 | |
| 3463 | 3463 | // set address to the OS-chosen IP/port. |
| 3464 | var slen: os.socklen_t = address.getOsSockLen(); | |
| 3465 | try os.getsockname(listener_socket, &address.any, &slen); | |
| 3464 | var slen: posix.socklen_t = address.getOsSockLen(); | |
| 3465 | try posix.getsockname(listener_socket, &address.any, &slen); | |
| 3466 | 3466 | |
| 3467 | 3467 | return listener_socket; |
| 3468 | 3468 | } |
| 3469 | 3469 | |
| 3470 | 3470 | test "accept multishot" { |
| 3471 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 3471 | if (!is_linux) return error.SkipZigTest; | |
| 3472 | 3472 | |
| 3473 | 3473 | var ring = IoUring.init(16, 0) catch |err| switch (err) { |
| 3474 | 3474 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -3482,8 +3482,8 @@ test "accept multishot" { |
| 3482 | 3482 | defer posix.close(listener_socket); |
| 3483 | 3483 | |
| 3484 | 3484 | // submit multishot accept operation |
| 3485 | var addr: os.sockaddr = undefined; | |
| 3486 | var addr_len: os.socklen_t = @sizeOf(@TypeOf(addr)); | |
| 3485 | var addr: posix.sockaddr = undefined; | |
| 3486 | var addr_len: posix.socklen_t = @sizeOf(@TypeOf(addr)); | |
| 3487 | 3487 | const userdata: u64 = 0xaaaaaaaa; |
| 3488 | 3488 | _ = try ring.accept_multishot(userdata, listener_socket, &addr, &addr_len, 0); |
| 3489 | 3489 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| ... | ... | @@ -3491,9 +3491,9 @@ test "accept multishot" { |
| 3491 | 3491 | var nr: usize = 4; // number of clients to connect |
| 3492 | 3492 | while (nr > 0) : (nr -= 1) { |
| 3493 | 3493 | // 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 | 3495 | errdefer posix.close(client); |
| 3496 | try os.connect(client, &address.any, address.getOsSockLen()); | |
| 3496 | try posix.connect(client, &address.any, address.getOsSockLen()); | |
| 3497 | 3497 | |
| 3498 | 3498 | // test accept completion |
| 3499 | 3499 | var cqe = try ring.copy_cqe(); |
| ... | ... | @@ -3571,7 +3571,7 @@ test "accept_direct" { |
| 3571 | 3571 | var address = try net.Address.parseIp4("127.0.0.1", 0); |
| 3572 | 3572 | |
| 3573 | 3573 | // register direct file descriptors |
| 3574 | var registered_fds = [_]os.fd_t{-1} ** 2; | |
| 3574 | var registered_fds = [_]posix.fd_t{-1} ** 2; | |
| 3575 | 3575 | try ring.register_files(registered_fds[0..]); |
| 3576 | 3576 | |
| 3577 | 3577 | const listener_socket = try createListenerSocket(&address); |
| ... | ... | @@ -3591,19 +3591,19 @@ test "accept_direct" { |
| 3591 | 3591 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 3592 | 3592 | |
| 3593 | 3593 | // connect |
| 3594 | const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); | |
| 3595 | try os.connect(client, &address.any, address.getOsSockLen()); | |
| 3594 | const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | |
| 3595 | try posix.connect(client, &address.any, address.getOsSockLen()); | |
| 3596 | 3596 | defer posix.close(client); |
| 3597 | 3597 | |
| 3598 | 3598 | // accept completion |
| 3599 | 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 | 3601 | const fd_index = cqe_accept.res; |
| 3602 | 3602 | try testing.expect(fd_index < registered_fds.len); |
| 3603 | 3603 | try testing.expect(cqe_accept.user_data == accept_userdata); |
| 3604 | 3604 | |
| 3605 | 3605 | // send data |
| 3606 | _ = try os.send(client, buffer_send, 0); | |
| 3606 | _ = try posix.send(client, buffer_send, 0); | |
| 3607 | 3607 | |
| 3608 | 3608 | // Example of how to use registered fd: |
| 3609 | 3609 | // Submit receive to fixed file returned by accept (fd_index). |
| ... | ... | @@ -3625,13 +3625,13 @@ test "accept_direct" { |
| 3625 | 3625 | _ = try ring.accept_direct(accept_userdata, listener_socket, null, null, 0); |
| 3626 | 3626 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 3627 | 3627 | // connect |
| 3628 | const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); | |
| 3629 | try os.connect(client, &address.any, address.getOsSockLen()); | |
| 3628 | const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | |
| 3629 | try posix.connect(client, &address.any, address.getOsSockLen()); | |
| 3630 | 3630 | defer posix.close(client); |
| 3631 | 3631 | // completion with error |
| 3632 | 3632 | const cqe_accept = try ring.copy_cqe(); |
| 3633 | 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 | 3636 | // return file descriptors to kernel |
| 3637 | 3637 | try ring.register_files_update(0, registered_fds[0..]); |
| ... | ... | @@ -3651,7 +3651,7 @@ test "accept_multishot_direct" { |
| 3651 | 3651 | |
| 3652 | 3652 | var address = try net.Address.parseIp4("127.0.0.1", 0); |
| 3653 | 3653 | |
| 3654 | var registered_fds = [_]os.fd_t{-1} ** 2; | |
| 3654 | var registered_fds = [_]posix.fd_t{-1} ** 2; | |
| 3655 | 3655 | try ring.register_files(registered_fds[0..]); |
| 3656 | 3656 | |
| 3657 | 3657 | const listener_socket = try createListenerSocket(&address); |
| ... | ... | @@ -3667,8 +3667,8 @@ test "accept_multishot_direct" { |
| 3667 | 3667 | |
| 3668 | 3668 | for (registered_fds) |_| { |
| 3669 | 3669 | // connect |
| 3670 | const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); | |
| 3671 | try os.connect(client, &address.any, address.getOsSockLen()); | |
| 3670 | const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | |
| 3671 | try posix.connect(client, &address.any, address.getOsSockLen()); | |
| 3672 | 3672 | defer posix.close(client); |
| 3673 | 3673 | |
| 3674 | 3674 | // accept completion |
| ... | ... | @@ -3682,13 +3682,13 @@ test "accept_multishot_direct" { |
| 3682 | 3682 | // Multishot is terminated (more flag is not set). |
| 3683 | 3683 | { |
| 3684 | 3684 | // connect |
| 3685 | const client = try os.socket(address.any.family, os.SOCK.STREAM | os.SOCK.CLOEXEC, 0); | |
| 3686 | try os.connect(client, &address.any, address.getOsSockLen()); | |
| 3685 | const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | |
| 3686 | try posix.connect(client, &address.any, address.getOsSockLen()); | |
| 3687 | 3687 | defer posix.close(client); |
| 3688 | 3688 | // completion with error |
| 3689 | 3689 | const cqe_accept = try ring.copy_cqe(); |
| 3690 | 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 | 3692 | try testing.expect(cqe_accept.flags & linux.IORING_CQE_F_MORE == 0); // has more is not set |
| 3693 | 3693 | } |
| 3694 | 3694 | // return file descriptors to kernel |
| ... | ... | @@ -3708,16 +3708,16 @@ test "socket" { |
| 3708 | 3708 | defer ring.deinit(); |
| 3709 | 3709 | |
| 3710 | 3710 | // 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 | 3712 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 3713 | 3713 | |
| 3714 | 3714 | // test completion |
| 3715 | 3715 | var cqe = try ring.copy_cqe(); |
| 3716 | try testing.expectEqual(os.E.SUCCESS, cqe.err()); | |
| 3717 | const fd: os.fd_t = @intCast(cqe.res); | |
| 3716 | try testing.expectEqual(posix.E.SUCCESS, cqe.err()); | |
| 3717 | const fd: posix.fd_t = @intCast(cqe.res); | |
| 3718 | 3718 | try testing.expect(fd > 2); |
| 3719 | 3719 | |
| 3720 | os.close(fd); | |
| 3720 | posix.close(fd); | |
| 3721 | 3721 | } |
| 3722 | 3722 | |
| 3723 | 3723 | test "socket_direct/socket_direct_alloc/close_direct" { |
| ... | ... | @@ -3730,29 +3730,29 @@ test "socket_direct/socket_direct_alloc/close_direct" { |
| 3730 | 3730 | }; |
| 3731 | 3731 | defer ring.deinit(); |
| 3732 | 3732 | |
| 3733 | var registered_fds = [_]os.fd_t{-1} ** 3; | |
| 3733 | var registered_fds = [_]posix.fd_t{-1} ** 3; | |
| 3734 | 3734 | try ring.register_files(registered_fds[0..]); |
| 3735 | 3735 | |
| 3736 | 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 | 3738 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 3739 | 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 | 3741 | try testing.expect(cqe_socket.res == 0); |
| 3742 | 3742 | |
| 3743 | 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 | 3745 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 3746 | 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 | 3748 | try testing.expect(cqe_socket.res == 0); // res is 0 when index is specified |
| 3749 | 3749 | |
| 3750 | 3750 | // create socket in kernel chosen file descriptor index (_alloc version) |
| 3751 | 3751 | // 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 | 3753 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 3754 | 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 | 3756 | try testing.expect(cqe_socket.res == 2); // returns registered file index |
| 3757 | 3757 | |
| 3758 | 3758 | // use sockets from registered_fds in connect operation |
| ... | ... | @@ -3782,17 +3782,17 @@ test "socket_direct/socket_direct_alloc/close_direct" { |
| 3782 | 3782 | } |
| 3783 | 3783 | // test connect completion |
| 3784 | 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 | 3786 | // test accept completion |
| 3787 | 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()); | |
| 3789 | 3789 | |
| 3790 | 3790 | // submit and test close_direct |
| 3791 | 3791 | _ = try ring.close_direct(close_userdata, @intCast(fd_index)); |
| 3792 | 3792 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 3793 | 3793 | var cqe_close = try ring.copy_cqe(); |
| 3794 | 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 | } |
| 3797 | 3797 | |
| 3798 | 3798 | try ring.unregister_files(); |
| ... | ... | @@ -3808,35 +3808,35 @@ test "openat_direct/close_direct" { |
| 3808 | 3808 | }; |
| 3809 | 3809 | defer ring.deinit(); |
| 3810 | 3810 | |
| 3811 | var registered_fds = [_]os.fd_t{-1} ** 3; | |
| 3811 | var registered_fds = [_]posix.fd_t{-1} ** 3; | |
| 3812 | 3812 | try ring.register_files(registered_fds[0..]); |
| 3813 | 3813 | |
| 3814 | 3814 | var tmp = std.testing.tmpDir(.{}); |
| 3815 | 3815 | defer tmp.cleanup(); |
| 3816 | 3816 | const path = "test_io_uring_close_direct"; |
| 3817 | 3817 | const flags: linux.O = .{ .ACCMODE = .RDWR, .CREAT = true }; |
| 3818 | const mode: os.mode_t = 0o666; | |
| 3818 | const mode: posix.mode_t = 0o666; | |
| 3819 | 3819 | const user_data: u64 = 0; |
| 3820 | 3820 | |
| 3821 | 3821 | // use registered file at index 0 (last param) |
| 3822 | 3822 | _ = try ring.openat_direct(user_data, tmp.dir.fd, path, flags, mode, 0); |
| 3823 | 3823 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 3824 | 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 | 3826 | try testing.expect(cqe.res == 0); |
| 3827 | 3827 | |
| 3828 | 3828 | // use registered file at index 1 |
| 3829 | 3829 | _ = try ring.openat_direct(user_data, tmp.dir.fd, path, flags, mode, 1); |
| 3830 | 3830 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 3831 | 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 | 3833 | try testing.expect(cqe.res == 0); // res is 0 when we specify index |
| 3834 | 3834 | |
| 3835 | 3835 | // let kernel choose registered file index |
| 3836 | 3836 | _ = try ring.openat_direct(user_data, tmp.dir.fd, path, flags, mode, linux.IORING_FILE_INDEX_ALLOC); |
| 3837 | 3837 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 3838 | 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 | 3840 | try testing.expect(cqe.res == 2); // chosen index is in res |
| 3841 | 3841 | |
| 3842 | 3842 | // close all open file descriptors |
| ... | ... | @@ -3844,7 +3844,7 @@ test "openat_direct/close_direct" { |
| 3844 | 3844 | _ = try ring.close_direct(user_data, @intCast(fd_index)); |
| 3845 | 3845 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 3846 | 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 | 3849 | try ring.unregister_files(); |
| 3850 | 3850 | } |
| ... | ... | @@ -3859,13 +3859,13 @@ test "waitid" { |
| 3859 | 3859 | }; |
| 3860 | 3860 | defer ring.deinit(); |
| 3861 | 3861 | |
| 3862 | const pid = try os.fork(); | |
| 3862 | const pid = try posix.fork(); | |
| 3863 | 3863 | if (pid == 0) { |
| 3864 | os.exit(7); | |
| 3864 | posix.exit(7); | |
| 3865 | 3865 | } |
| 3866 | 3866 | |
| 3867 | var siginfo: os.siginfo_t = undefined; | |
| 3868 | _ = try ring.waitid(0, .PID, pid, &siginfo, os.W.EXITED, 0); | |
| 3867 | var siginfo: posix.siginfo_t = undefined; | |
| 3868 | _ = try ring.waitid(0, .PID, pid, &siginfo, posix.W.EXITED, 0); | |
| 3869 | 3869 | |
| 3870 | 3870 | try testing.expectEqual(1, try ring.submit()); |
| 3871 | 3871 | |
| ... | ... | @@ -3877,13 +3877,13 @@ test "waitid" { |
| 3877 | 3877 | |
| 3878 | 3878 | /// For use in tests. Returns SkipZigTest if kernel version is less than required. |
| 3879 | 3879 | inline fn skipKernelLessThan(required: std.SemanticVersion) !void { |
| 3880 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 3880 | if (!is_linux) return error.SkipZigTest; | |
| 3881 | 3881 | |
| 3882 | 3882 | var uts: linux.utsname = undefined; |
| 3883 | 3883 | const res = linux.uname(&uts); |
| 3884 | switch (linux.getErrno(res)) { | |
| 3884 | switch (linux.E.init(res)) { | |
| 3885 | 3885 | .SUCCESS => {}, |
| 3886 | else => |errno| return os.unexpectedErrno(errno), | |
| 3886 | else => |errno| return posix.unexpectedErrno(errno), | |
| 3887 | 3887 | } |
| 3888 | 3888 | |
| 3889 | 3889 | const release = mem.sliceTo(&uts.release, 0); |
| ... | ... | @@ -3893,7 +3893,7 @@ inline fn skipKernelLessThan(required: std.SemanticVersion) !void { |
| 3893 | 3893 | } |
| 3894 | 3894 | |
| 3895 | 3895 | test BufferGroup { |
| 3896 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 3896 | if (!is_linux) return error.SkipZigTest; | |
| 3897 | 3897 | |
| 3898 | 3898 | // Init IoUring |
| 3899 | 3899 | var ring = IoUring.init(16, 0) catch |err| switch (err) { |
| ... | ... | @@ -3948,7 +3948,7 @@ test BufferGroup { |
| 3948 | 3948 | const cqe = try ring.copy_cqe(); |
| 3949 | 3949 | try testing.expectEqual(2, cqe.user_data); // matches submitted user_data |
| 3950 | 3950 | 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 | 3952 | try testing.expectEqual(data.len, @as(usize, @intCast(cqe.res))); // cqe.res holds received data len |
| 3953 | 3953 | |
| 3954 | 3954 | // Read buffer_id and used buffer len from cqe |
| ... | ... | @@ -3963,7 +3963,7 @@ test BufferGroup { |
| 3963 | 3963 | } |
| 3964 | 3964 | |
| 3965 | 3965 | test "ring mapped buffers recv" { |
| 3966 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 3966 | if (!is_linux) return error.SkipZigTest; | |
| 3967 | 3967 | |
| 3968 | 3968 | var ring = IoUring.init(16, 0) catch |err| switch (err) { |
| 3969 | 3969 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -4029,7 +4029,7 @@ test "ring mapped buffers recv" { |
| 4029 | 4029 | const cqe = try ring.copy_cqe(); |
| 4030 | 4030 | try testing.expectEqual(user_data, cqe.user_data); |
| 4031 | 4031 | 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 | 4033 | try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == 0); // IORING_CQE_F_BUFFER flags is set on success only |
| 4034 | 4034 | try testing.expectError(error.NoBufferSelected, cqe.buffer_id()); |
| 4035 | 4035 | } |
| ... | ... | @@ -4049,7 +4049,7 @@ test "ring mapped buffers recv" { |
| 4049 | 4049 | } |
| 4050 | 4050 | |
| 4051 | 4051 | test "ring mapped buffers multishot recv" { |
| 4052 | if (builtin.os.tag != .linux) return error.SkipZigTest; | |
| 4052 | if (!is_linux) return error.SkipZigTest; | |
| 4053 | 4053 | |
| 4054 | 4054 | var ring = IoUring.init(16, 0) catch |err| switch (err) { |
| 4055 | 4055 | error.SystemOutdated => return error.SkipZigTest, |
| ... | ... | @@ -4120,7 +4120,7 @@ test "ring mapped buffers multishot recv" { |
| 4120 | 4120 | const cqe = try ring.copy_cqe(); |
| 4121 | 4121 | try testing.expectEqual(recv_user_data, cqe.user_data); |
| 4122 | 4122 | 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 | 4124 | try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == 0); // IORING_CQE_F_BUFFER flags is set on success only |
| 4125 | 4125 | // has more is not set |
| 4126 | 4126 | // indicates that multishot is finished |
| ... | ... | @@ -4194,7 +4194,7 @@ test "ring mapped buffers multishot recv" { |
| 4194 | 4194 | fn expect_buf_grp_recv( |
| 4195 | 4195 | ring: *IoUring, |
| 4196 | 4196 | buf_grp: *BufferGroup, |
| 4197 | fd: os.fd_t, | |
| 4197 | fd: posix.fd_t, | |
| 4198 | 4198 | user_data: u64, |
| 4199 | 4199 | expected: []const u8, |
| 4200 | 4200 | ) !u16 { |
| ... | ... | @@ -4220,7 +4220,7 @@ fn expect_buf_grp_cqe( |
| 4220 | 4220 | try testing.expect(cqe.res >= 0); // success |
| 4221 | 4221 | try testing.expect(cqe.flags & linux.IORING_CQE_F_BUFFER == linux.IORING_CQE_F_BUFFER); // IORING_CQE_F_BUFFER flag is set |
| 4222 | 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()); | |
| 4224 | 4224 | |
| 4225 | 4225 | // get buffer from pool |
| 4226 | 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 | 2 | const maxInt = std.math.maxInt; |
| 3 | 3 | const linux = std.os.linux; |
| 4 | 4 | const SYS = linux.SYS; |
| 5 | const iovec = std.os.iovec; | |
| 6 | const iovec_const = std.os.iovec_const; | |
| 5 | const iovec = std.posix.iovec; | |
| 6 | const iovec_const = std.posix.iovec_const; | |
| 7 | 7 | const socklen_t = linux.socklen_t; |
| 8 | 8 | const stack_t = linux.stack_t; |
| 9 | 9 | const sigset_t = linux.sigset_t; |
lib/std/os/linux/arm64.zig+2-2| ... | ... | @@ -4,8 +4,8 @@ const linux = std.os.linux; |
| 4 | 4 | const SYS = linux.SYS; |
| 5 | 5 | const socklen_t = linux.socklen_t; |
| 6 | 6 | const sockaddr = linux.sockaddr; |
| 7 | const iovec = std.os.iovec; | |
| 8 | const iovec_const = std.os.iovec_const; | |
| 7 | const iovec = std.posix.iovec; | |
| 8 | const iovec_const = std.posix.iovec_const; | |
| 9 | 9 | const uid_t = linux.uid_t; |
| 10 | 10 | const gid_t = linux.gid_t; |
| 11 | 11 | const pid_t = linux.pid_t; |
lib/std/os/linux/bpf.zig+1-2| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | const std = @import("../../std.zig"); |
| 2 | const errno = getErrno; | |
| 2 | const errno = linux.E.init; | |
| 3 | 3 | const unexpectedErrno = std.os.unexpectedErrno; |
| 4 | 4 | const expectEqual = std.testing.expectEqual; |
| 5 | 5 | const expectError = std.testing.expectError; |
| ... | ... | @@ -8,7 +8,6 @@ const expect = std.testing.expect; |
| 8 | 8 | const linux = std.os.linux; |
| 9 | 9 | const fd_t = linux.fd_t; |
| 10 | 10 | const pid_t = linux.pid_t; |
| 11 | const getErrno = linux.getErrno; | |
| 12 | 11 | |
| 13 | 12 | pub const btf = @import("bpf/btf.zig"); |
| 14 | 13 | pub const kern = @import("bpf/kern.zig"); |
lib/std/os/linux/errno/generic.zig deleted-460| ... | ... | @@ -1,460 +0,0 @@ |
| 1 | pub 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. | |
| 2 | pub 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. | |
| 2 | pub 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 | 2 | //! Split into its own file to compartmentalize the initialization methods. |
| 3 | 3 | |
| 4 | 4 | const std = @import("../../std.zig"); |
| 5 | const os = std.os; | |
| 6 | const linux = os.linux; | |
| 5 | const linux = std.os.linux; | |
| 7 | 6 | |
| 8 | 7 | pub const io_uring_sqe = extern struct { |
| 9 | 8 | opcode: linux.IORING_OP, |
| ... | ... | @@ -40,7 +39,7 @@ pub const io_uring_sqe = extern struct { |
| 40 | 39 | }; |
| 41 | 40 | } |
| 42 | 41 | |
| 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 | 43 | sqe.* = .{ |
| 45 | 44 | .opcode = .FSYNC, |
| 46 | 45 | .flags = 0, |
| ... | ... | @@ -62,7 +61,7 @@ pub const io_uring_sqe = extern struct { |
| 62 | 61 | pub fn prep_rw( |
| 63 | 62 | sqe: *linux.io_uring_sqe, |
| 64 | 63 | op: linux.IORING_OP, |
| 65 | fd: os.fd_t, | |
| 64 | fd: linux.fd_t, | |
| 66 | 65 | addr: u64, |
| 67 | 66 | len: usize, |
| 68 | 67 | offset: u64, |
| ... | ... | @@ -85,15 +84,15 @@ pub const io_uring_sqe = extern struct { |
| 85 | 84 | }; |
| 86 | 85 | } |
| 87 | 86 | |
| 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 | 88 | sqe.prep_rw(.READ, fd, @intFromPtr(buffer.ptr), buffer.len, offset); |
| 90 | 89 | } |
| 91 | 90 | |
| 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 | 92 | sqe.prep_rw(.WRITE, fd, @intFromPtr(buffer.ptr), buffer.len, offset); |
| 94 | 93 | } |
| 95 | 94 | |
| 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 | 96 | sqe.prep_rw(.SPLICE, fd_out, undefined, len, off_out); |
| 98 | 97 | sqe.addr = off_in; |
| 99 | 98 | sqe.splice_fd_in = fd_in; |
| ... | ... | @@ -101,8 +100,8 @@ pub const io_uring_sqe = extern struct { |
| 101 | 100 | |
| 102 | 101 | pub fn prep_readv( |
| 103 | 102 | sqe: *linux.io_uring_sqe, |
| 104 | fd: os.fd_t, | |
| 105 | iovecs: []const os.iovec, | |
| 103 | fd: linux.fd_t, | |
| 104 | iovecs: []const std.posix.iovec, | |
| 106 | 105 | offset: u64, |
| 107 | 106 | ) void { |
| 108 | 107 | sqe.prep_rw(.READV, fd, @intFromPtr(iovecs.ptr), iovecs.len, offset); |
| ... | ... | @@ -110,28 +109,28 @@ pub const io_uring_sqe = extern struct { |
| 110 | 109 | |
| 111 | 110 | pub fn prep_writev( |
| 112 | 111 | sqe: *linux.io_uring_sqe, |
| 113 | fd: os.fd_t, | |
| 114 | iovecs: []const os.iovec_const, | |
| 112 | fd: linux.fd_t, | |
| 113 | iovecs: []const std.posix.iovec_const, | |
| 115 | 114 | offset: u64, |
| 116 | 115 | ) void { |
| 117 | 116 | sqe.prep_rw(.WRITEV, fd, @intFromPtr(iovecs.ptr), iovecs.len, offset); |
| 118 | 117 | } |
| 119 | 118 | |
| 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 | 120 | sqe.prep_rw(.READ_FIXED, fd, @intFromPtr(buffer.iov_base), buffer.iov_len, offset); |
| 122 | 121 | sqe.buf_index = buffer_index; |
| 123 | 122 | } |
| 124 | 123 | |
| 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 | 125 | sqe.prep_rw(.WRITE_FIXED, fd, @intFromPtr(buffer.iov_base), buffer.iov_len, offset); |
| 127 | 126 | sqe.buf_index = buffer_index; |
| 128 | 127 | } |
| 129 | 128 | |
| 130 | 129 | pub fn prep_accept( |
| 131 | 130 | sqe: *linux.io_uring_sqe, |
| 132 | fd: os.fd_t, | |
| 133 | addr: ?*os.sockaddr, | |
| 134 | addrlen: ?*os.socklen_t, | |
| 131 | fd: linux.fd_t, | |
| 132 | addr: ?*linux.sockaddr, | |
| 133 | addrlen: ?*linux.socklen_t, | |
| 135 | 134 | flags: u32, |
| 136 | 135 | ) void { |
| 137 | 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 | 141 | |
| 143 | 142 | pub fn prep_accept_direct( |
| 144 | 143 | sqe: *linux.io_uring_sqe, |
| 145 | fd: os.fd_t, | |
| 146 | addr: ?*os.sockaddr, | |
| 147 | addrlen: ?*os.socklen_t, | |
| 144 | fd: linux.fd_t, | |
| 145 | addr: ?*linux.sockaddr, | |
| 146 | addrlen: ?*linux.socklen_t, | |
| 148 | 147 | flags: u32, |
| 149 | 148 | file_index: u32, |
| 150 | 149 | ) void { |
| ... | ... | @@ -154,9 +153,9 @@ pub const io_uring_sqe = extern struct { |
| 154 | 153 | |
| 155 | 154 | pub fn prep_multishot_accept_direct( |
| 156 | 155 | sqe: *linux.io_uring_sqe, |
| 157 | fd: os.fd_t, | |
| 158 | addr: ?*os.sockaddr, | |
| 159 | addrlen: ?*os.socklen_t, | |
| 156 | fd: linux.fd_t, | |
| 157 | addr: ?*linux.sockaddr, | |
| 158 | addrlen: ?*linux.socklen_t, | |
| 160 | 159 | flags: u32, |
| 161 | 160 | ) void { |
| 162 | 161 | prep_multishot_accept(sqe, fd, addr, addrlen, flags); |
| ... | ... | @@ -177,9 +176,9 @@ pub const io_uring_sqe = extern struct { |
| 177 | 176 | |
| 178 | 177 | pub fn prep_connect( |
| 179 | 178 | sqe: *linux.io_uring_sqe, |
| 180 | fd: os.fd_t, | |
| 181 | addr: *const os.sockaddr, | |
| 182 | addrlen: os.socklen_t, | |
| 179 | fd: linux.fd_t, | |
| 180 | addr: *const linux.sockaddr, | |
| 181 | addrlen: linux.socklen_t, | |
| 183 | 182 | ) void { |
| 184 | 183 | // `addrlen` maps to `sqe.off` (u64) instead of `sqe.len` (which is only a u32). |
| 185 | 184 | sqe.prep_rw(.CONNECT, fd, @intFromPtr(addr), 0, addrlen); |
| ... | ... | @@ -187,22 +186,22 @@ pub const io_uring_sqe = extern struct { |
| 187 | 186 | |
| 188 | 187 | pub fn prep_epoll_ctl( |
| 189 | 188 | sqe: *linux.io_uring_sqe, |
| 190 | epfd: os.fd_t, | |
| 191 | fd: os.fd_t, | |
| 189 | epfd: linux.fd_t, | |
| 190 | fd: linux.fd_t, | |
| 192 | 191 | op: u32, |
| 193 | 192 | ev: ?*linux.epoll_event, |
| 194 | 193 | ) void { |
| 195 | 194 | sqe.prep_rw(.EPOLL_CTL, epfd, @intFromPtr(ev), op, @intCast(fd)); |
| 196 | 195 | } |
| 197 | 196 | |
| 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 | 198 | sqe.prep_rw(.RECV, fd, @intFromPtr(buffer.ptr), buffer.len, 0); |
| 200 | 199 | sqe.rw_flags = flags; |
| 201 | 200 | } |
| 202 | 201 | |
| 203 | 202 | pub fn prep_recv_multishot( |
| 204 | 203 | sqe: *linux.io_uring_sqe, |
| 205 | fd: os.fd_t, | |
| 204 | fd: linux.fd_t, | |
| 206 | 205 | buffer: []u8, |
| 207 | 206 | flags: u32, |
| 208 | 207 | ) void { |
| ... | ... | @@ -212,8 +211,8 @@ pub const io_uring_sqe = extern struct { |
| 212 | 211 | |
| 213 | 212 | pub fn prep_recvmsg( |
| 214 | 213 | sqe: *linux.io_uring_sqe, |
| 215 | fd: os.fd_t, | |
| 216 | msg: *os.msghdr, | |
| 214 | fd: linux.fd_t, | |
| 215 | msg: *linux.msghdr, | |
| 217 | 216 | flags: u32, |
| 218 | 217 | ) void { |
| 219 | 218 | sqe.prep_rw(.RECVMSG, fd, @intFromPtr(msg), 1, 0); |
| ... | ... | @@ -222,26 +221,26 @@ pub const io_uring_sqe = extern struct { |
| 222 | 221 | |
| 223 | 222 | pub fn prep_recvmsg_multishot( |
| 224 | 223 | sqe: *linux.io_uring_sqe, |
| 225 | fd: os.fd_t, | |
| 226 | msg: *os.msghdr, | |
| 224 | fd: linux.fd_t, | |
| 225 | msg: *linux.msghdr, | |
| 227 | 226 | flags: u32, |
| 228 | 227 | ) void { |
| 229 | 228 | sqe.prep_recvmsg(fd, msg, flags); |
| 230 | 229 | sqe.ioprio |= linux.IORING_RECV_MULTISHOT; |
| 231 | 230 | } |
| 232 | 231 | |
| 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 | 233 | sqe.prep_rw(.SEND, fd, @intFromPtr(buffer.ptr), buffer.len, 0); |
| 235 | 234 | sqe.rw_flags = flags; |
| 236 | 235 | } |
| 237 | 236 | |
| 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 | 238 | sqe.prep_rw(.SEND_ZC, fd, @intFromPtr(buffer.ptr), buffer.len, 0); |
| 240 | 239 | sqe.rw_flags = flags; |
| 241 | 240 | sqe.ioprio = zc_flags; |
| 242 | 241 | } |
| 243 | 242 | |
| 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 | 244 | prep_send_zc(sqe, fd, buffer, flags, zc_flags); |
| 246 | 245 | sqe.ioprio |= linux.IORING_RECVSEND_FIXED_BUF; |
| 247 | 246 | sqe.buf_index = buf_index; |
| ... | ... | @@ -249,8 +248,8 @@ pub const io_uring_sqe = extern struct { |
| 249 | 248 | |
| 250 | 249 | pub fn prep_sendmsg_zc( |
| 251 | 250 | sqe: *linux.io_uring_sqe, |
| 252 | fd: os.fd_t, | |
| 253 | msg: *const os.msghdr_const, | |
| 251 | fd: linux.fd_t, | |
| 252 | msg: *const linux.msghdr_const, | |
| 254 | 253 | flags: u32, |
| 255 | 254 | ) void { |
| 256 | 255 | prep_sendmsg(sqe, fd, msg, flags); |
| ... | ... | @@ -259,8 +258,8 @@ pub const io_uring_sqe = extern struct { |
| 259 | 258 | |
| 260 | 259 | pub fn prep_sendmsg( |
| 261 | 260 | sqe: *linux.io_uring_sqe, |
| 262 | fd: os.fd_t, | |
| 263 | msg: *const os.msghdr_const, | |
| 261 | fd: linux.fd_t, | |
| 262 | msg: *const linux.msghdr_const, | |
| 264 | 263 | flags: u32, |
| 265 | 264 | ) void { |
| 266 | 265 | sqe.prep_rw(.SENDMSG, fd, @intFromPtr(msg), 1, 0); |
| ... | ... | @@ -269,10 +268,10 @@ pub const io_uring_sqe = extern struct { |
| 269 | 268 | |
| 270 | 269 | pub fn prep_openat( |
| 271 | 270 | sqe: *linux.io_uring_sqe, |
| 272 | fd: os.fd_t, | |
| 271 | fd: linux.fd_t, | |
| 273 | 272 | path: [*:0]const u8, |
| 274 | 273 | flags: linux.O, |
| 275 | mode: os.mode_t, | |
| 274 | mode: linux.mode_t, | |
| 276 | 275 | ) void { |
| 277 | 276 | sqe.prep_rw(.OPENAT, fd, @intFromPtr(path), mode, 0); |
| 278 | 277 | sqe.rw_flags = @bitCast(flags); |
| ... | ... | @@ -280,17 +279,17 @@ pub const io_uring_sqe = extern struct { |
| 280 | 279 | |
| 281 | 280 | pub fn prep_openat_direct( |
| 282 | 281 | sqe: *linux.io_uring_sqe, |
| 283 | fd: os.fd_t, | |
| 282 | fd: linux.fd_t, | |
| 284 | 283 | path: [*:0]const u8, |
| 285 | 284 | flags: linux.O, |
| 286 | mode: os.mode_t, | |
| 285 | mode: linux.mode_t, | |
| 287 | 286 | file_index: u32, |
| 288 | 287 | ) void { |
| 289 | 288 | prep_openat(sqe, fd, path, flags, mode); |
| 290 | 289 | __io_uring_set_target_fixed_file(sqe, file_index); |
| 291 | 290 | } |
| 292 | 291 | |
| 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 | 293 | sqe.* = .{ |
| 295 | 294 | .opcode = .CLOSE, |
| 296 | 295 | .flags = 0, |
| ... | ... | @@ -316,7 +315,7 @@ pub const io_uring_sqe = extern struct { |
| 316 | 315 | |
| 317 | 316 | pub fn prep_timeout( |
| 318 | 317 | sqe: *linux.io_uring_sqe, |
| 319 | ts: *const os.linux.kernel_timespec, | |
| 318 | ts: *const linux.kernel_timespec, | |
| 320 | 319 | count: u32, |
| 321 | 320 | flags: u32, |
| 322 | 321 | ) void { |
| ... | ... | @@ -345,7 +344,7 @@ pub const io_uring_sqe = extern struct { |
| 345 | 344 | |
| 346 | 345 | pub fn prep_link_timeout( |
| 347 | 346 | sqe: *linux.io_uring_sqe, |
| 348 | ts: *const os.linux.kernel_timespec, | |
| 347 | ts: *const linux.kernel_timespec, | |
| 349 | 348 | flags: u32, |
| 350 | 349 | ) void { |
| 351 | 350 | sqe.prep_rw(.LINK_TIMEOUT, -1, @intFromPtr(ts), 1, 0); |
| ... | ... | @@ -354,7 +353,7 @@ pub const io_uring_sqe = extern struct { |
| 354 | 353 | |
| 355 | 354 | pub fn prep_poll_add( |
| 356 | 355 | sqe: *linux.io_uring_sqe, |
| 357 | fd: os.fd_t, | |
| 356 | fd: linux.fd_t, | |
| 358 | 357 | poll_mask: u32, |
| 359 | 358 | ) void { |
| 360 | 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 | 392 | |
| 394 | 393 | pub fn prep_fallocate( |
| 395 | 394 | sqe: *linux.io_uring_sqe, |
| 396 | fd: os.fd_t, | |
| 395 | fd: linux.fd_t, | |
| 397 | 396 | mode: i32, |
| 398 | 397 | offset: u64, |
| 399 | 398 | len: u64, |
| ... | ... | @@ -418,7 +417,7 @@ pub const io_uring_sqe = extern struct { |
| 418 | 417 | |
| 419 | 418 | pub fn prep_statx( |
| 420 | 419 | sqe: *linux.io_uring_sqe, |
| 421 | fd: os.fd_t, | |
| 420 | fd: linux.fd_t, | |
| 422 | 421 | path: [*:0]const u8, |
| 423 | 422 | flags: u32, |
| 424 | 423 | mask: u32, |
| ... | ... | @@ -439,7 +438,7 @@ pub const io_uring_sqe = extern struct { |
| 439 | 438 | |
| 440 | 439 | pub fn prep_shutdown( |
| 441 | 440 | sqe: *linux.io_uring_sqe, |
| 442 | sockfd: os.socket_t, | |
| 441 | sockfd: linux.socket_t, | |
| 443 | 442 | how: u32, |
| 444 | 443 | ) void { |
| 445 | 444 | sqe.prep_rw(.SHUTDOWN, sockfd, 0, how, 0); |
| ... | ... | @@ -447,9 +446,9 @@ pub const io_uring_sqe = extern struct { |
| 447 | 446 | |
| 448 | 447 | pub fn prep_renameat( |
| 449 | 448 | sqe: *linux.io_uring_sqe, |
| 450 | old_dir_fd: os.fd_t, | |
| 449 | old_dir_fd: linux.fd_t, | |
| 451 | 450 | old_path: [*:0]const u8, |
| 452 | new_dir_fd: os.fd_t, | |
| 451 | new_dir_fd: linux.fd_t, | |
| 453 | 452 | new_path: [*:0]const u8, |
| 454 | 453 | flags: u32, |
| 455 | 454 | ) void { |
| ... | ... | @@ -466,7 +465,7 @@ pub const io_uring_sqe = extern struct { |
| 466 | 465 | |
| 467 | 466 | pub fn prep_unlinkat( |
| 468 | 467 | sqe: *linux.io_uring_sqe, |
| 469 | dir_fd: os.fd_t, | |
| 468 | dir_fd: linux.fd_t, | |
| 470 | 469 | path: [*:0]const u8, |
| 471 | 470 | flags: u32, |
| 472 | 471 | ) void { |
| ... | ... | @@ -476,9 +475,9 @@ pub const io_uring_sqe = extern struct { |
| 476 | 475 | |
| 477 | 476 | pub fn prep_mkdirat( |
| 478 | 477 | sqe: *linux.io_uring_sqe, |
| 479 | dir_fd: os.fd_t, | |
| 478 | dir_fd: linux.fd_t, | |
| 480 | 479 | path: [*:0]const u8, |
| 481 | mode: os.mode_t, | |
| 480 | mode: linux.mode_t, | |
| 482 | 481 | ) void { |
| 483 | 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 | 485 | pub fn prep_symlinkat( |
| 487 | 486 | sqe: *linux.io_uring_sqe, |
| 488 | 487 | target: [*:0]const u8, |
| 489 | new_dir_fd: os.fd_t, | |
| 488 | new_dir_fd: linux.fd_t, | |
| 490 | 489 | link_path: [*:0]const u8, |
| 491 | 490 | ) void { |
| 492 | 491 | sqe.prep_rw( |
| ... | ... | @@ -500,9 +499,9 @@ pub const io_uring_sqe = extern struct { |
| 500 | 499 | |
| 501 | 500 | pub fn prep_linkat( |
| 502 | 501 | sqe: *linux.io_uring_sqe, |
| 503 | old_dir_fd: os.fd_t, | |
| 502 | old_dir_fd: linux.fd_t, | |
| 504 | 503 | old_path: [*:0]const u8, |
| 505 | new_dir_fd: os.fd_t, | |
| 504 | new_dir_fd: linux.fd_t, | |
| 506 | 505 | new_path: [*:0]const u8, |
| 507 | 506 | flags: u32, |
| 508 | 507 | ) void { |
| ... | ... | @@ -541,9 +540,9 @@ pub const io_uring_sqe = extern struct { |
| 541 | 540 | |
| 542 | 541 | pub fn prep_multishot_accept( |
| 543 | 542 | sqe: *linux.io_uring_sqe, |
| 544 | fd: os.fd_t, | |
| 545 | addr: ?*os.sockaddr, | |
| 546 | addrlen: ?*os.socklen_t, | |
| 543 | fd: linux.fd_t, | |
| 544 | addr: ?*linux.sockaddr, | |
| 545 | addrlen: ?*linux.socklen_t, | |
| 547 | 546 | flags: u32, |
| 548 | 547 | ) void { |
| 549 | 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 | 3 | const linux = std.os.linux; |
| 4 | 4 | const SYS = linux.SYS; |
| 5 | 5 | const socklen_t = linux.socklen_t; |
| 6 | const iovec = std.os.iovec; | |
| 7 | const iovec_const = std.os.iovec_const; | |
| 6 | const iovec = std.posix.iovec; | |
| 7 | const iovec_const = std.posix.iovec_const; | |
| 8 | 8 | const uid_t = linux.uid_t; |
| 9 | 9 | const gid_t = linux.gid_t; |
| 10 | 10 | const pid_t = linux.pid_t; |
lib/std/os/linux/mips64.zig+2-2| ... | ... | @@ -3,8 +3,8 @@ const maxInt = std.math.maxInt; |
| 3 | 3 | const linux = std.os.linux; |
| 4 | 4 | const SYS = linux.SYS; |
| 5 | 5 | const socklen_t = linux.socklen_t; |
| 6 | const iovec = std.os.iovec; | |
| 7 | const iovec_const = std.os.iovec_const; | |
| 6 | const iovec = std.posix.iovec; | |
| 7 | const iovec_const = std.posix.iovec_const; | |
| 8 | 8 | const uid_t = linux.uid_t; |
| 9 | 9 | const gid_t = linux.gid_t; |
| 10 | 10 | const pid_t = linux.pid_t; |
lib/std/os/linux/powerpc.zig+2-2| ... | ... | @@ -3,8 +3,8 @@ const maxInt = std.math.maxInt; |
| 3 | 3 | const linux = std.os.linux; |
| 4 | 4 | const SYS = linux.SYS; |
| 5 | 5 | const socklen_t = linux.socklen_t; |
| 6 | const iovec = std.os.iovec; | |
| 7 | const iovec_const = std.os.iovec_const; | |
| 6 | const iovec = std.posix.iovec; | |
| 7 | const iovec_const = std.posix.iovec_const; | |
| 8 | 8 | const uid_t = linux.uid_t; |
| 9 | 9 | const gid_t = linux.gid_t; |
| 10 | 10 | const pid_t = linux.pid_t; |
lib/std/os/linux/powerpc64.zig+2-2| ... | ... | @@ -3,8 +3,8 @@ const maxInt = std.math.maxInt; |
| 3 | 3 | const linux = std.os.linux; |
| 4 | 4 | const SYS = linux.SYS; |
| 5 | 5 | const socklen_t = linux.socklen_t; |
| 6 | const iovec = std.os.iovec; | |
| 7 | const iovec_const = std.os.iovec_const; | |
| 6 | const iovec = std.posix.iovec; | |
| 7 | const iovec_const = std.posix.iovec_const; | |
| 8 | 8 | const uid_t = linux.uid_t; |
| 9 | 9 | const gid_t = linux.gid_t; |
| 10 | 10 | const pid_t = linux.pid_t; |
lib/std/os/linux/riscv64.zig+2-2| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | const std = @import("../../std.zig"); |
| 2 | const iovec = std.os.iovec; | |
| 3 | const iovec_const = std.os.iovec_const; | |
| 2 | const iovec = std.posix.iovec; | |
| 3 | const iovec_const = std.posix.iovec_const; | |
| 4 | 4 | const linux = std.os.linux; |
| 5 | 5 | const SYS = linux.SYS; |
| 6 | 6 | const 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 | 10 | const SYS = linux.SYS; |
| 11 | 11 | const sockaddr = linux.sockaddr; |
| 12 | 12 | const socklen_t = linux.socklen_t; |
| 13 | const iovec = std.os.iovec; | |
| 14 | const iovec_const = std.os.iovec_const; | |
| 13 | const iovec = std.posix.iovec; | |
| 14 | const iovec_const = std.posix.iovec_const; | |
| 15 | 15 | const timespec = linux.timespec; |
| 16 | 16 | |
| 17 | 17 | pub 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 | 81 | break :base @intFromPtr(dynv) - phdr.p_vaddr; |
| 82 | 82 | } |
| 83 | 83 | // This is not supposed to happen for well-formed binaries. |
| 84 | std.os.abort(); | |
| 84 | @trap(); | |
| 85 | 85 | }; |
| 86 | 86 | |
| 87 | 87 | var rel_addr: usize = 0; |
lib/std/os/linux/test.zig+8-8| ... | ... | @@ -18,7 +18,7 @@ test "fallocate" { |
| 18 | 18 | try expect((try file.stat()).size == 0); |
| 19 | 19 | |
| 20 | 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 | 22 | .SUCCESS => {}, |
| 23 | 23 | .NOSYS => return error.SkipZigTest, |
| 24 | 24 | .OPNOTSUPP => return error.SkipZigTest, |
| ... | ... | @@ -34,11 +34,11 @@ test "getpid" { |
| 34 | 34 | |
| 35 | 35 | test "timer" { |
| 36 | 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 | 38 | try expect(err == .SUCCESS); |
| 39 | 39 | |
| 40 | 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); | |
| 42 | 42 | |
| 43 | 43 | const time_interval = linux.timespec{ |
| 44 | 44 | .tv_sec = 0, |
| ... | ... | @@ -50,7 +50,7 @@ test "timer" { |
| 50 | 50 | .it_value = time_interval, |
| 51 | 51 | }; |
| 52 | 52 | |
| 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 | 54 | try expect(err == .SUCCESS); |
| 55 | 55 | |
| 56 | 56 | var event = linux.epoll_event{ |
| ... | ... | @@ -58,13 +58,13 @@ test "timer" { |
| 58 | 58 | .data = linux.epoll_data{ .ptr = 0 }, |
| 59 | 59 | }; |
| 60 | 60 | |
| 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 | 62 | try expect(err == .SUCCESS); |
| 63 | 63 | |
| 64 | 64 | const events_one: linux.epoll_event = undefined; |
| 65 | 65 | var events = [_]linux.epoll_event{events_one} ** 8; |
| 66 | 66 | |
| 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 | 68 | try expect(err == .SUCCESS); |
| 69 | 69 | } |
| 70 | 70 | |
| ... | ... | @@ -77,7 +77,7 @@ test "statx" { |
| 77 | 77 | defer file.close(); |
| 78 | 78 | |
| 79 | 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 | 81 | .SUCCESS => {}, |
| 82 | 82 | // The statx syscall was only introduced in linux 4.11 |
| 83 | 83 | .NOSYS => return error.SkipZigTest, |
| ... | ... | @@ -85,7 +85,7 @@ test "statx" { |
| 85 | 85 | } |
| 86 | 86 | |
| 87 | 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 | 89 | .SUCCESS => {}, |
| 90 | 90 | else => unreachable, |
| 91 | 91 | } |
lib/std/os/linux/tls.zig+10-9| ... | ... | @@ -1,10 +1,11 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const os = std.os; | |
| 3 | 2 | const mem = std.mem; |
| 4 | 3 | const elf = std.elf; |
| 5 | 4 | const math = std.math; |
| 6 | 5 | const assert = std.debug.assert; |
| 7 | 6 | const native_arch = @import("builtin").cpu.arch; |
| 7 | const linux = std.os.linux; | |
| 8 | const posix = std.posix; | |
| 8 | 9 | |
| 9 | 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 | 112 | pub fn setThreadPointer(addr: usize) void { |
| 112 | 113 | switch (native_arch) { |
| 113 | 114 | .x86 => { |
| 114 | var user_desc = std.os.linux.user_desc{ | |
| 115 | var user_desc: linux.user_desc = .{ | |
| 115 | 116 | .entry_number = tls_image.gdt_entry_number, |
| 116 | 117 | .base_addr = addr, |
| 117 | 118 | .limit = 0xfffff, |
| ... | ... | @@ -124,7 +125,7 @@ pub fn setThreadPointer(addr: usize) void { |
| 124 | 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 | 129 | assert(rc == 0); |
| 129 | 130 | |
| 130 | 131 | const gdt_entry_number = user_desc.entry_number; |
| ... | ... | @@ -137,7 +138,7 @@ pub fn setThreadPointer(addr: usize) void { |
| 137 | 138 | ); |
| 138 | 139 | }, |
| 139 | 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 | 142 | assert(rc == 0); |
| 142 | 143 | }, |
| 143 | 144 | .aarch64, .aarch64_be => { |
| ... | ... | @@ -148,7 +149,7 @@ pub fn setThreadPointer(addr: usize) void { |
| 148 | 149 | ); |
| 149 | 150 | }, |
| 150 | 151 | .arm, .thumb => { |
| 151 | const rc = std.os.linux.syscall1(.set_tls, addr); | |
| 152 | const rc = linux.syscall1(.set_tls, addr); | |
| 152 | 153 | assert(rc == 0); |
| 153 | 154 | }, |
| 154 | 155 | .riscv64 => { |
| ... | ... | @@ -159,7 +160,7 @@ pub fn setThreadPointer(addr: usize) void { |
| 159 | 160 | ); |
| 160 | 161 | }, |
| 161 | 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 | 164 | assert(rc == 0); |
| 164 | 165 | }, |
| 165 | 166 | .powerpc, .powerpcle => { |
| ... | ... | @@ -320,14 +321,14 @@ pub fn initStaticTLS(phdrs: []elf.Phdr) void { |
| 320 | 321 | break :blk main_thread_tls_buffer[0..tls_image.alloc_size]; |
| 321 | 322 | } |
| 322 | 323 | |
| 323 | const alloc_tls_area = os.mmap( | |
| 324 | const alloc_tls_area = posix.mmap( | |
| 324 | 325 | null, |
| 325 | 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 | 328 | .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, |
| 328 | 329 | -1, |
| 329 | 330 | 0, |
| 330 | ) catch os.abort(); | |
| 331 | ) catch posix.abort(); | |
| 331 | 332 | |
| 332 | 333 | // Make sure the slice is correctly aligned. |
| 333 | 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 | 5 | const maxInt = std.math.maxInt; |
| 6 | 6 | |
| 7 | 7 | pub 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 | 9 | if (vdso_addr == 0) return 0; |
| 10 | 10 | |
| 11 | 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 | 3 | const linux = std.os.linux; |
| 4 | 4 | const SYS = linux.SYS; |
| 5 | 5 | const socklen_t = linux.socklen_t; |
| 6 | const iovec = std.os.iovec; | |
| 7 | const iovec_const = std.os.iovec_const; | |
| 6 | const iovec = std.posix.iovec; | |
| 7 | const iovec_const = std.posix.iovec_const; | |
| 8 | 8 | const uid_t = linux.uid_t; |
| 9 | 9 | const gid_t = linux.gid_t; |
| 10 | 10 | const pid_t = linux.pid_t; |
lib/std/os/linux/x86_64.zig+2-2| ... | ... | @@ -2,8 +2,8 @@ const std = @import("../../std.zig"); |
| 2 | 2 | const maxInt = std.math.maxInt; |
| 3 | 3 | const linux = std.os.linux; |
| 4 | 4 | const SYS = linux.SYS; |
| 5 | const iovec = std.os.iovec; | |
| 6 | const iovec_const = std.os.iovec_const; | |
| 5 | const iovec = std.posix.iovec; | |
| 6 | const iovec_const = std.posix.iovec_const; | |
| 7 | 7 | |
| 8 | 8 | const pid_t = linux.pid_t; |
| 9 | 9 | const 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 | 11 | .x86_64 => @import("plan9/x86_64.zig"), |
| 12 | 12 | else => @compileError("more plan9 syscall implementations (needs more inline asm in stage2"), |
| 13 | 13 | }; |
| 14 | pub const E = @import("plan9/errno.zig").E; | |
| 15 | /// Get the errno from a syscall return value, or 0 for no error. | |
| 16 | pub fn getErrno(r: usize) E { | |
| 17 | const signed_r = @as(isize, @bitCast(r)); | |
| 18 | const int = if (signed_r > -4096 and signed_r < 0) -signed_r else 0; | |
| 19 | return @as(E, @enumFromInt(int)); | |
| 20 | } | |
| 14 | /// Ported from /sys/include/ape/errno.h | |
| 15 | pub const E = enum(u16) { | |
| 16 | SUCCESS = 0, | |
| 17 | DOM = 1000, | |
| 18 | RANGE = 1001, | |
| 19 | PLAN9 = 1002, | |
| 20 | ||
| 21 | @"2BIG" = 1, | |
| 22 | ACCES = 2, | |
| 23 | AGAIN = 3, | |
| 24 | // WOULDBLOCK = 3, // TODO errno.h has 2 names for 3 | |
| 25 | BADF = 4, | |
| 26 | BUSY = 5, | |
| 27 | CHILD = 6, | |
| 28 | DEADLK = 7, | |
| 29 | EXIST = 8, | |
| 30 | FAULT = 9, | |
| 31 | FBIG = 10, | |
| 32 | INTR = 11, | |
| 33 | INVAL = 12, | |
| 34 | IO = 13, | |
| 35 | ISDIR = 14, | |
| 36 | MFILE = 15, | |
| 37 | MLINK = 16, | |
| 38 | NAMETOOLONG = 17, | |
| 39 | NFILE = 18, | |
| 40 | NODEV = 19, | |
| 41 | NOENT = 20, | |
| 42 | NOEXEC = 21, | |
| 43 | NOLCK = 22, | |
| 44 | NOMEM = 23, | |
| 45 | NOSPC = 24, | |
| 46 | NOSYS = 25, | |
| 47 | NOTDIR = 26, | |
| 48 | NOTEMPTY = 27, | |
| 49 | NOTTY = 28, | |
| 50 | NXIO = 29, | |
| 51 | PERM = 30, | |
| 52 | PIPE = 31, | |
| 53 | ROFS = 32, | |
| 54 | SPIPE = 33, | |
| 55 | SRCH = 34, | |
| 56 | XDEV = 35, | |
| 57 | ||
| 58 | // bsd networking software | |
| 59 | NOTSOCK = 36, | |
| 60 | PROTONOSUPPORT = 37, | |
| 61 | // PROTOTYPE = 37, // TODO errno.h has two names for 37 | |
| 62 | CONNREFUSED = 38, | |
| 63 | AFNOSUPPORT = 39, | |
| 64 | NOBUFS = 40, | |
| 65 | OPNOTSUPP = 41, | |
| 66 | ADDRINUSE = 42, | |
| 67 | DESTADDRREQ = 43, | |
| 68 | MSGSIZE = 44, | |
| 69 | NOPROTOOPT = 45, | |
| 70 | SOCKTNOSUPPORT = 46, | |
| 71 | PFNOSUPPORT = 47, | |
| 72 | ADDRNOTAVAIL = 48, | |
| 73 | NETDOWN = 49, | |
| 74 | NETUNREACH = 50, | |
| 75 | NETRESET = 51, | |
| 76 | CONNABORTED = 52, | |
| 77 | ISCONN = 53, | |
| 78 | NOTCONN = 54, | |
| 79 | SHUTDOWN = 55, | |
| 80 | TOOMANYREFS = 56, | |
| 81 | TIMEDOUT = 57, | |
| 82 | HOSTDOWN = 58, | |
| 83 | HOSTUNREACH = 59, | |
| 84 | GREG = 60, | |
| 85 | ||
| 86 | // These added in 1003.1b-1993 | |
| 87 | CANCELED = 61, | |
| 88 | INPROGRESS = 62, | |
| 89 | ||
| 90 | // We just add these to be compatible with std.os, which uses them, | |
| 91 | // They should never get used. | |
| 92 | DQUOT, | |
| 93 | CONNRESET, | |
| 94 | OVERFLOW, | |
| 95 | LOOP, | |
| 96 | TXTBSY, | |
| 97 | ||
| 98 | pub fn init(r: usize) E { | |
| 99 | const signed_r: isize = @bitCast(r); | |
| 100 | const int = if (signed_r > -4096 and signed_r < 0) -signed_r else 0; | |
| 101 | return @enumFromInt(int); | |
| 102 | } | |
| 103 | }; | |
| 21 | 104 | // The max bytes that can be in the errstr buff |
| 22 | 105 | pub const ERRMAX = 128; |
| 23 | 106 | var 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 | |
| 2 | pub 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 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const os = std.os; | |
| 3 | const testing = std.testing; | |
| 4 | const expect = testing.expect; | |
| 5 | const expectEqual = testing.expectEqual; | |
| 6 | const expectError = testing.expectError; | |
| 7 | const io = std.io; | |
| 8 | const fs = std.fs; | |
| 9 | const mem = std.mem; | |
| 10 | const elf = std.elf; | |
| 11 | const File = std.fs.File; | |
| 12 | const Thread = std.Thread; | |
| 13 | ||
| 14 | const a = std.testing.allocator; | |
| 15 | ||
| 16 | const builtin = @import("builtin"); | |
| 17 | const AtomicRmwOp = std.builtin.AtomicRmwOp; | |
| 18 | const AtomicOrder = std.builtin.AtomicOrder; | |
| 19 | const native_os = builtin.target.os.tag; | |
| 20 | const tmpDir = std.testing.tmpDir; | |
| 21 | const Dir = std.fs.Dir; | |
| 22 | const ArenaAllocator = std.heap.ArenaAllocator; | |
| 23 | ||
| 24 | test "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 | ||
| 88 | test "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 | ||
| 143 | test "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 | ||
| 199 | test "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 | ||
| 240 | test "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 | ||
| 248 | fn 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 | ||
| 254 | test "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 | ||
| 297 | test "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 | ||
| 341 | test "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 | ||
| 363 | test "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 | ||
| 392 | fn testThreadIdFn(thread_id: *Thread.Id) void { | |
| 393 | thread_id.* = Thread.getCurrentId(); | |
| 394 | } | |
| 395 | ||
| 396 | test "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 | ||
| 405 | test "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 | ||
| 423 | fn start1() u8 { | |
| 424 | return 0; | |
| 425 | } | |
| 426 | ||
| 427 | fn start2(ctx: *i32) u8 { | |
| 428 | _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.seq_cst); | |
| 429 | return 0; | |
| 430 | } | |
| 431 | ||
| 432 | test "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 | ||
| 439 | test "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 | ||
| 449 | threadlocal var x: i32 = 1234; | |
| 450 | fn testTls() !void { | |
| 451 | if (x != 1234) return error.TlsBadStartValue; | |
| 452 | x += 1; | |
| 453 | if (x != 1235) return error.TlsBadEndValue; | |
| 454 | } | |
| 455 | ||
| 456 | test "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 | ||
| 466 | test "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 | ||
| 472 | test "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 | |
| 485 | const dl_phdr_info = if (@hasDecl(os.system, "dl_phdr_info")) os.dl_phdr_info else anyopaque; | |
| 486 | ||
| 487 | const IterFnError = error{ | |
| 488 | MissingPtLoadSegment, | |
| 489 | MissingLoad, | |
| 490 | BadElfMagic, | |
| 491 | FailedConsistencyCheck, | |
| 492 | }; | |
| 493 | ||
| 494 | fn 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 | ||
| 526 | test "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 | ||
| 534 | test "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 | ||
| 543 | test "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 | ||
| 556 | test "argsAlloc" { | |
| 557 | const args = try std.process.argsAlloc(std.testing.allocator); | |
| 558 | std.process.argsFree(std.testing.allocator, args); | |
| 559 | } | |
| 560 | ||
| 561 | test "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 | ||
| 587 | test "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 | ||
| 684 | test "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 | ||
| 697 | test "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 | ||
| 729 | test "signalfd" { | |
| 730 | switch (native_os) { | |
| 731 | .linux, .solaris, .illumos => {}, | |
| 732 | else => return error.SkipZigTest, | |
| 733 | } | |
| 734 | _ = &os.signalfd; | |
| 735 | } | |
| 736 | ||
| 737 | test "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 | ||
| 755 | test "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 | ||
| 775 | test "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 | ||
| 806 | test "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 | ||
| 825 | test "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 | ||
| 900 | test "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 | ||
| 932 | test "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 | ||
| 946 | test "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 | ||
| 1011 | test "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 | ||
| 1068 | test "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 | ||
| 1115 | test "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 | ||
| 1134 | test "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 | ||
| 1144 | test "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 | ||
| 1169 | test "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 | ||
| 1194 | test "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 | ||
| 1219 | test "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 | ||
| 1244 | fn 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 | ||
| 1249 | test "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 | 17 | // assert(@alignOf(u64) == 8); |
| 18 | 18 | } |
| 19 | 19 | |
| 20 | pub const iovec_t = std.os.iovec; | |
| 21 | pub const ciovec_t = std.os.iovec_const; | |
| 20 | pub const iovec_t = std.posix.iovec; | |
| 21 | pub const ciovec_t = std.posix.iovec_const; | |
| 22 | 22 | |
| 23 | 23 | pub extern "wasi_snapshot_preview1" fn args_get(argv: [*][*:0]u8, argv_buf: [*]u8) errno_t; |
| 24 | 24 | pub 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 | 11 | const math = std.math; |
| 12 | 12 | const maxInt = std.math.maxInt; |
| 13 | 13 | const native_arch = builtin.cpu.arch; |
| 14 | const UnexpectedError = std.posix.UnexpectedError; | |
| 14 | 15 | |
| 15 | 16 | test { |
| 16 | 17 | if (builtin.os.tag == .windows) { |
| ... | ... | @@ -547,7 +548,7 @@ pub const GetQueuedCompletionStatusError = error{ |
| 547 | 548 | Cancelled, |
| 548 | 549 | EOF, |
| 549 | 550 | Timeout, |
| 550 | } || std.os.UnexpectedError; | |
| 551 | } || UnexpectedError; | |
| 551 | 552 | |
| 552 | 553 | pub fn GetQueuedCompletionStatusEx( |
| 553 | 554 | completion_port: HANDLE, |
| ... | ... | @@ -1701,7 +1702,7 @@ pub fn VirtualProtectEx(handle: HANDLE, addr: ?LPVOID, size: SIZE_T, new_prot: D |
| 1701 | 1702 | .SUCCESS => return old_prot, |
| 1702 | 1703 | .INVALID_ADDRESS => return error.InvalidAddress, |
| 1703 | 1704 | // TODO: map errors |
| 1704 | else => |rc| return std.os.windows.unexpectedStatus(rc), | |
| 1705 | else => |rc| return unexpectedStatus(rc), | |
| 1705 | 1706 | } |
| 1706 | 1707 | } |
| 1707 | 1708 | |
| ... | ... | @@ -1946,7 +1947,7 @@ pub fn SetFileTime( |
| 1946 | 1947 | pub const LockFileError = error{ |
| 1947 | 1948 | SystemResources, |
| 1948 | 1949 | WouldBlock, |
| 1949 | } || std.os.UnexpectedError; | |
| 1950 | } || UnexpectedError; | |
| 1950 | 1951 | |
| 1951 | 1952 | pub fn LockFile( |
| 1952 | 1953 | FileHandle: HANDLE, |
| ... | ... | @@ -1983,7 +1984,7 @@ pub fn LockFile( |
| 1983 | 1984 | |
| 1984 | 1985 | pub const UnlockFileError = error{ |
| 1985 | 1986 | RangeNotLocked, |
| 1986 | } || std.os.UnexpectedError; | |
| 1987 | } || UnexpectedError; | |
| 1987 | 1988 | |
| 1988 | 1989 | pub fn UnlockFile( |
| 1989 | 1990 | FileHandle: HANDLE, |
| ... | ... | @@ -2672,8 +2673,8 @@ pub fn loadWinsockExtensionFunction(comptime T: type, sock: ws2_32.SOCKET, guid: |
| 2672 | 2673 | |
| 2673 | 2674 | /// Call this when you made a windows DLL call or something that does SetLastError |
| 2674 | 2675 | /// and you get an unexpected error. |
| 2675 | pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError { | |
| 2676 | if (std.os.unexpected_error_tracing) { | |
| 2676 | pub fn unexpectedError(err: Win32Error) UnexpectedError { | |
| 2677 | if (std.posix.unexpected_error_tracing) { | |
| 2677 | 2678 | // 614 is the length of the longest windows error description |
| 2678 | 2679 | var buf_wstr: [614]WCHAR = undefined; |
| 2679 | 2680 | const len = kernel32.FormatMessageW( |
| ... | ... | @@ -2694,14 +2695,14 @@ pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError { |
| 2694 | 2695 | return error.Unexpected; |
| 2695 | 2696 | } |
| 2696 | 2697 | |
| 2697 | pub fn unexpectedWSAError(err: ws2_32.WinsockError) std.os.UnexpectedError { | |
| 2698 | pub fn unexpectedWSAError(err: ws2_32.WinsockError) UnexpectedError { | |
| 2698 | 2699 | return unexpectedError(@as(Win32Error, @enumFromInt(@intFromEnum(err)))); |
| 2699 | 2700 | } |
| 2700 | 2701 | |
| 2701 | 2702 | /// Call this when you made a windows NtDll call |
| 2702 | 2703 | /// and you get an unexpected status. |
| 2703 | pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError { | |
| 2704 | if (std.os.unexpected_error_tracing) { | |
| 2704 | pub fn unexpectedStatus(status: NTSTATUS) UnexpectedError { | |
| 2705 | if (std.posix.unexpected_error_tracing) { | |
| 2705 | 2706 | std.debug.print("error.Unexpected NTSTATUS=0x{x}\n", .{@intFromEnum(status)}); |
| 2706 | 2707 | std.debug.dumpCurrentStackTrace(@returnAddress()); |
| 2707 | 2708 | } |
| ... | ... | @@ -4246,7 +4247,7 @@ pub const KNONVOLATILE_CONTEXT_POINTERS = switch (native_arch) { |
| 4246 | 4247 | |
| 4247 | 4248 | pub const EXCEPTION_POINTERS = extern struct { |
| 4248 | 4249 | ExceptionRecord: *EXCEPTION_RECORD, |
| 4249 | ContextRecord: *std.os.windows.CONTEXT, | |
| 4250 | ContextRecord: *CONTEXT, | |
| 4250 | 4251 | }; |
| 4251 | 4252 | |
| 4252 | 4253 | pub 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 | 245 | const LPFN_CONNECTEX = *const fn ( |
| 246 | 246 | Socket: windows.ws2_32.SOCKET, |
| 247 | 247 | SockAddr: *const windows.ws2_32.sockaddr, |
| 248 | SockLen: std.os.socklen_t, | |
| 248 | SockLen: std.posix.socklen_t, | |
| 249 | 249 | SendBuf: ?*const anyopaque, |
| 250 | 250 | SendBufLen: windows.DWORD, |
| 251 | 251 | BytesSent: *windows.DWORD, |
| ... | ... | @@ -254,7 +254,7 @@ test "loadWinsockExtensionFunction" { |
| 254 | 254 | |
| 255 | 255 | _ = windows.loadWinsockExtensionFunction( |
| 256 | 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 | 258 | windows.ws2_32.WSAID_CONNECTEX, |
| 259 | 259 | ) catch |err| switch (err) { |
| 260 | 260 | error.OperationNotSupported => unreachable, |
lib/std/pdb.zig-1| ... | ... | @@ -2,7 +2,6 @@ const std = @import("std.zig"); |
| 2 | 2 | const io = std.io; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const mem = std.mem; |
| 5 | const os = std.os; | |
| 6 | 5 | const coff = std.coff; |
| 7 | 6 | const fs = std.fs; |
| 8 | 7 | const 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 | ||
| 17 | const builtin = @import("builtin"); | |
| 18 | const root = @import("root"); | |
| 19 | const std = @import("std.zig"); | |
| 20 | const mem = std.mem; | |
| 21 | const fs = std.fs; | |
| 22 | const max_path_bytes = fs.MAX_PATH_BYTES; | |
| 23 | const maxInt = std.math.maxInt; | |
| 24 | const cast = std.math.cast; | |
| 25 | const assert = std.debug.assert; | |
| 26 | const native_os = builtin.os.tag; | |
| 27 | ||
| 28 | test { | |
| 29 | _ = @import("posix/test.zig"); | |
| 30 | } | |
| 31 | ||
| 32 | /// Whether to use libc for the POSIX API layer. | |
| 33 | const use_libc = builtin.link_libc or switch (native_os) { | |
| 34 | .windows, .wasi => true, | |
| 35 | else => false, | |
| 36 | }; | |
| 37 | ||
| 38 | const linux = std.os.linux; | |
| 39 | const windows = std.os.windows; | |
| 40 | const 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. | |
| 45 | pub const system = if (@hasDecl(root, "os") and @hasDecl(root.os, "system") and root.os != @This()) | |
| 46 | root.os.system | |
| 47 | else if (use_libc) | |
| 48 | std.c | |
| 49 | else switch (native_os) { | |
| 50 | .linux => linux, | |
| 51 | .plan9 => std.os.plan9, | |
| 52 | else => struct {}, | |
| 53 | }; | |
| 54 | ||
| 55 | pub const AF = system.AF; | |
| 56 | pub const AF_SUN = system.AF_SUN; | |
| 57 | pub const ARCH = system.ARCH; | |
| 58 | pub const AT = system.AT; | |
| 59 | pub const AT_SUN = system.AT_SUN; | |
| 60 | pub const CLOCK = system.CLOCK; | |
| 61 | pub const CPU_COUNT = system.CPU_COUNT; | |
| 62 | pub const CTL = system.CTL; | |
| 63 | pub const DT = system.DT; | |
| 64 | pub const E = system.E; | |
| 65 | pub const Elf_Symndx = system.Elf_Symndx; | |
| 66 | pub const F = system.F; | |
| 67 | pub const FD_CLOEXEC = system.FD_CLOEXEC; | |
| 68 | pub const Flock = system.Flock; | |
| 69 | pub const HOST_NAME_MAX = system.HOST_NAME_MAX; | |
| 70 | pub const HW = system.HW; | |
| 71 | pub const IFNAMESIZE = system.IFNAMESIZE; | |
| 72 | pub const IOV_MAX = system.IOV_MAX; | |
| 73 | pub const IPPROTO = system.IPPROTO; | |
| 74 | pub const KERN = system.KERN; | |
| 75 | pub const Kevent = system.Kevent; | |
| 76 | pub const LOCK = system.LOCK; | |
| 77 | pub const MADV = system.MADV; | |
| 78 | pub const MAP = system.MAP; | |
| 79 | pub const MSF = system.MSF; | |
| 80 | pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN; | |
| 81 | pub const MFD = system.MFD; | |
| 82 | pub const MMAP2_UNIT = system.MMAP2_UNIT; | |
| 83 | pub const MSG = system.MSG; | |
| 84 | pub const NAME_MAX = system.NAME_MAX; | |
| 85 | pub const O = system.O; | |
| 86 | pub const PATH_MAX = system.PATH_MAX; | |
| 87 | pub const POLL = system.POLL; | |
| 88 | pub const POSIX_FADV = system.POSIX_FADV; | |
| 89 | pub const PR = system.PR; | |
| 90 | pub const PROT = system.PROT; | |
| 91 | pub const REG = system.REG; | |
| 92 | pub const RLIM = system.RLIM; | |
| 93 | pub const RR = system.RR; | |
| 94 | pub const S = system.S; | |
| 95 | pub const SA = system.SA; | |
| 96 | pub const SC = system.SC; | |
| 97 | pub const _SC = system._SC; | |
| 98 | pub const SEEK = system.SEEK; | |
| 99 | pub const SHUT = system.SHUT; | |
| 100 | pub const SIG = system.SIG; | |
| 101 | pub const SIOCGIFINDEX = system.SIOCGIFINDEX; | |
| 102 | pub const SO = system.SO; | |
| 103 | pub const SOCK = system.SOCK; | |
| 104 | pub const SOL = system.SOL; | |
| 105 | pub const STDERR_FILENO = system.STDERR_FILENO; | |
| 106 | pub const STDIN_FILENO = system.STDIN_FILENO; | |
| 107 | pub const STDOUT_FILENO = system.STDOUT_FILENO; | |
| 108 | pub const SYS = system.SYS; | |
| 109 | pub const Sigaction = system.Sigaction; | |
| 110 | pub const Stat = system.Stat; | |
| 111 | pub const T = system.T; | |
| 112 | pub const TCSA = system.TCSA; | |
| 113 | pub const TCP = system.TCP; | |
| 114 | pub const VDSO = system.VDSO; | |
| 115 | pub const W = system.W; | |
| 116 | pub const addrinfo = system.addrinfo; | |
| 117 | pub const blkcnt_t = system.blkcnt_t; | |
| 118 | pub const blksize_t = system.blksize_t; | |
| 119 | pub const clock_t = system.clock_t; | |
| 120 | pub const cpu_set_t = system.cpu_set_t; | |
| 121 | pub const dev_t = system.dev_t; | |
| 122 | pub const dl_phdr_info = system.dl_phdr_info; | |
| 123 | pub const empty_sigset = system.empty_sigset; | |
| 124 | pub const filled_sigset = system.filled_sigset; | |
| 125 | pub const fd_t = system.fd_t; | |
| 126 | pub const gid_t = system.gid_t; | |
| 127 | pub const ifreq = system.ifreq; | |
| 128 | pub const ino_t = system.ino_t; | |
| 129 | pub const mcontext_t = system.mcontext_t; | |
| 130 | pub const mode_t = system.mode_t; | |
| 131 | pub const msghdr = system.msghdr; | |
| 132 | pub const msghdr_const = system.msghdr_const; | |
| 133 | pub const nfds_t = system.nfds_t; | |
| 134 | pub const nlink_t = system.nlink_t; | |
| 135 | pub const off_t = system.off_t; | |
| 136 | pub const pid_t = system.pid_t; | |
| 137 | pub const pollfd = system.pollfd; | |
| 138 | pub const port_t = system.port_t; | |
| 139 | pub const port_event = system.port_event; | |
| 140 | pub const port_notify = system.port_notify; | |
| 141 | pub const file_obj = system.file_obj; | |
| 142 | pub const rlim_t = system.rlim_t; | |
| 143 | pub const rlimit = system.rlimit; | |
| 144 | pub const rlimit_resource = system.rlimit_resource; | |
| 145 | pub const rusage = system.rusage; | |
| 146 | pub const sa_family_t = system.sa_family_t; | |
| 147 | pub const siginfo_t = system.siginfo_t; | |
| 148 | pub const sigset_t = system.sigset_t; | |
| 149 | pub const sockaddr = system.sockaddr; | |
| 150 | pub const socklen_t = system.socklen_t; | |
| 151 | pub const stack_t = system.stack_t; | |
| 152 | pub const time_t = system.time_t; | |
| 153 | pub const timespec = system.timespec; | |
| 154 | pub const timestamp_t = system.timestamp_t; | |
| 155 | pub const timeval = system.timeval; | |
| 156 | pub const timezone = system.timezone; | |
| 157 | pub const ucontext_t = system.ucontext_t; | |
| 158 | pub const uid_t = system.uid_t; | |
| 159 | pub const user_desc = system.user_desc; | |
| 160 | pub const utsname = system.utsname; | |
| 161 | pub const winsize = system.winsize; | |
| 162 | ||
| 163 | pub const termios = system.termios; | |
| 164 | pub const CSIZE = system.CSIZE; | |
| 165 | pub const NCCS = system.NCCS; | |
| 166 | pub const cc_t = system.cc_t; | |
| 167 | pub const V = system.V; | |
| 168 | pub const speed_t = system.speed_t; | |
| 169 | pub const tc_iflag_t = system.tc_iflag_t; | |
| 170 | pub const tc_oflag_t = system.tc_oflag_t; | |
| 171 | pub const tc_cflag_t = system.tc_cflag_t; | |
| 172 | pub const tc_lflag_t = system.tc_lflag_t; | |
| 173 | ||
| 174 | pub const F_OK = system.F_OK; | |
| 175 | pub const R_OK = system.R_OK; | |
| 176 | pub const W_OK = system.W_OK; | |
| 177 | pub const X_OK = system.X_OK; | |
| 178 | ||
| 179 | pub const iovec = extern struct { | |
| 180 | iov_base: [*]u8, | |
| 181 | iov_len: usize, | |
| 182 | }; | |
| 183 | ||
| 184 | pub const iovec_const = extern struct { | |
| 185 | iov_base: [*]const u8, | |
| 186 | iov_len: usize, | |
| 187 | }; | |
| 188 | ||
| 189 | pub const ACCMODE = enum(u2) { | |
| 190 | RDONLY = 0, | |
| 191 | WRONLY = 1, | |
| 192 | RDWR = 2, | |
| 193 | }; | |
| 194 | ||
| 195 | pub 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 | ||
| 214 | pub 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. | |
| 222 | pub 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. | |
| 238 | pub 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 | ||
| 260 | pub 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. | |
| 274 | pub 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 | ||
| 298 | pub 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. | |
| 331 | pub 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 | ||
| 348 | fn 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 | ||
| 375 | fn 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 | ||
| 474 | pub 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. | |
| 488 | pub 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 | ||
| 517 | pub const RebootError = error{ | |
| 518 | PermissionDenied, | |
| 519 | } || UnexpectedError; | |
| 520 | ||
| 521 | pub 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 | ||
| 535 | pub 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 | ||
| 567 | pub 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. | |
| 574 | pub 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 | ||
| 623 | fn 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. | |
| 641 | pub 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 | ||
| 697 | pub const RaiseError = UnexpectedError; | |
| 698 | ||
| 699 | pub 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 | ||
| 727 | pub const KillError = error{ ProcessNotFound, PermissionDenied } || UnexpectedError; | |
| 728 | ||
| 729 | pub 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. | |
| 740 | pub 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 | ||
| 766 | pub 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)`. | |
| 796 | pub 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. | |
| 867 | pub 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 | ||
| 916 | pub 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)`. | |
| 932 | pub 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 | ||
| 998 | pub 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 | ||
| 1008 | pub 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. | |
| 1074 | pub 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 | ||
| 1135 | pub 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)`. | |
| 1185 | pub 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. | |
| 1265 | pub 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 | ||
| 1316 | pub 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)`. | |
| 1340 | pub 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. | |
| 1428 | pub 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 | ||
| 1491 | pub 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`. | |
| 1562 | pub 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`. | |
| 1577 | pub 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`. | |
| 1620 | pub 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. | |
| 1650 | pub 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` | |
| 1692 | const 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. | |
| 1701 | fn 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`. | |
| 1742 | pub 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 | ||
| 1783 | pub 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 | ||
| 1793 | pub 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 | ||
| 1806 | pub 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. | |
| 1821 | pub 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 | ||
| 1859 | pub 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. | |
| 1867 | pub 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`. | |
| 1919 | pub 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`. | |
| 1929 | pub 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`. | |
| 1965 | pub 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 | ||
| 1976 | pub const GetCwdError = error{ | |
| 1977 | NameTooLong, | |
| 1978 | CurrentWorkingDirectoryUnlinked, | |
| 1979 | } || UnexpectedError; | |
| 1980 | ||
| 1981 | /// The result is a slice of out_buffer, indexed from 0. | |
| 1982 | pub 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 | ||
| 2009 | pub 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. | |
| 2042 | pub 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`. | |
| 2055 | pub 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`. | |
| 2094 | pub 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`. | |
| 2107 | pub 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`. | |
| 2133 | pub 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 | ||
| 2163 | pub 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. | |
| 2183 | pub 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. | |
| 2214 | pub 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 | ||
| 2226 | pub 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. | |
| 2230 | pub 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. | |
| 2268 | pub 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 | ||
| 2316 | pub 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`. | |
| 2351 | pub 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. | |
| 2367 | pub 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. | |
| 2398 | pub 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 | ||
| 2405 | pub 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. | |
| 2415 | pub 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`. | |
| 2429 | pub 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. | |
| 2461 | pub 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. | |
| 2497 | pub 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 | ||
| 2502 | pub 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. | |
| 2545 | pub 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. | |
| 2560 | pub 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. | |
| 2598 | pub 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. | |
| 2607 | pub 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`. | |
| 2630 | fn 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. | |
| 2661 | const 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. | |
| 2669 | pub 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. | |
| 2713 | pub 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. | |
| 2821 | pub 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 | ||
| 2833 | pub 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. | |
| 2858 | pub 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. | |
| 2892 | pub 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 | ||
| 2909 | pub 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. | |
| 2939 | pub 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. | |
| 2955 | pub 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. | |
| 2986 | pub 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 | ||
| 3003 | pub 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. | |
| 3027 | pub 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. | |
| 3047 | pub 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. | |
| 3078 | pub 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 | ||
| 3085 | pub 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. | |
| 3105 | pub 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. | |
| 3123 | pub 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. | |
| 3151 | pub 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 | ||
| 3158 | pub const FchdirError = error{ | |
| 3159 | AccessDenied, | |
| 3160 | NotDir, | |
| 3161 | FileSystem, | |
| 3162 | } || UnexpectedError; | |
| 3163 | ||
| 3164 | pub 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 | ||
| 3179 | pub 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. | |
| 3211 | pub 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`. | |
| 3226 | pub 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. | |
| 3231 | pub 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`. | |
| 3267 | pub 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`. | |
| 3281 | pub 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`. | |
| 3303 | pub 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`. | |
| 3309 | pub 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 | ||
| 3336 | pub const SetEidError = error{ | |
| 3337 | InvalidUserId, | |
| 3338 | PermissionDenied, | |
| 3339 | } || UnexpectedError; | |
| 3340 | ||
| 3341 | pub const SetIdError = error{ResourceLimitReached} || SetEidError; | |
| 3342 | ||
| 3343 | pub 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 | ||
| 3353 | pub 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 | ||
| 3362 | pub 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 | ||
| 3372 | pub 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 | ||
| 3382 | pub 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 | ||
| 3391 | pub 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. | |
| 3402 | pub 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 | ||
| 3442 | pub 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 | ||
| 3470 | pub 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 | ||
| 3528 | pub 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 | ||
| 3543 | pub const ShutdownHow = enum { recv, send, both }; | |
| 3544 | ||
| 3545 | /// Shutdown socket send/receive operations | |
| 3546 | pub 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 | ||
| 3582 | pub 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 | |
| 3628 | pub 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 | ||
| 3671 | pub 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 | ||
| 3699 | pub 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 | ||
| 3730 | pub 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. | |
| 3772 | pub 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 | ||
| 3856 | fn 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 | ||
| 3914 | pub 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 | ||
| 3928 | pub 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 | ||
| 3941 | pub 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 | ||
| 3967 | pub 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. | |
| 3987 | pub 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 | ||
| 4002 | pub const EventFdError = error{ | |
| 4003 | SystemResources, | |
| 4004 | ProcessFdQuotaExceeded, | |
| 4005 | SystemFdQuotaExceeded, | |
| 4006 | } || UnexpectedError; | |
| 4007 | ||
| 4008 | pub 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 | ||
| 4022 | pub 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 | ||
| 4035 | pub 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 | ||
| 4064 | pub 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 | ||
| 4093 | pub 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. | |
| 4143 | pub 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 | ||
| 4197 | pub 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 | ||
| 4233 | pub 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. | |
| 4240 | pub 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 | ||
| 4257 | pub 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 | ||
| 4274 | pub 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. | |
| 4283 | pub 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 | ||
| 4303 | pub 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`. | |
| 4316 | pub 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`. | |
| 4332 | pub 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 | ||
| 4362 | pub 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 | ||
| 4370 | pub 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 | ||
| 4380 | pub 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 | ||
| 4398 | pub 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 | ||
| 4428 | pub const INotifyInitError = error{ | |
| 4429 | ProcessFdQuotaExceeded, | |
| 4430 | SystemFdQuotaExceeded, | |
| 4431 | SystemResources, | |
| 4432 | } || UnexpectedError; | |
| 4433 | ||
| 4434 | /// initialize an inotify instance | |
| 4435 | pub 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 | ||
| 4447 | pub 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 | |
| 4458 | pub 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. | |
| 4464 | pub 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 | |
| 4483 | pub 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 | ||
| 4492 | pub const FanotifyInitError = error{ | |
| 4493 | ProcessFdQuotaExceeded, | |
| 4494 | SystemFdQuotaExceeded, | |
| 4495 | SystemResources, | |
| 4496 | OperationNotSupported, | |
| 4497 | PermissionDenied, | |
| 4498 | } || UnexpectedError; | |
| 4499 | ||
| 4500 | pub 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 | ||
| 4514 | pub 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 | ||
| 4529 | pub 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 | ||
| 4538 | pub 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 | ||
| 4559 | pub 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. | |
| 4574 | pub 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 | ||
| 4603 | pub const ForkError = error{SystemResources} || UnexpectedError; | |
| 4604 | ||
| 4605 | pub 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 | ||
| 4615 | pub 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 | |
| 4639 | pub 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. | |
| 4680 | pub 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 | ||
| 4689 | pub const MSyncError = error{ | |
| 4690 | UnmappedMemory, | |
| 4691 | } || UnexpectedError; | |
| 4692 | ||
| 4693 | pub 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 | ||
| 4702 | pub 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. | |
| 4727 | pub 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. | |
| 4743 | pub 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. | |
| 4783 | pub 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. | |
| 4832 | pub 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. | |
| 4862 | pub 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 | ||
| 4897 | pub const PipeError = error{ | |
| 4898 | SystemFdQuotaExceeded, | |
| 4899 | ProcessFdQuotaExceeded, | |
| 4900 | } || UnexpectedError; | |
| 4901 | ||
| 4902 | /// Creates a unidirectional data channel that can be used for interprocess communication. | |
| 4903 | pub 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 | ||
| 4915 | pub 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 | ||
| 4972 | pub const SysCtlError = error{ | |
| 4973 | PermissionDenied, | |
| 4974 | SystemResources, | |
| 4975 | NameTooLong, | |
| 4976 | UnknownName, | |
| 4977 | } || UnexpectedError; | |
| 4978 | ||
| 4979 | pub 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 | ||
| 5004 | pub 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 | ||
| 5028 | pub 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 | ||
| 5036 | pub 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. | |
| 5045 | pub 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. | |
| 5088 | pub 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. | |
| 5130 | pub 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. | |
| 5172 | pub 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 | ||
| 5214 | pub const FcntlError = error{ | |
| 5215 | PermissionDenied, | |
| 5216 | FileBusy, | |
| 5217 | ProcessFdQuotaExceeded, | |
| 5218 | Locked, | |
| 5219 | DeadLock, | |
| 5220 | LockedRegionLimitExceeded, | |
| 5221 | } || UnexpectedError; | |
| 5222 | ||
| 5223 | pub 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 | ||
| 5243 | pub 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. | |
| 5255 | pub 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 | ||
| 5271 | pub 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. | |
| 5331 | pub 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. | |
| 5345 | pub 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. | |
| 5397 | pub 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. | |
| 5423 | pub 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 | ||
| 5447 | pub 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 | ||
| 5533 | pub 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 | |
| 5537 | pub 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 | ||
| 5578 | pub 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 | ||
| 5600 | pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError; | |
| 5601 | ||
| 5602 | pub 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 | ||
| 5614 | pub 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 | ||
| 5622 | pub 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. | |
| 5634 | pub 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. | |
| 5643 | pub 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 | ||
| 5652 | pub 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 | ||
| 5676 | pub 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 | ||
| 5710 | pub const GetHostNameError = error{PermissionDenied} || UnexpectedError; | |
| 5711 | ||
| 5712 | pub 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 | ||
| 5733 | pub 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 | ||
| 5742 | pub 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 | ||
| 5792 | pub 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 | ||
| 5839 | pub 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 | ||
| 5858 | pub 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 | ||
| 5929 | pub 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. | |
| 5959 | pub 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. | |
| 6049 | pub 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 | ||
| 6069 | pub 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)`. | |
| 6106 | pub 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 | ||
| 6357 | fn 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 | ||
| 6365 | pub 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)`. | |
| 6407 | pub 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 | ||
| 6465 | pub 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 | ||
| 6473 | pub 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 | ||
| 6503 | pub 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 | ||
| 6511 | pub 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 | ||
| 6530 | pub 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 | ||
| 6558 | pub 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. | |
| 6564 | pub 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 | ||
| 6610 | pub const DnExpandError = error{InvalidDnsPacket}; | |
| 6611 | ||
| 6612 | pub 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 | ||
| 6660 | pub 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. | |
| 6683 | pub 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 | ||
| 6716 | pub 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 | ||
| 6728 | pub 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 | ||
| 6764 | pub 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 | ||
| 6772 | pub 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 | ||
| 6783 | pub const TIOCError = error{NotATerminal}; | |
| 6784 | ||
| 6785 | pub const TermiosGetError = TIOCError || UnexpectedError; | |
| 6786 | ||
| 6787 | pub 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 | ||
| 6800 | pub const TermiosSetError = TermiosGetError || error{ProcessOrphaned}; | |
| 6801 | ||
| 6802 | pub 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 | ||
| 6816 | pub const TermioGetPgrpError = TIOCError || UnexpectedError; | |
| 6817 | ||
| 6818 | /// Returns the process group ID for the TTY associated with the given handle. | |
| 6819 | pub 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 | ||
| 6833 | pub 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. | |
| 6839 | pub 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 | ||
| 6853 | pub 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 | ||
| 6867 | pub 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. | |
| 6875 | pub 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. | |
| 6880 | pub 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. | |
| 6893 | pub 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. | |
| 6917 | pub 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 | ||
| 6935 | pub 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 | ||
| 6950 | pub 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 | ||
| 6977 | pub const GetrlimitError = UnexpectedError; | |
| 6978 | ||
| 6979 | pub 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 | ||
| 6991 | pub const SetrlimitError = error{ PermissionDenied, LimitTooBig } || UnexpectedError; | |
| 6992 | ||
| 6993 | pub 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 | ||
| 7005 | pub 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. | |
| 7022 | pub 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 | ||
| 7034 | pub 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. | |
| 7068 | pub 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 | ||
| 7082 | pub 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 | ||
| 7136 | pub 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 | ||
| 7166 | pub const TimerFdCreateError = error{ | |
| 7167 | AccessDenied, | |
| 7168 | ProcessFdQuotaExceeded, | |
| 7169 | SystemFdQuotaExceeded, | |
| 7170 | NoDevice, | |
| 7171 | SystemResources, | |
| 7172 | } || UnexpectedError; | |
| 7173 | ||
| 7174 | pub const TimerFdGetError = error{InvalidHandle} || UnexpectedError; | |
| 7175 | pub const TimerFdSetError = TimerFdGetError || error{Canceled}; | |
| 7176 | ||
| 7177 | pub 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 | ||
| 7191 | pub 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 | ||
| 7208 | pub 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 | ||
| 7220 | pub const PtraceError = error{ | |
| 7221 | DeviceBusy, | |
| 7222 | InputOutput, | |
| 7223 | ProcessNotFound, | |
| 7224 | PermissionDenied, | |
| 7225 | } || UnexpectedError; | |
| 7226 | ||
| 7227 | pub 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 | ||
| 7268 | pub const IoCtl_SIOCGIFINDEX_Error = error{ | |
| 7269 | FileSystem, | |
| 7270 | InterfaceNotFound, | |
| 7271 | } || UnexpectedError; | |
| 7272 | ||
| 7273 | pub 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 | ||
| 7290 | const 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. | |
| 7297 | pub const unexpected_error_tracing = builtin.zig_backend == .stage2_llvm and builtin.mode == .Debug; | |
| 7298 | ||
| 7299 | pub 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. | |
| 7313 | pub 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. | |
| 7322 | pub 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 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const posix = std.posix; | |
| 3 | const testing = std.testing; | |
| 4 | const expect = testing.expect; | |
| 5 | const expectEqual = testing.expectEqual; | |
| 6 | const expectError = testing.expectError; | |
| 7 | const io = std.io; | |
| 8 | const fs = std.fs; | |
| 9 | const mem = std.mem; | |
| 10 | const elf = std.elf; | |
| 11 | const File = std.fs.File; | |
| 12 | const Thread = std.Thread; | |
| 13 | const linux = std.os.linux; | |
| 14 | ||
| 15 | const a = std.testing.allocator; | |
| 16 | ||
| 17 | const builtin = @import("builtin"); | |
| 18 | const AtomicRmwOp = std.builtin.AtomicRmwOp; | |
| 19 | const AtomicOrder = std.builtin.AtomicOrder; | |
| 20 | const native_os = builtin.target.os.tag; | |
| 21 | const tmpDir = std.testing.tmpDir; | |
| 22 | const Dir = std.fs.Dir; | |
| 23 | const ArenaAllocator = std.heap.ArenaAllocator; | |
| 24 | ||
| 25 | test "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 | ||
| 89 | test "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 | ||
| 144 | test "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 | ||
| 200 | test "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 | ||
| 241 | test "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 | ||
| 249 | fn 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 | ||
| 255 | test "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 | ||
| 298 | test "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 | ||
| 342 | test "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 | ||
| 364 | test "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 | ||
| 393 | fn testThreadIdFn(thread_id: *Thread.Id) void { | |
| 394 | thread_id.* = Thread.getCurrentId(); | |
| 395 | } | |
| 396 | ||
| 397 | test "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 | ||
| 406 | test "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 | ||
| 424 | fn start1() u8 { | |
| 425 | return 0; | |
| 426 | } | |
| 427 | ||
| 428 | fn start2(ctx: *i32) u8 { | |
| 429 | _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.seq_cst); | |
| 430 | return 0; | |
| 431 | } | |
| 432 | ||
| 433 | test "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 | ||
| 440 | test "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 | ||
| 450 | threadlocal var x: i32 = 1234; | |
| 451 | fn testTls() !void { | |
| 452 | if (x != 1234) return error.TlsBadStartValue; | |
| 453 | x += 1; | |
| 454 | if (x != 1235) return error.TlsBadEndValue; | |
| 455 | } | |
| 456 | ||
| 457 | test "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 | ||
| 467 | test "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 | ||
| 473 | test "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 | |
| 486 | const dl_phdr_info = if (@hasDecl(posix.system, "dl_phdr_info")) posix.dl_phdr_info else anyopaque; | |
| 487 | ||
| 488 | const IterFnError = error{ | |
| 489 | MissingPtLoadSegment, | |
| 490 | MissingLoad, | |
| 491 | BadElfMagic, | |
| 492 | FailedConsistencyCheck, | |
| 493 | }; | |
| 494 | ||
| 495 | fn 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 | ||
| 527 | test "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 | ||
| 535 | test "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 | ||
| 544 | test "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 | ||
| 557 | test "argsAlloc" { | |
| 558 | const args = try std.process.argsAlloc(std.testing.allocator); | |
| 559 | std.process.argsFree(std.testing.allocator, args); | |
| 560 | } | |
| 561 | ||
| 562 | test "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 | ||
| 588 | test "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 | ||
| 685 | test "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 | ||
| 698 | test "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 | ||
| 730 | test "signalfd" { | |
| 731 | switch (native_os) { | |
| 732 | .linux, .solaris, .illumos => {}, | |
| 733 | else => return error.SkipZigTest, | |
| 734 | } | |
| 735 | _ = &posix.signalfd; | |
| 736 | } | |
| 737 | ||
| 738 | test "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 | ||
| 756 | test "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 | ||
| 776 | test "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 | ||
| 807 | test "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 | ||
| 826 | test "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 | ||
| 901 | test "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 | ||
| 933 | test "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 | ||
| 947 | test "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 | ||
| 1012 | test "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 | ||
| 1069 | test "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 | ||
| 1116 | test "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 | ||
| 1134 | test "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 | ||
| 1144 | test "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 | ||
| 1169 | test "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 | ||
| 1194 | test "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 | ||
| 1219 | test "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 | ||
| 1244 | fn 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 | ||
| 1249 | test "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 | ||
| 1288 | const 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 | 1 | const std = @import("std.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const os = std.os; | |
| 4 | 3 | const fs = std.fs; |
| 5 | 4 | const mem = std.mem; |
| 6 | 5 | const math = std.math; |
| ... | ... | @@ -8,20 +7,27 @@ const Allocator = mem.Allocator; |
| 8 | 7 | const assert = std.debug.assert; |
| 9 | 8 | const testing = std.testing; |
| 10 | 9 | const child_process = @import("child_process.zig"); |
| 10 | const native_os = builtin.os.tag; | |
| 11 | const posix = std.posix; | |
| 12 | const windows = std.os.windows; | |
| 11 | 13 | |
| 12 | 14 | pub const Child = child_process.ChildProcess; |
| 13 | pub const abort = os.abort; | |
| 14 | pub const exit = os.exit; | |
| 15 | pub const changeCurDir = os.chdir; | |
| 16 | pub const changeCurDirC = os.chdirC; | |
| 15 | pub const abort = posix.abort; | |
| 16 | pub const exit = posix.exit; | |
| 17 | pub const changeCurDir = posix.chdir; | |
| 18 | pub const changeCurDirC = posix.chdirC; | |
| 19 | ||
| 20 | pub const GetCwdError = posix.GetCwdError; | |
| 17 | 21 | |
| 18 | 22 | /// The result is a slice of `out_buffer`, from index `0`. |
| 19 | 23 | /// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). |
| 20 | 24 | /// On other platforms, the result is an opaque sequence of bytes with no particular encoding. |
| 21 | 25 | pub fn getCwd(out_buffer: []u8) ![]u8 { |
| 22 | return os.getcwd(out_buffer); | |
| 26 | return posix.getcwd(out_buffer); | |
| 23 | 27 | } |
| 24 | 28 | |
| 29 | pub const GetCwdAllocError = Allocator.Error || posix.GetCwdError; | |
| 30 | ||
| 25 | 31 | /// Caller must free the returned memory. |
| 26 | 32 | /// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). |
| 27 | 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 | 40 | |
| 35 | 41 | var current_buf: []u8 = &stack_buf; |
| 36 | 42 | while (true) { |
| 37 | if (os.getcwd(current_buf)) |slice| { | |
| 43 | if (posix.getcwd(current_buf)) |slice| { | |
| 38 | 44 | return allocator.dupe(u8, slice); |
| 39 | 45 | } else |err| switch (err) { |
| 40 | 46 | error.NameTooLong => { |
| ... | ... | @@ -51,7 +57,7 @@ pub fn getCwdAlloc(allocator: Allocator) ![]u8 { |
| 51 | 57 | } |
| 52 | 58 | |
| 53 | 59 | test getCwdAlloc { |
| 54 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 60 | if (native_os == .wasi) return error.SkipZigTest; | |
| 55 | 61 | |
| 56 | 62 | const cwd = try getCwdAlloc(testing.allocator); |
| 57 | 63 | testing.allocator.free(cwd); |
| ... | ... | @@ -72,13 +78,13 @@ pub const EnvMap = struct { |
| 72 | 78 | pub const EnvNameHashContext = struct { |
| 73 | 79 | fn upcase(c: u21) u21 { |
| 74 | 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 | 82 | return c; |
| 77 | 83 | } |
| 78 | 84 | |
| 79 | 85 | pub fn hash(self: @This(), s: []const u8) u64 { |
| 80 | 86 | _ = self; |
| 81 | if (builtin.os.tag == .windows) { | |
| 87 | if (native_os == .windows) { | |
| 82 | 88 | var h = std.hash.Wyhash.init(0); |
| 83 | 89 | var it = std.unicode.Wtf8View.initUnchecked(s).iterator(); |
| 84 | 90 | while (it.nextCodepoint()) |cp| { |
| ... | ... | @@ -96,7 +102,7 @@ pub const EnvMap = struct { |
| 96 | 102 | |
| 97 | 103 | pub fn eql(self: @This(), a: []const u8, b: []const u8) bool { |
| 98 | 104 | _ = self; |
| 99 | if (builtin.os.tag == .windows) { | |
| 105 | if (native_os == .windows) { | |
| 100 | 106 | var it_a = std.unicode.Wtf8View.initUnchecked(a).iterator(); |
| 101 | 107 | var it_b = std.unicode.Wtf8View.initUnchecked(b).iterator(); |
| 102 | 108 | while (true) { |
| ... | ... | @@ -228,7 +234,7 @@ test "EnvMap" { |
| 228 | 234 | try testing.expectEqual(@as(EnvMap.Size, 2), env.count()); |
| 229 | 235 | |
| 230 | 236 | // case insensitivity on Windows only |
| 231 | if (builtin.os.tag == .windows) { | |
| 237 | if (native_os == .windows) { | |
| 232 | 238 | try testing.expectEqualStrings("1", env.get("something_New_aNd_LONGER").?); |
| 233 | 239 | } else { |
| 234 | 240 | try testing.expect(null == env.get("something_New_aNd_LONGER")); |
| ... | ... | @@ -248,7 +254,7 @@ test "EnvMap" { |
| 248 | 254 | |
| 249 | 255 | try testing.expectEqual(@as(EnvMap.Size, 1), env.count()); |
| 250 | 256 | |
| 251 | if (builtin.os.tag == .windows) { | |
| 257 | if (native_os == .windows) { | |
| 252 | 258 | // test Unicode case-insensitivity on Windows |
| 253 | 259 | try env.put("КИРиллИЦА", "something else"); |
| 254 | 260 | try testing.expectEqualStrings("something else", env.get("кириллица").?); |
| ... | ... | @@ -279,8 +285,8 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap { |
| 279 | 285 | var result = EnvMap.init(allocator); |
| 280 | 286 | errdefer result.deinit(); |
| 281 | 287 | |
| 282 | if (builtin.os.tag == .windows) { | |
| 283 | const ptr = os.windows.peb().ProcessParameters.Environment; | |
| 288 | if (native_os == .windows) { | |
| 289 | const ptr = windows.peb().ProcessParameters.Environment; | |
| 284 | 290 | |
| 285 | 291 | var i: usize = 0; |
| 286 | 292 | while (ptr[i] != 0) { |
| ... | ... | @@ -310,13 +316,13 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap { |
| 310 | 316 | try result.putMove(key, value); |
| 311 | 317 | } |
| 312 | 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 | 320 | var environ_count: usize = undefined; |
| 315 | 321 | var environ_buf_size: usize = undefined; |
| 316 | 322 | |
| 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 | 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 | } |
| 321 | 327 | |
| 322 | 328 | if (environ_count == 0) { |
| ... | ... | @@ -328,9 +334,9 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap { |
| 328 | 334 | const environ_buf = try allocator.alloc(u8, environ_buf_size); |
| 329 | 335 | defer allocator.free(environ_buf); |
| 330 | 336 | |
| 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 | 338 | if (environ_get_ret != .SUCCESS) { |
| 333 | return os.unexpectedErrno(environ_get_ret); | |
| 339 | return posix.unexpectedErrno(environ_get_ret); | |
| 334 | 340 | } |
| 335 | 341 | |
| 336 | 342 | for (environ) |env| { |
| ... | ... | @@ -356,7 +362,7 @@ pub fn getEnvMap(allocator: Allocator) GetEnvMapError!EnvMap { |
| 356 | 362 | } |
| 357 | 363 | return result; |
| 358 | 364 | } else { |
| 359 | for (os.environ) |line| { | |
| 365 | for (std.os.environ) |line| { | |
| 360 | 366 | var line_i: usize = 0; |
| 361 | 367 | while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} |
| 362 | 368 | const key = line[0..line_i]; |
| ... | ... | @@ -391,37 +397,37 @@ pub const GetEnvVarOwnedError = error{ |
| 391 | 397 | /// On Windows, the value is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). |
| 392 | 398 | /// On other platforms, the value is an opaque sequence of bytes with no particular encoding. |
| 393 | 399 | pub fn getEnvVarOwned(allocator: Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { |
| 394 | if (builtin.os.tag == .windows) { | |
| 400 | if (native_os == .windows) { | |
| 395 | 401 | const result_w = blk: { |
| 396 | 402 | var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator); |
| 397 | 403 | const stack_allocator = stack_alloc.get(); |
| 398 | 404 | const key_w = try std.unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key); |
| 399 | 405 | defer stack_allocator.free(key_w); |
| 400 | 406 | |
| 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 | 409 | // wtf16LeToWtf8Alloc can only fail with OutOfMemory |
| 404 | 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 | 412 | var envmap = getEnvMap(allocator) catch return error.OutOfMemory; |
| 407 | 413 | defer envmap.deinit(); |
| 408 | 414 | const val = envmap.get(key) orelse return error.EnvironmentVariableNotFound; |
| 409 | 415 | return allocator.dupe(u8, val); |
| 410 | 416 | } else { |
| 411 | const result = os.getenv(key) orelse return error.EnvironmentVariableNotFound; | |
| 417 | const result = posix.getenv(key) orelse return error.EnvironmentVariableNotFound; | |
| 412 | 418 | return allocator.dupe(u8, result); |
| 413 | 419 | } |
| 414 | 420 | } |
| 415 | 421 | |
| 416 | 422 | /// On Windows, `key` must be valid UTF-8. |
| 417 | 423 | pub fn hasEnvVarConstant(comptime key: []const u8) bool { |
| 418 | if (builtin.os.tag == .windows) { | |
| 424 | if (native_os == .windows) { | |
| 419 | 425 | const key_w = comptime std.unicode.utf8ToUtf16LeStringLiteral(key); |
| 420 | return std.os.getenvW(key_w) != null; | |
| 421 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { | |
| 426 | return getenvW(key_w) != null; | |
| 427 | } else if (native_os == .wasi and !builtin.link_libc) { | |
| 422 | 428 | @compileError("hasEnvVarConstant is not supported for WASI without libc"); |
| 423 | 429 | } else { |
| 424 | return os.getenv(key) != null; | |
| 430 | return posix.getenv(key) != null; | |
| 425 | 431 | } |
| 426 | 432 | } |
| 427 | 433 | |
| ... | ... | @@ -436,19 +442,63 @@ pub const HasEnvVarError = error{ |
| 436 | 442 | /// On Windows, if `key` is not valid [WTF-8](https://simonsapin.github.io/wtf-8/), |
| 437 | 443 | /// then `error.InvalidWtf8` is returned. |
| 438 | 444 | pub fn hasEnvVar(allocator: Allocator, key: []const u8) HasEnvVarError!bool { |
| 439 | if (builtin.os.tag == .windows) { | |
| 445 | if (native_os == .windows) { | |
| 440 | 446 | var stack_alloc = std.heap.stackFallback(256 * @sizeOf(u16), allocator); |
| 441 | 447 | const stack_allocator = stack_alloc.get(); |
| 442 | 448 | const key_w = try std.unicode.wtf8ToWtf16LeAllocZ(stack_allocator, key); |
| 443 | 449 | defer stack_allocator.free(key_w); |
| 444 | return std.os.getenvW(key_w) != null; | |
| 445 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { | |
| 450 | return getenvW(key_w) != null; | |
| 451 | } else if (native_os == .wasi and !builtin.link_libc) { | |
| 446 | 452 | var envmap = getEnvMap(allocator) catch return error.OutOfMemory; |
| 447 | 453 | defer envmap.deinit(); |
| 448 | 454 | return envmap.getPtr(key) != null; |
| 449 | 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` | |
| 470 | pub 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 | } |
| 453 | 503 | |
| 454 | 504 | test getEnvVarOwned { |
| ... | ... | @@ -459,7 +509,7 @@ test getEnvVarOwned { |
| 459 | 509 | } |
| 460 | 510 | |
| 461 | 511 | test 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; | |
| 463 | 513 | |
| 464 | 514 | try testing.expect(!hasEnvVarConstant("BADENV")); |
| 465 | 515 | } |
| ... | ... | @@ -478,14 +528,14 @@ pub const ArgIteratorPosix = struct { |
| 478 | 528 | pub fn init() ArgIteratorPosix { |
| 479 | 529 | return ArgIteratorPosix{ |
| 480 | 530 | .index = 0, |
| 481 | .count = os.argv.len, | |
| 531 | .count = std.os.argv.len, | |
| 482 | 532 | }; |
| 483 | 533 | } |
| 484 | 534 | |
| 485 | 535 | pub fn next(self: *ArgIteratorPosix) ?[:0]const u8 { |
| 486 | 536 | if (self.index == self.count) return null; |
| 487 | 537 | |
| 488 | const s = os.argv[self.index]; | |
| 538 | const s = std.os.argv[self.index]; | |
| 489 | 539 | self.index += 1; |
| 490 | 540 | return mem.sliceTo(s, 0); |
| 491 | 541 | } |
| ... | ... | @@ -503,7 +553,7 @@ pub const ArgIteratorWasi = struct { |
| 503 | 553 | index: usize, |
| 504 | 554 | args: [][:0]u8, |
| 505 | 555 | |
| 506 | pub const InitError = error{OutOfMemory} || os.UnexpectedError; | |
| 556 | pub const InitError = error{OutOfMemory} || posix.UnexpectedError; | |
| 507 | 557 | |
| 508 | 558 | /// You must call deinit to free the internal buffer of the |
| 509 | 559 | /// iterator after you are done. |
| ... | ... | @@ -517,13 +567,12 @@ pub const ArgIteratorWasi = struct { |
| 517 | 567 | } |
| 518 | 568 | |
| 519 | 569 | fn internalInit(allocator: Allocator) InitError![][:0]u8 { |
| 520 | const w = os.wasi; | |
| 521 | 570 | var count: usize = undefined; |
| 522 | 571 | var buf_size: usize = undefined; |
| 523 | 572 | |
| 524 | switch (w.args_sizes_get(&count, &buf_size)) { | |
| 573 | switch (std.os.wasi.args_sizes_get(&count, &buf_size)) { | |
| 525 | 574 | .SUCCESS => {}, |
| 526 | else => |err| return os.unexpectedErrno(err), | |
| 575 | else => |err| return posix.unexpectedErrno(err), | |
| 527 | 576 | } |
| 528 | 577 | |
| 529 | 578 | if (count == 0) { |
| ... | ... | @@ -535,9 +584,9 @@ pub const ArgIteratorWasi = struct { |
| 535 | 584 | |
| 536 | 585 | const argv_buf = try allocator.alloc(u8, buf_size); |
| 537 | 586 | |
| 538 | switch (w.args_get(argv.ptr, argv_buf.ptr)) { | |
| 587 | switch (std.os.wasi.args_get(argv.ptr, argv_buf.ptr)) { | |
| 539 | 588 | .SUCCESS => {}, |
| 540 | else => |err| return os.unexpectedErrno(err), | |
| 589 | else => |err| return posix.unexpectedErrno(err), | |
| 541 | 590 | } |
| 542 | 591 | |
| 543 | 592 | var result_args = try allocator.alloc([:0]u8, count); |
| ... | ... | @@ -1007,7 +1056,7 @@ pub fn ArgIteratorGeneral(comptime options: ArgIteratorGeneralOptions) type { |
| 1007 | 1056 | |
| 1008 | 1057 | /// Cross-platform command line argument iterator. |
| 1009 | 1058 | pub const ArgIterator = struct { |
| 1010 | const InnerType = switch (builtin.os.tag) { | |
| 1059 | const InnerType = switch (native_os) { | |
| 1011 | 1060 | .windows => ArgIteratorWindows, |
| 1012 | 1061 | .wasi => if (builtin.link_libc) ArgIteratorPosix else ArgIteratorWasi, |
| 1013 | 1062 | else => ArgIteratorPosix, |
| ... | ... | @@ -1018,10 +1067,10 @@ pub const ArgIterator = struct { |
| 1018 | 1067 | /// Initialize the args iterator. Consider using initWithAllocator() instead |
| 1019 | 1068 | /// for cross-platform compatibility. |
| 1020 | 1069 | pub fn init() ArgIterator { |
| 1021 | if (builtin.os.tag == .wasi) { | |
| 1070 | if (native_os == .wasi) { | |
| 1022 | 1071 | @compileError("In WASI, use initWithAllocator instead."); |
| 1023 | 1072 | } |
| 1024 | if (builtin.os.tag == .windows) { | |
| 1073 | if (native_os == .windows) { | |
| 1025 | 1074 | @compileError("In Windows, use initWithAllocator instead."); |
| 1026 | 1075 | } |
| 1027 | 1076 | |
| ... | ... | @@ -1032,11 +1081,11 @@ pub const ArgIterator = struct { |
| 1032 | 1081 | |
| 1033 | 1082 | /// You must deinitialize iterator's internal buffers by calling `deinit` when done. |
| 1034 | 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 | 1085 | return ArgIterator{ .inner = try InnerType.init(allocator) }; |
| 1037 | 1086 | } |
| 1038 | if (builtin.os.tag == .windows) { | |
| 1039 | const cmd_line_w = os.windows.kernel32.GetCommandLineW(); | |
| 1087 | if (native_os == .windows) { | |
| 1088 | const cmd_line_w = windows.kernel32.GetCommandLineW(); | |
| 1040 | 1089 | return ArgIterator{ .inner = try InnerType.init(allocator, cmd_line_w) }; |
| 1041 | 1090 | } |
| 1042 | 1091 | |
| ... | ... | @@ -1061,11 +1110,11 @@ pub const ArgIterator = struct { |
| 1061 | 1110 | /// was created with `initWithAllocator` function. |
| 1062 | 1111 | pub fn deinit(self: *ArgIterator) void { |
| 1063 | 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 | 1114 | self.inner.deinit(); |
| 1066 | 1115 | } |
| 1067 | 1116 | |
| 1068 | if (builtin.os.tag == .windows) { | |
| 1117 | if (native_os == .windows) { | |
| 1069 | 1118 | self.inner.deinit(); |
| 1070 | 1119 | } |
| 1071 | 1120 | } |
| ... | ... | @@ -1334,13 +1383,13 @@ fn testResponseFileCmdLine(input_cmd_line: []const u8, expected_args: []const [] |
| 1334 | 1383 | } |
| 1335 | 1384 | |
| 1336 | 1385 | pub const UserInfo = struct { |
| 1337 | uid: os.uid_t, | |
| 1338 | gid: os.gid_t, | |
| 1386 | uid: posix.uid_t, | |
| 1387 | gid: posix.gid_t, | |
| 1339 | 1388 | }; |
| 1340 | 1389 | |
| 1341 | 1390 | /// POSIX function which gets a uid from username. |
| 1342 | 1391 | pub fn getUserInfo(name: []const u8) !UserInfo { |
| 1343 | return switch (builtin.os.tag) { | |
| 1392 | return switch (native_os) { | |
| 1344 | 1393 | .linux, |
| 1345 | 1394 | .macos, |
| 1346 | 1395 | .watchos, |
| ... | ... | @@ -1376,8 +1425,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { |
| 1376 | 1425 | var buf: [std.mem.page_size]u8 = undefined; |
| 1377 | 1426 | var name_index: usize = 0; |
| 1378 | 1427 | var state = State.Start; |
| 1379 | var uid: os.uid_t = 0; | |
| 1380 | var gid: os.gid_t = 0; | |
| 1428 | var uid: posix.uid_t = 0; | |
| 1429 | var gid: posix.gid_t = 0; | |
| 1381 | 1430 | |
| 1382 | 1431 | while (true) { |
| 1383 | 1432 | const amt_read = try reader.read(buf[0..]); |
| ... | ... | @@ -1462,36 +1511,36 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { |
| 1462 | 1511 | } |
| 1463 | 1512 | |
| 1464 | 1513 | pub fn getBaseAddress() usize { |
| 1465 | switch (builtin.os.tag) { | |
| 1514 | switch (native_os) { | |
| 1466 | 1515 | .linux => { |
| 1467 | const base = os.system.getauxval(std.elf.AT_BASE); | |
| 1516 | const base = std.os.linux.getauxval(std.elf.AT_BASE); | |
| 1468 | 1517 | if (base != 0) { |
| 1469 | 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 | 1521 | return phdr - @sizeOf(std.elf.Ehdr); |
| 1473 | 1522 | }, |
| 1474 | 1523 | .macos, .freebsd, .netbsd => { |
| 1475 | 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 | 1527 | else => @compileError("Unsupported OS"), |
| 1479 | 1528 | } |
| 1480 | 1529 | } |
| 1481 | 1530 | |
| 1482 | 1531 | /// Tells whether calling the `execv` or `execve` functions will be a compile error. |
| 1483 | pub const can_execv = switch (builtin.os.tag) { | |
| 1532 | pub const can_execv = switch (native_os) { | |
| 1484 | 1533 | .windows, .haiku, .wasi => false, |
| 1485 | 1534 | else => true, |
| 1486 | 1535 | }; |
| 1487 | 1536 | |
| 1488 | 1537 | /// Tells whether spawning child processes is supported (e.g. via ChildProcess) |
| 1489 | pub const can_spawn = switch (builtin.os.tag) { | |
| 1538 | pub const can_spawn = switch (native_os) { | |
| 1490 | 1539 | .wasi, .watchos, .tvos => false, |
| 1491 | 1540 | else => true, |
| 1492 | 1541 | }; |
| 1493 | 1542 | |
| 1494 | pub const ExecvError = std.os.ExecveError || error{OutOfMemory}; | |
| 1543 | pub const ExecvError = std.posix.ExecveError || error{OutOfMemory}; | |
| 1495 | 1544 | |
| 1496 | 1545 | /// Replaces the current process image with the executed process. |
| 1497 | 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 | 1549 | /// `argv[0]` is the executable path. |
| 1501 | 1550 | /// This function also uses the PATH environment variable to get the full path to the executable. |
| 1502 | 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. | |
| 1504 | 1553 | pub fn execv(allocator: Allocator, argv: []const []const u8) ExecvError { |
| 1505 | 1554 | return execve(allocator, argv, null); |
| 1506 | 1555 | } |
| ... | ... | @@ -1512,7 +1561,7 @@ pub fn execv(allocator: Allocator, argv: []const []const u8) ExecvError { |
| 1512 | 1561 | /// `argv[0]` is the executable path. |
| 1513 | 1562 | /// This function also uses the PATH environment variable to get the full path to the executable. |
| 1514 | 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. | |
| 1516 | 1565 | pub fn execve( |
| 1517 | 1566 | allocator: Allocator, |
| 1518 | 1567 | argv: []const []const u8, |
| ... | ... | @@ -1536,14 +1585,14 @@ pub fn execve( |
| 1536 | 1585 | } else if (builtin.output_mode == .Exe) { |
| 1537 | 1586 | // Then we have Zig start code and this works. |
| 1538 | 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 | 1589 | } else { |
| 1541 | 1590 | // TODO come up with a solution for this. |
| 1542 | 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 | }; |
| 1545 | 1594 | |
| 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 | } |
| 1548 | 1597 | |
| 1549 | 1598 | pub const TotalSystemMemoryError = error{ |
| ... | ... | @@ -1555,14 +1604,14 @@ pub const TotalSystemMemoryError = error{ |
| 1555 | 1604 | /// and Linux's /proc/meminfo reporting more memory when |
| 1556 | 1605 | /// using QEMU user mode emulation. |
| 1557 | 1606 | pub fn totalSystemMemory() TotalSystemMemoryError!u64 { |
| 1558 | switch (builtin.os.tag) { | |
| 1607 | switch (native_os) { | |
| 1559 | 1608 | .linux => { |
| 1560 | 1609 | return totalSystemMemoryLinux() catch return error.UnknownTotalSystemMemory; |
| 1561 | 1610 | }, |
| 1562 | 1611 | .freebsd => { |
| 1563 | 1612 | var physmem: c_ulong = undefined; |
| 1564 | 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 | 1615 | error.NameTooLong, error.UnknownName => unreachable, |
| 1567 | 1616 | else => return error.UnknownTotalSystemMemory, |
| 1568 | 1617 | }; |
| ... | ... | @@ -1570,12 +1619,12 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 { |
| 1570 | 1619 | }, |
| 1571 | 1620 | .openbsd => { |
| 1572 | 1621 | const mib: [2]c_int = [_]c_int{ |
| 1573 | std.os.CTL.HW, | |
| 1574 | std.os.HW.PHYSMEM64, | |
| 1622 | posix.CTL.HW, | |
| 1623 | posix.HW.PHYSMEM64, | |
| 1575 | 1624 | }; |
| 1576 | 1625 | var physmem: i64 = undefined; |
| 1577 | 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 | 1628 | error.NameTooLong => unreachable, // constant, known good value |
| 1580 | 1629 | error.PermissionDenied => unreachable, // only when setting values, |
| 1581 | 1630 | error.SystemResources => unreachable, // memory already on the stack |
| ... | ... | @@ -1586,11 +1635,11 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 { |
| 1586 | 1635 | return @as(u64, @bitCast(physmem)); |
| 1587 | 1636 | }, |
| 1588 | 1637 | .windows => { |
| 1589 | var sbi: std.os.windows.SYSTEM_BASIC_INFORMATION = undefined; | |
| 1590 | const rc = std.os.windows.ntdll.NtQuerySystemInformation( | |
| 1638 | var sbi: windows.SYSTEM_BASIC_INFORMATION = undefined; | |
| 1639 | const rc = windows.ntdll.NtQuerySystemInformation( | |
| 1591 | 1640 | .SystemBasicInformation, |
| 1592 | 1641 | &sbi, |
| 1593 | @sizeOf(std.os.windows.SYSTEM_BASIC_INFORMATION), | |
| 1642 | @sizeOf(windows.SYSTEM_BASIC_INFORMATION), | |
| 1594 | 1643 | null, |
| 1595 | 1644 | ); |
| 1596 | 1645 | if (rc != .SUCCESS) { |
lib/std/start.zig+40-5| ... | ... | @@ -407,7 +407,7 @@ fn posixCallMainAndExit() callconv(.C) noreturn { |
| 407 | 407 | // FIXME: Make __aeabi_read_tp call the kernel helper kuser_get_tls |
| 408 | 408 | // For the time being use a simple abort instead of a @panic call to |
| 409 | 409 | // keep the binary bloat under control. |
| 410 | std.os.abort(); | |
| 410 | std.posix.abort(); | |
| 411 | 411 | } |
| 412 | 412 | } |
| 413 | 413 | |
| ... | ... | @@ -422,7 +422,7 @@ fn posixCallMainAndExit() callconv(.C) noreturn { |
| 422 | 422 | expandStackSize(phdrs); |
| 423 | 423 | } |
| 424 | 424 | |
| 425 | std.os.exit(callMainWithArgs(argc, argv, envp)); | |
| 425 | std.posix.exit(callMainWithArgs(argc, argv, envp)); | |
| 426 | 426 | } |
| 427 | 427 | |
| 428 | 428 | fn expandStackSize(phdrs: []elf.Phdr) void { |
| ... | ... | @@ -432,13 +432,13 @@ fn expandStackSize(phdrs: []elf.Phdr) void { |
| 432 | 432 | assert(phdr.p_memsz % std.mem.page_size == 0); |
| 433 | 433 | |
| 434 | 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; | |
| 436 | 436 | |
| 437 | 437 | // Clamp to limits.max . |
| 438 | 438 | const wanted_stack_size = @min(phdr.p_memsz, limits.max); |
| 439 | 439 | |
| 440 | 440 | if (wanted_stack_size > limits.cur) { |
| 441 | std.os.setrlimit(.STACK, .{ | |
| 441 | std.posix.setrlimit(.STACK, .{ | |
| 442 | 442 | .cur = wanted_stack_size, |
| 443 | 443 | .max = limits.max, |
| 444 | 444 | }) catch { |
| ... | ... | @@ -464,7 +464,7 @@ inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { |
| 464 | 464 | std.os.environ = envp; |
| 465 | 465 | |
| 466 | 466 | std.debug.maybeEnableSegfaultHandler(); |
| 467 | std.os.maybeIgnoreSigpipe(); | |
| 467 | maybeIgnoreSigpipe(); | |
| 468 | 468 | |
| 469 | 469 | return callMain(); |
| 470 | 470 | } |
| ... | ... | @@ -563,3 +563,38 @@ pub fn call_wWinMain() std.os.windows.INT { |
| 563 | 563 | // second parameter hPrevInstance, MSDN: "This parameter is always NULL" |
| 564 | 564 | return root.wWinMain(hInstance, null, lpCmdLine, nCmdShow); |
| 565 | 565 | } |
| 566 | ||
| 567 | fn 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 | ||
| 600 | fn noopSigHandler(_: c_int) callconv(.C) void {} |
lib/std/std.zig+5-2| ... | ... | @@ -85,12 +85,11 @@ pub const math = @import("math.zig"); |
| 85 | 85 | pub const mem = @import("mem.zig"); |
| 86 | 86 | pub const meta = @import("meta.zig"); |
| 87 | 87 | pub const net = @import("net.zig"); |
| 88 | pub const posix = @import("os.zig"); | |
| 89 | /// Non-portable Operating System-specific API. | |
| 90 | 88 | pub const os = @import("os.zig"); |
| 91 | 89 | pub const once = @import("once.zig").once; |
| 92 | 90 | pub const packed_int_array = @import("packed_int_array.zig"); |
| 93 | 91 | pub const pdb = @import("pdb.zig"); |
| 92 | pub const posix = @import("posix.zig"); | |
| 94 | 93 | pub const process = @import("process.zig"); |
| 95 | 94 | /// Deprecated: use `Random` instead. |
| 96 | 95 | pub const rand = Random; |
| ... | ... | @@ -170,3 +169,7 @@ comptime { |
| 170 | 169 | test { |
| 171 | 170 | testing.refAllDecls(@This()); |
| 172 | 171 | } |
| 172 | ||
| 173 | comptime { | |
| 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 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const testing = std.testing; |
| 5 | const os = std.os; | |
| 6 | 5 | const math = std.math; |
| 6 | const windows = std.os.windows; | |
| 7 | const posix = std.posix; | |
| 7 | 8 | |
| 8 | 9 | pub const epoch = @import("time/epoch.zig"); |
| 9 | 10 | |
| ... | ... | @@ -11,8 +12,8 @@ pub const epoch = @import("time/epoch.zig"); |
| 11 | 12 | pub fn sleep(nanoseconds: u64) void { |
| 12 | 13 | if (builtin.os.tag == .windows) { |
| 13 | 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 | os.windows.kernel32.Sleep(ms); | |
| 15 | const ms = math.cast(windows.DWORD, big_ms_from_ns) orelse math.maxInt(windows.DWORD); | |
| 16 | windows.kernel32.Sleep(ms); | |
| 16 | 17 | return; |
| 17 | 18 | } |
| 18 | 19 | |
| ... | ... | @@ -40,7 +41,7 @@ pub fn sleep(nanoseconds: u64) void { |
| 40 | 41 | } |
| 41 | 42 | |
| 42 | 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 | 45 | const us_from_ns = nanoseconds / ns_per_us; |
| 45 | 46 | const us = math.cast(usize, us_from_ns) orelse math.maxInt(usize); |
| 46 | 47 | _ = boot_services.stall(us); |
| ... | ... | @@ -49,7 +50,7 @@ pub fn sleep(nanoseconds: u64) void { |
| 49 | 50 | |
| 50 | 51 | const s = nanoseconds / ns_per_s; |
| 51 | 52 | const ns = nanoseconds % ns_per_s; |
| 52 | std.os.nanosleep(s, ns); | |
| 53 | posix.nanosleep(s, ns); | |
| 53 | 54 | } |
| 54 | 55 | |
| 55 | 56 | test "sleep" { |
| ... | ... | @@ -60,7 +61,7 @@ test "sleep" { |
| 60 | 61 | /// Precision of timing depends on the hardware and operating system. |
| 61 | 62 | /// The return value is signed because it is possible to have a date that is |
| 62 | 63 | /// before the epoch. |
| 63 | /// See `std.os.clock_gettime` for a POSIX timestamp. | |
| 64 | /// See `posix.clock_gettime` for a POSIX timestamp. | |
| 64 | 65 | pub fn timestamp() i64 { |
| 65 | 66 | return @divFloor(milliTimestamp(), ms_per_s); |
| 66 | 67 | } |
| ... | ... | @@ -69,7 +70,7 @@ pub fn timestamp() i64 { |
| 69 | 70 | /// Precision of timing depends on the hardware and operating system. |
| 70 | 71 | /// The return value is signed because it is possible to have a date that is |
| 71 | 72 | /// before the epoch. |
| 72 | /// See `std.os.clock_gettime` for a POSIX timestamp. | |
| 73 | /// See `posix.clock_gettime` for a POSIX timestamp. | |
| 73 | 74 | pub fn milliTimestamp() i64 { |
| 74 | 75 | return @as(i64, @intCast(@divFloor(nanoTimestamp(), ns_per_ms))); |
| 75 | 76 | } |
| ... | ... | @@ -78,7 +79,7 @@ pub fn milliTimestamp() i64 { |
| 78 | 79 | /// Precision of timing depends on the hardware and operating system. |
| 79 | 80 | /// The return value is signed because it is possible to have a date that is |
| 80 | 81 | /// before the epoch. |
| 81 | /// See `std.os.clock_gettime` for a POSIX timestamp. | |
| 82 | /// See `posix.clock_gettime` for a POSIX timestamp. | |
| 82 | 83 | pub fn microTimestamp() i64 { |
| 83 | 84 | return @as(i64, @intCast(@divFloor(nanoTimestamp(), ns_per_us))); |
| 84 | 85 | } |
| ... | ... | @@ -88,21 +89,21 @@ pub fn microTimestamp() i64 { |
| 88 | 89 | /// On Windows this has a maximum granularity of 100 nanoseconds. |
| 89 | 90 | /// The return value is signed because it is possible to have a date that is |
| 90 | 91 | /// before the epoch. |
| 91 | /// See `std.os.clock_gettime` for a POSIX timestamp. | |
| 92 | /// See `posix.clock_gettime` for a POSIX timestamp. | |
| 92 | 93 | pub fn nanoTimestamp() i128 { |
| 93 | 94 | switch (builtin.os.tag) { |
| 94 | 95 | .windows => { |
| 95 | 96 | // FileTime has a granularity of 100 nanoseconds and uses the NTFS/Windows epoch, |
| 96 | 97 | // which is 1601-01-01. |
| 97 | 98 | const epoch_adj = epoch.windows * (ns_per_s / 100); |
| 98 | var ft: os.windows.FILETIME = undefined; | |
| 99 | os.windows.kernel32.GetSystemTimeAsFileTime(&ft); | |
| 99 | var ft: windows.FILETIME = undefined; | |
| 100 | windows.kernel32.GetSystemTimeAsFileTime(&ft); | |
| 100 | 101 | const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime; |
| 101 | 102 | return @as(i128, @as(i64, @bitCast(ft64)) + epoch_adj) * 100; |
| 102 | 103 | }, |
| 103 | 104 | .wasi => { |
| 104 | var ns: os.wasi.timestamp_t = undefined; | |
| 105 | const err = os.wasi.clock_time_get(.REALTIME, 1, &ns); | |
| 105 | var ns: std.os.wasi.timestamp_t = undefined; | |
| 106 | const err = std.os.wasi.clock_time_get(.REALTIME, 1, &ns); | |
| 106 | 107 | assert(err == .SUCCESS); |
| 107 | 108 | return ns; |
| 108 | 109 | }, |
| ... | ... | @@ -113,8 +114,8 @@ pub fn nanoTimestamp() i128 { |
| 113 | 114 | return value.toEpoch(); |
| 114 | 115 | }, |
| 115 | 116 | else => { |
| 116 | var ts: os.timespec = undefined; | |
| 117 | os.clock_gettime(os.CLOCK.REALTIME, &ts) catch |err| switch (err) { | |
| 117 | var ts: posix.timespec = undefined; | |
| 118 | posix.clock_gettime(posix.CLOCK.REALTIME, &ts) catch |err| switch (err) { | |
| 118 | 119 | error.UnsupportedClock, error.Unexpected => return 0, // "Precision of timing depends on hardware and OS". |
| 119 | 120 | }; |
| 120 | 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 | 173 | /// It also tries to be monotonic, but this is not a guarantee due to OS/hardware bugs. |
| 173 | 174 | /// If you need monotonic readings for elapsed time, consider `Timer` instead. |
| 174 | 175 | pub const Instant = struct { |
| 175 | timestamp: if (is_posix) os.timespec else u64, | |
| 176 | timestamp: if (is_posix) posix.timespec else u64, | |
| 176 | 177 | |
| 177 | 178 | // true if we should use clock_gettime() |
| 178 | 179 | const is_posix = switch (builtin.os.tag) { |
| ... | ... | @@ -188,11 +189,11 @@ pub const Instant = struct { |
| 188 | 189 | const clock_id = switch (builtin.os.tag) { |
| 189 | 190 | .windows => { |
| 190 | 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 | 194 | .wasi => { |
| 194 | var ns: os.wasi.timestamp_t = undefined; | |
| 195 | const rc = os.wasi.clock_time_get(.MONOTONIC, 1, &ns); | |
| 195 | var ns: std.os.wasi.timestamp_t = undefined; | |
| 196 | const rc = std.os.wasi.clock_time_get(.MONOTONIC, 1, &ns); | |
| 196 | 197 | if (rc != .SUCCESS) return error.Unsupported; |
| 197 | 198 | return .{ .timestamp = ns }; |
| 198 | 199 | }, |
| ... | ... | @@ -204,21 +205,21 @@ pub const Instant = struct { |
| 204 | 205 | }, |
| 205 | 206 | // On darwin, use UPTIME_RAW instead of MONOTONIC as it ticks while |
| 206 | 207 | // suspended. |
| 207 | .macos, .ios, .tvos, .watchos => os.CLOCK.UPTIME_RAW, | |
| 208 | .macos, .ios, .tvos, .watchos => posix.CLOCK.UPTIME_RAW, | |
| 208 | 209 | // On freebsd derivatives, use MONOTONIC_FAST as currently there's |
| 209 | 210 | // no precision tradeoff. |
| 210 | .freebsd, .dragonfly => os.CLOCK.MONOTONIC_FAST, | |
| 211 | .freebsd, .dragonfly => posix.CLOCK.MONOTONIC_FAST, | |
| 211 | 212 | // On linux, use BOOTTIME instead of MONOTONIC as it ticks while |
| 212 | 213 | // suspended. |
| 213 | .linux => os.CLOCK.BOOTTIME, | |
| 214 | .linux => posix.CLOCK.BOOTTIME, | |
| 214 | 215 | // On other posix systems, MONOTONIC is generally the fastest and |
| 215 | 216 | // ticks while suspended. |
| 216 | else => os.CLOCK.MONOTONIC, | |
| 217 | else => posix.CLOCK.MONOTONIC, | |
| 217 | 218 | }; |
| 218 | 219 | |
| 219 | var ts: os.timespec = undefined; | |
| 220 | os.clock_gettime(clock_id, &ts) catch return error.Unsupported; | |
| 221 | return Instant{ .timestamp = ts }; | |
| 220 | var ts: posix.timespec = undefined; | |
| 221 | posix.clock_gettime(clock_id, &ts) catch return error.Unsupported; | |
| 222 | return .{ .timestamp = ts }; | |
| 222 | 223 | } |
| 223 | 224 | |
| 224 | 225 | /// Quickly compares two instances between each other. |
| ... | ... | @@ -245,7 +246,7 @@ pub const Instant = struct { |
| 245 | 246 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data |
| 246 | 247 | // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm |
| 247 | 248 | const qpc = self.timestamp - earlier.timestamp; |
| 248 | const qpf = os.windows.QueryPerformanceFrequency(); | |
| 249 | const qpf = windows.QueryPerformanceFrequency(); | |
| 249 | 250 | |
| 250 | 251 | // 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it. |
| 251 | 252 | // 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 | 1002 | } |
| 1003 | 1003 | |
| 1004 | 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 | }; |
| 1008 | 1008 |
lib/std/zig/Server.zig+1-1| ... | ... | @@ -146,7 +146,7 @@ pub fn serveMessage( |
| 146 | 146 | header: OutMessage.Header, |
| 147 | 147 | bufs: []const []const u8, |
| 148 | 148 | ) !void { |
| 149 | var iovecs: [10]std.os.iovec_const = undefined; | |
| 149 | var iovecs: [10]std.posix.iovec_const = undefined; | |
| 150 | 150 | const header_le = bswap(header); |
| 151 | 151 | iovecs[0] = .{ |
| 152 | 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 | 168 | if (query.os_tag == null) { |
| 169 | 169 | switch (builtin.target.os.tag) { |
| 170 | 170 | .linux => { |
| 171 | const uts = std.os.uname(); | |
| 171 | const uts = posix.uname(); | |
| 172 | 172 | const release = mem.sliceTo(&uts.release, 0); |
| 173 | 173 | // The release field sometimes has a weird format, |
| 174 | 174 | // `Version.parse` will attempt to find some meaningful interpretation. |
| ... | ... | @@ -181,7 +181,7 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { |
| 181 | 181 | } |
| 182 | 182 | }, |
| 183 | 183 | .solaris, .illumos => { |
| 184 | const uts = std.os.uname(); | |
| 184 | const uts = posix.uname(); | |
| 185 | 185 | const release = mem.sliceTo(&uts.release, 0); |
| 186 | 186 | if (std.SemanticVersion.parse(release)) |ver| { |
| 187 | 187 | os.version_range.semver.min = ver; |
| ... | ... | @@ -206,7 +206,7 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { |
| 206 | 206 | var value: u32 = undefined; |
| 207 | 207 | var len: usize = @sizeOf(@TypeOf(value)); |
| 208 | 208 | |
| 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 | 210 | error.NameTooLong => unreachable, // constant, known good value |
| 211 | 211 | error.PermissionDenied => unreachable, // only when setting values, |
| 212 | 212 | error.SystemResources => unreachable, // memory already on the stack |
| ... | ... | @@ -257,15 +257,15 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { |
| 257 | 257 | }, |
| 258 | 258 | .openbsd => { |
| 259 | 259 | const mib: [2]c_int = [_]c_int{ |
| 260 | std.os.CTL.KERN, | |
| 261 | std.os.KERN.OSRELEASE, | |
| 260 | posix.CTL.KERN, | |
| 261 | posix.KERN.OSRELEASE, | |
| 262 | 262 | }; |
| 263 | 263 | var buf: [64]u8 = undefined; |
| 264 | 264 | // consider that sysctl result includes null-termination |
| 265 | 265 | // reserve 1 byte to ensure we never overflow when appending ".0" |
| 266 | 266 | var len: usize = buf.len - 1; |
| 267 | 267 | |
| 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 | 269 | error.NameTooLong => unreachable, // constant, known good value |
| 270 | 270 | error.PermissionDenied => unreachable, // only when setting values, |
| 271 | 271 | error.SystemResources => unreachable, // memory already on the stack |
| ... | ... | @@ -636,8 +636,8 @@ pub fn abiAndDynamicLinkerFromFile( |
| 636 | 636 | |
| 637 | 637 | // So far, no luck. Next we try to see if the information is |
| 638 | 638 | // present in the symlink data for the dynamic linker path. |
| 639 | var link_buf: [std.os.PATH_MAX]u8 = undefined; | |
| 640 | const link_name = std.os.readlink(dl_path, &link_buf) catch |err| switch (err) { | |
| 639 | var link_buf: [posix.PATH_MAX]u8 = undefined; | |
| 640 | const link_name = posix.readlink(dl_path, &link_buf) catch |err| switch (err) { | |
| 641 | 641 | error.NameTooLong => unreachable, |
| 642 | 642 | error.InvalidUtf8 => unreachable, // WASI only |
| 643 | 643 | error.InvalidWtf8 => unreachable, // Windows only |
| ... | ... | @@ -670,7 +670,7 @@ pub fn abiAndDynamicLinkerFromFile( |
| 670 | 670 | |
| 671 | 671 | // Nothing worked so far. Finally we fall back to hard-coded search paths. |
| 672 | 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 | 674 | var index: usize = 0; |
| 675 | 675 | const prefix = "/lib/"; |
| 676 | 676 | const cpu_arch = @tagName(result.cpu.arch); |
| ... | ... | @@ -1138,6 +1138,7 @@ const fs = std.fs; |
| 1138 | 1138 | const assert = std.debug.assert; |
| 1139 | 1139 | const Target = std.Target; |
| 1140 | 1140 | const native_endian = builtin.cpu.arch.endian(); |
| 1141 | const posix = std.posix; | |
| 1141 | 1142 | |
| 1142 | 1143 | test { |
| 1143 | 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 | 137 | // variables to search for headers and libraries. |
| 138 | 138 | // We use os.getenv here since this part won't be executed on |
| 139 | 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 | 141 | var it = mem.tokenizeScalar(u8, c_include_path, ':'); |
| 142 | 142 | while (it.next()) |dir| { |
| 143 | 143 | try self.addIncludeDir(dir); |
| 144 | 144 | } |
| 145 | 145 | } |
| 146 | 146 | |
| 147 | if (std.os.getenv("CPLUS_INCLUDE_PATH")) |cplus_include_path| { | |
| 147 | if (std.posix.getenv("CPLUS_INCLUDE_PATH")) |cplus_include_path| { | |
| 148 | 148 | var it = mem.tokenizeScalar(u8, cplus_include_path, ':'); |
| 149 | 149 | while (it.next()) |dir| { |
| 150 | 150 | try self.addIncludeDir(dir); |
| 151 | 151 | } |
| 152 | 152 | } |
| 153 | 153 | |
| 154 | if (std.os.getenv("LIBRARY_PATH")) |library_path| { | |
| 154 | if (std.posix.getenv("LIBRARY_PATH")) |library_path| { | |
| 155 | 155 | var it = mem.tokenizeScalar(u8, library_path, ':'); |
| 156 | 156 | while (it.next()) |dir| { |
| 157 | 157 | try self.addLibDir(dir); |
lib/std/zig/system/darwin/macos.zig+1-2| ... | ... | @@ -3,7 +3,6 @@ const builtin = @import("builtin"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const mem = std.mem; |
| 5 | 5 | const testing = std.testing; |
| 6 | const os = std.os; | |
| 7 | 6 | |
| 8 | 7 | const Target = std.Target; |
| 9 | 8 | |
| ... | ... | @@ -397,7 +396,7 @@ test "detect" { |
| 397 | 396 | pub fn detectNativeCpuAndFeatures() ?Target.Cpu { |
| 398 | 397 | var cpu_family: std.c.CPUFAMILY = undefined; |
| 399 | 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 | 400 | error.NameTooLong => unreachable, // constant, known good value |
| 402 | 401 | error.PermissionDenied => unreachable, // only when setting values, |
| 403 | 402 | error.SystemResources => unreachable, // memory already on the stack |
src/Compilation.zig+4-4| ... | ... | @@ -1137,7 +1137,7 @@ fn addModuleTableToCacheHash( |
| 1137 | 1137 | root_mod: *Package.Module, |
| 1138 | 1138 | main_mod: *Package.Module, |
| 1139 | 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 | 1141 | var seen_table: std.AutoArrayHashMapUnmanaged(*Package.Module, void) = .{}; |
| 1142 | 1142 | defer seen_table.deinit(gpa); |
| 1143 | 1143 | |
| ... | ... | @@ -2741,7 +2741,7 @@ const Header = extern struct { |
| 2741 | 2741 | /// saved, such as the target and most CLI flags. A cache hit will only occur |
| 2742 | 2742 | /// when subsequent compiler invocations use the same set of flags. |
| 2743 | 2743 | pub 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 | 2745 | var bufs_len: usize = 0; |
| 2746 | 2746 | |
| 2747 | 2747 | const lf = comp.bin_file orelse return; |
| ... | ... | @@ -2808,7 +2808,7 @@ pub fn saveState(comp: *Compilation) !void { |
| 2808 | 2808 | try af.finish(); |
| 2809 | 2809 | } |
| 2810 | 2810 | |
| 2811 | fn addBuf(bufs_list: []std.os.iovec_const, bufs_len: *usize, buf: []const u8) void { | |
| 2811 | fn addBuf(bufs_list: []std.posix.iovec_const, bufs_len: *usize, buf: []const u8) void { | |
| 2812 | 2812 | const i = bufs_len.*; |
| 2813 | 2813 | bufs_len.* = i + 1; |
| 2814 | 2814 | bufs_list[i] = .{ |
| ... | ... | @@ -3791,7 +3791,7 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void { |
| 3791 | 3791 | break :p padding_buffer[0..n]; |
| 3792 | 3792 | }; |
| 3793 | 3793 | |
| 3794 | var header_and_trailer: [2]std.os.iovec_const = .{ | |
| 3794 | var header_and_trailer: [2]std.posix.iovec_const = .{ | |
| 3795 | 3795 | .{ .iov_base = header_bytes.ptr, .iov_len = header_bytes.len }, |
| 3796 | 3796 | .{ .iov_base = padding.ptr, .iov_len = padding.len }, |
| 3797 | 3797 | }; |
src/DarwinPosixSpawn.zig created+224| ... | ... | @@ -0,0 +1,224 @@ |
| 1 | const errno = std.posix.errno; | |
| 2 | const unexpectedErrno = std.posix.unexpectedErrno; | |
| 3 | ||
| 4 | pub 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 | ||
| 21 | pub 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 | ||
| 61 | pub 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 | ||
| 160 | pub 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 | ||
| 171 | pub 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 | ||
| 207 | pub 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 | ||
| 224 | const std = @import("std"); |
src/Module.zig+2-2| ... | ... | @@ -2396,7 +2396,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 2396 | 2396 | .stat_inode = stat.inode, |
| 2397 | 2397 | .stat_mtime = stat.mtime, |
| 2398 | 2398 | }; |
| 2399 | var iovecs = [_]std.os.iovec_const{ | |
| 2399 | var iovecs = [_]std.posix.iovec_const{ | |
| 2400 | 2400 | .{ |
| 2401 | 2401 | .iov_base = @as([*]const u8, @ptrCast(&header)), |
| 2402 | 2402 | .iov_len = @sizeOf(Zir.Header), |
| ... | ... | @@ -2484,7 +2484,7 @@ fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.File) |
| 2484 | 2484 | else |
| 2485 | 2485 | @as([*]u8, @ptrCast(zir.instructions.items(.data).ptr)); |
| 2486 | 2486 | |
| 2487 | var iovecs = [_]std.os.iovec{ | |
| 2487 | var iovecs = [_]std.posix.iovec{ | |
| 2488 | 2488 | .{ |
| 2489 | 2489 | .iov_base = @as([*]u8, @ptrCast(zir.instructions.items(.tag).ptr)), |
| 2490 | 2490 | .iov_len = header.instructions_len, |
src/Package/Fetch.zig+10-9| ... | ... | @@ -482,7 +482,7 @@ fn runResource( |
| 482 | 482 | // Compute the package hash based on the remaining files in the temporary |
| 483 | 483 | // directory. |
| 484 | 484 | |
| 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 | 486 | // https://github.com/ziglang/zig/issues/17095 |
| 487 | 487 | tmp_directory.handle.close(); |
| 488 | 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 | 1153 | std.tar.pipeToFileSystem(out_dir, reader, .{ |
| 1154 | 1154 | .diagnostics = &diagnostics, |
| 1155 | 1155 | .strip_components = 1, |
| 1156 | // TODO: we would like to set this to executable_bit_only, but two | |
| 1157 | // things need to happen before that: | |
| 1158 | // 1. the tar implementation needs to support it | |
| 1159 | // 2. the hashing algorithm here needs to support detecting the is_executable | |
| 1160 | // bit on Windows from the ACLs (see the isExecutable function). | |
| 1156 | // https://github.com/ziglang/zig/issues/17463 | |
| 1161 | 1157 | .mode_mode = .ignore, |
| 1162 | 1158 | .exclude_empty_directories = true, |
| 1163 | 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 | 1538 | .file => { |
| 1543 | 1539 | var file = try dir.openFile(hashed_file.fs_path, .{}); |
| 1544 | 1540 | defer file.close(); |
| 1541 | // When implementing https://github.com/ziglang/zig/issues/17463 | |
| 1542 | // this will change to hard-coded `false`. | |
| 1545 | 1543 | hasher.update(&.{ 0, @intFromBool(try isExecutable(file)) }); |
| 1546 | 1544 | while (true) { |
| 1547 | 1545 | const bytes_read = try file.read(&buf); |
| ... | ... | @@ -1568,15 +1566,17 @@ fn deleteFileFallible(dir: fs.Dir, deleted_file: *DeletedFile) DeletedFile.Error |
| 1568 | 1566 | } |
| 1569 | 1567 | |
| 1570 | 1568 | fn isExecutable(file: fs.File) !bool { |
| 1571 | if (builtin.os.tag == .windows) { | |
| 1572 | // TODO check the ACL on Windows. | |
| 1569 | // When implementing https://github.com/ziglang/zig/issues/17463 | |
| 1570 | // this function will not check the mode but instead check if the file is an ELF | |
| 1571 | // file or has a shebang line. | |
| 1572 | if (native_os == .windows) { | |
| 1573 | 1573 | // Until this is implemented, this could be a false negative on |
| 1574 | 1574 | // Windows, which is why we do not yet set executable_bit_only above |
| 1575 | 1575 | // when unpacking the tarball. |
| 1576 | 1576 | return false; |
| 1577 | 1577 | } else { |
| 1578 | 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 | } |
| 1582 | 1582 | |
| ... | ... | @@ -1694,6 +1694,7 @@ const git = @import("Fetch/git.zig"); |
| 1694 | 1694 | const Package = @import("../Package.zig"); |
| 1695 | 1695 | const Manifest = Package.Manifest; |
| 1696 | 1696 | const ErrorBundle = std.zig.ErrorBundle; |
| 1697 | const native_os = builtin.os.tag; | |
| 1697 | 1698 | |
| 1698 | 1699 | test { |
| 1699 | 1700 | _ = Filter; |
src/crash_report.zig+21-20| ... | ... | @@ -2,9 +2,10 @@ const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const build_options = @import("build_options"); |
| 4 | 4 | const debug = std.debug; |
| 5 | const os = std.os; | |
| 6 | 5 | const io = std.io; |
| 7 | 6 | const print_zir = @import("print_zir.zig"); |
| 7 | const windows = std.os.windows; | |
| 8 | const posix = std.posix; | |
| 8 | 9 | const native_os = builtin.os.tag; |
| 9 | 10 | |
| 10 | 11 | const Module = @import("Module.zig"); |
| ... | ... | @@ -156,14 +157,14 @@ pub fn attachSegfaultHandler() void { |
| 156 | 157 | if (!debug.have_segfault_handling_support) { |
| 157 | 158 | @compileError("segfault handler not supported for this target"); |
| 158 | 159 | } |
| 159 | if (builtin.os.tag == .windows) { | |
| 160 | _ = os.windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows); | |
| 160 | if (native_os == .windows) { | |
| 161 | _ = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows); | |
| 161 | 162 | return; |
| 162 | 163 | } |
| 163 | var act = os.Sigaction{ | |
| 164 | var act: posix.Sigaction = .{ | |
| 164 | 165 | .handler = .{ .sigaction = handleSegfaultPosix }, |
| 165 | .mask = os.empty_sigset, | |
| 166 | .flags = (os.SA.SIGINFO | os.SA.RESTART | os.SA.RESETHAND), | |
| 166 | .mask = posix.empty_sigset, | |
| 167 | .flags = (posix.SA.SIGINFO | posix.SA.RESTART | posix.SA.RESETHAND), | |
| 167 | 168 | }; |
| 168 | 169 | |
| 169 | 170 | debug.updateSegfaultHandler(&act) catch { |
| ... | ... | @@ -171,11 +172,11 @@ pub fn attachSegfaultHandler() void { |
| 171 | 172 | }; |
| 172 | 173 | } |
| 173 | 174 | |
| 174 | fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const anyopaque) callconv(.C) noreturn { | |
| 175 | fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*const anyopaque) callconv(.C) noreturn { | |
| 175 | 176 | // TODO: use alarm() here to prevent infinite loops |
| 176 | 177 | PanicSwitch.preDispatch(); |
| 177 | 178 | |
| 178 | const addr = switch (builtin.os.tag) { | |
| 179 | const addr = switch (native_os) { | |
| 179 | 180 | .linux => @intFromPtr(info.fields.sigfault.addr), |
| 180 | 181 | .freebsd, .macos => @intFromPtr(info.addr), |
| 181 | 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 | 187 | |
| 187 | 188 | var err_buffer: [128]u8 = undefined; |
| 188 | 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 | os.SIG.ILL => std.fmt.bufPrint(&err_buffer, "Illegal instruction at address 0x{x}", .{addr}) catch "Illegal instruction", | |
| 191 | os.SIG.BUS => std.fmt.bufPrint(&err_buffer, "Bus error at address 0x{x}", .{addr}) catch "Bus error", | |
| 190 | posix.SIG.SEGV => std.fmt.bufPrint(&err_buffer, "Segmentation fault at address 0x{x}", .{addr}) catch "Segmentation fault", | |
| 191 | posix.SIG.ILL => std.fmt.bufPrint(&err_buffer, "Illegal instruction at address 0x{x}", .{addr}) catch "Illegal instruction", | |
| 192 | posix.SIG.BUS => std.fmt.bufPrint(&err_buffer, "Bus error at address 0x{x}", .{addr}) catch "Bus error", | |
| 192 | 193 | else => std.fmt.bufPrint(&err_buffer, "Unknown error (signal {}) at address 0x{x}", .{ sig, addr }) catch "Unknown error", |
| 193 | 194 | }; |
| 194 | 195 | |
| ... | ... | @@ -210,20 +211,20 @@ const WindowsSegfaultMessage = union(enum) { |
| 210 | 211 | illegal_instruction: void, |
| 211 | 212 | }; |
| 212 | 213 | |
| 213 | fn handleSegfaultWindows(info: *os.windows.EXCEPTION_POINTERS) callconv(os.windows.WINAPI) c_long { | |
| 214 | fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(windows.WINAPI) c_long { | |
| 214 | 215 | switch (info.ExceptionRecord.ExceptionCode) { |
| 215 | os.windows.EXCEPTION_DATATYPE_MISALIGNMENT => handleSegfaultWindowsExtra(info, .{ .literal = "Unaligned Memory Access" }), | |
| 216 | os.windows.EXCEPTION_ACCESS_VIOLATION => handleSegfaultWindowsExtra(info, .segfault), | |
| 217 | os.windows.EXCEPTION_ILLEGAL_INSTRUCTION => handleSegfaultWindowsExtra(info, .illegal_instruction), | |
| 218 | os.windows.EXCEPTION_STACK_OVERFLOW => handleSegfaultWindowsExtra(info, .{ .literal = "Stack Overflow" }), | |
| 219 | else => return os.windows.EXCEPTION_CONTINUE_SEARCH, | |
| 216 | windows.EXCEPTION_DATATYPE_MISALIGNMENT => handleSegfaultWindowsExtra(info, .{ .literal = "Unaligned Memory Access" }), | |
| 217 | windows.EXCEPTION_ACCESS_VIOLATION => handleSegfaultWindowsExtra(info, .segfault), | |
| 218 | windows.EXCEPTION_ILLEGAL_INSTRUCTION => handleSegfaultWindowsExtra(info, .illegal_instruction), | |
| 219 | windows.EXCEPTION_STACK_OVERFLOW => handleSegfaultWindowsExtra(info, .{ .literal = "Stack Overflow" }), | |
| 220 | else => return windows.EXCEPTION_CONTINUE_SEARCH, | |
| 220 | 221 | } |
| 221 | 222 | } |
| 222 | 223 | |
| 223 | fn handleSegfaultWindowsExtra(info: *os.windows.EXCEPTION_POINTERS, comptime msg: WindowsSegfaultMessage) noreturn { | |
| 224 | fn handleSegfaultWindowsExtra(info: *windows.EXCEPTION_POINTERS, comptime msg: WindowsSegfaultMessage) noreturn { | |
| 224 | 225 | PanicSwitch.preDispatch(); |
| 225 | 226 | |
| 226 | const stack_ctx = if (@hasDecl(os.windows, "CONTEXT")) | |
| 227 | const stack_ctx = if (@hasDecl(windows, "CONTEXT")) | |
| 227 | 228 | StackContext{ .exception = info.ContextRecord } |
| 228 | 229 | else ctx: { |
| 229 | 230 | const addr = @intFromPtr(info.ExceptionRecord.ExceptionAddress); |
| ... | ... | @@ -488,7 +489,7 @@ const PanicSwitch = struct { |
| 488 | 489 | } |
| 489 | 490 | |
| 490 | 491 | noinline fn abort() noreturn { |
| 491 | os.abort(); | |
| 492 | std.process.abort(); | |
| 492 | 493 | } |
| 493 | 494 | |
| 494 | 495 | inline fn goTo(comptime func: anytype, args: anytype) noreturn { |
src/link.zig+2-2| ... | ... | @@ -254,7 +254,7 @@ pub const File = struct { |
| 254 | 254 | try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{}); |
| 255 | 255 | try emit.directory.handle.rename(tmp_sub_path, emit.sub_path); |
| 256 | 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 | 258 | log.warn("ptrace failure: {s}", .{@errorName(err)}); |
| 259 | 259 | }, |
| 260 | 260 | .macos => base.cast(MachO).?.ptraceAttach(pid) catch |err| { |
| ... | ... | @@ -305,7 +305,7 @@ pub const File = struct { |
| 305 | 305 | |
| 306 | 306 | if (base.child_pid) |pid| { |
| 307 | 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 | 309 | log.warn("ptrace failure: {s}", .{@errorName(err)}); |
| 310 | 310 | }, |
| 311 | 311 | else => return error.HotSwapUnavailableOnHostOperatingSystem, |
src/link/C.zig+2-2| ... | ... | @@ -518,7 +518,7 @@ const Flush = struct { |
| 518 | 518 | asm_buf: std.ArrayListUnmanaged(u8) = .{}, |
| 519 | 519 | |
| 520 | 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 | 522 | /// Keeps track of the total bytes of `all_buffers`. |
| 523 | 523 | file_size: u64 = 0, |
| 524 | 524 | |
| ... | ... | @@ -752,7 +752,7 @@ pub fn flushEmitH(module: *Module) !void { |
| 752 | 752 | |
| 753 | 753 | // We collect a list of buffers to write, and write them all at once with pwritev 😎 |
| 754 | 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 | 756 | defer all_buffers.deinit(); |
| 757 | 757 | |
| 758 | 758 | var file_size: u64 = zig_h.len; |
src/link/Dwarf.zig+4-4| ... | ... | @@ -2118,7 +2118,7 @@ fn pwriteDbgLineNops( |
| 2118 | 2118 | |
| 2119 | 2119 | const page_of_nops = [1]u8{DW.LNS.negate_stmt} ** 4096; |
| 2120 | 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 | 2122 | var vec_index: usize = 0; |
| 2123 | 2123 | { |
| 2124 | 2124 | var padding_left = prev_padding_size; |
| ... | ... | @@ -2235,7 +2235,7 @@ fn pwriteDbgInfoNops( |
| 2235 | 2235 | defer tracy.end(); |
| 2236 | 2236 | |
| 2237 | 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 | 2239 | var vec_index: usize = 0; |
| 2240 | 2240 | { |
| 2241 | 2241 | var padding_left = prev_padding_size; |
| ... | ... | @@ -2807,10 +2807,10 @@ fn genIncludeDirsAndFileNames(self: *Dwarf, arena: Allocator) !struct { |
| 2807 | 2807 | const full_path = try dif.mod.root.joinString(arena, dif.sub_file_path); |
| 2808 | 2808 | const dir_path = std.fs.path.dirname(full_path) orelse "."; |
| 2809 | 2809 | const sub_file_path = std.fs.path.basename(full_path); |
| 2810 | // TODO re-investigate if realpath is needed here | |
| 2810 | // https://github.com/ziglang/zig/issues/19353 | |
| 2811 | 2811 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 2812 | 2812 | const resolved = if (!std.fs.path.isAbsolute(dir_path)) |
| 2813 | std.os.realpath(dir_path, &buffer) catch dir_path | |
| 2813 | std.posix.realpath(dir_path, &buffer) catch dir_path | |
| 2814 | 2814 | else |
| 2815 | 2815 | dir_path; |
| 2816 | 2816 |
src/link/Elf.zig+8-8| ... | ... | @@ -422,7 +422,7 @@ pub fn createEmpty( |
| 422 | 422 | const index: File.Index = @intCast(try self.files.addOne(gpa)); |
| 423 | 423 | self.files.set(index, .{ .zig_object = .{ |
| 424 | 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 | 426 | zcu.main_mod.root_src_path, |
| 427 | 427 | )}), |
| 428 | 428 | } }); |
| ... | ... | @@ -1673,7 +1673,7 @@ pub const ParseError = error{ |
| 1673 | 1673 | NotSupported, |
| 1674 | 1674 | InvalidCharacter, |
| 1675 | 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; | |
| 1677 | 1677 | |
| 1678 | 1678 | pub fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void { |
| 1679 | 1679 | const tracy = trace(@src()); |
| ... | ... | @@ -1703,7 +1703,7 @@ fn parseObject(self: *Elf, path: []const u8) ParseError!void { |
| 1703 | 1703 | defer tracy.end(); |
| 1704 | 1704 | |
| 1705 | 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 | 1707 | const fh = try self.addFileHandle(handle); |
| 1708 | 1708 | |
| 1709 | 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 | 1723 | defer tracy.end(); |
| 1724 | 1724 | |
| 1725 | 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 | 1727 | const fh = try self.addFileHandle(handle); |
| 1728 | 1728 | |
| 1729 | 1729 | var archive = Archive{}; |
| ... | ... | @@ -1749,7 +1749,7 @@ fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void { |
| 1749 | 1749 | defer tracy.end(); |
| 1750 | 1750 | |
| 1751 | 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 | 1753 | defer handle.close(); |
| 1754 | 1754 | |
| 1755 | 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 | 1770 | defer tracy.end(); |
| 1771 | 1771 | |
| 1772 | 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 | 1774 | defer in_file.close(); |
| 1775 | 1775 | const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); |
| 1776 | 1776 | defer gpa.free(data); |
| ... | ... | @@ -5468,7 +5468,7 @@ pub fn file(self: *Elf, index: File.Index) ?File { |
| 5468 | 5468 | }; |
| 5469 | 5469 | } |
| 5470 | 5470 | |
| 5471 | pub fn addFileHandle(self: *Elf, handle: std.fs.File) !File.HandleIndex { | |
| 5471 | pub fn addFileHandle(self: *Elf, handle: fs.File) !File.HandleIndex { | |
| 5472 | 5472 | const gpa = self.base.comp.gpa; |
| 5473 | 5473 | const index: File.HandleIndex = @intCast(self.file_handles.items.len); |
| 5474 | 5474 | const fh = try self.file_handles.addOne(gpa); |
| ... | ... | @@ -6045,7 +6045,7 @@ fn fmtDumpState( |
| 6045 | 6045 | } |
| 6046 | 6046 | |
| 6047 | 6047 | /// Caller owns the memory. |
| 6048 | pub fn preadAllAlloc(allocator: Allocator, handle: std.fs.File, offset: u64, size: u64) ![]u8 { | |
| 6048 | pub fn preadAllAlloc(allocator: Allocator, handle: fs.File, offset: u64, size: u64) ![]u8 { | |
| 6049 | 6049 | const buffer = try allocator.alloc(u8, math.cast(usize, size) orelse return error.Overflow); |
| 6050 | 6050 | errdefer allocator.free(buffer); |
| 6051 | 6051 | const amt = try handle.preadAll(buffer, offset); |
src/link/Elf/ZigObject.zig+3-3| ... | ... | @@ -965,16 +965,16 @@ fn updateDeclCode( |
| 965 | 965 | if (elf_file.base.child_pid) |pid| { |
| 966 | 966 | switch (builtin.os.tag) { |
| 967 | 967 | .linux => { |
| 968 | var code_vec: [1]std.os.iovec_const = .{.{ | |
| 968 | var code_vec: [1]std.posix.iovec_const = .{.{ | |
| 969 | 969 | .iov_base = code.ptr, |
| 970 | 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 | 973 | .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(sym.address(.{}, elf_file))))), |
| 974 | 974 | .iov_len = code.len, |
| 975 | 975 | }}; |
| 976 | 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 | 978 | .SUCCESS => assert(rc == code.len), |
| 979 | 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 | 317 | if (elf_file.base.child_pid) |pid| { |
| 318 | 318 | switch (builtin.os.tag) { |
| 319 | 319 | .linux => { |
| 320 | var local_vec: [1]std.os.iovec_const = .{.{ | |
| 320 | var local_vec: [1]std.posix.iovec_const = .{.{ | |
| 321 | 321 | .iov_base = &buf, |
| 322 | 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 | 325 | .iov_base = @as([*]u8, @ptrFromInt(@as(usize, @intCast(vaddr)))), |
| 326 | 326 | .iov_len = buf.len, |
| 327 | 327 | }}; |
| 328 | 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 | 330 | .SUCCESS => assert(rc == buf.len), |
| 331 | 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 | 258 | const index: File.Index = @intCast(try self.files.addOne(gpa)); |
| 259 | 259 | self.files.set(index, .{ .zig_object = .{ |
| 260 | 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 | 262 | zcu.main_mod.root_src_path, |
| 263 | 263 | )}), |
| 264 | 264 | } }); |
| ... | ... | @@ -843,7 +843,7 @@ fn dumpArgv(self: *MachO, comp: *Compilation) !void { |
| 843 | 843 | } |
| 844 | 844 | |
| 845 | 845 | for (self.frameworks) |framework| { |
| 846 | const name = std.fs.path.stem(framework.path); | |
| 846 | const name = fs.path.stem(framework.path); | |
| 847 | 847 | const arg = if (framework.needed) |
| 848 | 848 | try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name}) |
| 849 | 849 | else if (framework.weak) |
| ... | ... | @@ -917,7 +917,7 @@ pub const ParseError = error{ |
| 917 | 917 | NotSupported, |
| 918 | 918 | Unhandled, |
| 919 | 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; | |
| 921 | 921 | |
| 922 | 922 | pub fn parsePositional(self: *MachO, path: []const u8, must_link: bool) ParseError!void { |
| 923 | 923 | const tracy = trace(@src()); |
| ... | ... | @@ -956,7 +956,7 @@ fn parseObject(self: *MachO, path: []const u8) ParseError!void { |
| 956 | 956 | defer tracy.end(); |
| 957 | 957 | |
| 958 | 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 | 960 | const handle = try self.addFileHandle(file); |
| 961 | 961 | const mtime: u64 = mtime: { |
| 962 | 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 | 992 | |
| 993 | 993 | const gpa = self.base.comp.gpa; |
| 994 | 994 | |
| 995 | const file = try std.fs.cwd().openFile(lib.path, .{}); | |
| 995 | const file = try fs.cwd().openFile(lib.path, .{}); | |
| 996 | 996 | const handle = try self.addFileHandle(file); |
| 997 | 997 | |
| 998 | 998 | var archive = Archive{}; |
| ... | ... | @@ -1029,7 +1029,7 @@ fn parseDylib(self: *MachO, lib: SystemLib, explicit: bool, fat_arch: ?fat.Arch) |
| 1029 | 1029 | |
| 1030 | 1030 | const gpa = self.base.comp.gpa; |
| 1031 | 1031 | |
| 1032 | const file = try std.fs.cwd().openFile(lib.path, .{}); | |
| 1032 | const file = try fs.cwd().openFile(lib.path, .{}); | |
| 1033 | 1033 | defer file.close(); |
| 1034 | 1034 | |
| 1035 | 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 | 1054 | defer tracy.end(); |
| 1055 | 1055 | |
| 1056 | 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 | 1058 | defer file.close(); |
| 1059 | 1059 | |
| 1060 | 1060 | 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 | 1080 | /// image unless overriden by -no_implicit_dylibs. |
| 1081 | 1081 | fn isHoisted(self: *MachO, install_name: []const u8) bool { |
| 1082 | 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 | 1084 | if (mem.startsWith(u8, dirname, "/usr/lib")) return true; |
| 1085 | 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 | 1087 | if (mem.indexOfScalar(u8, path, '.')) |index| { |
| 1088 | 1088 | if (mem.eql(u8, basename, path[0..index])) return true; |
| 1089 | 1089 | } |
| ... | ... | @@ -1105,7 +1105,7 @@ fn accessLibPath( |
| 1105 | 1105 | test_path.clearRetainingCapacity(); |
| 1106 | 1106 | try test_path.writer().print("{s}" ++ sep ++ "lib{s}{s}", .{ search_dir, name, ext }); |
| 1107 | 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 | 1109 | error.FileNotFound => continue, |
| 1110 | 1110 | else => |e| return e, |
| 1111 | 1111 | }; |
| ... | ... | @@ -1133,7 +1133,7 @@ fn accessFrameworkPath( |
| 1133 | 1133 | ext, |
| 1134 | 1134 | }); |
| 1135 | 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 | 1137 | error.FileNotFound => continue, |
| 1138 | 1138 | else => |e| return e, |
| 1139 | 1139 | }; |
| ... | ... | @@ -1181,7 +1181,7 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1181 | 1181 | |
| 1182 | 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); | |
| 1185 | 1185 | |
| 1186 | 1186 | // Framework |
| 1187 | 1187 | for (framework_dirs) |dir| { |
| ... | ... | @@ -1197,18 +1197,18 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1197 | 1197 | } |
| 1198 | 1198 | } |
| 1199 | 1199 | |
| 1200 | if (std.fs.path.isAbsolute(id.name)) { | |
| 1201 | const existing_ext = std.fs.path.extension(id.name); | |
| 1200 | if (fs.path.isAbsolute(id.name)) { | |
| 1201 | const existing_ext = fs.path.extension(id.name); | |
| 1202 | 1202 | const path = if (existing_ext.len > 0) id.name[0 .. id.name.len - existing_ext.len] else id.name; |
| 1203 | 1203 | for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| { |
| 1204 | 1204 | test_path.clearRetainingCapacity(); |
| 1205 | 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 | 1207 | } else { |
| 1208 | 1208 | try test_path.writer().print("{s}{s}", .{ path, ext }); |
| 1209 | 1209 | } |
| 1210 | 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 | 1212 | error.FileNotFound => continue, |
| 1213 | 1213 | else => |e| return e, |
| 1214 | 1214 | }; |
| ... | ... | @@ -1220,10 +1220,10 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1220 | 1220 | const dylib = self.getFile(dylib_index).?.dylib; |
| 1221 | 1221 | for (self.getFile(dylib.umbrella).?.dylib.rpaths.keys()) |rpath| { |
| 1222 | 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 | 1224 | try checked_paths.append(rel_path); |
| 1225 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | |
| 1226 | const full_path = std.fs.realpath(rel_path, &buffer) catch continue; | |
| 1225 | var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; | |
| 1226 | const full_path = fs.realpath(rel_path, &buffer) catch continue; | |
| 1227 | 1227 | break :full_path try arena.dupe(u8, full_path); |
| 1228 | 1228 | } |
| 1229 | 1229 | } else if (eatPrefix(id.name, "@loader_path/")) |_| { |
| ... | ... | @@ -1235,8 +1235,8 @@ fn parseDependentDylibs(self: *MachO) !void { |
| 1235 | 1235 | } |
| 1236 | 1236 | |
| 1237 | 1237 | try checked_paths.append(try arena.dupe(u8, id.name)); |
| 1238 | var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | |
| 1239 | if (std.fs.realpath(id.name, &buffer)) |full_path| { | |
| 1238 | var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; | |
| 1239 | if (fs.realpath(id.name, &buffer)) |full_path| { | |
| 1240 | 1240 | break :full_path try arena.dupe(u8, full_path); |
| 1241 | 1241 | } else |_| { |
| 1242 | 1242 | try self.reportMissingDependencyError( |
| ... | ... | @@ -3651,7 +3651,7 @@ pub fn getTarget(self: MachO) std.Target { |
| 3651 | 3651 | /// into a new inode, remove the original file, and rename the copy to match |
| 3652 | 3652 | /// the original file. This is super messy, but there doesn't seem any other |
| 3653 | 3653 | /// way to please the XNU. |
| 3654 | pub fn invalidateKernelCache(dir: std.fs.Dir, sub_path: []const u8) !void { | |
| 3654 | pub fn invalidateKernelCache(dir: fs.Dir, sub_path: []const u8) !void { | |
| 3655 | 3655 | if (comptime builtin.target.isDarwin() and builtin.target.cpu.arch == .aarch64) { |
| 3656 | 3656 | try dir.copyFile(sub_path, dir, sub_path, .{}); |
| 3657 | 3657 | } |
| ... | ... | @@ -3839,7 +3839,7 @@ pub fn getInternalObject(self: *MachO) ?*InternalObject { |
| 3839 | 3839 | return self.getFile(index).?.internal; |
| 3840 | 3840 | } |
| 3841 | 3841 | |
| 3842 | pub fn addFileHandle(self: *MachO, file: std.fs.File) !File.HandleIndex { | |
| 3842 | pub fn addFileHandle(self: *MachO, file: fs.File) !File.HandleIndex { | |
| 3843 | 3843 | const gpa = self.base.comp.gpa; |
| 3844 | 3844 | const index: File.HandleIndex = @intCast(self.file_handles.items.len); |
| 3845 | 3845 | const fh = try self.file_handles.addOne(gpa); |
| ... | ... | @@ -4136,10 +4136,10 @@ pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols { |
| 4136 | 4136 | return null; |
| 4137 | 4137 | } |
| 4138 | 4138 | |
| 4139 | pub fn ptraceAttach(self: *MachO, pid: std.os.pid_t) !void { | |
| 4139 | pub fn ptraceAttach(self: *MachO, pid: std.posix.pid_t) !void { | |
| 4140 | 4140 | if (!is_hot_update_compatible) return; |
| 4141 | 4141 | |
| 4142 | const mach_task = try std.os.darwin.machTaskForPid(pid); | |
| 4142 | const mach_task = try std.c.machTaskForPid(pid); | |
| 4143 | 4143 | log.debug("Mach task for pid {d}: {any}", .{ pid, mach_task }); |
| 4144 | 4144 | self.hot_state.mach_task = mach_task; |
| 4145 | 4145 | |
| ... | ... | @@ -4149,7 +4149,7 @@ pub fn ptraceAttach(self: *MachO, pid: std.os.pid_t) !void { |
| 4149 | 4149 | // try std.os.ptrace(std.os.darwin.PT.ATTACHEXC, pid, 0, 0); |
| 4150 | 4150 | } |
| 4151 | 4151 | |
| 4152 | pub fn ptraceDetach(self: *MachO, pid: std.os.pid_t) !void { | |
| 4152 | pub fn ptraceDetach(self: *MachO, pid: std.posix.pid_t) !void { | |
| 4153 | 4153 | if (!is_hot_update_compatible) return; |
| 4154 | 4154 | |
| 4155 | 4155 | _ = pid; |
| ... | ... | @@ -4330,7 +4330,7 @@ const Section = struct { |
| 4330 | 4330 | }; |
| 4331 | 4331 | |
| 4332 | 4332 | const HotUpdateState = struct { |
| 4333 | mach_task: ?std.os.darwin.MachTask = null, | |
| 4333 | mach_task: ?std.c.MachTask = null, | |
| 4334 | 4334 | }; |
| 4335 | 4335 | |
| 4336 | 4336 | pub const DynamicRelocs = struct { |
| ... | ... | @@ -4530,7 +4530,7 @@ fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersi |
| 4530 | 4530 | |
| 4531 | 4531 | const sdk_dir = switch (sdk_layout) { |
| 4532 | 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 | 4535 | if (readSdkVersionFromSettings(arena, sdk_dir)) |ver| { |
| 4536 | 4536 | return parseSdkVersion(ver); |
| ... | ... | @@ -4541,7 +4541,7 @@ fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersi |
| 4541 | 4541 | } |
| 4542 | 4542 | |
| 4543 | 4543 | // infer from pathname |
| 4544 | const stem = std.fs.path.stem(sdk_dir); | |
| 4544 | const stem = fs.path.stem(sdk_dir); | |
| 4545 | 4545 | const start = for (stem, 0..) |c, i| { |
| 4546 | 4546 | if (std.ascii.isDigit(c)) break i; |
| 4547 | 4547 | } else stem.len; |
| ... | ... | @@ -4556,8 +4556,8 @@ fn inferSdkVersion(comp: *Compilation, sdk_layout: SdkLayout) ?std.SemanticVersi |
| 4556 | 4556 | // Use property `MinimalDisplayName` to determine version. |
| 4557 | 4557 | // The file/property is also available with vendored libc. |
| 4558 | 4558 | fn readSdkVersionFromSettings(arena: Allocator, dir: []const u8) ![]const u8 { |
| 4559 | const sdk_path = try std.fs.path.join(arena, &.{ dir, "SDKSettings.json" }); | |
| 4560 | const contents = try std.fs.cwd().readFileAlloc(arena, sdk_path, std.math.maxInt(u16)); | |
| 4559 | const sdk_path = try fs.path.join(arena, &.{ dir, "SDKSettings.json" }); | |
| 4560 | const contents = try fs.cwd().readFileAlloc(arena, sdk_path, std.math.maxInt(u16)); | |
| 4561 | 4561 | const parsed = try std.json.parseFromSlice(std.json.Value, arena, contents, .{}); |
| 4562 | 4562 | if (parsed.value.object.get("MinimalDisplayName")) |ver| return ver.string; |
| 4563 | 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 | 368 | // getting the full file path |
| 369 | 369 | var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 370 | 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 | 372 | file.mod.root.sub_path, |
| 373 | 373 | file.sub_file_path, |
| 374 | 374 | }); |
| ... | ... | @@ -722,7 +722,7 @@ pub fn flushModule(self: *Plan9, arena: Allocator, prog_node: *std.Progress.Node |
| 722 | 722 | defer gpa.free(got_table); |
| 723 | 723 | |
| 724 | 724 | // + 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 | 726 | defer gpa.free(iovecs); |
| 727 | 727 | |
| 728 | 728 | const file = self.base.file.?; |
src/link/Wasm.zig+2-2| ... | ... | @@ -3046,7 +3046,7 @@ fn writeToFile( |
| 3046 | 3046 | } |
| 3047 | 3047 | |
| 3048 | 3048 | // finally, write the entire binary into the file. |
| 3049 | var iovec = [_]std.os.iovec_const{.{ | |
| 3049 | var iovec = [_]std.posix.iovec_const{.{ | |
| 3050 | 3050 | .iov_base = binary_bytes.items.ptr, |
| 3051 | 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 | 3709 | // report a nice error here with the file path if it fails instead of |
| 3710 | 3710 | // just returning the error code. |
| 3711 | 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 | 3713 | error.OperationNotSupported => unreachable, // Not a symlink. |
| 3714 | 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 | 193 | const object_name = try archive.parseName(header); |
| 194 | 194 | const name = name: { |
| 195 | 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 | 197 | break :name try std.fmt.allocPrint(gpa, "{s}({s})", .{ path, object_name }); |
| 198 | 198 | }; |
| 199 | 199 | defer gpa.free(name); |
src/main.zig+31-26| ... | ... | @@ -12,6 +12,7 @@ const Color = std.zig.Color; |
| 12 | 12 | const warn = std.log.warn; |
| 13 | 13 | const ThreadPool = std.Thread.Pool; |
| 14 | 14 | const cleanExit = std.process.cleanExit; |
| 15 | const native_os = builtin.os.tag; | |
| 15 | 16 | |
| 16 | 17 | const tracy = @import("tracy.zig"); |
| 17 | 18 | const Compilation = @import("Compilation.zig"); |
| ... | ... | @@ -48,7 +49,7 @@ pub const panic = crash_report.panic; |
| 48 | 49 | var wasi_preopens: fs.wasi.Preopens = undefined; |
| 49 | 50 | pub fn wasi_cwd() std.os.wasi.fd_t { |
| 50 | 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 | 53 | assert(mem.eql(u8, wasi_preopens.names[cwd_fd], ".")); |
| 53 | 54 | return cwd_fd; |
| 54 | 55 | } |
| ... | ... | @@ -158,9 +159,9 @@ var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{ |
| 158 | 159 | pub fn main() anyerror!void { |
| 159 | 160 | crash_report.initialize(); |
| 160 | 161 | |
| 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 | 163 | const gpa = gpa: { |
| 163 | if (builtin.os.tag == .wasi) { | |
| 164 | if (native_os == .wasi) { | |
| 164 | 165 | break :gpa std.heap.wasm_allocator; |
| 165 | 166 | } |
| 166 | 167 | if (use_gpa) { |
| ... | ... | @@ -187,7 +188,7 @@ pub fn main() anyerror!void { |
| 187 | 188 | return mainArgs(gpa_tracy.allocator(), arena, args); |
| 188 | 189 | } |
| 189 | 190 | |
| 190 | if (builtin.os.tag == .wasi) { | |
| 191 | if (native_os == .wasi) { | |
| 191 | 192 | wasi_preopens = try fs.wasi.preopensAlloc(arena); |
| 192 | 193 | } |
| 193 | 194 | |
| ... | ... | @@ -222,7 +223,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 222 | 223 | fatal("expected command argument", .{}); |
| 223 | 224 | } |
| 224 | 225 | |
| 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 | 227 | // In this case we have accidentally invoked ourselves as "the system C compiler" |
| 227 | 228 | // to figure out where libc is installed. This is essentially infinite recursion |
| 228 | 229 | // via child process execution due to the CC environment variable pointing to Zig. |
| ... | ... | @@ -813,9 +814,9 @@ fn buildOutputType( |
| 813 | 814 | var no_builtin = false; |
| 814 | 815 | var listen: Listen = .none; |
| 815 | 816 | var debug_compile_errors = false; |
| 816 | var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and | |
| 817 | var verbose_link = (native_os != .wasi or builtin.link_libc) and | |
| 817 | 818 | EnvVar.ZIG_VERBOSE_LINK.isSet(); |
| 818 | var verbose_cc = (builtin.os.tag != .wasi or builtin.link_libc) and | |
| 819 | var verbose_cc = (native_os != .wasi or builtin.link_libc) and | |
| 819 | 820 | EnvVar.ZIG_VERBOSE_CC.isSet(); |
| 820 | 821 | var verbose_air = false; |
| 821 | 822 | var verbose_intern_pool = false; |
| ... | ... | @@ -991,7 +992,7 @@ fn buildOutputType( |
| 991 | 992 | // if it exists, default the color setting to .off |
| 992 | 993 | // explicit --color arguments will still override this setting. |
| 993 | 994 | // 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; | |
| 995 | 996 | |
| 996 | 997 | switch (arg_mode) { |
| 997 | 998 | .build, .translate_c, .zig_test, .run => { |
| ... | ... | @@ -2684,7 +2685,7 @@ fn buildOutputType( |
| 2684 | 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 | 2689 | break :d getWasiPreopen("/lib"); |
| 2689 | 2690 | } else if (self_exe_path) |p| { |
| 2690 | 2691 | break :d introspect.findZigLibDirFromSelfExe(arena, p) catch |err| { |
| ... | ... | @@ -2703,7 +2704,7 @@ fn buildOutputType( |
| 2703 | 2704 | .path = p, |
| 2704 | 2705 | }; |
| 2705 | 2706 | } |
| 2706 | if (builtin.os.tag == .wasi) { | |
| 2707 | if (native_os == .wasi) { | |
| 2707 | 2708 | break :l getWasiPreopen("/cache"); |
| 2708 | 2709 | } |
| 2709 | 2710 | const p = try introspect.resolveGlobalCacheDir(arena); |
| ... | ... | @@ -4368,7 +4369,7 @@ fn runOrTest( |
| 4368 | 4369 | } |
| 4369 | 4370 | } else { |
| 4370 | 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 | } |
| 4374 | 4375 | |
| ... | ... | @@ -4434,16 +4435,16 @@ fn runOrTestHotSwap( |
| 4434 | 4435 | |
| 4435 | 4436 | switch (builtin.target.os.tag) { |
| 4436 | 4437 | .macos, .ios, .tvos, .watchos => { |
| 4437 | const PosixSpawn = std.os.darwin.PosixSpawn; | |
| 4438 | const PosixSpawn = @import("DarwinPosixSpawn.zig"); | |
| 4438 | 4439 | |
| 4439 | 4440 | var attr = try PosixSpawn.Attr.init(); |
| 4440 | 4441 | defer attr.deinit(); |
| 4441 | 4442 | |
| 4442 | 4443 | // ASLR is probably a good default for better debugging experience/programming |
| 4443 | 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 | std.os.darwin.POSIX_SPAWN.SETSIGMASK | | |
| 4446 | std.os.darwin.POSIX_SPAWN.DISABLE_ASLR; | |
| 4445 | const flags: u16 = std.c.POSIX_SPAWN.SETSIGDEF | | |
| 4446 | std.c.POSIX_SPAWN.SETSIGMASK | | |
| 4447 | std.c.POSIX_SPAWN.DISABLE_ASLR; | |
| 4447 | 4448 | try attr.set(flags); |
| 4448 | 4449 | |
| 4449 | 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 | 4748 | var child_argv = std.ArrayList([]const u8).init(arena); |
| 4748 | 4749 | var reference_trace: ?u32 = null; |
| 4749 | 4750 | var debug_compile_errors = false; |
| 4750 | var verbose_link = (builtin.os.tag != .wasi or builtin.link_libc) and | |
| 4751 | var verbose_link = (native_os != .wasi or builtin.link_libc) and | |
| 4751 | 4752 | EnvVar.ZIG_VERBOSE_LINK.isSet(); |
| 4752 | var verbose_cc = (builtin.os.tag != .wasi or builtin.link_libc) and | |
| 4753 | var verbose_cc = (native_os != .wasi or builtin.link_libc) and | |
| 4753 | 4754 | EnvVar.ZIG_VERBOSE_CC.isSet(); |
| 4754 | 4755 | var verbose_air = false; |
| 4755 | 4756 | var verbose_intern_pool = false; |
| ... | ... | @@ -4894,7 +4895,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 4894 | 4895 | } |
| 4895 | 4896 | } |
| 4896 | 4897 | |
| 4897 | const work_around_btrfs_bug = builtin.os.tag == .linux and | |
| 4898 | const work_around_btrfs_bug = native_os == .linux and | |
| 4898 | 4899 | EnvVar.ZIG_BTRFS_WORKAROUND.isSet(); |
| 4899 | 4900 | const color: Color = .auto; |
| 4900 | 4901 | |
| ... | ... | @@ -5311,7 +5312,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 5311 | 5312 | } |
| 5312 | 5313 | } else { |
| 5313 | 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 | 5544 | if (!process.can_spawn) { |
| 5544 | 5545 | const cmd = try std.mem.join(arena, " ", child_argv.items); |
| 5545 | 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 | } |
| 5549 | 5550 | |
| ... | ... | @@ -5655,7 +5656,7 @@ pub fn lldMain( |
| 5655 | 5656 | var count: usize = 0; |
| 5656 | 5657 | }; |
| 5657 | 5658 | 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 | 5661 | CallCounter.count += 1; |
| 5661 | 5662 | |
| ... | ... | @@ -5985,17 +5986,21 @@ fn parseCodeModel(arg: []const u8) std.builtin.CodeModel { |
| 5985 | 5986 | /// garbage collector to run concurrently to zig processes, and to allow multiple |
| 5986 | 5987 | /// zig processes to run concurrently with each other, without clobbering each other. |
| 5987 | 5988 | fn gimmeMoreOfThoseSweetSweetFileDescriptors() void { |
| 5988 | if (!@hasDecl(std.os.system, "rlimit")) return; | |
| 5989 | const posix = std.os; | |
| 5989 | const have_rlimit = switch (native_os) { | |
| 5990 | .windows, .wasi => false, | |
| 5991 | else => true, | |
| 5992 | }; | |
| 5993 | if (!have_rlimit) return; | |
| 5994 | const posix = std.posix; | |
| 5990 | 5995 | |
| 5991 | 5996 | var lim = posix.getrlimit(.NOFILE) catch return; // Oh well; we tried. |
| 5992 | if (comptime builtin.target.isDarwin()) { | |
| 5997 | if (native_os.isDarwin()) { | |
| 5993 | 5998 | // On Darwin, `NOFILE` is bounded by a hardcoded value `OPEN_MAX`. |
| 5994 | 5999 | // According to the man pages for setrlimit(): |
| 5995 | 6000 | // setrlimit() now returns with errno set to EINVAL in places that historically succeeded. |
| 5996 | 6001 | // It no longer accepts "rlim_cur = RLIM.INFINITY" for RLIM.NOFILE. |
| 5997 | 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 | 6005 | if (lim.cur == lim.max) return; |
| 6001 | 6006 | |
| ... | ... | @@ -6766,7 +6771,7 @@ fn cmdFetch( |
| 6766 | 6771 | args: []const []const u8, |
| 6767 | 6772 | ) !void { |
| 6768 | 6773 | const color: Color = .auto; |
| 6769 | const work_around_btrfs_bug = builtin.os.tag == .linux and | |
| 6774 | const work_around_btrfs_bug = native_os == .linux and | |
| 6770 | 6775 | EnvVar.ZIG_BTRFS_WORKAROUND.isSet(); |
| 6771 | 6776 | var opt_path_or_url: ?[]const u8 = null; |
| 6772 | 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 | 8 | fn print(a: u32, b: u32) void { |
| 9 | 9 | const str = "123456789"; |
| 10 | 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 | } |
| 13 | 13 | |
| 14 | 14 | // run |
test/cases/arithmetic_operations.1.zig+1-1| ... | ... | @@ -8,7 +8,7 @@ pub fn main() void { |
| 8 | 8 | fn print(a: u32, b: u32) void { |
| 9 | 9 | const str = "123456789"; |
| 10 | 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 | } |
| 13 | 13 | |
| 14 | 14 | // run |
test/cases/arithmetic_operations.2.zig+1-1| ... | ... | @@ -8,7 +8,7 @@ pub fn main() void { |
| 8 | 8 | fn print(a: u32, b: u32) void { |
| 9 | 9 | const str = "123456789"; |
| 10 | 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 | } |
| 13 | 13 | |
| 14 | 14 | // run |
test/cases/arithmetic_operations.3.zig+1-1| ... | ... | @@ -8,7 +8,7 @@ pub fn main() void { |
| 8 | 8 | fn print(a: u32, b: u32) void { |
| 9 | 9 | const str = "123456789"; |
| 10 | 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 | } |
| 13 | 13 | |
| 14 | 14 | // run |
test/cases/arithmetic_operations.4.zig+1-1| ... | ... | @@ -8,7 +8,7 @@ pub fn main() void { |
| 8 | 8 | fn print(a: u32, b: u32) void { |
| 9 | 9 | const str = "123456789"; |
| 10 | 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 | } |
| 13 | 13 | |
| 14 | 14 | // run |
test/cases/errors.0.zig+1-1| ... | ... | @@ -7,7 +7,7 @@ pub fn main() void { |
| 7 | 7 | fn foo() anyerror!void {} |
| 8 | 8 | |
| 9 | 9 | fn print() void { |
| 10 | _ = std.os.write(1, "Hello, World!\n") catch {}; | |
| 10 | _ = std.posix.write(1, "Hello, World!\n") catch {}; | |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | 13 | // run |
test/cases/errors.1.zig+1-1| ... | ... | @@ -9,7 +9,7 @@ fn foo() anyerror!void { |
| 9 | 9 | } |
| 10 | 10 | |
| 11 | 11 | fn print() void { |
| 12 | _ = std.os.write(1, "Hello, World!\n") catch {}; | |
| 12 | _ = std.posix.write(1, "Hello, World!\n") catch {}; | |
| 13 | 13 | } |
| 14 | 14 | |
| 15 | 15 | // run |
test/cases/function_pointers.zig+2-2| ... | ... | @@ -12,11 +12,11 @@ pub fn main() void { |
| 12 | 12 | } |
| 13 | 13 | |
| 14 | 14 | fn 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 | } |
| 17 | 17 | |
| 18 | 18 | fn 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 | } |
| 21 | 21 | |
| 22 | 22 | // run |
test/cases/parameters_and_return_values.0.zig+1-1| ... | ... | @@ -10,7 +10,7 @@ fn id(x: u32) u32 { |
| 10 | 10 | |
| 11 | 11 | fn print(len: u32) void { |
| 12 | 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 | } |
| 15 | 15 | |
| 16 | 16 | // run |
test/cases/print_u32s.zig+2-2| ... | ... | @@ -12,10 +12,10 @@ fn printNumberHex(x: u32) void { |
| 12 | 12 | var i: u5 = 28; |
| 13 | 13 | while (true) : (i -= 4) { |
| 14 | 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 | 16 | if (i == 0) break; |
| 17 | 17 | } |
| 18 | _ = std.os.write(1, "\n") catch {}; | |
| 18 | _ = std.posix.write(1, "\n") catch {}; | |
| 19 | 19 | } |
| 20 | 20 | |
| 21 | 21 | // 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 | 11 | } |
| 12 | 12 | var f = async bar(@frame()); |
| 13 | 13 | _ = &f; |
| 14 | std.os.exit(1); | |
| 14 | std.process.exit(1); | |
| 15 | 15 | } |
| 16 | 16 | |
| 17 | 17 | fn bar(frame: anyframe) void { |
| 18 | 18 | suspend { |
| 19 | 19 | resume frame; |
| 20 | 20 | } |
| 21 | std.os.exit(1); | |
| 21 | std.process.exit(1); | |
| 22 | 22 | } |
| 23 | 23 | |
| 24 | 24 | var global_frame: anyframe = undefined; |
| 25 | 25 | pub fn main() !void { |
| 26 | 26 | _ = async foo(); |
| 27 | 27 | resume global_frame; |
| 28 | std.os.exit(1); | |
| 28 | std.process.exit(1); | |
| 29 | 29 | } |
| 30 | 30 | // run |
| 31 | 31 | // 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 | 8 | fn foo() void { |
| 9 | 9 | var f = async bar(@frame()); |
| 10 | 10 | _ = &f; |
| 11 | std.os.exit(1); | |
| 11 | std.process.exit(1); | |
| 12 | 12 | } |
| 13 | 13 | |
| 14 | 14 | fn bar(frame: anyframe) void { |
| 15 | 15 | suspend { |
| 16 | 16 | resume frame; |
| 17 | 17 | } |
| 18 | std.os.exit(1); | |
| 18 | std.process.exit(1); | |
| 19 | 19 | } |
| 20 | 20 | |
| 21 | 21 | pub fn main() !void { |
test/link/glibc_compat/glibc_runtime_check.zig+2-2| ... | ... | @@ -30,11 +30,11 @@ fn checkStat() !void { |
| 30 | 30 | var stat = std.mem.zeroes(std.c.Stat); |
| 31 | 31 | var result = std.c.fstatat(cwdFd, "a_file_that_definitely_does_not_exist", &stat, 0); |
| 32 | 32 | assert(result == -1); |
| 33 | assert(std.c.getErrno(result) == .NOENT); | |
| 33 | assert(std.posix.errno(result) == .NOENT); | |
| 34 | 34 | |
| 35 | 35 | result = std.c.stat("a_file_that_definitely_does_not_exist", &stat); |
| 36 | 36 | assert(result == -1); |
| 37 | assert(std.c.getErrno(result) == .NOENT); | |
| 37 | assert(std.posix.errno(result) == .NOENT); | |
| 38 | 38 | } |
| 39 | 39 | |
| 40 | 40 | // PR #17607 - reallocarray not visible in headers |
test/standalone/sigpipe/breakpipe.zig+7-9| ... | ... | @@ -1,19 +1,17 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const build_options = @import("build_options"); |
| 3 | 3 | |
| 4 | pub usingnamespace if (build_options.keep_sigpipe) struct { | |
| 5 | pub const std_options = .{ | |
| 6 | .keep_sigpipe = true, | |
| 7 | }; | |
| 8 | } else struct {}; | |
| 4 | pub const std_options = .{ | |
| 5 | .keep_sigpipe = build_options.keep_sigpipe, | |
| 6 | }; | |
| 9 | 7 | |
| 10 | 8 | pub fn main() !void { |
| 11 | const pipe = try std.os.pipe(); | |
| 12 | std.os.close(pipe[0]); | |
| 13 | _ = std.os.write(pipe[1], "a") catch |err| switch (err) { | |
| 9 | const pipe = try std.posix.pipe(); | |
| 10 | std.posix.close(pipe[0]); | |
| 11 | _ = std.posix.write(pipe[1], "a") catch |err| switch (err) { | |
| 14 | 12 | error.BrokenPipe => { |
| 15 | 13 | try std.io.getStdOut().writer().writeAll("BrokenPipe\n"); |
| 16 | std.os.exit(123); | |
| 14 | std.posix.exit(123); | |
| 17 | 15 | }, |
| 18 | 16 | else => |e| return e, |
| 19 | 17 | }; |
test/standalone/sigpipe/build.zig+6-6| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const os = std.os; | |
| 2 | const posix = std.posix; | |
| 3 | 3 | |
| 4 | 4 | pub fn build(b: *std.build.Builder) !void { |
| 5 | 5 | const test_step = b.step("test", "Test it"); |
| ... | ... | @@ -16,12 +16,12 @@ pub fn build(b: *std.build.Builder) !void { |
| 16 | 16 | // This test runs "breakpipe" as a child process and that process |
| 17 | 17 | // depends on inheriting a SIGPIPE disposition of "default". |
| 18 | 18 | { |
| 19 | const act = os.Sigaction{ | |
| 20 | .handler = .{ .handler = os.SIG.DFL }, | |
| 21 | .mask = os.empty_sigset, | |
| 19 | const act = posix.Sigaction{ | |
| 20 | .handler = .{ .handler = posix.SIG.DFL }, | |
| 21 | .mask = posix.empty_sigset, | |
| 22 | 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 | } |
| 26 | 26 | |
| 27 | 27 | for ([_]bool{ false, true }) |keep_sigpipe| { |
| ... | ... | @@ -34,7 +34,7 @@ pub fn build(b: *std.build.Builder) !void { |
| 34 | 34 | exe.addOptions("build_options", options); |
| 35 | 35 | const run = b.addRunArtifact(exe); |
| 36 | 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 | 38 | } else { |
| 39 | 39 | run.addCheck(.{ .expect_stdout_exact = "BrokenPipe\n" }); |
| 40 | 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 | 25 | defer allocator.free(tmp_relative_path); |
| 26 | 26 | |
| 27 | 27 | // Clear PATH |
| 28 | std.debug.assert(std.os.windows.kernel32.SetEnvironmentVariableW( | |
| 28 | std.debug.assert(windows.kernel32.SetEnvironmentVariableW( | |
| 29 | 29 | utf16Literal("PATH"), |
| 30 | 30 | null, |
| 31 | 31 | ) == windows.TRUE); |
| 32 | 32 | |
| 33 | 33 | // Set PATHEXT to something predictable |
| 34 | std.debug.assert(std.os.windows.kernel32.SetEnvironmentVariableW( | |
| 34 | std.debug.assert(windows.kernel32.SetEnvironmentVariableW( | |
| 35 | 35 | utf16Literal("PATHEXT"), |
| 36 | 36 | utf16Literal(".COM;.EXE;.BAT;.CMD;.JS"), |
| 37 | 37 | ) == windows.TRUE); |
| ... | ... | @@ -39,7 +39,7 @@ pub fn main() anyerror!void { |
| 39 | 39 | // No PATH, so it should fail to find anything not in the cwd |
| 40 | 40 | try testExecError(error.FileNotFound, allocator, "something_missing"); |
| 41 | 41 | |
| 42 | std.debug.assert(std.os.windows.kernel32.SetEnvironmentVariableW( | |
| 42 | std.debug.assert(windows.kernel32.SetEnvironmentVariableW( | |
| 43 | 43 | utf16Literal("PATH"), |
| 44 | 44 | tmp_absolute_path_w, |
| 45 | 45 | ) == windows.TRUE); |
| ... | ... | @@ -120,7 +120,7 @@ pub fn main() anyerror!void { |
| 120 | 120 | const something_subdir_abs_path = try std.mem.concatWithSentinel(allocator, u16, &.{ tmp_absolute_path_w, utf16Literal("\\something") }, 0); |
| 121 | 121 | defer allocator.free(something_subdir_abs_path); |
| 122 | 122 | |
| 123 | std.debug.assert(std.os.windows.kernel32.SetEnvironmentVariableW( | |
| 123 | std.debug.assert(windows.kernel32.SetEnvironmentVariableW( | |
| 124 | 124 | utf16Literal("PATH"), |
| 125 | 125 | something_subdir_abs_path, |
| 126 | 126 | ) == windows.TRUE); |