| author | |
| committer | |
| log | 67726e36b02d26343398ed8ede460622d706c539 |
| tree | e25a71f83d194f3a13112295174ea3de155dcff1 |
| parent | df7aa9a4f0360945db999f6a6190290eb91d6351 |
| signature |
See #238020 files changed, 2709 insertions(+), 2483 deletions(-)
CMakeLists.txt+1-1| ... | @@ -621,9 +621,9 @@ set(ZIG_STD_FILES | ... | @@ -621,9 +621,9 @@ set(ZIG_STD_FILES |
| 621 | "os/time.zig" | 621 | "os/time.zig" |
| 622 | "os/uefi.zig" | 622 | "os/uefi.zig" |
| 623 | "os/wasi.zig" | 623 | "os/wasi.zig" |
| 624 | "os/wasi/core.zig" | ||
| 625 | "os/windows.zig" | 624 | "os/windows.zig" |
| 626 | "os/windows/advapi32.zig" | 625 | "os/windows/advapi32.zig" |
| 626 | "os/windows/errno.zig" | ||
| 627 | "os/windows/error.zig" | 627 | "os/windows/error.zig" |
| 628 | "os/windows/kernel32.zig" | 628 | "os/windows/kernel32.zig" |
| 629 | "os/windows/ntdll.zig" | 629 | "os/windows/ntdll.zig" |
doc/langref.html.in+1-1| ... | @@ -195,7 +195,7 @@ const std = @import("std"); | ... | @@ -195,7 +195,7 @@ const std = @import("std"); |
| 195 | 195 | ||
| 196 | pub fn main() !void { | 196 | pub fn main() !void { |
| 197 | // If this program is run without stdout attached, exit with an error. | 197 | // If this program is run without stdout attached, exit with an error. |
| 198 | const stdout_file = try std.io.getStdOut(); | 198 | const stdout_file = try std.os.File.stdout(); |
| 199 | // If this program encounters pipe failure when printing to stdout, exit | 199 | // If this program encounters pipe failure when printing to stdout, exit |
| 200 | // with an error. | 200 | // with an error. |
| 201 | try stdout_file.write("Hello, world!\n"); | 201 | try stdout_file.write("Hello, world!\n"); |
example/hello_world/hello.zig+2-6| ... | @@ -1,9 +1,5 @@ | ... | @@ -1,9 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn main() !void { | 3 | pub fn main() void { |
| 4 | // If this program is run without stdout attached, exit with an error. | 4 | std.debug.warn("Hello, world!\n"); |
| 5 | const stdout_file = try std.io.getStdOut(); | ||
| 6 | // If this program encounters pipe failure when printing to stdout, exit | ||
| 7 | // with an error. | ||
| 8 | try stdout_file.write("Hello, world!\n"); | ||
| 9 | } | 5 | } |
example/hello_world/hello_libc.zig+2-6| ... | @@ -2,13 +2,9 @@ const c = @cImport({ | ... | @@ -2,13 +2,9 @@ const c = @cImport({ |
| 2 | // See https://github.com/ziglang/zig/issues/515 | 2 | // See https://github.com/ziglang/zig/issues/515 |
| 3 | @cDefine("_NO_CRT_STDIO_INLINE", "1"); | 3 | @cDefine("_NO_CRT_STDIO_INLINE", "1"); |
| 4 | @cInclude("stdio.h"); | 4 | @cInclude("stdio.h"); |
| 5 | @cInclude("string.h"); | ||
| 6 | }); | 5 | }); |
| 7 | 6 | ||
| 8 | const msg = c"Hello, world!\n"; | 7 | export fn main(argc: c_int, argv: [*]?[*]u8) c_int { |
| 9 | 8 | c.fprintf(c.stderr, c"Hello, world!\n"); | |
| 10 | export fn main(argc: c_int, argv: **u8) c_int { | ||
| 11 | if (c.printf(msg) != @intCast(c_int, c.strlen(msg))) return -1; | ||
| 12 | |||
| 13 | return 0; | 9 | return 0; |
| 14 | } | 10 | } |
std/c.zig+16-6| ... | @@ -1,15 +1,24 @@ | ... | @@ -1,15 +1,24 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const Os = builtin.Os; | 2 | |
| 3 | pub const is_the_target = builtin.link_libc; | ||
| 3 | 4 | ||
| 4 | pub use switch (builtin.os) { | 5 | pub use switch (builtin.os) { |
| 5 | Os.linux => @import("c/linux.zig"), | 6 | .linux => @import("c/linux.zig"), |
| 6 | Os.windows => @import("c/windows.zig"), | 7 | .windows => @import("c/windows.zig"), |
| 7 | Os.macosx, Os.ios => @import("c/darwin.zig"), | 8 | .macosx, .ios, .tvos, .watchos => @import("c/darwin.zig"), |
| 8 | Os.freebsd => @import("c/freebsd.zig"), | 9 | .freebsd => @import("c/freebsd.zig"), |
| 9 | Os.netbsd => @import("c/netbsd.zig"), | 10 | .netbsd => @import("c/netbsd.zig"), |
| 10 | else => struct {}, | 11 | else => struct {}, |
| 11 | }; | 12 | }; |
| 12 | 13 | ||
| 14 | pub fn getErrno(rc: var) u12 { | ||
| 15 | if (rc == -1) { | ||
| 16 | return @intCast(u12, _errno().*); | ||
| 17 | } else { | ||
| 18 | return 0; | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 13 | // TODO https://github.com/ziglang/zig/issues/265 on this whole file | 22 | // TODO https://github.com/ziglang/zig/issues/265 on this whole file |
| 14 | 23 | ||
| 15 | pub const FILE = @OpaqueType(); | 24 | pub const FILE = @OpaqueType(); |
| ... | @@ -56,6 +65,7 @@ pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; | ... | @@ -56,6 +65,7 @@ pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; |
| 56 | pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; | 65 | pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; |
| 57 | pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; | 66 | pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; |
| 58 | pub extern "c" fn rmdir(path: [*]const u8) c_int; | 67 | pub extern "c" fn rmdir(path: [*]const u8) c_int; |
| 68 | pub extern "c" fn getenv(name: [*]const u8) ?[*]u8; | ||
| 59 | 69 | ||
| 60 | pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; | 70 | pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; |
| 61 | pub extern "c" fn malloc(usize) ?*c_void; | 71 | pub extern "c" fn malloc(usize) ?*c_void; |
std/event/net.zig+1-8| ... | @@ -89,14 +89,7 @@ pub const Server = struct { | ... | @@ -89,14 +89,7 @@ pub const Server = struct { |
| 89 | }, | 89 | }, |
| 90 | }; | 90 | }; |
| 91 | } else |err| switch (err) { | 91 | } else |err| switch (err) { |
| 92 | error.ProcessFdQuotaExceeded => { | 92 | error.ProcessFdQuotaExceeded => @panic("TODO handle this error"), |
| 93 | errdefer os.emfile_promise_queue.remove(&self.waiting_for_emfile_node); | ||
| 94 | suspend { | ||
| 95 | self.waiting_for_emfile_node = PromiseNode.init(@handle()); | ||
| 96 | os.emfile_promise_queue.append(&self.waiting_for_emfile_node); | ||
| 97 | } | ||
| 98 | continue; | ||
| 99 | }, | ||
| 100 | error.ConnectionAborted => continue, | 93 | error.ConnectionAborted => continue, |
| 101 | 94 | ||
| 102 | error.FileDescriptorNotASocket => unreachable, | 95 | error.FileDescriptorNotASocket => unreachable, |
std/io.zig-17| ... | @@ -18,23 +18,6 @@ const testing = std.testing; | ... | @@ -18,23 +18,6 @@ const testing = std.testing; |
| 18 | const is_posix = builtin.os != builtin.Os.windows; | 18 | const is_posix = builtin.os != builtin.Os.windows; |
| 19 | const is_windows = builtin.os == builtin.Os.windows; | 19 | const is_windows = builtin.os == builtin.Os.windows; |
| 20 | 20 | ||
| 21 | const GetStdIoErrs = os.WindowsGetStdHandleErrs; | ||
| 22 | |||
| 23 | pub fn getStdErr() GetStdIoErrs!File { | ||
| 24 | const handle = if (is_windows) try os.windowsGetStdHandle(os.windows.STD_ERROR_HANDLE) else if (is_posix) os.posix.STDERR_FILENO else unreachable; | ||
| 25 | return File.openHandle(handle); | ||
| 26 | } | ||
| 27 | |||
| 28 | pub fn getStdOut() GetStdIoErrs!File { | ||
| 29 | const handle = if (is_windows) try os.windowsGetStdHandle(os.windows.STD_OUTPUT_HANDLE) else if (is_posix) os.posix.STDOUT_FILENO else unreachable; | ||
| 30 | return File.openHandle(handle); | ||
| 31 | } | ||
| 32 | |||
| 33 | pub fn getStdIn() GetStdIoErrs!File { | ||
| 34 | const handle = if (is_windows) try os.windowsGetStdHandle(os.windows.STD_INPUT_HANDLE) else if (is_posix) os.posix.STDIN_FILENO else unreachable; | ||
| 35 | return File.openHandle(handle); | ||
| 36 | } | ||
| 37 | |||
| 38 | pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; | 21 | pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; |
| 39 | pub const SliceSeekableInStream = @import("io/seekable_stream.zig").SliceSeekableInStream; | 22 | pub const SliceSeekableInStream = @import("io/seekable_stream.zig").SliceSeekableInStream; |
| 40 | pub const COutStream = @import("io/c_out_stream.zig").COutStream; | 23 | pub const COutStream = @import("io/c_out_stream.zig").COutStream; |
std/os.zig+85-1810| ... | @@ -2,10 +2,6 @@ const std = @import("std.zig"); | ... | @@ -2,10 +2,6 @@ const std = @import("std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Os = builtin.Os; | 3 | const Os = builtin.Os; |
| 4 | const is_windows = builtin.os == Os.windows; | 4 | const is_windows = builtin.os == Os.windows; |
| 5 | const is_posix = switch (builtin.os) { | ||
| 6 | builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => true, | ||
| 7 | else => false, | ||
| 8 | }; | ||
| 9 | const os = @This(); | 5 | const os = @This(); |
| 10 | 6 | ||
| 11 | comptime { | 7 | comptime { |
| ... | @@ -36,13 +32,13 @@ pub const zen = @import("os/zen.zig"); | ... | @@ -36,13 +32,13 @@ pub const zen = @import("os/zen.zig"); |
| 36 | pub const uefi = @import("os/uefi.zig"); | 32 | pub const uefi = @import("os/uefi.zig"); |
| 37 | pub const wasi = @import("os/wasi.zig"); | 33 | pub const wasi = @import("os/wasi.zig"); |
| 38 | 34 | ||
| 39 | pub const posix = switch (builtin.os) { | 35 | pub const system = if (builtin.link_libc) c else switch (builtin.os) { |
| 40 | Os.linux => linux, | 36 | .linux => linux, |
| 41 | Os.macosx, Os.ios => darwin, | 37 | .macosx, .ios, .watchos, .tvos => darwin, |
| 42 | Os.freebsd => freebsd, | 38 | .freebsd => freebsd, |
| 43 | Os.netbsd => netbsd, | 39 | .netbsd => netbsd, |
| 44 | Os.zen => zen, | 40 | .zen => zen, |
| 45 | Os.wasi => wasi, | 41 | .wasi => wasi, |
| 46 | else => @compileError("Unsupported OS"), | 42 | else => @compileError("Unsupported OS"), |
| 47 | }; | 43 | }; |
| 48 | 44 | ||
| ... | @@ -58,13 +54,17 @@ pub const page_size = switch (builtin.arch) { | ... | @@ -58,13 +54,17 @@ pub const page_size = switch (builtin.arch) { |
| 58 | else => 4 * 1024, | 54 | else => 4 * 1024, |
| 59 | }; | 55 | }; |
| 60 | 56 | ||
| 57 | /// This represents the maximum size of a UTF-8 encoded file path. | ||
| 58 | /// All file system operations which return a path are guaranteed to | ||
| 59 | /// fit into a UTF-8 encoded array of this length. | ||
| 60 | /// path being too long if it is this 0long | ||
| 61 | pub const MAX_PATH_BYTES = switch (builtin.os) { | 61 | pub const MAX_PATH_BYTES = switch (builtin.os) { |
| 62 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => posix.PATH_MAX, | 62 | .linux, .macosx, .ios, .freebsd, .netbsd => posix.PATH_MAX, |
| 63 | // Each UTF-16LE character may be expanded to 3 UTF-8 bytes. | 63 | // Each UTF-16LE character may be expanded to 3 UTF-8 bytes. |
| 64 | // If it would require 4 UTF-8 bytes, then there would be a surrogate | 64 | // If it would require 4 UTF-8 bytes, then there would be a surrogate |
| 65 | // pair in the UTF-16LE, and we (over)account 3 bytes for it that way. | 65 | // pair in the UTF-16LE, and we (over)account 3 bytes for it that way. |
| 66 | // +1 for the null byte at the end, which can be encoded in 1 byte. | 66 | // +1 for the null byte at the end, which can be encoded in 1 byte. |
| 67 | Os.windows => windows_util.PATH_MAX_WIDE * 3 + 1, | 67 | .windows => posix.PATH_MAX_WIDE * 3 + 1, |
| 68 | else => @compileError("Unsupported OS"), | 68 | else => @compileError("Unsupported OS"), |
| 69 | }; | 69 | }; |
| 70 | 70 | ||
| ... | @@ -98,6 +98,22 @@ pub const FileHandle = if (is_windows) windows.HANDLE else i32; | ... | @@ -98,6 +98,22 @@ pub const FileHandle = if (is_windows) windows.HANDLE else i32; |
| 98 | pub const getAppDataDir = @import("os/get_app_data_dir.zig").getAppDataDir; | 98 | pub const getAppDataDir = @import("os/get_app_data_dir.zig").getAppDataDir; |
| 99 | pub const GetAppDataDirError = @import("os/get_app_data_dir.zig").GetAppDataDirError; | 99 | pub const GetAppDataDirError = @import("os/get_app_data_dir.zig").GetAppDataDirError; |
| 100 | 100 | ||
| 101 | pub const getRandomBytes = posix.getrandom; | ||
| 102 | pub const abort = posix.abort; | ||
| 103 | pub const exit = posix.exit; | ||
| 104 | pub const symLink = posix.symlink; | ||
| 105 | pub const symLinkC = posix.symlinkC; | ||
| 106 | pub const symLinkW = posix.symlinkW; | ||
| 107 | pub const deleteFile = posix.unlink; | ||
| 108 | pub const deleteFileC = posix.unlinkC; | ||
| 109 | pub const deleteFileW = posix.unlinkW; | ||
| 110 | pub const rename = posix.rename; | ||
| 111 | pub const renameC = posix.renameC; | ||
| 112 | pub const renameW = posix.renameW; | ||
| 113 | pub const changeCurDir = posix.chdir; | ||
| 114 | pub const changeCurDirC = posix.chdirC; | ||
| 115 | pub const changeCurDirW = posix.chdirW; | ||
| 116 | |||
| 101 | const debug = std.debug; | 117 | const debug = std.debug; |
| 102 | const assert = debug.assert; | 118 | const assert = debug.assert; |
| 103 | const testing = std.testing; | 119 | const testing = std.testing; |
| ... | @@ -116,610 +132,6 @@ const ArrayList = std.ArrayList; | ... | @@ -116,610 +132,6 @@ const ArrayList = std.ArrayList; |
| 116 | const Buffer = std.Buffer; | 132 | const Buffer = std.Buffer; |
| 117 | const math = std.math; | 133 | const math = std.math; |
| 118 | 134 | ||
| 119 | /// Fills `buf` with random bytes. If linking against libc, this calls the | ||
| 120 | /// appropriate OS-specific library call. Otherwise it uses the zig standard | ||
| 121 | /// library implementation. | ||
| 122 | pub fn getRandomBytes(buf: []u8) !void { | ||
| 123 | switch (builtin.os) { | ||
| 124 | Os.linux => while (true) { | ||
| 125 | // TODO check libc version and potentially call c.getrandom. | ||
| 126 | // See #397 | ||
| 127 | const errno = posix.getErrno(posix.getrandom(buf.ptr, buf.len, 0)); | ||
| 128 | switch (errno) { | ||
| 129 | 0 => return, | ||
| 130 | posix.EINVAL => unreachable, | ||
| 131 | posix.EFAULT => unreachable, | ||
| 132 | posix.EINTR => continue, | ||
| 133 | posix.ENOSYS => return getRandomBytesDevURandom(buf), | ||
| 134 | else => return unexpectedErrorPosix(errno), | ||
| 135 | } | ||
| 136 | }, | ||
| 137 | Os.macosx, Os.ios, Os.freebsd, Os.netbsd => return getRandomBytesDevURandom(buf), | ||
| 138 | Os.windows => { | ||
| 139 | // Call RtlGenRandom() instead of CryptGetRandom() on Windows | ||
| 140 | // https://github.com/rust-lang-nursery/rand/issues/111 | ||
| 141 | // https://bugzilla.mozilla.org/show_bug.cgi?id=504270 | ||
| 142 | if (windows.RtlGenRandom(buf.ptr, buf.len) == 0) { | ||
| 143 | const err = windows.GetLastError(); | ||
| 144 | return switch (err) { | ||
| 145 | else => unexpectedErrorWindows(err), | ||
| 146 | }; | ||
| 147 | } | ||
| 148 | }, | ||
| 149 | Os.wasi => { | ||
| 150 | const random_get_result = os.wasi.random_get(buf.ptr, buf.len); | ||
| 151 | if (random_get_result != os.wasi.ESUCCESS) { | ||
| 152 | return error.Unknown; | ||
| 153 | } | ||
| 154 | }, | ||
| 155 | Os.zen => { | ||
| 156 | const randomness = []u8{ 42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45 }; | ||
| 157 | var i: usize = 0; | ||
| 158 | while (i < buf.len) : (i += 1) { | ||
| 159 | if (i > randomness.len) return error.Unknown; | ||
| 160 | buf[i] = randomness[i]; | ||
| 161 | } | ||
| 162 | }, | ||
| 163 | else => @compileError("Unsupported OS"), | ||
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | fn getRandomBytesDevURandom(buf: []u8) !void { | ||
| 168 | const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); | ||
| 169 | defer close(fd); | ||
| 170 | |||
| 171 | const stream = &File.openHandle(fd).inStream().stream; | ||
| 172 | stream.readNoEof(buf) catch |err| switch (err) { | ||
| 173 | error.EndOfStream => unreachable, | ||
| 174 | error.OperationAborted => unreachable, | ||
| 175 | error.BrokenPipe => unreachable, | ||
| 176 | error.Unexpected => return error.Unexpected, | ||
| 177 | error.InputOutput => return error.Unexpected, | ||
| 178 | error.SystemResources => return error.Unexpected, | ||
| 179 | error.IsDir => unreachable, | ||
| 180 | }; | ||
| 181 | } | ||
| 182 | |||
| 183 | test "os.getRandomBytes" { | ||
| 184 | var buf_a: [50]u8 = undefined; | ||
| 185 | var buf_b: [50]u8 = undefined; | ||
| 186 | // Call Twice | ||
| 187 | try getRandomBytes(buf_a[0..]); | ||
| 188 | try getRandomBytes(buf_b[0..]); | ||
| 189 | |||
| 190 | // Check if random (not 100% conclusive) | ||
| 191 | testing.expect(!mem.eql(u8, buf_a, buf_b)); | ||
| 192 | } | ||
| 193 | |||
| 194 | /// Raises a signal in the current kernel thread, ending its execution. | ||
| 195 | /// If linking against libc, this calls the abort() libc function. Otherwise | ||
| 196 | /// it uses the zig standard library implementation. | ||
| 197 | pub fn abort() noreturn { | ||
| 198 | @setCold(true); | ||
| 199 | if (builtin.link_libc) { | ||
| 200 | c.abort(); | ||
| 201 | } | ||
| 202 | switch (builtin.os) { | ||
| 203 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 204 | _ = posix.raise(posix.SIGABRT); | ||
| 205 | _ = posix.raise(posix.SIGKILL); | ||
| 206 | while (true) {} | ||
| 207 | }, | ||
| 208 | Os.windows => { | ||
| 209 | if (builtin.mode == builtin.Mode.Debug) { | ||
| 210 | @breakpoint(); | ||
| 211 | } | ||
| 212 | windows.ExitProcess(3); | ||
| 213 | }, | ||
| 214 | Os.wasi => { | ||
| 215 | _ = wasi.proc_raise(wasi.SIGABRT); | ||
| 216 | // TODO: Is SIGKILL even necessary? | ||
| 217 | _ = wasi.proc_raise(wasi.SIGKILL); | ||
| 218 | while (true) {} | ||
| 219 | }, | ||
| 220 | Os.uefi => { | ||
| 221 | // TODO there's gotta be a better thing to do here than loop forever | ||
| 222 | while (true) {} | ||
| 223 | }, | ||
| 224 | else => @compileError("Unsupported OS"), | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | /// Exits the program cleanly with the specified status code. | ||
| 229 | pub fn exit(status: u8) noreturn { | ||
| 230 | @setCold(true); | ||
| 231 | if (builtin.link_libc) { | ||
| 232 | c.exit(status); | ||
| 233 | } | ||
| 234 | switch (builtin.os) { | ||
| 235 | Os.linux => { | ||
| 236 | if (builtin.single_threaded) { | ||
| 237 | linux.exit(status); | ||
| 238 | } else { | ||
| 239 | linux.exit_group(status); | ||
| 240 | } | ||
| 241 | }, | ||
| 242 | Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 243 | posix.exit(status); | ||
| 244 | }, | ||
| 245 | Os.windows => { | ||
| 246 | windows.ExitProcess(status); | ||
| 247 | }, | ||
| 248 | Os.wasi => { | ||
| 249 | wasi.proc_exit(status); | ||
| 250 | }, | ||
| 251 | else => @compileError("Unsupported OS"), | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | /// When a file descriptor is closed on linux, it pops the first | ||
| 256 | /// node from this queue and resumes it. | ||
| 257 | /// Async functions which get the EMFILE error code can suspend, | ||
| 258 | /// putting their coroutine handle into this list. | ||
| 259 | /// TODO make this an atomic linked list | ||
| 260 | pub var emfile_promise_queue = std.LinkedList(promise).init(); | ||
| 261 | |||
| 262 | /// Closes the file handle. Keeps trying if it gets interrupted by a signal. | ||
| 263 | pub fn close(handle: FileHandle) void { | ||
| 264 | if (is_windows) { | ||
| 265 | windows_util.windowsClose(handle); | ||
| 266 | } else { | ||
| 267 | while (true) { | ||
| 268 | const err = posix.getErrno(posix.close(handle)); | ||
| 269 | switch (err) { | ||
| 270 | posix.EINTR => continue, | ||
| 271 | else => { | ||
| 272 | if (emfile_promise_queue.popFirst()) |p| resume p.data; | ||
| 273 | return; | ||
| 274 | }, | ||
| 275 | } | ||
| 276 | } | ||
| 277 | } | ||
| 278 | } | ||
| 279 | |||
| 280 | pub const PosixReadError = error{ | ||
| 281 | InputOutput, | ||
| 282 | SystemResources, | ||
| 283 | IsDir, | ||
| 284 | Unexpected, | ||
| 285 | }; | ||
| 286 | |||
| 287 | /// Returns the number of bytes that were read, which can be less than | ||
| 288 | /// buf.len. If 0 bytes were read, that means EOF. | ||
| 289 | pub fn posixRead(fd: i32, buf: []u8) PosixReadError!usize { | ||
| 290 | // Linux can return EINVAL when read amount is > 0x7ffff000 | ||
| 291 | // See https://github.com/ziglang/zig/pull/743#issuecomment-363158274 | ||
| 292 | const max_buf_len = 0x7ffff000; | ||
| 293 | |||
| 294 | var index: usize = 0; | ||
| 295 | while (index < buf.len) { | ||
| 296 | const want_to_read = math.min(buf.len - index, usize(max_buf_len)); | ||
| 297 | const rc = posix.read(fd, buf.ptr + index, want_to_read); | ||
| 298 | const err = posix.getErrno(rc); | ||
| 299 | switch (err) { | ||
| 300 | 0 => { | ||
| 301 | index += rc; | ||
| 302 | if (rc == want_to_read) continue; | ||
| 303 | // Read returned less than buf.len. | ||
| 304 | return index; | ||
| 305 | }, | ||
| 306 | posix.EINTR => continue, | ||
| 307 | posix.EINVAL => unreachable, | ||
| 308 | posix.EFAULT => unreachable, | ||
| 309 | posix.EAGAIN => unreachable, | ||
| 310 | posix.EBADF => unreachable, // always a race condition | ||
| 311 | posix.EIO => return error.InputOutput, | ||
| 312 | posix.EISDIR => return error.IsDir, | ||
| 313 | posix.ENOBUFS => return error.SystemResources, | ||
| 314 | posix.ENOMEM => return error.SystemResources, | ||
| 315 | else => return unexpectedErrorPosix(err), | ||
| 316 | } | ||
| 317 | } | ||
| 318 | return index; | ||
| 319 | } | ||
| 320 | |||
| 321 | /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. | ||
| 322 | pub fn posix_preadv(fd: i32, iov: [*]const posix.iovec, count: usize, offset: u64) PosixReadError!usize { | ||
| 323 | switch (builtin.os) { | ||
| 324 | builtin.Os.macosx => { | ||
| 325 | // Darwin does not have preadv but it does have pread. | ||
| 326 | var off: usize = 0; | ||
| 327 | var iov_i: usize = 0; | ||
| 328 | var inner_off: usize = 0; | ||
| 329 | while (true) { | ||
| 330 | const v = iov[iov_i]; | ||
| 331 | const rc = darwin.pread(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); | ||
| 332 | const err = darwin.getErrno(rc); | ||
| 333 | switch (err) { | ||
| 334 | 0 => { | ||
| 335 | off += rc; | ||
| 336 | inner_off += rc; | ||
| 337 | if (inner_off == v.iov_len) { | ||
| 338 | iov_i += 1; | ||
| 339 | inner_off = 0; | ||
| 340 | if (iov_i == count) { | ||
| 341 | return off; | ||
| 342 | } | ||
| 343 | } | ||
| 344 | if (rc == 0) return off; // EOF | ||
| 345 | continue; | ||
| 346 | }, | ||
| 347 | posix.EINTR => continue, | ||
| 348 | posix.EINVAL => unreachable, | ||
| 349 | posix.EFAULT => unreachable, | ||
| 350 | posix.ESPIPE => unreachable, // fd is not seekable | ||
| 351 | posix.EAGAIN => unreachable, // this function is not for non blocking | ||
| 352 | posix.EBADF => unreachable, // always a race condition | ||
| 353 | posix.EIO => return error.InputOutput, | ||
| 354 | posix.EISDIR => return error.IsDir, | ||
| 355 | posix.ENOBUFS => return error.SystemResources, | ||
| 356 | posix.ENOMEM => return error.SystemResources, | ||
| 357 | else => return unexpectedErrorPosix(err), | ||
| 358 | } | ||
| 359 | } | ||
| 360 | }, | ||
| 361 | builtin.Os.linux, builtin.Os.freebsd, Os.netbsd => while (true) { | ||
| 362 | const rc = posix.preadv(fd, iov, count, offset); | ||
| 363 | const err = posix.getErrno(rc); | ||
| 364 | switch (err) { | ||
| 365 | 0 => return rc, | ||
| 366 | posix.EINTR => continue, | ||
| 367 | posix.EINVAL => unreachable, | ||
| 368 | posix.EFAULT => unreachable, | ||
| 369 | posix.EAGAIN => unreachable, // don't call this function for non blocking | ||
| 370 | posix.EBADF => unreachable, // always a race condition | ||
| 371 | posix.EIO => return error.InputOutput, | ||
| 372 | posix.EISDIR => return error.IsDir, | ||
| 373 | posix.ENOBUFS => return error.SystemResources, | ||
| 374 | posix.ENOMEM => return error.SystemResources, | ||
| 375 | else => return unexpectedErrorPosix(err), | ||
| 376 | } | ||
| 377 | }, | ||
| 378 | else => @compileError("Unsupported OS"), | ||
| 379 | } | ||
| 380 | } | ||
| 381 | |||
| 382 | pub const PosixWriteError = error{ | ||
| 383 | DiskQuota, | ||
| 384 | FileTooBig, | ||
| 385 | InputOutput, | ||
| 386 | NoSpaceLeft, | ||
| 387 | AccessDenied, | ||
| 388 | BrokenPipe, | ||
| 389 | |||
| 390 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 391 | Unexpected, | ||
| 392 | }; | ||
| 393 | |||
| 394 | /// Calls POSIX write, and keeps trying if it gets interrupted. | ||
| 395 | pub fn posixWrite(fd: i32, bytes: []const u8) PosixWriteError!void { | ||
| 396 | // Linux can return EINVAL when write amount is > 0x7ffff000 | ||
| 397 | // See https://github.com/ziglang/zig/pull/743#issuecomment-363165856 | ||
| 398 | const max_bytes_len = 0x7ffff000; | ||
| 399 | |||
| 400 | var index: usize = 0; | ||
| 401 | while (index < bytes.len) { | ||
| 402 | const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len)); | ||
| 403 | const rc = posix.write(fd, bytes.ptr + index, amt_to_write); | ||
| 404 | const write_err = posix.getErrno(rc); | ||
| 405 | switch (write_err) { | ||
| 406 | 0 => { | ||
| 407 | index += rc; | ||
| 408 | continue; | ||
| 409 | }, | ||
| 410 | posix.EINTR => continue, | ||
| 411 | posix.EINVAL => unreachable, | ||
| 412 | posix.EFAULT => unreachable, | ||
| 413 | posix.EAGAIN => unreachable, // use posixAsyncWrite for non-blocking | ||
| 414 | posix.EBADF => unreachable, // always a race condition | ||
| 415 | posix.EDESTADDRREQ => unreachable, // connect was never called | ||
| 416 | posix.EDQUOT => return PosixWriteError.DiskQuota, | ||
| 417 | posix.EFBIG => return PosixWriteError.FileTooBig, | ||
| 418 | posix.EIO => return PosixWriteError.InputOutput, | ||
| 419 | posix.ENOSPC => return PosixWriteError.NoSpaceLeft, | ||
| 420 | posix.EPERM => return PosixWriteError.AccessDenied, | ||
| 421 | posix.EPIPE => return PosixWriteError.BrokenPipe, | ||
| 422 | else => return unexpectedErrorPosix(write_err), | ||
| 423 | } | ||
| 424 | } | ||
| 425 | } | ||
| 426 | |||
| 427 | pub fn posix_pwritev(fd: i32, iov: [*]const posix.iovec_const, count: usize, offset: u64) PosixWriteError!void { | ||
| 428 | switch (builtin.os) { | ||
| 429 | builtin.Os.macosx => { | ||
| 430 | // Darwin does not have pwritev but it does have pwrite. | ||
| 431 | var off: usize = 0; | ||
| 432 | var iov_i: usize = 0; | ||
| 433 | var inner_off: usize = 0; | ||
| 434 | while (true) { | ||
| 435 | const v = iov[iov_i]; | ||
| 436 | const rc = darwin.pwrite(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); | ||
| 437 | const err = darwin.getErrno(rc); | ||
| 438 | switch (err) { | ||
| 439 | 0 => { | ||
| 440 | off += rc; | ||
| 441 | inner_off += rc; | ||
| 442 | if (inner_off == v.iov_len) { | ||
| 443 | iov_i += 1; | ||
| 444 | inner_off = 0; | ||
| 445 | if (iov_i == count) { | ||
| 446 | return; | ||
| 447 | } | ||
| 448 | } | ||
| 449 | continue; | ||
| 450 | }, | ||
| 451 | posix.EINTR => continue, | ||
| 452 | posix.ESPIPE => unreachable, // fd is not seekable | ||
| 453 | posix.EINVAL => unreachable, | ||
| 454 | posix.EFAULT => unreachable, | ||
| 455 | posix.EAGAIN => unreachable, // use posixAsyncPWriteV for non-blocking | ||
| 456 | posix.EBADF => unreachable, // always a race condition | ||
| 457 | posix.EDESTADDRREQ => unreachable, // connect was never called | ||
| 458 | posix.EDQUOT => return PosixWriteError.DiskQuota, | ||
| 459 | posix.EFBIG => return PosixWriteError.FileTooBig, | ||
| 460 | posix.EIO => return PosixWriteError.InputOutput, | ||
| 461 | posix.ENOSPC => return PosixWriteError.NoSpaceLeft, | ||
| 462 | posix.EPERM => return PosixWriteError.AccessDenied, | ||
| 463 | posix.EPIPE => return PosixWriteError.BrokenPipe, | ||
| 464 | else => return unexpectedErrorPosix(err), | ||
| 465 | } | ||
| 466 | } | ||
| 467 | }, | ||
| 468 | builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => while (true) { | ||
| 469 | const rc = posix.pwritev(fd, iov, count, offset); | ||
| 470 | const err = posix.getErrno(rc); | ||
| 471 | switch (err) { | ||
| 472 | 0 => return, | ||
| 473 | posix.EINTR => continue, | ||
| 474 | posix.EINVAL => unreachable, | ||
| 475 | posix.EFAULT => unreachable, | ||
| 476 | posix.EAGAIN => unreachable, // use posixAsyncPWriteV for non-blocking | ||
| 477 | posix.EBADF => unreachable, // always a race condition | ||
| 478 | posix.EDESTADDRREQ => unreachable, // connect was never called | ||
| 479 | posix.EDQUOT => return PosixWriteError.DiskQuota, | ||
| 480 | posix.EFBIG => return PosixWriteError.FileTooBig, | ||
| 481 | posix.EIO => return PosixWriteError.InputOutput, | ||
| 482 | posix.ENOSPC => return PosixWriteError.NoSpaceLeft, | ||
| 483 | posix.EPERM => return PosixWriteError.AccessDenied, | ||
| 484 | posix.EPIPE => return PosixWriteError.BrokenPipe, | ||
| 485 | else => return unexpectedErrorPosix(err), | ||
| 486 | } | ||
| 487 | }, | ||
| 488 | else => @compileError("Unsupported OS"), | ||
| 489 | } | ||
| 490 | } | ||
| 491 | |||
| 492 | pub const PosixOpenError = error{ | ||
| 493 | AccessDenied, | ||
| 494 | FileTooBig, | ||
| 495 | IsDir, | ||
| 496 | SymLinkLoop, | ||
| 497 | ProcessFdQuotaExceeded, | ||
| 498 | NameTooLong, | ||
| 499 | SystemFdQuotaExceeded, | ||
| 500 | NoDevice, | ||
| 501 | FileNotFound, | ||
| 502 | SystemResources, | ||
| 503 | NoSpaceLeft, | ||
| 504 | NotDir, | ||
| 505 | PathAlreadyExists, | ||
| 506 | DeviceBusy, | ||
| 507 | |||
| 508 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 509 | Unexpected, | ||
| 510 | }; | ||
| 511 | |||
| 512 | /// ::file_path needs to be copied in memory to add a null terminating byte. | ||
| 513 | /// Calls POSIX open, keeps trying if it gets interrupted, and translates | ||
| 514 | /// the return value into zig errors. | ||
| 515 | pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 { | ||
| 516 | const file_path_c = try toPosixPath(file_path); | ||
| 517 | return posixOpenC(&file_path_c, flags, perm); | ||
| 518 | } | ||
| 519 | |||
| 520 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 521 | pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 { | ||
| 522 | while (true) { | ||
| 523 | const result = posix.open(file_path, flags, perm); | ||
| 524 | const err = posix.getErrno(result); | ||
| 525 | if (err > 0) { | ||
| 526 | switch (err) { | ||
| 527 | posix.EINTR => continue, | ||
| 528 | |||
| 529 | posix.EFAULT => unreachable, | ||
| 530 | posix.EINVAL => unreachable, | ||
| 531 | posix.EACCES => return PosixOpenError.AccessDenied, | ||
| 532 | posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig, | ||
| 533 | posix.EISDIR => return PosixOpenError.IsDir, | ||
| 534 | posix.ELOOP => return PosixOpenError.SymLinkLoop, | ||
| 535 | posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded, | ||
| 536 | posix.ENAMETOOLONG => return PosixOpenError.NameTooLong, | ||
| 537 | posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded, | ||
| 538 | posix.ENODEV => return PosixOpenError.NoDevice, | ||
| 539 | posix.ENOENT => return PosixOpenError.FileNotFound, | ||
| 540 | posix.ENOMEM => return PosixOpenError.SystemResources, | ||
| 541 | posix.ENOSPC => return PosixOpenError.NoSpaceLeft, | ||
| 542 | posix.ENOTDIR => return PosixOpenError.NotDir, | ||
| 543 | posix.EPERM => return PosixOpenError.AccessDenied, | ||
| 544 | posix.EEXIST => return PosixOpenError.PathAlreadyExists, | ||
| 545 | posix.EBUSY => return PosixOpenError.DeviceBusy, | ||
| 546 | else => return unexpectedErrorPosix(err), | ||
| 547 | } | ||
| 548 | } | ||
| 549 | return @intCast(i32, result); | ||
| 550 | } | ||
| 551 | } | ||
| 552 | |||
| 553 | /// Used to convert a slice to a null terminated slice on the stack. | ||
| 554 | /// TODO well defined copy elision | ||
| 555 | pub fn toPosixPath(file_path: []const u8) ![posix.PATH_MAX]u8 { | ||
| 556 | var path_with_null: [posix.PATH_MAX]u8 = undefined; | ||
| 557 | if (file_path.len >= posix.PATH_MAX) return error.NameTooLong; | ||
| 558 | mem.copy(u8, path_with_null[0..], file_path); | ||
| 559 | path_with_null[file_path.len] = 0; | ||
| 560 | return path_with_null; | ||
| 561 | } | ||
| 562 | |||
| 563 | pub fn posixDup2(old_fd: i32, new_fd: i32) !void { | ||
| 564 | while (true) { | ||
| 565 | const err = posix.getErrno(posix.dup2(old_fd, new_fd)); | ||
| 566 | if (err > 0) { | ||
| 567 | return switch (err) { | ||
| 568 | posix.EBUSY, posix.EINTR => continue, | ||
| 569 | posix.EMFILE => error.ProcessFdQuotaExceeded, | ||
| 570 | posix.EINVAL => unreachable, | ||
| 571 | else => unexpectedErrorPosix(err), | ||
| 572 | }; | ||
| 573 | } | ||
| 574 | return; | ||
| 575 | } | ||
| 576 | } | ||
| 577 | |||
| 578 | pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) ![]?[*]u8 { | ||
| 579 | const envp_count = env_map.count(); | ||
| 580 | const envp_buf = try allocator.alloc(?[*]u8, envp_count + 1); | ||
| 581 | mem.set(?[*]u8, envp_buf, null); | ||
| 582 | errdefer freeNullDelimitedEnvMap(allocator, envp_buf); | ||
| 583 | { | ||
| 584 | var it = env_map.iterator(); | ||
| 585 | var i: usize = 0; | ||
| 586 | while (it.next()) |pair| : (i += 1) { | ||
| 587 | const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2); | ||
| 588 | @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len); | ||
| 589 | env_buf[pair.key.len] = '='; | ||
| 590 | @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); | ||
| 591 | env_buf[env_buf.len - 1] = 0; | ||
| 592 | |||
| 593 | envp_buf[i] = env_buf.ptr; | ||
| 594 | } | ||
| 595 | assert(i == envp_count); | ||
| 596 | } | ||
| 597 | assert(envp_buf[envp_count] == null); | ||
| 598 | return envp_buf; | ||
| 599 | } | ||
| 600 | |||
| 601 | pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?[*]u8) void { | ||
| 602 | for (envp_buf) |env| { | ||
| 603 | const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break; | ||
| 604 | allocator.free(env_buf); | ||
| 605 | } | ||
| 606 | allocator.free(envp_buf); | ||
| 607 | } | ||
| 608 | |||
| 609 | /// This function must allocate memory to add a null terminating bytes on path and each arg. | ||
| 610 | /// It must also convert to KEY=VALUE\0 format for environment variables, and include null | ||
| 611 | /// pointers after the args and after the environment variables. | ||
| 612 | /// `argv[0]` is the executable path. | ||
| 613 | /// This function also uses the PATH environment variable to get the full path to the executable. | ||
| 614 | pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: *Allocator) !void { | ||
| 615 | const argv_buf = try allocator.alloc(?[*]u8, argv.len + 1); | ||
| 616 | mem.set(?[*]u8, argv_buf, null); | ||
| 617 | defer { | ||
| 618 | for (argv_buf) |arg| { | ||
| 619 | const arg_buf = if (arg) |ptr| cstr.toSlice(ptr) else break; | ||
| 620 | allocator.free(arg_buf); | ||
| 621 | } | ||
| 622 | allocator.free(argv_buf); | ||
| 623 | } | ||
| 624 | for (argv) |arg, i| { | ||
| 625 | const arg_buf = try allocator.alloc(u8, arg.len + 1); | ||
| 626 | @memcpy(arg_buf.ptr, arg.ptr, arg.len); | ||
| 627 | arg_buf[arg.len] = 0; | ||
| 628 | |||
| 629 | argv_buf[i] = arg_buf.ptr; | ||
| 630 | } | ||
| 631 | argv_buf[argv.len] = null; | ||
| 632 | |||
| 633 | const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); | ||
| 634 | defer freeNullDelimitedEnvMap(allocator, envp_buf); | ||
| 635 | |||
| 636 | const exe_path = argv[0]; | ||
| 637 | if (mem.indexOfScalar(u8, exe_path, '/') != null) { | ||
| 638 | return posixExecveErrnoToErr(posix.getErrno(posix.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr))); | ||
| 639 | } | ||
| 640 | |||
| 641 | const PATH = getEnvPosix("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; | ||
| 642 | // PATH.len because it is >= the largest search_path | ||
| 643 | // +1 for the / to join the search path and exe_path | ||
| 644 | // +1 for the null terminating byte | ||
| 645 | const path_buf = try allocator.alloc(u8, PATH.len + exe_path.len + 2); | ||
| 646 | defer allocator.free(path_buf); | ||
| 647 | var it = mem.tokenize(PATH, ":"); | ||
| 648 | var seen_eacces = false; | ||
| 649 | var err: usize = undefined; | ||
| 650 | while (it.next()) |search_path| { | ||
| 651 | mem.copy(u8, path_buf, search_path); | ||
| 652 | path_buf[search_path.len] = '/'; | ||
| 653 | mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path); | ||
| 654 | path_buf[search_path.len + exe_path.len + 1] = 0; | ||
| 655 | err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)); | ||
| 656 | assert(err > 0); | ||
| 657 | if (err == posix.EACCES) { | ||
| 658 | seen_eacces = true; | ||
| 659 | } else if (err != posix.ENOENT) { | ||
| 660 | return posixExecveErrnoToErr(err); | ||
| 661 | } | ||
| 662 | } | ||
| 663 | if (seen_eacces) { | ||
| 664 | err = posix.EACCES; | ||
| 665 | } | ||
| 666 | return posixExecveErrnoToErr(err); | ||
| 667 | } | ||
| 668 | |||
| 669 | pub const PosixExecveError = error{ | ||
| 670 | SystemResources, | ||
| 671 | AccessDenied, | ||
| 672 | InvalidExe, | ||
| 673 | FileSystem, | ||
| 674 | IsDir, | ||
| 675 | FileNotFound, | ||
| 676 | NotDir, | ||
| 677 | FileBusy, | ||
| 678 | |||
| 679 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 680 | Unexpected, | ||
| 681 | }; | ||
| 682 | |||
| 683 | fn posixExecveErrnoToErr(err: usize) PosixExecveError { | ||
| 684 | assert(err > 0); | ||
| 685 | switch (err) { | ||
| 686 | posix.EFAULT => unreachable, | ||
| 687 | posix.E2BIG => return error.SystemResources, | ||
| 688 | posix.EMFILE => return error.SystemResources, | ||
| 689 | posix.ENAMETOOLONG => return error.SystemResources, | ||
| 690 | posix.ENFILE => return error.SystemResources, | ||
| 691 | posix.ENOMEM => return error.SystemResources, | ||
| 692 | posix.EACCES => return error.AccessDenied, | ||
| 693 | posix.EPERM => return error.AccessDenied, | ||
| 694 | posix.EINVAL => return error.InvalidExe, | ||
| 695 | posix.ENOEXEC => return error.InvalidExe, | ||
| 696 | posix.EIO => return error.FileSystem, | ||
| 697 | posix.ELOOP => return error.FileSystem, | ||
| 698 | posix.EISDIR => return error.IsDir, | ||
| 699 | posix.ENOENT => return error.FileNotFound, | ||
| 700 | posix.ENOTDIR => return error.NotDir, | ||
| 701 | posix.ETXTBSY => return error.FileBusy, | ||
| 702 | else => return unexpectedErrorPosix(err), | ||
| 703 | } | ||
| 704 | } | ||
| 705 | |||
| 706 | pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null; | ||
| 707 | pub var posix_environ_raw: [][*]u8 = undefined; | ||
| 708 | |||
| 709 | /// See std.elf for the constants. | ||
| 710 | pub fn linuxGetAuxVal(index: usize) usize { | ||
| 711 | if (builtin.link_libc) { | ||
| 712 | return usize(std.c.getauxval(index)); | ||
| 713 | } else if (linux_elf_aux_maybe) |auxv| { | ||
| 714 | var i: usize = 0; | ||
| 715 | while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) { | ||
| 716 | if (auxv[i].a_type == index) | ||
| 717 | return auxv[i].a_un.a_val; | ||
| 718 | } | ||
| 719 | } | ||
| 720 | return 0; | ||
| 721 | } | ||
| 722 | |||
| 723 | pub fn getBaseAddress() usize { | 135 | pub fn getBaseAddress() usize { |
| 724 | switch (builtin.os) { | 136 | switch (builtin.os) { |
| 725 | builtin.Os.linux => { | 137 | builtin.Os.linux => { |
| ... | @@ -803,7 +215,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { | ... | @@ -803,7 +215,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { |
| 803 | } | 215 | } |
| 804 | return result; | 216 | return result; |
| 805 | } else { | 217 | } else { |
| 806 | for (posix_environ_raw) |ptr| { | 218 | for (posix.environ) |ptr| { |
| 807 | var line_i: usize = 0; | 219 | var line_i: usize = 0; |
| 808 | while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} | 220 | while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} |
| 809 | const key = ptr[0..line_i]; | 221 | const key = ptr[0..line_i]; |
| ... | @@ -823,23 +235,6 @@ test "os.getEnvMap" { | ... | @@ -823,23 +235,6 @@ test "os.getEnvMap" { |
| 823 | defer env.deinit(); | 235 | defer env.deinit(); |
| 824 | } | 236 | } |
| 825 | 237 | ||
| 826 | /// TODO make this go through libc when we have it | ||
| 827 | pub fn getEnvPosix(key: []const u8) ?[]const u8 { | ||
| 828 | for (posix_environ_raw) |ptr| { | ||
| 829 | var line_i: usize = 0; | ||
| 830 | while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} | ||
| 831 | const this_key = ptr[0..line_i]; | ||
| 832 | if (!mem.eql(u8, key, this_key)) continue; | ||
| 833 | |||
| 834 | var end_i: usize = line_i; | ||
| 835 | while (ptr[end_i] != 0) : (end_i += 1) {} | ||
| 836 | const this_value = ptr[line_i + 1 .. end_i]; | ||
| 837 | |||
| 838 | return this_value; | ||
| 839 | } | ||
| 840 | return null; | ||
| 841 | } | ||
| 842 | |||
| 843 | pub const GetEnvVarOwnedError = error{ | 238 | pub const GetEnvVarOwnedError = error{ |
| 844 | OutOfMemory, | 239 | OutOfMemory, |
| 845 | EnvironmentVariableNotFound, | 240 | EnvironmentVariableNotFound, |
| ... | @@ -896,130 +291,22 @@ test "os.getEnvVarOwned" { | ... | @@ -896,130 +291,22 @@ test "os.getEnvVarOwned" { |
| 896 | testing.expectError(error.EnvironmentVariableNotFound, getEnvVarOwned(ga, "BADENV")); | 291 | testing.expectError(error.EnvironmentVariableNotFound, getEnvVarOwned(ga, "BADENV")); |
| 897 | } | 292 | } |
| 898 | 293 | ||
| 899 | /// Caller must free the returned memory. | 294 | /// The result is a slice of `out_buffer`, from index `0`. |
| 900 | pub fn getCwdAlloc(allocator: *Allocator) ![]u8 { | 295 | pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { |
| 901 | var buf: [MAX_PATH_BYTES]u8 = undefined; | 296 | return posix.getcwd(out_buffer); |
| 902 | return mem.dupe(allocator, u8, try getCwd(&buf)); | ||
| 903 | } | 297 | } |
| 904 | 298 | ||
| 905 | pub const GetCwdError = error{Unexpected}; | 299 | /// Caller must free the returned memory. |
| 906 | 300 | pub fn getCwdAlloc(allocator: *Allocator) ![]u8 { | |
| 907 | /// The result is a slice of out_buffer. | 301 | var buf: [os.MAX_PATH_BYTES]u8 = undefined; |
| 908 | pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 { | 302 | return mem.dupe(allocator, u8, try posix.getcwd(&buf)); |
| 909 | switch (builtin.os) { | ||
| 910 | Os.windows => { | ||
| 911 | var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | ||
| 912 | const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast | ||
| 913 | const casted_ptr = ([*]u16)(&utf16le_buf); // TODO shouldn't need this cast | ||
| 914 | const result = windows.GetCurrentDirectoryW(casted_len, casted_ptr); | ||
| 915 | if (result == 0) { | ||
| 916 | const err = windows.GetLastError(); | ||
| 917 | switch (err) { | ||
| 918 | else => return unexpectedErrorWindows(err), | ||
| 919 | } | ||
| 920 | } | ||
| 921 | assert(result <= utf16le_buf.len); | ||
| 922 | const utf16le_slice = utf16le_buf[0..result]; | ||
| 923 | // Trust that Windows gives us valid UTF-16LE. | ||
| 924 | const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; | ||
| 925 | return out_buffer[0..end_index]; | ||
| 926 | }, | ||
| 927 | else => { | ||
| 928 | const err = posix.getErrno(posix.getcwd(out_buffer, out_buffer.len)); | ||
| 929 | switch (err) { | ||
| 930 | 0 => return cstr.toSlice(out_buffer), | ||
| 931 | posix.ERANGE => unreachable, | ||
| 932 | else => return unexpectedErrorPosix(err), | ||
| 933 | } | ||
| 934 | }, | ||
| 935 | } | ||
| 936 | } | 303 | } |
| 937 | 304 | ||
| 938 | test "os.getCwd" { | 305 | test "getCwdAlloc" { |
| 939 | // at least call it so it gets compiled | 306 | // at least call it so it gets compiled |
| 940 | _ = getCwdAlloc(debug.global_allocator) catch undefined; | 307 | var buf: [1000]u8 = undefined; |
| 941 | var buf: [MAX_PATH_BYTES]u8 = undefined; | 308 | const allocator = &std.heap.FixedBufferAllocator.init(&buf).allocator; |
| 942 | _ = getCwd(&buf) catch undefined; | 309 | _ = getCwdAlloc(allocator) catch {}; |
| 943 | } | ||
| 944 | |||
| 945 | pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError; | ||
| 946 | |||
| 947 | /// TODO add a symLinkC variant | ||
| 948 | pub fn symLink(existing_path: []const u8, new_path: []const u8) SymLinkError!void { | ||
| 949 | if (is_windows) { | ||
| 950 | return symLinkWindows(existing_path, new_path); | ||
| 951 | } else { | ||
| 952 | return symLinkPosix(existing_path, new_path); | ||
| 953 | } | ||
| 954 | } | ||
| 955 | |||
| 956 | pub const WindowsSymLinkError = error{ | ||
| 957 | NameTooLong, | ||
| 958 | InvalidUtf8, | ||
| 959 | BadPathName, | ||
| 960 | |||
| 961 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 962 | Unexpected, | ||
| 963 | }; | ||
| 964 | |||
| 965 | pub fn symLinkW(existing_path_w: [*]const u16, new_path_w: [*]const u16) WindowsSymLinkError!void { | ||
| 966 | if (windows.CreateSymbolicLinkW(existing_path_w, new_path_w, 0) == 0) { | ||
| 967 | const err = windows.GetLastError(); | ||
| 968 | switch (err) { | ||
| 969 | else => return unexpectedErrorWindows(err), | ||
| 970 | } | ||
| 971 | } | ||
| 972 | } | ||
| 973 | |||
| 974 | pub fn symLinkWindows(existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void { | ||
| 975 | const existing_path_w = try windows_util.sliceToPrefixedFileW(existing_path); | ||
| 976 | const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); | ||
| 977 | return symLinkW(&existing_path_w, &new_path_w); | ||
| 978 | } | ||
| 979 | |||
| 980 | pub const PosixSymLinkError = error{ | ||
| 981 | AccessDenied, | ||
| 982 | DiskQuota, | ||
| 983 | PathAlreadyExists, | ||
| 984 | FileSystem, | ||
| 985 | SymLinkLoop, | ||
| 986 | NameTooLong, | ||
| 987 | FileNotFound, | ||
| 988 | SystemResources, | ||
| 989 | NoSpaceLeft, | ||
| 990 | ReadOnlyFileSystem, | ||
| 991 | NotDir, | ||
| 992 | |||
| 993 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 994 | Unexpected, | ||
| 995 | }; | ||
| 996 | |||
| 997 | pub fn symLinkPosixC(existing_path: [*]const u8, new_path: [*]const u8) PosixSymLinkError!void { | ||
| 998 | const err = posix.getErrno(posix.symlink(existing_path, new_path)); | ||
| 999 | switch (err) { | ||
| 1000 | 0 => return, | ||
| 1001 | posix.EFAULT => unreachable, | ||
| 1002 | posix.EINVAL => unreachable, | ||
| 1003 | posix.EACCES => return error.AccessDenied, | ||
| 1004 | posix.EPERM => return error.AccessDenied, | ||
| 1005 | posix.EDQUOT => return error.DiskQuota, | ||
| 1006 | posix.EEXIST => return error.PathAlreadyExists, | ||
| 1007 | posix.EIO => return error.FileSystem, | ||
| 1008 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1009 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 1010 | posix.ENOENT => return error.FileNotFound, | ||
| 1011 | posix.ENOTDIR => return error.NotDir, | ||
| 1012 | posix.ENOMEM => return error.SystemResources, | ||
| 1013 | posix.ENOSPC => return error.NoSpaceLeft, | ||
| 1014 | posix.EROFS => return error.ReadOnlyFileSystem, | ||
| 1015 | else => return unexpectedErrorPosix(err), | ||
| 1016 | } | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | pub fn symLinkPosix(existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void { | ||
| 1020 | const existing_path_c = try toPosixPath(existing_path); | ||
| 1021 | const new_path_c = try toPosixPath(new_path); | ||
| 1022 | return symLinkPosixC(&existing_path_c, &new_path_c); | ||
| 1023 | } | 310 | } |
| 1024 | 311 | ||
| 1025 | // here we replace the standard +/ with -_ so that it can be used in a file name | 312 | // here we replace the standard +/ with -_ so that it can be used in a file name |
| ... | @@ -1054,78 +341,6 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: | ... | @@ -1054,78 +341,6 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: |
| 1054 | } | 341 | } |
| 1055 | } | 342 | } |
| 1056 | 343 | ||
| 1057 | pub const DeleteFileError = error{ | ||
| 1058 | FileNotFound, | ||
| 1059 | AccessDenied, | ||
| 1060 | FileBusy, | ||
| 1061 | FileSystem, | ||
| 1062 | IsDir, | ||
| 1063 | SymLinkLoop, | ||
| 1064 | NameTooLong, | ||
| 1065 | NotDir, | ||
| 1066 | SystemResources, | ||
| 1067 | ReadOnlyFileSystem, | ||
| 1068 | |||
| 1069 | /// On Windows, file paths must be valid Unicode. | ||
| 1070 | InvalidUtf8, | ||
| 1071 | |||
| 1072 | /// On Windows, file paths cannot contain these characters: | ||
| 1073 | /// '/', '*', '?', '"', '<', '>', '|' | ||
| 1074 | BadPathName, | ||
| 1075 | |||
| 1076 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 1077 | Unexpected, | ||
| 1078 | }; | ||
| 1079 | |||
| 1080 | pub fn deleteFile(file_path: []const u8) DeleteFileError!void { | ||
| 1081 | if (builtin.os == Os.windows) { | ||
| 1082 | const file_path_w = try windows_util.sliceToPrefixedFileW(file_path); | ||
| 1083 | return deleteFileW(&file_path_w); | ||
| 1084 | } else { | ||
| 1085 | const file_path_c = try toPosixPath(file_path); | ||
| 1086 | return deleteFileC(&file_path_c); | ||
| 1087 | } | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | pub fn deleteFileW(file_path: [*]const u16) DeleteFileError!void { | ||
| 1091 | if (windows.DeleteFileW(file_path) == 0) { | ||
| 1092 | const err = windows.GetLastError(); | ||
| 1093 | switch (err) { | ||
| 1094 | windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, | ||
| 1095 | windows.ERROR.ACCESS_DENIED => return error.AccessDenied, | ||
| 1096 | windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong, | ||
| 1097 | windows.ERROR.INVALID_PARAMETER => return error.NameTooLong, | ||
| 1098 | else => return unexpectedErrorWindows(err), | ||
| 1099 | } | ||
| 1100 | } | ||
| 1101 | } | ||
| 1102 | |||
| 1103 | pub fn deleteFileC(file_path: [*]const u8) DeleteFileError!void { | ||
| 1104 | if (is_windows) { | ||
| 1105 | const file_path_w = try windows_util.cStrToPrefixedFileW(file_path); | ||
| 1106 | return deleteFileW(&file_path_w); | ||
| 1107 | } else { | ||
| 1108 | const err = posix.getErrno(posix.unlink(file_path)); | ||
| 1109 | switch (err) { | ||
| 1110 | 0 => return, | ||
| 1111 | posix.EACCES => return error.AccessDenied, | ||
| 1112 | posix.EPERM => return error.AccessDenied, | ||
| 1113 | posix.EBUSY => return error.FileBusy, | ||
| 1114 | posix.EFAULT => unreachable, | ||
| 1115 | posix.EINVAL => unreachable, | ||
| 1116 | posix.EIO => return error.FileSystem, | ||
| 1117 | posix.EISDIR => return error.IsDir, | ||
| 1118 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1119 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 1120 | posix.ENOENT => return error.FileNotFound, | ||
| 1121 | posix.ENOTDIR => return error.NotDir, | ||
| 1122 | posix.ENOMEM => return error.SystemResources, | ||
| 1123 | posix.EROFS => return error.ReadOnlyFileSystem, | ||
| 1124 | else => return unexpectedErrorPosix(err), | ||
| 1125 | } | ||
| 1126 | } | ||
| 1127 | } | ||
| 1128 | |||
| 1129 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is | 344 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is |
| 1130 | /// merged and readily available, | 345 | /// merged and readily available, |
| 1131 | /// there is a possibility of power loss or application termination leaving temporary files present | 346 | /// there is a possibility of power loss or application termination leaving temporary files present |
| ... | @@ -1236,8 +451,8 @@ pub const AtomicFile = struct { | ... | @@ -1236,8 +451,8 @@ pub const AtomicFile = struct { |
| 1236 | const dest_path_c = try toPosixPath(self.dest_path); | 451 | const dest_path_c = try toPosixPath(self.dest_path); |
| 1237 | return renameC(&self.tmp_path_buf, &dest_path_c); | 452 | return renameC(&self.tmp_path_buf, &dest_path_c); |
| 1238 | } else if (is_windows) { | 453 | } else if (is_windows) { |
| 1239 | const dest_path_w = try windows_util.sliceToPrefixedFileW(self.dest_path); | 454 | const dest_path_w = try posix.sliceToPrefixedFileW(self.dest_path); |
| 1240 | const tmp_path_w = try windows_util.cStrToPrefixedFileW(&self.tmp_path_buf); | 455 | const tmp_path_w = try posix.cStrToPrefixedFileW(&self.tmp_path_buf); |
| 1241 | return renameW(&tmp_path_w, &dest_path_w); | 456 | return renameW(&tmp_path_w, &dest_path_w); |
| 1242 | } else { | 457 | } else { |
| 1243 | @compileError("Unsupported OS"); | 458 | @compileError("Unsupported OS"); |
| ... | @@ -1245,109 +460,27 @@ pub const AtomicFile = struct { | ... | @@ -1245,109 +460,27 @@ pub const AtomicFile = struct { |
| 1245 | } | 460 | } |
| 1246 | }; | 461 | }; |
| 1247 | 462 | ||
| 1248 | pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void { | 463 | const default_new_dir_mode = 0o755; |
| 1249 | if (is_windows) { | ||
| 1250 | const old_path_w = try windows_util.cStrToPrefixedFileW(old_path); | ||
| 1251 | const new_path_w = try windows_util.cStrToPrefixedFileW(new_path); | ||
| 1252 | return renameW(&old_path_w, &new_path_w); | ||
| 1253 | } else { | ||
| 1254 | const err = posix.getErrno(posix.rename(old_path, new_path)); | ||
| 1255 | switch (err) { | ||
| 1256 | 0 => return, | ||
| 1257 | posix.EACCES => return error.AccessDenied, | ||
| 1258 | posix.EPERM => return error.AccessDenied, | ||
| 1259 | posix.EBUSY => return error.FileBusy, | ||
| 1260 | posix.EDQUOT => return error.DiskQuota, | ||
| 1261 | posix.EFAULT => unreachable, | ||
| 1262 | posix.EINVAL => unreachable, | ||
| 1263 | posix.EISDIR => return error.IsDir, | ||
| 1264 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1265 | posix.EMLINK => return error.LinkQuotaExceeded, | ||
| 1266 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 1267 | posix.ENOENT => return error.FileNotFound, | ||
| 1268 | posix.ENOTDIR => return error.NotDir, | ||
| 1269 | posix.ENOMEM => return error.SystemResources, | ||
| 1270 | posix.ENOSPC => return error.NoSpaceLeft, | ||
| 1271 | posix.EEXIST => return error.PathAlreadyExists, | ||
| 1272 | posix.ENOTEMPTY => return error.PathAlreadyExists, | ||
| 1273 | posix.EROFS => return error.ReadOnlyFileSystem, | ||
| 1274 | posix.EXDEV => return error.RenameAcrossMountPoints, | ||
| 1275 | else => return unexpectedErrorPosix(err), | ||
| 1276 | } | ||
| 1277 | } | ||
| 1278 | } | ||
| 1279 | |||
| 1280 | pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) !void { | ||
| 1281 | const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; | ||
| 1282 | if (windows.MoveFileExW(old_path, new_path, flags) == 0) { | ||
| 1283 | const err = windows.GetLastError(); | ||
| 1284 | switch (err) { | ||
| 1285 | else => return unexpectedErrorWindows(err), | ||
| 1286 | } | ||
| 1287 | } | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | pub fn rename(old_path: []const u8, new_path: []const u8) !void { | ||
| 1291 | if (is_windows) { | ||
| 1292 | const old_path_w = try windows_util.sliceToPrefixedFileW(old_path); | ||
| 1293 | const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); | ||
| 1294 | return renameW(&old_path_w, &new_path_w); | ||
| 1295 | } else { | ||
| 1296 | const old_path_c = try toPosixPath(old_path); | ||
| 1297 | const new_path_c = try toPosixPath(new_path); | ||
| 1298 | return renameC(&old_path_c, &new_path_c); | ||
| 1299 | } | ||
| 1300 | } | ||
| 1301 | 464 | ||
| 465 | /// Create a new directory. | ||
| 1302 | pub fn makeDir(dir_path: []const u8) !void { | 466 | pub fn makeDir(dir_path: []const u8) !void { |
| 1303 | if (is_windows) { | 467 | return posix.mkdir(dir_path, default_new_dir_mode); |
| 1304 | return makeDirWindows(dir_path); | ||
| 1305 | } else { | ||
| 1306 | return makeDirPosix(dir_path); | ||
| 1307 | } | ||
| 1308 | } | 468 | } |
| 1309 | 469 | ||
| 1310 | pub fn makeDirWindows(dir_path: []const u8) !void { | 470 | /// Same as `makeDir` except the parameter is a null-terminated UTF8-encoded string. |
| 1311 | const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); | 471 | pub fn makeDirC(dir_path: [*]const u8) !void { |
| 1312 | 472 | return posix.mkdirC(dir_path, default_new_dir_mode); | |
| 1313 | if (windows.CreateDirectoryW(&dir_path_w, null) == 0) { | ||
| 1314 | const err = windows.GetLastError(); | ||
| 1315 | return switch (err) { | ||
| 1316 | windows.ERROR.ALREADY_EXISTS => error.PathAlreadyExists, | ||
| 1317 | windows.ERROR.PATH_NOT_FOUND => error.FileNotFound, | ||
| 1318 | else => unexpectedErrorWindows(err), | ||
| 1319 | }; | ||
| 1320 | } | ||
| 1321 | } | 473 | } |
| 1322 | 474 | ||
| 1323 | pub fn makeDirPosixC(dir_path: [*]const u8) !void { | 475 | /// Same as `makeDir` except the parameter is a null-terminated UTF16LE-encoded string. |
| 1324 | const err = posix.getErrno(posix.mkdir(dir_path, 0o755)); | 476 | pub fn makeDirW(dir_path: [*]const u16) !void { |
| 1325 | switch (err) { | 477 | return posix.mkdirW(dir_path, default_new_dir_mode); |
| 1326 | 0 => return, | ||
| 1327 | posix.EACCES => return error.AccessDenied, | ||
| 1328 | posix.EPERM => return error.AccessDenied, | ||
| 1329 | posix.EDQUOT => return error.DiskQuota, | ||
| 1330 | posix.EEXIST => return error.PathAlreadyExists, | ||
| 1331 | posix.EFAULT => unreachable, | ||
| 1332 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1333 | posix.EMLINK => return error.LinkQuotaExceeded, | ||
| 1334 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 1335 | posix.ENOENT => return error.FileNotFound, | ||
| 1336 | posix.ENOMEM => return error.SystemResources, | ||
| 1337 | posix.ENOSPC => return error.NoSpaceLeft, | ||
| 1338 | posix.ENOTDIR => return error.NotDir, | ||
| 1339 | posix.EROFS => return error.ReadOnlyFileSystem, | ||
| 1340 | else => return unexpectedErrorPosix(err), | ||
| 1341 | } | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | pub fn makeDirPosix(dir_path: []const u8) !void { | ||
| 1345 | const dir_path_c = try toPosixPath(dir_path); | ||
| 1346 | return makeDirPosixC(&dir_path_c); | ||
| 1347 | } | 478 | } |
| 1348 | 479 | ||
| 1349 | /// Calls makeDir recursively to make an entire path. Returns success if the path | 480 | /// Calls makeDir recursively to make an entire path. Returns success if the path |
| 1350 | /// already exists and is a directory. | 481 | /// already exists and is a directory. |
| 482 | /// This function is not atomic, and if it returns an error, the file system may | ||
| 483 | /// have been modified regardless. | ||
| 1351 | /// TODO determine if we can remove the allocator requirement from this function | 484 | /// TODO determine if we can remove the allocator requirement from this function |
| 1352 | pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { | 485 | pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { |
| 1353 | const resolved_path = try path.resolve(allocator, [][]const u8{full_path}); | 486 | const resolved_path = try path.resolve(allocator, [][]const u8{full_path}); |
| ... | @@ -1381,78 +514,20 @@ pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { | ... | @@ -1381,78 +514,20 @@ pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { |
| 1381 | } | 514 | } |
| 1382 | } | 515 | } |
| 1383 | 516 | ||
| 1384 | pub const DeleteDirError = error{ | 517 | /// Returns `error.DirNotEmpty` if the directory is not empty. |
| 1385 | AccessDenied, | 518 | /// To delete a directory recursively, see `deleteTree`. |
| 1386 | FileBusy, | 519 | pub fn deleteDir(dir_path: []const u8) DeleteDirError!void { |
| 1387 | SymLinkLoop, | 520 | return posix.rmdir(dir_path); |
| 1388 | NameTooLong, | ||
| 1389 | FileNotFound, | ||
| 1390 | SystemResources, | ||
| 1391 | NotDir, | ||
| 1392 | DirNotEmpty, | ||
| 1393 | ReadOnlyFileSystem, | ||
| 1394 | InvalidUtf8, | ||
| 1395 | BadPathName, | ||
| 1396 | |||
| 1397 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 1398 | Unexpected, | ||
| 1399 | }; | ||
| 1400 | |||
| 1401 | pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void { | ||
| 1402 | switch (builtin.os) { | ||
| 1403 | Os.windows => { | ||
| 1404 | const dir_path_w = try windows_util.cStrToPrefixedFileW(dir_path); | ||
| 1405 | return deleteDirW(&dir_path_w); | ||
| 1406 | }, | ||
| 1407 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 1408 | const err = posix.getErrno(posix.rmdir(dir_path)); | ||
| 1409 | switch (err) { | ||
| 1410 | 0 => return, | ||
| 1411 | posix.EACCES => return error.AccessDenied, | ||
| 1412 | posix.EPERM => return error.AccessDenied, | ||
| 1413 | posix.EBUSY => return error.FileBusy, | ||
| 1414 | posix.EFAULT => unreachable, | ||
| 1415 | posix.EINVAL => unreachable, | ||
| 1416 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1417 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 1418 | posix.ENOENT => return error.FileNotFound, | ||
| 1419 | posix.ENOMEM => return error.SystemResources, | ||
| 1420 | posix.ENOTDIR => return error.NotDir, | ||
| 1421 | posix.EEXIST => return error.DirNotEmpty, | ||
| 1422 | posix.ENOTEMPTY => return error.DirNotEmpty, | ||
| 1423 | posix.EROFS => return error.ReadOnlyFileSystem, | ||
| 1424 | else => return unexpectedErrorPosix(err), | ||
| 1425 | } | ||
| 1426 | }, | ||
| 1427 | else => @compileError("unimplemented"), | ||
| 1428 | } | ||
| 1429 | } | 521 | } |
| 1430 | 522 | ||
| 1431 | pub fn deleteDirW(dir_path_w: [*]const u16) DeleteDirError!void { | 523 | /// Same as `deleteDir` except the parameter is a null-terminated UTF8-encoded string. |
| 1432 | if (windows.RemoveDirectoryW(dir_path_w) == 0) { | 524 | pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void { |
| 1433 | const err = windows.GetLastError(); | 525 | return posix.rmdirC(dir_path); |
| 1434 | switch (err) { | ||
| 1435 | windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, | ||
| 1436 | windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty, | ||
| 1437 | else => return unexpectedErrorWindows(err), | ||
| 1438 | } | ||
| 1439 | } | ||
| 1440 | } | 526 | } |
| 1441 | 527 | ||
| 1442 | /// Returns ::error.DirNotEmpty if the directory is not empty. | 528 | /// Same as `deleteDir` except the parameter is a null-terminated UTF16LE-encoded string. |
| 1443 | /// To delete a directory recursively, see ::deleteTree | 529 | pub fn deleteDirW(dir_path: [*]const u16) DeleteDirError!void { |
| 1444 | pub fn deleteDir(dir_path: []const u8) DeleteDirError!void { | 530 | return posix.rmdirW(dir_path); |
| 1445 | switch (builtin.os) { | ||
| 1446 | Os.windows => { | ||
| 1447 | const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); | ||
| 1448 | return deleteDirW(&dir_path_w); | ||
| 1449 | }, | ||
| 1450 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 1451 | const dir_path_c = try toPosixPath(dir_path); | ||
| 1452 | return deleteDirC(&dir_path_c); | ||
| 1453 | }, | ||
| 1454 | else => @compileError("unimplemented"), | ||
| 1455 | } | ||
| 1456 | } | 531 | } |
| 1457 | 532 | ||
| 1458 | /// Whether ::full_path describes a symlink, file, or directory, this function | 533 | /// Whether ::full_path describes a symlink, file, or directory, this function |
| ... | @@ -1486,7 +561,6 @@ const DeleteTreeError = error{ | ... | @@ -1486,7 +561,6 @@ const DeleteTreeError = error{ |
| 1486 | /// '/', '*', '?', '"', '<', '>', '|' | 561 | /// '/', '*', '?', '"', '<', '>', '|' |
| 1487 | BadPathName, | 562 | BadPathName, |
| 1488 | 563 | ||
| 1489 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 1490 | Unexpected, | 564 | Unexpected, |
| 1491 | }; | 565 | }; |
| 1492 | 566 | ||
| ... | @@ -1624,7 +698,6 @@ pub const Dir = struct { | ... | @@ -1624,7 +698,6 @@ pub const Dir = struct { |
| 1624 | BadPathName, | 698 | BadPathName, |
| 1625 | DeviceBusy, | 699 | DeviceBusy, |
| 1626 | 700 | ||
| 1627 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 1628 | Unexpected, | 701 | Unexpected, |
| 1629 | }; | 702 | }; |
| 1630 | 703 | ||
| ... | @@ -1878,121 +951,23 @@ pub const Dir = struct { | ... | @@ -1878,121 +951,23 @@ pub const Dir = struct { |
| 1878 | posix.DT_WHT => Entry.Kind.Whiteout, | 951 | posix.DT_WHT => Entry.Kind.Whiteout, |
| 1879 | else => Entry.Kind.Unknown, | 952 | else => Entry.Kind.Unknown, |
| 1880 | }; | 953 | }; |
| 1881 | return Entry{ | 954 | return Entry{ |
| 1882 | .name = name, | 955 | .name = name, |
| 1883 | .kind = entry_kind, | 956 | .kind = entry_kind, |
| 1884 | }; | 957 | }; |
| 1885 | } | ||
| 1886 | } | ||
| 1887 | }; | ||
| 1888 | |||
| 1889 | pub fn changeCurDir(dir_path: []const u8) !void { | ||
| 1890 | const dir_path_c = try toPosixPath(dir_path); | ||
| 1891 | const err = posix.getErrno(posix.chdir(&dir_path_c)); | ||
| 1892 | switch (err) { | ||
| 1893 | 0 => return, | ||
| 1894 | posix.EACCES => return error.AccessDenied, | ||
| 1895 | posix.EFAULT => unreachable, | ||
| 1896 | posix.EIO => return error.FileSystem, | ||
| 1897 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1898 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 1899 | posix.ENOENT => return error.FileNotFound, | ||
| 1900 | posix.ENOMEM => return error.SystemResources, | ||
| 1901 | posix.ENOTDIR => return error.NotDir, | ||
| 1902 | else => return unexpectedErrorPosix(err), | ||
| 1903 | } | ||
| 1904 | } | ||
| 1905 | |||
| 1906 | /// Read value of a symbolic link. | ||
| 1907 | /// The return value is a slice of out_buffer. | ||
| 1908 | pub fn readLinkC(out_buffer: *[posix.PATH_MAX]u8, pathname: [*]const u8) ![]u8 { | ||
| 1909 | const rc = posix.readlink(pathname, out_buffer, out_buffer.len); | ||
| 1910 | const err = posix.getErrno(rc); | ||
| 1911 | switch (err) { | ||
| 1912 | 0 => return out_buffer[0..rc], | ||
| 1913 | posix.EACCES => return error.AccessDenied, | ||
| 1914 | posix.EFAULT => unreachable, | ||
| 1915 | posix.EINVAL => unreachable, | ||
| 1916 | posix.EIO => return error.FileSystem, | ||
| 1917 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1918 | posix.ENAMETOOLONG => unreachable, // out_buffer is at least PATH_MAX | ||
| 1919 | posix.ENOENT => return error.FileNotFound, | ||
| 1920 | posix.ENOMEM => return error.SystemResources, | ||
| 1921 | posix.ENOTDIR => return error.NotDir, | ||
| 1922 | else => return unexpectedErrorPosix(err), | ||
| 1923 | } | ||
| 1924 | } | ||
| 1925 | |||
| 1926 | /// Read value of a symbolic link. | ||
| 1927 | /// The return value is a slice of out_buffer. | ||
| 1928 | pub fn readLink(out_buffer: *[posix.PATH_MAX]u8, file_path: []const u8) ![]u8 { | ||
| 1929 | const file_path_c = try toPosixPath(file_path); | ||
| 1930 | return readLinkC(out_buffer, &file_path_c); | ||
| 1931 | } | ||
| 1932 | |||
| 1933 | pub fn posix_setuid(uid: u32) !void { | ||
| 1934 | const err = posix.getErrno(posix.setuid(uid)); | ||
| 1935 | if (err == 0) return; | ||
| 1936 | return switch (err) { | ||
| 1937 | posix.EAGAIN => error.ResourceLimitReached, | ||
| 1938 | posix.EINVAL => error.InvalidUserId, | ||
| 1939 | posix.EPERM => error.PermissionDenied, | ||
| 1940 | else => unexpectedErrorPosix(err), | ||
| 1941 | }; | ||
| 1942 | } | ||
| 1943 | |||
| 1944 | pub fn posix_setreuid(ruid: u32, euid: u32) !void { | ||
| 1945 | const err = posix.getErrno(posix.setreuid(ruid, euid)); | ||
| 1946 | if (err == 0) return; | ||
| 1947 | return switch (err) { | ||
| 1948 | posix.EAGAIN => error.ResourceLimitReached, | ||
| 1949 | posix.EINVAL => error.InvalidUserId, | ||
| 1950 | posix.EPERM => error.PermissionDenied, | ||
| 1951 | else => unexpectedErrorPosix(err), | ||
| 1952 | }; | ||
| 1953 | } | ||
| 1954 | |||
| 1955 | pub fn posix_setgid(gid: u32) !void { | ||
| 1956 | const err = posix.getErrno(posix.setgid(gid)); | ||
| 1957 | if (err == 0) return; | ||
| 1958 | return switch (err) { | ||
| 1959 | posix.EAGAIN => error.ResourceLimitReached, | ||
| 1960 | posix.EINVAL => error.InvalidUserId, | ||
| 1961 | posix.EPERM => error.PermissionDenied, | ||
| 1962 | else => unexpectedErrorPosix(err), | ||
| 1963 | }; | ||
| 1964 | } | ||
| 1965 | |||
| 1966 | pub fn posix_setregid(rgid: u32, egid: u32) !void { | ||
| 1967 | const err = posix.getErrno(posix.setregid(rgid, egid)); | ||
| 1968 | if (err == 0) return; | ||
| 1969 | return switch (err) { | ||
| 1970 | posix.EAGAIN => error.ResourceLimitReached, | ||
| 1971 | posix.EINVAL => error.InvalidUserId, | ||
| 1972 | posix.EPERM => error.PermissionDenied, | ||
| 1973 | else => unexpectedErrorPosix(err), | ||
| 1974 | }; | ||
| 1975 | } | ||
| 1976 | |||
| 1977 | pub const WindowsGetStdHandleErrs = error{ | ||
| 1978 | NoStdHandles, | ||
| 1979 | |||
| 1980 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 1981 | Unexpected, | ||
| 1982 | }; | ||
| 1983 | |||
| 1984 | pub fn windowsGetStdHandle(handle_id: windows.DWORD) WindowsGetStdHandleErrs!windows.HANDLE { | ||
| 1985 | if (windows.GetStdHandle(handle_id)) |handle| { | ||
| 1986 | if (handle == windows.INVALID_HANDLE_VALUE) { | ||
| 1987 | const err = windows.GetLastError(); | ||
| 1988 | return switch (err) { | ||
| 1989 | else => os.unexpectedErrorWindows(err), | ||
| 1990 | }; | ||
| 1991 | } | 958 | } |
| 1992 | return handle; | ||
| 1993 | } else { | ||
| 1994 | return error.NoStdHandles; | ||
| 1995 | } | 959 | } |
| 960 | }; | ||
| 961 | |||
| 962 | /// Read value of a symbolic link. | ||
| 963 | /// The return value is a slice of buffer, from index `0`. | ||
| 964 | pub fn readLink(buffer: *[posix.PATH_MAX]u8, pathname: []const u8) ![]u8 { | ||
| 965 | return posix.readlink(pathname, buffer); | ||
| 966 | } | ||
| 967 | |||
| 968 | /// Same as `readLink`, except the `pathname` parameter is null-terminated. | ||
| 969 | pub fn readLinkC(buffer: *[posix.PATH_MAX]u8, pathname: [*]const u8) ![]u8 { | ||
| 970 | return posix.readlinkC(pathname, buffer); | ||
| 1996 | } | 971 | } |
| 1997 | 972 | ||
| 1998 | pub const ArgIteratorPosix = struct { | 973 | pub const ArgIteratorPosix = struct { |
| ... | @@ -2328,34 +1303,6 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []cons | ... | @@ -2328,34 +1303,6 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []cons |
| 2328 | testing.expect(it.next(debug.global_allocator) == null); | 1303 | testing.expect(it.next(debug.global_allocator) == null); |
| 2329 | } | 1304 | } |
| 2330 | 1305 | ||
| 2331 | // TODO make this a build variable that you can set | ||
| 2332 | const unexpected_error_tracing = false; | ||
| 2333 | const UnexpectedError = error{ | ||
| 2334 | /// The Operating System returned an undocumented error code. | ||
| 2335 | Unexpected, | ||
| 2336 | }; | ||
| 2337 | |||
| 2338 | /// Call this when you made a syscall or something that sets errno | ||
| 2339 | /// and you get an unexpected error. | ||
| 2340 | pub fn unexpectedErrorPosix(errno: usize) UnexpectedError { | ||
| 2341 | if (unexpected_error_tracing) { | ||
| 2342 | debug.warn("unexpected errno: {}\n", errno); | ||
| 2343 | debug.dumpCurrentStackTrace(null); | ||
| 2344 | } | ||
| 2345 | return error.Unexpected; | ||
| 2346 | } | ||
| 2347 | |||
| 2348 | /// Call this when you made a windows DLL call or something that does SetLastError | ||
| 2349 | /// and you get an unexpected error. | ||
| 2350 | pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError { | ||
| 2351 | if (unexpected_error_tracing) { | ||
| 2352 | debug.warn("unexpected GetLastError(): {}\n", err); | ||
| 2353 | @breakpoint(); | ||
| 2354 | debug.dumpCurrentStackTrace(null); | ||
| 2355 | } | ||
| 2356 | return error.Unexpected; | ||
| 2357 | } | ||
| 2358 | |||
| 2359 | pub fn openSelfExe() !os.File { | 1306 | pub fn openSelfExe() !os.File { |
| 2360 | switch (builtin.os) { | 1307 | switch (builtin.os) { |
| 2361 | Os.linux => return os.File.openReadC(c"/proc/self/exe"), | 1308 | Os.linux => return os.File.openReadC(c"/proc/self/exe"), |
| ... | @@ -2366,7 +1313,7 @@ pub fn openSelfExe() !os.File { | ... | @@ -2366,7 +1313,7 @@ pub fn openSelfExe() !os.File { |
| 2366 | return os.File.openReadC(self_exe_path.ptr); | 1313 | return os.File.openReadC(self_exe_path.ptr); |
| 2367 | }, | 1314 | }, |
| 2368 | Os.windows => { | 1315 | Os.windows => { |
| 2369 | var buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | 1316 | var buf: [posix.PATH_MAX_WIDE]u16 = undefined; |
| 2370 | const wide_slice = try selfExePathW(&buf); | 1317 | const wide_slice = try selfExePathW(&buf); |
| 2371 | return os.File.openReadW(wide_slice.ptr); | 1318 | return os.File.openReadW(wide_slice.ptr); |
| 2372 | }, | 1319 | }, |
| ... | @@ -2381,7 +1328,7 @@ test "openSelfExe" { | ... | @@ -2381,7 +1328,7 @@ test "openSelfExe" { |
| 2381 | } | 1328 | } |
| 2382 | } | 1329 | } |
| 2383 | 1330 | ||
| 2384 | pub fn selfExePathW(out_buffer: *[windows_util.PATH_MAX_WIDE]u16) ![]u16 { | 1331 | pub fn selfExePathW(out_buffer: *[posix.PATH_MAX_WIDE]u16) ![]u16 { |
| 2385 | const casted_len = @intCast(windows.DWORD, out_buffer.len); // TODO shouldn't need this cast | 1332 | const casted_len = @intCast(windows.DWORD, out_buffer.len); // TODO shouldn't need this cast |
| 2386 | const rc = windows.GetModuleFileNameW(null, out_buffer, casted_len); | 1333 | const rc = windows.GetModuleFileNameW(null, out_buffer, casted_len); |
| 2387 | assert(rc <= out_buffer.len); | 1334 | assert(rc <= out_buffer.len); |
| ... | @@ -2434,7 +1381,7 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { | ... | @@ -2434,7 +1381,7 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { |
| 2434 | }; | 1381 | }; |
| 2435 | }, | 1382 | }, |
| 2436 | Os.windows => { | 1383 | Os.windows => { |
| 2437 | var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | 1384 | var utf16le_buf: [posix.PATH_MAX_WIDE]u16 = undefined; |
| 2438 | const utf16le_slice = try selfExePathW(&utf16le_buf); | 1385 | const utf16le_slice = try selfExePathW(&utf16le_buf); |
| 2439 | // Trust that Windows gives us valid UTF-16LE. | 1386 | // Trust that Windows gives us valid UTF-16LE. |
| 2440 | const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; | 1387 | const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; |
| ... | @@ -2481,521 +1428,6 @@ pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) ![]const u8 { | ... | @@ -2481,521 +1428,6 @@ pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) ![]const u8 { |
| 2481 | } | 1428 | } |
| 2482 | } | 1429 | } |
| 2483 | 1430 | ||
| 2484 | pub fn isTty(handle: FileHandle) bool { | ||
| 2485 | if (is_windows) { | ||
| 2486 | return windows_util.windowsIsTty(handle); | ||
| 2487 | } else { | ||
| 2488 | if (builtin.link_libc) { | ||
| 2489 | return c.isatty(handle) != 0; | ||
| 2490 | } else { | ||
| 2491 | return posix.isatty(handle); | ||
| 2492 | } | ||
| 2493 | } | ||
| 2494 | } | ||
| 2495 | |||
| 2496 | pub fn supportsAnsiEscapeCodes(handle: FileHandle) bool { | ||
| 2497 | if (is_windows) { | ||
| 2498 | return windows_util.windowsIsCygwinPty(handle); | ||
| 2499 | } else { | ||
| 2500 | if (builtin.link_libc) { | ||
| 2501 | return c.isatty(handle) != 0; | ||
| 2502 | } else { | ||
| 2503 | return posix.isatty(handle); | ||
| 2504 | } | ||
| 2505 | } | ||
| 2506 | } | ||
| 2507 | |||
| 2508 | pub const PosixSocketError = error{ | ||
| 2509 | /// Permission to create a socket of the specified type and/or | ||
| 2510 | /// pro‐tocol is denied. | ||
| 2511 | PermissionDenied, | ||
| 2512 | |||
| 2513 | /// The implementation does not support the specified address family. | ||
| 2514 | AddressFamilyNotSupported, | ||
| 2515 | |||
| 2516 | /// Unknown protocol, or protocol family not available. | ||
| 2517 | ProtocolFamilyNotAvailable, | ||
| 2518 | |||
| 2519 | /// The per-process limit on the number of open file descriptors has been reached. | ||
| 2520 | ProcessFdQuotaExceeded, | ||
| 2521 | |||
| 2522 | /// The system-wide limit on the total number of open files has been reached. | ||
| 2523 | SystemFdQuotaExceeded, | ||
| 2524 | |||
| 2525 | /// Insufficient memory is available. The socket cannot be created until sufficient | ||
| 2526 | /// resources are freed. | ||
| 2527 | SystemResources, | ||
| 2528 | |||
| 2529 | /// The protocol type or the specified protocol is not supported within this domain. | ||
| 2530 | ProtocolNotSupported, | ||
| 2531 | }; | ||
| 2532 | |||
| 2533 | pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 { | ||
| 2534 | const rc = posix.socket(domain, socket_type, protocol); | ||
| 2535 | const err = posix.getErrno(rc); | ||
| 2536 | switch (err) { | ||
| 2537 | 0 => return @intCast(i32, rc), | ||
| 2538 | posix.EACCES => return PosixSocketError.PermissionDenied, | ||
| 2539 | posix.EAFNOSUPPORT => return PosixSocketError.AddressFamilyNotSupported, | ||
| 2540 | posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable, | ||
| 2541 | posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded, | ||
| 2542 | posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded, | ||
| 2543 | posix.ENOBUFS, posix.ENOMEM => return PosixSocketError.SystemResources, | ||
| 2544 | posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported, | ||
| 2545 | else => return unexpectedErrorPosix(err), | ||
| 2546 | } | ||
| 2547 | } | ||
| 2548 | |||
| 2549 | pub const PosixBindError = error{ | ||
| 2550 | /// The address is protected, and the user is not the superuser. | ||
| 2551 | /// For UNIX domain sockets: Search permission is denied on a component | ||
| 2552 | /// of the path prefix. | ||
| 2553 | AccessDenied, | ||
| 2554 | |||
| 2555 | /// The given address is already in use, or in the case of Internet domain sockets, | ||
| 2556 | /// The port number was specified as zero in the socket | ||
| 2557 | /// address structure, but, upon attempting to bind to an ephemeral port, it was | ||
| 2558 | /// determined that all port numbers in the ephemeral port range are currently in | ||
| 2559 | /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7). | ||
| 2560 | AddressInUse, | ||
| 2561 | |||
| 2562 | /// A nonexistent interface was requested or the requested address was not local. | ||
| 2563 | AddressNotAvailable, | ||
| 2564 | |||
| 2565 | /// Too many symbolic links were encountered in resolving addr. | ||
| 2566 | SymLinkLoop, | ||
| 2567 | |||
| 2568 | /// addr is too long. | ||
| 2569 | NameTooLong, | ||
| 2570 | |||
| 2571 | /// A component in the directory prefix of the socket pathname does not exist. | ||
| 2572 | FileNotFound, | ||
| 2573 | |||
| 2574 | /// Insufficient kernel memory was available. | ||
| 2575 | SystemResources, | ||
| 2576 | |||
| 2577 | /// A component of the path prefix is not a directory. | ||
| 2578 | NotDir, | ||
| 2579 | |||
| 2580 | /// The socket inode would reside on a read-only filesystem. | ||
| 2581 | ReadOnlyFileSystem, | ||
| 2582 | |||
| 2583 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2584 | Unexpected, | ||
| 2585 | }; | ||
| 2586 | |||
| 2587 | /// addr is `&const T` where T is one of the sockaddr | ||
| 2588 | pub fn posixBind(fd: i32, addr: *const posix.sockaddr) PosixBindError!void { | ||
| 2589 | const rc = posix.bind(fd, addr, @sizeOf(posix.sockaddr)); | ||
| 2590 | const err = posix.getErrno(rc); | ||
| 2591 | switch (err) { | ||
| 2592 | 0 => return, | ||
| 2593 | posix.EACCES => return PosixBindError.AccessDenied, | ||
| 2594 | posix.EADDRINUSE => return PosixBindError.AddressInUse, | ||
| 2595 | posix.EBADF => unreachable, // always a race condition if this error is returned | ||
| 2596 | posix.EINVAL => unreachable, | ||
| 2597 | posix.ENOTSOCK => unreachable, | ||
| 2598 | posix.EADDRNOTAVAIL => return PosixBindError.AddressNotAvailable, | ||
| 2599 | posix.EFAULT => unreachable, | ||
| 2600 | posix.ELOOP => return PosixBindError.SymLinkLoop, | ||
| 2601 | posix.ENAMETOOLONG => return PosixBindError.NameTooLong, | ||
| 2602 | posix.ENOENT => return PosixBindError.FileNotFound, | ||
| 2603 | posix.ENOMEM => return PosixBindError.SystemResources, | ||
| 2604 | posix.ENOTDIR => return PosixBindError.NotDir, | ||
| 2605 | posix.EROFS => return PosixBindError.ReadOnlyFileSystem, | ||
| 2606 | else => return unexpectedErrorPosix(err), | ||
| 2607 | } | ||
| 2608 | } | ||
| 2609 | |||
| 2610 | const PosixListenError = error{ | ||
| 2611 | /// Another socket is already listening on the same port. | ||
| 2612 | /// For Internet domain sockets, the socket referred to by sockfd had not previously | ||
| 2613 | /// been bound to an address and, upon attempting to bind it to an ephemeral port, it | ||
| 2614 | /// was determined that all port numbers in the ephemeral port range are currently in | ||
| 2615 | /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). | ||
| 2616 | AddressInUse, | ||
| 2617 | |||
| 2618 | /// The file descriptor sockfd does not refer to a socket. | ||
| 2619 | FileDescriptorNotASocket, | ||
| 2620 | |||
| 2621 | /// The socket is not of a type that supports the listen() operation. | ||
| 2622 | OperationNotSupported, | ||
| 2623 | |||
| 2624 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2625 | Unexpected, | ||
| 2626 | }; | ||
| 2627 | |||
| 2628 | pub fn posixListen(sockfd: i32, backlog: u32) PosixListenError!void { | ||
| 2629 | const rc = posix.listen(sockfd, backlog); | ||
| 2630 | const err = posix.getErrno(rc); | ||
| 2631 | switch (err) { | ||
| 2632 | 0 => return, | ||
| 2633 | posix.EADDRINUSE => return PosixListenError.AddressInUse, | ||
| 2634 | posix.EBADF => unreachable, | ||
| 2635 | posix.ENOTSOCK => return PosixListenError.FileDescriptorNotASocket, | ||
| 2636 | posix.EOPNOTSUPP => return PosixListenError.OperationNotSupported, | ||
| 2637 | else => return unexpectedErrorPosix(err), | ||
| 2638 | } | ||
| 2639 | } | ||
| 2640 | |||
| 2641 | pub const PosixAcceptError = error{ | ||
| 2642 | ConnectionAborted, | ||
| 2643 | |||
| 2644 | /// The per-process limit on the number of open file descriptors has been reached. | ||
| 2645 | ProcessFdQuotaExceeded, | ||
| 2646 | |||
| 2647 | /// The system-wide limit on the total number of open files has been reached. | ||
| 2648 | SystemFdQuotaExceeded, | ||
| 2649 | |||
| 2650 | /// Not enough free memory. This often means that the memory allocation is limited | ||
| 2651 | /// by the socket buffer limits, not by the system memory. | ||
| 2652 | SystemResources, | ||
| 2653 | |||
| 2654 | /// The file descriptor sockfd does not refer to a socket. | ||
| 2655 | FileDescriptorNotASocket, | ||
| 2656 | |||
| 2657 | /// The referenced socket is not of type SOCK_STREAM. | ||
| 2658 | OperationNotSupported, | ||
| 2659 | |||
| 2660 | ProtocolFailure, | ||
| 2661 | |||
| 2662 | /// Firewall rules forbid connection. | ||
| 2663 | BlockedByFirewall, | ||
| 2664 | |||
| 2665 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2666 | Unexpected, | ||
| 2667 | }; | ||
| 2668 | |||
| 2669 | pub fn posixAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError!i32 { | ||
| 2670 | while (true) { | ||
| 2671 | var sockaddr_size = u32(@sizeOf(posix.sockaddr)); | ||
| 2672 | const rc = posix.accept4(fd, addr, &sockaddr_size, flags); | ||
| 2673 | const err = posix.getErrno(rc); | ||
| 2674 | switch (err) { | ||
| 2675 | 0 => return @intCast(i32, rc), | ||
| 2676 | posix.EINTR => continue, | ||
| 2677 | else => return unexpectedErrorPosix(err), | ||
| 2678 | |||
| 2679 | posix.EAGAIN => unreachable, // use posixAsyncAccept for non-blocking | ||
| 2680 | posix.EBADF => unreachable, // always a race condition | ||
| 2681 | posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted, | ||
| 2682 | posix.EFAULT => unreachable, | ||
| 2683 | posix.EINVAL => unreachable, | ||
| 2684 | posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, | ||
| 2685 | posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, | ||
| 2686 | posix.ENOBUFS => return PosixAcceptError.SystemResources, | ||
| 2687 | posix.ENOMEM => return PosixAcceptError.SystemResources, | ||
| 2688 | posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, | ||
| 2689 | posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, | ||
| 2690 | posix.EPROTO => return PosixAcceptError.ProtocolFailure, | ||
| 2691 | posix.EPERM => return PosixAcceptError.BlockedByFirewall, | ||
| 2692 | } | ||
| 2693 | } | ||
| 2694 | } | ||
| 2695 | |||
| 2696 | /// Returns -1 if would block. | ||
| 2697 | pub fn posixAsyncAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError!i32 { | ||
| 2698 | while (true) { | ||
| 2699 | var sockaddr_size = u32(@sizeOf(posix.sockaddr)); | ||
| 2700 | const rc = posix.accept4(fd, addr, &sockaddr_size, flags); | ||
| 2701 | const err = posix.getErrno(rc); | ||
| 2702 | switch (err) { | ||
| 2703 | 0 => return @intCast(i32, rc), | ||
| 2704 | posix.EINTR => continue, | ||
| 2705 | else => return unexpectedErrorPosix(err), | ||
| 2706 | |||
| 2707 | posix.EAGAIN => return -1, | ||
| 2708 | posix.EBADF => unreachable, // always a race condition | ||
| 2709 | posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted, | ||
| 2710 | posix.EFAULT => unreachable, | ||
| 2711 | posix.EINVAL => unreachable, | ||
| 2712 | posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, | ||
| 2713 | posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, | ||
| 2714 | posix.ENOBUFS => return PosixAcceptError.SystemResources, | ||
| 2715 | posix.ENOMEM => return PosixAcceptError.SystemResources, | ||
| 2716 | posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, | ||
| 2717 | posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, | ||
| 2718 | posix.EPROTO => return PosixAcceptError.ProtocolFailure, | ||
| 2719 | posix.EPERM => return PosixAcceptError.BlockedByFirewall, | ||
| 2720 | } | ||
| 2721 | } | ||
| 2722 | } | ||
| 2723 | |||
| 2724 | pub const LinuxEpollCreateError = error{ | ||
| 2725 | /// The per-user limit on the number of epoll instances imposed by | ||
| 2726 | /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further | ||
| 2727 | /// details. | ||
| 2728 | /// Or, The per-process limit on the number of open file descriptors has been reached. | ||
| 2729 | ProcessFdQuotaExceeded, | ||
| 2730 | |||
| 2731 | /// The system-wide limit on the total number of open files has been reached. | ||
| 2732 | SystemFdQuotaExceeded, | ||
| 2733 | |||
| 2734 | /// There was insufficient memory to create the kernel object. | ||
| 2735 | SystemResources, | ||
| 2736 | |||
| 2737 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2738 | Unexpected, | ||
| 2739 | }; | ||
| 2740 | |||
| 2741 | pub fn linuxEpollCreate(flags: u32) LinuxEpollCreateError!i32 { | ||
| 2742 | const rc = posix.epoll_create1(flags); | ||
| 2743 | const err = posix.getErrno(rc); | ||
| 2744 | switch (err) { | ||
| 2745 | 0 => return @intCast(i32, rc), | ||
| 2746 | else => return unexpectedErrorPosix(err), | ||
| 2747 | |||
| 2748 | posix.EINVAL => unreachable, | ||
| 2749 | posix.EMFILE => return LinuxEpollCreateError.ProcessFdQuotaExceeded, | ||
| 2750 | posix.ENFILE => return LinuxEpollCreateError.SystemFdQuotaExceeded, | ||
| 2751 | posix.ENOMEM => return LinuxEpollCreateError.SystemResources, | ||
| 2752 | } | ||
| 2753 | } | ||
| 2754 | |||
| 2755 | pub const LinuxEpollCtlError = error{ | ||
| 2756 | /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered | ||
| 2757 | /// with this epoll instance. | ||
| 2758 | FileDescriptorAlreadyPresentInSet, | ||
| 2759 | |||
| 2760 | /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a | ||
| 2761 | /// circular loop of epoll instances monitoring one another. | ||
| 2762 | OperationCausesCircularLoop, | ||
| 2763 | |||
| 2764 | /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll | ||
| 2765 | /// instance. | ||
| 2766 | FileDescriptorNotRegistered, | ||
| 2767 | |||
| 2768 | /// There was insufficient memory to handle the requested op control operation. | ||
| 2769 | SystemResources, | ||
| 2770 | |||
| 2771 | /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while | ||
| 2772 | /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance. | ||
| 2773 | /// See epoll(7) for further details. | ||
| 2774 | UserResourceLimitReached, | ||
| 2775 | |||
| 2776 | /// The target file fd does not support epoll. This error can occur if fd refers to, | ||
| 2777 | /// for example, a regular file or a directory. | ||
| 2778 | FileDescriptorIncompatibleWithEpoll, | ||
| 2779 | |||
| 2780 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2781 | Unexpected, | ||
| 2782 | }; | ||
| 2783 | |||
| 2784 | pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: *linux.epoll_event) LinuxEpollCtlError!void { | ||
| 2785 | const rc = posix.epoll_ctl(epfd, op, fd, event); | ||
| 2786 | const err = posix.getErrno(rc); | ||
| 2787 | switch (err) { | ||
| 2788 | 0 => return, | ||
| 2789 | else => return unexpectedErrorPosix(err), | ||
| 2790 | |||
| 2791 | posix.EBADF => unreachable, // always a race condition if this happens | ||
| 2792 | posix.EEXIST => return LinuxEpollCtlError.FileDescriptorAlreadyPresentInSet, | ||
| 2793 | posix.EINVAL => unreachable, | ||
| 2794 | posix.ELOOP => return LinuxEpollCtlError.OperationCausesCircularLoop, | ||
| 2795 | posix.ENOENT => return LinuxEpollCtlError.FileDescriptorNotRegistered, | ||
| 2796 | posix.ENOMEM => return LinuxEpollCtlError.SystemResources, | ||
| 2797 | posix.ENOSPC => return LinuxEpollCtlError.UserResourceLimitReached, | ||
| 2798 | posix.EPERM => return LinuxEpollCtlError.FileDescriptorIncompatibleWithEpoll, | ||
| 2799 | } | ||
| 2800 | } | ||
| 2801 | |||
| 2802 | pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize { | ||
| 2803 | while (true) { | ||
| 2804 | const rc = posix.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout); | ||
| 2805 | const err = posix.getErrno(rc); | ||
| 2806 | switch (err) { | ||
| 2807 | 0 => return rc, | ||
| 2808 | posix.EINTR => continue, | ||
| 2809 | posix.EBADF => unreachable, | ||
| 2810 | posix.EFAULT => unreachable, | ||
| 2811 | posix.EINVAL => unreachable, | ||
| 2812 | else => unreachable, | ||
| 2813 | } | ||
| 2814 | } | ||
| 2815 | } | ||
| 2816 | |||
| 2817 | pub const LinuxEventFdError = error{ | ||
| 2818 | InvalidFlagValue, | ||
| 2819 | SystemResources, | ||
| 2820 | ProcessFdQuotaExceeded, | ||
| 2821 | SystemFdQuotaExceeded, | ||
| 2822 | |||
| 2823 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2824 | Unexpected, | ||
| 2825 | }; | ||
| 2826 | |||
| 2827 | pub fn linuxEventFd(initval: u32, flags: u32) LinuxEventFdError!i32 { | ||
| 2828 | const rc = posix.eventfd(initval, flags); | ||
| 2829 | const err = posix.getErrno(rc); | ||
| 2830 | switch (err) { | ||
| 2831 | 0 => return @intCast(i32, rc), | ||
| 2832 | else => return unexpectedErrorPosix(err), | ||
| 2833 | |||
| 2834 | posix.EINVAL => return LinuxEventFdError.InvalidFlagValue, | ||
| 2835 | posix.EMFILE => return LinuxEventFdError.ProcessFdQuotaExceeded, | ||
| 2836 | posix.ENFILE => return LinuxEventFdError.SystemFdQuotaExceeded, | ||
| 2837 | posix.ENODEV => return LinuxEventFdError.SystemResources, | ||
| 2838 | posix.ENOMEM => return LinuxEventFdError.SystemResources, | ||
| 2839 | } | ||
| 2840 | } | ||
| 2841 | |||
| 2842 | pub const PosixGetSockNameError = error{ | ||
| 2843 | /// Insufficient resources were available in the system to perform the operation. | ||
| 2844 | SystemResources, | ||
| 2845 | |||
| 2846 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2847 | Unexpected, | ||
| 2848 | }; | ||
| 2849 | |||
| 2850 | pub fn posixGetSockName(sockfd: i32) PosixGetSockNameError!posix.sockaddr { | ||
| 2851 | var addr: posix.sockaddr = undefined; | ||
| 2852 | var addrlen: posix.socklen_t = @sizeOf(posix.sockaddr); | ||
| 2853 | const rc = posix.getsockname(sockfd, &addr, &addrlen); | ||
| 2854 | const err = posix.getErrno(rc); | ||
| 2855 | switch (err) { | ||
| 2856 | 0 => return addr, | ||
| 2857 | else => return unexpectedErrorPosix(err), | ||
| 2858 | |||
| 2859 | posix.EBADF => unreachable, | ||
| 2860 | posix.EFAULT => unreachable, | ||
| 2861 | posix.EINVAL => unreachable, | ||
| 2862 | posix.ENOTSOCK => unreachable, | ||
| 2863 | posix.ENOBUFS => return PosixGetSockNameError.SystemResources, | ||
| 2864 | } | ||
| 2865 | } | ||
| 2866 | |||
| 2867 | pub const PosixConnectError = error{ | ||
| 2868 | /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket | ||
| 2869 | /// file, or search permission is denied for one of the directories in the path prefix. | ||
| 2870 | /// or | ||
| 2871 | /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or | ||
| 2872 | /// the connection request failed because of a local firewall rule. | ||
| 2873 | PermissionDenied, | ||
| 2874 | |||
| 2875 | /// Local address is already in use. | ||
| 2876 | AddressInUse, | ||
| 2877 | |||
| 2878 | /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an | ||
| 2879 | /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers | ||
| 2880 | /// in the ephemeral port range are currently in use. See the discussion of | ||
| 2881 | /// /proc/sys/net/ipv4/ip_local_port_range in ip(7). | ||
| 2882 | AddressNotAvailable, | ||
| 2883 | |||
| 2884 | /// The passed address didn't have the correct address family in its sa_family field. | ||
| 2885 | AddressFamilyNotSupported, | ||
| 2886 | |||
| 2887 | /// Insufficient entries in the routing cache. | ||
| 2888 | SystemResources, | ||
| 2889 | |||
| 2890 | /// A connect() on a stream socket found no one listening on the remote address. | ||
| 2891 | ConnectionRefused, | ||
| 2892 | |||
| 2893 | /// Network is unreachable. | ||
| 2894 | NetworkUnreachable, | ||
| 2895 | |||
| 2896 | /// Timeout while attempting connection. The server may be too busy to accept new connections. Note | ||
| 2897 | /// that for IP sockets the timeout may be very long when syncookies are enabled on the server. | ||
| 2898 | ConnectionTimedOut, | ||
| 2899 | |||
| 2900 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2901 | Unexpected, | ||
| 2902 | }; | ||
| 2903 | |||
| 2904 | pub fn posixConnect(sockfd: i32, sockaddr: *const posix.sockaddr) PosixConnectError!void { | ||
| 2905 | while (true) { | ||
| 2906 | const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); | ||
| 2907 | const err = posix.getErrno(rc); | ||
| 2908 | switch (err) { | ||
| 2909 | 0 => return, | ||
| 2910 | else => return unexpectedErrorPosix(err), | ||
| 2911 | |||
| 2912 | posix.EACCES => return PosixConnectError.PermissionDenied, | ||
| 2913 | posix.EPERM => return PosixConnectError.PermissionDenied, | ||
| 2914 | posix.EADDRINUSE => return PosixConnectError.AddressInUse, | ||
| 2915 | posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, | ||
| 2916 | posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, | ||
| 2917 | posix.EAGAIN => return PosixConnectError.SystemResources, | ||
| 2918 | posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | ||
| 2919 | posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. | ||
| 2920 | posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, | ||
| 2921 | posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. | ||
| 2922 | posix.EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately. | ||
| 2923 | posix.EINTR => continue, | ||
| 2924 | posix.EISCONN => unreachable, // The socket is already connected. | ||
| 2925 | posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, | ||
| 2926 | posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 2927 | posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. | ||
| 2928 | posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, | ||
| 2929 | } | ||
| 2930 | } | ||
| 2931 | } | ||
| 2932 | |||
| 2933 | /// Same as posixConnect except it is for blocking socket file descriptors. | ||
| 2934 | /// It expects to receive EINPROGRESS. | ||
| 2935 | pub fn posixConnectAsync(sockfd: i32, sockaddr: *const c_void, len: u32) PosixConnectError!void { | ||
| 2936 | while (true) { | ||
| 2937 | const rc = posix.connect(sockfd, sockaddr, len); | ||
| 2938 | const err = posix.getErrno(rc); | ||
| 2939 | switch (err) { | ||
| 2940 | 0, posix.EINPROGRESS => return, | ||
| 2941 | else => return unexpectedErrorPosix(err), | ||
| 2942 | |||
| 2943 | posix.EACCES => return PosixConnectError.PermissionDenied, | ||
| 2944 | posix.EPERM => return PosixConnectError.PermissionDenied, | ||
| 2945 | posix.EADDRINUSE => return PosixConnectError.AddressInUse, | ||
| 2946 | posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, | ||
| 2947 | posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, | ||
| 2948 | posix.EAGAIN => return PosixConnectError.SystemResources, | ||
| 2949 | posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | ||
| 2950 | posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. | ||
| 2951 | posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, | ||
| 2952 | posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. | ||
| 2953 | posix.EINTR => continue, | ||
| 2954 | posix.EISCONN => unreachable, // The socket is already connected. | ||
| 2955 | posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, | ||
| 2956 | posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 2957 | posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. | ||
| 2958 | posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, | ||
| 2959 | } | ||
| 2960 | } | ||
| 2961 | } | ||
| 2962 | |||
| 2963 | pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { | ||
| 2964 | var err_code: i32 = undefined; | ||
| 2965 | var size: u32 = @sizeOf(i32); | ||
| 2966 | const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast([*]u8, &err_code), &size); | ||
| 2967 | assert(size == 4); | ||
| 2968 | const err = posix.getErrno(rc); | ||
| 2969 | switch (err) { | ||
| 2970 | 0 => switch (err_code) { | ||
| 2971 | 0 => return, | ||
| 2972 | else => return unexpectedErrorPosix(err), | ||
| 2973 | |||
| 2974 | posix.EACCES => return PosixConnectError.PermissionDenied, | ||
| 2975 | posix.EPERM => return PosixConnectError.PermissionDenied, | ||
| 2976 | posix.EADDRINUSE => return PosixConnectError.AddressInUse, | ||
| 2977 | posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, | ||
| 2978 | posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, | ||
| 2979 | posix.EAGAIN => return PosixConnectError.SystemResources, | ||
| 2980 | posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | ||
| 2981 | posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. | ||
| 2982 | posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, | ||
| 2983 | posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. | ||
| 2984 | posix.EISCONN => unreachable, // The socket is already connected. | ||
| 2985 | posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, | ||
| 2986 | posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 2987 | posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. | ||
| 2988 | posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, | ||
| 2989 | }, | ||
| 2990 | else => return unexpectedErrorPosix(err), | ||
| 2991 | posix.EBADF => unreachable, // The argument sockfd is not a valid file descriptor. | ||
| 2992 | posix.EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space. | ||
| 2993 | posix.EINVAL => unreachable, | ||
| 2994 | posix.ENOPROTOOPT => unreachable, // The option is unknown at the level indicated. | ||
| 2995 | posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 2996 | } | ||
| 2997 | } | ||
| 2998 | |||
| 2999 | pub const Thread = struct { | 1431 | pub const Thread = struct { |
| 3000 | data: Data, | 1432 | data: Data, |
| 3001 | 1433 | ||
| ... | @@ -3123,7 +1555,6 @@ pub const SpawnThreadError = error{ | ... | @@ -3123,7 +1555,6 @@ pub const SpawnThreadError = error{ |
| 3123 | /// Not enough userland memory to spawn the thread. | 1555 | /// Not enough userland memory to spawn the thread. |
| 3124 | OutOfMemory, | 1556 | OutOfMemory, |
| 3125 | 1557 | ||
| 3126 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 3127 | Unexpected, | 1558 | Unexpected, |
| 3128 | }; | 1559 | }; |
| 3129 | 1560 | ||
| ... | @@ -3301,47 +1732,16 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread | ... | @@ -3301,47 +1732,16 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread |
| 3301 | } | 1732 | } |
| 3302 | } | 1733 | } |
| 3303 | 1734 | ||
| 3304 | pub fn posixWait(pid: i32) i32 { | ||
| 3305 | var status: i32 = undefined; | ||
| 3306 | while (true) { | ||
| 3307 | const err = posix.getErrno(posix.waitpid(pid, &status, 0)); | ||
| 3308 | switch (err) { | ||
| 3309 | 0 => return status, | ||
| 3310 | posix.EINTR => continue, | ||
| 3311 | posix.ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. | ||
| 3312 | posix.EINVAL => unreachable, // The options argument was invalid | ||
| 3313 | else => unreachable, | ||
| 3314 | } | ||
| 3315 | } | ||
| 3316 | } | ||
| 3317 | |||
| 3318 | pub fn posixFStat(fd: i32) !posix.Stat { | ||
| 3319 | var stat: posix.Stat = undefined; | ||
| 3320 | const err = posix.getErrno(posix.fstat(fd, &stat)); | ||
| 3321 | if (err > 0) { | ||
| 3322 | return switch (err) { | ||
| 3323 | // We do not make this an error code because if you get EBADF it's always a bug, | ||
| 3324 | // since the fd could have been reused. | ||
| 3325 | posix.EBADF => unreachable, | ||
| 3326 | posix.ENOMEM => error.SystemResources, | ||
| 3327 | else => os.unexpectedErrorPosix(err), | ||
| 3328 | }; | ||
| 3329 | } | ||
| 3330 | |||
| 3331 | return stat; | ||
| 3332 | } | ||
| 3333 | |||
| 3334 | pub const CpuCountError = error{ | 1735 | pub const CpuCountError = error{ |
| 3335 | OutOfMemory, | 1736 | OutOfMemory, |
| 3336 | PermissionDenied, | 1737 | PermissionDenied, |
| 3337 | 1738 | ||
| 3338 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 3339 | Unexpected, | 1739 | Unexpected, |
| 3340 | }; | 1740 | }; |
| 3341 | 1741 | ||
| 3342 | pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { | 1742 | pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { |
| 3343 | switch (builtin.os) { | 1743 | switch (builtin.os) { |
| 3344 | builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { | 1744 | .macosx, .freebsd, .netbsd => { |
| 3345 | var count: c_int = undefined; | 1745 | var count: c_int = undefined; |
| 3346 | var count_len: usize = @sizeOf(c_int); | 1746 | var count_len: usize = @sizeOf(c_int); |
| 3347 | const rc = posix.sysctlbyname(switch (builtin.os) { | 1747 | const rc = posix.sysctlbyname(switch (builtin.os) { |
| ... | @@ -3361,7 +1761,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { | ... | @@ -3361,7 +1761,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { |
| 3361 | else => return os.unexpectedErrorPosix(err), | 1761 | else => return os.unexpectedErrorPosix(err), |
| 3362 | } | 1762 | } |
| 3363 | }, | 1763 | }, |
| 3364 | builtin.Os.linux => { | 1764 | .linux => { |
| 3365 | const usize_count = 16; | 1765 | const usize_count = 16; |
| 3366 | const allocator = std.heap.stackFallback(usize_count * @sizeOf(usize), fallback_allocator).get(); | 1766 | const allocator = std.heap.stackFallback(usize_count * @sizeOf(usize), fallback_allocator).get(); |
| 3367 | 1767 | ||
| ... | @@ -3393,7 +1793,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { | ... | @@ -3393,7 +1793,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { |
| 3393 | } | 1793 | } |
| 3394 | } | 1794 | } |
| 3395 | }, | 1795 | }, |
| 3396 | builtin.Os.windows => { | 1796 | .windows => { |
| 3397 | var system_info: windows.SYSTEM_INFO = undefined; | 1797 | var system_info: windows.SYSTEM_INFO = undefined; |
| 3398 | windows.GetSystemInfo(&system_info); | 1798 | windows.GetSystemInfo(&system_info); |
| 3399 | return @intCast(usize, system_info.dwNumberOfProcessors); | 1799 | return @intCast(usize, system_info.dwNumberOfProcessors); |
| ... | @@ -3401,128 +1801,3 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { | ... | @@ -3401,128 +1801,3 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { |
| 3401 | else => @compileError("unsupported OS"), | 1801 | else => @compileError("unsupported OS"), |
| 3402 | } | 1802 | } |
| 3403 | } | 1803 | } |
| 3404 | |||
| 3405 | pub const BsdKQueueError = error{ | ||
| 3406 | /// The per-process limit on the number of open file descriptors has been reached. | ||
| 3407 | ProcessFdQuotaExceeded, | ||
| 3408 | |||
| 3409 | /// The system-wide limit on the total number of open files has been reached. | ||
| 3410 | SystemFdQuotaExceeded, | ||
| 3411 | |||
| 3412 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 3413 | Unexpected, | ||
| 3414 | }; | ||
| 3415 | |||
| 3416 | pub fn bsdKQueue() BsdKQueueError!i32 { | ||
| 3417 | const rc = posix.kqueue(); | ||
| 3418 | const err = posix.getErrno(rc); | ||
| 3419 | switch (err) { | ||
| 3420 | 0 => return @intCast(i32, rc), | ||
| 3421 | posix.EMFILE => return BsdKQueueError.ProcessFdQuotaExceeded, | ||
| 3422 | posix.ENFILE => return BsdKQueueError.SystemFdQuotaExceeded, | ||
| 3423 | else => return unexpectedErrorPosix(err), | ||
| 3424 | } | ||
| 3425 | } | ||
| 3426 | |||
| 3427 | pub const BsdKEventError = error{ | ||
| 3428 | /// The process does not have permission to register a filter. | ||
| 3429 | AccessDenied, | ||
| 3430 | |||
| 3431 | /// The event could not be found to be modified or deleted. | ||
| 3432 | EventNotFound, | ||
| 3433 | |||
| 3434 | /// No memory was available to register the event. | ||
| 3435 | SystemResources, | ||
| 3436 | |||
| 3437 | /// The specified process to attach to does not exist. | ||
| 3438 | ProcessNotFound, | ||
| 3439 | }; | ||
| 3440 | |||
| 3441 | pub fn bsdKEvent( | ||
| 3442 | kq: i32, | ||
| 3443 | changelist: []const posix.Kevent, | ||
| 3444 | eventlist: []posix.Kevent, | ||
| 3445 | timeout: ?*const posix.timespec, | ||
| 3446 | ) BsdKEventError!usize { | ||
| 3447 | while (true) { | ||
| 3448 | const rc = posix.kevent(kq, changelist, eventlist, timeout); | ||
| 3449 | const err = posix.getErrno(rc); | ||
| 3450 | switch (err) { | ||
| 3451 | 0 => return rc, | ||
| 3452 | posix.EACCES => return BsdKEventError.AccessDenied, | ||
| 3453 | posix.EFAULT => unreachable, | ||
| 3454 | posix.EBADF => unreachable, | ||
| 3455 | posix.EINTR => continue, | ||
| 3456 | posix.EINVAL => unreachable, | ||
| 3457 | posix.ENOENT => return BsdKEventError.EventNotFound, | ||
| 3458 | posix.ENOMEM => return BsdKEventError.SystemResources, | ||
| 3459 | posix.ESRCH => return BsdKEventError.ProcessNotFound, | ||
| 3460 | else => unreachable, | ||
| 3461 | } | ||
| 3462 | } | ||
| 3463 | } | ||
| 3464 | |||
| 3465 | pub fn linuxINotifyInit1(flags: u32) !i32 { | ||
| 3466 | const rc = linux.inotify_init1(flags); | ||
| 3467 | const err = posix.getErrno(rc); | ||
| 3468 | switch (err) { | ||
| 3469 | 0 => return @intCast(i32, rc), | ||
| 3470 | posix.EINVAL => unreachable, | ||
| 3471 | posix.EMFILE => return error.ProcessFdQuotaExceeded, | ||
| 3472 | posix.ENFILE => return error.SystemFdQuotaExceeded, | ||
| 3473 | posix.ENOMEM => return error.SystemResources, | ||
| 3474 | else => return unexpectedErrorPosix(err), | ||
| 3475 | } | ||
| 3476 | } | ||
| 3477 | |||
| 3478 | pub fn linuxINotifyAddWatchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) !i32 { | ||
| 3479 | const rc = linux.inotify_add_watch(inotify_fd, pathname, mask); | ||
| 3480 | const err = posix.getErrno(rc); | ||
| 3481 | switch (err) { | ||
| 3482 | 0 => return @intCast(i32, rc), | ||
| 3483 | posix.EACCES => return error.AccessDenied, | ||
| 3484 | posix.EBADF => unreachable, | ||
| 3485 | posix.EFAULT => unreachable, | ||
| 3486 | posix.EINVAL => unreachable, | ||
| 3487 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 3488 | posix.ENOENT => return error.FileNotFound, | ||
| 3489 | posix.ENOMEM => return error.SystemResources, | ||
| 3490 | posix.ENOSPC => return error.UserResourceLimitReached, | ||
| 3491 | else => return unexpectedErrorPosix(err), | ||
| 3492 | } | ||
| 3493 | } | ||
| 3494 | |||
| 3495 | pub fn linuxINotifyRmWatch(inotify_fd: i32, wd: i32) !void { | ||
| 3496 | const rc = linux.inotify_rm_watch(inotify_fd, wd); | ||
| 3497 | const err = posix.getErrno(rc); | ||
| 3498 | switch (err) { | ||
| 3499 | 0 => return rc, | ||
| 3500 | posix.EBADF => unreachable, | ||
| 3501 | posix.EINVAL => unreachable, | ||
| 3502 | else => unreachable, | ||
| 3503 | } | ||
| 3504 | } | ||
| 3505 | |||
| 3506 | pub const MProtectError = error{ | ||
| 3507 | AccessDenied, | ||
| 3508 | OutOfMemory, | ||
| 3509 | Unexpected, | ||
| 3510 | }; | ||
| 3511 | |||
| 3512 | /// address and length must be page-aligned | ||
| 3513 | pub fn posixMProtect(address: usize, length: usize, protection: u32) MProtectError!void { | ||
| 3514 | const negative_page_size = @bitCast(usize, -isize(page_size)); | ||
| 3515 | const aligned_address = address & negative_page_size; | ||
| 3516 | const aligned_end = (address + length + page_size - 1) & negative_page_size; | ||
| 3517 | assert(address == aligned_address); | ||
| 3518 | assert(length == aligned_end - aligned_address); | ||
| 3519 | const rc = posix.mprotect(address, length, protection); | ||
| 3520 | const err = posix.getErrno(rc); | ||
| 3521 | switch (err) { | ||
| 3522 | 0 => return, | ||
| 3523 | posix.EINVAL => unreachable, | ||
| 3524 | posix.EACCES => return error.AccessDenied, | ||
| 3525 | posix.ENOMEM => return error.OutOfMemory, | ||
| 3526 | else => return unexpectedErrorPosix(err), | ||
| 3527 | } | ||
| 3528 | } |
std/os/child_process.zig+1-1| ... | @@ -415,7 +415,7 @@ pub const ChildProcess = struct { | ... | @@ -415,7 +415,7 @@ pub const ChildProcess = struct { |
| 415 | os.posix_setreuid(uid, uid) catch |err| forkChildErrReport(err_pipe[1], err); | 415 | os.posix_setreuid(uid, uid) catch |err| forkChildErrReport(err_pipe[1], err); |
| 416 | } | 416 | } |
| 417 | 417 | ||
| 418 | os.posixExecve(self.argv, env_map, self.allocator) catch |err| forkChildErrReport(err_pipe[1], err); | 418 | os.posix.execve(self.allocator, self.argv, env_map) catch |err| forkChildErrReport(err_pipe[1], err); |
| 419 | } | 419 | } |
| 420 | 420 | ||
| 421 | // we are the parent | 421 | // we are the parent |
std/os/darwin.zig+8-1| ... | @@ -1,9 +1,16 @@ | ... | @@ -1,9 +1,16 @@ |
| 1 | const builtin = @import("builtin"); | ||
| 1 | const std = @import("../std.zig"); | 2 | const std = @import("../std.zig"); |
| 2 | const c = std.c; | 3 | const c = std.c; |
| 3 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 4 | const maxInt = std.math.maxInt; | 5 | const maxInt = std.math.maxInt; |
| 5 | 6 | ||
| 6 | pub use @import("darwin/errno.zig"); | 7 | pub const is_the_target = switch (builtin.os) { |
| 8 | .ios, .macosx, .watchos, .tvos => true, | ||
| 9 | else => false, | ||
| 10 | }; | ||
| 11 | |||
| 12 | pub const errno_codes = @import("darwin/errno.zig"); | ||
| 13 | pub use errno_codes; | ||
| 7 | 14 | ||
| 8 | pub const PATH_MAX = 1024; | 15 | pub const PATH_MAX = 1024; |
| 9 | 16 |
std/os/file.zig+30-33| ... | @@ -223,9 +223,18 @@ pub const File = struct { | ... | @@ -223,9 +223,18 @@ pub const File = struct { |
| 223 | os.close(self.handle); | 223 | os.close(self.handle); |
| 224 | } | 224 | } |
| 225 | 225 | ||
| 226 | /// Calls `os.isTty` on `self.handle`. | 226 | /// Test whether the file refers to a terminal. |
| 227 | /// See also `supportsAnsiEscapeCodes`. | ||
| 227 | pub fn isTty(self: File) bool { | 228 | pub fn isTty(self: File) bool { |
| 228 | return os.isTty(self.handle); | 229 | return posix.isatty(self.handle); |
| 230 | } | ||
| 231 | |||
| 232 | /// Test whether ANSI escape codes will be treated as such. | ||
| 233 | pub fn supportsAnsiEscapeCodes(self: File) bool { | ||
| 234 | if (windows.is_the_target) { | ||
| 235 | return posix.isCygwinPty(self.handle); | ||
| 236 | } | ||
| 237 | return self.isTty(); | ||
| 229 | } | 238 | } |
| 230 | 239 | ||
| 231 | pub const SeekError = error{ | 240 | pub const SeekError = error{ |
| ... | @@ -389,43 +398,16 @@ pub const File = struct { | ... | @@ -389,43 +398,16 @@ pub const File = struct { |
| 389 | } | 398 | } |
| 390 | } | 399 | } |
| 391 | 400 | ||
| 392 | pub const ReadError = os.WindowsReadError || os.PosixReadError; | 401 | pub const ReadError = posix.ReadError; |
| 393 | 402 | ||
| 394 | pub fn read(self: File, buffer: []u8) ReadError!usize { | 403 | pub fn read(self: File, buffer: []u8) ReadError!usize { |
| 395 | if (is_posix) { | 404 | return posix.read(self.handle, buffer); |
| 396 | return os.posixRead(self.handle, buffer); | ||
| 397 | } else if (is_windows) { | ||
| 398 | var index: usize = 0; | ||
| 399 | while (index < buffer.len) { | ||
| 400 | const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(maxInt(windows.DWORD)), buffer.len - index)); | ||
| 401 | var amt_read: windows.DWORD = undefined; | ||
| 402 | if (windows.ReadFile(self.handle, buffer.ptr + index, want_read_count, &amt_read, null) == 0) { | ||
| 403 | const err = windows.GetLastError(); | ||
| 404 | return switch (err) { | ||
| 405 | windows.ERROR.OPERATION_ABORTED => continue, | ||
| 406 | windows.ERROR.BROKEN_PIPE => return index, | ||
| 407 | else => os.unexpectedErrorWindows(err), | ||
| 408 | }; | ||
| 409 | } | ||
| 410 | if (amt_read == 0) return index; | ||
| 411 | index += amt_read; | ||
| 412 | } | ||
| 413 | return index; | ||
| 414 | } else { | ||
| 415 | @compileError("Unsupported OS"); | ||
| 416 | } | ||
| 417 | } | 405 | } |
| 418 | 406 | ||
| 419 | pub const WriteError = os.WindowsWriteError || os.PosixWriteError; | 407 | pub const WriteError = posix.WriteError; |
| 420 | 408 | ||
| 421 | pub fn write(self: File, bytes: []const u8) WriteError!void { | 409 | pub fn write(self: File, bytes: []const u8) WriteError!void { |
| 422 | if (is_posix) { | 410 | return posix.write(self.handle, bytes); |
| 423 | try os.posixWrite(self.handle, bytes); | ||
| 424 | } else if (is_windows) { | ||
| 425 | try os.windowsWrite(self.handle, bytes); | ||
| 426 | } else { | ||
| 427 | @compileError("Unsupported OS"); | ||
| 428 | } | ||
| 429 | } | 411 | } |
| 430 | 412 | ||
| 431 | pub fn inStream(file: File) InStream { | 413 | pub fn inStream(file: File) InStream { |
| ... | @@ -509,4 +491,19 @@ pub const File = struct { | ... | @@ -509,4 +491,19 @@ pub const File = struct { |
| 509 | return self.file.getPos(); | 491 | return self.file.getPos(); |
| 510 | } | 492 | } |
| 511 | }; | 493 | }; |
| 494 | |||
| 495 | pub fn stdout() !File { | ||
| 496 | const handle = try posix.GetStdHandle(posix.STD_OUTPUT_HANDLE); | ||
| 497 | return openHandle(handle); | ||
| 498 | } | ||
| 499 | |||
| 500 | pub fn stderr() !File { | ||
| 501 | const handle = try posix.GetStdHandle(posix.STD_ERROR_HANDLE); | ||
| 502 | return openHandle(handle); | ||
| 503 | } | ||
| 504 | |||
| 505 | pub fn stdin() !File { | ||
| 506 | const handle = try posix.GetStdHandle(posix.STD_INPUT_HANDLE); | ||
| 507 | return openHandle(handle); | ||
| 508 | } | ||
| 512 | }; | 509 | }; |
std/os/linux.zig+9-18| ... | @@ -12,7 +12,12 @@ pub use switch (builtin.arch) { | ... | @@ -12,7 +12,12 @@ pub use switch (builtin.arch) { |
| 12 | builtin.Arch.aarch64 => @import("linux/arm64.zig"), | 12 | builtin.Arch.aarch64 => @import("linux/arm64.zig"), |
| 13 | else => @compileError("unsupported arch"), | 13 | else => @compileError("unsupported arch"), |
| 14 | }; | 14 | }; |
| 15 | pub use @import("linux/errno.zig"); | 15 | pub const is_the_target = builtin.os == .linux; |
| 16 | pub const errno_codes = @import("linux/errno.zig"); | ||
| 17 | pub use errno_codes; | ||
| 18 | |||
| 19 | /// See `std.os.posix.getauxval`. | ||
| 20 | pub var elf_aux_maybe: ?[*]std.elf.Auxv = null; | ||
| 16 | 21 | ||
| 17 | pub const PATH_MAX = 4096; | 22 | pub const PATH_MAX = 4096; |
| 18 | pub const IOV_MAX = 1024; | 23 | pub const IOV_MAX = 1024; |
| ... | @@ -697,9 +702,9 @@ pub const winsize = extern struct { | ... | @@ -697,9 +702,9 @@ pub const winsize = extern struct { |
| 697 | }; | 702 | }; |
| 698 | 703 | ||
| 699 | /// Get the errno from a syscall return value, or 0 for no error. | 704 | /// Get the errno from a syscall return value, or 0 for no error. |
| 700 | pub fn getErrno(r: usize) usize { | 705 | pub fn getErrno(r: usize) u12 { |
| 701 | const signed_r = @bitCast(isize, r); | 706 | const signed_r = @bitCast(isize, r); |
| 702 | return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; | 707 | return if (signed_r > -4096 and signed_r < 0) @intCast(u12, -signed_r) else 0; |
| 703 | } | 708 | } |
| 704 | 709 | ||
| 705 | pub fn dup2(old: i32, new: i32) usize { | 710 | pub fn dup2(old: i32, new: i32) usize { |
| ... | @@ -766,11 +771,6 @@ pub fn inotify_rm_watch(fd: i32, wd: i32) usize { | ... | @@ -766,11 +771,6 @@ pub fn inotify_rm_watch(fd: i32, wd: i32) usize { |
| 766 | return syscall2(SYS_inotify_rm_watch, @bitCast(usize, isize(fd)), @bitCast(usize, isize(wd))); | 771 | return syscall2(SYS_inotify_rm_watch, @bitCast(usize, isize(fd)), @bitCast(usize, isize(wd))); |
| 767 | } | 772 | } |
| 768 | 773 | ||
| 769 | pub fn isatty(fd: i32) bool { | ||
| 770 | var wsz: winsize = undefined; | ||
| 771 | return syscall3(SYS_ioctl, @bitCast(usize, isize(fd)), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; | ||
| 772 | } | ||
| 773 | |||
| 774 | // TODO https://github.com/ziglang/zig/issues/265 | 774 | // TODO https://github.com/ziglang/zig/issues/265 |
| 775 | pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { | 775 | pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { |
| 776 | return readlinkat(AT_FDCWD, path, buf_ptr, buf_len); | 776 | return readlinkat(AT_FDCWD, path, buf_ptr, buf_len); |
| ... | @@ -1137,15 +1137,6 @@ pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); | ... | @@ -1137,15 +1137,6 @@ pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); |
| 1137 | pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); | 1137 | pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); |
| 1138 | pub const empty_sigset = []usize{0} ** sigset_t.len; | 1138 | pub const empty_sigset = []usize{0} ** sigset_t.len; |
| 1139 | 1139 | ||
| 1140 | pub fn raise(sig: i32) usize { | ||
| 1141 | var set: sigset_t = undefined; | ||
| 1142 | blockAppSignals(&set); | ||
| 1143 | const tid = syscall0(SYS_gettid); | ||
| 1144 | const ret = syscall2(SYS_tkill, tid, @bitCast(usize, isize(sig))); | ||
| 1145 | restoreSignals(&set); | ||
| 1146 | return ret; | ||
| 1147 | } | ||
| 1148 | |||
| 1149 | fn blockAllSignals(set: *sigset_t) void { | 1140 | fn blockAllSignals(set: *sigset_t) void { |
| 1150 | _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8); | 1141 | _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8); |
| 1151 | } | 1142 | } |
| ... | @@ -1672,7 +1663,7 @@ pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_inf | ... | @@ -1672,7 +1663,7 @@ pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_inf |
| 1672 | } | 1663 | } |
| 1673 | 1664 | ||
| 1674 | test "import" { | 1665 | test "import" { |
| 1675 | if (builtin.os == builtin.Os.linux) { | 1666 | if (is_the_target) { |
| 1676 | _ = @import("linux/test.zig"); | 1667 | _ = @import("linux/test.zig"); |
| 1677 | } | 1668 | } |
| 1678 | } | 1669 | } |
std/os/linux/tls.zig+1-1| ... | @@ -126,7 +126,7 @@ pub fn initTLS() void { | ... | @@ -126,7 +126,7 @@ pub fn initTLS() void { |
| 126 | var tls_phdr: ?*elf.Phdr = null; | 126 | var tls_phdr: ?*elf.Phdr = null; |
| 127 | var img_base: usize = 0; | 127 | var img_base: usize = 0; |
| 128 | 128 | ||
| 129 | const auxv = std.os.linux_elf_aux_maybe.?; | 129 | const auxv = std.os.linux.elf_aux_maybe.?; |
| 130 | var at_phent: usize = undefined; | 130 | var at_phent: usize = undefined; |
| 131 | var at_phnum: usize = undefined; | 131 | var at_phnum: usize = undefined; |
| 132 | var at_phdr: usize = undefined; | 132 | var at_phdr: usize = undefined; |
std/os/posix.zig created+2159| ... | @@ -0,0 +1,2159 @@ | ||
| 1 | // This is the "Zig-flavored POSIX" API layer. | ||
| 2 | // The purpose is not to match POSIX as closely as possible. Instead, | ||
| 3 | // the goal is to provide a very specific layer of abstraction: | ||
| 4 | // * Implement the POSIX functions, types, and definitions where possible, | ||
| 5 | // using lower-level target-specific API. For example, on Linux `rename` might call | ||
| 6 | // SYS_renameat or SYS_rename depending on the architecture. | ||
| 7 | // * When null-terminated byte buffers are required, provide APIs which accept | ||
| 8 | // slices as well as APIs which accept null-terminated byte buffers. Same goes | ||
| 9 | // for UTF-16LE encoding. | ||
| 10 | // * Convert "errno"-style error codes into Zig errors. | ||
| 11 | // * Work around kernel bugs and limitations. For example, if a function accepts | ||
| 12 | // a `usize` number of bytes to write, but the kernel can only handle maxInt(u32) | ||
| 13 | // number of bytes, this API layer should introduce a loop to make multiple | ||
| 14 | // syscalls so that the full `usize` number of bytes are written. | ||
| 15 | // * Implement the OS-specific functions, types, and definitions that the Zig | ||
| 16 | // standard library needs, at the same API abstraction layer as outlined above. | ||
| 17 | // this includes, for example Windows functions. | ||
| 18 | // * When there exists a corresponding libc function and linking libc, call the | ||
| 19 | // libc function. | ||
| 20 | // Note: The Zig standard library does not support POSIX thread cancellation, and | ||
| 21 | // in general EINTR is handled by trying again. | ||
| 22 | |||
| 23 | const std = @import("../std.zig"); | ||
| 24 | const builtin = @import("builtin"); | ||
| 25 | const assert = std.debug.assert; | ||
| 26 | const os = @import("../os.zig"); | ||
| 27 | const system = os.system; | ||
| 28 | const mem = std.mem; | ||
| 29 | const BufMap = std.BufMap; | ||
| 30 | const Allocator = mem.Allocator; | ||
| 31 | const windows = os.windows; | ||
| 32 | const wasi = os.wasi; | ||
| 33 | const linux = os.linux; | ||
| 34 | const testing = std.testing; | ||
| 35 | |||
| 36 | pub const FileHandle = if (windows.is_the_target) windows.HANDLE else if (wasi.is_the_target) wasi.fd_t else i32; | ||
| 37 | pub use system.errno_codes; | ||
| 38 | |||
| 39 | pub const PATH_MAX = system.PATH_MAX; | ||
| 40 | |||
| 41 | /// > The maximum path of 32,767 characters is approximate, because the "\\?\" | ||
| 42 | /// > prefix may be expanded to a longer string by the system at run time, and | ||
| 43 | /// > this expansion applies to the total length. | ||
| 44 | /// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation | ||
| 45 | pub const PATH_MAX_WIDE = 32767; | ||
| 46 | |||
| 47 | pub const iovec = system.iovec; | ||
| 48 | pub const iovec_const = system.iovec_const; | ||
| 49 | |||
| 50 | /// See also `getenv`. | ||
| 51 | pub var environ: [][*]u8 = undefined; | ||
| 52 | |||
| 53 | /// To obtain errno, call this function with the return value of the | ||
| 54 | /// system function call. For some systems this will obtain the value directly | ||
| 55 | /// from the return code; for others it will use a thread-local errno variable. | ||
| 56 | /// Therefore, this function only returns a well-defined value when it is called | ||
| 57 | /// directly after the system function call which one wants to learn the errno | ||
| 58 | /// value of. | ||
| 59 | pub const errno = system.getErrno; | ||
| 60 | |||
| 61 | /// Closes the file handle. | ||
| 62 | /// This function is not capable of returning any indication of failure. An | ||
| 63 | /// application which wants to ensure writes have succeeded before closing | ||
| 64 | /// must call `fsync` before `close`. | ||
| 65 | /// Note: The Zig standard library does not support POSIX thread cancellation. | ||
| 66 | pub fn close(handle: FileHandle) void { | ||
| 67 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 68 | assert(windows.CloseHandle(handle) != 0); | ||
| 69 | return; | ||
| 70 | } | ||
| 71 | if (wasi.is_the_target) { | ||
| 72 | switch (wasi.fd_close(handle)) { | ||
| 73 | 0 => return, | ||
| 74 | else => |err| return unexpectedErrno(err), | ||
| 75 | } | ||
| 76 | } | ||
| 77 | switch (system.getErrno(system.close(handle))) { | ||
| 78 | EBADF => unreachable, // Always a race condition. | ||
| 79 | EINTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425 | ||
| 80 | else => return, | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | pub const GetRandomError = error{}; | ||
| 85 | |||
| 86 | /// Obtain a series of random bytes. These bytes can be used to seed user-space | ||
| 87 | /// random number generators or for cryptographic purposes. | ||
| 88 | /// When linking against libc, this calls the | ||
| 89 | /// appropriate OS-specific library call. Otherwise it uses the zig standard | ||
| 90 | /// library implementation. | ||
| 91 | pub fn getrandom(buf: []u8) GetRandomError!void { | ||
| 92 | if (windows.is_the_target) { | ||
| 93 | // Call RtlGenRandom() instead of CryptGetRandom() on Windows | ||
| 94 | // https://github.com/rust-lang-nursery/rand/issues/111 | ||
| 95 | // https://bugzilla.mozilla.org/show_bug.cgi?id=504270 | ||
| 96 | if (windows.RtlGenRandom(buf.ptr, buf.len) == 0) { | ||
| 97 | const err = windows.GetLastError(); | ||
| 98 | return switch (err) { | ||
| 99 | else => unexpectedErrorWindows(err), | ||
| 100 | }; | ||
| 101 | } | ||
| 102 | return; | ||
| 103 | } | ||
| 104 | if (linux.is_the_target) { | ||
| 105 | while (true) { | ||
| 106 | switch (system.getErrno(system.getrandom(buf.ptr, buf.len, 0))) { | ||
| 107 | 0 => return, | ||
| 108 | EINVAL => unreachable, | ||
| 109 | EFAULT => unreachable, | ||
| 110 | EINTR => continue, | ||
| 111 | ENOSYS => return getRandomBytesDevURandom(buf), | ||
| 112 | else => |err| return unexpectedErrno(err), | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | if (wasi.is_the_target) { | ||
| 117 | switch (os.wasi.random_get(buf.ptr, buf.len)) { | ||
| 118 | 0 => return, | ||
| 119 | else => |err| return unexpectedErrno(err), | ||
| 120 | } | ||
| 121 | } | ||
| 122 | return getRandomBytesDevURandom(buf); | ||
| 123 | } | ||
| 124 | |||
| 125 | fn getRandomBytesDevURandom(buf: []u8) !void { | ||
| 126 | const fd = try openC(c"/dev/urandom", O_RDONLY | O_CLOEXEC, 0); | ||
| 127 | defer close(fd); | ||
| 128 | |||
| 129 | const stream = &os.File.openHandle(fd).inStream().stream; | ||
| 130 | stream.readNoEof(buf) catch return error.Unexpected; | ||
| 131 | } | ||
| 132 | |||
| 133 | test "os.getRandomBytes" { | ||
| 134 | var buf_a: [50]u8 = undefined; | ||
| 135 | var buf_b: [50]u8 = undefined; | ||
| 136 | try getRandomBytes(&buf_a); | ||
| 137 | try getRandomBytes(&buf_b); | ||
| 138 | // If this test fails the chance is significantly higher that there is a bug than | ||
| 139 | // that two sets of 50 bytes were equal. | ||
| 140 | testing.expect(!mem.eql(u8, buf_a, buf_b)); | ||
| 141 | } | ||
| 142 | |||
| 143 | /// Causes abnormal process termination. | ||
| 144 | /// If linking against libc, this calls the abort() libc function. Otherwise | ||
| 145 | /// it raises SIGABRT followed by SIGKILL and finally lo | ||
| 146 | pub fn abort() noreturn { | ||
| 147 | @setCold(true); | ||
| 148 | if (builtin.link_libc) { | ||
| 149 | c.abort(); | ||
| 150 | } | ||
| 151 | if (windows.is_the_target) { | ||
| 152 | if (builtin.mode == .Debug) { | ||
| 153 | @breakpoint(); | ||
| 154 | } | ||
| 155 | windows.ExitProcess(3); | ||
| 156 | } | ||
| 157 | if (builtin.os == .uefi) { | ||
| 158 | // TODO there must be a better thing to do here than loop forever | ||
| 159 | while (true) {} | ||
| 160 | } | ||
| 161 | |||
| 162 | raise(SIGABRT); | ||
| 163 | |||
| 164 | // TODO the rest of the implementation of abort() from musl libc here | ||
| 165 | |||
| 166 | raise(SIGKILL); | ||
| 167 | exit(127); | ||
| 168 | } | ||
| 169 | |||
| 170 | pub const RaiseError = error{}; | ||
| 171 | |||
| 172 | pub fn raise(sig: u8) RaiseError!void { | ||
| 173 | if (builtin.link_libc) { | ||
| 174 | switch (system.getErrno(system.raise(sig))) { | ||
| 175 | 0 => return, | ||
| 176 | else => |err| return unexpectedErrno(err), | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | if (wasi.is_the_target) { | ||
| 181 | switch (wasi.proc_raise(SIGABRT)) { | ||
| 182 | 0 => return, | ||
| 183 | else => |err| return unexpectedErrno(err), | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | if (windows.is_the_target) { | ||
| 188 | @compileError("TODO implement std.posix.raise for Windows"); | ||
| 189 | } | ||
| 190 | |||
| 191 | var set: system.sigset_t = undefined; | ||
| 192 | system.blockAppSignals(&set); | ||
| 193 | const tid = system.syscall0(system.SYS_gettid); | ||
| 194 | const rc = system.syscall2(system.SYS_tkill, tid, sig); | ||
| 195 | system.restoreSignals(&set); | ||
| 196 | switch (system.getErrno(rc)) { | ||
| 197 | 0 => return, | ||
| 198 | else => |err| return unexpectedErrno(err), | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | /// Exits the program cleanly with the specified status code. | ||
| 203 | pub fn exit(status: u8) noreturn { | ||
| 204 | if (builtin.link_libc) { | ||
| 205 | std.c.exit(status); | ||
| 206 | } | ||
| 207 | if (windows.is_the_target) { | ||
| 208 | windows.ExitProcess(status); | ||
| 209 | } | ||
| 210 | if (wasi.is_the_target) { | ||
| 211 | wasi.proc_exit(status); | ||
| 212 | } | ||
| 213 | if (linux.is_the_target and !builtin.single_threaded) { | ||
| 214 | linux.exit_group(status); | ||
| 215 | } | ||
| 216 | system.exit(status); | ||
| 217 | } | ||
| 218 | |||
| 219 | pub const ReadError = error{ | ||
| 220 | InputOutput, | ||
| 221 | SystemResources, | ||
| 222 | IsDir, | ||
| 223 | OperationAborted, | ||
| 224 | BrokenPipe, | ||
| 225 | Unexpected, | ||
| 226 | }; | ||
| 227 | |||
| 228 | /// Returns the number of bytes that were read, which can be less than | ||
| 229 | /// buf.len. If 0 bytes were read, that means EOF. | ||
| 230 | /// This function is for blocking file descriptors only. For non-blocking, see | ||
| 231 | /// `readAsync`. | ||
| 232 | pub fn read(fd: FileHandle, buf: []u8) ReadError!usize { | ||
| 233 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 234 | var index: usize = 0; | ||
| 235 | while (index < buffer.len) { | ||
| 236 | const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(math.maxInt(windows.DWORD)), buffer.len - index)); | ||
| 237 | var amt_read: windows.DWORD = undefined; | ||
| 238 | if (windows.ReadFile(fd, buffer.ptr + index, want_read_count, &amt_read, null) == 0) { | ||
| 239 | const err = windows.GetLastError(); | ||
| 240 | return switch (err) { | ||
| 241 | windows.ERROR.OPERATION_ABORTED => continue, | ||
| 242 | windows.ERROR.BROKEN_PIPE => return index, | ||
| 243 | else => unexpectedErrorWindows(err), | ||
| 244 | }; | ||
| 245 | } | ||
| 246 | if (amt_read == 0) return index; | ||
| 247 | index += amt_read; | ||
| 248 | } | ||
| 249 | return index; | ||
| 250 | } | ||
| 251 | |||
| 252 | if (wasi.is_the_target and !builtin.link_libc) { | ||
| 253 | const iovs = [1]was.iovec_t{wasi.iovec_t{ | ||
| 254 | .buf = buf.ptr, | ||
| 255 | .buf_len = buf.len, | ||
| 256 | }}; | ||
| 257 | |||
| 258 | var nread: usize = undefined; | ||
| 259 | switch (fd_read(fd, &iovs, iovs.len, &nread)) { | ||
| 260 | 0 => return nread, | ||
| 261 | else => |err| return unexpectedErrno(err), | ||
| 262 | } | ||
| 263 | } | ||
| 264 | |||
| 265 | // Linux can return EINVAL when read amount is > 0x7ffff000 | ||
| 266 | // See https://github.com/ziglang/zig/pull/743#issuecomment-363158274 | ||
| 267 | const max_buf_len = 0x7ffff000; | ||
| 268 | |||
| 269 | var index: usize = 0; | ||
| 270 | while (index < buf.len) { | ||
| 271 | const want_to_read = math.min(buf.len - index, usize(max_buf_len)); | ||
| 272 | const rc = system.read(fd, buf.ptr + index, want_to_read); | ||
| 273 | switch (system.getErrno(rc)) { | ||
| 274 | 0 => { | ||
| 275 | index += rc; | ||
| 276 | if (rc == want_to_read) continue; | ||
| 277 | // Read returned less than buf.len. | ||
| 278 | return index; | ||
| 279 | }, | ||
| 280 | EINTR => continue, | ||
| 281 | EINVAL => unreachable, | ||
| 282 | EFAULT => unreachable, | ||
| 283 | EAGAIN => unreachable, // This function is for blocking reads. | ||
| 284 | EBADF => unreachable, // Always a race condition. | ||
| 285 | EIO => return error.InputOutput, | ||
| 286 | EISDIR => return error.IsDir, | ||
| 287 | ENOBUFS => return error.SystemResources, | ||
| 288 | ENOMEM => return error.SystemResources, | ||
| 289 | else => |err| return unexpectedErrno(err), | ||
| 290 | } | ||
| 291 | } | ||
| 292 | return index; | ||
| 293 | } | ||
| 294 | |||
| 295 | /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. | ||
| 296 | /// This function is for blocking file descriptors only. For non-blocking, see | ||
| 297 | /// `preadvAsync`. | ||
| 298 | pub fn preadv(fd: FileHandle, iov: [*]const iovec, count: usize, offset: u64) ReadError!usize { | ||
| 299 | if (os.darwin.is_the_target) { | ||
| 300 | // Darwin does not have preadv but it does have pread. | ||
| 301 | var off: usize = 0; | ||
| 302 | var iov_i: usize = 0; | ||
| 303 | var inner_off: usize = 0; | ||
| 304 | while (true) { | ||
| 305 | const v = iov[iov_i]; | ||
| 306 | const rc = darwin.pread(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); | ||
| 307 | const err = darwin.getErrno(rc); | ||
| 308 | switch (err) { | ||
| 309 | 0 => { | ||
| 310 | off += rc; | ||
| 311 | inner_off += rc; | ||
| 312 | if (inner_off == v.iov_len) { | ||
| 313 | iov_i += 1; | ||
| 314 | inner_off = 0; | ||
| 315 | if (iov_i == count) { | ||
| 316 | return off; | ||
| 317 | } | ||
| 318 | } | ||
| 319 | if (rc == 0) return off; // EOF | ||
| 320 | continue; | ||
| 321 | }, | ||
| 322 | EINTR => continue, | ||
| 323 | EINVAL => unreachable, | ||
| 324 | EFAULT => unreachable, | ||
| 325 | ESPIPE => unreachable, // fd is not seekable | ||
| 326 | EAGAIN => unreachable, // This function is for blocking reads. | ||
| 327 | EBADF => unreachable, // always a race condition | ||
| 328 | EIO => return error.InputOutput, | ||
| 329 | EISDIR => return error.IsDir, | ||
| 330 | ENOBUFS => return error.SystemResources, | ||
| 331 | ENOMEM => return error.SystemResources, | ||
| 332 | else => return unexpectedErrno(err), | ||
| 333 | } | ||
| 334 | } | ||
| 335 | } | ||
| 336 | while (true) { | ||
| 337 | const rc = system.preadv(fd, iov, count, offset); | ||
| 338 | const err = system.getErrno(rc); | ||
| 339 | switch (err) { | ||
| 340 | 0 => return rc, | ||
| 341 | EINTR => continue, | ||
| 342 | EINVAL => unreachable, | ||
| 343 | EFAULT => unreachable, | ||
| 344 | EAGAIN => unreachable, // This function is for blocking reads. | ||
| 345 | EBADF => unreachable, // always a race condition | ||
| 346 | EIO => return error.InputOutput, | ||
| 347 | EISDIR => return error.IsDir, | ||
| 348 | ENOBUFS => return error.SystemResources, | ||
| 349 | ENOMEM => return error.SystemResources, | ||
| 350 | else => return unexpectedErrno(err), | ||
| 351 | } | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | pub const WriteError = error{ | ||
| 356 | DiskQuota, | ||
| 357 | FileTooBig, | ||
| 358 | InputOutput, | ||
| 359 | NoSpaceLeft, | ||
| 360 | AccessDenied, | ||
| 361 | BrokenPipe, | ||
| 362 | SystemResources, | ||
| 363 | OperationAborted, | ||
| 364 | Unexpected, | ||
| 365 | }; | ||
| 366 | |||
| 367 | /// Write to a file descriptor. Keeps trying if it gets interrupted. | ||
| 368 | /// This function is for blocking file descriptors only. For non-blocking, see | ||
| 369 | /// `writeAsync`. | ||
| 370 | pub fn write(fd: FileHandle, bytes: []const u8) WriteError!void { | ||
| 371 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 372 | var bytes_written: windows.DWORD = undefined; | ||
| 373 | // TODO replace this @intCast with a loop that writes all the bytes | ||
| 374 | if (windows.WriteFile(handle, bytes.ptr, @intCast(u32, bytes.len), &bytes_written, null) == 0) { | ||
| 375 | switch (windows.GetLastError()) { | ||
| 376 | windows.ERROR.INVALID_USER_BUFFER => return error.SystemResources, | ||
| 377 | windows.ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources, | ||
| 378 | windows.ERROR.OPERATION_ABORTED => return error.OperationAborted, | ||
| 379 | windows.ERROR.NOT_ENOUGH_QUOTA => return error.SystemResources, | ||
| 380 | windows.ERROR.IO_PENDING => unreachable, | ||
| 381 | windows.ERROR.BROKEN_PIPE => return error.BrokenPipe, | ||
| 382 | else => |err| return unexpectedErrorWindows(err), | ||
| 383 | } | ||
| 384 | } | ||
| 385 | } | ||
| 386 | |||
| 387 | if (wasi.is_the_target and !builtin.link_libc) { | ||
| 388 | const ciovs = [1]wasi.ciovec_t{wasi.ciovec_t{ | ||
| 389 | .buf = bytes.ptr, | ||
| 390 | .buf_len = bytes.len, | ||
| 391 | }}; | ||
| 392 | var nwritten: usize = undefined; | ||
| 393 | switch (fd_write(fd, &ciovs, ciovs.len, &nwritten)) { | ||
| 394 | 0 => return, | ||
| 395 | else => |err| return unexpectedErrno(err), | ||
| 396 | } | ||
| 397 | } | ||
| 398 | |||
| 399 | // Linux can return EINVAL when write amount is > 0x7ffff000 | ||
| 400 | // See https://github.com/ziglang/zig/pull/743#issuecomment-363165856 | ||
| 401 | const max_bytes_len = 0x7ffff000; | ||
| 402 | |||
| 403 | var index: usize = 0; | ||
| 404 | while (index < bytes.len) { | ||
| 405 | const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len)); | ||
| 406 | const rc = system.write(fd, bytes.ptr + index, amt_to_write); | ||
| 407 | const write_err = system.getErrno(rc); | ||
| 408 | switch (write_err) { | ||
| 409 | 0 => { | ||
| 410 | index += rc; | ||
| 411 | continue; | ||
| 412 | }, | ||
| 413 | EINTR => continue, | ||
| 414 | EINVAL => unreachable, | ||
| 415 | EFAULT => unreachable, | ||
| 416 | EAGAIN => unreachable, // This function is for blocking writes. | ||
| 417 | EBADF => unreachable, // Always a race condition. | ||
| 418 | EDESTADDRREQ => unreachable, // `connect` was never called. | ||
| 419 | EDQUOT => return error.DiskQuota, | ||
| 420 | EFBIG => return error.FileTooBig, | ||
| 421 | EIO => return error.InputOutput, | ||
| 422 | ENOSPC => return error.NoSpaceLeft, | ||
| 423 | EPERM => return error.AccessDenied, | ||
| 424 | EPIPE => return error.BrokenPipe, | ||
| 425 | else => return unexpectedErrno(write_err), | ||
| 426 | } | ||
| 427 | } | ||
| 428 | } | ||
| 429 | |||
| 430 | /// Write multiple buffers to a file descriptor. Keeps trying if it gets interrupted. | ||
| 431 | /// This function is for blocking file descriptors only. For non-blocking, see | ||
| 432 | /// `pwritevAsync`. | ||
| 433 | pub fn pwritev(fd: FileHandle, iov: [*]const iovec_const, count: usize, offset: u64) WriteError!void { | ||
| 434 | if (darwin.is_the_target) { | ||
| 435 | // Darwin does not have pwritev but it does have pwrite. | ||
| 436 | var off: usize = 0; | ||
| 437 | var iov_i: usize = 0; | ||
| 438 | var inner_off: usize = 0; | ||
| 439 | while (true) { | ||
| 440 | const v = iov[iov_i]; | ||
| 441 | const rc = darwin.pwrite(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); | ||
| 442 | const err = darwin.getErrno(rc); | ||
| 443 | switch (err) { | ||
| 444 | 0 => { | ||
| 445 | off += rc; | ||
| 446 | inner_off += rc; | ||
| 447 | if (inner_off == v.iov_len) { | ||
| 448 | iov_i += 1; | ||
| 449 | inner_off = 0; | ||
| 450 | if (iov_i == count) { | ||
| 451 | return; | ||
| 452 | } | ||
| 453 | } | ||
| 454 | continue; | ||
| 455 | }, | ||
| 456 | EINTR => continue, | ||
| 457 | ESPIPE => unreachable, // `fd` is not seekable. | ||
| 458 | EINVAL => unreachable, | ||
| 459 | EFAULT => unreachable, | ||
| 460 | EAGAIN => unreachable, // This function is for blocking writes. | ||
| 461 | EBADF => unreachable, // Always a race condition. | ||
| 462 | EDESTADDRREQ => unreachable, // `connect` was never called. | ||
| 463 | EDQUOT => return error.DiskQuota, | ||
| 464 | EFBIG => return error.FileTooBig, | ||
| 465 | EIO => return error.InputOutput, | ||
| 466 | ENOSPC => return error.NoSpaceLeft, | ||
| 467 | EPERM => return error.AccessDenied, | ||
| 468 | EPIPE => return error.BrokenPipe, | ||
| 469 | else => return unexpectedErrno(err), | ||
| 470 | } | ||
| 471 | } | ||
| 472 | } | ||
| 473 | |||
| 474 | while (true) { | ||
| 475 | const rc = system.pwritev(fd, iov, count, offset); | ||
| 476 | const err = system.getErrno(rc); | ||
| 477 | switch (err) { | ||
| 478 | 0 => return, | ||
| 479 | EINTR => continue, | ||
| 480 | EINVAL => unreachable, | ||
| 481 | EFAULT => unreachable, | ||
| 482 | EAGAIN => unreachable, // This function is for blocking writes. | ||
| 483 | EBADF => unreachable, // Always a race condition. | ||
| 484 | EDESTADDRREQ => unreachable, // `connect` was never called. | ||
| 485 | EDQUOT => return error.DiskQuota, | ||
| 486 | EFBIG => return error.FileTooBig, | ||
| 487 | EIO => return error.InputOutput, | ||
| 488 | ENOSPC => return error.NoSpaceLeft, | ||
| 489 | EPERM => return error.AccessDenied, | ||
| 490 | EPIPE => return error.BrokenPipe, | ||
| 491 | else => return unexpectedErrno(err), | ||
| 492 | } | ||
| 493 | } | ||
| 494 | } | ||
| 495 | |||
| 496 | pub const OpenError = error{ | ||
| 497 | AccessDenied, | ||
| 498 | FileTooBig, | ||
| 499 | IsDir, | ||
| 500 | SymLinkLoop, | ||
| 501 | ProcessFdQuotaExceeded, | ||
| 502 | NameTooLong, | ||
| 503 | SystemFdQuotaExceeded, | ||
| 504 | NoDevice, | ||
| 505 | FileNotFound, | ||
| 506 | SystemResources, | ||
| 507 | NoSpaceLeft, | ||
| 508 | NotDir, | ||
| 509 | PathAlreadyExists, | ||
| 510 | DeviceBusy, | ||
| 511 | Unexpected, | ||
| 512 | }; | ||
| 513 | |||
| 514 | /// Open and possibly create a file. Keeps trying if it gets interrupted. | ||
| 515 | /// `file_path` needs to be copied in memory to add a null terminating byte. | ||
| 516 | /// See also `openC`. | ||
| 517 | pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!FileHandle { | ||
| 518 | const file_path_c = try toPosixPath(file_path); | ||
| 519 | return openC(&file_path_c, flags, perm); | ||
| 520 | } | ||
| 521 | |||
| 522 | /// Open and possibly create a file. Keeps trying if it gets interrupted. | ||
| 523 | /// See also `open`. | ||
| 524 | /// TODO https://github.com/ziglang/zig/issues/265 | ||
| 525 | pub fn openC(file_path: [*]const u8, flags: u32, perm: usize) OpenError!FileHandle { | ||
| 526 | while (true) { | ||
| 527 | const rc = system.open(file_path, flags, perm); | ||
| 528 | switch (system.getErrno(rc)) { | ||
| 529 | 0 => return @intCast(FileHandle, rc), | ||
| 530 | EINTR => continue, | ||
| 531 | |||
| 532 | EFAULT => unreachable, | ||
| 533 | EINVAL => unreachable, | ||
| 534 | EACCES => return error.AccessDenied, | ||
| 535 | EFBIG => return error.FileTooBig, | ||
| 536 | EOVERFLOW => return error.FileTooBig, | ||
| 537 | EISDIR => return error.IsDir, | ||
| 538 | ELOOP => return error.SymLinkLoop, | ||
| 539 | EMFILE => return error.ProcessFdQuotaExceeded, | ||
| 540 | ENAMETOOLONG => return error.NameTooLong, | ||
| 541 | ENFILE => return error.SystemFdQuotaExceeded, | ||
| 542 | ENODEV => return error.NoDevice, | ||
| 543 | ENOENT => return error.FileNotFound, | ||
| 544 | ENOMEM => return error.SystemResources, | ||
| 545 | ENOSPC => return error.NoSpaceLeft, | ||
| 546 | ENOTDIR => return error.NotDir, | ||
| 547 | EPERM => return error.AccessDenied, | ||
| 548 | EEXIST => return error.PathAlreadyExists, | ||
| 549 | EBUSY => return error.DeviceBusy, | ||
| 550 | else => |err| return unexpectedErrno(err), | ||
| 551 | } | ||
| 552 | } | ||
| 553 | } | ||
| 554 | |||
| 555 | pub const WindowsOpenError = error{ | ||
| 556 | SharingViolation, | ||
| 557 | PathAlreadyExists, | ||
| 558 | |||
| 559 | /// When any of the path components can not be found or the file component can not | ||
| 560 | /// be found. Some operating systems distinguish between path components not found and | ||
| 561 | /// file components not found, but they are collapsed into FileNotFound to gain | ||
| 562 | /// consistency across operating systems. | ||
| 563 | FileNotFound, | ||
| 564 | |||
| 565 | AccessDenied, | ||
| 566 | PipeBusy, | ||
| 567 | NameTooLong, | ||
| 568 | |||
| 569 | /// On Windows, file paths must be valid Unicode. | ||
| 570 | InvalidUtf8, | ||
| 571 | |||
| 572 | /// On Windows, file paths cannot contain these characters: | ||
| 573 | /// '/', '*', '?', '"', '<', '>', '|' | ||
| 574 | BadPathName, | ||
| 575 | |||
| 576 | Unexpected, | ||
| 577 | }; | ||
| 578 | |||
| 579 | pub fn openWindows( | ||
| 580 | file_path: []const u8, | ||
| 581 | desired_access: windows.DWORD, | ||
| 582 | share_mode: windows.DWORD, | ||
| 583 | creation_disposition: windows.DWORD, | ||
| 584 | flags_and_attrs: windows.DWORD, | ||
| 585 | ) WindowsOpenError!FileHandle { | ||
| 586 | const file_path_w = try sliceToPrefixedFileW(file_path); | ||
| 587 | return openW(&file_path_w, desired_access, share_mode, creation_disposition, flags_and_attrs); | ||
| 588 | } | ||
| 589 | |||
| 590 | pub fn openW( | ||
| 591 | file_path_w: [*]const u16, | ||
| 592 | desired_access: windows.DWORD, | ||
| 593 | share_mode: windows.DWORD, | ||
| 594 | creation_disposition: windows.DWORD, | ||
| 595 | flags_and_attrs: windows.DWORD, | ||
| 596 | ) WindowsOpenError!windows.HANDLE { | ||
| 597 | const result = windows.CreateFileW(file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null); | ||
| 598 | |||
| 599 | if (result == windows.INVALID_HANDLE_VALUE) { | ||
| 600 | const err = windows.GetLastError(); | ||
| 601 | switch (err) { | ||
| 602 | windows.ERROR.SHARING_VIOLATION => return error.SharingViolation, | ||
| 603 | windows.ERROR.ALREADY_EXISTS => return error.PathAlreadyExists, | ||
| 604 | windows.ERROR.FILE_EXISTS => return error.PathAlreadyExists, | ||
| 605 | windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, | ||
| 606 | windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, | ||
| 607 | windows.ERROR.ACCESS_DENIED => return error.AccessDenied, | ||
| 608 | windows.ERROR.PIPE_BUSY => return error.PipeBusy, | ||
| 609 | else => return unexpectedErrorWindows(err), | ||
| 610 | } | ||
| 611 | } | ||
| 612 | |||
| 613 | return result; | ||
| 614 | } | ||
| 615 | |||
| 616 | pub fn dup2(old_fd: FileHandle, new_fd: FileHandle) !void { | ||
| 617 | while (true) { | ||
| 618 | switch (system.getErrno(system.dup2(old_fd, new_fd))) { | ||
| 619 | 0 => return, | ||
| 620 | EBUSY, EINTR => continue, | ||
| 621 | EMFILE => return error.ProcessFdQuotaExceeded, | ||
| 622 | EINVAL => unreachable, | ||
| 623 | else => |err| return unexpectedErrno(err), | ||
| 624 | } | ||
| 625 | } | ||
| 626 | } | ||
| 627 | |||
| 628 | /// This function must allocate memory to add a null terminating bytes on path and each arg. | ||
| 629 | /// It must also convert to KEY=VALUE\0 format for environment variables, and include null | ||
| 630 | /// pointers after the args and after the environment variables. | ||
| 631 | /// `argv[0]` is the executable path. | ||
| 632 | /// This function also uses the PATH environment variable to get the full path to the executable. | ||
| 633 | pub fn execve(allocator: *Allocator, argv: []const []const u8, env_map: *const BufMap) !void { | ||
| 634 | const argv_buf = try allocator.alloc(?[*]u8, argv.len + 1); | ||
| 635 | mem.set(?[*]u8, argv_buf, null); | ||
| 636 | defer { | ||
| 637 | for (argv_buf) |arg| { | ||
| 638 | const arg_buf = if (arg) |ptr| cstr.toSlice(ptr) else break; | ||
| 639 | allocator.free(arg_buf); | ||
| 640 | } | ||
| 641 | allocator.free(argv_buf); | ||
| 642 | } | ||
| 643 | for (argv) |arg, i| { | ||
| 644 | const arg_buf = try allocator.alloc(u8, arg.len + 1); | ||
| 645 | @memcpy(arg_buf.ptr, arg.ptr, arg.len); | ||
| 646 | arg_buf[arg.len] = 0; | ||
| 647 | |||
| 648 | argv_buf[i] = arg_buf.ptr; | ||
| 649 | } | ||
| 650 | argv_buf[argv.len] = null; | ||
| 651 | |||
| 652 | const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); | ||
| 653 | defer freeNullDelimitedEnvMap(allocator, envp_buf); | ||
| 654 | |||
| 655 | const exe_path = argv[0]; | ||
| 656 | if (mem.indexOfScalar(u8, exe_path, '/') != null) { | ||
| 657 | return execveErrnoToErr(system.getErrno(system.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr))); | ||
| 658 | } | ||
| 659 | |||
| 660 | const PATH = getenv("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; | ||
| 661 | // PATH.len because it is >= the largest search_path | ||
| 662 | // +1 for the / to join the search path and exe_path | ||
| 663 | // +1 for the null terminating byte | ||
| 664 | const path_buf = try allocator.alloc(u8, PATH.len + exe_path.len + 2); | ||
| 665 | defer allocator.free(path_buf); | ||
| 666 | var it = mem.tokenize(PATH, ":"); | ||
| 667 | var seen_eacces = false; | ||
| 668 | var err: usize = undefined; | ||
| 669 | while (it.next()) |search_path| { | ||
| 670 | mem.copy(u8, path_buf, search_path); | ||
| 671 | path_buf[search_path.len] = '/'; | ||
| 672 | mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path); | ||
| 673 | path_buf[search_path.len + exe_path.len + 1] = 0; | ||
| 674 | err = system.getErrno(system.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)); | ||
| 675 | assert(err > 0); | ||
| 676 | if (err == EACCES) { | ||
| 677 | seen_eacces = true; | ||
| 678 | } else if (err != ENOENT) { | ||
| 679 | return execveErrnoToErr(err); | ||
| 680 | } | ||
| 681 | } | ||
| 682 | if (seen_eacces) { | ||
| 683 | err = EACCES; | ||
| 684 | } | ||
| 685 | return execveErrnoToErr(err); | ||
| 686 | } | ||
| 687 | |||
| 688 | pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) ![]?[*]u8 { | ||
| 689 | const envp_count = env_map.count(); | ||
| 690 | const envp_buf = try allocator.alloc(?[*]u8, envp_count + 1); | ||
| 691 | mem.set(?[*]u8, envp_buf, null); | ||
| 692 | errdefer freeNullDelimitedEnvMap(allocator, envp_buf); | ||
| 693 | { | ||
| 694 | var it = env_map.iterator(); | ||
| 695 | var i: usize = 0; | ||
| 696 | while (it.next()) |pair| : (i += 1) { | ||
| 697 | const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2); | ||
| 698 | @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len); | ||
| 699 | env_buf[pair.key.len] = '='; | ||
| 700 | @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); | ||
| 701 | env_buf[env_buf.len - 1] = 0; | ||
| 702 | |||
| 703 | envp_buf[i] = env_buf.ptr; | ||
| 704 | } | ||
| 705 | assert(i == envp_count); | ||
| 706 | } | ||
| 707 | assert(envp_buf[envp_count] == null); | ||
| 708 | return envp_buf; | ||
| 709 | } | ||
| 710 | |||
| 711 | pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?[*]u8) void { | ||
| 712 | for (envp_buf) |env| { | ||
| 713 | const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break; | ||
| 714 | allocator.free(env_buf); | ||
| 715 | } | ||
| 716 | allocator.free(envp_buf); | ||
| 717 | } | ||
| 718 | |||
| 719 | pub const ExecveError = error{ | ||
| 720 | SystemResources, | ||
| 721 | AccessDenied, | ||
| 722 | InvalidExe, | ||
| 723 | FileSystem, | ||
| 724 | IsDir, | ||
| 725 | FileNotFound, | ||
| 726 | NotDir, | ||
| 727 | FileBusy, | ||
| 728 | |||
| 729 | Unexpected, | ||
| 730 | }; | ||
| 731 | |||
| 732 | fn execveErrnoToErr(err: usize) ExecveError { | ||
| 733 | assert(err > 0); | ||
| 734 | switch (err) { | ||
| 735 | EFAULT => unreachable, | ||
| 736 | E2BIG => return error.SystemResources, | ||
| 737 | EMFILE => return error.ProcessFdQuotaExceeded, | ||
| 738 | ENAMETOOLONG => return error.NameTooLong, | ||
| 739 | ENFILE => return error.SystemFdQuotaExceeded, | ||
| 740 | ENOMEM => return error.SystemResources, | ||
| 741 | EACCES => return error.AccessDenied, | ||
| 742 | EPERM => return error.AccessDenied, | ||
| 743 | EINVAL => return error.InvalidExe, | ||
| 744 | ENOEXEC => return error.InvalidExe, | ||
| 745 | EIO => return error.FileSystem, | ||
| 746 | ELOOP => return error.FileSystem, | ||
| 747 | EISDIR => return error.IsDir, | ||
| 748 | ENOENT => return error.FileNotFound, | ||
| 749 | ENOTDIR => return error.NotDir, | ||
| 750 | ETXTBSY => return error.FileBusy, | ||
| 751 | else => return unexpectedErrno(err), | ||
| 752 | } | ||
| 753 | } | ||
| 754 | |||
| 755 | /// Get an environment variable. | ||
| 756 | /// See also `getenvC`. | ||
| 757 | /// TODO make this go through libc when we have it | ||
| 758 | pub fn getenv(key: []const u8) ?[]const u8 { | ||
| 759 | for (environ) |ptr| { | ||
| 760 | var line_i: usize = 0; | ||
| 761 | while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} | ||
| 762 | const this_key = ptr[0..line_i]; | ||
| 763 | if (!mem.eql(u8, key, this_key)) continue; | ||
| 764 | |||
| 765 | var end_i: usize = line_i; | ||
| 766 | while (ptr[end_i] != 0) : (end_i += 1) {} | ||
| 767 | const this_value = ptr[line_i + 1 .. end_i]; | ||
| 768 | |||
| 769 | return this_value; | ||
| 770 | } | ||
| 771 | return null; | ||
| 772 | } | ||
| 773 | |||
| 774 | /// Get an environment variable with a null-terminated name. | ||
| 775 | /// See also `getenv`. | ||
| 776 | /// TODO https://github.com/ziglang/zig/issues/265 | ||
| 777 | pub fn getenvC(key: [*]const u8) ?[]const u8 { | ||
| 778 | if (builtin.link_libc) { | ||
| 779 | const value = std.c.getenv(key) orelse return null; | ||
| 780 | return mem.toSliceConst(u8, value); | ||
| 781 | } | ||
| 782 | return getenv(mem.toSliceConst(u8, key)); | ||
| 783 | } | ||
| 784 | |||
| 785 | /// See std.elf for the constants. | ||
| 786 | pub fn getauxval(index: usize) usize { | ||
| 787 | if (builtin.link_libc) { | ||
| 788 | return usize(std.c.getauxval(index)); | ||
| 789 | } else if (linux.elf_aux_maybe) |auxv| { | ||
| 790 | var i: usize = 0; | ||
| 791 | while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) { | ||
| 792 | if (auxv[i].a_type == index) | ||
| 793 | return auxv[i].a_un.a_val; | ||
| 794 | } | ||
| 795 | } | ||
| 796 | return 0; | ||
| 797 | } | ||
| 798 | |||
| 799 | pub const GetCwdError = error{ | ||
| 800 | NameTooLong, | ||
| 801 | CurrentWorkingDirectoryUnlinked, | ||
| 802 | Unexpected, | ||
| 803 | }; | ||
| 804 | |||
| 805 | /// The result is a slice of out_buffer, indexed from 0. | ||
| 806 | pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { | ||
| 807 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 808 | var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | ||
| 809 | const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast | ||
| 810 | const casted_ptr = ([*]u16)(&utf16le_buf); // TODO shouldn't need this cast | ||
| 811 | const result = windows.GetCurrentDirectoryW(casted_len, casted_ptr); | ||
| 812 | if (result == 0) { | ||
| 813 | const err = windows.GetLastError(); | ||
| 814 | switch (err) { | ||
| 815 | else => return unexpectedErrorWindows(err), | ||
| 816 | } | ||
| 817 | } | ||
| 818 | assert(result <= utf16le_buf.len); | ||
| 819 | const utf16le_slice = utf16le_buf[0..result]; | ||
| 820 | // Trust that Windows gives us valid UTF-16LE. | ||
| 821 | var end_index: usize = 0; | ||
| 822 | var it = std.unicode.Utf16LeIterator.init(utf16le); | ||
| 823 | while (it.nextCodepoint() catch unreachable) |codepoint| { | ||
| 824 | if (end_index + std.unicode.utf8CodepointSequenceLength(codepoint) >= out_buffer.len) | ||
| 825 | return error.NameTooLong; | ||
| 826 | end_index += utf8Encode(codepoint, out_buffer[end_index..]) catch unreachable; | ||
| 827 | } | ||
| 828 | return out_buffer[0..end_index]; | ||
| 829 | } | ||
| 830 | |||
| 831 | const err = if (builtin.link_libc) blk: { | ||
| 832 | break :blk if (std.c.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else std.c._errno().*; | ||
| 833 | } else blk: { | ||
| 834 | break :blk system.getErrno(system.getcwd(out_buffer, out_buffer.len)); | ||
| 835 | }; | ||
| 836 | switch (err) { | ||
| 837 | 0 => return mem.toSlice(u8, out_buffer), | ||
| 838 | EFAULT => unreachable, | ||
| 839 | EINVAL => unreachable, | ||
| 840 | ENOENT => return error.CurrentWorkingDirectoryUnlinked, | ||
| 841 | ERANGE => return error.NameTooLong, | ||
| 842 | else => |err| return unexpectedErrno(err), | ||
| 843 | } | ||
| 844 | } | ||
| 845 | |||
| 846 | test "getcwd" { | ||
| 847 | // at least call it so it gets compiled | ||
| 848 | var buf: [os.MAX_PATH_BYTES]u8 = undefined; | ||
| 849 | _ = getcwd(&buf) catch {}; | ||
| 850 | } | ||
| 851 | |||
| 852 | pub const SymLinkError = error{ | ||
| 853 | AccessDenied, | ||
| 854 | DiskQuota, | ||
| 855 | PathAlreadyExists, | ||
| 856 | FileSystem, | ||
| 857 | SymLinkLoop, | ||
| 858 | FileNotFound, | ||
| 859 | SystemResources, | ||
| 860 | NoSpaceLeft, | ||
| 861 | ReadOnlyFileSystem, | ||
| 862 | NotDir, | ||
| 863 | NameTooLong, | ||
| 864 | InvalidUtf8, | ||
| 865 | BadPathName, | ||
| 866 | Unexpected, | ||
| 867 | }; | ||
| 868 | |||
| 869 | /// Creates a symbolic link named `new_path` which contains the string `target_path`. | ||
| 870 | /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent | ||
| 871 | /// one; the latter case is known as a dangling link. | ||
| 872 | /// If `new_path` exists, it will not be overwritten. | ||
| 873 | /// See also `symlinkC` and `symlinkW`. | ||
| 874 | pub fn symlink(target_path: []const u8, new_path: []const u8) SymLinkError!void { | ||
| 875 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 876 | const target_path_w = try cStrToPrefixedFileW(target_path); | ||
| 877 | const new_path_w = try cStrToPrefixedFileW(new_path); | ||
| 878 | return symlinkW(&target_path_w, &new_path_w); | ||
| 879 | } else { | ||
| 880 | const target_path_c = try toPosixPath(target_path); | ||
| 881 | const new_path_c = try toPosixPath(new_path); | ||
| 882 | return symlinkC(&target_path_c, &new_path_c); | ||
| 883 | } | ||
| 884 | } | ||
| 885 | |||
| 886 | pub fn symlinkat(target_path: []const u8, newdirfd: FileHandle, new_path: []const u8) SymLinkError!void { | ||
| 887 | const target_path_c = try toPosixPath(target_path); | ||
| 888 | const new_path_c = try toPosixPath(new_path); | ||
| 889 | return symlinkatC(target_path_c, newdirfd, new_path_c); | ||
| 890 | } | ||
| 891 | |||
| 892 | pub fn symlinkatC(target_path: [*]const u8, newdirfd: FileHandle, new_path: [*]const u8) SymLinkError!void { | ||
| 893 | const err = blk: { | ||
| 894 | if (builtin.link_libc) { | ||
| 895 | break :blk if (std.c.symlinkat(target_path, newdirfd, new_path) == -1) errno().* else 0; | ||
| 896 | } else { | ||
| 897 | break :blk system.getErrno(system.symlinkat(target_path, newdirfd, new_path)); | ||
| 898 | } | ||
| 899 | }; | ||
| 900 | switch (err) { | ||
| 901 | 0 => return, | ||
| 902 | EFAULT => unreachable, | ||
| 903 | EINVAL => unreachable, | ||
| 904 | EACCES => return error.AccessDenied, | ||
| 905 | EPERM => return error.AccessDenied, | ||
| 906 | EDQUOT => return error.DiskQuota, | ||
| 907 | EEXIST => return error.PathAlreadyExists, | ||
| 908 | EIO => return error.FileSystem, | ||
| 909 | ELOOP => return error.SymLinkLoop, | ||
| 910 | ENAMETOOLONG => return error.NameTooLong, | ||
| 911 | ENOENT => return error.FileNotFound, | ||
| 912 | ENOTDIR => return error.NotDir, | ||
| 913 | ENOMEM => return error.SystemResources, | ||
| 914 | ENOSPC => return error.NoSpaceLeft, | ||
| 915 | EROFS => return error.ReadOnlyFileSystem, | ||
| 916 | else => return unexpectedErrno(err), | ||
| 917 | } | ||
| 918 | } | ||
| 919 | |||
| 920 | /// This is the same as `symlink` except the parameters are null-terminated pointers. | ||
| 921 | /// See also `symlink` and `symlinkW`. | ||
| 922 | pub fn symlinkC(target_path: [*]const u8, new_path: [*]const u8) SymLinkError!void { | ||
| 923 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 924 | const target_path_w = try cStrToPrefixedFileW(target_path); | ||
| 925 | const new_path_w = try cStrToPrefixedFileW(new_path); | ||
| 926 | return symlinkW(&target_path_w, &new_path_w); | ||
| 927 | } | ||
| 928 | const err = if (builtin.link_libc) blk: { | ||
| 929 | break :blk if (std.c.symlink(target_path, new_path) == -1) errno().* else 0; | ||
| 930 | } else if (@hasDecl(system, "symlink")) blk: { | ||
| 931 | break :blk system.getErrno(system.symlink(target_path, new_path)); | ||
| 932 | } else blk: { | ||
| 933 | break :blk system.getErrno(system.symlinkat(target_path, AT_FDCWD, new_path)); | ||
| 934 | }; | ||
| 935 | switch (err) { | ||
| 936 | 0 => return, | ||
| 937 | EFAULT => unreachable, | ||
| 938 | EINVAL => unreachable, | ||
| 939 | EACCES => return error.AccessDenied, | ||
| 940 | EPERM => return error.AccessDenied, | ||
| 941 | EDQUOT => return error.DiskQuota, | ||
| 942 | EEXIST => return error.PathAlreadyExists, | ||
| 943 | EIO => return error.FileSystem, | ||
| 944 | ELOOP => return error.SymLinkLoop, | ||
| 945 | ENAMETOOLONG => return error.NameTooLong, | ||
| 946 | ENOENT => return error.FileNotFound, | ||
| 947 | ENOTDIR => return error.NotDir, | ||
| 948 | ENOMEM => return error.SystemResources, | ||
| 949 | ENOSPC => return error.NoSpaceLeft, | ||
| 950 | EROFS => return error.ReadOnlyFileSystem, | ||
| 951 | else => return unexpectedErrno(err), | ||
| 952 | } | ||
| 953 | } | ||
| 954 | |||
| 955 | /// This is the same as `symlink` except the parameters are null-terminated pointers to | ||
| 956 | /// UTF-16LE encoded strings. | ||
| 957 | /// See also `symlink` and `symlinkC`. | ||
| 958 | /// TODO handle when linking libc | ||
| 959 | pub fn symlinkW(target_path_w: [*]const u16, new_path_w: [*]const u16) SymLinkError!void { | ||
| 960 | if (windows.CreateSymbolicLinkW(target_path_w, new_path_w, 0) == 0) { | ||
| 961 | const err = windows.GetLastError(); | ||
| 962 | switch (err) { | ||
| 963 | else => return unexpectedErrorWindows(err), | ||
| 964 | } | ||
| 965 | } | ||
| 966 | } | ||
| 967 | |||
| 968 | pub const UnlinkError = error{ | ||
| 969 | FileNotFound, | ||
| 970 | AccessDenied, | ||
| 971 | FileBusy, | ||
| 972 | FileSystem, | ||
| 973 | IsDir, | ||
| 974 | SymLinkLoop, | ||
| 975 | NameTooLong, | ||
| 976 | NotDir, | ||
| 977 | SystemResources, | ||
| 978 | ReadOnlyFileSystem, | ||
| 979 | Unexpected, | ||
| 980 | |||
| 981 | /// On Windows, file paths must be valid Unicode. | ||
| 982 | InvalidUtf8, | ||
| 983 | |||
| 984 | /// On Windows, file paths cannot contain these characters: | ||
| 985 | /// '/', '*', '?', '"', '<', '>', '|' | ||
| 986 | BadPathName, | ||
| 987 | }; | ||
| 988 | |||
| 989 | /// Delete a name and possibly the file it refers to. | ||
| 990 | pub fn unlink(file_path: []const u8) UnlinkError!void { | ||
| 991 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 992 | const file_path_w = try sliceToPrefixedFileW(file_path); | ||
| 993 | return unlinkW(&file_path_w); | ||
| 994 | } else { | ||
| 995 | const file_path_c = try toPosixPath(file_path); | ||
| 996 | return unlinkC(&file_path_c); | ||
| 997 | } | ||
| 998 | } | ||
| 999 | |||
| 1000 | /// Same as `unlink` except the parameter is a UTF16LE-encoded string. | ||
| 1001 | /// TODO handle when linking libc | ||
| 1002 | pub fn unlinkW(file_path: [*]const u16) UnlinkError!void { | ||
| 1003 | if (windows.unlinkW(file_path) == 0) { | ||
| 1004 | const err = windows.GetLastError(); | ||
| 1005 | switch (err) { | ||
| 1006 | windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, | ||
| 1007 | windows.ERROR.ACCESS_DENIED => return error.AccessDenied, | ||
| 1008 | windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong, | ||
| 1009 | windows.ERROR.INVALID_PARAMETER => return error.NameTooLong, | ||
| 1010 | else => return unexpectedErrorWindows(err), | ||
| 1011 | } | ||
| 1012 | } | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | /// Same as `unlink` except the parameter is a null terminated UTF8-encoded string. | ||
| 1016 | pub fn unlinkC(file_path: [*]const u8) UnlinkError!void { | ||
| 1017 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 1018 | const file_path_w = try cStrToPrefixedFileW(file_path); | ||
| 1019 | return unlinkW(&file_path_w); | ||
| 1020 | } | ||
| 1021 | const err = if (builtin.link_libc) blk: { | ||
| 1022 | break :blk if (std.c.unlink(file_path) == -1) errno().* else 0; | ||
| 1023 | } else if (@hasDecl(system, "unlink")) blk: { | ||
| 1024 | break :blk system.getErrno(system.unlink(file_path)); | ||
| 1025 | } else blk: { | ||
| 1026 | break :blk system.getErrno(system.unlinkat(AT_FDCWD, file_path, 0)); | ||
| 1027 | }; | ||
| 1028 | switch (err) { | ||
| 1029 | 0 => return, | ||
| 1030 | EACCES => return error.AccessDenied, | ||
| 1031 | EPERM => return error.AccessDenied, | ||
| 1032 | EBUSY => return error.FileBusy, | ||
| 1033 | EFAULT => unreachable, | ||
| 1034 | EINVAL => unreachable, | ||
| 1035 | EIO => return error.FileSystem, | ||
| 1036 | EISDIR => return error.IsDir, | ||
| 1037 | ELOOP => return error.SymLinkLoop, | ||
| 1038 | ENAMETOOLONG => return error.NameTooLong, | ||
| 1039 | ENOENT => return error.FileNotFound, | ||
| 1040 | ENOTDIR => return error.NotDir, | ||
| 1041 | ENOMEM => return error.SystemResources, | ||
| 1042 | EROFS => return error.ReadOnlyFileSystem, | ||
| 1043 | else => return unexpectedErrno(err), | ||
| 1044 | } | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | const RenameError = error{}; // TODO | ||
| 1048 | |||
| 1049 | /// Change the name or location of a file. | ||
| 1050 | pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void { | ||
| 1051 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 1052 | const old_path_w = try sliceToPrefixedFileW(old_path); | ||
| 1053 | const new_path_w = try sliceToPrefixedFileW(new_path); | ||
| 1054 | return renameW(&old_path_w, &new_path_w); | ||
| 1055 | } else { | ||
| 1056 | const old_path_c = try toPosixPath(old_path); | ||
| 1057 | const new_path_c = try toPosixPath(new_path); | ||
| 1058 | return renameC(&old_path_c, &new_path_c); | ||
| 1059 | } | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | /// Same as `rename` except the parameters are null-terminated byte arrays. | ||
| 1063 | pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void { | ||
| 1064 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 1065 | const old_path_w = try cStrToPrefixedFileW(old_path); | ||
| 1066 | const new_path_w = try cStrToPrefixedFileW(new_path); | ||
| 1067 | return renameW(&old_path_w, &new_path_w); | ||
| 1068 | } | ||
| 1069 | const err = if (builtin.link_libc) blk: { | ||
| 1070 | break :blk if (std.c.rename(old_path, new_path) == -1) errno().* else 0; | ||
| 1071 | } else if (@hasDecl(system, "rename")) blk: { | ||
| 1072 | break :blk system.getErrno(system.rename(old_path, new_path)); | ||
| 1073 | } else if (@hasDecl(system, "renameat")) blk: { | ||
| 1074 | break :blk system.getErrno(system.renameat(AT_FDCWD, old_path, AT_FDCWD, new_path)); | ||
| 1075 | } else blk: { | ||
| 1076 | break :blk system.getErrno(system.renameat2(AT_FDCWD, old_path, AT_FDCWD, new_path, 0)); | ||
| 1077 | }; | ||
| 1078 | switch (err) { | ||
| 1079 | 0 => return, | ||
| 1080 | EACCES => return error.AccessDenied, | ||
| 1081 | EPERM => return error.AccessDenied, | ||
| 1082 | EBUSY => return error.FileBusy, | ||
| 1083 | EDQUOT => return error.DiskQuota, | ||
| 1084 | EFAULT => unreachable, | ||
| 1085 | EINVAL => unreachable, | ||
| 1086 | EISDIR => return error.IsDir, | ||
| 1087 | ELOOP => return error.SymLinkLoop, | ||
| 1088 | EMLINK => return error.LinkQuotaExceeded, | ||
| 1089 | ENAMETOOLONG => return error.NameTooLong, | ||
| 1090 | ENOENT => return error.FileNotFound, | ||
| 1091 | ENOTDIR => return error.NotDir, | ||
| 1092 | ENOMEM => return error.SystemResources, | ||
| 1093 | ENOSPC => return error.NoSpaceLeft, | ||
| 1094 | EEXIST => return error.PathAlreadyExists, | ||
| 1095 | ENOTEMPTY => return error.PathAlreadyExists, | ||
| 1096 | EROFS => return error.ReadOnlyFileSystem, | ||
| 1097 | EXDEV => return error.RenameAcrossMountPoints, | ||
| 1098 | else => return unexpectedErrno(err), | ||
| 1099 | } | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | /// Same as `rename` except the parameters are null-terminated UTF16LE-encoded strings. | ||
| 1103 | /// TODO handle when linking libc | ||
| 1104 | pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) RenameError!void { | ||
| 1105 | const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; | ||
| 1106 | if (windows.MoveFileExW(old_path, new_path, flags) == 0) { | ||
| 1107 | const err = windows.GetLastError(); | ||
| 1108 | switch (err) { | ||
| 1109 | else => return unexpectedErrorWindows(err), | ||
| 1110 | } | ||
| 1111 | } | ||
| 1112 | } | ||
| 1113 | |||
| 1114 | pub const MakeDirError = error{}; | ||
| 1115 | |||
| 1116 | /// Create a directory. | ||
| 1117 | /// `mode` is ignored on Windows. | ||
| 1118 | pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { | ||
| 1119 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 1120 | const dir_path_w = try sliceToPrefixedFileW(dir_path); | ||
| 1121 | return mkdirW(&dir_path_w, mode); | ||
| 1122 | } else { | ||
| 1123 | const dir_path_c = try toPosixPath(dir_path); | ||
| 1124 | return mkdirC(&dir_path_c, mode); | ||
| 1125 | } | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | /// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string. | ||
| 1129 | pub fn mkdirC(dir_path: [*]const u8, mode: u32) MakeDirError!void { | ||
| 1130 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 1131 | const dir_path_w = try cStrToPrefixedFileW(dir_path); | ||
| 1132 | return mkdirW(&dir_path_w, mode); | ||
| 1133 | } | ||
| 1134 | const err = if (builtin.link_libc) blk: { | ||
| 1135 | break :blk if (std.c.mkdir(dir_path, mode) == -1) errno().* else 0; | ||
| 1136 | } else if (@hasDecl(system, "mkdir")) blk: { | ||
| 1137 | break :blk system.getErrno(system.mkdir(dir_path, mode)); | ||
| 1138 | } else blk: { | ||
| 1139 | break :blk system.getErrno(system.mkdirat(AT_FDCWD, dir_path, mode)); | ||
| 1140 | }; | ||
| 1141 | switch (err) { | ||
| 1142 | 0 => return, | ||
| 1143 | EACCES => return error.AccessDenied, | ||
| 1144 | EPERM => return error.AccessDenied, | ||
| 1145 | EDQUOT => return error.DiskQuota, | ||
| 1146 | EEXIST => return error.PathAlreadyExists, | ||
| 1147 | EFAULT => unreachable, | ||
| 1148 | ELOOP => return error.SymLinkLoop, | ||
| 1149 | EMLINK => return error.LinkQuotaExceeded, | ||
| 1150 | ENAMETOOLONG => return error.NameTooLong, | ||
| 1151 | ENOENT => return error.FileNotFound, | ||
| 1152 | ENOMEM => return error.SystemResources, | ||
| 1153 | ENOSPC => return error.NoSpaceLeft, | ||
| 1154 | ENOTDIR => return error.NotDir, | ||
| 1155 | EROFS => return error.ReadOnlyFileSystem, | ||
| 1156 | else => return unexpectedErrno(err), | ||
| 1157 | } | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | /// Same as `mkdir` but the parameter is a null-terminated UTF16LE-encoded string. | ||
| 1161 | pub fn mkdirW(dir_path: []const u8, mode: u32) MakeDirError!void { | ||
| 1162 | const dir_path_w = try sliceToPrefixedFileW(dir_path); | ||
| 1163 | |||
| 1164 | if (windows.CreateDirectoryW(&dir_path_w, null) == 0) { | ||
| 1165 | const err = windows.GetLastError(); | ||
| 1166 | switch (err) { | ||
| 1167 | windows.ERROR.ALREADY_EXISTS => return error.PathAlreadyExists, | ||
| 1168 | windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, | ||
| 1169 | else => return unexpectedErrorWindows(err), | ||
| 1170 | } | ||
| 1171 | } | ||
| 1172 | } | ||
| 1173 | |||
| 1174 | pub const DeleteDirError = error{ | ||
| 1175 | AccessDenied, | ||
| 1176 | FileBusy, | ||
| 1177 | SymLinkLoop, | ||
| 1178 | NameTooLong, | ||
| 1179 | FileNotFound, | ||
| 1180 | SystemResources, | ||
| 1181 | NotDir, | ||
| 1182 | DirNotEmpty, | ||
| 1183 | ReadOnlyFileSystem, | ||
| 1184 | InvalidUtf8, | ||
| 1185 | BadPathName, | ||
| 1186 | Unexpected, | ||
| 1187 | }; | ||
| 1188 | |||
| 1189 | /// Deletes an empty directory. | ||
| 1190 | pub fn rmdir(dir_path: []const u8) DeleteDirError!void { | ||
| 1191 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 1192 | const dir_path_w = try sliceToPrefixedFileW(dir_path); | ||
| 1193 | return rmdirW(&dir_path_w); | ||
| 1194 | } else { | ||
| 1195 | const dir_path_c = try toPosixPath(dir_path); | ||
| 1196 | return rmdirC(&dir_path_c); | ||
| 1197 | } | ||
| 1198 | } | ||
| 1199 | |||
| 1200 | /// Same as `rmdir` except the parameter is a null-terminated UTF8-encoded string. | ||
| 1201 | pub fn rmdirC(dir_path: [*]const u8) DeleteDirError!void { | ||
| 1202 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 1203 | const dir_path_w = try cStrToPrefixedFileW(dir_path); | ||
| 1204 | return rmdirW(&dir_path_w); | ||
| 1205 | } | ||
| 1206 | const err = if (builtin.link_libc) blk: { | ||
| 1207 | break :blk if (std.c.rmdir(dir_path) == -1) errno().* else 0; | ||
| 1208 | } else if (@hasDecl(system, "rmdir")) blk: { | ||
| 1209 | break :blk system.getErrno(system.rmdir(dir_path)); | ||
| 1210 | } else blk: { | ||
| 1211 | break :blk system.getErrno(system.unlinkat(AT_FDCWD, dir_path, AT_REMOVEDIR)); | ||
| 1212 | }; | ||
| 1213 | switch (err) { | ||
| 1214 | 0 => return, | ||
| 1215 | EACCES => return error.AccessDenied, | ||
| 1216 | EPERM => return error.AccessDenied, | ||
| 1217 | EBUSY => return error.FileBusy, | ||
| 1218 | EFAULT => unreachable, | ||
| 1219 | EINVAL => unreachable, | ||
| 1220 | ELOOP => return error.SymLinkLoop, | ||
| 1221 | ENAMETOOLONG => return error.NameTooLong, | ||
| 1222 | ENOENT => return error.FileNotFound, | ||
| 1223 | ENOMEM => return error.SystemResources, | ||
| 1224 | ENOTDIR => return error.NotDir, | ||
| 1225 | EEXIST => return error.DirNotEmpty, | ||
| 1226 | ENOTEMPTY => return error.DirNotEmpty, | ||
| 1227 | EROFS => return error.ReadOnlyFileSystem, | ||
| 1228 | else => return unexpectedErrno(err), | ||
| 1229 | } | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | /// Same as `rmdir` except the parameter is a null-terminated UTF16LE-encoded string. | ||
| 1233 | /// TODO handle linking libc | ||
| 1234 | pub fn rmdirW(dir_path_w: [*]const u16) DeleteDirError!void { | ||
| 1235 | if (windows.RemoveDirectoryW(dir_path_w) == 0) { | ||
| 1236 | const err = windows.GetLastError(); | ||
| 1237 | switch (err) { | ||
| 1238 | windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, | ||
| 1239 | windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty, | ||
| 1240 | else => return unexpectedErrorWindows(err), | ||
| 1241 | } | ||
| 1242 | } | ||
| 1243 | } | ||
| 1244 | |||
| 1245 | pub const ChangeCurDirError = error{}; | ||
| 1246 | |||
| 1247 | /// Changes the current working directory of the calling process. | ||
| 1248 | /// `dir_path` is recommended to be a UTF-8 encoded string. | ||
| 1249 | pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { | ||
| 1250 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 1251 | const dir_path_w = try sliceToPrefixedFileW(dir_path); | ||
| 1252 | return chdirW(&dir_path_w); | ||
| 1253 | } else { | ||
| 1254 | const dir_path_c = try toPosixPath(dir_path); | ||
| 1255 | return chdirC(&dir_path_c); | ||
| 1256 | } | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | /// Same as `chdir` except the parameter is null-terminated. | ||
| 1260 | pub fn chdirC(dir_path: [*]const u8) ChangeCurDirError!void { | ||
| 1261 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 1262 | const dir_path_w = try cStrToPrefixedFileW(dir_path); | ||
| 1263 | return chdirW(&dir_path_w); | ||
| 1264 | } | ||
| 1265 | const err = if (builtin.link_libc) blk: { | ||
| 1266 | break :blk if (std.c.chdir(dir_path) == -1) errno().* else 0; | ||
| 1267 | } else blk: { | ||
| 1268 | break :blk system.getErrno(system.chdir(dir_path)); | ||
| 1269 | }; | ||
| 1270 | switch (err) { | ||
| 1271 | 0 => return, | ||
| 1272 | EACCES => return error.AccessDenied, | ||
| 1273 | EFAULT => unreachable, | ||
| 1274 | EIO => return error.FileSystem, | ||
| 1275 | ELOOP => return error.SymLinkLoop, | ||
| 1276 | ENAMETOOLONG => return error.NameTooLong, | ||
| 1277 | ENOENT => return error.FileNotFound, | ||
| 1278 | ENOMEM => return error.SystemResources, | ||
| 1279 | ENOTDIR => return error.NotDir, | ||
| 1280 | else => return unexpectedErrno(err), | ||
| 1281 | } | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | /// Same as `chdir` except the parameter is a null-terminated, UTF16LE-encoded string. | ||
| 1285 | /// TODO handle linking libc | ||
| 1286 | pub fn chdirW(dir_path: [*]const u16) ChangeCurDirError!void { | ||
| 1287 | @compileError("TODO implement chdir for Windows"); | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | pub const ReadLinkError = error{}; | ||
| 1291 | |||
| 1292 | /// Read value of a symbolic link. | ||
| 1293 | /// The return value is a slice of `out_buffer` from index 0. | ||
| 1294 | pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { | ||
| 1295 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 1296 | const file_path_w = try sliceToPrefixedFileW(file_path); | ||
| 1297 | return readlinkW(&file_path_w, out_buffer); | ||
| 1298 | } else { | ||
| 1299 | const file_path_c = try toPosixPath(file_path); | ||
| 1300 | return readlinkC(&file_path_c, out_buffer); | ||
| 1301 | } | ||
| 1302 | } | ||
| 1303 | |||
| 1304 | /// Same as `readlink` except `file_path` is null-terminated. | ||
| 1305 | pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 { | ||
| 1306 | if (windows.is_the_target and !builtin.link_libc) { | ||
| 1307 | const file_path_w = try cStrToPrefixedFileW(file_path); | ||
| 1308 | return readlinkW(&file_path_w, out_buffer); | ||
| 1309 | } | ||
| 1310 | const err = if (builtin.link_libc) blk: { | ||
| 1311 | break :blk if (std.c.readlink(file_path, out_buffer.ptr, out_buffer.len) == -1) errno().* else 0; | ||
| 1312 | } else if (@hasDecl(system, "readlink")) blk: { | ||
| 1313 | break :blk system.getErrno(system.readlink(file_path, out_buffer.ptr, out_buffer.len)); | ||
| 1314 | } else blk: { | ||
| 1315 | break :blk system.getErrno(system.readlinkat(AT_FDCWD, file_path, out_buffer.ptr, out_buffer.len)); | ||
| 1316 | }; | ||
| 1317 | const rc = system.readlink(file_path, out_buffer, out_buffer.len); | ||
| 1318 | switch (system.getErrno(rc)) { | ||
| 1319 | 0 => return out_buffer[0..rc], | ||
| 1320 | EACCES => return error.AccessDenied, | ||
| 1321 | EFAULT => unreachable, | ||
| 1322 | EINVAL => unreachable, | ||
| 1323 | EIO => return error.FileSystem, | ||
| 1324 | ELOOP => return error.SymLinkLoop, | ||
| 1325 | ENAMETOOLONG => return error.NameTooLong, | ||
| 1326 | ENOENT => return error.FileNotFound, | ||
| 1327 | ENOMEM => return error.SystemResources, | ||
| 1328 | ENOTDIR => return error.NotDir, | ||
| 1329 | else => |err| return unexpectedErrno(err), | ||
| 1330 | } | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | pub const SetIdError = error{ | ||
| 1334 | ResourceLimitReached, | ||
| 1335 | InvalidUserId, | ||
| 1336 | PermissionDenied, | ||
| 1337 | Unexpected, | ||
| 1338 | }; | ||
| 1339 | |||
| 1340 | pub fn setuid(uid: u32) SetIdError!void { | ||
| 1341 | switch (system.getErrno(system.setuid(uid))) { | ||
| 1342 | 0 => return, | ||
| 1343 | EAGAIN => return error.ResourceLimitReached, | ||
| 1344 | EINVAL => return error.InvalidUserId, | ||
| 1345 | EPERM => return error.PermissionDenied, | ||
| 1346 | else => |err| return unexpectedErrno(err), | ||
| 1347 | } | ||
| 1348 | } | ||
| 1349 | |||
| 1350 | pub fn setreuid(ruid: u32, euid: u32) SetIdError!void { | ||
| 1351 | switch (system.getErrno(system.setreuid(ruid, euid))) { | ||
| 1352 | 0 => return, | ||
| 1353 | EAGAIN => return error.ResourceLimitReached, | ||
| 1354 | EINVAL => return error.InvalidUserId, | ||
| 1355 | EPERM => return error.PermissionDenied, | ||
| 1356 | else => |err| return unexpectedErrno(err), | ||
| 1357 | } | ||
| 1358 | } | ||
| 1359 | |||
| 1360 | pub fn setgid(gid: u32) SetIdError!void { | ||
| 1361 | switch (system.getErrno(system.setgid(gid))) { | ||
| 1362 | 0 => return, | ||
| 1363 | EAGAIN => return error.ResourceLimitReached, | ||
| 1364 | EINVAL => return error.InvalidUserId, | ||
| 1365 | EPERM => return error.PermissionDenied, | ||
| 1366 | else => |err| return unexpectedErrno(err), | ||
| 1367 | } | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | pub fn setregid(rgid: u32, egid: u32) SetIdError!void { | ||
| 1371 | switch (system.getErrno(system.setregid(rgid, egid))) { | ||
| 1372 | 0 => return, | ||
| 1373 | EAGAIN => return error.ResourceLimitReached, | ||
| 1374 | EINVAL => return error.InvalidUserId, | ||
| 1375 | EPERM => return error.PermissionDenied, | ||
| 1376 | else => |err| return unexpectedErrno(err), | ||
| 1377 | } | ||
| 1378 | } | ||
| 1379 | |||
| 1380 | pub const GetStdHandleError = error{ | ||
| 1381 | NoStandardHandleAttached, | ||
| 1382 | Unexpected, | ||
| 1383 | }; | ||
| 1384 | |||
| 1385 | pub fn GetStdHandle(handle_id: windows.DWORD) GetStdHandleError!FileHandle { | ||
| 1386 | if (windows.is_the_target) { | ||
| 1387 | const handle = windows.GetStdHandle(handle_id) orelse return error.NoStandardHandleAttached; | ||
| 1388 | if (handle == windows.INVALID_HANDLE_VALUE) { | ||
| 1389 | switch (windows.GetLastError()) { | ||
| 1390 | else => |err| unexpectedErrorWindows(err), | ||
| 1391 | } | ||
| 1392 | } | ||
| 1393 | return handle; | ||
| 1394 | } | ||
| 1395 | |||
| 1396 | switch (handle_id) { | ||
| 1397 | windows.STD_ERROR_HANDLE => return STDERR_FILENO, | ||
| 1398 | windows.STD_OUTPUT_HANDLE => return STDOUT_FILENO, | ||
| 1399 | windows.STD_INPUT_HANDLE => return STDIN_FILENO, | ||
| 1400 | else => unreachable, | ||
| 1401 | } | ||
| 1402 | } | ||
| 1403 | |||
| 1404 | /// Test whether a file descriptor refers to a terminal. | ||
| 1405 | pub fn isatty(handle: FileHandle) bool { | ||
| 1406 | if (builtin.link_libc) { | ||
| 1407 | return c.isatty(handle) != 0; | ||
| 1408 | } | ||
| 1409 | if (windows.is_the_target) { | ||
| 1410 | if (isCygwinPty(handle)) | ||
| 1411 | return true; | ||
| 1412 | |||
| 1413 | var out: windows.DWORD = undefined; | ||
| 1414 | return windows.GetConsoleMode(handle, &out) != 0; | ||
| 1415 | } | ||
| 1416 | if (wasi.is_the_target) { | ||
| 1417 | @compileError("TODO implement std.os.posix.isatty for WASI"); | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | var wsz: system.winsize = undefined; | ||
| 1421 | return system.syscall3(system.SYS_ioctl, @bitCast(usize, isize(handle)), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; | ||
| 1422 | } | ||
| 1423 | |||
| 1424 | pub fn isCygwinPty(handle: FileHandle) bool { | ||
| 1425 | if (!windows.is_the_target) return false; | ||
| 1426 | |||
| 1427 | const size = @sizeOf(windows.FILE_NAME_INFO); | ||
| 1428 | var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = []u8{0} ** (size + windows.MAX_PATH); | ||
| 1429 | |||
| 1430 | if (windows.GetFileInformationByHandleEx( | ||
| 1431 | handle, | ||
| 1432 | windows.FileNameInfo, | ||
| 1433 | @ptrCast(*c_void, &name_info_bytes[0]), | ||
| 1434 | @intCast(u32, name_info_bytes.len), | ||
| 1435 | ) == 0) { | ||
| 1436 | return false; | ||
| 1437 | } | ||
| 1438 | |||
| 1439 | const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]); | ||
| 1440 | const name_bytes = name_info_bytes[size .. size + usize(name_info.FileNameLength)]; | ||
| 1441 | const name_wide = @bytesToSlice(u16, name_bytes); | ||
| 1442 | return mem.indexOf(u16, name_wide, []u16{ 'm', 's', 'y', 's', '-' }) != null or | ||
| 1443 | mem.indexOf(u16, name_wide, []u16{ '-', 'p', 't', 'y' }) != null; | ||
| 1444 | } | ||
| 1445 | |||
| 1446 | pub const SocketError = error{ | ||
| 1447 | /// Permission to create a socket of the specified type and/or | ||
| 1448 | /// pro‐tocol is denied. | ||
| 1449 | PermissionDenied, | ||
| 1450 | |||
| 1451 | /// The implementation does not support the specified address family. | ||
| 1452 | AddressFamilyNotSupported, | ||
| 1453 | |||
| 1454 | /// Unknown protocol, or protocol family not available. | ||
| 1455 | ProtocolFamilyNotAvailable, | ||
| 1456 | |||
| 1457 | /// The per-process limit on the number of open file descriptors has been reached. | ||
| 1458 | ProcessFdQuotaExceeded, | ||
| 1459 | |||
| 1460 | /// The system-wide limit on the total number of open files has been reached. | ||
| 1461 | SystemFdQuotaExceeded, | ||
| 1462 | |||
| 1463 | /// Insufficient memory is available. The socket cannot be created until sufficient | ||
| 1464 | /// resources are freed. | ||
| 1465 | SystemResources, | ||
| 1466 | |||
| 1467 | /// The protocol type or the specified protocol is not supported within this domain. | ||
| 1468 | ProtocolNotSupported, | ||
| 1469 | |||
| 1470 | Unexpected, | ||
| 1471 | }; | ||
| 1472 | |||
| 1473 | pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!i32 { | ||
| 1474 | const rc = system.socket(domain, socket_type, protocol); | ||
| 1475 | switch (system.getErrno(rc)) { | ||
| 1476 | 0 => return @intCast(i32, rc), | ||
| 1477 | EACCES => return error.PermissionDenied, | ||
| 1478 | EAFNOSUPPORT => return error.AddressFamilyNotSupported, | ||
| 1479 | EINVAL => return error.ProtocolFamilyNotAvailable, | ||
| 1480 | EMFILE => return error.ProcessFdQuotaExceeded, | ||
| 1481 | ENFILE => return error.SystemFdQuotaExceeded, | ||
| 1482 | ENOBUFS, ENOMEM => return error.SystemResources, | ||
| 1483 | EPROTONOSUPPORT => return error.ProtocolNotSupported, | ||
| 1484 | else => |err| return unexpectedErrno(err), | ||
| 1485 | } | ||
| 1486 | } | ||
| 1487 | |||
| 1488 | pub const BindError = error{ | ||
| 1489 | /// The address is protected, and the user is not the superuser. | ||
| 1490 | /// For UNIX domain sockets: Search permission is denied on a component | ||
| 1491 | /// of the path prefix. | ||
| 1492 | AccessDenied, | ||
| 1493 | |||
| 1494 | /// The given address is already in use, or in the case of Internet domain sockets, | ||
| 1495 | /// The port number was specified as zero in the socket | ||
| 1496 | /// address structure, but, upon attempting to bind to an ephemeral port, it was | ||
| 1497 | /// determined that all port numbers in the ephemeral port range are currently in | ||
| 1498 | /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7). | ||
| 1499 | AddressInUse, | ||
| 1500 | |||
| 1501 | /// A nonexistent interface was requested or the requested address was not local. | ||
| 1502 | AddressNotAvailable, | ||
| 1503 | |||
| 1504 | /// Too many symbolic links were encountered in resolving addr. | ||
| 1505 | SymLinkLoop, | ||
| 1506 | |||
| 1507 | /// addr is too long. | ||
| 1508 | NameTooLong, | ||
| 1509 | |||
| 1510 | /// A component in the directory prefix of the socket pathname does not exist. | ||
| 1511 | FileNotFound, | ||
| 1512 | |||
| 1513 | /// Insufficient kernel memory was available. | ||
| 1514 | SystemResources, | ||
| 1515 | |||
| 1516 | /// A component of the path prefix is not a directory. | ||
| 1517 | NotDir, | ||
| 1518 | |||
| 1519 | /// The socket inode would reside on a read-only filesystem. | ||
| 1520 | ReadOnlyFileSystem, | ||
| 1521 | |||
| 1522 | Unexpected, | ||
| 1523 | }; | ||
| 1524 | |||
| 1525 | /// addr is `*const T` where T is one of the sockaddr | ||
| 1526 | pub fn bind(fd: i32, addr: *const sockaddr) BindError!void { | ||
| 1527 | const rc = system.bind(fd, system, @sizeOf(sockaddr)); | ||
| 1528 | switch (system.getErrno(rc)) { | ||
| 1529 | 0 => return, | ||
| 1530 | EACCES => return error.AccessDenied, | ||
| 1531 | EADDRINUSE => return error.AddressInUse, | ||
| 1532 | EBADF => unreachable, // always a race condition if this error is returned | ||
| 1533 | EINVAL => unreachable, | ||
| 1534 | ENOTSOCK => unreachable, | ||
| 1535 | EADDRNOTAVAIL => return error.AddressNotAvailable, | ||
| 1536 | EFAULT => unreachable, | ||
| 1537 | ELOOP => return error.SymLinkLoop, | ||
| 1538 | ENAMETOOLONG => return error.NameTooLong, | ||
| 1539 | ENOENT => return error.FileNotFound, | ||
| 1540 | ENOMEM => return error.SystemResources, | ||
| 1541 | ENOTDIR => return error.NotDir, | ||
| 1542 | EROFS => return error.ReadOnlyFileSystem, | ||
| 1543 | else => |err| return unexpectedErrno(err), | ||
| 1544 | } | ||
| 1545 | } | ||
| 1546 | |||
| 1547 | const ListenError = error{ | ||
| 1548 | /// Another socket is already listening on the same port. | ||
| 1549 | /// For Internet domain sockets, the socket referred to by sockfd had not previously | ||
| 1550 | /// been bound to an address and, upon attempting to bind it to an ephemeral port, it | ||
| 1551 | /// was determined that all port numbers in the ephemeral port range are currently in | ||
| 1552 | /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). | ||
| 1553 | AddressInUse, | ||
| 1554 | |||
| 1555 | /// The file descriptor sockfd does not refer to a socket. | ||
| 1556 | FileDescriptorNotASocket, | ||
| 1557 | |||
| 1558 | /// The socket is not of a type that supports the listen() operation. | ||
| 1559 | OperationNotSupported, | ||
| 1560 | |||
| 1561 | Unexpected, | ||
| 1562 | }; | ||
| 1563 | |||
| 1564 | pub fn listen(sockfd: i32, backlog: u32) ListenError!void { | ||
| 1565 | const rc = system.listen(sockfd, backlog); | ||
| 1566 | switch (system.getErrno(rc)) { | ||
| 1567 | 0 => return, | ||
| 1568 | EADDRINUSE => return error.AddressInUse, | ||
| 1569 | EBADF => unreachable, | ||
| 1570 | ENOTSOCK => return error.FileDescriptorNotASocket, | ||
| 1571 | EOPNOTSUPP => return error.OperationNotSupported, | ||
| 1572 | else => |err| return unexpectedErrno(err), | ||
| 1573 | } | ||
| 1574 | } | ||
| 1575 | |||
| 1576 | pub const AcceptError = error{ | ||
| 1577 | ConnectionAborted, | ||
| 1578 | |||
| 1579 | /// The per-process limit on the number of open file descriptors has been reached. | ||
| 1580 | ProcessFdQuotaExceeded, | ||
| 1581 | |||
| 1582 | /// The system-wide limit on the total number of open files has been reached. | ||
| 1583 | SystemFdQuotaExceeded, | ||
| 1584 | |||
| 1585 | /// Not enough free memory. This often means that the memory allocation is limited | ||
| 1586 | /// by the socket buffer limits, not by the system memory. | ||
| 1587 | SystemResources, | ||
| 1588 | |||
| 1589 | /// The file descriptor sockfd does not refer to a socket. | ||
| 1590 | FileDescriptorNotASocket, | ||
| 1591 | |||
| 1592 | /// The referenced socket is not of type SOCK_STREAM. | ||
| 1593 | OperationNotSupported, | ||
| 1594 | |||
| 1595 | ProtocolFailure, | ||
| 1596 | |||
| 1597 | /// Firewall rules forbid connection. | ||
| 1598 | BlockedByFirewall, | ||
| 1599 | |||
| 1600 | Unexpected, | ||
| 1601 | }; | ||
| 1602 | |||
| 1603 | /// Accept a connection on a socket. `fd` must be opened in blocking mode. | ||
| 1604 | /// See also `accept4_async`. | ||
| 1605 | pub fn accept4(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 { | ||
| 1606 | while (true) { | ||
| 1607 | var sockaddr_size = u32(@sizeOf(sockaddr)); | ||
| 1608 | const rc = system.accept4(fd, addr, &sockaddr_size, flags); | ||
| 1609 | switch (system.getErrno(rc)) { | ||
| 1610 | 0 => return @intCast(i32, rc), | ||
| 1611 | EINTR => continue, | ||
| 1612 | else => |err| return unexpectedErrno(err), | ||
| 1613 | |||
| 1614 | EAGAIN => unreachable, // This function is for blocking only. | ||
| 1615 | EBADF => unreachable, // always a race condition | ||
| 1616 | ECONNABORTED => return error.ConnectionAborted, | ||
| 1617 | EFAULT => unreachable, | ||
| 1618 | EINVAL => unreachable, | ||
| 1619 | EMFILE => return error.ProcessFdQuotaExceeded, | ||
| 1620 | ENFILE => return error.SystemFdQuotaExceeded, | ||
| 1621 | ENOBUFS => return error.SystemResources, | ||
| 1622 | ENOMEM => return error.SystemResources, | ||
| 1623 | ENOTSOCK => return error.FileDescriptorNotASocket, | ||
| 1624 | EOPNOTSUPP => return error.OperationNotSupported, | ||
| 1625 | EPROTO => return error.ProtocolFailure, | ||
| 1626 | EPERM => return error.BlockedByFirewall, | ||
| 1627 | } | ||
| 1628 | } | ||
| 1629 | } | ||
| 1630 | |||
| 1631 | /// This is the same as `accept4` except `fd` is expected to be non-blocking. | ||
| 1632 | /// Returns -1 if would block. | ||
| 1633 | pub fn accept4_async(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 { | ||
| 1634 | while (true) { | ||
| 1635 | var sockaddr_size = u32(@sizeOf(sockaddr)); | ||
| 1636 | const rc = system.accept4(fd, addr, &sockaddr_size, flags); | ||
| 1637 | switch (system.getErrno(rc)) { | ||
| 1638 | 0 => return @intCast(i32, rc), | ||
| 1639 | EINTR => continue, | ||
| 1640 | else => |err| return unexpectedErrno(err), | ||
| 1641 | |||
| 1642 | EAGAIN => return -1, | ||
| 1643 | EBADF => unreachable, // always a race condition | ||
| 1644 | ECONNABORTED => return error.ConnectionAborted, | ||
| 1645 | EFAULT => unreachable, | ||
| 1646 | EINVAL => unreachable, | ||
| 1647 | EMFILE => return error.ProcessFdQuotaExceeded, | ||
| 1648 | ENFILE => return error.SystemFdQuotaExceeded, | ||
| 1649 | ENOBUFS => return error.SystemResources, | ||
| 1650 | ENOMEM => return error.SystemResources, | ||
| 1651 | ENOTSOCK => return error.FileDescriptorNotASocket, | ||
| 1652 | EOPNOTSUPP => return error.OperationNotSupported, | ||
| 1653 | EPROTO => return error.ProtocolFailure, | ||
| 1654 | EPERM => return error.BlockedByFirewall, | ||
| 1655 | } | ||
| 1656 | } | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | pub const EpollCreateError = error{ | ||
| 1660 | /// The per-user limit on the number of epoll instances imposed by | ||
| 1661 | /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further | ||
| 1662 | /// details. | ||
| 1663 | /// Or, The per-process limit on the number of open file descriptors has been reached. | ||
| 1664 | ProcessFdQuotaExceeded, | ||
| 1665 | |||
| 1666 | /// The system-wide limit on the total number of open files has been reached. | ||
| 1667 | SystemFdQuotaExceeded, | ||
| 1668 | |||
| 1669 | /// There was insufficient memory to create the kernel object. | ||
| 1670 | SystemResources, | ||
| 1671 | |||
| 1672 | Unexpected, | ||
| 1673 | }; | ||
| 1674 | |||
| 1675 | pub fn epoll_create1(flags: u32) EpollCreateError!i32 { | ||
| 1676 | const rc = system.epoll_create1(flags); | ||
| 1677 | switch (system.getErrno(rc)) { | ||
| 1678 | 0 => return @intCast(i32, rc), | ||
| 1679 | else => |err| return unexpectedErrno(err), | ||
| 1680 | |||
| 1681 | EINVAL => unreachable, | ||
| 1682 | EMFILE => return error.ProcessFdQuotaExceeded, | ||
| 1683 | ENFILE => return error.SystemFdQuotaExceeded, | ||
| 1684 | ENOMEM => return error.SystemResources, | ||
| 1685 | } | ||
| 1686 | } | ||
| 1687 | |||
| 1688 | pub const EpollCtlError = error{ | ||
| 1689 | /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered | ||
| 1690 | /// with this epoll instance. | ||
| 1691 | FileDescriptorAlreadyPresentInSet, | ||
| 1692 | |||
| 1693 | /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a | ||
| 1694 | /// circular loop of epoll instances monitoring one another. | ||
| 1695 | OperationCausesCircularLoop, | ||
| 1696 | |||
| 1697 | /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll | ||
| 1698 | /// instance. | ||
| 1699 | FileDescriptorNotRegistered, | ||
| 1700 | |||
| 1701 | /// There was insufficient memory to handle the requested op control operation. | ||
| 1702 | SystemResources, | ||
| 1703 | |||
| 1704 | /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while | ||
| 1705 | /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance. | ||
| 1706 | /// See epoll(7) for further details. | ||
| 1707 | UserResourceLimitReached, | ||
| 1708 | |||
| 1709 | /// The target file fd does not support epoll. This error can occur if fd refers to, | ||
| 1710 | /// for example, a regular file or a directory. | ||
| 1711 | FileDescriptorIncompatibleWithEpoll, | ||
| 1712 | |||
| 1713 | Unexpected, | ||
| 1714 | }; | ||
| 1715 | |||
| 1716 | pub fn epoll_ctl(epfd: i32, op: u32, fd: i32, event: *epoll_event) EpollCtlError!void { | ||
| 1717 | const rc = system.epoll_ctl(epfd, op, fd, event); | ||
| 1718 | switch (system.getErrno(rc)) { | ||
| 1719 | 0 => return, | ||
| 1720 | else => |err| return unexpectedErrno(err), | ||
| 1721 | |||
| 1722 | EBADF => unreachable, // always a race condition if this happens | ||
| 1723 | EEXIST => return error.FileDescriptorAlreadyPresentInSet, | ||
| 1724 | EINVAL => unreachable, | ||
| 1725 | ELOOP => return error.OperationCausesCircularLoop, | ||
| 1726 | ENOENT => return error.FileDescriptorNotRegistered, | ||
| 1727 | ENOMEM => return error.SystemResources, | ||
| 1728 | ENOSPC => return error.UserResourceLimitReached, | ||
| 1729 | EPERM => return error.FileDescriptorIncompatibleWithEpoll, | ||
| 1730 | } | ||
| 1731 | } | ||
| 1732 | |||
| 1733 | /// Waits for an I/O event on an epoll file descriptor. | ||
| 1734 | /// Returns the number of file descriptors ready for the requested I/O, | ||
| 1735 | /// or zero if no file descriptor became ready during the requested timeout milliseconds. | ||
| 1736 | pub fn epoll_wait(epfd: i32, events: []epoll_event, timeout: i32) usize { | ||
| 1737 | while (true) { | ||
| 1738 | // TODO get rid of the @intCast | ||
| 1739 | const rc = system.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout); | ||
| 1740 | switch (system.getErrno(rc)) { | ||
| 1741 | 0 => return rc, | ||
| 1742 | EINTR => continue, | ||
| 1743 | EBADF => unreachable, | ||
| 1744 | EFAULT => unreachable, | ||
| 1745 | EINVAL => unreachable, | ||
| 1746 | else => unreachable, | ||
| 1747 | } | ||
| 1748 | } | ||
| 1749 | } | ||
| 1750 | |||
| 1751 | pub const EventFdError = error{ | ||
| 1752 | SystemResources, | ||
| 1753 | ProcessFdQuotaExceeded, | ||
| 1754 | SystemFdQuotaExceeded, | ||
| 1755 | Unexpected, | ||
| 1756 | }; | ||
| 1757 | |||
| 1758 | pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 { | ||
| 1759 | const rc = system.eventfd(initval, flags); | ||
| 1760 | switch (system.getErrno(rc)) { | ||
| 1761 | 0 => return @intCast(i32, rc), | ||
| 1762 | else => |err| return unexpectedErrno(err), | ||
| 1763 | |||
| 1764 | EINVAL => unreachable, // invalid parameters | ||
| 1765 | EMFILE => return error.ProcessFdQuotaExceeded, | ||
| 1766 | ENFILE => return error.SystemFdQuotaExceeded, | ||
| 1767 | ENODEV => return error.SystemResources, | ||
| 1768 | ENOMEM => return error.SystemResources, | ||
| 1769 | } | ||
| 1770 | } | ||
| 1771 | |||
| 1772 | pub const GetSockNameError = error{ | ||
| 1773 | /// Insufficient resources were available in the system to perform the operation. | ||
| 1774 | SystemResources, | ||
| 1775 | |||
| 1776 | Unexpected, | ||
| 1777 | }; | ||
| 1778 | |||
| 1779 | pub fn getsockname(sockfd: i32) GetSockNameError!sockaddr { | ||
| 1780 | var addr: sockaddr = undefined; | ||
| 1781 | var addrlen: socklen_t = @sizeOf(sockaddr); | ||
| 1782 | switch (system.getErrno(system.getsockname(sockfd, &addr, &addrlen))) { | ||
| 1783 | 0 => return addr, | ||
| 1784 | else => |err| return unexpectedErrno(err), | ||
| 1785 | |||
| 1786 | EBADF => unreachable, // always a race condition | ||
| 1787 | EFAULT => unreachable, | ||
| 1788 | EINVAL => unreachable, // invalid parameters | ||
| 1789 | ENOTSOCK => unreachable, | ||
| 1790 | ENOBUFS => return error.SystemResources, | ||
| 1791 | } | ||
| 1792 | } | ||
| 1793 | |||
| 1794 | pub const ConnectError = error{ | ||
| 1795 | /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket | ||
| 1796 | /// file, or search permission is denied for one of the directories in the path prefix. | ||
| 1797 | /// or | ||
| 1798 | /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or | ||
| 1799 | /// the connection request failed because of a local firewall rule. | ||
| 1800 | PermissionDenied, | ||
| 1801 | |||
| 1802 | /// Local address is already in use. | ||
| 1803 | AddressInUse, | ||
| 1804 | |||
| 1805 | /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an | ||
| 1806 | /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers | ||
| 1807 | /// in the ephemeral port range are currently in use. See the discussion of | ||
| 1808 | /// /proc/sys/net/ipv4/ip_local_port_range in ip(7). | ||
| 1809 | AddressNotAvailable, | ||
| 1810 | |||
| 1811 | /// The passed address didn't have the correct address family in its sa_family field. | ||
| 1812 | AddressFamilyNotSupported, | ||
| 1813 | |||
| 1814 | /// Insufficient entries in the routing cache. | ||
| 1815 | SystemResources, | ||
| 1816 | |||
| 1817 | /// A connect() on a stream socket found no one listening on the remote address. | ||
| 1818 | ConnectionRefused, | ||
| 1819 | |||
| 1820 | /// Network is unreachable. | ||
| 1821 | NetworkUnreachable, | ||
| 1822 | |||
| 1823 | /// Timeout while attempting connection. The server may be too busy to accept new connections. Note | ||
| 1824 | /// that for IP sockets the timeout may be very long when syncookies are enabled on the server. | ||
| 1825 | ConnectionTimedOut, | ||
| 1826 | |||
| 1827 | Unexpected, | ||
| 1828 | }; | ||
| 1829 | |||
| 1830 | /// Initiate a connection on a socket. | ||
| 1831 | /// This is for blocking file descriptors only. | ||
| 1832 | /// For non-blocking, see `connect_async`. | ||
| 1833 | pub fn connect(sockfd: i32, sockaddr: *const sockaddr) ConnectError!void { | ||
| 1834 | while (true) { | ||
| 1835 | switch (system.getErrno(system.connect(sockfd, sockaddr, @sizeOf(sockaddr)))) { | ||
| 1836 | 0 => return, | ||
| 1837 | else => |err| return unexpectedErrno(err), | ||
| 1838 | |||
| 1839 | EACCES => return error.PermissionDenied, | ||
| 1840 | EPERM => return error.PermissionDenied, | ||
| 1841 | EADDRINUSE => return error.AddressInUse, | ||
| 1842 | EADDRNOTAVAIL => return error.AddressNotAvailable, | ||
| 1843 | EAFNOSUPPORT => return error.AddressFamilyNotSupported, | ||
| 1844 | EAGAIN => return error.SystemResources, | ||
| 1845 | EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | ||
| 1846 | EBADF => unreachable, // sockfd is not a valid open file descriptor. | ||
| 1847 | ECONNREFUSED => return error.ConnectionRefused, | ||
| 1848 | EFAULT => unreachable, // The socket structure address is outside the user's address space. | ||
| 1849 | EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately. | ||
| 1850 | EINTR => continue, | ||
| 1851 | EISCONN => unreachable, // The socket is already connected. | ||
| 1852 | ENETUNREACH => return error.NetworkUnreachable, | ||
| 1853 | ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 1854 | EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. | ||
| 1855 | ETIMEDOUT => return error.ConnectionTimedOut, | ||
| 1856 | } | ||
| 1857 | } | ||
| 1858 | } | ||
| 1859 | |||
| 1860 | /// Same as `connect` except it is for blocking socket file descriptors. | ||
| 1861 | /// It expects to receive EINPROGRESS`. | ||
| 1862 | pub fn connect_async(sockfd: i32, sockaddr: *const c_void, len: u32) ConnectError!void { | ||
| 1863 | while (true) { | ||
| 1864 | switch (system.getErrno(system.connect(sockfd, sockaddr, len))) { | ||
| 1865 | 0, EINPROGRESS => return, | ||
| 1866 | EINTR => continue, | ||
| 1867 | else => return unexpectedErrno(err), | ||
| 1868 | |||
| 1869 | EACCES => return error.PermissionDenied, | ||
| 1870 | EPERM => return error.PermissionDenied, | ||
| 1871 | EADDRINUSE => return error.AddressInUse, | ||
| 1872 | EADDRNOTAVAIL => return error.AddressNotAvailable, | ||
| 1873 | EAFNOSUPPORT => return error.AddressFamilyNotSupported, | ||
| 1874 | EAGAIN => return error.SystemResources, | ||
| 1875 | EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | ||
| 1876 | EBADF => unreachable, // sockfd is not a valid open file descriptor. | ||
| 1877 | ECONNREFUSED => return error.ConnectionRefused, | ||
| 1878 | EFAULT => unreachable, // The socket structure address is outside the user's address space. | ||
| 1879 | EISCONN => unreachable, // The socket is already connected. | ||
| 1880 | ENETUNREACH => return error.NetworkUnreachable, | ||
| 1881 | ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 1882 | EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. | ||
| 1883 | ETIMEDOUT => return error.ConnectionTimedOut, | ||
| 1884 | } | ||
| 1885 | } | ||
| 1886 | } | ||
| 1887 | |||
| 1888 | pub fn getsockoptError(sockfd: i32) ConnectError!void { | ||
| 1889 | var err_code: i32 = undefined; | ||
| 1890 | var size: u32 = @sizeOf(i32); | ||
| 1891 | const rc = system.getsockopt(sockfd, SOL_SOCKET, SO_ERROR, @ptrCast([*]u8, &err_code), &size); | ||
| 1892 | assert(size == 4); | ||
| 1893 | switch (system.getErrno(rc)) { | ||
| 1894 | 0 => switch (err_code) { | ||
| 1895 | 0 => return, | ||
| 1896 | EACCES => return error.PermissionDenied, | ||
| 1897 | EPERM => return error.PermissionDenied, | ||
| 1898 | EADDRINUSE => return error.AddressInUse, | ||
| 1899 | EADDRNOTAVAIL => return error.AddressNotAvailable, | ||
| 1900 | EAFNOSUPPORT => return error.AddressFamilyNotSupported, | ||
| 1901 | EAGAIN => return error.SystemResources, | ||
| 1902 | EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | ||
| 1903 | EBADF => unreachable, // sockfd is not a valid open file descriptor. | ||
| 1904 | ECONNREFUSED => return error.ConnectionRefused, | ||
| 1905 | EFAULT => unreachable, // The socket structure address is outside the user's address space. | ||
| 1906 | EISCONN => unreachable, // The socket is already connected. | ||
| 1907 | ENETUNREACH => return error.NetworkUnreachable, | ||
| 1908 | ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 1909 | EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. | ||
| 1910 | ETIMEDOUT => return error.ConnectionTimedOut, | ||
| 1911 | else => |err| return unexpectedErrno(err), | ||
| 1912 | }, | ||
| 1913 | EBADF => unreachable, // The argument sockfd is not a valid file descriptor. | ||
| 1914 | EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space. | ||
| 1915 | EINVAL => unreachable, | ||
| 1916 | ENOPROTOOPT => unreachable, // The option is unknown at the level indicated. | ||
| 1917 | ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 1918 | else => |err| return unexpectedErrno(err), | ||
| 1919 | } | ||
| 1920 | } | ||
| 1921 | |||
| 1922 | pub fn wait(pid: i32) i32 { | ||
| 1923 | var status: i32 = undefined; | ||
| 1924 | while (true) { | ||
| 1925 | switch (system.getErrno(system.waitpid(pid, &status, 0))) { | ||
| 1926 | 0 => return status, | ||
| 1927 | EINTR => continue, | ||
| 1928 | ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. | ||
| 1929 | EINVAL => unreachable, // The options argument was invalid | ||
| 1930 | else => unreachable, | ||
| 1931 | } | ||
| 1932 | } | ||
| 1933 | } | ||
| 1934 | |||
| 1935 | pub fn fstat(fd: FileHandle) !Stat { | ||
| 1936 | var stat: Stat = undefined; | ||
| 1937 | switch (system.getErrno(system.fstat(fd, &stat))) { | ||
| 1938 | 0 => return stat, | ||
| 1939 | EBADF => unreachable, // Always a race condition. | ||
| 1940 | ENOMEM => return error.SystemResources, | ||
| 1941 | else => |err| return unexpectedErrno(err), | ||
| 1942 | } | ||
| 1943 | |||
| 1944 | return stat; | ||
| 1945 | } | ||
| 1946 | |||
| 1947 | pub const KQueueError = error{ | ||
| 1948 | /// The per-process limit on the number of open file descriptors has been reached. | ||
| 1949 | ProcessFdQuotaExceeded, | ||
| 1950 | |||
| 1951 | /// The system-wide limit on the total number of open files has been reached. | ||
| 1952 | SystemFdQuotaExceeded, | ||
| 1953 | |||
| 1954 | Unexpected, | ||
| 1955 | }; | ||
| 1956 | |||
| 1957 | pub fn kqueue() KQueueError!i32 { | ||
| 1958 | const rc = system.kqueue(); | ||
| 1959 | switch (system.getErrno(rc)) { | ||
| 1960 | 0 => return @intCast(i32, rc), | ||
| 1961 | EMFILE => return error.ProcessFdQuotaExceeded, | ||
| 1962 | ENFILE => return error.SystemFdQuotaExceeded, | ||
| 1963 | else => |err| return unexpectedErrno(err), | ||
| 1964 | } | ||
| 1965 | } | ||
| 1966 | |||
| 1967 | pub const KEventError = error{ | ||
| 1968 | /// The process does not have permission to register a filter. | ||
| 1969 | AccessDenied, | ||
| 1970 | |||
| 1971 | /// The event could not be found to be modified or deleted. | ||
| 1972 | EventNotFound, | ||
| 1973 | |||
| 1974 | /// No memory was available to register the event. | ||
| 1975 | SystemResources, | ||
| 1976 | |||
| 1977 | /// The specified process to attach to does not exist. | ||
| 1978 | ProcessNotFound, | ||
| 1979 | }; | ||
| 1980 | |||
| 1981 | pub fn kevent( | ||
| 1982 | kq: i32, | ||
| 1983 | changelist: []const Kevent, | ||
| 1984 | eventlist: []Kevent, | ||
| 1985 | timeout: ?*const timespec, | ||
| 1986 | ) KEventError!usize { | ||
| 1987 | while (true) { | ||
| 1988 | const rc = system.kevent(kq, changelist, eventlist, timeout); | ||
| 1989 | switch (system.getErrno(rc)) { | ||
| 1990 | 0 => return rc, | ||
| 1991 | EACCES => return error.AccessDenied, | ||
| 1992 | EFAULT => unreachable, | ||
| 1993 | EBADF => unreachable, // Always a race condition. | ||
| 1994 | EINTR => continue, | ||
| 1995 | EINVAL => unreachable, | ||
| 1996 | ENOENT => return error.EventNotFound, | ||
| 1997 | ENOMEM => return error.SystemResources, | ||
| 1998 | ESRCH => return error.ProcessNotFound, | ||
| 1999 | else => unreachable, | ||
| 2000 | } | ||
| 2001 | } | ||
| 2002 | } | ||
| 2003 | |||
| 2004 | pub const INotifyInitError = error{ | ||
| 2005 | ProcessFdQuotaExceeded, | ||
| 2006 | SystemFdQuotaExceeded, | ||
| 2007 | SystemResources, | ||
| 2008 | Unexpected, | ||
| 2009 | }; | ||
| 2010 | |||
| 2011 | /// initialize an inotify instance | ||
| 2012 | pub fn inotify_init1(flags: u32) INotifyInitError!i32 { | ||
| 2013 | const rc = system.inotify_init1(flags); | ||
| 2014 | switch (system.getErrno(rc)) { | ||
| 2015 | 0 => return @intCast(i32, rc), | ||
| 2016 | EINVAL => unreachable, | ||
| 2017 | EMFILE => return error.ProcessFdQuotaExceeded, | ||
| 2018 | ENFILE => return error.SystemFdQuotaExceeded, | ||
| 2019 | ENOMEM => return error.SystemResources, | ||
| 2020 | else => |err| return unexpectedErrno(err), | ||
| 2021 | } | ||
| 2022 | } | ||
| 2023 | |||
| 2024 | pub const INotifyAddWatchError = error{ | ||
| 2025 | AccessDenied, | ||
| 2026 | NameTooLong, | ||
| 2027 | FileNotFound, | ||
| 2028 | SystemResources, | ||
| 2029 | UserResourceLimitReached, | ||
| 2030 | Unexpected, | ||
| 2031 | }; | ||
| 2032 | |||
| 2033 | /// add a watch to an initialized inotify instance | ||
| 2034 | pub fn inotify_add_watch(inotify_fd: i32, pathname: []const u8, mask: u32) INotifyAddWatchError!i32 { | ||
| 2035 | const pathname_c = try toPosixPath(pathname); | ||
| 2036 | return inotify_add_watchC(inotify_fd, &pathname_c, mask); | ||
| 2037 | } | ||
| 2038 | |||
| 2039 | /// Same as `inotify_add_watch` except pathname is null-terminated. | ||
| 2040 | pub fn inotify_add_watchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) INotifyAddWatchError!i32 { | ||
| 2041 | const rc = system.inotify_add_watch(inotify_fd, pathname, mask); | ||
| 2042 | switch (system.getErrno(rc)) { | ||
| 2043 | 0 => return @intCast(i32, rc), | ||
| 2044 | EACCES => return error.AccessDenied, | ||
| 2045 | EBADF => unreachable, | ||
| 2046 | EFAULT => unreachable, | ||
| 2047 | EINVAL => unreachable, | ||
| 2048 | ENAMETOOLONG => return error.NameTooLong, | ||
| 2049 | ENOENT => return error.FileNotFound, | ||
| 2050 | ENOMEM => return error.SystemResources, | ||
| 2051 | ENOSPC => return error.UserResourceLimitReached, | ||
| 2052 | else => |err| return unexpectedErrno(err), | ||
| 2053 | } | ||
| 2054 | } | ||
| 2055 | |||
| 2056 | /// remove an existing watch from an inotify instance | ||
| 2057 | pub fn inotify_rm_watch(inotify_fd: i32, wd: i32) void { | ||
| 2058 | switch (system.getErrno(system.inotify_rm_watch(inotify_fd, wd))) { | ||
| 2059 | 0 => return, | ||
| 2060 | EBADF => unreachable, | ||
| 2061 | EINVAL => unreachable, | ||
| 2062 | else => unreachable, | ||
| 2063 | } | ||
| 2064 | } | ||
| 2065 | |||
| 2066 | pub const MProtectError = error{ | ||
| 2067 | AccessDenied, | ||
| 2068 | OutOfMemory, | ||
| 2069 | Unexpected, | ||
| 2070 | }; | ||
| 2071 | |||
| 2072 | /// address and length must be page-aligned | ||
| 2073 | pub fn mprotect(address: usize, length: usize, protection: u32) MProtectError!void { | ||
| 2074 | const negative_page_size = @bitCast(usize, -isize(page_size)); | ||
| 2075 | const aligned_address = address & negative_page_size; | ||
| 2076 | const aligned_end = (address + length + page_size - 1) & negative_page_size; | ||
| 2077 | assert(address == aligned_address); | ||
| 2078 | assert(length == aligned_end - aligned_address); | ||
| 2079 | switch (system.getErrno(system.mprotect(address, length, protection))) { | ||
| 2080 | 0 => return, | ||
| 2081 | EINVAL => unreachable, | ||
| 2082 | EACCES => return error.AccessDenied, | ||
| 2083 | ENOMEM => return error.OutOfMemory, | ||
| 2084 | else => return unexpectedErrno(err), | ||
| 2085 | } | ||
| 2086 | } | ||
| 2087 | |||
| 2088 | /// Used to convert a slice to a null terminated slice on the stack. | ||
| 2089 | /// TODO https://github.com/ziglang/zig/issues/287 | ||
| 2090 | pub fn toPosixPath(file_path: []const u8) ![PATH_MAX]u8 { | ||
| 2091 | var path_with_null: [PATH_MAX]u8 = undefined; | ||
| 2092 | // >= rather than > to make room for the null byte | ||
| 2093 | if (file_path.len >= PATH_MAX) return error.NameTooLong; | ||
| 2094 | mem.copy(u8, &path_with_null, file_path); | ||
| 2095 | path_with_null[file_path.len] = 0; | ||
| 2096 | return path_with_null; | ||
| 2097 | } | ||
| 2098 | |||
| 2099 | const unexpected_error_tracing = builtin.mode == .Debug; | ||
| 2100 | const UnexpectedError = error{ | ||
| 2101 | /// The Operating System returned an undocumented error code. | ||
| 2102 | Unexpected, | ||
| 2103 | }; | ||
| 2104 | |||
| 2105 | /// Call this when you made a syscall or something that sets errno | ||
| 2106 | /// and you get an unexpected error. | ||
| 2107 | pub fn unexpectedErrno(errno: usize) UnexpectedError { | ||
| 2108 | if (unexpected_error_tracing) { | ||
| 2109 | std.debug.warn("unexpected errno: {}\n", errno); | ||
| 2110 | std.debug.dumpCurrentStackTrace(null); | ||
| 2111 | } | ||
| 2112 | return error.Unexpected; | ||
| 2113 | } | ||
| 2114 | |||
| 2115 | /// Call this when you made a windows DLL call or something that does SetLastError | ||
| 2116 | /// and you get an unexpected error. | ||
| 2117 | pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError { | ||
| 2118 | if (unexpected_error_tracing) { | ||
| 2119 | std.debug.warn("unexpected GetLastError(): {}\n", err); | ||
| 2120 | std.debug.dumpCurrentStackTrace(null); | ||
| 2121 | } | ||
| 2122 | return error.Unexpected; | ||
| 2123 | } | ||
| 2124 | |||
| 2125 | pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 { | ||
| 2126 | return sliceToPrefixedFileW(mem.toSliceConst(u8, s)); | ||
| 2127 | } | ||
| 2128 | |||
| 2129 | pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { | ||
| 2130 | return sliceToPrefixedSuffixedFileW(s, []u16{0}); | ||
| 2131 | } | ||
| 2132 | |||
| 2133 | pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 { | ||
| 2134 | // TODO well defined copy elision | ||
| 2135 | var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined; | ||
| 2136 | |||
| 2137 | // > File I/O functions in the Windows API convert "/" to "\" as part of | ||
| 2138 | // > converting the name to an NT-style name, except when using the "\\?\" | ||
| 2139 | // > prefix as detailed in the following sections. | ||
| 2140 | // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation | ||
| 2141 | // Because we want the larger maximum path length for absolute paths, we | ||
| 2142 | // disallow forward slashes in zig std lib file functions on Windows. | ||
| 2143 | for (s) |byte| { | ||
| 2144 | switch (byte) { | ||
| 2145 | '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName, | ||
| 2146 | else => {}, | ||
| 2147 | } | ||
| 2148 | } | ||
| 2149 | const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: { | ||
| 2150 | const prefix = []u16{ '\\', '\\', '?', '\\' }; | ||
| 2151 | mem.copy(u16, result[0..], prefix); | ||
| 2152 | break :blk prefix.len; | ||
| 2153 | }; | ||
| 2154 | const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s); | ||
| 2155 | assert(end_index <= result.len); | ||
| 2156 | if (end_index + suffix.len > result.len) return error.NameTooLong; | ||
| 2157 | mem.copy(u16, result[end_index..], suffix); | ||
| 2158 | return result; | ||
| 2159 | } | ||
std/os/wasi.zig+381-30| ... | @@ -1,42 +1,393 @@ | ... | @@ -1,42 +1,393 @@ |
| 1 | pub use @import("wasi/core.zig"); | 1 | // Based on https://github.com/CraneStation/wasi-sysroot/blob/wasi/libc-bottom-half/headers/public/wasi/core.h |
| 2 | // and https://github.com/WebAssembly/WASI/blob/master/design/WASI-core.md | ||
| 3 | const std = @import("std"); | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | |||
| 6 | pub const is_the_target = @import("builtin").os == .wasi; | ||
| 2 | 7 | ||
| 3 | pub const STDIN_FILENO = 0; | 8 | pub const STDIN_FILENO = 0; |
| 4 | pub const STDOUT_FILENO = 1; | 9 | pub const STDOUT_FILENO = 1; |
| 5 | pub const STDERR_FILENO = 2; | 10 | pub const STDERR_FILENO = 2; |
| 6 | 11 | ||
| 7 | pub fn getErrno(r: usize) usize { | 12 | comptime { |
| 8 | const signed_r = @bitCast(isize, r); | 13 | assert(@alignOf(i8) == 1); |
| 9 | return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; | 14 | assert(@alignOf(u8) == 1); |
| 15 | assert(@alignOf(i16) == 2); | ||
| 16 | assert(@alignOf(u16) == 2); | ||
| 17 | assert(@alignOf(i32) == 4); | ||
| 18 | assert(@alignOf(u32) == 4); | ||
| 19 | assert(@alignOf(i64) == 8); | ||
| 20 | assert(@alignOf(u64) == 8); | ||
| 10 | } | 21 | } |
| 11 | 22 | ||
| 12 | pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { | 23 | pub const advice_t = u8; |
| 13 | var nwritten: usize = undefined; | 24 | pub const ADVICE_NORMAL: advice_t = 0; |
| 25 | pub const ADVICE_SEQUENTIAL: advice_t = 1; | ||
| 26 | pub const ADVICE_RANDOM: advice_t = 2; | ||
| 27 | pub const ADVICE_WILLNEED: advice_t = 3; | ||
| 28 | pub const ADVICE_DONTNEED: advice_t = 4; | ||
| 29 | pub const ADVICE_NOREUSE: advice_t = 5; | ||
| 14 | 30 | ||
| 15 | const ciovs = ciovec_t{ | 31 | pub const ciovec_t = extern struct { |
| 16 | .buf = buf, | 32 | buf: [*]const u8, |
| 17 | .buf_len = count, | 33 | buf_len: usize, |
| 18 | }; | 34 | }; |
| 19 | 35 | ||
| 20 | const err = fd_write(@bitCast(fd_t, isize(fd)), &ciovs, 1, &nwritten); | 36 | pub const clockid_t = u32; |
| 21 | if (err == ESUCCESS) { | 37 | pub const CLOCK_REALTIME: clockid_t = 0; |
| 22 | return nwritten; | 38 | pub const CLOCK_MONOTONIC: clockid_t = 1; |
| 23 | } else { | 39 | pub const CLOCK_PROCESS_CPUTIME_ID: clockid_t = 2; |
| 24 | return @bitCast(usize, -isize(err)); | 40 | pub const CLOCK_THREAD_CPUTIME_ID: clockid_t = 3; |
| 25 | } | ||
| 26 | } | ||
| 27 | 41 | ||
| 28 | pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { | 42 | pub const device_t = u64; |
| 29 | var nread: usize = undefined; | ||
| 30 | 43 | ||
| 31 | const iovs = iovec_t{ | 44 | pub const dircookie_t = u64; |
| 32 | .buf = buf, | 45 | pub const DIRCOOKIE_START: dircookie_t = 0; |
| 33 | .buf_len = nbyte, | ||
| 34 | }; | ||
| 35 | 46 | ||
| 36 | const err = fd_read(@bitCast(fd_t, isize(fd)), &iovs, 1, &nread); | 47 | pub const dirent_t = extern struct { |
| 37 | if (err == ESUCCESS) { | 48 | d_next: dircookie_t, |
| 38 | return nread; | 49 | d_ino: inode_t, |
| 39 | } else { | 50 | d_namlen: u32, |
| 40 | return @bitCast(usize, -isize(err)); | 51 | d_type: filetype_t, |
| 41 | } | 52 | }; |
| 42 | } | 53 | |
| 54 | pub const errno_t = u16; | ||
| 55 | pub const ESUCCESS: errno_t = 0; | ||
| 56 | pub const E2BIG: errno_t = 1; | ||
| 57 | pub const EACCES: errno_t = 2; | ||
| 58 | pub const EADDRINUSE: errno_t = 3; | ||
| 59 | pub const EADDRNOTAVAIL: errno_t = 4; | ||
| 60 | pub const EAFNOSUPPORT: errno_t = 5; | ||
| 61 | pub const EAGAIN: errno_t = 6; | ||
| 62 | pub const EALREADY: errno_t = 7; | ||
| 63 | pub const EBADF: errno_t = 8; | ||
| 64 | pub const EBADMSG: errno_t = 9; | ||
| 65 | pub const EBUSY: errno_t = 10; | ||
| 66 | pub const ECANCELED: errno_t = 11; | ||
| 67 | pub const ECHILD: errno_t = 12; | ||
| 68 | pub const ECONNABORTED: errno_t = 13; | ||
| 69 | pub const ECONNREFUSED: errno_t = 14; | ||
| 70 | pub const ECONNRESET: errno_t = 15; | ||
| 71 | pub const EDEADLK: errno_t = 16; | ||
| 72 | pub const EDESTADDRREQ: errno_t = 17; | ||
| 73 | pub const EDOM: errno_t = 18; | ||
| 74 | pub const EDQUOT: errno_t = 19; | ||
| 75 | pub const EEXIST: errno_t = 20; | ||
| 76 | pub const EFAULT: errno_t = 21; | ||
| 77 | pub const EFBIG: errno_t = 22; | ||
| 78 | pub const EHOSTUNREACH: errno_t = 23; | ||
| 79 | pub const EIDRM: errno_t = 24; | ||
| 80 | pub const EILSEQ: errno_t = 25; | ||
| 81 | pub const EINPROGRESS: errno_t = 26; | ||
| 82 | pub const EINTR: errno_t = 27; | ||
| 83 | pub const EINVAL: errno_t = 28; | ||
| 84 | pub const EIO: errno_t = 29; | ||
| 85 | pub const EISCONN: errno_t = 30; | ||
| 86 | pub const EISDIR: errno_t = 31; | ||
| 87 | pub const ELOOP: errno_t = 32; | ||
| 88 | pub const EMFILE: errno_t = 33; | ||
| 89 | pub const EMLINK: errno_t = 34; | ||
| 90 | pub const EMSGSIZE: errno_t = 35; | ||
| 91 | pub const EMULTIHOP: errno_t = 36; | ||
| 92 | pub const ENAMETOOLONG: errno_t = 37; | ||
| 93 | pub const ENETDOWN: errno_t = 38; | ||
| 94 | pub const ENETRESET: errno_t = 39; | ||
| 95 | pub const ENETUNREACH: errno_t = 40; | ||
| 96 | pub const ENFILE: errno_t = 41; | ||
| 97 | pub const ENOBUFS: errno_t = 42; | ||
| 98 | pub const ENODEV: errno_t = 43; | ||
| 99 | pub const ENOENT: errno_t = 44; | ||
| 100 | pub const ENOEXEC: errno_t = 45; | ||
| 101 | pub const ENOLCK: errno_t = 46; | ||
| 102 | pub const ENOLINK: errno_t = 47; | ||
| 103 | pub const ENOMEM: errno_t = 48; | ||
| 104 | pub const ENOMSG: errno_t = 49; | ||
| 105 | pub const ENOPROTOOPT: errno_t = 50; | ||
| 106 | pub const ENOSPC: errno_t = 51; | ||
| 107 | pub const ENOSYS: errno_t = 52; | ||
| 108 | pub const ENOTCONN: errno_t = 53; | ||
| 109 | pub const ENOTDIR: errno_t = 54; | ||
| 110 | pub const ENOTEMPTY: errno_t = 55; | ||
| 111 | pub const ENOTRECOVERABLE: errno_t = 56; | ||
| 112 | pub const ENOTSOCK: errno_t = 57; | ||
| 113 | pub const ENOTSUP: errno_t = 58; | ||
| 114 | pub const ENOTTY: errno_t = 59; | ||
| 115 | pub const ENXIO: errno_t = 60; | ||
| 116 | pub const EOVERFLOW: errno_t = 61; | ||
| 117 | pub const EOWNERDEAD: errno_t = 62; | ||
| 118 | pub const EPERM: errno_t = 63; | ||
| 119 | pub const EPIPE: errno_t = 64; | ||
| 120 | pub const EPROTO: errno_t = 65; | ||
| 121 | pub const EPROTONOSUPPORT: errno_t = 66; | ||
| 122 | pub const EPROTOTYPE: errno_t = 67; | ||
| 123 | pub const ERANGE: errno_t = 68; | ||
| 124 | pub const EROFS: errno_t = 69; | ||
| 125 | pub const ESPIPE: errno_t = 70; | ||
| 126 | pub const ESRCH: errno_t = 71; | ||
| 127 | pub const ESTALE: errno_t = 72; | ||
| 128 | pub const ETIMEDOUT: errno_t = 73; | ||
| 129 | pub const ETXTBSY: errno_t = 74; | ||
| 130 | pub const EXDEV: errno_t = 75; | ||
| 131 | pub const ENOTCAPABLE: errno_t = 76; | ||
| 132 | |||
| 133 | pub const event_t = extern struct { | ||
| 134 | userdata: userdata_t, | ||
| 135 | @"error": errno_t, | ||
| 136 | @"type": eventtype_t, | ||
| 137 | u: extern union { | ||
| 138 | fd_readwrite: extern struct { | ||
| 139 | nbytes: filesize_t, | ||
| 140 | flags: eventrwflags_t, | ||
| 141 | }, | ||
| 142 | }, | ||
| 143 | }; | ||
| 144 | |||
| 145 | pub const eventrwflags_t = u16; | ||
| 146 | pub const EVENT_FD_READWRITE_HANGUP: eventrwflags_t = 0x0001; | ||
| 147 | |||
| 148 | pub const eventtype_t = u8; | ||
| 149 | pub const EVENTTYPE_CLOCK: eventtype_t = 0; | ||
| 150 | pub const EVENTTYPE_FD_READ: eventtype_t = 1; | ||
| 151 | pub const EVENTTYPE_FD_WRITE: eventtype_t = 2; | ||
| 152 | |||
| 153 | pub const exitcode_t = u32; | ||
| 154 | |||
| 155 | pub const fd_t = u32; | ||
| 156 | |||
| 157 | pub const fdflags_t = u16; | ||
| 158 | pub const FDFLAG_APPEND: fdflags_t = 0x0001; | ||
| 159 | pub const FDFLAG_DSYNC: fdflags_t = 0x0002; | ||
| 160 | pub const FDFLAG_NONBLOCK: fdflags_t = 0x0004; | ||
| 161 | pub const FDFLAG_RSYNC: fdflags_t = 0x0008; | ||
| 162 | pub const FDFLAG_SYNC: fdflags_t = 0x0010; | ||
| 163 | |||
| 164 | const fdstat_t = extern struct { | ||
| 165 | fs_filetype: filetype_t, | ||
| 166 | fs_flags: fdflags_t, | ||
| 167 | fs_rights_base: rights_t, | ||
| 168 | fs_rights_inheriting: rights_t, | ||
| 169 | }; | ||
| 170 | |||
| 171 | pub const filedelta_t = i64; | ||
| 172 | |||
| 173 | pub const filesize_t = u64; | ||
| 174 | |||
| 175 | pub const filestat_t = extern struct { | ||
| 176 | st_dev: device_t, | ||
| 177 | st_ino: inode_t, | ||
| 178 | st_filetype: filetype_t, | ||
| 179 | st_nlink: linkcount_t, | ||
| 180 | st_size: filesize_t, | ||
| 181 | st_atim: timestamp_t, | ||
| 182 | st_mtim: timestamp_t, | ||
| 183 | st_ctim: timestamp_t, | ||
| 184 | }; | ||
| 185 | |||
| 186 | pub const filetype_t = u8; | ||
| 187 | pub const FILETYPE_UNKNOWN: filetype_t = 0; | ||
| 188 | pub const FILETYPE_BLOCK_DEVICE: filetype_t = 1; | ||
| 189 | pub const FILETYPE_CHARACTER_DEVICE: filetype_t = 2; | ||
| 190 | pub const FILETYPE_DIRECTORY: filetype_t = 3; | ||
| 191 | pub const FILETYPE_REGULAR_FILE: filetype_t = 4; | ||
| 192 | pub const FILETYPE_SOCKET_DGRAM: filetype_t = 5; | ||
| 193 | pub const FILETYPE_SOCKET_STREAM: filetype_t = 6; | ||
| 194 | pub const FILETYPE_SYMBOLIC_LINK: filetype_t = 7; | ||
| 195 | |||
| 196 | pub const fstflags_t = u16; | ||
| 197 | pub const FILESTAT_SET_ATIM: fstflags_t = 0x0001; | ||
| 198 | pub const FILESTAT_SET_ATIM_NOW: fstflags_t = 0x0002; | ||
| 199 | pub const FILESTAT_SET_MTIM: fstflags_t = 0x0004; | ||
| 200 | pub const FILESTAT_SET_MTIM_NOW: fstflags_t = 0x0008; | ||
| 201 | |||
| 202 | pub const inode_t = u64; | ||
| 203 | |||
| 204 | pub const iovec_t = extern struct { | ||
| 205 | buf: [*]u8, | ||
| 206 | buf_len: usize, | ||
| 207 | }; | ||
| 208 | |||
| 209 | pub const linkcount_t = u32; | ||
| 210 | |||
| 211 | pub const lookupflags_t = u32; | ||
| 212 | pub const LOOKUP_SYMLINK_FOLLOW: lookupflags_t = 0x00000001; | ||
| 213 | |||
| 214 | pub const oflags_t = u16; | ||
| 215 | pub const O_CREAT: oflags_t = 0x0001; | ||
| 216 | pub const O_DIRECTORY: oflags_t = 0x0002; | ||
| 217 | pub const O_EXCL: oflags_t = 0x0004; | ||
| 218 | pub const O_TRUNC: oflags_t = 0x0008; | ||
| 219 | |||
| 220 | pub const preopentype_t = u8; | ||
| 221 | pub const PREOPENTYPE_DIR: preopentype_t = 0; | ||
| 222 | |||
| 223 | pub const prestat_t = extern struct { | ||
| 224 | pr_type: preopentype_t, | ||
| 225 | u: extern union { | ||
| 226 | dir: extern struct { | ||
| 227 | pr_name_len: usize, | ||
| 228 | }, | ||
| 229 | }, | ||
| 230 | }; | ||
| 231 | |||
| 232 | pub const riflags_t = u16; | ||
| 233 | pub const SOCK_RECV_PEEK: riflags_t = 0x0001; | ||
| 234 | pub const SOCK_RECV_WAITALL: riflags_t = 0x0002; | ||
| 235 | |||
| 236 | pub const rights_t = u64; | ||
| 237 | pub const RIGHT_FD_DATASYNC: rights_t = 0x0000000000000001; | ||
| 238 | pub const RIGHT_FD_READ: rights_t = 0x0000000000000002; | ||
| 239 | pub const RIGHT_FD_SEEK: rights_t = 0x0000000000000004; | ||
| 240 | pub const RIGHT_FD_FDSTAT_SET_FLAGS: rights_t = 0x0000000000000008; | ||
| 241 | pub const RIGHT_FD_SYNC: rights_t = 0x0000000000000010; | ||
| 242 | pub const RIGHT_FD_TELL: rights_t = 0x0000000000000020; | ||
| 243 | pub const RIGHT_FD_WRITE: rights_t = 0x0000000000000040; | ||
| 244 | pub const RIGHT_FD_ADVISE: rights_t = 0x0000000000000080; | ||
| 245 | pub const RIGHT_FD_ALLOCATE: rights_t = 0x0000000000000100; | ||
| 246 | pub const RIGHT_PATH_CREATE_DIRECTORY: rights_t = 0x0000000000000200; | ||
| 247 | pub const RIGHT_PATH_CREATE_FILE: rights_t = 0x0000000000000400; | ||
| 248 | pub const RIGHT_PATH_LINK_SOURCE: rights_t = 0x0000000000000800; | ||
| 249 | pub const RIGHT_PATH_LINK_TARGET: rights_t = 0x0000000000001000; | ||
| 250 | pub const RIGHT_PATH_OPEN: rights_t = 0x0000000000002000; | ||
| 251 | pub const RIGHT_FD_READDIR: rights_t = 0x0000000000004000; | ||
| 252 | pub const RIGHT_PATH_READLINK: rights_t = 0x0000000000008000; | ||
| 253 | pub const RIGHT_PATH_RENAME_SOURCE: rights_t = 0x0000000000010000; | ||
| 254 | pub const RIGHT_PATH_RENAME_TARGET: rights_t = 0x0000000000020000; | ||
| 255 | pub const RIGHT_PATH_FILESTAT_GET: rights_t = 0x0000000000040000; | ||
| 256 | pub const RIGHT_PATH_FILESTAT_SET_SIZE: rights_t = 0x0000000000080000; | ||
| 257 | pub const RIGHT_PATH_FILESTAT_SET_TIMES: rights_t = 0x0000000000100000; | ||
| 258 | pub const RIGHT_FD_FILESTAT_GET: rights_t = 0x0000000000200000; | ||
| 259 | pub const RIGHT_FD_FILESTAT_SET_SIZE: rights_t = 0x0000000000400000; | ||
| 260 | pub const RIGHT_FD_FILESTAT_SET_TIMES: rights_t = 0x0000000000800000; | ||
| 261 | pub const RIGHT_PATH_SYMLINK: rights_t = 0x0000000001000000; | ||
| 262 | pub const RIGHT_PATH_REMOVE_DIRECTORY: rights_t = 0x0000000002000000; | ||
| 263 | pub const RIGHT_PATH_UNLINK_FILE: rights_t = 0x0000000004000000; | ||
| 264 | pub const RIGHT_POLL_FD_READWRITE: rights_t = 0x0000000008000000; | ||
| 265 | pub const RIGHT_SOCK_SHUTDOWN: rights_t = 0x0000000010000000; | ||
| 266 | |||
| 267 | pub const roflags_t = u16; | ||
| 268 | pub const SOCK_RECV_DATA_TRUNCATED: roflags_t = 0x0001; | ||
| 269 | |||
| 270 | pub const sdflags_t = u8; | ||
| 271 | pub const SHUT_RD: sdflags_t = 0x01; | ||
| 272 | pub const SHUT_WR: sdflags_t = 0x02; | ||
| 273 | |||
| 274 | pub const siflags_t = u16; | ||
| 275 | |||
| 276 | pub const signal_t = u8; | ||
| 277 | pub const SIGHUP: signal_t = 1; | ||
| 278 | pub const SIGINT: signal_t = 2; | ||
| 279 | pub const SIGQUIT: signal_t = 3; | ||
| 280 | pub const SIGILL: signal_t = 4; | ||
| 281 | pub const SIGTRAP: signal_t = 5; | ||
| 282 | pub const SIGABRT: signal_t = 6; | ||
| 283 | pub const SIGBUS: signal_t = 7; | ||
| 284 | pub const SIGFPE: signal_t = 8; | ||
| 285 | pub const SIGKILL: signal_t = 9; | ||
| 286 | pub const SIGUSR1: signal_t = 10; | ||
| 287 | pub const SIGSEGV: signal_t = 11; | ||
| 288 | pub const SIGUSR2: signal_t = 12; | ||
| 289 | pub const SIGPIPE: signal_t = 13; | ||
| 290 | pub const SIGALRM: signal_t = 14; | ||
| 291 | pub const SIGTERM: signal_t = 15; | ||
| 292 | pub const SIGCHLD: signal_t = 16; | ||
| 293 | pub const SIGCONT: signal_t = 17; | ||
| 294 | pub const SIGSTOP: signal_t = 18; | ||
| 295 | pub const SIGTSTP: signal_t = 19; | ||
| 296 | pub const SIGTTIN: signal_t = 20; | ||
| 297 | pub const SIGTTOU: signal_t = 21; | ||
| 298 | pub const SIGURG: signal_t = 22; | ||
| 299 | pub const SIGXCPU: signal_t = 23; | ||
| 300 | pub const SIGXFSZ: signal_t = 24; | ||
| 301 | pub const SIGVTALRM: signal_t = 25; | ||
| 302 | pub const SIGPROF: signal_t = 26; | ||
| 303 | pub const SIGWINCH: signal_t = 27; | ||
| 304 | pub const SIGPOLL: signal_t = 28; | ||
| 305 | pub const SIGPWR: signal_t = 29; | ||
| 306 | pub const SIGSYS: signal_t = 30; | ||
| 307 | |||
| 308 | pub const subclockflags_t = u16; | ||
| 309 | pub const SUBSCRIPTION_CLOCK_ABSTIME: subclockflags_t = 0x0001; | ||
| 310 | |||
| 311 | pub const subscription_t = extern struct { | ||
| 312 | userdata: userdata_t, | ||
| 313 | @"type": eventtype_t, | ||
| 314 | u: extern union { | ||
| 315 | clock: extern struct { | ||
| 316 | identifier: userdata_t, | ||
| 317 | clock_id: clockid_t, | ||
| 318 | timeout: timestamp_t, | ||
| 319 | precision: timestamp_t, | ||
| 320 | flags: subclockflags_t, | ||
| 321 | }, | ||
| 322 | fd_readwrite: extern struct { | ||
| 323 | fd: fd_t, | ||
| 324 | }, | ||
| 325 | }, | ||
| 326 | }; | ||
| 327 | |||
| 328 | pub const timestamp_t = u64; | ||
| 329 | |||
| 330 | pub const userdata_t = u64; | ||
| 331 | |||
| 332 | pub const whence_t = u8; | ||
| 333 | pub const WHENCE_CUR: whence_t = 0; | ||
| 334 | pub const WHENCE_END: whence_t = 1; | ||
| 335 | pub const WHENCE_SET: whence_t = 2; | ||
| 336 | |||
| 337 | pub extern "wasi_unstable" fn args_get(argv: [*][*]u8, argv_buf: [*]u8) errno_t; | ||
| 338 | pub extern "wasi_unstable" fn args_sizes_get(argc: *usize, argv_buf_size: *usize) errno_t; | ||
| 339 | |||
| 340 | pub extern "wasi_unstable" fn clock_res_get(clock_id: clockid_t, resolution: *timestamp_t) errno_t; | ||
| 341 | pub extern "wasi_unstable" fn clock_time_get(clock_id: clockid_t, precision: timestamp_t, timestamp: *timestamp_t) errno_t; | ||
| 342 | |||
| 343 | pub extern "wasi_unstable" fn environ_get(environ: [*]?[*]u8, environ_buf: [*]u8) errno_t; | ||
| 344 | pub extern "wasi_unstable" fn environ_sizes_get(environ_count: *usize, environ_buf_size: *usize) errno_t; | ||
| 345 | |||
| 346 | pub extern "wasi_unstable" fn fd_advise(fd: fd_t, offset: filesize_t, len: filesize_t, advice: advice_t) errno_t; | ||
| 347 | pub extern "wasi_unstable" fn fd_allocate(fd: fd_t, offset: filesize_t, len: filesize_t) errno_t; | ||
| 348 | pub extern "wasi_unstable" fn fd_close(fd: fd_t) errno_t; | ||
| 349 | pub extern "wasi_unstable" fn fd_datasync(fd: fd_t) errno_t; | ||
| 350 | pub extern "wasi_unstable" fn fd_pread(fd: fd_t, iovs: [*]const iovec_t, iovs_len: usize, offset: filesize_t, nread: *usize) errno_t; | ||
| 351 | pub extern "wasi_unstable" fn fd_pwrite(fd: fd_t, iovs: [*]const ciovec_t, iovs_len: usize, offset: filesize_t, nwritten: *usize) errno_t; | ||
| 352 | pub extern "wasi_unstable" fn fd_read(fd: fd_t, iovs: [*]const iovec_t, iovs_len: usize, nread: *usize) errno_t; | ||
| 353 | pub extern "wasi_unstable" fn fd_readdir(fd: fd_t, buf: [*]u8, buf_len: usize, cookie: dircookie_t, bufused: *usize) errno_t; | ||
| 354 | pub extern "wasi_unstable" fn fd_renumber(from: fd_t, to: fd_t) errno_t; | ||
| 355 | pub extern "wasi_unstable" fn fd_seek(fd: fd_t, offset: filedelta_t, whence: whence_t, newoffset: *filesize_t) errno_t; | ||
| 356 | pub extern "wasi_unstable" fn fd_sync(fd: fd_t) errno_t; | ||
| 357 | pub extern "wasi_unstable" fn fd_tell(fd: fd_t, newoffset: *filesize_t) errno_t; | ||
| 358 | pub extern "wasi_unstable" fn fd_write(fd: fd_t, iovs: [*]const ciovec_t, iovs_len: usize, nwritten: *usize) errno_t; | ||
| 359 | |||
| 360 | pub extern "wasi_unstable" fn fd_fdstat_get(fd: fd_t, buf: *fdstat_t) errno_t; | ||
| 361 | pub extern "wasi_unstable" fn fd_fdstat_set_flags(fd: fd_t, flags: fdflags_t) errno_t; | ||
| 362 | pub extern "wasi_unstable" fn fd_fdstat_set_rights(fd: fd_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t) errno_t; | ||
| 363 | |||
| 364 | pub extern "wasi_unstable" fn fd_filestat_get(fd: fd_t, buf: *filestat_t) errno_t; | ||
| 365 | pub extern "wasi_unstable" fn fd_filestat_set_size(fd: fd_t, st_size: filesize_t) errno_t; | ||
| 366 | pub extern "wasi_unstable" fn fd_filestat_set_times(fd: fd_t, st_atim: timestamp_t, st_mtim: timestamp_t, fstflags: fstflags_t) errno_t; | ||
| 367 | |||
| 368 | pub extern "wasi_unstable" fn fd_prestat_get(fd: fd_t, buf: *prestat_t) errno_t; | ||
| 369 | pub extern "wasi_unstable" fn fd_prestat_dir_name(fd: fd_t, path: [*]u8, path_len: usize) errno_t; | ||
| 370 | |||
| 371 | pub extern "wasi_unstable" fn path_create_directory(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; | ||
| 372 | pub extern "wasi_unstable" fn path_filestat_get(fd: fd_t, flags: lookupflags_t, path: [*]const u8, path_len: usize, buf: *filestat_t) errno_t; | ||
| 373 | pub extern "wasi_unstable" fn path_filestat_set_times(fd: fd_t, flags: lookupflags_t, path: [*]const u8, path_len: usize, st_atim: timestamp_t, st_mtim: timestamp_t, fstflags: fstflags_t) errno_t; | ||
| 374 | pub extern "wasi_unstable" fn path_link(old_fd: fd_t, old_flags: lookupflags_t, old_path: [*]const u8, old_path_len: usize, new_fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; | ||
| 375 | pub extern "wasi_unstable" fn path_open(dirfd: fd_t, dirflags: lookupflags_t, path: [*]const u8, path_len: usize, oflags: oflags_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t, fs_flags: fdflags_t, fd: *fd_t) errno_t; | ||
| 376 | pub extern "wasi_unstable" fn path_readlink(fd: fd_t, path: [*]const u8, path_len: usize, buf: [*]u8, buf_len: usize, bufused: *usize) errno_t; | ||
| 377 | pub extern "wasi_unstable" fn path_remove_directory(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; | ||
| 378 | pub extern "wasi_unstable" fn path_rename(old_fd: fd_t, old_path: [*]const u8, old_path_len: usize, new_fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; | ||
| 379 | pub extern "wasi_unstable" fn path_symlink(old_path: [*]const u8, old_path_len: usize, fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; | ||
| 380 | pub extern "wasi_unstable" fn path_unlink_file(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; | ||
| 381 | |||
| 382 | pub extern "wasi_unstable" fn poll_oneoff(in: *const subscription_t, out: *event_t, nsubscriptions: usize, nevents: *usize) errno_t; | ||
| 383 | |||
| 384 | pub extern "wasi_unstable" fn proc_exit(rval: exitcode_t) noreturn; | ||
| 385 | pub extern "wasi_unstable" fn proc_raise(sig: signal_t) errno_t; | ||
| 386 | |||
| 387 | pub extern "wasi_unstable" fn random_get(buf: [*]u8, buf_len: usize) errno_t; | ||
| 388 | |||
| 389 | pub extern "wasi_unstable" fn sched_yield() errno_t; | ||
| 390 | |||
| 391 | pub extern "wasi_unstable" fn sock_recv(sock: fd_t, ri_data: *const iovec_t, ri_data_len: usize, ri_flags: riflags_t, ro_datalen: *usize, ro_flags: *roflags_t) errno_t; | ||
| 392 | pub extern "wasi_unstable" fn sock_send(sock: fd_t, si_data: *const ciovec_t, si_data_len: usize, si_flags: siflags_t, so_datalen: *usize) errno_t; | ||
| 393 | pub extern "wasi_unstable" fn sock_shutdown(sock: fd_t, how: sdflags_t) errno_t; |
std/os/wasi/core.zig deleted-374| ... | @@ -1,374 +0,0 @@ | ||
| 1 | // Based on https://github.com/CraneStation/wasi-sysroot/blob/wasi/libc-bottom-half/headers/public/wasi/core.h | ||
| 2 | // and https://github.com/WebAssembly/WASI/blob/master/design/WASI-core.md | ||
| 3 | |||
| 4 | pub const advice_t = u8; | ||
| 5 | pub const ADVICE_NORMAL: advice_t = 0; | ||
| 6 | pub const ADVICE_SEQUENTIAL: advice_t = 1; | ||
| 7 | pub const ADVICE_RANDOM: advice_t = 2; | ||
| 8 | pub const ADVICE_WILLNEED: advice_t = 3; | ||
| 9 | pub const ADVICE_DONTNEED: advice_t = 4; | ||
| 10 | pub const ADVICE_NOREUSE: advice_t = 5; | ||
| 11 | |||
| 12 | pub const ciovec_t = extern struct { | ||
| 13 | buf: [*]const u8, | ||
| 14 | buf_len: usize, | ||
| 15 | }; | ||
| 16 | |||
| 17 | pub const clockid_t = u32; | ||
| 18 | pub const CLOCK_REALTIME: clockid_t = 0; | ||
| 19 | pub const CLOCK_MONOTONIC: clockid_t = 1; | ||
| 20 | pub const CLOCK_PROCESS_CPUTIME_ID: clockid_t = 2; | ||
| 21 | pub const CLOCK_THREAD_CPUTIME_ID: clockid_t = 3; | ||
| 22 | |||
| 23 | pub const device_t = u64; | ||
| 24 | |||
| 25 | pub const dircookie_t = u64; | ||
| 26 | pub const DIRCOOKIE_START: dircookie_t = 0; | ||
| 27 | |||
| 28 | pub const dirent_t = extern struct { | ||
| 29 | d_next: dircookie_t, | ||
| 30 | d_ino: inode_t, | ||
| 31 | d_namlen: u32, | ||
| 32 | d_type: filetype_t, | ||
| 33 | }; | ||
| 34 | |||
| 35 | pub const errno_t = u16; | ||
| 36 | pub const ESUCCESS: errno_t = 0; | ||
| 37 | pub const E2BIG: errno_t = 1; | ||
| 38 | pub const EACCES: errno_t = 2; | ||
| 39 | pub const EADDRINUSE: errno_t = 3; | ||
| 40 | pub const EADDRNOTAVAIL: errno_t = 4; | ||
| 41 | pub const EAFNOSUPPORT: errno_t = 5; | ||
| 42 | pub const EAGAIN: errno_t = 6; | ||
| 43 | pub const EALREADY: errno_t = 7; | ||
| 44 | pub const EBADF: errno_t = 8; | ||
| 45 | pub const EBADMSG: errno_t = 9; | ||
| 46 | pub const EBUSY: errno_t = 10; | ||
| 47 | pub const ECANCELED: errno_t = 11; | ||
| 48 | pub const ECHILD: errno_t = 12; | ||
| 49 | pub const ECONNABORTED: errno_t = 13; | ||
| 50 | pub const ECONNREFUSED: errno_t = 14; | ||
| 51 | pub const ECONNRESET: errno_t = 15; | ||
| 52 | pub const EDEADLK: errno_t = 16; | ||
| 53 | pub const EDESTADDRREQ: errno_t = 17; | ||
| 54 | pub const EDOM: errno_t = 18; | ||
| 55 | pub const EDQUOT: errno_t = 19; | ||
| 56 | pub const EEXIST: errno_t = 20; | ||
| 57 | pub const EFAULT: errno_t = 21; | ||
| 58 | pub const EFBIG: errno_t = 22; | ||
| 59 | pub const EHOSTUNREACH: errno_t = 23; | ||
| 60 | pub const EIDRM: errno_t = 24; | ||
| 61 | pub const EILSEQ: errno_t = 25; | ||
| 62 | pub const EINPROGRESS: errno_t = 26; | ||
| 63 | pub const EINTR: errno_t = 27; | ||
| 64 | pub const EINVAL: errno_t = 28; | ||
| 65 | pub const EIO: errno_t = 29; | ||
| 66 | pub const EISCONN: errno_t = 30; | ||
| 67 | pub const EISDIR: errno_t = 31; | ||
| 68 | pub const ELOOP: errno_t = 32; | ||
| 69 | pub const EMFILE: errno_t = 33; | ||
| 70 | pub const EMLINK: errno_t = 34; | ||
| 71 | pub const EMSGSIZE: errno_t = 35; | ||
| 72 | pub const EMULTIHOP: errno_t = 36; | ||
| 73 | pub const ENAMETOOLONG: errno_t = 37; | ||
| 74 | pub const ENETDOWN: errno_t = 38; | ||
| 75 | pub const ENETRESET: errno_t = 39; | ||
| 76 | pub const ENETUNREACH: errno_t = 40; | ||
| 77 | pub const ENFILE: errno_t = 41; | ||
| 78 | pub const ENOBUFS: errno_t = 42; | ||
| 79 | pub const ENODEV: errno_t = 43; | ||
| 80 | pub const ENOENT: errno_t = 44; | ||
| 81 | pub const ENOEXEC: errno_t = 45; | ||
| 82 | pub const ENOLCK: errno_t = 46; | ||
| 83 | pub const ENOLINK: errno_t = 47; | ||
| 84 | pub const ENOMEM: errno_t = 48; | ||
| 85 | pub const ENOMSG: errno_t = 49; | ||
| 86 | pub const ENOPROTOOPT: errno_t = 50; | ||
| 87 | pub const ENOSPC: errno_t = 51; | ||
| 88 | pub const ENOSYS: errno_t = 52; | ||
| 89 | pub const ENOTCONN: errno_t = 53; | ||
| 90 | pub const ENOTDIR: errno_t = 54; | ||
| 91 | pub const ENOTEMPTY: errno_t = 55; | ||
| 92 | pub const ENOTRECOVERABLE: errno_t = 56; | ||
| 93 | pub const ENOTSOCK: errno_t = 57; | ||
| 94 | pub const ENOTSUP: errno_t = 58; | ||
| 95 | pub const ENOTTY: errno_t = 59; | ||
| 96 | pub const ENXIO: errno_t = 60; | ||
| 97 | pub const EOVERFLOW: errno_t = 61; | ||
| 98 | pub const EOWNERDEAD: errno_t = 62; | ||
| 99 | pub const EPERM: errno_t = 63; | ||
| 100 | pub const EPIPE: errno_t = 64; | ||
| 101 | pub const EPROTO: errno_t = 65; | ||
| 102 | pub const EPROTONOSUPPORT: errno_t = 66; | ||
| 103 | pub const EPROTOTYPE: errno_t = 67; | ||
| 104 | pub const ERANGE: errno_t = 68; | ||
| 105 | pub const EROFS: errno_t = 69; | ||
| 106 | pub const ESPIPE: errno_t = 70; | ||
| 107 | pub const ESRCH: errno_t = 71; | ||
| 108 | pub const ESTALE: errno_t = 72; | ||
| 109 | pub const ETIMEDOUT: errno_t = 73; | ||
| 110 | pub const ETXTBSY: errno_t = 74; | ||
| 111 | pub const EXDEV: errno_t = 75; | ||
| 112 | pub const ENOTCAPABLE: errno_t = 76; | ||
| 113 | |||
| 114 | pub const event_t = extern struct { | ||
| 115 | userdata: userdata_t, | ||
| 116 | @"error": errno_t, | ||
| 117 | @"type": eventtype_t, | ||
| 118 | u: extern union { | ||
| 119 | fd_readwrite: extern struct { | ||
| 120 | nbytes: filesize_t, | ||
| 121 | flags: eventrwflags_t, | ||
| 122 | }, | ||
| 123 | }, | ||
| 124 | }; | ||
| 125 | |||
| 126 | pub const eventrwflags_t = u16; | ||
| 127 | pub const EVENT_FD_READWRITE_HANGUP: eventrwflags_t = 0x0001; | ||
| 128 | |||
| 129 | pub const eventtype_t = u8; | ||
| 130 | pub const EVENTTYPE_CLOCK: eventtype_t = 0; | ||
| 131 | pub const EVENTTYPE_FD_READ: eventtype_t = 1; | ||
| 132 | pub const EVENTTYPE_FD_WRITE: eventtype_t = 2; | ||
| 133 | |||
| 134 | pub const exitcode_t = u32; | ||
| 135 | |||
| 136 | pub const fd_t = u32; | ||
| 137 | |||
| 138 | pub const fdflags_t = u16; | ||
| 139 | pub const FDFLAG_APPEND: fdflags_t = 0x0001; | ||
| 140 | pub const FDFLAG_DSYNC: fdflags_t = 0x0002; | ||
| 141 | pub const FDFLAG_NONBLOCK: fdflags_t = 0x0004; | ||
| 142 | pub const FDFLAG_RSYNC: fdflags_t = 0x0008; | ||
| 143 | pub const FDFLAG_SYNC: fdflags_t = 0x0010; | ||
| 144 | |||
| 145 | const fdstat_t = extern struct { | ||
| 146 | fs_filetype: filetype_t, | ||
| 147 | fs_flags: fdflags_t, | ||
| 148 | fs_rights_base: rights_t, | ||
| 149 | fs_rights_inheriting: rights_t, | ||
| 150 | }; | ||
| 151 | |||
| 152 | pub const filedelta_t = i64; | ||
| 153 | |||
| 154 | pub const filesize_t = u64; | ||
| 155 | |||
| 156 | pub const filestat_t = extern struct { | ||
| 157 | st_dev: device_t, | ||
| 158 | st_ino: inode_t, | ||
| 159 | st_filetype: filetype_t, | ||
| 160 | st_nlink: linkcount_t, | ||
| 161 | st_size: filesize_t, | ||
| 162 | st_atim: timestamp_t, | ||
| 163 | st_mtim: timestamp_t, | ||
| 164 | st_ctim: timestamp_t, | ||
| 165 | }; | ||
| 166 | |||
| 167 | pub const filetype_t = u8; | ||
| 168 | pub const FILETYPE_UNKNOWN: filetype_t = 0; | ||
| 169 | pub const FILETYPE_BLOCK_DEVICE: filetype_t = 1; | ||
| 170 | pub const FILETYPE_CHARACTER_DEVICE: filetype_t = 2; | ||
| 171 | pub const FILETYPE_DIRECTORY: filetype_t = 3; | ||
| 172 | pub const FILETYPE_REGULAR_FILE: filetype_t = 4; | ||
| 173 | pub const FILETYPE_SOCKET_DGRAM: filetype_t = 5; | ||
| 174 | pub const FILETYPE_SOCKET_STREAM: filetype_t = 6; | ||
| 175 | pub const FILETYPE_SYMBOLIC_LINK: filetype_t = 7; | ||
| 176 | |||
| 177 | pub const fstflags_t = u16; | ||
| 178 | pub const FILESTAT_SET_ATIM: fstflags_t = 0x0001; | ||
| 179 | pub const FILESTAT_SET_ATIM_NOW: fstflags_t = 0x0002; | ||
| 180 | pub const FILESTAT_SET_MTIM: fstflags_t = 0x0004; | ||
| 181 | pub const FILESTAT_SET_MTIM_NOW: fstflags_t = 0x0008; | ||
| 182 | |||
| 183 | pub const inode_t = u64; | ||
| 184 | |||
| 185 | pub const iovec_t = extern struct { | ||
| 186 | buf: [*]u8, | ||
| 187 | buf_len: usize, | ||
| 188 | }; | ||
| 189 | |||
| 190 | pub const linkcount_t = u32; | ||
| 191 | |||
| 192 | pub const lookupflags_t = u32; | ||
| 193 | pub const LOOKUP_SYMLINK_FOLLOW: lookupflags_t = 0x00000001; | ||
| 194 | |||
| 195 | pub const oflags_t = u16; | ||
| 196 | pub const O_CREAT: oflags_t = 0x0001; | ||
| 197 | pub const O_DIRECTORY: oflags_t = 0x0002; | ||
| 198 | pub const O_EXCL: oflags_t = 0x0004; | ||
| 199 | pub const O_TRUNC: oflags_t = 0x0008; | ||
| 200 | |||
| 201 | pub const preopentype_t = u8; | ||
| 202 | pub const PREOPENTYPE_DIR: preopentype_t = 0; | ||
| 203 | |||
| 204 | pub const prestat_t = extern struct { | ||
| 205 | pr_type: preopentype_t, | ||
| 206 | u: extern union { | ||
| 207 | dir: extern struct { | ||
| 208 | pr_name_len: usize, | ||
| 209 | }, | ||
| 210 | }, | ||
| 211 | }; | ||
| 212 | |||
| 213 | pub const riflags_t = u16; | ||
| 214 | pub const SOCK_RECV_PEEK: riflags_t = 0x0001; | ||
| 215 | pub const SOCK_RECV_WAITALL: riflags_t = 0x0002; | ||
| 216 | |||
| 217 | pub const rights_t = u64; | ||
| 218 | pub const RIGHT_FD_DATASYNC: rights_t = 0x0000000000000001; | ||
| 219 | pub const RIGHT_FD_READ: rights_t = 0x0000000000000002; | ||
| 220 | pub const RIGHT_FD_SEEK: rights_t = 0x0000000000000004; | ||
| 221 | pub const RIGHT_FD_FDSTAT_SET_FLAGS: rights_t = 0x0000000000000008; | ||
| 222 | pub const RIGHT_FD_SYNC: rights_t = 0x0000000000000010; | ||
| 223 | pub const RIGHT_FD_TELL: rights_t = 0x0000000000000020; | ||
| 224 | pub const RIGHT_FD_WRITE: rights_t = 0x0000000000000040; | ||
| 225 | pub const RIGHT_FD_ADVISE: rights_t = 0x0000000000000080; | ||
| 226 | pub const RIGHT_FD_ALLOCATE: rights_t = 0x0000000000000100; | ||
| 227 | pub const RIGHT_PATH_CREATE_DIRECTORY: rights_t = 0x0000000000000200; | ||
| 228 | pub const RIGHT_PATH_CREATE_FILE: rights_t = 0x0000000000000400; | ||
| 229 | pub const RIGHT_PATH_LINK_SOURCE: rights_t = 0x0000000000000800; | ||
| 230 | pub const RIGHT_PATH_LINK_TARGET: rights_t = 0x0000000000001000; | ||
| 231 | pub const RIGHT_PATH_OPEN: rights_t = 0x0000000000002000; | ||
| 232 | pub const RIGHT_FD_READDIR: rights_t = 0x0000000000004000; | ||
| 233 | pub const RIGHT_PATH_READLINK: rights_t = 0x0000000000008000; | ||
| 234 | pub const RIGHT_PATH_RENAME_SOURCE: rights_t = 0x0000000000010000; | ||
| 235 | pub const RIGHT_PATH_RENAME_TARGET: rights_t = 0x0000000000020000; | ||
| 236 | pub const RIGHT_PATH_FILESTAT_GET: rights_t = 0x0000000000040000; | ||
| 237 | pub const RIGHT_PATH_FILESTAT_SET_SIZE: rights_t = 0x0000000000080000; | ||
| 238 | pub const RIGHT_PATH_FILESTAT_SET_TIMES: rights_t = 0x0000000000100000; | ||
| 239 | pub const RIGHT_FD_FILESTAT_GET: rights_t = 0x0000000000200000; | ||
| 240 | pub const RIGHT_FD_FILESTAT_SET_SIZE: rights_t = 0x0000000000400000; | ||
| 241 | pub const RIGHT_FD_FILESTAT_SET_TIMES: rights_t = 0x0000000000800000; | ||
| 242 | pub const RIGHT_PATH_SYMLINK: rights_t = 0x0000000001000000; | ||
| 243 | pub const RIGHT_PATH_REMOVE_DIRECTORY: rights_t = 0x0000000002000000; | ||
| 244 | pub const RIGHT_PATH_UNLINK_FILE: rights_t = 0x0000000004000000; | ||
| 245 | pub const RIGHT_POLL_FD_READWRITE: rights_t = 0x0000000008000000; | ||
| 246 | pub const RIGHT_SOCK_SHUTDOWN: rights_t = 0x0000000010000000; | ||
| 247 | |||
| 248 | pub const roflags_t = u16; | ||
| 249 | pub const SOCK_RECV_DATA_TRUNCATED: roflags_t = 0x0001; | ||
| 250 | |||
| 251 | pub const sdflags_t = u8; | ||
| 252 | pub const SHUT_RD: sdflags_t = 0x01; | ||
| 253 | pub const SHUT_WR: sdflags_t = 0x02; | ||
| 254 | |||
| 255 | pub const siflags_t = u16; | ||
| 256 | |||
| 257 | pub const signal_t = u8; | ||
| 258 | pub const SIGHUP: signal_t = 1; | ||
| 259 | pub const SIGINT: signal_t = 2; | ||
| 260 | pub const SIGQUIT: signal_t = 3; | ||
| 261 | pub const SIGILL: signal_t = 4; | ||
| 262 | pub const SIGTRAP: signal_t = 5; | ||
| 263 | pub const SIGABRT: signal_t = 6; | ||
| 264 | pub const SIGBUS: signal_t = 7; | ||
| 265 | pub const SIGFPE: signal_t = 8; | ||
| 266 | pub const SIGKILL: signal_t = 9; | ||
| 267 | pub const SIGUSR1: signal_t = 10; | ||
| 268 | pub const SIGSEGV: signal_t = 11; | ||
| 269 | pub const SIGUSR2: signal_t = 12; | ||
| 270 | pub const SIGPIPE: signal_t = 13; | ||
| 271 | pub const SIGALRM: signal_t = 14; | ||
| 272 | pub const SIGTERM: signal_t = 15; | ||
| 273 | pub const SIGCHLD: signal_t = 16; | ||
| 274 | pub const SIGCONT: signal_t = 17; | ||
| 275 | pub const SIGSTOP: signal_t = 18; | ||
| 276 | pub const SIGTSTP: signal_t = 19; | ||
| 277 | pub const SIGTTIN: signal_t = 20; | ||
| 278 | pub const SIGTTOU: signal_t = 21; | ||
| 279 | pub const SIGURG: signal_t = 22; | ||
| 280 | pub const SIGXCPU: signal_t = 23; | ||
| 281 | pub const SIGXFSZ: signal_t = 24; | ||
| 282 | pub const SIGVTALRM: signal_t = 25; | ||
| 283 | pub const SIGPROF: signal_t = 26; | ||
| 284 | pub const SIGWINCH: signal_t = 27; | ||
| 285 | pub const SIGPOLL: signal_t = 28; | ||
| 286 | pub const SIGPWR: signal_t = 29; | ||
| 287 | pub const SIGSYS: signal_t = 30; | ||
| 288 | |||
| 289 | pub const subclockflags_t = u16; | ||
| 290 | pub const SUBSCRIPTION_CLOCK_ABSTIME: subclockflags_t = 0x0001; | ||
| 291 | |||
| 292 | pub const subscription_t = extern struct { | ||
| 293 | userdata: userdata_t, | ||
| 294 | @"type": eventtype_t, | ||
| 295 | u: extern union { | ||
| 296 | clock: extern struct { | ||
| 297 | identifier: userdata_t, | ||
| 298 | clock_id: clockid_t, | ||
| 299 | timeout: timestamp_t, | ||
| 300 | precision: timestamp_t, | ||
| 301 | flags: subclockflags_t, | ||
| 302 | }, | ||
| 303 | fd_readwrite: extern struct { | ||
| 304 | fd: fd_t, | ||
| 305 | }, | ||
| 306 | }, | ||
| 307 | }; | ||
| 308 | |||
| 309 | pub const timestamp_t = u64; | ||
| 310 | |||
| 311 | pub const userdata_t = u64; | ||
| 312 | |||
| 313 | pub const whence_t = u8; | ||
| 314 | pub const WHENCE_CUR: whence_t = 0; | ||
| 315 | pub const WHENCE_END: whence_t = 1; | ||
| 316 | pub const WHENCE_SET: whence_t = 2; | ||
| 317 | |||
| 318 | pub extern "wasi_unstable" fn args_get(argv: [*][*]u8, argv_buf: [*]u8) errno_t; | ||
| 319 | pub extern "wasi_unstable" fn args_sizes_get(argc: *usize, argv_buf_size: *usize) errno_t; | ||
| 320 | |||
| 321 | pub extern "wasi_unstable" fn clock_res_get(clock_id: clockid_t, resolution: *timestamp_t) errno_t; | ||
| 322 | pub extern "wasi_unstable" fn clock_time_get(clock_id: clockid_t, precision: timestamp_t, timestamp: *timestamp_t) errno_t; | ||
| 323 | |||
| 324 | pub extern "wasi_unstable" fn environ_get(environ: [*]?[*]u8, environ_buf: [*]u8) errno_t; | ||
| 325 | pub extern "wasi_unstable" fn environ_sizes_get(environ_count: *usize, environ_buf_size: *usize) errno_t; | ||
| 326 | |||
| 327 | pub extern "wasi_unstable" fn fd_advise(fd: fd_t, offset: filesize_t, len: filesize_t, advice: advice_t) errno_t; | ||
| 328 | pub extern "wasi_unstable" fn fd_allocate(fd: fd_t, offset: filesize_t, len: filesize_t) errno_t; | ||
| 329 | pub extern "wasi_unstable" fn fd_close(fd: fd_t) errno_t; | ||
| 330 | pub extern "wasi_unstable" fn fd_datasync(fd: fd_t) errno_t; | ||
| 331 | pub extern "wasi_unstable" fn fd_pread(fd: fd_t, iovs: *const iovec_t, iovs_len: usize, offset: filesize_t, nread: *usize) errno_t; | ||
| 332 | pub extern "wasi_unstable" fn fd_pwrite(fd: fd_t, iovs: *const ciovec_t, iovs_len: usize, offset: filesize_t, nwritten: *usize) errno_t; | ||
| 333 | pub extern "wasi_unstable" fn fd_read(fd: fd_t, iovs: *const iovec_t, iovs_len: usize, nread: *usize) errno_t; | ||
| 334 | pub extern "wasi_unstable" fn fd_readdir(fd: fd_t, buf: [*]u8, buf_len: usize, cookie: dircookie_t, bufused: *usize) errno_t; | ||
| 335 | pub extern "wasi_unstable" fn fd_renumber(from: fd_t, to: fd_t) errno_t; | ||
| 336 | pub extern "wasi_unstable" fn fd_seek(fd: fd_t, offset: filedelta_t, whence: whence_t, newoffset: *filesize_t) errno_t; | ||
| 337 | pub extern "wasi_unstable" fn fd_sync(fd: fd_t) errno_t; | ||
| 338 | pub extern "wasi_unstable" fn fd_tell(fd: fd_t, newoffset: *filesize_t) errno_t; | ||
| 339 | pub extern "wasi_unstable" fn fd_write(fd: fd_t, iovs: *const ciovec_t, iovs_len: usize, nwritten: *usize) errno_t; | ||
| 340 | |||
| 341 | pub extern "wasi_unstable" fn fd_fdstat_get(fd: fd_t, buf: *fdstat_t) errno_t; | ||
| 342 | pub extern "wasi_unstable" fn fd_fdstat_set_flags(fd: fd_t, flags: fdflags_t) errno_t; | ||
| 343 | pub extern "wasi_unstable" fn fd_fdstat_set_rights(fd: fd_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t) errno_t; | ||
| 344 | |||
| 345 | pub extern "wasi_unstable" fn fd_filestat_get(fd: fd_t, buf: *filestat_t) errno_t; | ||
| 346 | pub extern "wasi_unstable" fn fd_filestat_set_size(fd: fd_t, st_size: filesize_t) errno_t; | ||
| 347 | pub extern "wasi_unstable" fn fd_filestat_set_times(fd: fd_t, st_atim: timestamp_t, st_mtim: timestamp_t, fstflags: fstflags_t) errno_t; | ||
| 348 | |||
| 349 | pub extern "wasi_unstable" fn fd_prestat_get(fd: fd_t, buf: *prestat_t) errno_t; | ||
| 350 | pub extern "wasi_unstable" fn fd_prestat_dir_name(fd: fd_t, path: [*]u8, path_len: usize) errno_t; | ||
| 351 | |||
| 352 | pub extern "wasi_unstable" fn path_create_directory(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; | ||
| 353 | pub extern "wasi_unstable" fn path_filestat_get(fd: fd_t, flags: lookupflags_t, path: [*]const u8, path_len: usize, buf: *filestat_t) errno_t; | ||
| 354 | pub extern "wasi_unstable" fn path_filestat_set_times(fd: fd_t, flags: lookupflags_t, path: [*]const u8, path_len: usize, st_atim: timestamp_t, st_mtim: timestamp_t, fstflags: fstflags_t) errno_t; | ||
| 355 | pub extern "wasi_unstable" fn path_link(old_fd: fd_t, old_flags: lookupflags_t, old_path: [*]const u8, old_path_len: usize, new_fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; | ||
| 356 | pub extern "wasi_unstable" fn path_open(dirfd: fd_t, dirflags: lookupflags_t, path: [*]const u8, path_len: usize, oflags: oflags_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t, fs_flags: fdflags_t, fd: *fd_t) errno_t; | ||
| 357 | pub extern "wasi_unstable" fn path_readlink(fd: fd_t, path: [*]const u8, path_len: usize, buf: [*]u8, buf_len: usize, bufused: *usize) errno_t; | ||
| 358 | pub extern "wasi_unstable" fn path_remove_directory(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; | ||
| 359 | pub extern "wasi_unstable" fn path_rename(old_fd: fd_t, old_path: [*]const u8, old_path_len: usize, new_fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; | ||
| 360 | pub extern "wasi_unstable" fn path_symlink(old_path: [*]const u8, old_path_len: usize, fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; | ||
| 361 | pub extern "wasi_unstable" fn path_unlink_file(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; | ||
| 362 | |||
| 363 | pub extern "wasi_unstable" fn poll_oneoff(in: *const subscription_t, out: *event_t, nsubscriptions: usize, nevents: *usize) errno_t; | ||
| 364 | |||
| 365 | pub extern "wasi_unstable" fn proc_exit(rval: exitcode_t) noreturn; | ||
| 366 | pub extern "wasi_unstable" fn proc_raise(sig: signal_t) errno_t; | ||
| 367 | |||
| 368 | pub extern "wasi_unstable" fn random_get(buf: [*]u8, buf_len: usize) errno_t; | ||
| 369 | |||
| 370 | pub extern "wasi_unstable" fn sched_yield() errno_t; | ||
| 371 | |||
| 372 | pub extern "wasi_unstable" fn sock_recv(sock: fd_t, ri_data: *const iovec_t, ri_data_len: usize, ri_flags: riflags_t, ro_datalen: *usize, ro_flags: *roflags_t) errno_t; | ||
| 373 | pub extern "wasi_unstable" fn sock_send(sock: fd_t, si_data: *const ciovec_t, si_data_len: usize, si_flags: siflags_t, so_datalen: *usize) errno_t; | ||
| 374 | pub extern "wasi_unstable" fn sock_shutdown(sock: fd_t, how: sdflags_t) errno_t; | ||
std/os/windows.zig+9-1| ... | @@ -2,6 +2,11 @@ const std = @import("../std.zig"); | ... | @@ -2,6 +2,11 @@ const std = @import("../std.zig"); |
| 2 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| 3 | const maxInt = std.math.maxInt; | 3 | const maxInt = std.math.maxInt; |
| 4 | 4 | ||
| 5 | pub const is_the_target = switch (builtin.os) { | ||
| 6 | .windows => true, | ||
| 7 | else => false, | ||
| 8 | }; | ||
| 9 | |||
| 5 | pub use @import("windows/advapi32.zig"); | 10 | pub use @import("windows/advapi32.zig"); |
| 6 | pub use @import("windows/kernel32.zig"); | 11 | pub use @import("windows/kernel32.zig"); |
| 7 | pub use @import("windows/ntdll.zig"); | 12 | pub use @import("windows/ntdll.zig"); |
| ... | @@ -9,10 +14,13 @@ pub use @import("windows/ole32.zig"); | ... | @@ -9,10 +14,13 @@ pub use @import("windows/ole32.zig"); |
| 9 | pub use @import("windows/shell32.zig"); | 14 | pub use @import("windows/shell32.zig"); |
| 10 | 15 | ||
| 11 | test "import" { | 16 | test "import" { |
| 12 | _ = @import("windows/util.zig"); | 17 | if (is_the_target) { |
| 18 | _ = @import("windows/util.zig"); | ||
| 19 | } | ||
| 13 | } | 20 | } |
| 14 | 21 | ||
| 15 | pub const ERROR = @import("windows/error.zig"); | 22 | pub const ERROR = @import("windows/error.zig"); |
| 23 | pub const errno_codes = @import("windows/errno.zig"); | ||
| 16 | 24 | ||
| 17 | pub const SHORT = c_short; | 25 | pub const SHORT = c_short; |
| 18 | pub const BOOL = c_int; | 26 | pub const BOOL = c_int; |
std/os/windows/errno.zig created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | // TODO get these values from msvcrt | ||
std/os/windows/util.zig-167| ... | @@ -8,12 +8,6 @@ const mem = std.mem; | ... | @@ -8,12 +8,6 @@ const mem = std.mem; |
| 8 | const BufMap = std.BufMap; | 8 | const BufMap = std.BufMap; |
| 9 | const cstr = std.cstr; | 9 | const cstr = std.cstr; |
| 10 | 10 | ||
| 11 | // > The maximum path of 32,767 characters is approximate, because the "\\?\" | ||
| 12 | // > prefix may be expanded to a longer string by the system at run time, and | ||
| 13 | // > this expansion applies to the total length. | ||
| 14 | // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation | ||
| 15 | pub const PATH_MAX_WIDE = 32767; | ||
| 16 | |||
| 17 | pub const WaitError = error{ | 11 | pub const WaitError = error{ |
| 18 | WaitAbandoned, | 12 | WaitAbandoned, |
| 19 | WaitTimeOut, | 13 | WaitTimeOut, |
| ... | @@ -38,131 +32,6 @@ pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) Wa | ... | @@ -38,131 +32,6 @@ pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) Wa |
| 38 | }; | 32 | }; |
| 39 | } | 33 | } |
| 40 | 34 | ||
| 41 | pub fn windowsClose(handle: windows.HANDLE) void { | ||
| 42 | assert(windows.CloseHandle(handle) != 0); | ||
| 43 | } | ||
| 44 | |||
| 45 | pub const ReadError = error{ | ||
| 46 | OperationAborted, | ||
| 47 | BrokenPipe, | ||
| 48 | Unexpected, | ||
| 49 | }; | ||
| 50 | |||
| 51 | pub const WriteError = error{ | ||
| 52 | SystemResources, | ||
| 53 | OperationAborted, | ||
| 54 | BrokenPipe, | ||
| 55 | |||
| 56 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 57 | Unexpected, | ||
| 58 | }; | ||
| 59 | |||
| 60 | pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void { | ||
| 61 | var bytes_written: windows.DWORD = undefined; | ||
| 62 | if (windows.WriteFile(handle, bytes.ptr, @intCast(u32, bytes.len), &bytes_written, null) == 0) { | ||
| 63 | const err = windows.GetLastError(); | ||
| 64 | return switch (err) { | ||
| 65 | windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources, | ||
| 66 | windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources, | ||
| 67 | windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted, | ||
| 68 | windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources, | ||
| 69 | windows.ERROR.IO_PENDING => unreachable, | ||
| 70 | windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe, | ||
| 71 | else => os.unexpectedErrorWindows(err), | ||
| 72 | }; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | pub fn windowsIsTty(handle: windows.HANDLE) bool { | ||
| 77 | if (windowsIsCygwinPty(handle)) | ||
| 78 | return true; | ||
| 79 | |||
| 80 | var out: windows.DWORD = undefined; | ||
| 81 | return windows.GetConsoleMode(handle, &out) != 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool { | ||
| 85 | const size = @sizeOf(windows.FILE_NAME_INFO); | ||
| 86 | var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = []u8{0} ** (size + windows.MAX_PATH); | ||
| 87 | |||
| 88 | if (windows.GetFileInformationByHandleEx( | ||
| 89 | handle, | ||
| 90 | windows.FileNameInfo, | ||
| 91 | @ptrCast(*c_void, &name_info_bytes[0]), | ||
| 92 | @intCast(u32, name_info_bytes.len), | ||
| 93 | ) == 0) { | ||
| 94 | return false; | ||
| 95 | } | ||
| 96 | |||
| 97 | const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]); | ||
| 98 | const name_bytes = name_info_bytes[size .. size + usize(name_info.FileNameLength)]; | ||
| 99 | const name_wide = @bytesToSlice(u16, name_bytes); | ||
| 100 | return mem.indexOf(u16, name_wide, []u16{ 'm', 's', 'y', 's', '-' }) != null or | ||
| 101 | mem.indexOf(u16, name_wide, []u16{ '-', 'p', 't', 'y' }) != null; | ||
| 102 | } | ||
| 103 | |||
| 104 | pub const OpenError = error{ | ||
| 105 | SharingViolation, | ||
| 106 | PathAlreadyExists, | ||
| 107 | |||
| 108 | /// When any of the path components can not be found or the file component can not | ||
| 109 | /// be found. Some operating systems distinguish between path components not found and | ||
| 110 | /// file components not found, but they are collapsed into FileNotFound to gain | ||
| 111 | /// consistency across operating systems. | ||
| 112 | FileNotFound, | ||
| 113 | |||
| 114 | AccessDenied, | ||
| 115 | PipeBusy, | ||
| 116 | NameTooLong, | ||
| 117 | |||
| 118 | /// On Windows, file paths must be valid Unicode. | ||
| 119 | InvalidUtf8, | ||
| 120 | |||
| 121 | /// On Windows, file paths cannot contain these characters: | ||
| 122 | /// '/', '*', '?', '"', '<', '>', '|' | ||
| 123 | BadPathName, | ||
| 124 | |||
| 125 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 126 | Unexpected, | ||
| 127 | }; | ||
| 128 | |||
| 129 | pub fn windowsOpenW( | ||
| 130 | file_path_w: [*]const u16, | ||
| 131 | desired_access: windows.DWORD, | ||
| 132 | share_mode: windows.DWORD, | ||
| 133 | creation_disposition: windows.DWORD, | ||
| 134 | flags_and_attrs: windows.DWORD, | ||
| 135 | ) OpenError!windows.HANDLE { | ||
| 136 | const result = windows.CreateFileW(file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null); | ||
| 137 | |||
| 138 | if (result == windows.INVALID_HANDLE_VALUE) { | ||
| 139 | const err = windows.GetLastError(); | ||
| 140 | switch (err) { | ||
| 141 | windows.ERROR.SHARING_VIOLATION => return OpenError.SharingViolation, | ||
| 142 | windows.ERROR.ALREADY_EXISTS => return OpenError.PathAlreadyExists, | ||
| 143 | windows.ERROR.FILE_EXISTS => return OpenError.PathAlreadyExists, | ||
| 144 | windows.ERROR.FILE_NOT_FOUND => return OpenError.FileNotFound, | ||
| 145 | windows.ERROR.PATH_NOT_FOUND => return OpenError.FileNotFound, | ||
| 146 | windows.ERROR.ACCESS_DENIED => return OpenError.AccessDenied, | ||
| 147 | windows.ERROR.PIPE_BUSY => return OpenError.PipeBusy, | ||
| 148 | else => return os.unexpectedErrorWindows(err), | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | return result; | ||
| 153 | } | ||
| 154 | |||
| 155 | pub fn windowsOpen( | ||
| 156 | file_path: []const u8, | ||
| 157 | desired_access: windows.DWORD, | ||
| 158 | share_mode: windows.DWORD, | ||
| 159 | creation_disposition: windows.DWORD, | ||
| 160 | flags_and_attrs: windows.DWORD, | ||
| 161 | ) OpenError!windows.HANDLE { | ||
| 162 | const file_path_w = try sliceToPrefixedFileW(file_path); | ||
| 163 | return windowsOpenW(&file_path_w, desired_access, share_mode, creation_disposition, flags_and_attrs); | ||
| 164 | } | ||
| 165 | |||
| 166 | /// Caller must free result. | 35 | /// Caller must free result. |
| 167 | pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 { | 36 | pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 { |
| 168 | // count bytes needed | 37 | // count bytes needed |
| ... | @@ -278,39 +147,3 @@ pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_t | ... | @@ -278,39 +147,3 @@ pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_t |
| 278 | } | 147 | } |
| 279 | return WindowsWaitResult.Normal; | 148 | return WindowsWaitResult.Normal; |
| 280 | } | 149 | } |
| 281 | |||
| 282 | pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 { | ||
| 283 | return sliceToPrefixedFileW(mem.toSliceConst(u8, s)); | ||
| 284 | } | ||
| 285 | |||
| 286 | pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { | ||
| 287 | return sliceToPrefixedSuffixedFileW(s, []u16{0}); | ||
| 288 | } | ||
| 289 | |||
| 290 | pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 { | ||
| 291 | // TODO well defined copy elision | ||
| 292 | var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined; | ||
| 293 | |||
| 294 | // > File I/O functions in the Windows API convert "/" to "\" as part of | ||
| 295 | // > converting the name to an NT-style name, except when using the "\\?\" | ||
| 296 | // > prefix as detailed in the following sections. | ||
| 297 | // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation | ||
| 298 | // Because we want the larger maximum path length for absolute paths, we | ||
| 299 | // disallow forward slashes in zig std lib file functions on Windows. | ||
| 300 | for (s) |byte| { | ||
| 301 | switch (byte) { | ||
| 302 | '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName, | ||
| 303 | else => {}, | ||
| 304 | } | ||
| 305 | } | ||
| 306 | const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: { | ||
| 307 | const prefix = []u16{ '\\', '\\', '?', '\\' }; | ||
| 308 | mem.copy(u16, result[0..], prefix); | ||
| 309 | break :blk prefix.len; | ||
| 310 | }; | ||
| 311 | const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s); | ||
| 312 | assert(end_index <= result.len); | ||
| 313 | if (end_index + suffix.len > result.len) return error.NameTooLong; | ||
| 314 | mem.copy(u16, result[end_index..], suffix); | ||
| 315 | return result; | ||
| 316 | } |
std/special/bootstrap.zig+2-2| ... | @@ -81,7 +81,7 @@ fn posixCallMainAndExit() noreturn { | ... | @@ -81,7 +81,7 @@ fn posixCallMainAndExit() noreturn { |
| 81 | if (builtin.os == builtin.Os.linux) { | 81 | if (builtin.os == builtin.Os.linux) { |
| 82 | // Find the beginning of the auxiliary vector | 82 | // Find the beginning of the auxiliary vector |
| 83 | const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1); | 83 | const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1); |
| 84 | std.os.linux_elf_aux_maybe = auxv; | 84 | std.os.linux.elf_aux_maybe = auxv; |
| 85 | // Initialize the TLS area | 85 | // Initialize the TLS area |
| 86 | std.os.linux.tls.initTLS(); | 86 | std.os.linux.tls.initTLS(); |
| 87 | 87 | ||
| ... | @@ -99,7 +99,7 @@ fn posixCallMainAndExit() noreturn { | ... | @@ -99,7 +99,7 @@ fn posixCallMainAndExit() noreturn { |
| 99 | // and we want fewer call frames in stack traces. | 99 | // and we want fewer call frames in stack traces. |
| 100 | inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 { | 100 | inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 { |
| 101 | std.os.ArgIteratorPosix.raw = argv[0..argc]; | 101 | std.os.ArgIteratorPosix.raw = argv[0..argc]; |
| 102 | std.os.posix_environ_raw = envp; | 102 | std.os.posix.environ = envp; |
| 103 | return callMain(); | 103 | return callMain(); |
| 104 | } | 104 | } |
| 105 | 105 |