authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-24 00:13:13-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-26 18:32:44-04:00
log2def23063fbabb4128da17aca27745a7e8062ce5
tree53d8e79eebc72a0ff171ff6f911e2c4eb2ef5cdc
parentdaae7e1f5aa5aac02a342c9a60d703c2a0cba579
signaturelock-open Commit is signed but in an unrecognized format.

more progress. moving windows API layer to its own file


14 files changed, 1331 insertions(+), 1317 deletions(-)

CMakeLists.txt+1
...@@ -619,6 +619,7 @@ set(ZIG_STD_FILES...@@ -619,6 +619,7 @@ set(ZIG_STD_FILES
619 "os/linux/posix.zig"619 "os/linux/posix.zig"
620 "os/linux/posix/arm64.zig"620 "os/linux/posix/arm64.zig"
621 "os/linux/posix/x86_64.zig"621 "os/linux/posix/x86_64.zig"
622 "os/linux/sys.zig"
622 "os/linux/tls.zig"623 "os/linux/tls.zig"
623 "os/linux/vdso.zig"624 "os/linux/vdso.zig"
624 "os/linux/x86_64.zig"625 "os/linux/x86_64.zig"
std/dynamic_library.zig+2-3
...@@ -261,12 +261,11 @@ pub const WindowsDynLib = struct {...@@ -261,12 +261,11 @@ pub const WindowsDynLib = struct {
261 return WindowsDynLib{261 return WindowsDynLib{
262 .allocator = allocator,262 .allocator = allocator,
263 .dll = windows.LoadLibraryW(&wpath) orelse {263 .dll = windows.LoadLibraryW(&wpath) orelse {
264 const err = windows.GetLastError();264 switch (windows.GetLastError()) {
265 switch (err) {
266 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,265 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
267 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,266 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
268 windows.ERROR.MOD_NOT_FOUND => return error.FileNotFound,267 windows.ERROR.MOD_NOT_FOUND => return error.FileNotFound,
269 else => return os.unexpectedErrorWindows(err),268 else => |err| return windows.unexpectedError(err),
270 }269 }
271 },270 },
272 };271 };
std/event/fs.zig+14-18
...@@ -154,16 +154,15 @@ pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64)...@@ -154,16 +154,15 @@ pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64)
154 }154 }
155 var bytes_transferred: windows.DWORD = undefined;155 var bytes_transferred: windows.DWORD = undefined;
156 if (windows.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) {156 if (windows.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) {
157 const err = windows.GetLastError();157 switch (windows.GetLastError()) {
158 return switch (err) {
159 windows.ERROR.IO_PENDING => unreachable,158 windows.ERROR.IO_PENDING => unreachable,
160 windows.ERROR.INVALID_USER_BUFFER => error.SystemResources,159 windows.ERROR.INVALID_USER_BUFFER => return error.SystemResources,
161 windows.ERROR.NOT_ENOUGH_MEMORY => error.SystemResources,160 windows.ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources,
162 windows.ERROR.OPERATION_ABORTED => error.OperationAborted,161 windows.ERROR.OPERATION_ABORTED => return error.OperationAborted,
163 windows.ERROR.NOT_ENOUGH_QUOTA => error.SystemResources,162 windows.ERROR.NOT_ENOUGH_QUOTA => return error.SystemResources,
164 windows.ERROR.BROKEN_PIPE => error.BrokenPipe,163 windows.ERROR.BROKEN_PIPE => return error.BrokenPipe,
165 else => os.unexpectedErrorWindows(err),164 else => |err| return windows.unexpectedError(err),
166 };165 }
167 }166 }
168}167}
169168
...@@ -304,13 +303,12 @@ pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize...@@ -304,13 +303,12 @@ pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize
304 }303 }
305 var bytes_transferred: windows.DWORD = undefined;304 var bytes_transferred: windows.DWORD = undefined;
306 if (windows.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) {305 if (windows.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) {
307 const err = windows.GetLastError();306 switch (windows.GetLastError()) {
308 switch (err) {
309 windows.ERROR.IO_PENDING => unreachable,307 windows.ERROR.IO_PENDING => unreachable,
310 windows.ERROR.OPERATION_ABORTED => return error.OperationAborted,308 windows.ERROR.OPERATION_ABORTED => return error.OperationAborted,
311 windows.ERROR.BROKEN_PIPE => return error.BrokenPipe,309 windows.ERROR.BROKEN_PIPE => return error.BrokenPipe,
312 windows.ERROR.HANDLE_EOF => return usize(bytes_transferred),310 windows.ERROR.HANDLE_EOF => return usize(bytes_transferred),
313 else => return os.unexpectedErrorWindows(err),311 else => |err| return windows.unexpectedError(err),
314 }312 }
315 }313 }
316 return usize(bytes_transferred);314 return usize(bytes_transferred);
...@@ -1042,11 +1040,10 @@ pub fn Watch(comptime V: type) type {...@@ -1042,11 +1040,10 @@ pub fn Watch(comptime V: type) type {
1042 null,1040 null,
1043 );1041 );
1044 if (dir_handle == windows.INVALID_HANDLE_VALUE) {1042 if (dir_handle == windows.INVALID_HANDLE_VALUE) {
1045 const err = windows.GetLastError();1043 switch (windows.GetLastError()) {
1046 switch (err) {
1047 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,1044 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
1048 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,1045 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
1049 else => return os.unexpectedErrorWindows(err),1046 else => |err| return windows.unexpectedError(err),
1050 }1047 }
1051 }1048 }
1052 var dir_handle_consumed = false;1049 var dir_handle_consumed = false;
...@@ -1165,9 +1162,8 @@ pub fn Watch(comptime V: type) type {...@@ -1165,9 +1162,8 @@ pub fn Watch(comptime V: type) type {
1165 }1162 }
1166 var bytes_transferred: windows.DWORD = undefined;1163 var bytes_transferred: windows.DWORD = undefined;
1167 if (windows.GetOverlappedResult(dir_handle, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) {1164 if (windows.GetOverlappedResult(dir_handle, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) {
1168 const errno = windows.GetLastError();1165 const err = switch (windows.GetLastError()) {
1169 const err = switch (errno) {1166 else => |err| windows.unexpectedError(err),
1170 else => os.unexpectedErrorWindows(errno),
1171 };1167 };
1172 await (async self.channel.put(err) catch unreachable);1168 await (async self.channel.put(err) catch unreachable);
1173 } else {1169 } else {
std/os.zig+14-8
...@@ -31,13 +31,14 @@ pub const zen = @import("os/zen.zig");...@@ -31,13 +31,14 @@ pub const zen = @import("os/zen.zig");
31pub const uefi = @import("os/uefi.zig");31pub const uefi = @import("os/uefi.zig");
32pub const wasi = @import("os/wasi.zig");32pub const wasi = @import("os/wasi.zig");
3333
34pub const system = switch (builtin.os) {34pub const system = if (builtin.link_libc) std.c else switch (builtin.os) {
35 .linux => linux,35 .linux => linux,
36 .macosx, .ios, .watchos, .tvos => darwin,36 .macosx, .ios, .watchos, .tvos => darwin,
37 .freebsd => freebsd,37 .freebsd => freebsd,
38 .netbsd => netbsd,38 .netbsd => netbsd,
39 .zen => zen,39 .zen => zen,
40 .wasi => wasi,40 .wasi => wasi,
41 .windows => windows,
41 else => struct {},42 else => struct {},
42};43};
4344
...@@ -53,6 +54,12 @@ pub const page_size = switch (builtin.arch) {...@@ -53,6 +54,12 @@ pub const page_size = switch (builtin.arch) {
53 else => 4 * 1024,54 else => 4 * 1024,
54};55};
5556
57pub const unexpected_error_tracing = builtin.mode == .Debug;
58pub const UnexpectedError = error{
59 /// The Operating System returned an undocumented error code.
60 Unexpected,
61};
62
56/// This represents the maximum size of a UTF-8 encoded file path.63/// This represents the maximum size of a UTF-8 encoded file path.
57/// All file system operations which return a path are guaranteed to64/// All file system operations which return a path are guaranteed to
58/// fit into a UTF-8 encoded array of this length.65/// fit into a UTF-8 encoded array of this length.
...@@ -259,7 +266,7 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned...@@ -259,7 +266,7 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned
259 return switch (err) {266 return switch (err) {
260 windows.ERROR.ENVVAR_NOT_FOUND => error.EnvironmentVariableNotFound,267 windows.ERROR.ENVVAR_NOT_FOUND => error.EnvironmentVariableNotFound,
261 else => {268 else => {
262 unexpectedErrorWindows(err) catch {};269 windows.unexpectedError(err) catch {};
263 return error.EnvironmentVariableNotFound;270 return error.EnvironmentVariableNotFound;
264 },271 },
265 };272 };
...@@ -825,7 +832,7 @@ pub const Dir = struct {...@@ -825,7 +832,7 @@ pub const Dir = struct {
825 if (self.handle.first) {832 if (self.handle.first) {
826 self.handle.first = false;833 self.handle.first = false;
827 } else {834 } else {
828 if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data))835 if (!try posix.FindNextFile(self.handle.handle, &self.handle.find_file_data))
829 return null;836 return null;
830 }837 }
831 const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr);838 const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr);
...@@ -1333,7 +1340,7 @@ pub fn selfExePathW(out_buffer: *[posix.PATH_MAX_WIDE]u16) ![]u16 {...@@ -1333,7 +1340,7 @@ pub fn selfExePathW(out_buffer: *[posix.PATH_MAX_WIDE]u16) ![]u16 {
1333 if (rc == 0) {1340 if (rc == 0) {
1334 const err = windows.GetLastError();1341 const err = windows.GetLastError();
1335 switch (err) {1342 switch (err) {
1336 else => return unexpectedErrorWindows(err),1343 else => return windows.unexpectedError(err),
1337 }1344 }
1338 }1345 }
1339 return out_buffer[0..rc];1346 return out_buffer[0..rc];
...@@ -1597,10 +1604,9 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread...@@ -1597,10 +1604,9 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
15971604
1598 const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner);1605 const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner);
1599 outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse {1606 outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse {
1600 const err = windows.GetLastError();1607 switch (windows.GetLastError()) {
1601 return switch (err) {1608 else => |err| windows.unexpectedError(err),
1602 else => os.unexpectedErrorWindows(err),1609 }
1603 };
1604 };1610 };
1605 return &outer_context.thread;1611 return &outer_context.thread;
1606 }1612 }
std/os/child_process.zig+42-28
...@@ -558,7 +558,7 @@ pub const ChildProcess = struct {...@@ -558,7 +558,7 @@ pub const ChildProcess = struct {
558 defer if (cwd_w) |cwd| self.allocator.free(cwd);558 defer if (cwd_w) |cwd| self.allocator.free(cwd);
559 const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null;559 const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null;
560560
561 const maybe_envp_buf = if (self.env_map) |env_map| try os.createWindowsEnvBlock(self.allocator, env_map) else null;561 const maybe_envp_buf = if (self.env_map) |env_map| try createWindowsEnvBlock(self.allocator, env_map) else null;
562 defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf);562 defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf);
563 const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null;563 const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null;
564564
...@@ -679,13 +679,12 @@ fn windowsCreateProcess(app_name: [*]u16, cmd_line: [*]u16, envp_ptr: ?[*]u16, c...@@ -679,13 +679,12 @@ fn windowsCreateProcess(app_name: [*]u16, cmd_line: [*]u16, envp_ptr: ?[*]u16, c
679 lpStartupInfo,679 lpStartupInfo,
680 lpProcessInformation,680 lpProcessInformation,
681 ) == 0) {681 ) == 0) {
682 const err = windows.GetLastError();682 switch (windows.GetLastError()) {
683 switch (err) {
684 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,683 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
685 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,684 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
686 windows.ERROR.INVALID_PARAMETER => unreachable,685 windows.ERROR.INVALID_PARAMETER => unreachable,
687 windows.ERROR.INVALID_NAME => return error.InvalidName,686 windows.ERROR.INVALID_NAME => return error.InvalidName,
688 else => return os.unexpectedErrorWindows(err),687 else => |err| return windows.unexpectedError(err),
689 }688 }
690 }689 }
691}690}
...@@ -733,32 +732,10 @@ fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void {...@@ -733,32 +732,10 @@ fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void {
733 if (wr) |h| os.close(h);732 if (wr) |h| os.close(h);
734}733}
735734
736// TODO: workaround for bug where the `const` from `&const` is dropped when the type is
737// a namespace field lookup
738const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES;
739
740fn windowsMakePipe(rd: *windows.HANDLE, wr: *windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void {
741 if (windows.CreatePipe(rd, wr, sattr, 0) == 0) {
742 const err = windows.GetLastError();
743 return switch (err) {
744 else => os.unexpectedErrorWindows(err),
745 };
746 }
747}
748
749fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.DWORD) !void {
750 if (windows.SetHandleInformation(h, mask, flags) == 0) {
751 const err = windows.GetLastError();
752 return switch (err) {
753 else => os.unexpectedErrorWindows(err),
754 };
755 }
756}
757
758fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void {735fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void {
759 var rd_h: windows.HANDLE = undefined;736 var rd_h: windows.HANDLE = undefined;
760 var wr_h: windows.HANDLE = undefined;737 var wr_h: windows.HANDLE = undefined;
761 try windowsMakePipe(&rd_h, &wr_h, sattr);738 try windows.CreatePipe(&rd_h, &wr_h, sattr);
762 errdefer windowsDestroyPipe(rd_h, wr_h);739 errdefer windowsDestroyPipe(rd_h, wr_h);
763 try windowsSetHandleInfo(wr_h, windows.HANDLE_FLAG_INHERIT, 0);740 try windowsSetHandleInfo(wr_h, windows.HANDLE_FLAG_INHERIT, 0);
764 rd.* = rd_h;741 rd.* = rd_h;
...@@ -768,7 +745,7 @@ fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const S...@@ -768,7 +745,7 @@ fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const S
768fn windowsMakePipeOut(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void {745fn windowsMakePipeOut(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void {
769 var rd_h: windows.HANDLE = undefined;746 var rd_h: windows.HANDLE = undefined;
770 var wr_h: windows.HANDLE = undefined;747 var wr_h: windows.HANDLE = undefined;
771 try windowsMakePipe(&rd_h, &wr_h, sattr);748 try windows.CreatePipe(&rd_h, &wr_h, sattr);
772 errdefer windowsDestroyPipe(rd_h, wr_h);749 errdefer windowsDestroyPipe(rd_h, wr_h);
773 try windowsSetHandleInfo(rd_h, windows.HANDLE_FLAG_INHERIT, 0);750 try windowsSetHandleInfo(rd_h, windows.HANDLE_FLAG_INHERIT, 0);
774 rd.* = rd_h;751 rd.* = rd_h;
...@@ -810,3 +787,40 @@ fn readIntFd(fd: i32) !ErrInt {...@@ -810,3 +787,40 @@ fn readIntFd(fd: i32) !ErrInt {
810 const stream = &os.File.openHandle(fd).inStream().stream;787 const stream = &os.File.openHandle(fd).inStream().stream;
811 return stream.readIntNative(ErrInt) catch return error.SystemResources;788 return stream.readIntNative(ErrInt) catch return error.SystemResources;
812}789}
790
791/// Caller must free result.
792pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 {
793 // count bytes needed
794 const max_chars_needed = x: {
795 var max_chars_needed: usize = 4; // 4 for the final 4 null bytes
796 var it = env_map.iterator();
797 while (it.next()) |pair| {
798 // +1 for '='
799 // +1 for null byte
800 max_chars_needed += pair.key.len + pair.value.len + 2;
801 }
802 break :x max_chars_needed;
803 };
804 const result = try allocator.alloc(u16, max_chars_needed);
805 errdefer allocator.free(result);
806
807 var it = env_map.iterator();
808 var i: usize = 0;
809 while (it.next()) |pair| {
810 i += try unicode.utf8ToUtf16Le(result[i..], pair.key);
811 result[i] = '=';
812 i += 1;
813 i += try unicode.utf8ToUtf16Le(result[i..], pair.value);
814 result[i] = 0;
815 i += 1;
816 }
817 result[i] = 0;
818 i += 1;
819 result[i] = 0;
820 i += 1;
821 result[i] = 0;
822 i += 1;
823 result[i] = 0;
824 i += 1;
825 return allocator.shrink(result, i);
826}
std/os/linux.zig+2-765
...@@ -1,771 +1,8 @@...@@ -1,771 +1,8 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const builtin = @import("builtin");2const builtin = @import("builtin");
4const maxInt = std.math.maxInt;
5const elf = std.elf;
6pub const tls = @import("linux/tls.zig");
7const vdso = @import("linux/vdso.zig");
8const dl = @import("../dynamic_library.zig");
9pub use switch (builtin.arch) {
10 .x86_64 => @import("linux/x86_64.zig"),
11 .aarch64 => @import("linux/arm64.zig"),
12 else => struct {},
13};
14pub const is_the_target = builtin.os == .linux;3pub const is_the_target = builtin.os == .linux;
15pub const posix = @import("linux/posix.zig");4pub const sys = @import("linux/sys.zig");
16pub use posix;5pub use if (builtin.link_libc) std.c else sys;
17
18/// See `std.os.posix.getauxval`.
19pub var elf_aux_maybe: ?[*]std.elf.Auxv = null;
20
21/// Get the errno from a syscall return value, or 0 for no error.
22pub fn getErrno(r: usize) u12 {
23 const signed_r = @bitCast(isize, r);
24 return if (signed_r > -4096 and signed_r < 0) @intCast(u12, -signed_r) else 0;
25}
26
27pub fn dup2(old: i32, new: i32) usize {
28 return syscall2(SYS_dup2, @bitCast(usize, isize(old)), @bitCast(usize, isize(new)));
29}
30
31pub fn dup3(old: i32, new: i32, flags: u32) usize {
32 return syscall3(SYS_dup3, @bitCast(usize, isize(old)), @bitCast(usize, isize(new)), flags);
33}
34
35// TODO https://github.com/ziglang/zig/issues/265
36pub fn chdir(path: [*]const u8) usize {
37 return syscall1(SYS_chdir, @ptrToInt(path));
38}
39
40// TODO https://github.com/ziglang/zig/issues/265
41pub fn chroot(path: [*]const u8) usize {
42 return syscall1(SYS_chroot, @ptrToInt(path));
43}
44
45// TODO https://github.com/ziglang/zig/issues/265
46pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize {
47 return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp));
48}
49
50pub fn fork() usize {
51 return syscall0(SYS_fork);
52}
53
54/// This must be inline, and inline call the syscall function, because if the
55/// child does a return it will clobber the parent's stack.
56/// It is advised to avoid this function and use clone instead, because
57/// the compiler is not aware of how vfork affects control flow and you may
58/// see different results in optimized builds.
59pub inline fn vfork() usize {
60 return @inlineCall(syscall0, SYS_vfork);
61}
62
63pub fn futex_wait(uaddr: *const i32, futex_op: u32, val: i32, timeout: ?*timespec) usize {
64 return syscall4(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val), @ptrToInt(timeout));
65}
66
67pub fn futex_wake(uaddr: *const i32, futex_op: u32, val: i32) usize {
68 return syscall3(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val));
69}
70
71pub fn getcwd(buf: [*]u8, size: usize) usize {
72 return syscall2(SYS_getcwd, @ptrToInt(buf), size);
73}
74
75pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize {
76 return syscall3(SYS_getdents, @bitCast(usize, isize(fd)), @ptrToInt(dirp), count);
77}
78
79pub fn getdents64(fd: i32, dirp: [*]u8, count: usize) usize {
80 return syscall3(SYS_getdents64, @bitCast(usize, isize(fd)), @ptrToInt(dirp), count);
81}
82
83pub fn inotify_init1(flags: u32) usize {
84 return syscall1(SYS_inotify_init1, flags);
85}
86
87pub fn inotify_add_watch(fd: i32, pathname: [*]const u8, mask: u32) usize {
88 return syscall3(SYS_inotify_add_watch, @bitCast(usize, isize(fd)), @ptrToInt(pathname), mask);
89}
90
91pub fn inotify_rm_watch(fd: i32, wd: i32) usize {
92 return syscall2(SYS_inotify_rm_watch, @bitCast(usize, isize(fd)), @bitCast(usize, isize(wd)));
93}
94
95// TODO https://github.com/ziglang/zig/issues/265
96pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize {
97 return syscall3(SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len);
98}
99
100// TODO https://github.com/ziglang/zig/issues/265
101pub fn readlinkat(dirfd: i32, noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize {
102 return syscall4(SYS_readlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(buf_ptr), buf_len);
103}
104
105// TODO https://github.com/ziglang/zig/issues/265
106pub fn mkdir(path: [*]const u8, mode: u32) usize {
107 return syscall2(SYS_mkdir, @ptrToInt(path), mode);
108}
109
110// TODO https://github.com/ziglang/zig/issues/265
111pub fn mkdirat(dirfd: i32, path: [*]const u8, mode: u32) usize {
112 return syscall3(SYS_mkdirat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode);
113}
114
115// TODO https://github.com/ziglang/zig/issues/265
116pub fn mount(special: [*]const u8, dir: [*]const u8, fstype: [*]const u8, flags: u32, data: usize) usize {
117 return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data);
118}
119
120// TODO https://github.com/ziglang/zig/issues/265
121pub fn umount(special: [*]const u8) usize {
122 return syscall2(SYS_umount2, @ptrToInt(special), 0);
123}
124
125// TODO https://github.com/ziglang/zig/issues/265
126pub fn umount2(special: [*]const u8, flags: u32) usize {
127 return syscall2(SYS_umount2, @ptrToInt(special), flags);
128}
129
130pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {
131 return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, @bitCast(usize, isize(fd)), @bitCast(usize, offset));
132}
133
134pub fn mprotect(address: usize, length: usize, protection: usize) usize {
135 return syscall3(SYS_mprotect, address, length, protection);
136}
137
138pub fn munmap(address: usize, length: usize) usize {
139 return syscall2(SYS_munmap, address, length);
140}
141
142pub fn read(fd: i32, buf: [*]u8, count: usize) usize {
143 return syscall3(SYS_read, @bitCast(usize, isize(fd)), @ptrToInt(buf), count);
144}
145
146pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize {
147 return syscall4(SYS_preadv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset);
148}
149
150pub fn readv(fd: i32, iov: [*]const iovec, count: usize) usize {
151 return syscall3(SYS_readv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count);
152}
153
154pub fn writev(fd: i32, iov: [*]const iovec_const, count: usize) usize {
155 return syscall3(SYS_writev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count);
156}
157
158pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) usize {
159 return syscall4(SYS_pwritev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset);
160}
161
162// TODO https://github.com/ziglang/zig/issues/265
163pub fn rmdir(path: [*]const u8) usize {
164 return syscall1(SYS_rmdir, @ptrToInt(path));
165}
166
167// TODO https://github.com/ziglang/zig/issues/265
168pub fn symlink(existing: [*]const u8, new: [*]const u8) usize {
169 return syscall2(SYS_symlink, @ptrToInt(existing), @ptrToInt(new));
170}
171
172// TODO https://github.com/ziglang/zig/issues/265
173pub fn symlinkat(existing: [*]const u8, newfd: i32, newpath: [*]const u8) usize {
174 return syscall3(SYS_symlinkat, @ptrToInt(existing), @bitCast(usize, isize(newfd)), @ptrToInt(newpath));
175}
176
177// TODO https://github.com/ziglang/zig/issues/265
178pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: usize) usize {
179 return syscall4(SYS_pread, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset);
180}
181
182// TODO https://github.com/ziglang/zig/issues/265
183pub fn access(path: [*]const u8, mode: u32) usize {
184 return syscall2(SYS_access, @ptrToInt(path), mode);
185}
186
187// TODO https://github.com/ziglang/zig/issues/265
188pub fn faccessat(dirfd: i32, path: [*]const u8, mode: u32) usize {
189 return syscall3(SYS_faccessat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode);
190}
191
192pub fn pipe(fd: *[2]i32) usize {
193 return pipe2(fd, 0);
194}
195
196pub fn pipe2(fd: *[2]i32, flags: u32) usize {
197 return syscall2(SYS_pipe2, @ptrToInt(fd), flags);
198}
199
200pub fn write(fd: i32, buf: [*]const u8, count: usize) usize {
201 return syscall3(SYS_write, @bitCast(usize, isize(fd)), @ptrToInt(buf), count);
202}
203
204pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize {
205 return syscall4(SYS_pwrite, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset);
206}
207
208// TODO https://github.com/ziglang/zig/issues/265
209pub fn rename(old: [*]const u8, new: [*]const u8) usize {
210 return syscall2(SYS_rename, @ptrToInt(old), @ptrToInt(new));
211}
212
213// TODO https://github.com/ziglang/zig/issues/265
214pub fn renameat2(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const u8, flags: u32) usize {
215 return syscall5(SYS_renameat2, @bitCast(usize, isize(oldfd)), @ptrToInt(oldpath), @bitCast(usize, isize(newfd)), @ptrToInt(newpath), flags);
216}
217
218// TODO https://github.com/ziglang/zig/issues/265
219pub fn open(path: [*]const u8, flags: u32, perm: usize) usize {
220 return syscall3(SYS_open, @ptrToInt(path), flags, perm);
221}
222
223// TODO https://github.com/ziglang/zig/issues/265
224pub fn create(path: [*]const u8, perm: usize) usize {
225 return syscall2(SYS_creat, @ptrToInt(path), perm);
226}
227
228// TODO https://github.com/ziglang/zig/issues/265
229pub fn openat(dirfd: i32, path: [*]const u8, flags: u32, mode: usize) usize {
230 // dirfd could be negative, for example AT_FDCWD is -100
231 return syscall4(SYS_openat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode);
232}
233
234/// See also `clone` (from the arch-specific include)
235pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: *i32, child_tid: *i32, newtls: usize) usize {
236 return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls);
237}
238
239/// See also `clone` (from the arch-specific include)
240pub fn clone2(flags: u32, child_stack_ptr: usize) usize {
241 return syscall2(SYS_clone, flags, child_stack_ptr);
242}
243
244pub fn close(fd: i32) usize {
245 return syscall1(SYS_close, @bitCast(usize, isize(fd)));
246}
247
248pub fn lseek(fd: i32, offset: isize, ref_pos: usize) usize {
249 return syscall3(SYS_lseek, @bitCast(usize, isize(fd)), @bitCast(usize, offset), ref_pos);
250}
251
252pub fn exit(status: i32) noreturn {
253 _ = syscall1(SYS_exit, @bitCast(usize, isize(status)));
254 unreachable;
255}
256
257pub fn exit_group(status: i32) noreturn {
258 _ = syscall1(SYS_exit_group, @bitCast(usize, isize(status)));
259 unreachable;
260}
261
262pub fn getrandom(buf: [*]u8, count: usize, flags: u32) usize {
263 return syscall3(SYS_getrandom, @ptrToInt(buf), count, flags);
264}
265
266pub fn kill(pid: i32, sig: i32) usize {
267 return syscall2(SYS_kill, @bitCast(usize, isize(pid)), @bitCast(usize, isize(sig)));
268}
269
270// TODO https://github.com/ziglang/zig/issues/265
271pub fn unlink(path: [*]const u8) usize {
272 return syscall1(SYS_unlink, @ptrToInt(path));
273}
274
275// TODO https://github.com/ziglang/zig/issues/265
276pub fn unlinkat(dirfd: i32, path: [*]const u8, flags: u32) usize {
277 return syscall3(SYS_unlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags);
278}
279
280pub fn waitpid(pid: i32, status: *i32, options: i32) usize {
281 return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0);
282}
283
284var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime);
285
286// We must follow the C calling convention when we call into the VDSO
287const vdso_clock_gettime_ty = extern fn (i32, *timespec) usize;
288
289pub fn clock_gettime(clk_id: i32, tp: *timespec) usize {
290 if (VDSO_CGT_SYM.len != 0) {
291 const ptr = @atomicLoad(?*const c_void, &vdso_clock_gettime, .Unordered);
292 if (ptr) |fn_ptr| {
293 const f = @ptrCast(vdso_clock_gettime_ty, fn_ptr);
294 const rc = f(clk_id, tp);
295 switch (rc) {
296 0, @bitCast(usize, isize(-EINVAL)) => return rc,
297 else => {},
298 }
299 }
300 }
301 return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
302}
303
304extern fn init_vdso_clock_gettime(clk: i32, ts: *timespec) usize {
305 const ptr = @intToPtr(?*const c_void, vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM));
306 // Note that we may not have a VDSO at all, update the stub address anyway
307 // so that clock_gettime will fall back on the good old (and slow) syscall
308 _ = @cmpxchgStrong(?*const c_void, &vdso_clock_gettime, &init_vdso_clock_gettime, ptr, .Monotonic, .Monotonic);
309 // Call into the VDSO if available
310 if (ptr) |fn_ptr| {
311 const f = @ptrCast(vdso_clock_gettime_ty, fn_ptr);
312 return f(clk, ts);
313 }
314 return @bitCast(usize, isize(-ENOSYS));
315}
316
317pub fn clock_getres(clk_id: i32, tp: *timespec) usize {
318 return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
319}
320
321pub fn clock_settime(clk_id: i32, tp: *const timespec) usize {
322 return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
323}
324
325pub fn gettimeofday(tv: *timeval, tz: *timezone) usize {
326 return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz));
327}
328
329pub fn settimeofday(tv: *const timeval, tz: *const timezone) usize {
330 return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz));
331}
332
333pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize {
334 return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem));
335}
336
337pub fn setuid(uid: u32) usize {
338 return syscall1(SYS_setuid, uid);
339}
340
341pub fn setgid(gid: u32) usize {
342 return syscall1(SYS_setgid, gid);
343}
344
345pub fn setreuid(ruid: u32, euid: u32) usize {
346 return syscall2(SYS_setreuid, ruid, euid);
347}
348
349pub fn setregid(rgid: u32, egid: u32) usize {
350 return syscall2(SYS_setregid, rgid, egid);
351}
352
353pub fn getuid() u32 {
354 return u32(syscall0(SYS_getuid));
355}
356
357pub fn getgid() u32 {
358 return u32(syscall0(SYS_getgid));
359}
360
361pub fn geteuid() u32 {
362 return u32(syscall0(SYS_geteuid));
363}
364
365pub fn getegid() u32 {
366 return u32(syscall0(SYS_getegid));
367}
368
369pub fn seteuid(euid: u32) usize {
370 return syscall1(SYS_seteuid, euid);
371}
372
373pub fn setegid(egid: u32) usize {
374 return syscall1(SYS_setegid, egid);
375}
376
377pub fn getresuid(ruid: *u32, euid: *u32, suid: *u32) usize {
378 return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid));
379}
380
381pub fn getresgid(rgid: *u32, egid: *u32, sgid: *u32) usize {
382 return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid));
383}
384
385pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize {
386 return syscall3(SYS_setresuid, ruid, euid, suid);
387}
388
389pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize {
390 return syscall3(SYS_setresgid, rgid, egid, sgid);
391}
392
393pub fn getgroups(size: usize, list: *u32) usize {
394 return syscall2(SYS_getgroups, size, @ptrToInt(list));
395}
396
397pub fn setgroups(size: usize, list: *const u32) usize {
398 return syscall2(SYS_setgroups, size, @ptrToInt(list));
399}
400
401pub fn getpid() i32 {
402 return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid)));
403}
404
405pub fn gettid() i32 {
406 return @bitCast(i32, @truncate(u32, syscall0(SYS_gettid)));
407}
408
409pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize {
410 return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8);
411}
412
413pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize {
414 assert(sig >= 1);
415 assert(sig != SIGKILL);
416 assert(sig != SIGSTOP);
417 var ksa = k_sigaction{
418 .handler = act.handler,
419 .flags = act.flags | SA_RESTORER,
420 .mask = undefined,
421 .restorer = @ptrCast(extern fn () void, restore_rt),
422 };
423 var ksa_old: k_sigaction = undefined;
424 @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &act.mask), 8);
425 const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask)));
426 const err = getErrno(result);
427 if (err != 0) {
428 return result;
429 }
430 if (oact) |old| {
431 old.handler = ksa_old.handler;
432 old.flags = @truncate(u32, ksa_old.flags);
433 @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask)));
434 }
435 return 0;
436}
437
438fn blockAllSignals(set: *sigset_t) void {
439 _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8);
440}
441
442fn blockAppSignals(set: *sigset_t) void {
443 _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG / 8);
444}
445
446fn restoreSignals(set: *sigset_t) void {
447 _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG / 8);
448}
449
450pub fn sigaddset(set: *sigset_t, sig: u6) void {
451 const s = sig - 1;
452 (set.*)[@intCast(usize, s) / usize.bit_count] |= @intCast(usize, 1) << (s & (usize.bit_count - 1));
453}
454
455pub fn sigismember(set: *const sigset_t, sig: u6) bool {
456 const s = sig - 1;
457 return ((set.*)[@intCast(usize, s) / usize.bit_count] & (@intCast(usize, 1) << (s & (usize.bit_count - 1)))) != 0;
458}
459
460pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
461 return syscall3(SYS_getsockname, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len));
462}
463
464pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
465 return syscall3(SYS_getpeername, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len));
466}
467
468pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize {
469 return syscall3(SYS_socket, domain, socket_type, protocol);
470}
471
472pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize {
473 return syscall5(SYS_setsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen));
474}
475
476pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize {
477 return syscall5(SYS_getsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen));
478}
479
480pub fn sendmsg(fd: i32, msg: *msghdr_const, flags: u32) usize {
481 return syscall3(SYS_sendmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags);
482}
483
484pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize {
485 if (@typeInfo(usize).Int.bits > @typeInfo(@typeOf(mmsghdr(undefined).msg_len)).Int.bits) {
486 // workaround kernel brokenness:
487 // if adding up all iov_len overflows a i32 then split into multiple calls
488 // see https://www.openwall.com/lists/musl/2014/06/07/5
489 const kvlen = if (vlen > IOV_MAX) IOV_MAX else vlen; // matches kernel
490 var next_unsent: usize = 0;
491 for (msgvec[0..kvlen]) |*msg, i| {
492 var size: i32 = 0;
493 const msg_iovlen = @intCast(usize, msg.msg_hdr.msg_iovlen); // kernel side this is treated as unsigned
494 for (msg.msg_hdr.msg_iov[0..msg_iovlen]) |iov, j| {
495 if (iov.iov_len > std.math.maxInt(i32) or @addWithOverflow(i32, size, @intCast(i32, iov.iov_len), &size)) {
496 // batch-send all messages up to the current message
497 if (next_unsent < i) {
498 const batch_size = i - next_unsent;
499 const r = syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags);
500 if (getErrno(r) != 0) return next_unsent;
501 if (r < batch_size) return next_unsent + r;
502 }
503 // send current message as own packet
504 const r = sendmsg(fd, &msg.msg_hdr, flags);
505 if (getErrno(r) != 0) return r;
506 // Linux limits the total bytes sent by sendmsg to INT_MAX, so this cast is safe.
507 msg.msg_len = @intCast(u32, r);
508 next_unsent = i + 1;
509 break;
510 }
511 }
512 }
513 if (next_unsent < kvlen or next_unsent == 0) { // want to make sure at least one syscall occurs (e.g. to trigger MSG_EOR)
514 const batch_size = kvlen - next_unsent;
515 const r = syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags);
516 if (getErrno(r) != 0) return r;
517 return next_unsent + r;
518 }
519 return kvlen;
520 }
521 return syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(msgvec), vlen, flags);
522}
523
524pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize {
525 return syscall3(SYS_connect, @bitCast(usize, isize(fd)), @ptrToInt(addr), len);
526}
527
528pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize {
529 return syscall3(SYS_recvmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags);
530}
531
532pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize {
533 return syscall6(SYS_recvfrom, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen));
534}
535
536pub fn shutdown(fd: i32, how: i32) usize {
537 return syscall2(SYS_shutdown, @bitCast(usize, isize(fd)), @bitCast(usize, isize(how)));
538}
539
540pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize {
541 return syscall3(SYS_bind, @bitCast(usize, isize(fd)), @ptrToInt(addr), @intCast(usize, len));
542}
543
544pub fn listen(fd: i32, backlog: u32) usize {
545 return syscall2(SYS_listen, @bitCast(usize, isize(fd)), backlog);
546}
547
548pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize {
549 return syscall6(SYS_sendto, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen));
550}
551
552pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize {
553 return syscall4(SYS_socketpair, @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0]));
554}
555
556pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
557 return accept4(fd, addr, len, 0);
558}
559
560pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize {
561 return syscall4(SYS_accept4, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len), flags);
562}
563
564pub fn fstat(fd: i32, stat_buf: *Stat) usize {
565 return syscall2(SYS_fstat, @bitCast(usize, isize(fd)), @ptrToInt(stat_buf));
566}
567
568// TODO https://github.com/ziglang/zig/issues/265
569pub fn stat(pathname: [*]const u8, statbuf: *Stat) usize {
570 return syscall2(SYS_stat, @ptrToInt(pathname), @ptrToInt(statbuf));
571}
572
573// TODO https://github.com/ziglang/zig/issues/265
574pub fn lstat(pathname: [*]const u8, statbuf: *Stat) usize {
575 return syscall2(SYS_lstat, @ptrToInt(pathname), @ptrToInt(statbuf));
576}
577
578// TODO https://github.com/ziglang/zig/issues/265
579pub fn fstatat(dirfd: i32, path: [*]const u8, stat_buf: *Stat, flags: u32) usize {
580 return syscall4(SYS_fstatat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(stat_buf), flags);
581}
582
583// TODO https://github.com/ziglang/zig/issues/265
584pub fn listxattr(path: [*]const u8, list: [*]u8, size: usize) usize {
585 return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size);
586}
587
588// TODO https://github.com/ziglang/zig/issues/265
589pub fn llistxattr(path: [*]const u8, list: [*]u8, size: usize) usize {
590 return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size);
591}
592
593pub fn flistxattr(fd: usize, list: [*]u8, size: usize) usize {
594 return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size);
595}
596
597// TODO https://github.com/ziglang/zig/issues/265
598pub fn getxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize {
599 return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size);
600}
601
602// TODO https://github.com/ziglang/zig/issues/265
603pub fn lgetxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize {
604 return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size);
605}
606
607// TODO https://github.com/ziglang/zig/issues/265
608pub fn fgetxattr(fd: usize, name: [*]const u8, value: [*]u8, size: usize) usize {
609 return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size);
610}
611
612// TODO https://github.com/ziglang/zig/issues/265
613pub fn setxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize {
614 return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags);
615}
616
617// TODO https://github.com/ziglang/zig/issues/265
618pub fn lsetxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize {
619 return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags);
620}
621
622// TODO https://github.com/ziglang/zig/issues/265
623pub fn fsetxattr(fd: usize, name: [*]const u8, value: *const void, size: usize, flags: usize) usize {
624 return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), size, flags);
625}
626
627// TODO https://github.com/ziglang/zig/issues/265
628pub fn removexattr(path: [*]const u8, name: [*]const u8) usize {
629 return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name));
630}
631
632// TODO https://github.com/ziglang/zig/issues/265
633pub fn lremovexattr(path: [*]const u8, name: [*]const u8) usize {
634 return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name));
635}
636
637// TODO https://github.com/ziglang/zig/issues/265
638pub fn fremovexattr(fd: usize, name: [*]const u8) usize {
639 return syscall2(SYS_fremovexattr, fd, @ptrToInt(name));
640}
641
642pub fn sched_getaffinity(pid: i32, set: []usize) usize {
643 return syscall3(SYS_sched_getaffinity, @bitCast(usize, isize(pid)), set.len * @sizeOf(usize), @ptrToInt(set.ptr));
644}
645
646pub fn epoll_create() usize {
647 return epoll_create1(0);
648}
649
650pub fn epoll_create1(flags: usize) usize {
651 return syscall1(SYS_epoll_create1, flags);
652}
653
654pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: *epoll_event) usize {
655 return syscall4(SYS_epoll_ctl, @bitCast(usize, isize(epoll_fd)), @intCast(usize, op), @bitCast(usize, isize(fd)), @ptrToInt(ev));
656}
657
658pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32) usize {
659 return syscall4(
660 SYS_epoll_wait,
661 @bitCast(usize, isize(epoll_fd)),
662 @ptrToInt(events),
663 maxevents,
664 @bitCast(usize, isize(timeout)),
665 );
666}
667
668pub fn epoll_pwait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32, sigmask: ?*sigset_t) usize {
669 return syscall6(
670 SYS_epoll_pwait,
671 @bitCast(usize, isize(epoll_fd)),
672 @ptrToInt(events),
673 @intCast(usize, maxevents),
674 @bitCast(usize, isize(timeout)),
675 @ptrToInt(sigmask),
676 @sizeOf(sigset_t),
677 );
678}
679
680pub fn eventfd(count: u32, flags: u32) usize {
681 return syscall2(SYS_eventfd2, count, flags);
682}
683
684pub fn timerfd_create(clockid: i32, flags: u32) usize {
685 return syscall2(SYS_timerfd_create, @bitCast(usize, isize(clockid)), flags);
686}
687
688pub const itimerspec = extern struct {
689 it_interval: timespec,
690 it_value: timespec,
691};
692
693pub fn timerfd_gettime(fd: i32, curr_value: *itimerspec) usize {
694 return syscall2(SYS_timerfd_gettime, @bitCast(usize, isize(fd)), @ptrToInt(curr_value));
695}
696
697pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const itimerspec, old_value: ?*itimerspec) usize {
698 return syscall4(SYS_timerfd_settime, @bitCast(usize, isize(fd)), flags, @ptrToInt(new_value), @ptrToInt(old_value));
699}
700
701pub fn unshare(flags: usize) usize {
702 return syscall1(SYS_unshare, flags);
703}
704
705pub fn capget(hdrp: *cap_user_header_t, datap: *cap_user_data_t) usize {
706 return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap));
707}
708
709pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize {
710 return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
711}
712
713// XXX: This should be weak
714extern const __ehdr_start: elf.Ehdr = undefined;
715
716pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_info, size: usize, data: ?*T) i32, data: ?*T) isize {
717 if (builtin.link_libc) {
718 return std.c.dl_iterate_phdr(@ptrCast(std.c.dl_iterate_phdr_callback, callback), @ptrCast(?*c_void, data));
719 }
720
721 const elf_base = @ptrToInt(&__ehdr_start);
722 const n_phdr = __ehdr_start.e_phnum;
723 const phdrs = (@intToPtr([*]elf.Phdr, elf_base + __ehdr_start.e_phoff))[0..n_phdr];
724
725 var it = dl.linkmap_iterator(phdrs) catch return 0;
726
727 // The executable has no dynamic link segment, create a single entry for
728 // the whole ELF image
729 if (it.end()) {
730 var info = dl_phdr_info{
731 .dlpi_addr = elf_base,
732 .dlpi_name = c"/proc/self/exe",
733 .dlpi_phdr = @intToPtr([*]elf.Phdr, elf_base + __ehdr_start.e_phoff),
734 .dlpi_phnum = __ehdr_start.e_phnum,
735 };
736
737 return callback(&info, @sizeOf(dl_phdr_info), data);
738 }
739
740 // Last return value from the callback function
741 var last_r: isize = 0;
742 while (it.next()) |entry| {
743 var dlpi_phdr: usize = undefined;
744 var dlpi_phnum: u16 = undefined;
745
746 if (entry.l_addr != 0) {
747 const elf_header = @intToPtr(*elf.Ehdr, entry.l_addr);
748 dlpi_phdr = entry.l_addr + elf_header.e_phoff;
749 dlpi_phnum = elf_header.e_phnum;
750 } else {
751 // This is the running ELF image
752 dlpi_phdr = elf_base + __ehdr_start.e_phoff;
753 dlpi_phnum = __ehdr_start.e_phnum;
754 }
755
756 var info = dl_phdr_info{
757 .dlpi_addr = entry.l_addr,
758 .dlpi_name = entry.l_name,
759 .dlpi_phdr = @intToPtr([*]elf.Phdr, dlpi_phdr),
760 .dlpi_phnum = dlpi_phnum,
761 };
762
763 last_r = callback(&info, @sizeOf(dl_phdr_info), data);
764 if (last_r != 0) break;
765 }
766
767 return last_r;
768}
7696
770test "import" {7test "import" {
771 if (is_the_target) {8 if (is_the_target) {
std/os/linux/sys.zig created+847
...@@ -0,0 +1,847 @@
1// This file provides the system interface functions for Linux matching those
2// that are provided by libc, whether or not libc is linked. The following
3// abstractions are made:
4// * Work around kernel bugs and limitations. For example, see sendmmsg.
5// * Implement all the syscalls in the same way that libc functions will
6// provide `rename` when only the `renameat` syscall exists.
7// * Does not support POSIX thread cancellation.
8const std = @import("../../std.zig");
9const builtin = @import("builtin");
10const assert = std.debug.assert;
11const maxInt = std.math.maxInt;
12const elf = std.elf;
13const vdso = @import("vdso.zig");
14const dl = @import("../../dynamic_library.zig");
15pub use switch (builtin.arch) {
16 .x86_64 => @import("x86_64.zig"),
17 .aarch64 => @import("arm64.zig"),
18 else => struct {},
19};
20pub const posix = @import("posix.zig");
21pub use posix;
22
23/// See `std.os.posix.getauxval`.
24pub var elf_aux_maybe: ?[*]std.elf.Auxv = null;
25
26/// Get the errno from a syscall return value, or 0 for no error.
27pub fn getErrno(r: usize) u12 {
28 const signed_r = @bitCast(isize, r);
29 return if (signed_r > -4096 and signed_r < 0) @intCast(u12, -signed_r) else 0;
30}
31
32pub fn dup2(old: i32, new: i32) usize {
33 if (@hasDecl(@This(), "SYS_dup2")) {
34 return syscall2(SYS_dup2, @bitCast(usize, isize(old)), @bitCast(usize, isize(new)));
35 } else {
36 if (old == new) {
37 if (std.debug.runtime_safety) {
38 const rc = syscall2(SYS_fcntl, @bitCast(usize, isize(old)), F_GETFD);
39 if (@bitCast(isize, rc) < 0) return rc;
40 }
41 return @intCast(usize, old);
42 } else {
43 return syscall3(SYS_dup3, @bitCast(usize, isize(old)), @bitCast(usize, isize(new)), 0);
44 }
45 }
46}
47
48pub fn dup3(old: i32, new: i32, flags: u32) usize {
49 return syscall3(SYS_dup3, @bitCast(usize, isize(old)), @bitCast(usize, isize(new)), flags);
50}
51
52// TODO https://github.com/ziglang/zig/issues/265
53pub fn chdir(path: [*]const u8) usize {
54 return syscall1(SYS_chdir, @ptrToInt(path));
55}
56
57// TODO https://github.com/ziglang/zig/issues/265
58pub fn chroot(path: [*]const u8) usize {
59 return syscall1(SYS_chroot, @ptrToInt(path));
60}
61
62// TODO https://github.com/ziglang/zig/issues/265
63pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize {
64 return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp));
65}
66
67pub fn fork() usize {
68 if (@hasDecl(@This(), "SYS_fork")) {
69 return syscall0(SYS_fork);
70 } else {
71 return syscall2(SYS_clone, SIGCHLD, 0);
72 }
73}
74
75/// This must be inline, and inline call the syscall function, because if the
76/// child does a return it will clobber the parent's stack.
77/// It is advised to avoid this function and use clone instead, because
78/// the compiler is not aware of how vfork affects control flow and you may
79/// see different results in optimized builds.
80pub inline fn vfork() usize {
81 return @inlineCall(syscall0, SYS_vfork);
82}
83
84pub fn futex_wait(uaddr: *const i32, futex_op: u32, val: i32, timeout: ?*timespec) usize {
85 return syscall4(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val), @ptrToInt(timeout));
86}
87
88pub fn futex_wake(uaddr: *const i32, futex_op: u32, val: i32) usize {
89 return syscall3(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val));
90}
91
92pub fn getcwd(buf: [*]u8, size: usize) usize {
93 return syscall2(SYS_getcwd, @ptrToInt(buf), size);
94}
95
96pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize {
97 return syscall3(SYS_getdents, @bitCast(usize, isize(fd)), @ptrToInt(dirp), count);
98}
99
100pub fn getdents64(fd: i32, dirp: [*]u8, count: usize) usize {
101 return syscall3(SYS_getdents64, @bitCast(usize, isize(fd)), @ptrToInt(dirp), count);
102}
103
104pub fn inotify_init1(flags: u32) usize {
105 return syscall1(SYS_inotify_init1, flags);
106}
107
108pub fn inotify_add_watch(fd: i32, pathname: [*]const u8, mask: u32) usize {
109 return syscall3(SYS_inotify_add_watch, @bitCast(usize, isize(fd)), @ptrToInt(pathname), mask);
110}
111
112pub fn inotify_rm_watch(fd: i32, wd: i32) usize {
113 return syscall2(SYS_inotify_rm_watch, @bitCast(usize, isize(fd)), @bitCast(usize, isize(wd)));
114}
115
116// TODO https://github.com/ziglang/zig/issues/265
117pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize {
118 if (@hasDecl(@This(), "SYS_readlink")) {
119 return syscall3(SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len);
120 } else {
121 return syscall4(SYS_readlinkat, AT_FDCWD, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len);
122 }
123}
124
125// TODO https://github.com/ziglang/zig/issues/265
126pub fn readlinkat(dirfd: i32, noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize {
127 return syscall4(SYS_readlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(buf_ptr), buf_len);
128}
129
130// TODO https://github.com/ziglang/zig/issues/265
131pub fn mkdir(path: [*]const u8, mode: u32) usize {
132 if (@hasDecl(@This(), "SYS_mkdir")) {
133 return syscall2(SYS_mkdir, @ptrToInt(path), mode);
134 } else {
135 return syscall3(SYS_mkdirat, AT_FDCWD, @ptrToInt(path), mode);
136 }
137}
138
139// TODO https://github.com/ziglang/zig/issues/265
140pub fn mkdirat(dirfd: i32, path: [*]const u8, mode: u32) usize {
141 return syscall3(SYS_mkdirat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode);
142}
143
144// TODO https://github.com/ziglang/zig/issues/265
145pub fn mount(special: [*]const u8, dir: [*]const u8, fstype: [*]const u8, flags: u32, data: usize) usize {
146 return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data);
147}
148
149// TODO https://github.com/ziglang/zig/issues/265
150pub fn umount(special: [*]const u8) usize {
151 return syscall2(SYS_umount2, @ptrToInt(special), 0);
152}
153
154// TODO https://github.com/ziglang/zig/issues/265
155pub fn umount2(special: [*]const u8, flags: u32) usize {
156 return syscall2(SYS_umount2, @ptrToInt(special), flags);
157}
158
159pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {
160 return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, @bitCast(usize, isize(fd)), @bitCast(usize, offset));
161}
162
163pub fn mprotect(address: usize, length: usize, protection: usize) usize {
164 return syscall3(SYS_mprotect, address, length, protection);
165}
166
167pub fn munmap(address: usize, length: usize) usize {
168 return syscall2(SYS_munmap, address, length);
169}
170
171pub fn read(fd: i32, buf: [*]u8, count: usize) usize {
172 return syscall3(SYS_read, @bitCast(usize, isize(fd)), @ptrToInt(buf), count);
173}
174
175pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize {
176 return syscall4(SYS_preadv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset);
177}
178
179pub fn readv(fd: i32, iov: [*]const iovec, count: usize) usize {
180 return syscall3(SYS_readv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count);
181}
182
183pub fn writev(fd: i32, iov: [*]const iovec_const, count: usize) usize {
184 return syscall3(SYS_writev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count);
185}
186
187pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) usize {
188 return syscall4(SYS_pwritev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset);
189}
190
191// TODO https://github.com/ziglang/zig/issues/265
192pub fn rmdir(path: [*]const u8) usize {
193 if (@hasDecl(@This(), "SYS_rmdir")) {
194 return syscall1(SYS_rmdir, @ptrToInt(path));
195 } else {
196 return syscall3(SYS_unlinkat, AT_FDCWD, @ptrToInt(path), AT_REMOVEDIR);
197 }
198}
199
200// TODO https://github.com/ziglang/zig/issues/265
201pub fn symlink(existing: [*]const u8, new: [*]const u8) usize {
202 if (@hasDecl(@This(), "SYS_symlink")) {
203 return syscall2(SYS_symlink, @ptrToInt(existing), @ptrToInt(new));
204 } else {
205 return syscall3(SYS_symlinkat, @ptrToInt(existing), AT_FDCWD, @ptrToInt(new));
206 }
207}
208
209// TODO https://github.com/ziglang/zig/issues/265
210pub fn symlinkat(existing: [*]const u8, newfd: i32, newpath: [*]const u8) usize {
211 return syscall3(SYS_symlinkat, @ptrToInt(existing), @bitCast(usize, isize(newfd)), @ptrToInt(newpath));
212}
213
214// TODO https://github.com/ziglang/zig/issues/265
215pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: usize) usize {
216 return syscall4(SYS_pread, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset);
217}
218
219// TODO https://github.com/ziglang/zig/issues/265
220pub fn access(path: [*]const u8, mode: u32) usize {
221 return syscall2(SYS_access, @ptrToInt(path), mode);
222}
223
224// TODO https://github.com/ziglang/zig/issues/265
225pub fn faccessat(dirfd: i32, path: [*]const u8, mode: u32) usize {
226 return syscall3(SYS_faccessat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode);
227}
228
229pub fn pipe(fd: *[2]i32) usize {
230 if (@hasDecl(@This(), "SYS_pipe")) {
231 return syscall1(SYS_pipe, @ptrToInt(fd));
232 } else {
233 return syscall2(SYS_pipe2, @ptrToInt(fd), 0);
234 }
235}
236
237pub fn pipe2(fd: *[2]i32, flags: u32) usize {
238 return syscall2(SYS_pipe2, @ptrToInt(fd), flags);
239}
240
241pub fn write(fd: i32, buf: [*]const u8, count: usize) usize {
242 return syscall3(SYS_write, @bitCast(usize, isize(fd)), @ptrToInt(buf), count);
243}
244
245pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize {
246 return syscall4(SYS_pwrite, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset);
247}
248
249// TODO https://github.com/ziglang/zig/issues/265
250pub fn rename(old: [*]const u8, new: [*]const u8) usize {
251 if (@hasDecl(@This(), "SYS_rename")) {
252 return syscall2(SYS_rename, @ptrToInt(old), @ptrToInt(new));
253 } else if (@hasDecl(@This(), "SYS_renameat")) {
254 return syscall4(SYS_renameat, AT_FDCWD, @ptrToInt(old), AT_FDCWD, @ptrToInt(new));
255 } else {
256 return syscall5(SYS_renameat2, AT_FDCWD, @ptrToInt(old), AT_FDCWD, @ptrToInt(new), 0);
257 }
258}
259
260pub fn renameat(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const u8) usize {
261 if (@hasDecl(@This(), "SYS_renameat")) {
262 return syscall4(
263 SYS_renameat,
264 @bitCast(usize, isize(oldfd)),
265 @ptrToInt(old),
266 @bitCast(usize, isize(newfd)),
267 @ptrToInt(new),
268 );
269 } else {
270 return syscall5(
271 SYS_renameat2,
272 @bitCast(usize, isize(oldfd)),
273 @ptrToInt(old),
274 @bitCast(usize, isize(newfd)),
275 @ptrToInt(new),
276 0,
277 );
278 }
279}
280
281// TODO https://github.com/ziglang/zig/issues/265
282pub fn renameat2(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const u8, flags: u32) usize {
283 return syscall5(
284 SYS_renameat2,
285 @bitCast(usize, isize(oldfd)),
286 @ptrToInt(oldpath),
287 @bitCast(usize, isize(newfd)),
288 @ptrToInt(newpath),
289 flags,
290 );
291}
292
293// TODO https://github.com/ziglang/zig/issues/265
294pub fn open(path: [*]const u8, flags: u32, perm: usize) usize {
295 return syscall3(SYS_open, @ptrToInt(path), flags, perm);
296}
297
298// TODO https://github.com/ziglang/zig/issues/265
299pub fn create(path: [*]const u8, perm: usize) usize {
300 return syscall2(SYS_creat, @ptrToInt(path), perm);
301}
302
303// TODO https://github.com/ziglang/zig/issues/265
304pub fn openat(dirfd: i32, path: [*]const u8, flags: u32, mode: usize) usize {
305 // dirfd could be negative, for example AT_FDCWD is -100
306 return syscall4(SYS_openat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode);
307}
308
309/// See also `clone` (from the arch-specific include)
310pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: *i32, child_tid: *i32, newtls: usize) usize {
311 return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls);
312}
313
314/// See also `clone` (from the arch-specific include)
315pub fn clone2(flags: u32, child_stack_ptr: usize) usize {
316 return syscall2(SYS_clone, flags, child_stack_ptr);
317}
318
319pub fn close(fd: i32) usize {
320 return syscall1(SYS_close, @bitCast(usize, isize(fd)));
321}
322
323pub fn lseek(fd: i32, offset: isize, ref_pos: usize) usize {
324 return syscall3(SYS_lseek, @bitCast(usize, isize(fd)), @bitCast(usize, offset), ref_pos);
325}
326
327pub fn exit(status: i32) noreturn {
328 _ = syscall1(SYS_exit, @bitCast(usize, isize(status)));
329 unreachable;
330}
331
332pub fn exit_group(status: i32) noreturn {
333 _ = syscall1(SYS_exit_group, @bitCast(usize, isize(status)));
334 unreachable;
335}
336
337pub fn getrandom(buf: [*]u8, count: usize, flags: u32) usize {
338 return syscall3(SYS_getrandom, @ptrToInt(buf), count, flags);
339}
340
341pub fn kill(pid: i32, sig: i32) usize {
342 return syscall2(SYS_kill, @bitCast(usize, isize(pid)), @bitCast(usize, isize(sig)));
343}
344
345// TODO https://github.com/ziglang/zig/issues/265
346pub fn unlink(path: [*]const u8) usize {
347 if (@hasDecl(@This(), "SYS_unlink")) {
348 return syscall1(SYS_unlink, @ptrToInt(path));
349 } else {
350 return syscall3(SYS_unlinkat, AT_FDCWD, @ptrToInt(path), 0);
351 }
352}
353
354// TODO https://github.com/ziglang/zig/issues/265
355pub fn unlinkat(dirfd: i32, path: [*]const u8, flags: u32) usize {
356 return syscall3(SYS_unlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags);
357}
358
359pub fn waitpid(pid: i32, status: *i32, options: i32) usize {
360 return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0);
361}
362
363var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime);
364
365// We must follow the C calling convention when we call into the VDSO
366const vdso_clock_gettime_ty = extern fn (i32, *timespec) usize;
367
368pub fn clock_gettime(clk_id: i32, tp: *timespec) usize {
369 if (VDSO_CGT_SYM.len != 0) {
370 const ptr = @atomicLoad(?*const c_void, &vdso_clock_gettime, .Unordered);
371 if (ptr) |fn_ptr| {
372 const f = @ptrCast(vdso_clock_gettime_ty, fn_ptr);
373 const rc = f(clk_id, tp);
374 switch (rc) {
375 0, @bitCast(usize, isize(-EINVAL)) => return rc,
376 else => {},
377 }
378 }
379 }
380 return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
381}
382
383extern fn init_vdso_clock_gettime(clk: i32, ts: *timespec) usize {
384 const ptr = @intToPtr(?*const c_void, vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM));
385 // Note that we may not have a VDSO at all, update the stub address anyway
386 // so that clock_gettime will fall back on the good old (and slow) syscall
387 _ = @cmpxchgStrong(?*const c_void, &vdso_clock_gettime, &init_vdso_clock_gettime, ptr, .Monotonic, .Monotonic);
388 // Call into the VDSO if available
389 if (ptr) |fn_ptr| {
390 const f = @ptrCast(vdso_clock_gettime_ty, fn_ptr);
391 return f(clk, ts);
392 }
393 return @bitCast(usize, isize(-ENOSYS));
394}
395
396pub fn clock_getres(clk_id: i32, tp: *timespec) usize {
397 return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
398}
399
400pub fn clock_settime(clk_id: i32, tp: *const timespec) usize {
401 return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
402}
403
404pub fn gettimeofday(tv: *timeval, tz: *timezone) usize {
405 return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz));
406}
407
408pub fn settimeofday(tv: *const timeval, tz: *const timezone) usize {
409 return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz));
410}
411
412pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize {
413 return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem));
414}
415
416pub fn setuid(uid: u32) usize {
417 return syscall1(SYS_setuid, uid);
418}
419
420pub fn setgid(gid: u32) usize {
421 return syscall1(SYS_setgid, gid);
422}
423
424pub fn setreuid(ruid: u32, euid: u32) usize {
425 return syscall2(SYS_setreuid, ruid, euid);
426}
427
428pub fn setregid(rgid: u32, egid: u32) usize {
429 return syscall2(SYS_setregid, rgid, egid);
430}
431
432pub fn getuid() u32 {
433 return u32(syscall0(SYS_getuid));
434}
435
436pub fn getgid() u32 {
437 return u32(syscall0(SYS_getgid));
438}
439
440pub fn geteuid() u32 {
441 return u32(syscall0(SYS_geteuid));
442}
443
444pub fn getegid() u32 {
445 return u32(syscall0(SYS_getegid));
446}
447
448pub fn seteuid(euid: u32) usize {
449 return syscall1(SYS_seteuid, euid);
450}
451
452pub fn setegid(egid: u32) usize {
453 return syscall1(SYS_setegid, egid);
454}
455
456pub fn getresuid(ruid: *u32, euid: *u32, suid: *u32) usize {
457 return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid));
458}
459
460pub fn getresgid(rgid: *u32, egid: *u32, sgid: *u32) usize {
461 return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid));
462}
463
464pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize {
465 return syscall3(SYS_setresuid, ruid, euid, suid);
466}
467
468pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize {
469 return syscall3(SYS_setresgid, rgid, egid, sgid);
470}
471
472pub fn getgroups(size: usize, list: *u32) usize {
473 return syscall2(SYS_getgroups, size, @ptrToInt(list));
474}
475
476pub fn setgroups(size: usize, list: *const u32) usize {
477 return syscall2(SYS_setgroups, size, @ptrToInt(list));
478}
479
480pub fn getpid() i32 {
481 return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid)));
482}
483
484pub fn gettid() i32 {
485 return @bitCast(i32, @truncate(u32, syscall0(SYS_gettid)));
486}
487
488pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize {
489 return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8);
490}
491
492pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize {
493 assert(sig >= 1);
494 assert(sig != SIGKILL);
495 assert(sig != SIGSTOP);
496 var ksa = k_sigaction{
497 .handler = act.handler,
498 .flags = act.flags | SA_RESTORER,
499 .mask = undefined,
500 .restorer = @ptrCast(extern fn () void, restore_rt),
501 };
502 var ksa_old: k_sigaction = undefined;
503 @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &act.mask), 8);
504 const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask)));
505 const err = getErrno(result);
506 if (err != 0) {
507 return result;
508 }
509 if (oact) |old| {
510 old.handler = ksa_old.handler;
511 old.flags = @truncate(u32, ksa_old.flags);
512 @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask)));
513 }
514 return 0;
515}
516
517fn blockAllSignals(set: *sigset_t) void {
518 _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8);
519}
520
521fn blockAppSignals(set: *sigset_t) void {
522 _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG / 8);
523}
524
525fn restoreSignals(set: *sigset_t) void {
526 _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG / 8);
527}
528
529pub fn sigaddset(set: *sigset_t, sig: u6) void {
530 const s = sig - 1;
531 (set.*)[@intCast(usize, s) / usize.bit_count] |= @intCast(usize, 1) << (s & (usize.bit_count - 1));
532}
533
534pub fn sigismember(set: *const sigset_t, sig: u6) bool {
535 const s = sig - 1;
536 return ((set.*)[@intCast(usize, s) / usize.bit_count] & (@intCast(usize, 1) << (s & (usize.bit_count - 1)))) != 0;
537}
538
539pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
540 return syscall3(SYS_getsockname, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len));
541}
542
543pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
544 return syscall3(SYS_getpeername, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len));
545}
546
547pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize {
548 return syscall3(SYS_socket, domain, socket_type, protocol);
549}
550
551pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize {
552 return syscall5(SYS_setsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen));
553}
554
555pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize {
556 return syscall5(SYS_getsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen));
557}
558
559pub fn sendmsg(fd: i32, msg: *msghdr_const, flags: u32) usize {
560 return syscall3(SYS_sendmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags);
561}
562
563pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize {
564 if (@typeInfo(usize).Int.bits > @typeInfo(@typeOf(mmsghdr(undefined).msg_len)).Int.bits) {
565 // workaround kernel brokenness:
566 // if adding up all iov_len overflows a i32 then split into multiple calls
567 // see https://www.openwall.com/lists/musl/2014/06/07/5
568 const kvlen = if (vlen > IOV_MAX) IOV_MAX else vlen; // matches kernel
569 var next_unsent: usize = 0;
570 for (msgvec[0..kvlen]) |*msg, i| {
571 var size: i32 = 0;
572 const msg_iovlen = @intCast(usize, msg.msg_hdr.msg_iovlen); // kernel side this is treated as unsigned
573 for (msg.msg_hdr.msg_iov[0..msg_iovlen]) |iov, j| {
574 if (iov.iov_len > std.math.maxInt(i32) or @addWithOverflow(i32, size, @intCast(i32, iov.iov_len), &size)) {
575 // batch-send all messages up to the current message
576 if (next_unsent < i) {
577 const batch_size = i - next_unsent;
578 const r = syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags);
579 if (getErrno(r) != 0) return next_unsent;
580 if (r < batch_size) return next_unsent + r;
581 }
582 // send current message as own packet
583 const r = sendmsg(fd, &msg.msg_hdr, flags);
584 if (getErrno(r) != 0) return r;
585 // Linux limits the total bytes sent by sendmsg to INT_MAX, so this cast is safe.
586 msg.msg_len = @intCast(u32, r);
587 next_unsent = i + 1;
588 break;
589 }
590 }
591 }
592 if (next_unsent < kvlen or next_unsent == 0) { // want to make sure at least one syscall occurs (e.g. to trigger MSG_EOR)
593 const batch_size = kvlen - next_unsent;
594 const r = syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags);
595 if (getErrno(r) != 0) return r;
596 return next_unsent + r;
597 }
598 return kvlen;
599 }
600 return syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(msgvec), vlen, flags);
601}
602
603pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize {
604 return syscall3(SYS_connect, @bitCast(usize, isize(fd)), @ptrToInt(addr), len);
605}
606
607pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize {
608 return syscall3(SYS_recvmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags);
609}
610
611pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize {
612 return syscall6(SYS_recvfrom, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen));
613}
614
615pub fn shutdown(fd: i32, how: i32) usize {
616 return syscall2(SYS_shutdown, @bitCast(usize, isize(fd)), @bitCast(usize, isize(how)));
617}
618
619pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize {
620 return syscall3(SYS_bind, @bitCast(usize, isize(fd)), @ptrToInt(addr), @intCast(usize, len));
621}
622
623pub fn listen(fd: i32, backlog: u32) usize {
624 return syscall2(SYS_listen, @bitCast(usize, isize(fd)), backlog);
625}
626
627pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize {
628 return syscall6(SYS_sendto, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen));
629}
630
631pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize {
632 return syscall4(SYS_socketpair, @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0]));
633}
634
635pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
636 return accept4(fd, addr, len, 0);
637}
638
639pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize {
640 return syscall4(SYS_accept4, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len), flags);
641}
642
643pub fn fstat(fd: i32, stat_buf: *Stat) usize {
644 return syscall2(SYS_fstat, @bitCast(usize, isize(fd)), @ptrToInt(stat_buf));
645}
646
647// TODO https://github.com/ziglang/zig/issues/265
648pub fn stat(pathname: [*]const u8, statbuf: *Stat) usize {
649 return syscall2(SYS_stat, @ptrToInt(pathname), @ptrToInt(statbuf));
650}
651
652// TODO https://github.com/ziglang/zig/issues/265
653pub fn lstat(pathname: [*]const u8, statbuf: *Stat) usize {
654 return syscall2(SYS_lstat, @ptrToInt(pathname), @ptrToInt(statbuf));
655}
656
657// TODO https://github.com/ziglang/zig/issues/265
658pub fn fstatat(dirfd: i32, path: [*]const u8, stat_buf: *Stat, flags: u32) usize {
659 return syscall4(SYS_fstatat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(stat_buf), flags);
660}
661
662// TODO https://github.com/ziglang/zig/issues/265
663pub fn listxattr(path: [*]const u8, list: [*]u8, size: usize) usize {
664 return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size);
665}
666
667// TODO https://github.com/ziglang/zig/issues/265
668pub fn llistxattr(path: [*]const u8, list: [*]u8, size: usize) usize {
669 return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size);
670}
671
672pub fn flistxattr(fd: usize, list: [*]u8, size: usize) usize {
673 return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size);
674}
675
676// TODO https://github.com/ziglang/zig/issues/265
677pub fn getxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize {
678 return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size);
679}
680
681// TODO https://github.com/ziglang/zig/issues/265
682pub fn lgetxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize {
683 return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size);
684}
685
686// TODO https://github.com/ziglang/zig/issues/265
687pub fn fgetxattr(fd: usize, name: [*]const u8, value: [*]u8, size: usize) usize {
688 return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size);
689}
690
691// TODO https://github.com/ziglang/zig/issues/265
692pub fn setxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize {
693 return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags);
694}
695
696// TODO https://github.com/ziglang/zig/issues/265
697pub fn lsetxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize {
698 return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags);
699}
700
701// TODO https://github.com/ziglang/zig/issues/265
702pub fn fsetxattr(fd: usize, name: [*]const u8, value: *const void, size: usize, flags: usize) usize {
703 return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), size, flags);
704}
705
706// TODO https://github.com/ziglang/zig/issues/265
707pub fn removexattr(path: [*]const u8, name: [*]const u8) usize {
708 return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name));
709}
710
711// TODO https://github.com/ziglang/zig/issues/265
712pub fn lremovexattr(path: [*]const u8, name: [*]const u8) usize {
713 return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name));
714}
715
716// TODO https://github.com/ziglang/zig/issues/265
717pub fn fremovexattr(fd: usize, name: [*]const u8) usize {
718 return syscall2(SYS_fremovexattr, fd, @ptrToInt(name));
719}
720
721pub fn sched_getaffinity(pid: i32, set: []usize) usize {
722 return syscall3(SYS_sched_getaffinity, @bitCast(usize, isize(pid)), set.len * @sizeOf(usize), @ptrToInt(set.ptr));
723}
724
725pub fn epoll_create() usize {
726 return epoll_create1(0);
727}
728
729pub fn epoll_create1(flags: usize) usize {
730 return syscall1(SYS_epoll_create1, flags);
731}
732
733pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: *epoll_event) usize {
734 return syscall4(SYS_epoll_ctl, @bitCast(usize, isize(epoll_fd)), @intCast(usize, op), @bitCast(usize, isize(fd)), @ptrToInt(ev));
735}
736
737pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32) usize {
738 return syscall4(
739 SYS_epoll_wait,
740 @bitCast(usize, isize(epoll_fd)),
741 @ptrToInt(events),
742 maxevents,
743 @bitCast(usize, isize(timeout)),
744 );
745}
746
747pub fn epoll_pwait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32, sigmask: ?*sigset_t) usize {
748 return syscall6(
749 SYS_epoll_pwait,
750 @bitCast(usize, isize(epoll_fd)),
751 @ptrToInt(events),
752 @intCast(usize, maxevents),
753 @bitCast(usize, isize(timeout)),
754 @ptrToInt(sigmask),
755 @sizeOf(sigset_t),
756 );
757}
758
759pub fn eventfd(count: u32, flags: u32) usize {
760 return syscall2(SYS_eventfd2, count, flags);
761}
762
763pub fn timerfd_create(clockid: i32, flags: u32) usize {
764 return syscall2(SYS_timerfd_create, @bitCast(usize, isize(clockid)), flags);
765}
766
767pub const itimerspec = extern struct {
768 it_interval: timespec,
769 it_value: timespec,
770};
771
772pub fn timerfd_gettime(fd: i32, curr_value: *itimerspec) usize {
773 return syscall2(SYS_timerfd_gettime, @bitCast(usize, isize(fd)), @ptrToInt(curr_value));
774}
775
776pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const itimerspec, old_value: ?*itimerspec) usize {
777 return syscall4(SYS_timerfd_settime, @bitCast(usize, isize(fd)), flags, @ptrToInt(new_value), @ptrToInt(old_value));
778}
779
780pub fn unshare(flags: usize) usize {
781 return syscall1(SYS_unshare, flags);
782}
783
784pub fn capget(hdrp: *cap_user_header_t, datap: *cap_user_data_t) usize {
785 return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap));
786}
787
788pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize {
789 return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
790}
791
792// XXX: This should be weak
793extern const __ehdr_start: elf.Ehdr = undefined;
794
795pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_info, size: usize, data: ?*T) i32, data: ?*T) isize {
796 if (builtin.link_libc) {
797 return std.c.dl_iterate_phdr(@ptrCast(std.c.dl_iterate_phdr_callback, callback), @ptrCast(?*c_void, data));
798 }
799
800 const elf_base = @ptrToInt(&__ehdr_start);
801 const n_phdr = __ehdr_start.e_phnum;
802 const phdrs = (@intToPtr([*]elf.Phdr, elf_base + __ehdr_start.e_phoff))[0..n_phdr];
803
804 var it = dl.linkmap_iterator(phdrs) catch return 0;
805
806 // The executable has no dynamic link segment, create a single entry for
807 // the whole ELF image
808 if (it.end()) {
809 var info = dl_phdr_info{
810 .dlpi_addr = elf_base,
811 .dlpi_name = c"/proc/self/exe",
812 .dlpi_phdr = @intToPtr([*]elf.Phdr, elf_base + __ehdr_start.e_phoff),
813 .dlpi_phnum = __ehdr_start.e_phnum,
814 };
815
816 return callback(&info, @sizeOf(dl_phdr_info), data);
817 }
818
819 // Last return value from the callback function
820 var last_r: isize = 0;
821 while (it.next()) |entry| {
822 var dlpi_phdr: usize = undefined;
823 var dlpi_phnum: u16 = undefined;
824
825 if (entry.l_addr != 0) {
826 const elf_header = @intToPtr(*elf.Ehdr, entry.l_addr);
827 dlpi_phdr = entry.l_addr + elf_header.e_phoff;
828 dlpi_phnum = elf_header.e_phnum;
829 } else {
830 // This is the running ELF image
831 dlpi_phdr = elf_base + __ehdr_start.e_phoff;
832 dlpi_phnum = __ehdr_start.e_phnum;
833 }
834
835 var info = dl_phdr_info{
836 .dlpi_addr = entry.l_addr,
837 .dlpi_name = entry.l_name,
838 .dlpi_phdr = @intToPtr([*]elf.Phdr, dlpi_phdr),
839 .dlpi_phnum = dlpi_phnum,
840 };
841
842 last_r = callback(&info, @sizeOf(dl_phdr_info), data);
843 if (last_r != 0) break;
844 }
845
846 return last_r;
847}
std/os/posix.zig+157-220
...@@ -2,21 +2,18 @@...@@ -2,21 +2,18 @@
2// The purpose is not to match POSIX as closely as possible. Instead,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:3// the goal is to provide a very specific layer of abstraction:
4// * Implement the POSIX functions, types, and definitions where possible,4// * Implement the POSIX functions, types, and definitions where possible,
5// using lower-level target-specific API. For example, on Linux `rename` might call5// using lower-level target-specific API.
6// SYS_renameat or SYS_rename depending on the architecture.
7// * When null-terminated byte buffers are required, provide APIs which accept6// * When null-terminated byte buffers are required, provide APIs which accept
8// slices as well as APIs which accept null-terminated byte buffers. Same goes7// slices as well as APIs which accept null-terminated byte buffers. Same goes
9// for UTF-16LE encoding.8// for UTF-16LE encoding.
10// * Convert "errno"-style error codes into Zig errors.9// * 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 Zig10// * Implement the OS-specific functions, types, and definitions that the Zig
16// standard library needs, at the same API abstraction layer as outlined above.11// standard library needs, at the same API abstraction layer as outlined above.
17// this includes, for example Windows functions.12// For example kevent() and getrandom(). Windows-specific functions are separate,
18// * When there exists a corresponding libc function and linking libc, call the13// in `std.os.windows`.
19// libc function.14// * When there exists a corresponding libc function and linking libc, the libc
15// implementation is used. Exceptions are made for known buggy areas of libc.
16// On Linux libc can be side-stepped by using `std.os.linux.sys`.
20// Note: The Zig standard library does not support POSIX thread cancellation, and17// Note: The Zig standard library does not support POSIX thread cancellation, and
21// in general EINTR is handled by trying again.18// in general EINTR is handled by trying again.
2219
...@@ -29,18 +26,13 @@ const mem = std.mem;...@@ -29,18 +26,13 @@ const mem = std.mem;
29const BufMap = std.BufMap;26const BufMap = std.BufMap;
30const Allocator = mem.Allocator;27const Allocator = mem.Allocator;
31const windows = os.windows;28const windows = os.windows;
29const kernel32 = windows.kernel32;
32const wasi = os.wasi;30const wasi = os.wasi;
33const linux = os.linux;31const linux = os.linux;
34const testing = std.testing;32const testing = std.testing;
3533
36pub use system.posix;34pub use system.posix;
3735
38/// > The maximum path of 32,767 characters is approximate, because the "\\?\"
39/// > prefix may be expanded to a longer string by the system at run time, and
40/// > this expansion applies to the total length.
41/// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
42pub const PATH_MAX_WIDE = 32767;
43
44/// See also `getenv`.36/// See also `getenv`.
45pub var environ: [][*]u8 = undefined;37pub var environ: [][*]u8 = undefined;
4638
...@@ -59,7 +51,7 @@ pub const errno = system.getErrno;...@@ -59,7 +51,7 @@ pub const errno = system.getErrno;
59/// Note: The Zig standard library does not support POSIX thread cancellation.51/// Note: The Zig standard library does not support POSIX thread cancellation.
60pub fn close(fd: fd_t) void {52pub fn close(fd: fd_t) void {
61 if (windows.is_the_target and !builtin.link_libc) {53 if (windows.is_the_target and !builtin.link_libc) {
62 assert(windows.CloseHandle(fd) != 0);54 assert(kernel32.CloseHandle(fd) != 0);
63 return;55 return;
64 }56 }
65 if (wasi.is_the_target) {57 if (wasi.is_the_target) {
...@@ -87,11 +79,10 @@ pub fn getrandom(buf: []u8) GetRandomError!void {...@@ -87,11 +79,10 @@ pub fn getrandom(buf: []u8) GetRandomError!void {
87 // Call RtlGenRandom() instead of CryptGetRandom() on Windows79 // Call RtlGenRandom() instead of CryptGetRandom() on Windows
88 // https://github.com/rust-lang-nursery/rand/issues/11180 // https://github.com/rust-lang-nursery/rand/issues/111
89 // https://bugzilla.mozilla.org/show_bug.cgi?id=50427081 // https://bugzilla.mozilla.org/show_bug.cgi?id=504270
90 if (windows.RtlGenRandom(buf.ptr, buf.len) == 0) {82 if (windows.advapi32.RtlGenRandom(buf.ptr, buf.len) == 0) {
91 const err = windows.GetLastError();83 switch (kernel32.GetLastError()) {
92 return switch (err) {84 else => |err| return windows.unexpectedError(err),
93 else => unexpectedErrorWindows(err),85 }
94 };
95 }86 }
96 return;87 return;
97 }88 }
...@@ -230,12 +221,11 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {...@@ -230,12 +221,11 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
230 const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(math.maxInt(windows.DWORD)), buffer.len - index));221 const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(math.maxInt(windows.DWORD)), buffer.len - index));
231 var amt_read: windows.DWORD = undefined;222 var amt_read: windows.DWORD = undefined;
232 if (windows.ReadFile(fd, buffer.ptr + index, want_read_count, &amt_read, null) == 0) {223 if (windows.ReadFile(fd, buffer.ptr + index, want_read_count, &amt_read, null) == 0) {
233 const err = windows.GetLastError();224 switch (windows.GetLastError()) {
234 return switch (err) {
235 windows.ERROR.OPERATION_ABORTED => continue,225 windows.ERROR.OPERATION_ABORTED => continue,
236 windows.ERROR.BROKEN_PIPE => return index,226 windows.ERROR.BROKEN_PIPE => return index,
237 else => unexpectedErrorWindows(err),227 else => |err| return windows.unexpectedError(err),
238 };228 }
239 }229 }
240 if (amt_read == 0) return index;230 if (amt_read == 0) return index;
241 index += amt_read;231 index += amt_read;
...@@ -372,7 +362,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {...@@ -372,7 +362,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {
372 windows.ERROR.NOT_ENOUGH_QUOTA => return error.SystemResources,362 windows.ERROR.NOT_ENOUGH_QUOTA => return error.SystemResources,
373 windows.ERROR.IO_PENDING => unreachable,363 windows.ERROR.IO_PENDING => unreachable,
374 windows.ERROR.BROKEN_PIPE => return error.BrokenPipe,364 windows.ERROR.BROKEN_PIPE => return error.BrokenPipe,
375 else => |err| return unexpectedErrorWindows(err),365 else => |err| return windows.unexpectedError(err),
376 }366 }
377 }367 }
378 }368 }
...@@ -543,67 +533,6 @@ pub fn openC(file_path: [*]const u8, flags: u32, perm: usize) OpenError!fd_t {...@@ -543,67 +533,6 @@ pub fn openC(file_path: [*]const u8, flags: u32, perm: usize) OpenError!fd_t {
543 }533 }
544}534}
545535
546pub const WindowsOpenError = error{
547 SharingViolation,
548 PathAlreadyExists,
549
550 /// When any of the path components can not be found or the file component can not
551 /// be found. Some operating systems distinguish between path components not found and
552 /// file components not found, but they are collapsed into FileNotFound to gain
553 /// consistency across operating systems.
554 FileNotFound,
555
556 AccessDenied,
557 PipeBusy,
558 NameTooLong,
559
560 /// On Windows, file paths must be valid Unicode.
561 InvalidUtf8,
562
563 /// On Windows, file paths cannot contain these characters:
564 /// '/', '*', '?', '"', '<', '>', '|'
565 BadPathName,
566
567 Unexpected,
568};
569
570pub fn openWindows(
571 file_path: []const u8,
572 desired_access: windows.DWORD,
573 share_mode: windows.DWORD,
574 creation_disposition: windows.DWORD,
575 flags_and_attrs: windows.DWORD,
576) WindowsOpenError!fd_t {
577 const file_path_w = try sliceToPrefixedFileW(file_path);
578 return openW(&file_path_w, desired_access, share_mode, creation_disposition, flags_and_attrs);
579}
580
581pub fn openW(
582 file_path_w: [*]const u16,
583 desired_access: windows.DWORD,
584 share_mode: windows.DWORD,
585 creation_disposition: windows.DWORD,
586 flags_and_attrs: windows.DWORD,
587) WindowsOpenError!windows.HANDLE {
588 const result = windows.CreateFileW(file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null);
589
590 if (result == windows.INVALID_HANDLE_VALUE) {
591 const err = windows.GetLastError();
592 switch (err) {
593 windows.ERROR.SHARING_VIOLATION => return error.SharingViolation,
594 windows.ERROR.ALREADY_EXISTS => return error.PathAlreadyExists,
595 windows.ERROR.FILE_EXISTS => return error.PathAlreadyExists,
596 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
597 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
598 windows.ERROR.ACCESS_DENIED => return error.AccessDenied,
599 windows.ERROR.PIPE_BUSY => return error.PipeBusy,
600 else => return unexpectedErrorWindows(err),
601 }
602 }
603
604 return result;
605}
606
607pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {536pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
608 while (true) {537 while (true) {
609 switch (errno(system.dup2(old_fd, new_fd))) {538 switch (errno(system.dup2(old_fd, new_fd))) {
...@@ -797,14 +726,13 @@ pub const GetCwdError = error{...@@ -797,14 +726,13 @@ pub const GetCwdError = error{
797/// The result is a slice of out_buffer, indexed from 0.726/// The result is a slice of out_buffer, indexed from 0.
798pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {727pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
799 if (windows.is_the_target and !builtin.link_libc) {728 if (windows.is_the_target and !builtin.link_libc) {
800 var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;729 var utf16le_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
801 const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast730 const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast
802 const casted_ptr = ([*]u16)(&utf16le_buf); // TODO shouldn't need this cast731 const casted_ptr = ([*]u16)(&utf16le_buf); // TODO shouldn't need this cast
803 const result = windows.GetCurrentDirectoryW(casted_len, casted_ptr);732 const result = windows.GetCurrentDirectoryW(casted_len, casted_ptr);
804 if (result == 0) {733 if (result == 0) {
805 const err = windows.GetLastError();734 switch (windows.GetLastError()) {
806 switch (err) {735 else => |err| return windows.unexpectedError(err),
807 else => return unexpectedErrorWindows(err),
808 }736 }
809 }737 }
810 assert(result <= utf16le_buf.len);738 assert(result <= utf16le_buf.len);
...@@ -910,12 +838,7 @@ pub fn symlinkC(target_path: [*]const u8, new_path: [*]const u8) SymLinkError!vo...@@ -910,12 +838,7 @@ pub fn symlinkC(target_path: [*]const u8, new_path: [*]const u8) SymLinkError!vo
910 const new_path_w = try cStrToPrefixedFileW(new_path);838 const new_path_w = try cStrToPrefixedFileW(new_path);
911 return symlinkW(&target_path_w, &new_path_w);839 return symlinkW(&target_path_w, &new_path_w);
912 }840 }
913 const err = if (builtin.link_libc or @hasDecl(system, "SYS_symlink")) blk: {841 switch (errno(system.symlink(target_path, new_path))) {
914 break :blk errno(system.symlink(target_path, new_path));
915 } else blk: {
916 break :blk errno(system.symlinkat(target_path, AT_FDCWD, new_path));
917 };
918 switch (err) {
919 0 => return,842 0 => return,
920 EFAULT => unreachable,843 EFAULT => unreachable,
921 EINVAL => unreachable,844 EINVAL => unreachable,
...@@ -931,7 +854,7 @@ pub fn symlinkC(target_path: [*]const u8, new_path: [*]const u8) SymLinkError!vo...@@ -931,7 +854,7 @@ pub fn symlinkC(target_path: [*]const u8, new_path: [*]const u8) SymLinkError!vo
931 ENOMEM => return error.SystemResources,854 ENOMEM => return error.SystemResources,
932 ENOSPC => return error.NoSpaceLeft,855 ENOSPC => return error.NoSpaceLeft,
933 EROFS => return error.ReadOnlyFileSystem,856 EROFS => return error.ReadOnlyFileSystem,
934 else => return unexpectedErrno(err),857 else => |err| return unexpectedErrno(err),
935 }858 }
936}859}
937860
...@@ -941,9 +864,8 @@ pub fn symlinkC(target_path: [*]const u8, new_path: [*]const u8) SymLinkError!vo...@@ -941,9 +864,8 @@ pub fn symlinkC(target_path: [*]const u8, new_path: [*]const u8) SymLinkError!vo
941/// TODO handle when linking libc864/// TODO handle when linking libc
942pub fn symlinkW(target_path_w: [*]const u16, new_path_w: [*]const u16) SymLinkError!void {865pub fn symlinkW(target_path_w: [*]const u16, new_path_w: [*]const u16) SymLinkError!void {
943 if (windows.CreateSymbolicLinkW(target_path_w, new_path_w, 0) == 0) {866 if (windows.CreateSymbolicLinkW(target_path_w, new_path_w, 0) == 0) {
944 const err = windows.GetLastError();867 switch (windows.GetLastError()) {
945 switch (err) {868 else => |err| return windows.unexpectedError(err),
946 else => return unexpectedErrorWindows(err),
947 }869 }
948 }870 }
949}871}
...@@ -984,13 +906,12 @@ pub fn unlink(file_path: []const u8) UnlinkError!void {...@@ -984,13 +906,12 @@ pub fn unlink(file_path: []const u8) UnlinkError!void {
984/// TODO handle when linking libc906/// TODO handle when linking libc
985pub fn unlinkW(file_path: [*]const u16) UnlinkError!void {907pub fn unlinkW(file_path: [*]const u16) UnlinkError!void {
986 if (windows.unlinkW(file_path) == 0) {908 if (windows.unlinkW(file_path) == 0) {
987 const err = windows.GetLastError();909 switch (windows.GetLastError()) {
988 switch (err) {
989 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,910 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
990 windows.ERROR.ACCESS_DENIED => return error.AccessDenied,911 windows.ERROR.ACCESS_DENIED => return error.AccessDenied,
991 windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,912 windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
992 windows.ERROR.INVALID_PARAMETER => return error.NameTooLong,913 windows.ERROR.INVALID_PARAMETER => return error.NameTooLong,
993 else => return unexpectedErrorWindows(err),914 else => |err| return windows.unexpectedError(err),
994 }915 }
995 }916 }
996}917}
...@@ -1001,12 +922,7 @@ pub fn unlinkC(file_path: [*]const u8) UnlinkError!void {...@@ -1001,12 +922,7 @@ pub fn unlinkC(file_path: [*]const u8) UnlinkError!void {
1001 const file_path_w = try cStrToPrefixedFileW(file_path);922 const file_path_w = try cStrToPrefixedFileW(file_path);
1002 return unlinkW(&file_path_w);923 return unlinkW(&file_path_w);
1003 }924 }
1004 const err = if (builtin.link_libc or @hasDecl(system, "SYS_unlink")) blk: {925 switch (errno(system.unlink(file_path))) {
1005 break :blk errno(system.unlink(file_path));
1006 } else blk: {
1007 break :blk errno(system.unlinkat(AT_FDCWD, file_path, 0));
1008 };
1009 switch (err) {
1010 0 => return,926 0 => return,
1011 EACCES => return error.AccessDenied,927 EACCES => return error.AccessDenied,
1012 EPERM => return error.AccessDenied,928 EPERM => return error.AccessDenied,
...@@ -1021,7 +937,7 @@ pub fn unlinkC(file_path: [*]const u8) UnlinkError!void {...@@ -1021,7 +937,7 @@ pub fn unlinkC(file_path: [*]const u8) UnlinkError!void {
1021 ENOTDIR => return error.NotDir,937 ENOTDIR => return error.NotDir,
1022 ENOMEM => return error.SystemResources,938 ENOMEM => return error.SystemResources,
1023 EROFS => return error.ReadOnlyFileSystem,939 EROFS => return error.ReadOnlyFileSystem,
1024 else => return unexpectedErrno(err),940 else => |err| return unexpectedErrno(err),
1025 }941 }
1026}942}
1027943
...@@ -1047,14 +963,7 @@ pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void {...@@ -1047,14 +963,7 @@ pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void {
1047 const new_path_w = try cStrToPrefixedFileW(new_path);963 const new_path_w = try cStrToPrefixedFileW(new_path);
1048 return renameW(&old_path_w, &new_path_w);964 return renameW(&old_path_w, &new_path_w);
1049 }965 }
1050 const err = if (builtin.link_libc or @hasDecl(system, "SYS_rename")) blk: {966 switch (errno(system.rename(old_path, new_path))) {
1051 break :blk errno(system.rename(old_path, new_path));
1052 } else if (@hasDecl(system, "SYS_renameat")) blk: {
1053 break :blk errno(system.renameat(AT_FDCWD, old_path, AT_FDCWD, new_path));
1054 } else blk: {
1055 break :blk errno(system.renameat2(AT_FDCWD, old_path, AT_FDCWD, new_path, 0));
1056 };
1057 switch (err) {
1058 0 => return,967 0 => return,
1059 EACCES => return error.AccessDenied,968 EACCES => return error.AccessDenied,
1060 EPERM => return error.AccessDenied,969 EPERM => return error.AccessDenied,
...@@ -1074,7 +983,7 @@ pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void {...@@ -1074,7 +983,7 @@ pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void {
1074 ENOTEMPTY => return error.PathAlreadyExists,983 ENOTEMPTY => return error.PathAlreadyExists,
1075 EROFS => return error.ReadOnlyFileSystem,984 EROFS => return error.ReadOnlyFileSystem,
1076 EXDEV => return error.RenameAcrossMountPoints,985 EXDEV => return error.RenameAcrossMountPoints,
1077 else => return unexpectedErrno(err),986 else => |err| return unexpectedErrno(err),
1078 }987 }
1079}988}
1080989
...@@ -1083,9 +992,8 @@ pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void {...@@ -1083,9 +992,8 @@ pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void {
1083pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) RenameError!void {992pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) RenameError!void {
1084 const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;993 const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
1085 if (windows.MoveFileExW(old_path, new_path, flags) == 0) {994 if (windows.MoveFileExW(old_path, new_path, flags) == 0) {
1086 const err = windows.GetLastError();995 switch (windows.GetLastError()) {
1087 switch (err) {996 else => |err| return windows.unexpectedError(err),
1088 else => return unexpectedErrorWindows(err),
1089 }997 }
1090 }998 }
1091}999}
...@@ -1110,12 +1018,7 @@ pub fn mkdirC(dir_path: [*]const u8, mode: u32) MakeDirError!void {...@@ -1110,12 +1018,7 @@ pub fn mkdirC(dir_path: [*]const u8, mode: u32) MakeDirError!void {
1110 const dir_path_w = try cStrToPrefixedFileW(dir_path);1018 const dir_path_w = try cStrToPrefixedFileW(dir_path);
1111 return mkdirW(&dir_path_w, mode);1019 return mkdirW(&dir_path_w, mode);
1112 }1020 }
1113 const err = if (builtin.link_libc or @hasDecl(system, "SYS_mkdir")) blk: {1021 switch (errno(system.mkdir(dir_path, mode))) {
1114 break :blk errno(system.mkdir(dir_path, mode));
1115 } else blk: {
1116 break :blk errno(system.mkdirat(AT_FDCWD, dir_path, mode));
1117 };
1118 switch (err) {
1119 0 => return,1022 0 => return,
1120 EACCES => return error.AccessDenied,1023 EACCES => return error.AccessDenied,
1121 EPERM => return error.AccessDenied,1024 EPERM => return error.AccessDenied,
...@@ -1130,7 +1033,7 @@ pub fn mkdirC(dir_path: [*]const u8, mode: u32) MakeDirError!void {...@@ -1130,7 +1033,7 @@ pub fn mkdirC(dir_path: [*]const u8, mode: u32) MakeDirError!void {
1130 ENOSPC => return error.NoSpaceLeft,1033 ENOSPC => return error.NoSpaceLeft,
1131 ENOTDIR => return error.NotDir,1034 ENOTDIR => return error.NotDir,
1132 EROFS => return error.ReadOnlyFileSystem,1035 EROFS => return error.ReadOnlyFileSystem,
1133 else => return unexpectedErrno(err),1036 else => |err| return unexpectedErrno(err),
1134 }1037 }
1135}1038}
11361039
...@@ -1139,11 +1042,10 @@ pub fn mkdirW(dir_path: []const u8, mode: u32) MakeDirError!void {...@@ -1139,11 +1042,10 @@ pub fn mkdirW(dir_path: []const u8, mode: u32) MakeDirError!void {
1139 const dir_path_w = try sliceToPrefixedFileW(dir_path);1042 const dir_path_w = try sliceToPrefixedFileW(dir_path);
11401043
1141 if (windows.CreateDirectoryW(&dir_path_w, null) == 0) {1044 if (windows.CreateDirectoryW(&dir_path_w, null) == 0) {
1142 const err = windows.GetLastError();1045 switch (windows.GetLastError()) {
1143 switch (err) {
1144 windows.ERROR.ALREADY_EXISTS => return error.PathAlreadyExists,1046 windows.ERROR.ALREADY_EXISTS => return error.PathAlreadyExists,
1145 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,1047 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
1146 else => return unexpectedErrorWindows(err),1048 else => |err| return windows.unexpectedError(err),
1147 }1049 }
1148 }1050 }
1149}1051}
...@@ -1180,12 +1082,7 @@ pub fn rmdirC(dir_path: [*]const u8) DeleteDirError!void {...@@ -1180,12 +1082,7 @@ pub fn rmdirC(dir_path: [*]const u8) DeleteDirError!void {
1180 const dir_path_w = try cStrToPrefixedFileW(dir_path);1082 const dir_path_w = try cStrToPrefixedFileW(dir_path);
1181 return rmdirW(&dir_path_w);1083 return rmdirW(&dir_path_w);
1182 }1084 }
1183 const err = if (builtin.link_libc or @hasDecl(system, "SYS_rmdir")) blk: {1085 switch (errno(system.rmdir(dir_path))) {
1184 break :blk errno(system.rmdir(dir_path));
1185 } else blk: {
1186 break :blk errno(system.unlinkat(AT_FDCWD, dir_path, AT_REMOVEDIR));
1187 };
1188 switch (err) {
1189 0 => return,1086 0 => return,
1190 EACCES => return error.AccessDenied,1087 EACCES => return error.AccessDenied,
1191 EPERM => return error.AccessDenied,1088 EPERM => return error.AccessDenied,
...@@ -1200,7 +1097,7 @@ pub fn rmdirC(dir_path: [*]const u8) DeleteDirError!void {...@@ -1200,7 +1097,7 @@ pub fn rmdirC(dir_path: [*]const u8) DeleteDirError!void {
1200 EEXIST => return error.DirNotEmpty,1097 EEXIST => return error.DirNotEmpty,
1201 ENOTEMPTY => return error.DirNotEmpty,1098 ENOTEMPTY => return error.DirNotEmpty,
1202 EROFS => return error.ReadOnlyFileSystem,1099 EROFS => return error.ReadOnlyFileSystem,
1203 else => return unexpectedErrno(err),1100 else => |err| return unexpectedErrno(err),
1204 }1101 }
1205}1102}
12061103
...@@ -1208,11 +1105,10 @@ pub fn rmdirC(dir_path: [*]const u8) DeleteDirError!void {...@@ -1208,11 +1105,10 @@ pub fn rmdirC(dir_path: [*]const u8) DeleteDirError!void {
1208/// TODO handle linking libc1105/// TODO handle linking libc
1209pub fn rmdirW(dir_path_w: [*]const u16) DeleteDirError!void {1106pub fn rmdirW(dir_path_w: [*]const u16) DeleteDirError!void {
1210 if (windows.RemoveDirectoryW(dir_path_w) == 0) {1107 if (windows.RemoveDirectoryW(dir_path_w) == 0) {
1211 const err = windows.GetLastError();1108 switch (windows.GetLastError()) {
1212 switch (err) {
1213 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,1109 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
1214 windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty,1110 windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty,
1215 else => return unexpectedErrorWindows(err),1111 else => |err| return windows.unexpectedError(err),
1216 }1112 }
1217 }1113 }
1218}1114}
...@@ -1277,11 +1173,7 @@ pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {...@@ -1277,11 +1173,7 @@ pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {
1277 const file_path_w = try cStrToPrefixedFileW(file_path);1173 const file_path_w = try cStrToPrefixedFileW(file_path);
1278 return readlinkW(&file_path_w, out_buffer);1174 return readlinkW(&file_path_w, out_buffer);
1279 }1175 }
1280 const rc = if (builtin.link_libc or @hasDecl(system, "SYS_readlink")) blk: {1176 const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
1281 break :blk system.readlink(file_path, out_buffer.ptr, out_buffer.len);
1282 } else blk: {
1283 break :blk system.readlinkat(AT_FDCWD, file_path, out_buffer.ptr, out_buffer.len);
1284 };
1285 switch (errno(rc)) {1177 switch (errno(rc)) {
1286 0 => return out_buffer[0..rc],1178 0 => return out_buffer[0..rc],
1287 EACCES => return error.AccessDenied,1179 EACCES => return error.AccessDenied,
...@@ -1354,7 +1246,7 @@ pub fn GetStdHandle(handle_id: windows.DWORD) GetStdHandleError!fd_t {...@@ -1354,7 +1246,7 @@ pub fn GetStdHandle(handle_id: windows.DWORD) GetStdHandleError!fd_t {
1354 const handle = windows.GetStdHandle(handle_id) orelse return error.NoStandardHandleAttached;1246 const handle = windows.GetStdHandle(handle_id) orelse return error.NoStandardHandleAttached;
1355 if (handle == windows.INVALID_HANDLE_VALUE) {1247 if (handle == windows.INVALID_HANDLE_VALUE) {
1356 switch (windows.GetLastError()) {1248 switch (windows.GetLastError()) {
1357 else => |err| unexpectedErrorWindows(err),1249 else => |err| windows.unexpectedError(err),
1358 }1250 }
1359 }1251 }
1360 return handle;1252 return handle;
...@@ -2070,17 +1962,12 @@ pub const ForkError = error{...@@ -2070,17 +1962,12 @@ pub const ForkError = error{
2070};1962};
20711963
2072pub fn fork() ForkError!pid_t {1964pub fn fork() ForkError!pid_t {
2073 if (builtin.link_libc) {1965 const rc = system.fork();
2074 return system.fork();1966 switch (errno(rc)) {
2075 }1967 0 => return rc,
2076 if (linux.is_the_target) {1968 EAGAIN => return error.SystemResources,
2077 const rc = if (@hasDecl(system, "SYS_fork")) system.fork() else system.clone2(SIGCHLD, 0);1969 ENOMEM => return error.SystemResources,
2078 switch (errno(rc)) {1970 else => |err| return unexpectedErrno(err),
2079 0 => return rc,
2080 EAGAIN => return error.SystemResources,
2081 ENOMEM => return error.SystemResources,
2082 else => |err| return unexpectedErrno(err),
2083 }
2084 }1971 }
2085}1972}
20861973
...@@ -2173,7 +2060,7 @@ pub fn accessW(path: [*]const u16, mode: u32) AccessError!void {...@@ -2173,7 +2060,7 @@ pub fn accessW(path: [*]const u16, mode: u32) AccessError!void {
2173 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,2060 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
2174 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,2061 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
2175 windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,2062 windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
2176 else => |err| return unexpectedErrorWindows(err),2063 else => |err| return windows.unexpectedError(err),
2177 }2064 }
2178}2065}
21792066
...@@ -2209,11 +2096,7 @@ pub const PipeError = error{...@@ -2209,11 +2096,7 @@ pub const PipeError = error{
22092096
2210/// Creates a unidirectional data channel that can be used for interprocess communication.2097/// Creates a unidirectional data channel that can be used for interprocess communication.
2211pub fn pipe(fds: *[2]fd_t) PipeError!void {2098pub fn pipe(fds: *[2]fd_t) PipeError!void {
2212 const rc = if (builtin.link_libc or @hasDecl(system, SYS_pipe))2099 switch (errno(system.pipe(fds))) {
2213 system.pipe(fds)
2214 else
2215 system.pipe2(fds, 0);
2216 switch (errno(rc)) {
2217 0 => return,2100 0 => return,
2218 EINVAL => unreachable, // Invalid parameters to pipe()2101 EINVAL => unreachable, // Invalid parameters to pipe()
2219 EFAULT => unreachable, // Invalid fds pointer2102 EFAULT => unreachable, // Invalid fds pointer
...@@ -2296,6 +2179,112 @@ pub const realpath = std.os.path.real;...@@ -2296,6 +2179,112 @@ pub const realpath = std.os.path.real;
2296pub const realpathC = std.os.path.realC;2179pub const realpathC = std.os.path.realC;
2297pub const realpathW = std.os.path.realW;2180pub const realpathW = std.os.path.realW;
22982181
2182pub const WaitForSingleObjectError = error{
2183 WaitAbandoned,
2184 WaitTimeOut,
2185 Unexpected,
2186};
2187
2188pub fn WaitForSingleObject(handle: windows.HANDLE, milliseconds: windows.DWORD) WaitForSingleObjectError!void {
2189 switch (windows.WaitForSingleObject(handle, milliseconds)) {
2190 windows.WAIT_ABANDONED => return error.WaitAbandoned,
2191 windows.WAIT_OBJECT_0 => return,
2192 windows.WAIT_TIMEOUT => return error.WaitTimeOut,
2193 windows.WAIT_FAILED => {
2194 switch (windows.GetLastError()) {
2195 else => |err| return windows.unexpectedError(err),
2196 }
2197 },
2198 else => return error.Unexpected,
2199 }
2200}
2201
2202pub fn FindFirstFile(
2203 dir_path: []const u8,
2204 find_file_data: *windows.WIN32_FIND_DATAW,
2205) !windows.HANDLE {
2206 const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{ '\\', '*', 0 });
2207 const handle = windows.FindFirstFileW(&dir_path_w, find_file_data);
2208
2209 if (handle == windows.INVALID_HANDLE_VALUE) {
2210 switch (windows.GetLastError()) {
2211 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
2212 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
2213 else => |err| return windows.unexpectedError(err),
2214 }
2215 }
2216
2217 return handle;
2218}
2219
2220/// Returns `true` if there was another file, `false` otherwise.
2221pub fn FindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAW) !bool {
2222 if (windows.FindNextFileW(handle, find_file_data) == 0) {
2223 switch (windows.GetLastError()) {
2224 windows.ERROR.NO_MORE_FILES => return false,
2225 else => |err| return windows.unexpectedError(err),
2226 }
2227 }
2228 return true;
2229}
2230
2231pub const CreateIoCompletionPortError = error{Unexpected};
2232
2233pub fn CreateIoCompletionPort(
2234 file_handle: windows.HANDLE,
2235 existing_completion_port: ?windows.HANDLE,
2236 completion_key: usize,
2237 concurrent_thread_count: windows.DWORD,
2238) CreateIoCompletionPortError!windows.HANDLE {
2239 const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
2240 switch (windows.GetLastError()) {
2241 windows.ERROR.INVALID_PARAMETER => unreachable,
2242 else => |err| return windows.unexpectedError(err),
2243 }
2244 };
2245 return handle;
2246}
2247
2248pub const WindowsPostQueuedCompletionStatusError = error{Unexpected};
2249
2250pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: windows.DWORD, completion_key: usize, lpOverlapped: ?*windows.OVERLAPPED) WindowsPostQueuedCompletionStatusError!void {
2251 if (windows.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {
2252 const err = windows.GetLastError();
2253 switch (err) {
2254 else => return windows.unexpectedError(err),
2255 }
2256 }
2257}
2258
2259pub const GetQueuedCompletionStatusResult = enum {
2260 Normal,
2261 Aborted,
2262 Cancelled,
2263 EOF,
2264};
2265
2266pub fn GetQueuedCompletionStatus(
2267 completion_port: windows.HANDLE,
2268 bytes_transferred_count: *windows.DWORD,
2269 lpCompletionKey: *usize,
2270 lpOverlapped: *?*windows.OVERLAPPED,
2271 dwMilliseconds: windows.DWORD,
2272) GetQueuedCompletionStatusResult {
2273 if (windows.GetQueuedCompletionStatus(completion_port, bytes_transferred_count, lpCompletionKey, lpOverlapped, dwMilliseconds) == windows.FALSE) {
2274 switch (windows.GetLastError()) {
2275 windows.ERROR.ABANDONED_WAIT_0 => return GetQueuedCompletionStatusResult.Aborted,
2276 windows.ERROR.OPERATION_ABORTED => return GetQueuedCompletionStatusResult.Cancelled,
2277 windows.ERROR.HANDLE_EOF => return GetQueuedCompletionStatusResult.EOF,
2278 else => |err| {
2279 if (std.debug.runtime_safety) {
2280 std.debug.panic("unexpected error: {}\n", err);
2281 }
2282 },
2283 }
2284 }
2285 return GetQueuedCompletionStatusResult.Normal;
2286}
2287
2299/// Used to convert a slice to a null terminated slice on the stack.2288/// Used to convert a slice to a null terminated slice on the stack.
2300/// TODO https://github.com/ziglang/zig/issues/2872289/// TODO https://github.com/ziglang/zig/issues/287
2301pub fn toPosixPath(file_path: []const u8) ![PATH_MAX]u8 {2290pub fn toPosixPath(file_path: []const u8) ![PATH_MAX]u8 {
...@@ -2307,64 +2296,12 @@ pub fn toPosixPath(file_path: []const u8) ![PATH_MAX]u8 {...@@ -2307,64 +2296,12 @@ pub fn toPosixPath(file_path: []const u8) ![PATH_MAX]u8 {
2307 return path_with_null;2296 return path_with_null;
2308}2297}
23092298
2310const unexpected_error_tracing = builtin.mode == .Debug;
2311const UnexpectedError = error{
2312 /// The Operating System returned an undocumented error code.
2313 Unexpected,
2314};
2315
2316/// Call this when you made a syscall or something that sets errno2299/// Call this when you made a syscall or something that sets errno
2317/// and you get an unexpected error.2300/// and you get an unexpected error.
2318pub fn unexpectedErrno(errno: usize) UnexpectedError {2301pub fn unexpectedErrno(errno: usize) os.UnexpectedError {
2319 if (unexpected_error_tracing) {2302 if (os.unexpected_error_tracing) {
2320 std.debug.warn("unexpected errno: {}\n", errno);2303 std.debug.warn("unexpected errno: {}\n", errno);
2321 std.debug.dumpCurrentStackTrace(null);2304 std.debug.dumpCurrentStackTrace(null);
2322 }2305 }
2323 return error.Unexpected;2306 return error.Unexpected;
2324}2307}
2325
2326/// Call this when you made a windows DLL call or something that does SetLastError
2327/// and you get an unexpected error.
2328pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {
2329 if (unexpected_error_tracing) {
2330 std.debug.warn("unexpected GetLastError(): {}\n", err);
2331 std.debug.dumpCurrentStackTrace(null);
2332 }
2333 return error.Unexpected;
2334}
2335
2336pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 {
2337 return sliceToPrefixedFileW(mem.toSliceConst(u8, s));
2338}
2339
2340pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
2341 return sliceToPrefixedSuffixedFileW(s, []u16{0});
2342}
2343
2344pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 {
2345 // TODO well defined copy elision
2346 var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined;
2347
2348 // > File I/O functions in the Windows API convert "/" to "\" as part of
2349 // > converting the name to an NT-style name, except when using the "\\?\"
2350 // > prefix as detailed in the following sections.
2351 // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
2352 // Because we want the larger maximum path length for absolute paths, we
2353 // disallow forward slashes in zig std lib file functions on Windows.
2354 for (s) |byte| {
2355 switch (byte) {
2356 '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
2357 else => {},
2358 }
2359 }
2360 const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: {
2361 const prefix = []u16{ '\\', '\\', '?', '\\' };
2362 mem.copy(u16, result[0..], prefix);
2363 break :blk prefix.len;
2364 };
2365 const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s);
2366 assert(end_index <= result.len);
2367 if (end_index + suffix.len > result.len) return error.NameTooLong;
2368 mem.copy(u16, result[end_index..], suffix);
2369 return result;
2370}
std/os/windows.zig+248-16
...@@ -1,28 +1,30 @@...@@ -1,28 +1,30 @@
1// This file contains the types and constants of the Windows API,
2// as well as the Windows-equivalent of "Zig-flavored POSIX" API layer.
1const std = @import("../std.zig");3const std = @import("../std.zig");
2const assert = std.debug.assert;4const assert = std.debug.assert;
3const maxInt = std.math.maxInt;5const maxInt = std.math.maxInt;
46
5pub const is_the_target = switch (builtin.os) {7pub const is_the_target = builtin.os == .windows;
6 .windows => true,8pub const posix = if (builtin.link_libc) struct {} else @import("windows/posix.zig");
7 else => false,
8};
9pub const posix = @import("windows/posix.zig");
10pub use posix;9pub use posix;
1110
12pub use @import("windows/advapi32.zig");11pub const advapi32 = @import("windows/advapi32.zig");
13pub use @import("windows/kernel32.zig");12pub const kernel32 = @import("windows/kernel32.zig");
14pub use @import("windows/ntdll.zig");13pub const ntdll = @import("windows/ntdll.zig");
15pub use @import("windows/ole32.zig");14pub const ole32 = @import("windows/ole32.zig");
16pub use @import("windows/shell32.zig");15pub const shell32 = @import("windows/shell32.zig");
17
18test "import" {
19 if (is_the_target) {
20 _ = @import("windows/util.zig");
21 }
22}
2316
24pub const ERROR = @import("windows/error.zig");17pub const ERROR = @import("windows/error.zig");
2518
19/// The standard input device. Initially, this is the console input buffer, CONIN$.
20pub const STD_INPUT_HANDLE = maxInt(DWORD) - 10 + 1;
21
22/// The standard output device. Initially, this is the active console screen buffer, CONOUT$.
23pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1;
24
25/// The standard error device. Initially, this is the active console screen buffer, CONOUT$.
26pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1;
27
26pub const SHORT = c_short;28pub const SHORT = c_short;
27pub const BOOL = c_int;29pub const BOOL = c_int;
28pub const BOOLEAN = BYTE;30pub const BOOLEAN = BYTE;
...@@ -428,3 +430,233 @@ pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY;...@@ -428,3 +430,233 @@ pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY;
428pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY;430pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY;
429431
430pub const PIMAGE_TLS_CALLBACK = ?extern fn (PVOID, DWORD, PVOID) void;432pub const PIMAGE_TLS_CALLBACK = ?extern fn (PVOID, DWORD, PVOID) void;
433
434pub const PROV_RSA_FULL = 1;
435
436pub const REGSAM = ACCESS_MASK;
437pub const ACCESS_MASK = DWORD;
438pub const PHKEY = *HKEY;
439pub const HKEY = *HKEY__;
440pub const HKEY__ = extern struct {
441 unused: c_int,
442};
443pub const LSTATUS = LONG;
444
445pub const FILE_NOTIFY_INFORMATION = extern struct {
446 NextEntryOffset: DWORD,
447 Action: DWORD,
448 FileNameLength: DWORD,
449 FileName: [1]WCHAR,
450};
451
452pub const FILE_ACTION_ADDED = 0x00000001;
453pub const FILE_ACTION_REMOVED = 0x00000002;
454pub const FILE_ACTION_MODIFIED = 0x00000003;
455pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
456pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
457
458pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?extern fn (DWORD, DWORD, *OVERLAPPED) void;
459
460pub const FILE_LIST_DIRECTORY = 1;
461
462pub const FILE_NOTIFY_CHANGE_CREATION = 64;
463pub const FILE_NOTIFY_CHANGE_SIZE = 8;
464pub const FILE_NOTIFY_CHANGE_SECURITY = 256;
465pub const FILE_NOTIFY_CHANGE_LAST_ACCESS = 32;
466pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16;
467pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2;
468pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1;
469pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4;
470
471pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct {
472 dwSize: COORD,
473 dwCursorPosition: COORD,
474 wAttributes: WORD,
475 srWindow: SMALL_RECT,
476 dwMaximumWindowSize: COORD,
477};
478
479pub const FOREGROUND_BLUE = 1;
480pub const FOREGROUND_GREEN = 2;
481pub const FOREGROUND_RED = 4;
482pub const FOREGROUND_INTENSITY = 8;
483
484pub const LIST_ENTRY = extern struct {
485 Flink: *LIST_ENTRY,
486 Blink: *LIST_ENTRY,
487};
488
489pub const RTL_CRITICAL_SECTION_DEBUG = extern struct {
490 Type: WORD,
491 CreatorBackTraceIndex: WORD,
492 CriticalSection: *RTL_CRITICAL_SECTION,
493 ProcessLocksList: LIST_ENTRY,
494 EntryCount: DWORD,
495 ContentionCount: DWORD,
496 Flags: DWORD,
497 CreatorBackTraceIndexHigh: WORD,
498 SpareWORD: WORD,
499};
500
501pub const RTL_CRITICAL_SECTION = extern struct {
502 DebugInfo: *RTL_CRITICAL_SECTION_DEBUG,
503 LockCount: LONG,
504 RecursionCount: LONG,
505 OwningThread: HANDLE,
506 LockSemaphore: HANDLE,
507 SpinCount: ULONG_PTR,
508};
509
510pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION;
511pub const INIT_ONCE = RTL_RUN_ONCE;
512pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT;
513pub const INIT_ONCE_FN = extern fn (InitOnce: *INIT_ONCE, Parameter: ?*c_void, Context: ?*c_void) BOOL;
514
515pub const RTL_RUN_ONCE = extern struct {
516 Ptr: ?*c_void,
517};
518
519pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null };
520
521pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED;
522pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED;
523pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE;
524pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY;
525pub const COINIT = extern enum {
526 COINIT_APARTMENTTHREADED = 2,
527 COINIT_MULTITHREADED = 0,
528 COINIT_DISABLE_OLE1DDE = 4,
529 COINIT_SPEED_OVER_MEMORY = 8,
530};
531
532/// > The maximum path of 32,767 characters is approximate, because the "\\?\"
533/// > prefix may be expanded to a longer string by the system at run time, and
534/// > this expansion applies to the total length.
535/// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
536pub const PATH_MAX_WIDE = 32767;
537
538pub const OpenError = error{
539 SharingViolation,
540 PathAlreadyExists,
541
542 /// When any of the path components can not be found or the file component can not
543 /// be found. Some operating systems distinguish between path components not found and
544 /// file components not found, but they are collapsed into FileNotFound to gain
545 /// consistency across operating systems.
546 FileNotFound,
547
548 AccessDenied,
549 PipeBusy,
550 NameTooLong,
551
552 /// On Windows, file paths must be valid Unicode.
553 InvalidUtf8,
554
555 /// On Windows, file paths cannot contain these characters:
556 /// '/', '*', '?', '"', '<', '>', '|'
557 BadPathName,
558
559 Unexpected,
560};
561
562pub fn CreateFile(
563 file_path: []const u8,
564 desired_access: DWORD,
565 share_mode: DWORD,
566 creation_disposition: DWORD,
567 flags_and_attrs: DWORD,
568) OpenError!fd_t {
569 const file_path_w = try sliceToPrefixedFileW(file_path);
570 return CreateFileW(&file_path_w, desired_access, share_mode, creation_disposition, flags_and_attrs);
571}
572
573pub fn CreateFileW(
574 file_path_w: [*]const u16,
575 desired_access: DWORD,
576 share_mode: DWORD,
577 creation_disposition: DWORD,
578 flags_and_attrs: DWORD,
579) OpenError!HANDLE {
580 const result = kernel32.CreateFileW(file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null);
581
582 if (result == INVALID_HANDLE_VALUE) {
583 switch (kernel32.GetLastError()) {
584 ERROR.SHARING_VIOLATION => return error.SharingViolation,
585 ERROR.ALREADY_EXISTS => return error.PathAlreadyExists,
586 ERROR.FILE_EXISTS => return error.PathAlreadyExists,
587 ERROR.FILE_NOT_FOUND => return error.FileNotFound,
588 ERROR.PATH_NOT_FOUND => return error.FileNotFound,
589 ERROR.ACCESS_DENIED => return error.AccessDenied,
590 ERROR.PIPE_BUSY => return error.PipeBusy,
591 else => |err| return unexpectedErrorWindows(err),
592 }
593 }
594
595 return result;
596}
597
598pub const CreatePipeError = error{Unexpected};
599
600fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void {
601 if (kernel32.CreatePipe(rd, wr, sattr, 0) == 0) {
602 switch (kernel32.GetLastError()) {
603 else => |err| return unexpectedError(err),
604 }
605 }
606}
607
608pub const SetHandleInformationError = error{Unexpected};
609
610fn SetHandleInformation(h: HANDLE, mask: DWORD, flags: DWORD) SetHandleInformationError!void {
611 if (SetHandleInformation(h, mask, flags) == 0) {
612 switch (kernel32.GetLastError()) {
613 else => |err| return unexpectedError(err),
614 }
615 }
616}
617
618pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 {
619 return sliceToPrefixedFileW(mem.toSliceConst(u8, s));
620}
621
622pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
623 return sliceToPrefixedSuffixedFileW(s, []u16{0});
624}
625
626pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 {
627 // TODO well defined copy elision
628 var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined;
629
630 // > File I/O functions in the Windows API convert "/" to "\" as part of
631 // > converting the name to an NT-style name, except when using the "\\?\"
632 // > prefix as detailed in the following sections.
633 // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
634 // Because we want the larger maximum path length for absolute paths, we
635 // disallow forward slashes in zig std lib file functions on Windows.
636 for (s) |byte| {
637 switch (byte) {
638 '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
639 else => {},
640 }
641 }
642 const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: {
643 const prefix = []u16{ '\\', '\\', '?', '\\' };
644 mem.copy(u16, result[0..], prefix);
645 break :blk prefix.len;
646 };
647 const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s);
648 assert(end_index <= result.len);
649 if (end_index + suffix.len > result.len) return error.NameTooLong;
650 mem.copy(u16, result[end_index..], suffix);
651 return result;
652}
653
654/// Call this when you made a windows DLL call or something that does SetLastError
655/// and you get an unexpected error.
656pub fn unexpectedError(err: DWORD) std.os.UnexpectedError {
657 if (std.os.unexpected_error_tracing) {
658 std.debug.warn("unexpected GetLastError(): {}\n", err);
659 std.debug.dumpCurrentStackTrace(null);
660 }
661 return error.Unexpected;
662}
std/os/windows/advapi32.zig-11
...@@ -1,16 +1,5 @@...@@ -1,16 +1,5 @@
1use @import("../windows.zig");1use @import("../windows.zig");
22
3pub const PROV_RSA_FULL = 1;
4
5pub const REGSAM = ACCESS_MASK;
6pub const ACCESS_MASK = DWORD;
7pub const PHKEY = *HKEY;
8pub const HKEY = *HKEY__;
9pub const HKEY__ = extern struct {
10 unused: c_int,
11};
12pub const LSTATUS = LONG;
13
14pub extern "advapi32" stdcallcc fn RegOpenKeyExW(3pub extern "advapi32" stdcallcc fn RegOpenKeyExW(
15 hKey: HKEY,4 hKey: HKEY,
16 lpSubKey: LPCWSTR,5 lpSubKey: LPCWSTR,
std/os/windows/kernel32.zig-77
...@@ -189,86 +189,9 @@ pub extern "kernel32" stdcallcc fn GetProcAddress(hModule: HMODULE, lpProcName:...@@ -189,86 +189,9 @@ pub extern "kernel32" stdcallcc fn GetProcAddress(hModule: HMODULE, lpProcName:
189189
190pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;190pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
191191
192pub const FILE_NOTIFY_INFORMATION = extern struct {
193 NextEntryOffset: DWORD,
194 Action: DWORD,
195 FileNameLength: DWORD,
196 FileName: [1]WCHAR,
197};
198
199pub const FILE_ACTION_ADDED = 0x00000001;
200pub const FILE_ACTION_REMOVED = 0x00000002;
201pub const FILE_ACTION_MODIFIED = 0x00000003;
202pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
203pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
204
205pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?extern fn (DWORD, DWORD, *OVERLAPPED) void;
206
207pub const FILE_LIST_DIRECTORY = 1;
208
209pub const FILE_NOTIFY_CHANGE_CREATION = 64;
210pub const FILE_NOTIFY_CHANGE_SIZE = 8;
211pub const FILE_NOTIFY_CHANGE_SECURITY = 256;
212pub const FILE_NOTIFY_CHANGE_LAST_ACCESS = 32;
213pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16;
214pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2;
215pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1;
216pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4;
217
218pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct {
219 dwSize: COORD,
220 dwCursorPosition: COORD,
221 wAttributes: WORD,
222 srWindow: SMALL_RECT,
223 dwMaximumWindowSize: COORD,
224};
225
226pub const FOREGROUND_BLUE = 1;
227pub const FOREGROUND_GREEN = 2;
228pub const FOREGROUND_RED = 4;
229pub const FOREGROUND_INTENSITY = 8;
230
231pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;192pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;
232pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;193pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;
233pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;194pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;
234pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;195pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void;
235196
236pub const LIST_ENTRY = extern struct {
237 Flink: *LIST_ENTRY,
238 Blink: *LIST_ENTRY,
239};
240
241pub const RTL_CRITICAL_SECTION_DEBUG = extern struct {
242 Type: WORD,
243 CreatorBackTraceIndex: WORD,
244 CriticalSection: *RTL_CRITICAL_SECTION,
245 ProcessLocksList: LIST_ENTRY,
246 EntryCount: DWORD,
247 ContentionCount: DWORD,
248 Flags: DWORD,
249 CreatorBackTraceIndexHigh: WORD,
250 SpareWORD: WORD,
251};
252
253pub const RTL_CRITICAL_SECTION = extern struct {
254 DebugInfo: *RTL_CRITICAL_SECTION_DEBUG,
255 LockCount: LONG,
256 RecursionCount: LONG,
257 OwningThread: HANDLE,
258 LockSemaphore: HANDLE,
259 SpinCount: ULONG_PTR,
260};
261
262pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION;
263pub const INIT_ONCE = RTL_RUN_ONCE;
264pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT;
265
266pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) BOOL;197pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) BOOL;
267
268pub const INIT_ONCE_FN = extern fn (InitOnce: *INIT_ONCE, Parameter: ?*c_void, Context: ?*c_void) BOOL;
269
270pub const RTL_RUN_ONCE = extern struct {
271 Ptr: ?*c_void,
272};
273
274pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null };
std/os/windows/ole32.zig-11
...@@ -4,14 +4,3 @@ pub extern "ole32" stdcallcc fn CoTaskMemFree(pv: LPVOID) void;...@@ -4,14 +4,3 @@ pub extern "ole32" stdcallcc fn CoTaskMemFree(pv: LPVOID) void;
4pub extern "ole32" stdcallcc fn CoUninitialize() void;4pub extern "ole32" stdcallcc fn CoUninitialize() void;
5pub extern "ole32" stdcallcc fn CoGetCurrentProcess() DWORD;5pub extern "ole32" stdcallcc fn CoGetCurrentProcess() DWORD;
6pub extern "ole32" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT;6pub extern "ole32" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT;
7
8pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED;
9pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED;
10pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE;
11pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY;
12pub const COINIT = extern enum {
13 COINIT_APARTMENTTHREADED = 2,
14 COINIT_MULTITHREADED = 0,
15 COINIT_DISABLE_OLE1DDE = 4,
16 COINIT_SPEED_OVER_MEMORY = 8,
17};
std/os/windows/posix.zig+4-13
...@@ -1,15 +1,6 @@...@@ -1,15 +1,6 @@
1// Declarations that are intended to be imported into the POSIX namespace.1// Declarations that are intended to be imported into the POSIX namespace,
2// when not linking libc.
2const std = @import("../../std.zig");3const std = @import("../../std.zig");
3const maxInt = std.math.maxInt;4const builtin = @import("builtin");
4use std.os.windows;
55
6pub const fd_t = HANDLE;6pub const fd_t = std.os.windows.HANDLE;
7
8/// The standard input device. Initially, this is the console input buffer, CONIN$.
9pub const STD_INPUT_HANDLE = maxInt(DWORD) - 10 + 1;
10
11/// The standard output device. Initially, this is the active console screen buffer, CONOUT$.
12pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1;
13
14/// The standard error device. Initially, this is the active console screen buffer, CONOUT$.
15pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1;
std/os/windows/util.zig deleted-147
...@@ -1,147 +0,0 @@
1const std = @import("../../std.zig");
2const builtin = @import("builtin");
3const os = std.os;
4const unicode = std.unicode;
5const windows = std.os.windows;
6const assert = std.debug.assert;
7const mem = std.mem;
8const BufMap = std.BufMap;
9const cstr = std.cstr;
10
11pub const WaitError = error{
12 WaitAbandoned,
13 WaitTimeOut,
14 Unexpected,
15};
16
17pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) WaitError!void {
18 const result = windows.WaitForSingleObject(handle, milliseconds);
19 return switch (result) {
20 windows.WAIT_ABANDONED => error.WaitAbandoned,
21 windows.WAIT_OBJECT_0 => {},
22 windows.WAIT_TIMEOUT => error.WaitTimeOut,
23 windows.WAIT_FAILED => x: {
24 const err = windows.GetLastError();
25 break :x switch (err) {
26 else => os.unexpectedErrorWindows(err),
27 };
28 },
29 else => error.Unexpected,
30 };
31}
32
33/// Caller must free result.
34pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 {
35 // count bytes needed
36 const max_chars_needed = x: {
37 var max_chars_needed: usize = 4; // 4 for the final 4 null bytes
38 var it = env_map.iterator();
39 while (it.next()) |pair| {
40 // +1 for '='
41 // +1 for null byte
42 max_chars_needed += pair.key.len + pair.value.len + 2;
43 }
44 break :x max_chars_needed;
45 };
46 const result = try allocator.alloc(u16, max_chars_needed);
47 errdefer allocator.free(result);
48
49 var it = env_map.iterator();
50 var i: usize = 0;
51 while (it.next()) |pair| {
52 i += try unicode.utf8ToUtf16Le(result[i..], pair.key);
53 result[i] = '=';
54 i += 1;
55 i += try unicode.utf8ToUtf16Le(result[i..], pair.value);
56 result[i] = 0;
57 i += 1;
58 }
59 result[i] = 0;
60 i += 1;
61 result[i] = 0;
62 i += 1;
63 result[i] = 0;
64 i += 1;
65 result[i] = 0;
66 i += 1;
67 return allocator.shrink(result, i);
68}
69
70pub fn windowsFindFirstFile(
71 dir_path: []const u8,
72 find_file_data: *windows.WIN32_FIND_DATAW,
73) !windows.HANDLE {
74 const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{ '\\', '*', 0 });
75 const handle = windows.FindFirstFileW(&dir_path_w, find_file_data);
76
77 if (handle == windows.INVALID_HANDLE_VALUE) {
78 const err = windows.GetLastError();
79 switch (err) {
80 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
81 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
82 else => return os.unexpectedErrorWindows(err),
83 }
84 }
85
86 return handle;
87}
88
89/// Returns `true` if there was another file, `false` otherwise.
90pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAW) !bool {
91 if (windows.FindNextFileW(handle, find_file_data) == 0) {
92 const err = windows.GetLastError();
93 return switch (err) {
94 windows.ERROR.NO_MORE_FILES => false,
95 else => os.unexpectedErrorWindows(err),
96 };
97 }
98 return true;
99}
100
101pub const WindowsCreateIoCompletionPortError = error{Unexpected};
102
103pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_completion_port: ?windows.HANDLE, completion_key: usize, concurrent_thread_count: windows.DWORD) !windows.HANDLE {
104 const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
105 const err = windows.GetLastError();
106 switch (err) {
107 windows.ERROR.INVALID_PARAMETER => unreachable,
108 else => return os.unexpectedErrorWindows(err),
109 }
110 };
111 return handle;
112}
113
114pub const WindowsPostQueuedCompletionStatusError = error{Unexpected};
115
116pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: windows.DWORD, completion_key: usize, lpOverlapped: ?*windows.OVERLAPPED) WindowsPostQueuedCompletionStatusError!void {
117 if (windows.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {
118 const err = windows.GetLastError();
119 switch (err) {
120 else => return os.unexpectedErrorWindows(err),
121 }
122 }
123}
124
125pub const WindowsWaitResult = enum {
126 Normal,
127 Aborted,
128 Cancelled,
129 EOF,
130};
131
132pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: *windows.DWORD, lpCompletionKey: *usize, lpOverlapped: *?*windows.OVERLAPPED, dwMilliseconds: windows.DWORD) WindowsWaitResult {
133 if (windows.GetQueuedCompletionStatus(completion_port, bytes_transferred_count, lpCompletionKey, lpOverlapped, dwMilliseconds) == windows.FALSE) {
134 const err = windows.GetLastError();
135 switch (err) {
136 windows.ERROR.ABANDONED_WAIT_0 => return WindowsWaitResult.Aborted,
137 windows.ERROR.OPERATION_ABORTED => return WindowsWaitResult.Cancelled,
138 windows.ERROR.HANDLE_EOF => return WindowsWaitResult.EOF,
139 else => {
140 if (std.debug.runtime_safety) {
141 std.debug.panic("unexpected error: {}\n", err);
142 }
143 },
144 }
145 }
146 return WindowsWaitResult.Normal;
147}