From 2def23063fbabb4128da17aca27745a7e8062ce5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 24 May 2019 00:13:13 -0400 Subject: [PATCH] more progress. moving windows API layer to its own file --- CMakeLists.txt | 1 + std/dynamic_library.zig | 5 +- std/event/fs.zig | 32 +- std/os.zig | 22 +- std/os/child_process.zig | 70 +-- std/os/linux.zig | 767 +------------------------------- std/os/linux/sys.zig | 847 ++++++++++++++++++++++++++++++++++++ std/os/posix.zig | 377 +++++++--------- std/os/windows.zig | 264 ++++++++++- std/os/windows/advapi32.zig | 11 - std/os/windows/kernel32.zig | 77 ---- std/os/windows/ole32.zig | 11 - std/os/windows/posix.zig | 17 +- std/os/windows/util.zig | 147 ------- 14 files changed, 1331 insertions(+), 1317 deletions(-) create mode 100644 std/os/linux/sys.zig delete mode 100644 std/os/windows/util.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 699b674c21..1c41b20569 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -619,6 +619,7 @@ set(ZIG_STD_FILES "os/linux/posix.zig" "os/linux/posix/arm64.zig" "os/linux/posix/x86_64.zig" + "os/linux/sys.zig" "os/linux/tls.zig" "os/linux/vdso.zig" "os/linux/x86_64.zig" diff --git a/std/dynamic_library.zig b/std/dynamic_library.zig index 57122fd69c..2a04ac6c9d 100644 --- a/std/dynamic_library.zig +++ b/std/dynamic_library.zig @@ -261,12 +261,11 @@ pub const WindowsDynLib = struct { return WindowsDynLib{ .allocator = allocator, .dll = windows.LoadLibraryW(&wpath) orelse { - const err = windows.GetLastError(); - switch (err) { + switch (windows.GetLastError()) { windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, windows.ERROR.MOD_NOT_FOUND => return error.FileNotFound, - else => return os.unexpectedErrorWindows(err), + else => |err| return windows.unexpectedError(err), } }, }; diff --git a/std/event/fs.zig b/std/event/fs.zig index 07d4dd8af1..34325d1b62 100644 --- a/std/event/fs.zig +++ b/std/event/fs.zig @@ -154,16 +154,15 @@ pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) } var bytes_transferred: windows.DWORD = undefined; if (windows.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { - const err = windows.GetLastError(); - return switch (err) { + switch (windows.GetLastError()) { windows.ERROR.IO_PENDING => unreachable, - windows.ERROR.INVALID_USER_BUFFER => error.SystemResources, - windows.ERROR.NOT_ENOUGH_MEMORY => error.SystemResources, - windows.ERROR.OPERATION_ABORTED => error.OperationAborted, - windows.ERROR.NOT_ENOUGH_QUOTA => error.SystemResources, - windows.ERROR.BROKEN_PIPE => error.BrokenPipe, - else => os.unexpectedErrorWindows(err), - }; + windows.ERROR.INVALID_USER_BUFFER => return error.SystemResources, + windows.ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources, + windows.ERROR.OPERATION_ABORTED => return error.OperationAborted, + windows.ERROR.NOT_ENOUGH_QUOTA => return error.SystemResources, + windows.ERROR.BROKEN_PIPE => return error.BrokenPipe, + else => |err| return windows.unexpectedError(err), + } } } @@ -304,13 +303,12 @@ pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize } var bytes_transferred: windows.DWORD = undefined; if (windows.GetOverlappedResult(fd, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { - const err = windows.GetLastError(); - switch (err) { + switch (windows.GetLastError()) { windows.ERROR.IO_PENDING => unreachable, windows.ERROR.OPERATION_ABORTED => return error.OperationAborted, windows.ERROR.BROKEN_PIPE => return error.BrokenPipe, windows.ERROR.HANDLE_EOF => return usize(bytes_transferred), - else => return os.unexpectedErrorWindows(err), + else => |err| return windows.unexpectedError(err), } } return usize(bytes_transferred); @@ -1042,11 +1040,10 @@ pub fn Watch(comptime V: type) type { null, ); if (dir_handle == windows.INVALID_HANDLE_VALUE) { - const err = windows.GetLastError(); - switch (err) { + switch (windows.GetLastError()) { windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, - else => return os.unexpectedErrorWindows(err), + else => |err| return windows.unexpectedError(err), } } var dir_handle_consumed = false; @@ -1165,9 +1162,8 @@ pub fn Watch(comptime V: type) type { } var bytes_transferred: windows.DWORD = undefined; if (windows.GetOverlappedResult(dir_handle, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) { - const errno = windows.GetLastError(); - const err = switch (errno) { - else => os.unexpectedErrorWindows(errno), + const err = switch (windows.GetLastError()) { + else => |err| windows.unexpectedError(err), }; await (async self.channel.put(err) catch unreachable); } else { diff --git a/std/os.zig b/std/os.zig index 5928b1ce78..b6be5bf5ee 100644 --- a/std/os.zig +++ b/std/os.zig @@ -31,13 +31,14 @@ pub const zen = @import("os/zen.zig"); pub const uefi = @import("os/uefi.zig"); pub const wasi = @import("os/wasi.zig"); -pub const system = switch (builtin.os) { +pub const system = if (builtin.link_libc) std.c else switch (builtin.os) { .linux => linux, .macosx, .ios, .watchos, .tvos => darwin, .freebsd => freebsd, .netbsd => netbsd, .zen => zen, .wasi => wasi, + .windows => windows, else => struct {}, }; @@ -53,6 +54,12 @@ pub const page_size = switch (builtin.arch) { else => 4 * 1024, }; +pub const unexpected_error_tracing = builtin.mode == .Debug; +pub const UnexpectedError = error{ + /// The Operating System returned an undocumented error code. + Unexpected, +}; + /// This represents the maximum size of a UTF-8 encoded file path. /// All file system operations which return a path are guaranteed to /// fit into a UTF-8 encoded array of this length. @@ -259,7 +266,7 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned return switch (err) { windows.ERROR.ENVVAR_NOT_FOUND => error.EnvironmentVariableNotFound, else => { - unexpectedErrorWindows(err) catch {}; + windows.unexpectedError(err) catch {}; return error.EnvironmentVariableNotFound; }, }; @@ -825,7 +832,7 @@ pub const Dir = struct { if (self.handle.first) { self.handle.first = false; } else { - if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data)) + if (!try posix.FindNextFile(self.handle.handle, &self.handle.find_file_data)) return null; } 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 { if (rc == 0) { const err = windows.GetLastError(); switch (err) { - else => return unexpectedErrorWindows(err), + else => return windows.unexpectedError(err), } } return out_buffer[0..rc]; @@ -1597,10 +1604,9 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner); outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse { - const err = windows.GetLastError(); - return switch (err) { - else => os.unexpectedErrorWindows(err), - }; + switch (windows.GetLastError()) { + else => |err| windows.unexpectedError(err), + } }; return &outer_context.thread; } diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 705d5e5306..fa5db9b594 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -558,7 +558,7 @@ pub const ChildProcess = struct { defer if (cwd_w) |cwd| self.allocator.free(cwd); const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null; - const maybe_envp_buf = if (self.env_map) |env_map| try os.createWindowsEnvBlock(self.allocator, env_map) else null; + const maybe_envp_buf = if (self.env_map) |env_map| try createWindowsEnvBlock(self.allocator, env_map) else null; defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf); const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null; @@ -679,13 +679,12 @@ fn windowsCreateProcess(app_name: [*]u16, cmd_line: [*]u16, envp_ptr: ?[*]u16, c lpStartupInfo, lpProcessInformation, ) == 0) { - const err = windows.GetLastError(); - switch (err) { + switch (windows.GetLastError()) { windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, windows.ERROR.INVALID_PARAMETER => unreachable, windows.ERROR.INVALID_NAME => return error.InvalidName, - else => return os.unexpectedErrorWindows(err), + else => |err| return windows.unexpectedError(err), } } } @@ -733,32 +732,10 @@ fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void { if (wr) |h| os.close(h); } -// TODO: workaround for bug where the `const` from `&const` is dropped when the type is -// a namespace field lookup -const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES; - -fn windowsMakePipe(rd: *windows.HANDLE, wr: *windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void { - if (windows.CreatePipe(rd, wr, sattr, 0) == 0) { - const err = windows.GetLastError(); - return switch (err) { - else => os.unexpectedErrorWindows(err), - }; - } -} - -fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.DWORD) !void { - if (windows.SetHandleInformation(h, mask, flags) == 0) { - const err = windows.GetLastError(); - return switch (err) { - else => os.unexpectedErrorWindows(err), - }; - } -} - fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void { var rd_h: windows.HANDLE = undefined; var wr_h: windows.HANDLE = undefined; - try windowsMakePipe(&rd_h, &wr_h, sattr); + try windows.CreatePipe(&rd_h, &wr_h, sattr); errdefer windowsDestroyPipe(rd_h, wr_h); try windowsSetHandleInfo(wr_h, windows.HANDLE_FLAG_INHERIT, 0); rd.* = rd_h; @@ -768,7 +745,7 @@ fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const S fn windowsMakePipeOut(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void { var rd_h: windows.HANDLE = undefined; var wr_h: windows.HANDLE = undefined; - try windowsMakePipe(&rd_h, &wr_h, sattr); + try windows.CreatePipe(&rd_h, &wr_h, sattr); errdefer windowsDestroyPipe(rd_h, wr_h); try windowsSetHandleInfo(rd_h, windows.HANDLE_FLAG_INHERIT, 0); rd.* = rd_h; @@ -810,3 +787,40 @@ fn readIntFd(fd: i32) !ErrInt { const stream = &os.File.openHandle(fd).inStream().stream; return stream.readIntNative(ErrInt) catch return error.SystemResources; } + +/// Caller must free result. +pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 { + // count bytes needed + const max_chars_needed = x: { + var max_chars_needed: usize = 4; // 4 for the final 4 null bytes + var it = env_map.iterator(); + while (it.next()) |pair| { + // +1 for '=' + // +1 for null byte + max_chars_needed += pair.key.len + pair.value.len + 2; + } + break :x max_chars_needed; + }; + const result = try allocator.alloc(u16, max_chars_needed); + errdefer allocator.free(result); + + var it = env_map.iterator(); + var i: usize = 0; + while (it.next()) |pair| { + i += try unicode.utf8ToUtf16Le(result[i..], pair.key); + result[i] = '='; + i += 1; + i += try unicode.utf8ToUtf16Le(result[i..], pair.value); + result[i] = 0; + i += 1; + } + result[i] = 0; + i += 1; + result[i] = 0; + i += 1; + result[i] = 0; + i += 1; + result[i] = 0; + i += 1; + return allocator.shrink(result, i); +} diff --git a/std/os/linux.zig b/std/os/linux.zig index 5f468af73c..4a3cd59132 100644 --- a/std/os/linux.zig +++ b/std/os/linux.zig @@ -1,771 +1,8 @@ const std = @import("../std.zig"); -const assert = std.debug.assert; const builtin = @import("builtin"); -const maxInt = std.math.maxInt; -const elf = std.elf; -pub const tls = @import("linux/tls.zig"); -const vdso = @import("linux/vdso.zig"); -const dl = @import("../dynamic_library.zig"); -pub use switch (builtin.arch) { - .x86_64 => @import("linux/x86_64.zig"), - .aarch64 => @import("linux/arm64.zig"), - else => struct {}, -}; pub const is_the_target = builtin.os == .linux; -pub const posix = @import("linux/posix.zig"); -pub use posix; - -/// See `std.os.posix.getauxval`. -pub var elf_aux_maybe: ?[*]std.elf.Auxv = null; - -/// Get the errno from a syscall return value, or 0 for no error. -pub fn getErrno(r: usize) u12 { - const signed_r = @bitCast(isize, r); - return if (signed_r > -4096 and signed_r < 0) @intCast(u12, -signed_r) else 0; -} - -pub fn dup2(old: i32, new: i32) usize { - return syscall2(SYS_dup2, @bitCast(usize, isize(old)), @bitCast(usize, isize(new))); -} - -pub fn dup3(old: i32, new: i32, flags: u32) usize { - return syscall3(SYS_dup3, @bitCast(usize, isize(old)), @bitCast(usize, isize(new)), flags); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn chdir(path: [*]const u8) usize { - return syscall1(SYS_chdir, @ptrToInt(path)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn chroot(path: [*]const u8) usize { - return syscall1(SYS_chroot, @ptrToInt(path)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { - return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp)); -} - -pub fn fork() usize { - return syscall0(SYS_fork); -} - -/// This must be inline, and inline call the syscall function, because if the -/// child does a return it will clobber the parent's stack. -/// It is advised to avoid this function and use clone instead, because -/// the compiler is not aware of how vfork affects control flow and you may -/// see different results in optimized builds. -pub inline fn vfork() usize { - return @inlineCall(syscall0, SYS_vfork); -} - -pub fn futex_wait(uaddr: *const i32, futex_op: u32, val: i32, timeout: ?*timespec) usize { - return syscall4(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val), @ptrToInt(timeout)); -} - -pub fn futex_wake(uaddr: *const i32, futex_op: u32, val: i32) usize { - return syscall3(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val)); -} - -pub fn getcwd(buf: [*]u8, size: usize) usize { - return syscall2(SYS_getcwd, @ptrToInt(buf), size); -} - -pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { - return syscall3(SYS_getdents, @bitCast(usize, isize(fd)), @ptrToInt(dirp), count); -} - -pub fn getdents64(fd: i32, dirp: [*]u8, count: usize) usize { - return syscall3(SYS_getdents64, @bitCast(usize, isize(fd)), @ptrToInt(dirp), count); -} - -pub fn inotify_init1(flags: u32) usize { - return syscall1(SYS_inotify_init1, flags); -} - -pub fn inotify_add_watch(fd: i32, pathname: [*]const u8, mask: u32) usize { - return syscall3(SYS_inotify_add_watch, @bitCast(usize, isize(fd)), @ptrToInt(pathname), mask); -} - -pub fn inotify_rm_watch(fd: i32, wd: i32) usize { - return syscall2(SYS_inotify_rm_watch, @bitCast(usize, isize(fd)), @bitCast(usize, isize(wd))); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { - return syscall3(SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn readlinkat(dirfd: i32, noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { - return syscall4(SYS_readlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn mkdir(path: [*]const u8, mode: u32) usize { - return syscall2(SYS_mkdir, @ptrToInt(path), mode); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn mkdirat(dirfd: i32, path: [*]const u8, mode: u32) usize { - return syscall3(SYS_mkdirat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn mount(special: [*]const u8, dir: [*]const u8, fstype: [*]const u8, flags: u32, data: usize) usize { - return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn umount(special: [*]const u8) usize { - return syscall2(SYS_umount2, @ptrToInt(special), 0); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn umount2(special: [*]const u8, flags: u32) usize { - return syscall2(SYS_umount2, @ptrToInt(special), flags); -} - -pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, @bitCast(usize, isize(fd)), @bitCast(usize, offset)); -} - -pub fn mprotect(address: usize, length: usize, protection: usize) usize { - return syscall3(SYS_mprotect, address, length, protection); -} - -pub fn munmap(address: usize, length: usize) usize { - return syscall2(SYS_munmap, address, length); -} - -pub fn read(fd: i32, buf: [*]u8, count: usize) usize { - return syscall3(SYS_read, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); -} - -pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize { - return syscall4(SYS_preadv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset); -} - -pub fn readv(fd: i32, iov: [*]const iovec, count: usize) usize { - return syscall3(SYS_readv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count); -} - -pub fn writev(fd: i32, iov: [*]const iovec_const, count: usize) usize { - return syscall3(SYS_writev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count); -} - -pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) usize { - return syscall4(SYS_pwritev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn rmdir(path: [*]const u8) usize { - return syscall1(SYS_rmdir, @ptrToInt(path)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { - return syscall2(SYS_symlink, @ptrToInt(existing), @ptrToInt(new)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn symlinkat(existing: [*]const u8, newfd: i32, newpath: [*]const u8) usize { - return syscall3(SYS_symlinkat, @ptrToInt(existing), @bitCast(usize, isize(newfd)), @ptrToInt(newpath)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: usize) usize { - return syscall4(SYS_pread, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn access(path: [*]const u8, mode: u32) usize { - return syscall2(SYS_access, @ptrToInt(path), mode); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn faccessat(dirfd: i32, path: [*]const u8, mode: u32) usize { - return syscall3(SYS_faccessat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode); -} - -pub fn pipe(fd: *[2]i32) usize { - return pipe2(fd, 0); -} - -pub fn pipe2(fd: *[2]i32, flags: u32) usize { - return syscall2(SYS_pipe2, @ptrToInt(fd), flags); -} - -pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { - return syscall3(SYS_write, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); -} - -pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { - return syscall4(SYS_pwrite, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn rename(old: [*]const u8, new: [*]const u8) usize { - return syscall2(SYS_rename, @ptrToInt(old), @ptrToInt(new)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn renameat2(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const u8, flags: u32) usize { - return syscall5(SYS_renameat2, @bitCast(usize, isize(oldfd)), @ptrToInt(oldpath), @bitCast(usize, isize(newfd)), @ptrToInt(newpath), flags); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn open(path: [*]const u8, flags: u32, perm: usize) usize { - return syscall3(SYS_open, @ptrToInt(path), flags, perm); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn create(path: [*]const u8, perm: usize) usize { - return syscall2(SYS_creat, @ptrToInt(path), perm); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn openat(dirfd: i32, path: [*]const u8, flags: u32, mode: usize) usize { - // dirfd could be negative, for example AT_FDCWD is -100 - return syscall4(SYS_openat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode); -} - -/// See also `clone` (from the arch-specific include) -pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: *i32, child_tid: *i32, newtls: usize) usize { - return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls); -} - -/// See also `clone` (from the arch-specific include) -pub fn clone2(flags: u32, child_stack_ptr: usize) usize { - return syscall2(SYS_clone, flags, child_stack_ptr); -} - -pub fn close(fd: i32) usize { - return syscall1(SYS_close, @bitCast(usize, isize(fd))); -} - -pub fn lseek(fd: i32, offset: isize, ref_pos: usize) usize { - return syscall3(SYS_lseek, @bitCast(usize, isize(fd)), @bitCast(usize, offset), ref_pos); -} - -pub fn exit(status: i32) noreturn { - _ = syscall1(SYS_exit, @bitCast(usize, isize(status))); - unreachable; -} - -pub fn exit_group(status: i32) noreturn { - _ = syscall1(SYS_exit_group, @bitCast(usize, isize(status))); - unreachable; -} - -pub fn getrandom(buf: [*]u8, count: usize, flags: u32) usize { - return syscall3(SYS_getrandom, @ptrToInt(buf), count, flags); -} - -pub fn kill(pid: i32, sig: i32) usize { - return syscall2(SYS_kill, @bitCast(usize, isize(pid)), @bitCast(usize, isize(sig))); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn unlink(path: [*]const u8) usize { - return syscall1(SYS_unlink, @ptrToInt(path)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn unlinkat(dirfd: i32, path: [*]const u8, flags: u32) usize { - return syscall3(SYS_unlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags); -} - -pub fn waitpid(pid: i32, status: *i32, options: i32) usize { - return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0); -} - -var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime); - -// We must follow the C calling convention when we call into the VDSO -const vdso_clock_gettime_ty = extern fn (i32, *timespec) usize; - -pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { - if (VDSO_CGT_SYM.len != 0) { - const ptr = @atomicLoad(?*const c_void, &vdso_clock_gettime, .Unordered); - if (ptr) |fn_ptr| { - const f = @ptrCast(vdso_clock_gettime_ty, fn_ptr); - const rc = f(clk_id, tp); - switch (rc) { - 0, @bitCast(usize, isize(-EINVAL)) => return rc, - else => {}, - } - } - } - return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); -} - -extern fn init_vdso_clock_gettime(clk: i32, ts: *timespec) usize { - const ptr = @intToPtr(?*const c_void, vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM)); - // Note that we may not have a VDSO at all, update the stub address anyway - // so that clock_gettime will fall back on the good old (and slow) syscall - _ = @cmpxchgStrong(?*const c_void, &vdso_clock_gettime, &init_vdso_clock_gettime, ptr, .Monotonic, .Monotonic); - // Call into the VDSO if available - if (ptr) |fn_ptr| { - const f = @ptrCast(vdso_clock_gettime_ty, fn_ptr); - return f(clk, ts); - } - return @bitCast(usize, isize(-ENOSYS)); -} - -pub fn clock_getres(clk_id: i32, tp: *timespec) usize { - return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); -} - -pub fn clock_settime(clk_id: i32, tp: *const timespec) usize { - return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); -} - -pub fn gettimeofday(tv: *timeval, tz: *timezone) usize { - return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz)); -} - -pub fn settimeofday(tv: *const timeval, tz: *const timezone) usize { - return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz)); -} - -pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { - return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem)); -} - -pub fn setuid(uid: u32) usize { - return syscall1(SYS_setuid, uid); -} - -pub fn setgid(gid: u32) usize { - return syscall1(SYS_setgid, gid); -} - -pub fn setreuid(ruid: u32, euid: u32) usize { - return syscall2(SYS_setreuid, ruid, euid); -} - -pub fn setregid(rgid: u32, egid: u32) usize { - return syscall2(SYS_setregid, rgid, egid); -} - -pub fn getuid() u32 { - return u32(syscall0(SYS_getuid)); -} - -pub fn getgid() u32 { - return u32(syscall0(SYS_getgid)); -} - -pub fn geteuid() u32 { - return u32(syscall0(SYS_geteuid)); -} - -pub fn getegid() u32 { - return u32(syscall0(SYS_getegid)); -} - -pub fn seteuid(euid: u32) usize { - return syscall1(SYS_seteuid, euid); -} - -pub fn setegid(egid: u32) usize { - return syscall1(SYS_setegid, egid); -} - -pub fn getresuid(ruid: *u32, euid: *u32, suid: *u32) usize { - return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid)); -} - -pub fn getresgid(rgid: *u32, egid: *u32, sgid: *u32) usize { - return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid)); -} - -pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize { - return syscall3(SYS_setresuid, ruid, euid, suid); -} - -pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize { - return syscall3(SYS_setresgid, rgid, egid, sgid); -} - -pub fn getgroups(size: usize, list: *u32) usize { - return syscall2(SYS_getgroups, size, @ptrToInt(list)); -} - -pub fn setgroups(size: usize, list: *const u32) usize { - return syscall2(SYS_setgroups, size, @ptrToInt(list)); -} - -pub fn getpid() i32 { - return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid))); -} - -pub fn gettid() i32 { - return @bitCast(i32, @truncate(u32, syscall0(SYS_gettid))); -} - -pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { - return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8); -} - -pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize { - assert(sig >= 1); - assert(sig != SIGKILL); - assert(sig != SIGSTOP); - var ksa = k_sigaction{ - .handler = act.handler, - .flags = act.flags | SA_RESTORER, - .mask = undefined, - .restorer = @ptrCast(extern fn () void, restore_rt), - }; - var ksa_old: k_sigaction = undefined; - @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &act.mask), 8); - const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask))); - const err = getErrno(result); - if (err != 0) { - return result; - } - if (oact) |old| { - old.handler = ksa_old.handler; - old.flags = @truncate(u32, ksa_old.flags); - @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); - } - return 0; -} - -fn blockAllSignals(set: *sigset_t) void { - _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8); -} - -fn blockAppSignals(set: *sigset_t) void { - _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG / 8); -} - -fn restoreSignals(set: *sigset_t) void { - _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG / 8); -} - -pub fn sigaddset(set: *sigset_t, sig: u6) void { - const s = sig - 1; - (set.*)[@intCast(usize, s) / usize.bit_count] |= @intCast(usize, 1) << (s & (usize.bit_count - 1)); -} - -pub fn sigismember(set: *const sigset_t, sig: u6) bool { - const s = sig - 1; - return ((set.*)[@intCast(usize, s) / usize.bit_count] & (@intCast(usize, 1) << (s & (usize.bit_count - 1)))) != 0; -} - -pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { - return syscall3(SYS_getsockname, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len)); -} - -pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { - return syscall3(SYS_getpeername, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len)); -} - -pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { - return syscall3(SYS_socket, domain, socket_type, protocol); -} - -pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize { - return syscall5(SYS_setsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen)); -} - -pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize { - return syscall5(SYS_getsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); -} - -pub fn sendmsg(fd: i32, msg: *msghdr_const, flags: u32) usize { - return syscall3(SYS_sendmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags); -} - -pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize { - if (@typeInfo(usize).Int.bits > @typeInfo(@typeOf(mmsghdr(undefined).msg_len)).Int.bits) { - // workaround kernel brokenness: - // if adding up all iov_len overflows a i32 then split into multiple calls - // see https://www.openwall.com/lists/musl/2014/06/07/5 - const kvlen = if (vlen > IOV_MAX) IOV_MAX else vlen; // matches kernel - var next_unsent: usize = 0; - for (msgvec[0..kvlen]) |*msg, i| { - var size: i32 = 0; - const msg_iovlen = @intCast(usize, msg.msg_hdr.msg_iovlen); // kernel side this is treated as unsigned - for (msg.msg_hdr.msg_iov[0..msg_iovlen]) |iov, j| { - if (iov.iov_len > std.math.maxInt(i32) or @addWithOverflow(i32, size, @intCast(i32, iov.iov_len), &size)) { - // batch-send all messages up to the current message - if (next_unsent < i) { - const batch_size = i - next_unsent; - const r = syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags); - if (getErrno(r) != 0) return next_unsent; - if (r < batch_size) return next_unsent + r; - } - // send current message as own packet - const r = sendmsg(fd, &msg.msg_hdr, flags); - if (getErrno(r) != 0) return r; - // Linux limits the total bytes sent by sendmsg to INT_MAX, so this cast is safe. - msg.msg_len = @intCast(u32, r); - next_unsent = i + 1; - break; - } - } - } - if (next_unsent < kvlen or next_unsent == 0) { // want to make sure at least one syscall occurs (e.g. to trigger MSG_EOR) - const batch_size = kvlen - next_unsent; - const r = syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags); - if (getErrno(r) != 0) return r; - return next_unsent + r; - } - return kvlen; - } - return syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(msgvec), vlen, flags); -} - -pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize { - return syscall3(SYS_connect, @bitCast(usize, isize(fd)), @ptrToInt(addr), len); -} - -pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { - return syscall3(SYS_recvmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags); -} - -pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { - return syscall6(SYS_recvfrom, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); -} - -pub fn shutdown(fd: i32, how: i32) usize { - return syscall2(SYS_shutdown, @bitCast(usize, isize(fd)), @bitCast(usize, isize(how))); -} - -pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize { - return syscall3(SYS_bind, @bitCast(usize, isize(fd)), @ptrToInt(addr), @intCast(usize, len)); -} - -pub fn listen(fd: i32, backlog: u32) usize { - return syscall2(SYS_listen, @bitCast(usize, isize(fd)), backlog); -} - -pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { - return syscall6(SYS_sendto, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen)); -} - -pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize { - return syscall4(SYS_socketpair, @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0])); -} - -pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { - return accept4(fd, addr, len, 0); -} - -pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize { - return syscall4(SYS_accept4, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len), flags); -} - -pub fn fstat(fd: i32, stat_buf: *Stat) usize { - return syscall2(SYS_fstat, @bitCast(usize, isize(fd)), @ptrToInt(stat_buf)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn stat(pathname: [*]const u8, statbuf: *Stat) usize { - return syscall2(SYS_stat, @ptrToInt(pathname), @ptrToInt(statbuf)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn lstat(pathname: [*]const u8, statbuf: *Stat) usize { - return syscall2(SYS_lstat, @ptrToInt(pathname), @ptrToInt(statbuf)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn fstatat(dirfd: i32, path: [*]const u8, stat_buf: *Stat, flags: u32) usize { - return syscall4(SYS_fstatat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(stat_buf), flags); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn listxattr(path: [*]const u8, list: [*]u8, size: usize) usize { - return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn llistxattr(path: [*]const u8, list: [*]u8, size: usize) usize { - return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size); -} - -pub fn flistxattr(fd: usize, list: [*]u8, size: usize) usize { - return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn getxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { - return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn lgetxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { - return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn fgetxattr(fd: usize, name: [*]const u8, value: [*]u8, size: usize) usize { - return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn setxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { - return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn lsetxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { - return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn fsetxattr(fd: usize, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { - return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), size, flags); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn removexattr(path: [*]const u8, name: [*]const u8) usize { - return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn lremovexattr(path: [*]const u8, name: [*]const u8) usize { - return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn fremovexattr(fd: usize, name: [*]const u8) usize { - return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); -} - -pub fn sched_getaffinity(pid: i32, set: []usize) usize { - return syscall3(SYS_sched_getaffinity, @bitCast(usize, isize(pid)), set.len * @sizeOf(usize), @ptrToInt(set.ptr)); -} - -pub fn epoll_create() usize { - return epoll_create1(0); -} - -pub fn epoll_create1(flags: usize) usize { - return syscall1(SYS_epoll_create1, flags); -} - -pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: *epoll_event) usize { - return syscall4(SYS_epoll_ctl, @bitCast(usize, isize(epoll_fd)), @intCast(usize, op), @bitCast(usize, isize(fd)), @ptrToInt(ev)); -} - -pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32) usize { - return syscall4( - SYS_epoll_wait, - @bitCast(usize, isize(epoll_fd)), - @ptrToInt(events), - maxevents, - @bitCast(usize, isize(timeout)), - ); -} - -pub fn epoll_pwait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32, sigmask: ?*sigset_t) usize { - return syscall6( - SYS_epoll_pwait, - @bitCast(usize, isize(epoll_fd)), - @ptrToInt(events), - @intCast(usize, maxevents), - @bitCast(usize, isize(timeout)), - @ptrToInt(sigmask), - @sizeOf(sigset_t), - ); -} - -pub fn eventfd(count: u32, flags: u32) usize { - return syscall2(SYS_eventfd2, count, flags); -} - -pub fn timerfd_create(clockid: i32, flags: u32) usize { - return syscall2(SYS_timerfd_create, @bitCast(usize, isize(clockid)), flags); -} - -pub const itimerspec = extern struct { - it_interval: timespec, - it_value: timespec, -}; - -pub fn timerfd_gettime(fd: i32, curr_value: *itimerspec) usize { - return syscall2(SYS_timerfd_gettime, @bitCast(usize, isize(fd)), @ptrToInt(curr_value)); -} - -pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const itimerspec, old_value: ?*itimerspec) usize { - return syscall4(SYS_timerfd_settime, @bitCast(usize, isize(fd)), flags, @ptrToInt(new_value), @ptrToInt(old_value)); -} - -pub fn unshare(flags: usize) usize { - return syscall1(SYS_unshare, flags); -} - -pub fn capget(hdrp: *cap_user_header_t, datap: *cap_user_data_t) usize { - return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap)); -} - -pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize { - return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap)); -} - -// XXX: This should be weak -extern const __ehdr_start: elf.Ehdr = undefined; - -pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_info, size: usize, data: ?*T) i32, data: ?*T) isize { - if (builtin.link_libc) { - return std.c.dl_iterate_phdr(@ptrCast(std.c.dl_iterate_phdr_callback, callback), @ptrCast(?*c_void, data)); - } - - const elf_base = @ptrToInt(&__ehdr_start); - const n_phdr = __ehdr_start.e_phnum; - const phdrs = (@intToPtr([*]elf.Phdr, elf_base + __ehdr_start.e_phoff))[0..n_phdr]; - - var it = dl.linkmap_iterator(phdrs) catch return 0; - - // The executable has no dynamic link segment, create a single entry for - // the whole ELF image - if (it.end()) { - var info = dl_phdr_info{ - .dlpi_addr = elf_base, - .dlpi_name = c"/proc/self/exe", - .dlpi_phdr = @intToPtr([*]elf.Phdr, elf_base + __ehdr_start.e_phoff), - .dlpi_phnum = __ehdr_start.e_phnum, - }; - - return callback(&info, @sizeOf(dl_phdr_info), data); - } - - // Last return value from the callback function - var last_r: isize = 0; - while (it.next()) |entry| { - var dlpi_phdr: usize = undefined; - var dlpi_phnum: u16 = undefined; - - if (entry.l_addr != 0) { - const elf_header = @intToPtr(*elf.Ehdr, entry.l_addr); - dlpi_phdr = entry.l_addr + elf_header.e_phoff; - dlpi_phnum = elf_header.e_phnum; - } else { - // This is the running ELF image - dlpi_phdr = elf_base + __ehdr_start.e_phoff; - dlpi_phnum = __ehdr_start.e_phnum; - } - - var info = dl_phdr_info{ - .dlpi_addr = entry.l_addr, - .dlpi_name = entry.l_name, - .dlpi_phdr = @intToPtr([*]elf.Phdr, dlpi_phdr), - .dlpi_phnum = dlpi_phnum, - }; - - last_r = callback(&info, @sizeOf(dl_phdr_info), data); - if (last_r != 0) break; - } - - return last_r; -} +pub const sys = @import("linux/sys.zig"); +pub use if (builtin.link_libc) std.c else sys; test "import" { if (is_the_target) { diff --git a/std/os/linux/sys.zig b/std/os/linux/sys.zig new file mode 100644 index 0000000000..61b21ed2b7 --- /dev/null +++ b/std/os/linux/sys.zig @@ -0,0 +1,847 @@ +// This file provides the system interface functions for Linux matching those +// that are provided by libc, whether or not libc is linked. The following +// abstractions are made: +// * Work around kernel bugs and limitations. For example, see sendmmsg. +// * Implement all the syscalls in the same way that libc functions will +// provide `rename` when only the `renameat` syscall exists. +// * Does not support POSIX thread cancellation. +const std = @import("../../std.zig"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const maxInt = std.math.maxInt; +const elf = std.elf; +const vdso = @import("vdso.zig"); +const dl = @import("../../dynamic_library.zig"); +pub use switch (builtin.arch) { + .x86_64 => @import("x86_64.zig"), + .aarch64 => @import("arm64.zig"), + else => struct {}, +}; +pub const posix = @import("posix.zig"); +pub use posix; + +/// See `std.os.posix.getauxval`. +pub var elf_aux_maybe: ?[*]std.elf.Auxv = null; + +/// Get the errno from a syscall return value, or 0 for no error. +pub fn getErrno(r: usize) u12 { + const signed_r = @bitCast(isize, r); + return if (signed_r > -4096 and signed_r < 0) @intCast(u12, -signed_r) else 0; +} + +pub fn dup2(old: i32, new: i32) usize { + if (@hasDecl(@This(), "SYS_dup2")) { + return syscall2(SYS_dup2, @bitCast(usize, isize(old)), @bitCast(usize, isize(new))); + } else { + if (old == new) { + if (std.debug.runtime_safety) { + const rc = syscall2(SYS_fcntl, @bitCast(usize, isize(old)), F_GETFD); + if (@bitCast(isize, rc) < 0) return rc; + } + return @intCast(usize, old); + } else { + return syscall3(SYS_dup3, @bitCast(usize, isize(old)), @bitCast(usize, isize(new)), 0); + } + } +} + +pub fn dup3(old: i32, new: i32, flags: u32) usize { + return syscall3(SYS_dup3, @bitCast(usize, isize(old)), @bitCast(usize, isize(new)), flags); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn chdir(path: [*]const u8) usize { + return syscall1(SYS_chdir, @ptrToInt(path)); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn chroot(path: [*]const u8) usize { + return syscall1(SYS_chroot, @ptrToInt(path)); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { + return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp)); +} + +pub fn fork() usize { + if (@hasDecl(@This(), "SYS_fork")) { + return syscall0(SYS_fork); + } else { + return syscall2(SYS_clone, SIGCHLD, 0); + } +} + +/// This must be inline, and inline call the syscall function, because if the +/// child does a return it will clobber the parent's stack. +/// It is advised to avoid this function and use clone instead, because +/// the compiler is not aware of how vfork affects control flow and you may +/// see different results in optimized builds. +pub inline fn vfork() usize { + return @inlineCall(syscall0, SYS_vfork); +} + +pub fn futex_wait(uaddr: *const i32, futex_op: u32, val: i32, timeout: ?*timespec) usize { + return syscall4(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val), @ptrToInt(timeout)); +} + +pub fn futex_wake(uaddr: *const i32, futex_op: u32, val: i32) usize { + return syscall3(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val)); +} + +pub fn getcwd(buf: [*]u8, size: usize) usize { + return syscall2(SYS_getcwd, @ptrToInt(buf), size); +} + +pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { + return syscall3(SYS_getdents, @bitCast(usize, isize(fd)), @ptrToInt(dirp), count); +} + +pub fn getdents64(fd: i32, dirp: [*]u8, count: usize) usize { + return syscall3(SYS_getdents64, @bitCast(usize, isize(fd)), @ptrToInt(dirp), count); +} + +pub fn inotify_init1(flags: u32) usize { + return syscall1(SYS_inotify_init1, flags); +} + +pub fn inotify_add_watch(fd: i32, pathname: [*]const u8, mask: u32) usize { + return syscall3(SYS_inotify_add_watch, @bitCast(usize, isize(fd)), @ptrToInt(pathname), mask); +} + +pub fn inotify_rm_watch(fd: i32, wd: i32) usize { + return syscall2(SYS_inotify_rm_watch, @bitCast(usize, isize(fd)), @bitCast(usize, isize(wd))); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { + if (@hasDecl(@This(), "SYS_readlink")) { + return syscall3(SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); + } else { + return syscall4(SYS_readlinkat, AT_FDCWD, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); + } +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn readlinkat(dirfd: i32, noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { + return syscall4(SYS_readlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn mkdir(path: [*]const u8, mode: u32) usize { + if (@hasDecl(@This(), "SYS_mkdir")) { + return syscall2(SYS_mkdir, @ptrToInt(path), mode); + } else { + return syscall3(SYS_mkdirat, AT_FDCWD, @ptrToInt(path), mode); + } +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn mkdirat(dirfd: i32, path: [*]const u8, mode: u32) usize { + return syscall3(SYS_mkdirat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn mount(special: [*]const u8, dir: [*]const u8, fstype: [*]const u8, flags: u32, data: usize) usize { + return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn umount(special: [*]const u8) usize { + return syscall2(SYS_umount2, @ptrToInt(special), 0); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn umount2(special: [*]const u8, flags: u32) usize { + return syscall2(SYS_umount2, @ptrToInt(special), flags); +} + +pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { + return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, @bitCast(usize, isize(fd)), @bitCast(usize, offset)); +} + +pub fn mprotect(address: usize, length: usize, protection: usize) usize { + return syscall3(SYS_mprotect, address, length, protection); +} + +pub fn munmap(address: usize, length: usize) usize { + return syscall2(SYS_munmap, address, length); +} + +pub fn read(fd: i32, buf: [*]u8, count: usize) usize { + return syscall3(SYS_read, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); +} + +pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize { + return syscall4(SYS_preadv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset); +} + +pub fn readv(fd: i32, iov: [*]const iovec, count: usize) usize { + return syscall3(SYS_readv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count); +} + +pub fn writev(fd: i32, iov: [*]const iovec_const, count: usize) usize { + return syscall3(SYS_writev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count); +} + +pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) usize { + return syscall4(SYS_pwritev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn rmdir(path: [*]const u8) usize { + if (@hasDecl(@This(), "SYS_rmdir")) { + return syscall1(SYS_rmdir, @ptrToInt(path)); + } else { + return syscall3(SYS_unlinkat, AT_FDCWD, @ptrToInt(path), AT_REMOVEDIR); + } +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { + if (@hasDecl(@This(), "SYS_symlink")) { + return syscall2(SYS_symlink, @ptrToInt(existing), @ptrToInt(new)); + } else { + return syscall3(SYS_symlinkat, @ptrToInt(existing), AT_FDCWD, @ptrToInt(new)); + } +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn symlinkat(existing: [*]const u8, newfd: i32, newpath: [*]const u8) usize { + return syscall3(SYS_symlinkat, @ptrToInt(existing), @bitCast(usize, isize(newfd)), @ptrToInt(newpath)); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: usize) usize { + return syscall4(SYS_pread, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn access(path: [*]const u8, mode: u32) usize { + return syscall2(SYS_access, @ptrToInt(path), mode); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn faccessat(dirfd: i32, path: [*]const u8, mode: u32) usize { + return syscall3(SYS_faccessat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode); +} + +pub fn pipe(fd: *[2]i32) usize { + if (@hasDecl(@This(), "SYS_pipe")) { + return syscall1(SYS_pipe, @ptrToInt(fd)); + } else { + return syscall2(SYS_pipe2, @ptrToInt(fd), 0); + } +} + +pub fn pipe2(fd: *[2]i32, flags: u32) usize { + return syscall2(SYS_pipe2, @ptrToInt(fd), flags); +} + +pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { + return syscall3(SYS_write, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); +} + +pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { + return syscall4(SYS_pwrite, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn rename(old: [*]const u8, new: [*]const u8) usize { + if (@hasDecl(@This(), "SYS_rename")) { + return syscall2(SYS_rename, @ptrToInt(old), @ptrToInt(new)); + } else if (@hasDecl(@This(), "SYS_renameat")) { + return syscall4(SYS_renameat, AT_FDCWD, @ptrToInt(old), AT_FDCWD, @ptrToInt(new)); + } else { + return syscall5(SYS_renameat2, AT_FDCWD, @ptrToInt(old), AT_FDCWD, @ptrToInt(new), 0); + } +} + +pub fn renameat(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const u8) usize { + if (@hasDecl(@This(), "SYS_renameat")) { + return syscall4( + SYS_renameat, + @bitCast(usize, isize(oldfd)), + @ptrToInt(old), + @bitCast(usize, isize(newfd)), + @ptrToInt(new), + ); + } else { + return syscall5( + SYS_renameat2, + @bitCast(usize, isize(oldfd)), + @ptrToInt(old), + @bitCast(usize, isize(newfd)), + @ptrToInt(new), + 0, + ); + } +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn renameat2(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const u8, flags: u32) usize { + return syscall5( + SYS_renameat2, + @bitCast(usize, isize(oldfd)), + @ptrToInt(oldpath), + @bitCast(usize, isize(newfd)), + @ptrToInt(newpath), + flags, + ); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn open(path: [*]const u8, flags: u32, perm: usize) usize { + return syscall3(SYS_open, @ptrToInt(path), flags, perm); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn create(path: [*]const u8, perm: usize) usize { + return syscall2(SYS_creat, @ptrToInt(path), perm); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn openat(dirfd: i32, path: [*]const u8, flags: u32, mode: usize) usize { + // dirfd could be negative, for example AT_FDCWD is -100 + return syscall4(SYS_openat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode); +} + +/// See also `clone` (from the arch-specific include) +pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: *i32, child_tid: *i32, newtls: usize) usize { + return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls); +} + +/// See also `clone` (from the arch-specific include) +pub fn clone2(flags: u32, child_stack_ptr: usize) usize { + return syscall2(SYS_clone, flags, child_stack_ptr); +} + +pub fn close(fd: i32) usize { + return syscall1(SYS_close, @bitCast(usize, isize(fd))); +} + +pub fn lseek(fd: i32, offset: isize, ref_pos: usize) usize { + return syscall3(SYS_lseek, @bitCast(usize, isize(fd)), @bitCast(usize, offset), ref_pos); +} + +pub fn exit(status: i32) noreturn { + _ = syscall1(SYS_exit, @bitCast(usize, isize(status))); + unreachable; +} + +pub fn exit_group(status: i32) noreturn { + _ = syscall1(SYS_exit_group, @bitCast(usize, isize(status))); + unreachable; +} + +pub fn getrandom(buf: [*]u8, count: usize, flags: u32) usize { + return syscall3(SYS_getrandom, @ptrToInt(buf), count, flags); +} + +pub fn kill(pid: i32, sig: i32) usize { + return syscall2(SYS_kill, @bitCast(usize, isize(pid)), @bitCast(usize, isize(sig))); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn unlink(path: [*]const u8) usize { + if (@hasDecl(@This(), "SYS_unlink")) { + return syscall1(SYS_unlink, @ptrToInt(path)); + } else { + return syscall3(SYS_unlinkat, AT_FDCWD, @ptrToInt(path), 0); + } +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn unlinkat(dirfd: i32, path: [*]const u8, flags: u32) usize { + return syscall3(SYS_unlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags); +} + +pub fn waitpid(pid: i32, status: *i32, options: i32) usize { + return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0); +} + +var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime); + +// We must follow the C calling convention when we call into the VDSO +const vdso_clock_gettime_ty = extern fn (i32, *timespec) usize; + +pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { + if (VDSO_CGT_SYM.len != 0) { + const ptr = @atomicLoad(?*const c_void, &vdso_clock_gettime, .Unordered); + if (ptr) |fn_ptr| { + const f = @ptrCast(vdso_clock_gettime_ty, fn_ptr); + const rc = f(clk_id, tp); + switch (rc) { + 0, @bitCast(usize, isize(-EINVAL)) => return rc, + else => {}, + } + } + } + return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); +} + +extern fn init_vdso_clock_gettime(clk: i32, ts: *timespec) usize { + const ptr = @intToPtr(?*const c_void, vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM)); + // Note that we may not have a VDSO at all, update the stub address anyway + // so that clock_gettime will fall back on the good old (and slow) syscall + _ = @cmpxchgStrong(?*const c_void, &vdso_clock_gettime, &init_vdso_clock_gettime, ptr, .Monotonic, .Monotonic); + // Call into the VDSO if available + if (ptr) |fn_ptr| { + const f = @ptrCast(vdso_clock_gettime_ty, fn_ptr); + return f(clk, ts); + } + return @bitCast(usize, isize(-ENOSYS)); +} + +pub fn clock_getres(clk_id: i32, tp: *timespec) usize { + return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); +} + +pub fn clock_settime(clk_id: i32, tp: *const timespec) usize { + return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); +} + +pub fn gettimeofday(tv: *timeval, tz: *timezone) usize { + return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz)); +} + +pub fn settimeofday(tv: *const timeval, tz: *const timezone) usize { + return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz)); +} + +pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { + return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem)); +} + +pub fn setuid(uid: u32) usize { + return syscall1(SYS_setuid, uid); +} + +pub fn setgid(gid: u32) usize { + return syscall1(SYS_setgid, gid); +} + +pub fn setreuid(ruid: u32, euid: u32) usize { + return syscall2(SYS_setreuid, ruid, euid); +} + +pub fn setregid(rgid: u32, egid: u32) usize { + return syscall2(SYS_setregid, rgid, egid); +} + +pub fn getuid() u32 { + return u32(syscall0(SYS_getuid)); +} + +pub fn getgid() u32 { + return u32(syscall0(SYS_getgid)); +} + +pub fn geteuid() u32 { + return u32(syscall0(SYS_geteuid)); +} + +pub fn getegid() u32 { + return u32(syscall0(SYS_getegid)); +} + +pub fn seteuid(euid: u32) usize { + return syscall1(SYS_seteuid, euid); +} + +pub fn setegid(egid: u32) usize { + return syscall1(SYS_setegid, egid); +} + +pub fn getresuid(ruid: *u32, euid: *u32, suid: *u32) usize { + return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid)); +} + +pub fn getresgid(rgid: *u32, egid: *u32, sgid: *u32) usize { + return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid)); +} + +pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize { + return syscall3(SYS_setresuid, ruid, euid, suid); +} + +pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize { + return syscall3(SYS_setresgid, rgid, egid, sgid); +} + +pub fn getgroups(size: usize, list: *u32) usize { + return syscall2(SYS_getgroups, size, @ptrToInt(list)); +} + +pub fn setgroups(size: usize, list: *const u32) usize { + return syscall2(SYS_setgroups, size, @ptrToInt(list)); +} + +pub fn getpid() i32 { + return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid))); +} + +pub fn gettid() i32 { + return @bitCast(i32, @truncate(u32, syscall0(SYS_gettid))); +} + +pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { + return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8); +} + +pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize { + assert(sig >= 1); + assert(sig != SIGKILL); + assert(sig != SIGSTOP); + var ksa = k_sigaction{ + .handler = act.handler, + .flags = act.flags | SA_RESTORER, + .mask = undefined, + .restorer = @ptrCast(extern fn () void, restore_rt), + }; + var ksa_old: k_sigaction = undefined; + @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &act.mask), 8); + const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask))); + const err = getErrno(result); + if (err != 0) { + return result; + } + if (oact) |old| { + old.handler = ksa_old.handler; + old.flags = @truncate(u32, ksa_old.flags); + @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); + } + return 0; +} + +fn blockAllSignals(set: *sigset_t) void { + _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8); +} + +fn blockAppSignals(set: *sigset_t) void { + _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG / 8); +} + +fn restoreSignals(set: *sigset_t) void { + _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG / 8); +} + +pub fn sigaddset(set: *sigset_t, sig: u6) void { + const s = sig - 1; + (set.*)[@intCast(usize, s) / usize.bit_count] |= @intCast(usize, 1) << (s & (usize.bit_count - 1)); +} + +pub fn sigismember(set: *const sigset_t, sig: u6) bool { + const s = sig - 1; + return ((set.*)[@intCast(usize, s) / usize.bit_count] & (@intCast(usize, 1) << (s & (usize.bit_count - 1)))) != 0; +} + +pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { + return syscall3(SYS_getsockname, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len)); +} + +pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { + return syscall3(SYS_getpeername, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len)); +} + +pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { + return syscall3(SYS_socket, domain, socket_type, protocol); +} + +pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize { + return syscall5(SYS_setsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen)); +} + +pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize { + return syscall5(SYS_getsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); +} + +pub fn sendmsg(fd: i32, msg: *msghdr_const, flags: u32) usize { + return syscall3(SYS_sendmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags); +} + +pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize { + if (@typeInfo(usize).Int.bits > @typeInfo(@typeOf(mmsghdr(undefined).msg_len)).Int.bits) { + // workaround kernel brokenness: + // if adding up all iov_len overflows a i32 then split into multiple calls + // see https://www.openwall.com/lists/musl/2014/06/07/5 + const kvlen = if (vlen > IOV_MAX) IOV_MAX else vlen; // matches kernel + var next_unsent: usize = 0; + for (msgvec[0..kvlen]) |*msg, i| { + var size: i32 = 0; + const msg_iovlen = @intCast(usize, msg.msg_hdr.msg_iovlen); // kernel side this is treated as unsigned + for (msg.msg_hdr.msg_iov[0..msg_iovlen]) |iov, j| { + if (iov.iov_len > std.math.maxInt(i32) or @addWithOverflow(i32, size, @intCast(i32, iov.iov_len), &size)) { + // batch-send all messages up to the current message + if (next_unsent < i) { + const batch_size = i - next_unsent; + const r = syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags); + if (getErrno(r) != 0) return next_unsent; + if (r < batch_size) return next_unsent + r; + } + // send current message as own packet + const r = sendmsg(fd, &msg.msg_hdr, flags); + if (getErrno(r) != 0) return r; + // Linux limits the total bytes sent by sendmsg to INT_MAX, so this cast is safe. + msg.msg_len = @intCast(u32, r); + next_unsent = i + 1; + break; + } + } + } + if (next_unsent < kvlen or next_unsent == 0) { // want to make sure at least one syscall occurs (e.g. to trigger MSG_EOR) + const batch_size = kvlen - next_unsent; + const r = syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags); + if (getErrno(r) != 0) return r; + return next_unsent + r; + } + return kvlen; + } + return syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(msgvec), vlen, flags); +} + +pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize { + return syscall3(SYS_connect, @bitCast(usize, isize(fd)), @ptrToInt(addr), len); +} + +pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { + return syscall3(SYS_recvmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags); +} + +pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { + return syscall6(SYS_recvfrom, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); +} + +pub fn shutdown(fd: i32, how: i32) usize { + return syscall2(SYS_shutdown, @bitCast(usize, isize(fd)), @bitCast(usize, isize(how))); +} + +pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize { + return syscall3(SYS_bind, @bitCast(usize, isize(fd)), @ptrToInt(addr), @intCast(usize, len)); +} + +pub fn listen(fd: i32, backlog: u32) usize { + return syscall2(SYS_listen, @bitCast(usize, isize(fd)), backlog); +} + +pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { + return syscall6(SYS_sendto, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen)); +} + +pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize { + return syscall4(SYS_socketpair, @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0])); +} + +pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { + return accept4(fd, addr, len, 0); +} + +pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize { + return syscall4(SYS_accept4, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len), flags); +} + +pub fn fstat(fd: i32, stat_buf: *Stat) usize { + return syscall2(SYS_fstat, @bitCast(usize, isize(fd)), @ptrToInt(stat_buf)); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn stat(pathname: [*]const u8, statbuf: *Stat) usize { + return syscall2(SYS_stat, @ptrToInt(pathname), @ptrToInt(statbuf)); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn lstat(pathname: [*]const u8, statbuf: *Stat) usize { + return syscall2(SYS_lstat, @ptrToInt(pathname), @ptrToInt(statbuf)); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn fstatat(dirfd: i32, path: [*]const u8, stat_buf: *Stat, flags: u32) usize { + return syscall4(SYS_fstatat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(stat_buf), flags); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn listxattr(path: [*]const u8, list: [*]u8, size: usize) usize { + return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn llistxattr(path: [*]const u8, list: [*]u8, size: usize) usize { + return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size); +} + +pub fn flistxattr(fd: usize, list: [*]u8, size: usize) usize { + return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn getxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { + return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn lgetxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { + return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn fgetxattr(fd: usize, name: [*]const u8, value: [*]u8, size: usize) usize { + return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn setxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { + return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn lsetxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { + return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn fsetxattr(fd: usize, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { + return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), size, flags); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn removexattr(path: [*]const u8, name: [*]const u8) usize { + return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name)); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn lremovexattr(path: [*]const u8, name: [*]const u8) usize { + return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name)); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn fremovexattr(fd: usize, name: [*]const u8) usize { + return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); +} + +pub fn sched_getaffinity(pid: i32, set: []usize) usize { + return syscall3(SYS_sched_getaffinity, @bitCast(usize, isize(pid)), set.len * @sizeOf(usize), @ptrToInt(set.ptr)); +} + +pub fn epoll_create() usize { + return epoll_create1(0); +} + +pub fn epoll_create1(flags: usize) usize { + return syscall1(SYS_epoll_create1, flags); +} + +pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: *epoll_event) usize { + return syscall4(SYS_epoll_ctl, @bitCast(usize, isize(epoll_fd)), @intCast(usize, op), @bitCast(usize, isize(fd)), @ptrToInt(ev)); +} + +pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32) usize { + return syscall4( + SYS_epoll_wait, + @bitCast(usize, isize(epoll_fd)), + @ptrToInt(events), + maxevents, + @bitCast(usize, isize(timeout)), + ); +} + +pub fn epoll_pwait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32, sigmask: ?*sigset_t) usize { + return syscall6( + SYS_epoll_pwait, + @bitCast(usize, isize(epoll_fd)), + @ptrToInt(events), + @intCast(usize, maxevents), + @bitCast(usize, isize(timeout)), + @ptrToInt(sigmask), + @sizeOf(sigset_t), + ); +} + +pub fn eventfd(count: u32, flags: u32) usize { + return syscall2(SYS_eventfd2, count, flags); +} + +pub fn timerfd_create(clockid: i32, flags: u32) usize { + return syscall2(SYS_timerfd_create, @bitCast(usize, isize(clockid)), flags); +} + +pub const itimerspec = extern struct { + it_interval: timespec, + it_value: timespec, +}; + +pub fn timerfd_gettime(fd: i32, curr_value: *itimerspec) usize { + return syscall2(SYS_timerfd_gettime, @bitCast(usize, isize(fd)), @ptrToInt(curr_value)); +} + +pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const itimerspec, old_value: ?*itimerspec) usize { + return syscall4(SYS_timerfd_settime, @bitCast(usize, isize(fd)), flags, @ptrToInt(new_value), @ptrToInt(old_value)); +} + +pub fn unshare(flags: usize) usize { + return syscall1(SYS_unshare, flags); +} + +pub fn capget(hdrp: *cap_user_header_t, datap: *cap_user_data_t) usize { + return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap)); +} + +pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize { + return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap)); +} + +// XXX: This should be weak +extern const __ehdr_start: elf.Ehdr = undefined; + +pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_info, size: usize, data: ?*T) i32, data: ?*T) isize { + if (builtin.link_libc) { + return std.c.dl_iterate_phdr(@ptrCast(std.c.dl_iterate_phdr_callback, callback), @ptrCast(?*c_void, data)); + } + + const elf_base = @ptrToInt(&__ehdr_start); + const n_phdr = __ehdr_start.e_phnum; + const phdrs = (@intToPtr([*]elf.Phdr, elf_base + __ehdr_start.e_phoff))[0..n_phdr]; + + var it = dl.linkmap_iterator(phdrs) catch return 0; + + // The executable has no dynamic link segment, create a single entry for + // the whole ELF image + if (it.end()) { + var info = dl_phdr_info{ + .dlpi_addr = elf_base, + .dlpi_name = c"/proc/self/exe", + .dlpi_phdr = @intToPtr([*]elf.Phdr, elf_base + __ehdr_start.e_phoff), + .dlpi_phnum = __ehdr_start.e_phnum, + }; + + return callback(&info, @sizeOf(dl_phdr_info), data); + } + + // Last return value from the callback function + var last_r: isize = 0; + while (it.next()) |entry| { + var dlpi_phdr: usize = undefined; + var dlpi_phnum: u16 = undefined; + + if (entry.l_addr != 0) { + const elf_header = @intToPtr(*elf.Ehdr, entry.l_addr); + dlpi_phdr = entry.l_addr + elf_header.e_phoff; + dlpi_phnum = elf_header.e_phnum; + } else { + // This is the running ELF image + dlpi_phdr = elf_base + __ehdr_start.e_phoff; + dlpi_phnum = __ehdr_start.e_phnum; + } + + var info = dl_phdr_info{ + .dlpi_addr = entry.l_addr, + .dlpi_name = entry.l_name, + .dlpi_phdr = @intToPtr([*]elf.Phdr, dlpi_phdr), + .dlpi_phnum = dlpi_phnum, + }; + + last_r = callback(&info, @sizeOf(dl_phdr_info), data); + if (last_r != 0) break; + } + + return last_r; +} diff --git a/std/os/posix.zig b/std/os/posix.zig index 9378f375e0..dd0a6b54e1 100644 --- a/std/os/posix.zig +++ b/std/os/posix.zig @@ -2,21 +2,18 @@ // The purpose is not to match POSIX as closely as possible. Instead, // the goal is to provide a very specific layer of abstraction: // * Implement the POSIX functions, types, and definitions where possible, -// using lower-level target-specific API. For example, on Linux `rename` might call -// SYS_renameat or SYS_rename depending on the architecture. +// using lower-level target-specific API. // * When null-terminated byte buffers are required, provide APIs which accept // slices as well as APIs which accept null-terminated byte buffers. Same goes // for UTF-16LE encoding. // * Convert "errno"-style error codes into Zig errors. -// * Work around kernel bugs and limitations. For example, if a function accepts -// a `usize` number of bytes to write, but the kernel can only handle maxInt(u32) -// number of bytes, this API layer should introduce a loop to make multiple -// syscalls so that the full `usize` number of bytes are written. // * Implement the OS-specific functions, types, and definitions that the Zig // standard library needs, at the same API abstraction layer as outlined above. -// this includes, for example Windows functions. -// * When there exists a corresponding libc function and linking libc, call the -// libc function. +// For example kevent() and getrandom(). Windows-specific functions are separate, +// in `std.os.windows`. +// * When there exists a corresponding libc function and linking libc, the libc +// implementation is used. Exceptions are made for known buggy areas of libc. +// On Linux libc can be side-stepped by using `std.os.linux.sys`. // Note: The Zig standard library does not support POSIX thread cancellation, and // in general EINTR is handled by trying again. @@ -29,18 +26,13 @@ const mem = std.mem; const BufMap = std.BufMap; const Allocator = mem.Allocator; const windows = os.windows; +const kernel32 = windows.kernel32; const wasi = os.wasi; const linux = os.linux; const testing = std.testing; pub use system.posix; -/// > The maximum path of 32,767 characters is approximate, because the "\\?\" -/// > prefix may be expanded to a longer string by the system at run time, and -/// > this expansion applies to the total length. -/// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation -pub const PATH_MAX_WIDE = 32767; - /// See also `getenv`. pub var environ: [][*]u8 = undefined; @@ -59,7 +51,7 @@ pub const errno = system.getErrno; /// Note: The Zig standard library does not support POSIX thread cancellation. pub fn close(fd: fd_t) void { if (windows.is_the_target and !builtin.link_libc) { - assert(windows.CloseHandle(fd) != 0); + assert(kernel32.CloseHandle(fd) != 0); return; } if (wasi.is_the_target) { @@ -87,11 +79,10 @@ pub fn getrandom(buf: []u8) GetRandomError!void { // Call RtlGenRandom() instead of CryptGetRandom() on Windows // https://github.com/rust-lang-nursery/rand/issues/111 // https://bugzilla.mozilla.org/show_bug.cgi?id=504270 - if (windows.RtlGenRandom(buf.ptr, buf.len) == 0) { - const err = windows.GetLastError(); - return switch (err) { - else => unexpectedErrorWindows(err), - }; + if (windows.advapi32.RtlGenRandom(buf.ptr, buf.len) == 0) { + switch (kernel32.GetLastError()) { + else => |err| return windows.unexpectedError(err), + } } return; } @@ -230,12 +221,11 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(math.maxInt(windows.DWORD)), buffer.len - index)); var amt_read: windows.DWORD = undefined; if (windows.ReadFile(fd, buffer.ptr + index, want_read_count, &amt_read, null) == 0) { - const err = windows.GetLastError(); - return switch (err) { + switch (windows.GetLastError()) { windows.ERROR.OPERATION_ABORTED => continue, windows.ERROR.BROKEN_PIPE => return index, - else => unexpectedErrorWindows(err), - }; + else => |err| return windows.unexpectedError(err), + } } if (amt_read == 0) return index; index += amt_read; @@ -372,7 +362,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void { windows.ERROR.NOT_ENOUGH_QUOTA => return error.SystemResources, windows.ERROR.IO_PENDING => unreachable, windows.ERROR.BROKEN_PIPE => return error.BrokenPipe, - else => |err| return unexpectedErrorWindows(err), + else => |err| return windows.unexpectedError(err), } } } @@ -543,67 +533,6 @@ pub fn openC(file_path: [*]const u8, flags: u32, perm: usize) OpenError!fd_t { } } -pub const WindowsOpenError = error{ - SharingViolation, - PathAlreadyExists, - - /// When any of the path components can not be found or the file component can not - /// be found. Some operating systems distinguish between path components not found and - /// file components not found, but they are collapsed into FileNotFound to gain - /// consistency across operating systems. - FileNotFound, - - AccessDenied, - PipeBusy, - NameTooLong, - - /// On Windows, file paths must be valid Unicode. - InvalidUtf8, - - /// On Windows, file paths cannot contain these characters: - /// '/', '*', '?', '"', '<', '>', '|' - BadPathName, - - Unexpected, -}; - -pub fn openWindows( - file_path: []const u8, - desired_access: windows.DWORD, - share_mode: windows.DWORD, - creation_disposition: windows.DWORD, - flags_and_attrs: windows.DWORD, -) WindowsOpenError!fd_t { - const file_path_w = try sliceToPrefixedFileW(file_path); - return openW(&file_path_w, desired_access, share_mode, creation_disposition, flags_and_attrs); -} - -pub fn openW( - file_path_w: [*]const u16, - desired_access: windows.DWORD, - share_mode: windows.DWORD, - creation_disposition: windows.DWORD, - flags_and_attrs: windows.DWORD, -) WindowsOpenError!windows.HANDLE { - const result = windows.CreateFileW(file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null); - - if (result == windows.INVALID_HANDLE_VALUE) { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.SHARING_VIOLATION => return error.SharingViolation, - windows.ERROR.ALREADY_EXISTS => return error.PathAlreadyExists, - windows.ERROR.FILE_EXISTS => return error.PathAlreadyExists, - windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, - windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, - windows.ERROR.ACCESS_DENIED => return error.AccessDenied, - windows.ERROR.PIPE_BUSY => return error.PipeBusy, - else => return unexpectedErrorWindows(err), - } - } - - return result; -} - pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void { while (true) { switch (errno(system.dup2(old_fd, new_fd))) { @@ -797,14 +726,13 @@ pub const GetCwdError = error{ /// The result is a slice of out_buffer, indexed from 0. pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { if (windows.is_the_target and !builtin.link_libc) { - var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; + var utf16le_buf: [windows.PATH_MAX_WIDE]u16 = undefined; const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast const casted_ptr = ([*]u16)(&utf16le_buf); // TODO shouldn't need this cast const result = windows.GetCurrentDirectoryW(casted_len, casted_ptr); if (result == 0) { - const err = windows.GetLastError(); - switch (err) { - else => return unexpectedErrorWindows(err), + switch (windows.GetLastError()) { + else => |err| return windows.unexpectedError(err), } } assert(result <= utf16le_buf.len); @@ -910,12 +838,7 @@ pub fn symlinkC(target_path: [*]const u8, new_path: [*]const u8) SymLinkError!vo const new_path_w = try cStrToPrefixedFileW(new_path); return symlinkW(&target_path_w, &new_path_w); } - const err = if (builtin.link_libc or @hasDecl(system, "SYS_symlink")) blk: { - break :blk errno(system.symlink(target_path, new_path)); - } else blk: { - break :blk errno(system.symlinkat(target_path, AT_FDCWD, new_path)); - }; - switch (err) { + switch (errno(system.symlink(target_path, new_path))) { 0 => return, EFAULT => unreachable, EINVAL => unreachable, @@ -931,7 +854,7 @@ pub fn symlinkC(target_path: [*]const u8, new_path: [*]const u8) SymLinkError!vo ENOMEM => return error.SystemResources, ENOSPC => return error.NoSpaceLeft, EROFS => return error.ReadOnlyFileSystem, - else => return unexpectedErrno(err), + else => |err| return unexpectedErrno(err), } } @@ -941,9 +864,8 @@ pub fn symlinkC(target_path: [*]const u8, new_path: [*]const u8) SymLinkError!vo /// TODO handle when linking libc pub fn symlinkW(target_path_w: [*]const u16, new_path_w: [*]const u16) SymLinkError!void { if (windows.CreateSymbolicLinkW(target_path_w, new_path_w, 0) == 0) { - const err = windows.GetLastError(); - switch (err) { - else => return unexpectedErrorWindows(err), + switch (windows.GetLastError()) { + else => |err| return windows.unexpectedError(err), } } } @@ -984,13 +906,12 @@ pub fn unlink(file_path: []const u8) UnlinkError!void { /// TODO handle when linking libc pub fn unlinkW(file_path: [*]const u16) UnlinkError!void { if (windows.unlinkW(file_path) == 0) { - const err = windows.GetLastError(); - switch (err) { + switch (windows.GetLastError()) { windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, windows.ERROR.ACCESS_DENIED => return error.AccessDenied, windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong, windows.ERROR.INVALID_PARAMETER => return error.NameTooLong, - else => return unexpectedErrorWindows(err), + else => |err| return windows.unexpectedError(err), } } } @@ -1001,12 +922,7 @@ pub fn unlinkC(file_path: [*]const u8) UnlinkError!void { const file_path_w = try cStrToPrefixedFileW(file_path); return unlinkW(&file_path_w); } - const err = if (builtin.link_libc or @hasDecl(system, "SYS_unlink")) blk: { - break :blk errno(system.unlink(file_path)); - } else blk: { - break :blk errno(system.unlinkat(AT_FDCWD, file_path, 0)); - }; - switch (err) { + switch (errno(system.unlink(file_path))) { 0 => return, EACCES => return error.AccessDenied, EPERM => return error.AccessDenied, @@ -1021,7 +937,7 @@ pub fn unlinkC(file_path: [*]const u8) UnlinkError!void { ENOTDIR => return error.NotDir, ENOMEM => return error.SystemResources, EROFS => return error.ReadOnlyFileSystem, - else => return unexpectedErrno(err), + else => |err| return unexpectedErrno(err), } } @@ -1047,14 +963,7 @@ pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void { const new_path_w = try cStrToPrefixedFileW(new_path); return renameW(&old_path_w, &new_path_w); } - const err = if (builtin.link_libc or @hasDecl(system, "SYS_rename")) blk: { - break :blk errno(system.rename(old_path, new_path)); - } else if (@hasDecl(system, "SYS_renameat")) blk: { - break :blk errno(system.renameat(AT_FDCWD, old_path, AT_FDCWD, new_path)); - } else blk: { - break :blk errno(system.renameat2(AT_FDCWD, old_path, AT_FDCWD, new_path, 0)); - }; - switch (err) { + switch (errno(system.rename(old_path, new_path))) { 0 => return, EACCES => return error.AccessDenied, EPERM => return error.AccessDenied, @@ -1074,7 +983,7 @@ pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void { ENOTEMPTY => return error.PathAlreadyExists, EROFS => return error.ReadOnlyFileSystem, EXDEV => return error.RenameAcrossMountPoints, - else => return unexpectedErrno(err), + else => |err| return unexpectedErrno(err), } } @@ -1083,9 +992,8 @@ pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void { pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) RenameError!void { const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; if (windows.MoveFileExW(old_path, new_path, flags) == 0) { - const err = windows.GetLastError(); - switch (err) { - else => return unexpectedErrorWindows(err), + switch (windows.GetLastError()) { + else => |err| return windows.unexpectedError(err), } } } @@ -1110,12 +1018,7 @@ pub fn mkdirC(dir_path: [*]const u8, mode: u32) MakeDirError!void { const dir_path_w = try cStrToPrefixedFileW(dir_path); return mkdirW(&dir_path_w, mode); } - const err = if (builtin.link_libc or @hasDecl(system, "SYS_mkdir")) blk: { - break :blk errno(system.mkdir(dir_path, mode)); - } else blk: { - break :blk errno(system.mkdirat(AT_FDCWD, dir_path, mode)); - }; - switch (err) { + switch (errno(system.mkdir(dir_path, mode))) { 0 => return, EACCES => return error.AccessDenied, EPERM => return error.AccessDenied, @@ -1130,7 +1033,7 @@ pub fn mkdirC(dir_path: [*]const u8, mode: u32) MakeDirError!void { ENOSPC => return error.NoSpaceLeft, ENOTDIR => return error.NotDir, EROFS => return error.ReadOnlyFileSystem, - else => return unexpectedErrno(err), + else => |err| return unexpectedErrno(err), } } @@ -1139,11 +1042,10 @@ pub fn mkdirW(dir_path: []const u8, mode: u32) MakeDirError!void { const dir_path_w = try sliceToPrefixedFileW(dir_path); if (windows.CreateDirectoryW(&dir_path_w, null) == 0) { - const err = windows.GetLastError(); - switch (err) { + switch (windows.GetLastError()) { windows.ERROR.ALREADY_EXISTS => return error.PathAlreadyExists, windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, - else => return unexpectedErrorWindows(err), + else => |err| return windows.unexpectedError(err), } } } @@ -1180,12 +1082,7 @@ pub fn rmdirC(dir_path: [*]const u8) DeleteDirError!void { const dir_path_w = try cStrToPrefixedFileW(dir_path); return rmdirW(&dir_path_w); } - const err = if (builtin.link_libc or @hasDecl(system, "SYS_rmdir")) blk: { - break :blk errno(system.rmdir(dir_path)); - } else blk: { - break :blk errno(system.unlinkat(AT_FDCWD, dir_path, AT_REMOVEDIR)); - }; - switch (err) { + switch (errno(system.rmdir(dir_path))) { 0 => return, EACCES => return error.AccessDenied, EPERM => return error.AccessDenied, @@ -1200,7 +1097,7 @@ pub fn rmdirC(dir_path: [*]const u8) DeleteDirError!void { EEXIST => return error.DirNotEmpty, ENOTEMPTY => return error.DirNotEmpty, EROFS => return error.ReadOnlyFileSystem, - else => return unexpectedErrno(err), + else => |err| return unexpectedErrno(err), } } @@ -1208,11 +1105,10 @@ pub fn rmdirC(dir_path: [*]const u8) DeleteDirError!void { /// TODO handle linking libc pub fn rmdirW(dir_path_w: [*]const u16) DeleteDirError!void { if (windows.RemoveDirectoryW(dir_path_w) == 0) { - const err = windows.GetLastError(); - switch (err) { + switch (windows.GetLastError()) { windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty, - else => return unexpectedErrorWindows(err), + else => |err| return windows.unexpectedError(err), } } } @@ -1277,11 +1173,7 @@ pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 { const file_path_w = try cStrToPrefixedFileW(file_path); return readlinkW(&file_path_w, out_buffer); } - const rc = if (builtin.link_libc or @hasDecl(system, "SYS_readlink")) blk: { - break :blk system.readlink(file_path, out_buffer.ptr, out_buffer.len); - } else blk: { - break :blk system.readlinkat(AT_FDCWD, file_path, out_buffer.ptr, out_buffer.len); - }; + const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len); switch (errno(rc)) { 0 => return out_buffer[0..rc], EACCES => return error.AccessDenied, @@ -1354,7 +1246,7 @@ pub fn GetStdHandle(handle_id: windows.DWORD) GetStdHandleError!fd_t { const handle = windows.GetStdHandle(handle_id) orelse return error.NoStandardHandleAttached; if (handle == windows.INVALID_HANDLE_VALUE) { switch (windows.GetLastError()) { - else => |err| unexpectedErrorWindows(err), + else => |err| windows.unexpectedError(err), } } return handle; @@ -2070,17 +1962,12 @@ pub const ForkError = error{ }; pub fn fork() ForkError!pid_t { - if (builtin.link_libc) { - return system.fork(); - } - if (linux.is_the_target) { - const rc = if (@hasDecl(system, "SYS_fork")) system.fork() else system.clone2(SIGCHLD, 0); - switch (errno(rc)) { - 0 => return rc, - EAGAIN => return error.SystemResources, - ENOMEM => return error.SystemResources, - else => |err| return unexpectedErrno(err), - } + const rc = system.fork(); + switch (errno(rc)) { + 0 => return rc, + EAGAIN => return error.SystemResources, + ENOMEM => return error.SystemResources, + else => |err| return unexpectedErrno(err), } } @@ -2173,7 +2060,7 @@ pub fn accessW(path: [*]const u16, mode: u32) AccessError!void { windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, windows.ERROR.ACCESS_DENIED => return error.PermissionDenied, - else => |err| return unexpectedErrorWindows(err), + else => |err| return windows.unexpectedError(err), } } @@ -2209,11 +2096,7 @@ pub const PipeError = error{ /// Creates a unidirectional data channel that can be used for interprocess communication. pub fn pipe(fds: *[2]fd_t) PipeError!void { - const rc = if (builtin.link_libc or @hasDecl(system, SYS_pipe)) - system.pipe(fds) - else - system.pipe2(fds, 0); - switch (errno(rc)) { + switch (errno(system.pipe(fds))) { 0 => return, EINVAL => unreachable, // Invalid parameters to pipe() EFAULT => unreachable, // Invalid fds pointer @@ -2296,6 +2179,112 @@ pub const realpath = std.os.path.real; pub const realpathC = std.os.path.realC; pub const realpathW = std.os.path.realW; +pub const WaitForSingleObjectError = error{ + WaitAbandoned, + WaitTimeOut, + Unexpected, +}; + +pub fn WaitForSingleObject(handle: windows.HANDLE, milliseconds: windows.DWORD) WaitForSingleObjectError!void { + switch (windows.WaitForSingleObject(handle, milliseconds)) { + windows.WAIT_ABANDONED => return error.WaitAbandoned, + windows.WAIT_OBJECT_0 => return, + windows.WAIT_TIMEOUT => return error.WaitTimeOut, + windows.WAIT_FAILED => { + switch (windows.GetLastError()) { + else => |err| return windows.unexpectedError(err), + } + }, + else => return error.Unexpected, + } +} + +pub fn FindFirstFile( + dir_path: []const u8, + find_file_data: *windows.WIN32_FIND_DATAW, +) !windows.HANDLE { + const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{ '\\', '*', 0 }); + const handle = windows.FindFirstFileW(&dir_path_w, find_file_data); + + if (handle == windows.INVALID_HANDLE_VALUE) { + switch (windows.GetLastError()) { + windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, + windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, + else => |err| return windows.unexpectedError(err), + } + } + + return handle; +} + +/// Returns `true` if there was another file, `false` otherwise. +pub fn FindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAW) !bool { + if (windows.FindNextFileW(handle, find_file_data) == 0) { + switch (windows.GetLastError()) { + windows.ERROR.NO_MORE_FILES => return false, + else => |err| return windows.unexpectedError(err), + } + } + return true; +} + +pub const CreateIoCompletionPortError = error{Unexpected}; + +pub fn CreateIoCompletionPort( + file_handle: windows.HANDLE, + existing_completion_port: ?windows.HANDLE, + completion_key: usize, + concurrent_thread_count: windows.DWORD, +) CreateIoCompletionPortError!windows.HANDLE { + const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse { + switch (windows.GetLastError()) { + windows.ERROR.INVALID_PARAMETER => unreachable, + else => |err| return windows.unexpectedError(err), + } + }; + return handle; +} + +pub const WindowsPostQueuedCompletionStatusError = error{Unexpected}; + +pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: windows.DWORD, completion_key: usize, lpOverlapped: ?*windows.OVERLAPPED) WindowsPostQueuedCompletionStatusError!void { + if (windows.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) { + const err = windows.GetLastError(); + switch (err) { + else => return windows.unexpectedError(err), + } + } +} + +pub const GetQueuedCompletionStatusResult = enum { + Normal, + Aborted, + Cancelled, + EOF, +}; + +pub fn GetQueuedCompletionStatus( + completion_port: windows.HANDLE, + bytes_transferred_count: *windows.DWORD, + lpCompletionKey: *usize, + lpOverlapped: *?*windows.OVERLAPPED, + dwMilliseconds: windows.DWORD, +) GetQueuedCompletionStatusResult { + if (windows.GetQueuedCompletionStatus(completion_port, bytes_transferred_count, lpCompletionKey, lpOverlapped, dwMilliseconds) == windows.FALSE) { + switch (windows.GetLastError()) { + windows.ERROR.ABANDONED_WAIT_0 => return GetQueuedCompletionStatusResult.Aborted, + windows.ERROR.OPERATION_ABORTED => return GetQueuedCompletionStatusResult.Cancelled, + windows.ERROR.HANDLE_EOF => return GetQueuedCompletionStatusResult.EOF, + else => |err| { + if (std.debug.runtime_safety) { + std.debug.panic("unexpected error: {}\n", err); + } + }, + } + } + return GetQueuedCompletionStatusResult.Normal; +} + /// Used to convert a slice to a null terminated slice on the stack. /// TODO https://github.com/ziglang/zig/issues/287 pub fn toPosixPath(file_path: []const u8) ![PATH_MAX]u8 { @@ -2307,64 +2296,12 @@ pub fn toPosixPath(file_path: []const u8) ![PATH_MAX]u8 { return path_with_null; } -const unexpected_error_tracing = builtin.mode == .Debug; -const UnexpectedError = error{ - /// The Operating System returned an undocumented error code. - Unexpected, -}; - /// Call this when you made a syscall or something that sets errno /// and you get an unexpected error. -pub fn unexpectedErrno(errno: usize) UnexpectedError { - if (unexpected_error_tracing) { +pub fn unexpectedErrno(errno: usize) os.UnexpectedError { + if (os.unexpected_error_tracing) { std.debug.warn("unexpected errno: {}\n", errno); std.debug.dumpCurrentStackTrace(null); } return error.Unexpected; } - -/// Call this when you made a windows DLL call or something that does SetLastError -/// and you get an unexpected error. -pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError { - if (unexpected_error_tracing) { - std.debug.warn("unexpected GetLastError(): {}\n", err); - std.debug.dumpCurrentStackTrace(null); - } - return error.Unexpected; -} - -pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 { - return sliceToPrefixedFileW(mem.toSliceConst(u8, s)); -} - -pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { - return sliceToPrefixedSuffixedFileW(s, []u16{0}); -} - -pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 { - // TODO well defined copy elision - var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined; - - // > File I/O functions in the Windows API convert "/" to "\" as part of - // > converting the name to an NT-style name, except when using the "\\?\" - // > prefix as detailed in the following sections. - // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation - // Because we want the larger maximum path length for absolute paths, we - // disallow forward slashes in zig std lib file functions on Windows. - for (s) |byte| { - switch (byte) { - '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName, - else => {}, - } - } - const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: { - const prefix = []u16{ '\\', '\\', '?', '\\' }; - mem.copy(u16, result[0..], prefix); - break :blk prefix.len; - }; - const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s); - assert(end_index <= result.len); - if (end_index + suffix.len > result.len) return error.NameTooLong; - mem.copy(u16, result[end_index..], suffix); - return result; -} diff --git a/std/os/windows.zig b/std/os/windows.zig index f66b4385b6..c8ee63f338 100644 --- a/std/os/windows.zig +++ b/std/os/windows.zig @@ -1,28 +1,30 @@ +// This file contains the types and constants of the Windows API, +// as well as the Windows-equivalent of "Zig-flavored POSIX" API layer. const std = @import("../std.zig"); const assert = std.debug.assert; const maxInt = std.math.maxInt; -pub const is_the_target = switch (builtin.os) { - .windows => true, - else => false, -}; -pub const posix = @import("windows/posix.zig"); +pub const is_the_target = builtin.os == .windows; +pub const posix = if (builtin.link_libc) struct {} else @import("windows/posix.zig"); pub use posix; -pub use @import("windows/advapi32.zig"); -pub use @import("windows/kernel32.zig"); -pub use @import("windows/ntdll.zig"); -pub use @import("windows/ole32.zig"); -pub use @import("windows/shell32.zig"); - -test "import" { - if (is_the_target) { - _ = @import("windows/util.zig"); - } -} +pub const advapi32 = @import("windows/advapi32.zig"); +pub const kernel32 = @import("windows/kernel32.zig"); +pub const ntdll = @import("windows/ntdll.zig"); +pub const ole32 = @import("windows/ole32.zig"); +pub const shell32 = @import("windows/shell32.zig"); pub const ERROR = @import("windows/error.zig"); +/// The standard input device. Initially, this is the console input buffer, CONIN$. +pub const STD_INPUT_HANDLE = maxInt(DWORD) - 10 + 1; + +/// The standard output device. Initially, this is the active console screen buffer, CONOUT$. +pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1; + +/// The standard error device. Initially, this is the active console screen buffer, CONOUT$. +pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1; + pub const SHORT = c_short; pub const BOOL = c_int; pub const BOOLEAN = BYTE; @@ -428,3 +430,233 @@ pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY; pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY; pub const PIMAGE_TLS_CALLBACK = ?extern fn (PVOID, DWORD, PVOID) void; + +pub const PROV_RSA_FULL = 1; + +pub const REGSAM = ACCESS_MASK; +pub const ACCESS_MASK = DWORD; +pub const PHKEY = *HKEY; +pub const HKEY = *HKEY__; +pub const HKEY__ = extern struct { + unused: c_int, +}; +pub const LSTATUS = LONG; + +pub const FILE_NOTIFY_INFORMATION = extern struct { + NextEntryOffset: DWORD, + Action: DWORD, + FileNameLength: DWORD, + FileName: [1]WCHAR, +}; + +pub const FILE_ACTION_ADDED = 0x00000001; +pub const FILE_ACTION_REMOVED = 0x00000002; +pub const FILE_ACTION_MODIFIED = 0x00000003; +pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004; +pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005; + +pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?extern fn (DWORD, DWORD, *OVERLAPPED) void; + +pub const FILE_LIST_DIRECTORY = 1; + +pub const FILE_NOTIFY_CHANGE_CREATION = 64; +pub const FILE_NOTIFY_CHANGE_SIZE = 8; +pub const FILE_NOTIFY_CHANGE_SECURITY = 256; +pub const FILE_NOTIFY_CHANGE_LAST_ACCESS = 32; +pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16; +pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2; +pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1; +pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4; + +pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct { + dwSize: COORD, + dwCursorPosition: COORD, + wAttributes: WORD, + srWindow: SMALL_RECT, + dwMaximumWindowSize: COORD, +}; + +pub const FOREGROUND_BLUE = 1; +pub const FOREGROUND_GREEN = 2; +pub const FOREGROUND_RED = 4; +pub const FOREGROUND_INTENSITY = 8; + +pub const LIST_ENTRY = extern struct { + Flink: *LIST_ENTRY, + Blink: *LIST_ENTRY, +}; + +pub const RTL_CRITICAL_SECTION_DEBUG = extern struct { + Type: WORD, + CreatorBackTraceIndex: WORD, + CriticalSection: *RTL_CRITICAL_SECTION, + ProcessLocksList: LIST_ENTRY, + EntryCount: DWORD, + ContentionCount: DWORD, + Flags: DWORD, + CreatorBackTraceIndexHigh: WORD, + SpareWORD: WORD, +}; + +pub const RTL_CRITICAL_SECTION = extern struct { + DebugInfo: *RTL_CRITICAL_SECTION_DEBUG, + LockCount: LONG, + RecursionCount: LONG, + OwningThread: HANDLE, + LockSemaphore: HANDLE, + SpinCount: ULONG_PTR, +}; + +pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION; +pub const INIT_ONCE = RTL_RUN_ONCE; +pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT; +pub const INIT_ONCE_FN = extern fn (InitOnce: *INIT_ONCE, Parameter: ?*c_void, Context: ?*c_void) BOOL; + +pub const RTL_RUN_ONCE = extern struct { + Ptr: ?*c_void, +}; + +pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null }; + +pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED; +pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED; +pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE; +pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY; +pub const COINIT = extern enum { + COINIT_APARTMENTTHREADED = 2, + COINIT_MULTITHREADED = 0, + COINIT_DISABLE_OLE1DDE = 4, + COINIT_SPEED_OVER_MEMORY = 8, +}; + +/// > The maximum path of 32,767 characters is approximate, because the "\\?\" +/// > prefix may be expanded to a longer string by the system at run time, and +/// > this expansion applies to the total length. +/// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation +pub const PATH_MAX_WIDE = 32767; + +pub const OpenError = error{ + SharingViolation, + PathAlreadyExists, + + /// When any of the path components can not be found or the file component can not + /// be found. Some operating systems distinguish between path components not found and + /// file components not found, but they are collapsed into FileNotFound to gain + /// consistency across operating systems. + FileNotFound, + + AccessDenied, + PipeBusy, + NameTooLong, + + /// On Windows, file paths must be valid Unicode. + InvalidUtf8, + + /// On Windows, file paths cannot contain these characters: + /// '/', '*', '?', '"', '<', '>', '|' + BadPathName, + + Unexpected, +}; + +pub fn CreateFile( + file_path: []const u8, + desired_access: DWORD, + share_mode: DWORD, + creation_disposition: DWORD, + flags_and_attrs: DWORD, +) OpenError!fd_t { + const file_path_w = try sliceToPrefixedFileW(file_path); + return CreateFileW(&file_path_w, desired_access, share_mode, creation_disposition, flags_and_attrs); +} + +pub fn CreateFileW( + file_path_w: [*]const u16, + desired_access: DWORD, + share_mode: DWORD, + creation_disposition: DWORD, + flags_and_attrs: DWORD, +) OpenError!HANDLE { + const result = kernel32.CreateFileW(file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null); + + if (result == INVALID_HANDLE_VALUE) { + switch (kernel32.GetLastError()) { + ERROR.SHARING_VIOLATION => return error.SharingViolation, + ERROR.ALREADY_EXISTS => return error.PathAlreadyExists, + ERROR.FILE_EXISTS => return error.PathAlreadyExists, + ERROR.FILE_NOT_FOUND => return error.FileNotFound, + ERROR.PATH_NOT_FOUND => return error.FileNotFound, + ERROR.ACCESS_DENIED => return error.AccessDenied, + ERROR.PIPE_BUSY => return error.PipeBusy, + else => |err| return unexpectedErrorWindows(err), + } + } + + return result; +} + +pub const CreatePipeError = error{Unexpected}; + +fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void { + if (kernel32.CreatePipe(rd, wr, sattr, 0) == 0) { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + } +} + +pub const SetHandleInformationError = error{Unexpected}; + +fn SetHandleInformation(h: HANDLE, mask: DWORD, flags: DWORD) SetHandleInformationError!void { + if (SetHandleInformation(h, mask, flags) == 0) { + switch (kernel32.GetLastError()) { + else => |err| return unexpectedError(err), + } + } +} + +pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 { + return sliceToPrefixedFileW(mem.toSliceConst(u8, s)); +} + +pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { + return sliceToPrefixedSuffixedFileW(s, []u16{0}); +} + +pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 { + // TODO well defined copy elision + var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined; + + // > File I/O functions in the Windows API convert "/" to "\" as part of + // > converting the name to an NT-style name, except when using the "\\?\" + // > prefix as detailed in the following sections. + // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation + // Because we want the larger maximum path length for absolute paths, we + // disallow forward slashes in zig std lib file functions on Windows. + for (s) |byte| { + switch (byte) { + '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName, + else => {}, + } + } + const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: { + const prefix = []u16{ '\\', '\\', '?', '\\' }; + mem.copy(u16, result[0..], prefix); + break :blk prefix.len; + }; + const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s); + assert(end_index <= result.len); + if (end_index + suffix.len > result.len) return error.NameTooLong; + mem.copy(u16, result[end_index..], suffix); + return result; +} + +/// Call this when you made a windows DLL call or something that does SetLastError +/// and you get an unexpected error. +pub fn unexpectedError(err: DWORD) std.os.UnexpectedError { + if (std.os.unexpected_error_tracing) { + std.debug.warn("unexpected GetLastError(): {}\n", err); + std.debug.dumpCurrentStackTrace(null); + } + return error.Unexpected; +} diff --git a/std/os/windows/advapi32.zig b/std/os/windows/advapi32.zig index 9672767500..6acb8a2c88 100644 --- a/std/os/windows/advapi32.zig +++ b/std/os/windows/advapi32.zig @@ -1,16 +1,5 @@ use @import("../windows.zig"); -pub const PROV_RSA_FULL = 1; - -pub const REGSAM = ACCESS_MASK; -pub const ACCESS_MASK = DWORD; -pub const PHKEY = *HKEY; -pub const HKEY = *HKEY__; -pub const HKEY__ = extern struct { - unused: c_int, -}; -pub const LSTATUS = LONG; - pub extern "advapi32" stdcallcc fn RegOpenKeyExW( hKey: HKEY, lpSubKey: LPCWSTR, diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 7e99f7eda9..b14e404c13 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -189,86 +189,9 @@ pub extern "kernel32" stdcallcc fn GetProcAddress(hModule: HMODULE, lpProcName: pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; -pub const FILE_NOTIFY_INFORMATION = extern struct { - NextEntryOffset: DWORD, - Action: DWORD, - FileNameLength: DWORD, - FileName: [1]WCHAR, -}; - -pub const FILE_ACTION_ADDED = 0x00000001; -pub const FILE_ACTION_REMOVED = 0x00000002; -pub const FILE_ACTION_MODIFIED = 0x00000003; -pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004; -pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005; - -pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?extern fn (DWORD, DWORD, *OVERLAPPED) void; - -pub const FILE_LIST_DIRECTORY = 1; - -pub const FILE_NOTIFY_CHANGE_CREATION = 64; -pub const FILE_NOTIFY_CHANGE_SIZE = 8; -pub const FILE_NOTIFY_CHANGE_SECURITY = 256; -pub const FILE_NOTIFY_CHANGE_LAST_ACCESS = 32; -pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16; -pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2; -pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1; -pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4; - -pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct { - dwSize: COORD, - dwCursorPosition: COORD, - wAttributes: WORD, - srWindow: SMALL_RECT, - dwMaximumWindowSize: COORD, -}; - -pub const FOREGROUND_BLUE = 1; -pub const FOREGROUND_GREEN = 2; -pub const FOREGROUND_RED = 4; -pub const FOREGROUND_INTENSITY = 8; - pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; -pub const LIST_ENTRY = extern struct { - Flink: *LIST_ENTRY, - Blink: *LIST_ENTRY, -}; - -pub const RTL_CRITICAL_SECTION_DEBUG = extern struct { - Type: WORD, - CreatorBackTraceIndex: WORD, - CriticalSection: *RTL_CRITICAL_SECTION, - ProcessLocksList: LIST_ENTRY, - EntryCount: DWORD, - ContentionCount: DWORD, - Flags: DWORD, - CreatorBackTraceIndexHigh: WORD, - SpareWORD: WORD, -}; - -pub const RTL_CRITICAL_SECTION = extern struct { - DebugInfo: *RTL_CRITICAL_SECTION_DEBUG, - LockCount: LONG, - RecursionCount: LONG, - OwningThread: HANDLE, - LockSemaphore: HANDLE, - SpinCount: ULONG_PTR, -}; - -pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION; -pub const INIT_ONCE = RTL_RUN_ONCE; -pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT; - pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) BOOL; - -pub const INIT_ONCE_FN = extern fn (InitOnce: *INIT_ONCE, Parameter: ?*c_void, Context: ?*c_void) BOOL; - -pub const RTL_RUN_ONCE = extern struct { - Ptr: ?*c_void, -}; - -pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null }; diff --git a/std/os/windows/ole32.zig b/std/os/windows/ole32.zig index 1ecfecba3c..a69137fcab 100644 --- a/std/os/windows/ole32.zig +++ b/std/os/windows/ole32.zig @@ -4,14 +4,3 @@ pub extern "ole32" stdcallcc fn CoTaskMemFree(pv: LPVOID) void; pub extern "ole32" stdcallcc fn CoUninitialize() void; pub extern "ole32" stdcallcc fn CoGetCurrentProcess() DWORD; pub extern "ole32" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT; - -pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED; -pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED; -pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE; -pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY; -pub const COINIT = extern enum { - COINIT_APARTMENTTHREADED = 2, - COINIT_MULTITHREADED = 0, - COINIT_DISABLE_OLE1DDE = 4, - COINIT_SPEED_OVER_MEMORY = 8, -}; diff --git a/std/os/windows/posix.zig b/std/os/windows/posix.zig index 7ac187648d..0bec450bd8 100644 --- a/std/os/windows/posix.zig +++ b/std/os/windows/posix.zig @@ -1,15 +1,6 @@ -// Declarations that are intended to be imported into the POSIX namespace. +// Declarations that are intended to be imported into the POSIX namespace, +// when not linking libc. const std = @import("../../std.zig"); -const maxInt = std.math.maxInt; -use std.os.windows; +const builtin = @import("builtin"); -pub const fd_t = HANDLE; - -/// The standard input device. Initially, this is the console input buffer, CONIN$. -pub const STD_INPUT_HANDLE = maxInt(DWORD) - 10 + 1; - -/// The standard output device. Initially, this is the active console screen buffer, CONOUT$. -pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1; - -/// The standard error device. Initially, this is the active console screen buffer, CONOUT$. -pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1; +pub const fd_t = std.os.windows.HANDLE; diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig deleted file mode 100644 index 7344c04318..0000000000 --- a/std/os/windows/util.zig +++ /dev/null @@ -1,147 +0,0 @@ -const std = @import("../../std.zig"); -const builtin = @import("builtin"); -const os = std.os; -const unicode = std.unicode; -const windows = std.os.windows; -const assert = std.debug.assert; -const mem = std.mem; -const BufMap = std.BufMap; -const cstr = std.cstr; - -pub const WaitError = error{ - WaitAbandoned, - WaitTimeOut, - Unexpected, -}; - -pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) WaitError!void { - const result = windows.WaitForSingleObject(handle, milliseconds); - return switch (result) { - windows.WAIT_ABANDONED => error.WaitAbandoned, - windows.WAIT_OBJECT_0 => {}, - windows.WAIT_TIMEOUT => error.WaitTimeOut, - windows.WAIT_FAILED => x: { - const err = windows.GetLastError(); - break :x switch (err) { - else => os.unexpectedErrorWindows(err), - }; - }, - else => error.Unexpected, - }; -} - -/// Caller must free result. -pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 { - // count bytes needed - const max_chars_needed = x: { - var max_chars_needed: usize = 4; // 4 for the final 4 null bytes - var it = env_map.iterator(); - while (it.next()) |pair| { - // +1 for '=' - // +1 for null byte - max_chars_needed += pair.key.len + pair.value.len + 2; - } - break :x max_chars_needed; - }; - const result = try allocator.alloc(u16, max_chars_needed); - errdefer allocator.free(result); - - var it = env_map.iterator(); - var i: usize = 0; - while (it.next()) |pair| { - i += try unicode.utf8ToUtf16Le(result[i..], pair.key); - result[i] = '='; - i += 1; - i += try unicode.utf8ToUtf16Le(result[i..], pair.value); - result[i] = 0; - i += 1; - } - result[i] = 0; - i += 1; - result[i] = 0; - i += 1; - result[i] = 0; - i += 1; - result[i] = 0; - i += 1; - return allocator.shrink(result, i); -} - -pub fn windowsFindFirstFile( - dir_path: []const u8, - find_file_data: *windows.WIN32_FIND_DATAW, -) !windows.HANDLE { - const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{ '\\', '*', 0 }); - const handle = windows.FindFirstFileW(&dir_path_w, find_file_data); - - if (handle == windows.INVALID_HANDLE_VALUE) { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, - windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, - else => return os.unexpectedErrorWindows(err), - } - } - - return handle; -} - -/// Returns `true` if there was another file, `false` otherwise. -pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAW) !bool { - if (windows.FindNextFileW(handle, find_file_data) == 0) { - const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.NO_MORE_FILES => false, - else => os.unexpectedErrorWindows(err), - }; - } - return true; -} - -pub const WindowsCreateIoCompletionPortError = error{Unexpected}; - -pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_completion_port: ?windows.HANDLE, completion_key: usize, concurrent_thread_count: windows.DWORD) !windows.HANDLE { - const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.INVALID_PARAMETER => unreachable, - else => return os.unexpectedErrorWindows(err), - } - }; - return handle; -} - -pub const WindowsPostQueuedCompletionStatusError = error{Unexpected}; - -pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: windows.DWORD, completion_key: usize, lpOverlapped: ?*windows.OVERLAPPED) WindowsPostQueuedCompletionStatusError!void { - if (windows.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) { - const err = windows.GetLastError(); - switch (err) { - else => return os.unexpectedErrorWindows(err), - } - } -} - -pub const WindowsWaitResult = enum { - Normal, - Aborted, - Cancelled, - EOF, -}; - -pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: *windows.DWORD, lpCompletionKey: *usize, lpOverlapped: *?*windows.OVERLAPPED, dwMilliseconds: windows.DWORD) WindowsWaitResult { - if (windows.GetQueuedCompletionStatus(completion_port, bytes_transferred_count, lpCompletionKey, lpOverlapped, dwMilliseconds) == windows.FALSE) { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.ABANDONED_WAIT_0 => return WindowsWaitResult.Aborted, - windows.ERROR.OPERATION_ABORTED => return WindowsWaitResult.Cancelled, - windows.ERROR.HANDLE_EOF => return WindowsWaitResult.EOF, - else => { - if (std.debug.runtime_safety) { - std.debug.panic("unexpected error: {}\n", err); - } - }, - } - } - return WindowsWaitResult.Normal; -}