From 7680c5330cbc9141b9a5444e30c512b6068ab50d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 8 Feb 2024 01:39:39 -0700 Subject: [PATCH] some API work on std.c, std.os, std.os.wasi * std.c: consolidate some definitions, making them share code. For example, freebsd, dragonfly, and openbsd can all share the same `pthread_mutex_t` definition. * add type safety to std.c.O - this caught a bug where mode flags were incorrectly passed as the open flags. * 3 fewer uses of usingnamespace keyword * as per convention, remove purposeless field prefixes from struct field names even if they have those prefixes in the corresponding C code. * fix incorrect wasi libc Stat definition * remove C definitions from incorrectly being in std.os.wasi * make std.os.wasi definitions type safe * go through wasi native APIs even when linking libc because the libc APIs are problematic and wasteful * don't expose WASI definitions in std.posix * remove std.os.wasi.rights_t.ALL: this is a footgun. should it be all future rights too? or only all current rights known? both are the wrong answer. --- lib/std/c.zig | 605 ++++++++++++++++++++-- lib/std/c/darwin.zig | 108 +--- lib/std/c/dragonfly.zig | 62 +-- lib/std/c/emscripten.zig | 25 +- lib/std/c/freebsd.zig | 79 +-- lib/std/c/fuchsia.zig | 11 - lib/std/c/haiku.zig | 66 +-- lib/std/c/hermit.zig | 12 - lib/std/c/linux.zig | 60 +-- lib/std/c/minix.zig | 18 - lib/std/c/netbsd.zig | 114 +--- lib/std/c/openbsd.zig | 83 +-- lib/std/c/solaris.zig | 78 +-- lib/std/c/wasi.zig | 135 +++-- lib/std/c/windows.zig | 33 -- lib/std/child_process.zig | 6 +- lib/std/compress/deflate/deflate_fast.zig | 8 + lib/std/dynamic_library.zig | 2 +- lib/std/fs/Dir.zig | 463 +++++++++-------- lib/std/fs/File.zig | 142 +++-- lib/std/fs/test.zig | 7 +- lib/std/os.zig | 434 ++++++++-------- lib/std/os/emscripten.zig | 41 -- lib/std/os/linux.zig | 263 ++++++++-- lib/std/os/linux/arm-eabi.zig | 23 - lib/std/os/linux/arm64.zig | 23 - lib/std/os/linux/io_uring.zig | 26 +- lib/std/os/linux/mips.zig | 23 - lib/std/os/linux/mips64.zig | 23 - lib/std/os/linux/powerpc.zig | 23 - lib/std/os/linux/powerpc64.zig | 23 - lib/std/os/linux/riscv64.zig | 23 - lib/std/os/linux/sparc64.zig | 23 - lib/std/os/linux/test.zig | 4 +- lib/std/os/linux/x86.zig | 23 - lib/std/os/linux/x86_64.zig | 23 - lib/std/os/plan9.zig | 28 +- lib/std/os/test.zig | 74 ++- lib/std/os/wasi.zig | 387 +++++--------- lib/std/std.zig | 5 +- lib/std/time.zig | 127 +++-- 41 files changed, 1758 insertions(+), 1978 deletions(-) delete mode 100644 lib/std/c/fuchsia.zig delete mode 100644 lib/std/c/hermit.zig delete mode 100644 lib/std/c/minix.zig diff --git a/lib/std/c.zig b/lib/std/c.zig index e03a16314f..522f0beb9e 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -4,6 +4,10 @@ const c = @This(); const page_size = std.mem.page_size; const iovec = std.os.iovec; const iovec_const = std.os.iovec_const; +const wasi = @import("c/wasi.zig"); +const native_abi = builtin.abi; +const native_arch = builtin.cpu.arch; +const native_os = builtin.os.tag; /// If not linking libc, returns false. /// If linking musl libc, returns true. @@ -13,7 +17,7 @@ const iovec_const = std.os.iovec_const; pub inline fn versionCheck(comptime glibc_version: std.SemanticVersion) bool { return comptime blk: { if (!builtin.link_libc) break :blk false; - if (builtin.abi.isMusl()) break :blk true; + if (native_abi.isMusl()) break :blk true; if (builtin.target.isGnuLibC()) { const ver = builtin.os.version_range.linux.glibc; const order = ver.order(glibc_version); @@ -27,7 +31,7 @@ pub inline fn versionCheck(comptime glibc_version: std.SemanticVersion) bool { }; } -pub usingnamespace switch (builtin.os.tag) { +pub usingnamespace switch (native_os) { .linux => @import("c/linux.zig"), .windows => @import("c/windows.zig"), .macos, .ios, .tvos, .watchos => @import("c/darwin.zig"), @@ -36,16 +40,504 @@ pub usingnamespace switch (builtin.os.tag) { .dragonfly => @import("c/dragonfly.zig"), .openbsd => @import("c/openbsd.zig"), .haiku => @import("c/haiku.zig"), - .hermit => @import("c/hermit.zig"), .solaris, .illumos => @import("c/solaris.zig"), - .fuchsia => @import("c/fuchsia.zig"), - .minix => @import("c/minix.zig"), .emscripten => @import("c/emscripten.zig"), - .wasi => @import("c/wasi.zig"), + .wasi => wasi, else => struct {}, }; -pub const MAP = switch (builtin.os.tag) { +pub const pthread_mutex_t = switch (native_os) { + .linux, .minix => extern struct { + data: [data_len]u8 align(@alignOf(usize)) = [_]u8{0} ** data_len, + + const data_len = switch (native_abi) { + .musl, .musleabi, .musleabihf => if (@sizeOf(usize) == 8) 40 else 24, + .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (native_arch) { + .aarch64 => 48, + .x86_64 => if (native_abi == .gnux32) 40 else 32, + .mips64, .powerpc64, .powerpc64le, .sparc64 => 40, + else => if (@sizeOf(usize) == 8) 40 else 24, + }, + .android => if (@sizeOf(usize) == 8) 40 else 4, + else => @compileError("unsupported ABI"), + }; + }, + .macos, .ios, .tvos, .watchos => extern struct { + sig: c_long = 0x32AAABA7, + data: [data_len]u8 = [_]u8{0} ** data_len, + + const data_len = if (@sizeOf(usize) == 8) 56 else 40; + }, + .freebsd, .kfreebsd, .dragonfly, .openbsd => extern struct { + inner: ?*anyopaque = null, + }, + .hermit => extern struct { + ptr: usize = std.math.maxInt(usize), + }, + .netbsd => extern struct { + magic: u32 = 0x33330003, + errorcheck: c.padded_pthread_spin_t = 0, + ceiling: c.padded_pthread_spin_t = 0, + owner: usize = 0, + waiters: ?*u8 = null, + recursed: u32 = 0, + spare2: ?*anyopaque = null, + }, + .haiku => extern struct { + flags: u32 = 0, + lock: i32 = 0, + unused: i32 = -42, + owner: i32 = -1, + owner_count: i32 = 0, + }, + .solaris, .illumos => extern struct { + flag1: u16 = 0, + flag2: u8 = 0, + ceiling: u8 = 0, + type: u16 = 0, + magic: u16 = 0x4d58, + lock: u64 = 0, + data: u64 = 0, + }, + .fuchsia => extern struct { + data: [40]u8 align(@alignOf(usize)) = [_]u8{0} ** 40, + }, + .emscripten => extern struct { + data: [24]u8 align(4) = [_]u8{0} ** 24, + }, + else => @compileError("target libc does not have pthread_mutex_t"), +}; + +pub const pthread_cond_t = switch (native_os) { + .linux => extern struct { + data: [48]u8 align(@alignOf(usize)) = [_]u8{0} ** 48, + }, + .macos, .ios, .tvos, .watchos => extern struct { + sig: c_long = 0x3CB0B1BB, + data: [data_len]u8 = [_]u8{0} ** data_len, + const data_len = if (@sizeOf(usize) == 8) 40 else 24; + }, + .freebsd, .kfreebsd, .dragonfly, .openbsd => extern struct { + inner: ?*anyopaque = null, + }, + .hermit => extern struct { + ptr: usize = std.math.maxInt(usize), + }, + .netbsd => extern struct { + magic: u32 = 0x55550005, + lock: c.pthread_spin_t = 0, + waiters_first: ?*u8 = null, + waiters_last: ?*u8 = null, + mutex: ?*pthread_mutex_t = null, + private: ?*anyopaque = null, + }, + .haiku => extern struct { + flags: u32 = 0, + unused: i32 = -42, + mutex: ?*anyopaque = null, + waiter_count: i32 = 0, + lock: i32 = 0, + }, + .solaris, .illumos => extern struct { + flag: [4]u8 = [_]u8{0} ** 4, + type: u16 = 0, + magic: u16 = 0x4356, + data: u64 = 0, + }, + .fuchsia, .minix, .emscripten => extern struct { + data: [48]u8 align(@alignOf(usize)) = [_]u8{0} ** 48, + }, + else => @compileError("target libc does not have pthread_cond_t"), +}; + +pub const pthread_rwlock_t = switch (native_os) { + .linux => switch (native_abi) { + .android => switch (@sizeOf(usize)) { + 4 => extern struct { + data: [40]u8 align(@alignOf(usize)) = [_]u8{0} ** 40, + }, + 8 => extern struct { + data: [56]u8 align(@alignOf(usize)) = [_]u8{0} ** 56, + }, + else => @compileError("impossible pointer size"), + }, + else => extern struct { + data: [56]u8 align(@alignOf(usize)) = [_]u8{0} ** 56, + }, + }, + .macos, .ios, .tvos, .watchos => extern struct { + sig: c_long = 0x2DA8B3B4, + data: [192]u8 = [_]u8{0} ** 192, + }, + .freebsd, .kfreebsd, .dragonfly, .openbsd => extern struct { + ptr: ?*anyopaque = null, + }, + .hermit => extern struct { + ptr: usize = std.math.maxInt(usize), + }, + .netbsd => extern struct { + magic: c_uint = 0x99990009, + interlock: switch (builtin.cpu.arch) { + .aarch64, .sparc, .x86_64, .x86 => u8, + .arm, .powerpc => c_int, + else => unreachable, + } = 0, + rblocked_first: ?*u8 = null, + rblocked_last: ?*u8 = null, + wblocked_first: ?*u8 = null, + wblocked_last: ?*u8 = null, + nreaders: c_uint = 0, + owner: ?pthread_t = null, + private: ?*anyopaque = null, + }, + .solaris, .illumos => extern struct { + readers: i32 = 0, + type: u16 = 0, + magic: u16 = 0x5257, + mutex: pthread_mutex_t = .{}, + readercv: pthread_cond_t = .{}, + writercv: pthread_cond_t = .{}, + }, + .fuchsia => extern struct { + size: [56]u8 align(@alignOf(usize)) = [_]u8{0} ** 56, + }, + .emscripten => extern struct { + size: [32]u8 align(4) = [_]u8{0} ** 32, + }, + else => @compileError("target libc does not have pthread_rwlock_t"), +}; + +pub const AT = switch (native_os) { + .linux => std.os.linux.AT, + .windows => struct { + /// Remove directory instead of unlinking file + pub const REMOVEDIR = 0x200; + }, + .macos, .ios, .tvos, .watchos => struct { + pub const FDCWD = -2; + /// Use effective ids in access check + pub const EACCESS = 0x0010; + /// Act on the symlink itself not the target + pub const SYMLINK_NOFOLLOW = 0x0020; + /// Act on target of symlink + pub const SYMLINK_FOLLOW = 0x0040; + /// Path refers to directory + pub const REMOVEDIR = 0x0080; + }, + .freebsd, .kfreebsd => struct { + /// Magic value that specify the use of the current working directory + /// to determine the target of relative file paths in the openat() and + /// similar syscalls. + pub const FDCWD = -100; + /// Check access using effective user and group ID + pub const EACCESS = 0x0100; + /// Do not follow symbolic links + pub const SYMLINK_NOFOLLOW = 0x0200; + /// Follow symbolic link + pub const SYMLINK_FOLLOW = 0x0400; + /// Remove directory instead of file + pub const REMOVEDIR = 0x0800; + /// Fail if not under dirfd + pub const BENEATH = 0x1000; + }, + .netbsd => struct { + /// Magic value that specify the use of the current working directory + /// to determine the target of relative file paths in the openat() and + /// similar syscalls. + pub const FDCWD = -100; + /// Check access using effective user and group ID + pub const EACCESS = 0x0100; + /// Do not follow symbolic links + pub const SYMLINK_NOFOLLOW = 0x0200; + /// Follow symbolic link + pub const SYMLINK_FOLLOW = 0x0400; + /// Remove directory instead of file + pub const REMOVEDIR = 0x0800; + }, + .dragonfly => struct { + pub const FDCWD = -328243; + pub const SYMLINK_NOFOLLOW = 1; + pub const REMOVEDIR = 2; + pub const EACCESS = 4; + pub const SYMLINK_FOLLOW = 8; + }, + .openbsd => struct { + /// Magic value that specify the use of the current working directory + /// to determine the target of relative file paths in the openat() and + /// similar syscalls. + pub const FDCWD = -100; + /// Check access using effective user and group ID + pub const EACCESS = 0x01; + /// Do not follow symbolic links + pub const SYMLINK_NOFOLLOW = 0x02; + /// Follow symbolic link + pub const SYMLINK_FOLLOW = 0x04; + /// Remove directory instead of file + pub const REMOVEDIR = 0x08; + }, + .haiku => struct { + pub const FDCWD = -1; + pub const SYMLINK_NOFOLLOW = 0x01; + pub const SYMLINK_FOLLOW = 0x02; + pub const REMOVEDIR = 0x04; + pub const EACCESS = 0x08; + }, + .solaris, .illumos => struct { + /// Magic value that specify the use of the current working directory + /// to determine the target of relative file paths in the openat() and + /// similar syscalls. + pub const FDCWD: c.fd_t = @bitCast(@as(u32, 0xffd19553)); + /// Do not follow symbolic links + pub const SYMLINK_NOFOLLOW = 0x1000; + /// Follow symbolic link + pub const SYMLINK_FOLLOW = 0x2000; + /// Remove directory instead of file + pub const REMOVEDIR = 0x1; + pub const TRIGGER = 0x2; + /// Check access using effective user and group ID + pub const EACCESS = 0x4; + }, + .emscripten => struct { + pub const FDCWD = -100; + pub const SYMLINK_NOFOLLOW = 0x100; + pub const REMOVEDIR = 0x200; + pub const SYMLINK_FOLLOW = 0x400; + pub const NO_AUTOMOUNT = 0x800; + pub const EMPTY_PATH = 0x1000; + pub const STATX_SYNC_TYPE = 0x6000; + pub const STATX_SYNC_AS_STAT = 0x0000; + pub const STATX_FORCE_SYNC = 0x2000; + pub const STATX_DONT_SYNC = 0x4000; + pub const RECURSIVE = 0x8000; + }, + .wasi => struct { + pub const SYMLINK_NOFOLLOW = 0x100; + pub const SYMLINK_FOLLOW = 0x400; + pub const REMOVEDIR: u32 = 0x4; + /// When linking libc, we follow their convention and use -2 for current working directory. + /// However, without libc, Zig does a different convention: it assumes the + /// current working directory is the first preopen. This behavior can be + /// overridden with a public function called `wasi_cwd` in the root source + /// file. + pub const FDCWD: c.fd_t = if (builtin.link_libc) -2 else 3; + }, + + else => @compileError("target libc does not have AT"), +}; + +pub const O = switch (native_os) { + .linux => std.os.linux.O, + .emscripten => packed struct(u32) { + ACCMODE: std.os.ACCMODE = .RDONLY, + _2: u4 = 0, + CREAT: bool = false, + EXCL: bool = false, + NOCTTY: bool = false, + TRUNC: bool = false, + APPEND: bool = false, + NONBLOCK: bool = false, + DSYNC: bool = false, + ASYNC: bool = false, + DIRECT: bool = false, + LARGEFILE: bool = false, + DIRECTORY: bool = false, + NOFOLLOW: bool = false, + NOATIME: bool = false, + CLOEXEC: bool = false, + SYNC: bool = false, + PATH: bool = false, + TMPFILE: bool = false, + _: u9 = 0, + }, + .wasi => packed struct(u32) { + APPEND: bool = false, + DSYNC: bool = false, + NONBLOCK: bool = false, + RSYNC: bool = false, + SYNC: bool = false, + _5: u7 = 0, + CREAT: bool = false, + DIRECTORY: bool = false, + EXCL: bool = false, + TRUNC: bool = false, + _16: u8 = 0, + NOFOLLOW: bool = false, + EXEC: bool = false, + read: bool = false, + SEARCH: bool = false, + write: bool = false, + _: u3 = 0, + }, + .solaris => packed struct(u32) { + ACCMODE: std.os.ACCMODE = .RDONLY, + NDELAY: bool = false, + APPEND: bool = false, + SYNC: bool = false, + _5: u1 = 0, + DSYNC: bool = false, + NONBLOCK: bool = false, + CREAT: bool = false, + TRUNC: bool = false, + EXCL: bool = false, + NOCTTY: bool = false, + _12: u1 = 0, + LARGEFILE: bool = false, + XATTR: bool = false, + RSYNC: bool = false, + _16: u1 = 0, + NOFOLLOW: bool = false, + NOLINKS: bool = false, + _19: u2 = 0, + SEARCH: bool = false, + EXEC: bool = false, + CLOEXEC: bool = false, + DIRECTORY: bool = false, + DIRECT: bool = false, + _: u6 = 0, + }, + .netbsd => packed struct(u32) { + ACCMODE: std.os.ACCMODE = .RDONLY, + NONBLOCK: bool = false, + APPEND: bool = false, + SHLOCK: bool = false, + EXLOCK: bool = false, + ASYNC: bool = false, + SYNC: bool = false, + NOFOLLOW: bool = false, + CREAT: bool = false, + TRUNC: bool = false, + EXCL: bool = false, + _12: u3 = 0, + NOCTTY: bool = false, + DSYNC: bool = false, + RSYNC: bool = false, + ALT_IO: bool = false, + DIRECT: bool = false, + _20: u1 = 0, + DIRECTORY: bool = false, + CLOEXEC: bool = false, + SEARCH: bool = false, + _: u8 = 0, + }, + .openbsd => packed struct(u32) { + ACCMODE: std.os.ACCMODE = .RDONLY, + NONBLOCK: bool = false, + APPEND: bool = false, + SHLOCK: bool = false, + EXLOCK: bool = false, + ASYNC: bool = false, + SYNC: bool = false, + NOFOLLOW: bool = false, + CREAT: bool = false, + TRUNC: bool = false, + EXCL: bool = false, + _12: u3 = 0, + NOCTTY: bool = false, + CLOEXEC: bool = false, + DIRECTORY: bool = false, + _: u14 = 0, + }, + .haiku => packed struct(u32) { + ACCMODE: std.os.ACCMODE = .RDONLY, + _2: u4 = 0, + CLOEXEC: bool = false, + NONBLOCK: bool = false, + EXCL: bool = false, + CREAT: bool = false, + TRUNC: bool = false, + APPEND: bool = false, + NOCTTY: bool = false, + NOTRAVERSE: bool = false, + _14: u2 = 0, + SYNC: bool = false, + RSYNC: bool = false, + DSYNC: bool = false, + NOFOLLOW: bool = false, + DIRECT: bool = false, + DIRECTORY: bool = false, + _: u10 = 0, + }, + .macos, .ios, .tvos, .watchos => packed struct(u32) { + ACCMODE: std.os.ACCMODE = .RDONLY, + NONBLOCK: bool = false, + APPEND: bool = false, + SHLOCK: bool = false, + EXLOCK: bool = false, + ASYNC: bool = false, + SYNC: bool = false, + NOFOLLOW: bool = false, + CREAT: bool = false, + TRUNC: bool = false, + EXCL: bool = false, + _12: u3 = 0, + EVTONLY: bool = false, + _16: u1 = 0, + NOCTTY: bool = false, + _18: u2 = 0, + DIRECTORY: bool = false, + SYMLINK: bool = false, + DSYNC: bool = false, + _23: u1 = 0, + CLOEXEC: bool = false, + _25: u4 = 0, + ALERT: bool = false, + _30: u1 = 0, + POPUP: bool = false, + }, + .dragonfly => packed struct(u32) { + ACCMODE: std.os.ACCMODE = .RDONLY, + NONBLOCK: bool = false, + APPEND: bool = false, + SHLOCK: bool = false, + EXLOCK: bool = false, + ASYNC: bool = false, + SYNC: bool = false, + NOFOLLOW: bool = false, + CREAT: bool = false, + TRUNC: bool = false, + EXCL: bool = false, + _12: u3 = 0, + NOCTTY: bool = false, + DIRECT: bool = false, + CLOEXEC: bool = false, + FBLOCKING: bool = false, + FNONBLOCKING: bool = false, + FAPPEND: bool = false, + FOFFSET: bool = false, + FSYNCWRITE: bool = false, + FASYNCWRITE: bool = false, + _24: u3 = 0, + DIRECTORY: bool = false, + _: u4 = 0, + }, + .freebsd => packed struct(u32) { + ACCMODE: std.os.ACCMODE = .RDONLY, + NONBLOCK: bool = false, + APPEND: bool = false, + SHLOCK: bool = false, + EXLOCK: bool = false, + ASYNC: bool = false, + SYNC: bool = false, + NOFOLLOW: bool = false, + CREAT: bool = false, + TRUNC: bool = false, + EXCL: bool = false, + DSYNC: bool = false, + _13: u2 = 0, + NOCTTY: bool = false, + DIRECT: bool = false, + DIRECTORY: bool = false, + NOATIME: bool = false, + _19: u1 = 0, + CLOEXEC: bool = false, + PATH: bool = false, + TMPFILE: bool = false, + _: u9 = 0, + }, + else => @compileError("target libc does not have O"), +}; + +pub const MAP = switch (native_os) { .linux => std.os.linux.MAP, .emscripten => packed struct(u32) { TYPE: enum(u4) { @@ -187,10 +679,10 @@ pub const MAP = switch (builtin.os.tag) { /// Used by libc to communicate failure. Not actually part of the underlying syscall. pub const MAP_FAILED: *anyopaque = @ptrFromInt(std.math.maxInt(usize)); -pub const whence_t = if (builtin.os.tag == .wasi) std.os.wasi.whence_t else c_int; +pub const whence_t = if (native_os == .wasi) std.os.wasi.whence_t else c_int; // Unix-like systems -pub usingnamespace switch (builtin.os.tag) { +pub usingnamespace switch (native_os) { .netbsd, .windows => struct {}, else => struct { pub const DIR = opaque {}; @@ -225,25 +717,40 @@ pub usingnamespace switch (builtin.os.tag) { }, }; -pub usingnamespace switch (builtin.os.tag) { - .netbsd, .macos, .ios, .watchos, .tvos, .windows => struct {}, - else => struct { - pub extern "c" fn fstat(fd: c.fd_t, buf: *c.Stat) c_int; - pub extern "c" fn readdir(dp: *c.DIR) ?*c.dirent; +pub const fstat = switch (native_os) { + .netbsd => private.__fstat50, + .macos, .ios, .watchos, .tvos => switch (native_arch) { + .aarch64 => private.fstat, + else => private.@"fstat$INODE64", }, + else => private.fstat, }; -pub usingnamespace switch (builtin.os.tag) { - .macos, .ios, .watchos, .tvos => struct {}, - else => struct { - pub extern "c" fn realpath(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8; - pub extern "c" fn fstatat(dirfd: c.fd_t, path: [*:0]const u8, stat_buf: *c.Stat, flags: u32) c_int; +pub const fstatat = switch (native_os) { + .macos, .ios, .watchos, .tvos => switch (native_arch) { + .aarch64 => private.fstatat, + else => private.@"fstatat$INODE64", }, + else => private.fstatat, +}; + +pub const readdir = switch (native_os) { + .macos, .ios, .watchos, .tvos => switch (native_arch) { + .aarch64 => private.readdir, + else => private.@"readdir$INODE64", + }, + .windows => @compileError("not available"), + else => private.readdir, +}; + +pub const realpath = switch (native_os) { + .macos, .ios, .watchos, .tvos => private.@"realpath$DARWIN_EXTSN", + else => private.realpath, }; pub fn getErrno(rc: anytype) c.E { if (rc == -1) { - return @as(c.E, @enumFromInt(c._errno().*)); + return @enumFromInt(c._errno().*); } else { return .SUCCESS; } @@ -263,8 +770,8 @@ pub extern "c" fn _exit(code: c_int) noreturn; pub extern "c" fn isatty(fd: c.fd_t) c_int; pub extern "c" fn close(fd: c.fd_t) c_int; pub extern "c" fn lseek(fd: c.fd_t, offset: c.off_t, whence: whence_t) c.off_t; -pub extern "c" fn open(path: [*:0]const u8, oflag: c_uint, ...) c_int; -pub extern "c" fn openat(fd: c_int, path: [*:0]const u8, oflag: c_uint, ...) c_int; +pub extern "c" fn open(path: [*:0]const u8, oflag: O, ...) c_int; +pub extern "c" fn openat(fd: c_int, path: [*:0]const u8, oflag: O, ...) c_int; pub extern "c" fn ftruncate(fd: c_int, length: c.off_t) c_int; pub extern "c" fn raise(sig: c_int) c_int; pub extern "c" fn read(fd: c.fd_t, buf: [*]u8, nbyte: usize) isize; @@ -275,7 +782,7 @@ pub extern "c" fn writev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint) i pub extern "c" fn pwritev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint, offset: c.off_t) isize; pub extern "c" fn write(fd: c.fd_t, buf: [*]const u8, nbyte: usize) isize; pub extern "c" fn pwrite(fd: c.fd_t, buf: [*]const u8, nbyte: usize, offset: c.off_t) isize; -pub extern "c" fn mmap(addr: ?*align(page_size) anyopaque, len: usize, prot: c_uint, flags: c_uint, fd: c.fd_t, offset: c.off_t) *anyopaque; +pub extern "c" fn mmap(addr: ?*align(page_size) anyopaque, len: usize, prot: c_uint, flags: MAP, fd: c.fd_t, offset: c.off_t) *anyopaque; pub extern "c" fn munmap(addr: *align(page_size) const anyopaque, len: usize) c_int; pub extern "c" fn mprotect(addr: *align(page_size) anyopaque, len: usize, prot: c_uint) c_int; pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: c_int) c_int; @@ -348,7 +855,7 @@ pub extern "c" fn recv( arg1: ?*anyopaque, arg2: usize, arg3: c_int, -) if (builtin.os.tag == .windows) c_int else isize; +) if (native_os == .windows) c_int else isize; pub extern "c" fn recvfrom( sockfd: c.fd_t, noalias buf: *anyopaque, @@ -356,7 +863,7 @@ pub extern "c" fn recvfrom( flags: u32, noalias src_addr: ?*c.sockaddr, noalias addrlen: ?*c.socklen_t, -) if (builtin.os.tag == .windows) c_int else isize; +) if (native_os == .windows) c_int else isize; pub extern "c" fn recvmsg(sockfd: c.fd_t, msg: *c.msghdr, flags: u32) isize; pub extern "c" fn kill(pid: c.pid_t, sig: c_int) c_int; @@ -492,18 +999,18 @@ pub extern "c" fn dn_expand( length: c_int, ) c_int; -pub const PTHREAD_MUTEX_INITIALIZER = c.pthread_mutex_t{}; -pub extern "c" fn pthread_mutex_lock(mutex: *c.pthread_mutex_t) c.E; -pub extern "c" fn pthread_mutex_unlock(mutex: *c.pthread_mutex_t) c.E; -pub extern "c" fn pthread_mutex_trylock(mutex: *c.pthread_mutex_t) c.E; -pub extern "c" fn pthread_mutex_destroy(mutex: *c.pthread_mutex_t) c.E; +pub const PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{}; +pub extern "c" fn pthread_mutex_lock(mutex: *pthread_mutex_t) c.E; +pub extern "c" fn pthread_mutex_unlock(mutex: *pthread_mutex_t) c.E; +pub extern "c" fn pthread_mutex_trylock(mutex: *pthread_mutex_t) c.E; +pub extern "c" fn pthread_mutex_destroy(mutex: *pthread_mutex_t) c.E; -pub const PTHREAD_COND_INITIALIZER = c.pthread_cond_t{}; -pub extern "c" fn pthread_cond_wait(noalias cond: *c.pthread_cond_t, noalias mutex: *c.pthread_mutex_t) c.E; -pub extern "c" fn pthread_cond_timedwait(noalias cond: *c.pthread_cond_t, noalias mutex: *c.pthread_mutex_t, noalias abstime: *const c.timespec) c.E; -pub extern "c" fn pthread_cond_signal(cond: *c.pthread_cond_t) c.E; -pub extern "c" fn pthread_cond_broadcast(cond: *c.pthread_cond_t) c.E; -pub extern "c" fn pthread_cond_destroy(cond: *c.pthread_cond_t) c.E; +pub const PTHREAD_COND_INITIALIZER = pthread_cond_t{}; +pub extern "c" fn pthread_cond_wait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t) c.E; +pub extern "c" fn pthread_cond_timedwait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t, noalias abstime: *const c.timespec) c.E; +pub extern "c" fn pthread_cond_signal(cond: *pthread_cond_t) c.E; +pub extern "c" fn pthread_cond_broadcast(cond: *pthread_cond_t) c.E; +pub extern "c" fn pthread_cond_destroy(cond: *pthread_cond_t) c.E; pub extern "c" fn pthread_rwlock_destroy(rwl: *c.pthread_rwlock_t) callconv(.C) c.E; pub extern "c" fn pthread_rwlock_rdlock(rwl: *c.pthread_rwlock_t) callconv(.C) c.E; @@ -542,14 +1049,14 @@ pub usingnamespace if (builtin.target.isAndroid()) struct { // android bionic libc does not implement getcontext, // and std.os.linux.getcontext also cannot be built for // bionic libc currently. -} else if (builtin.os.tag == .linux and builtin.target.isMusl()) struct { +} else if (native_os == .linux and builtin.target.isMusl()) struct { // musl does not implement getcontext pub const getcontext = std.os.linux.getcontext; } else struct { pub extern "c" fn getcontext(ucp: *std.os.ucontext_t) c_int; }; -pub const max_align_t = if (builtin.abi == .msvc) +pub const max_align_t = if (native_abi == .msvc) f64 else if (builtin.target.isDarwin()) c_longdouble @@ -558,3 +1065,25 @@ else a: c_longlong, b: c_longdouble, }; + +const private = struct { + extern "c" fn fstat(fd: c.fd_t, buf: *c.Stat) c_int; + /// On x86_64 Darwin, fstat has to be manually linked with $INODE64 suffix to + /// force 64bit version. + /// Note that this is fixed on aarch64 and no longer necessary. + extern "c" fn @"fstat$INODE64"(fd: c.fd_t, buf: *c.Stat) c_int; + + extern "c" fn fstatat(dirfd: c.fd_t, path: [*:0]const u8, stat_buf: *c.Stat, flags: u32) c_int; + /// On x86_64 Darwin, fstatat has to be manually linked with $INODE64 suffix to + /// force 64bit version. + /// Note that this is fixed on aarch64 and no longer necessary. + extern "c" fn @"fstatat$INODE64"(dirfd: c.fd_t, path_name: [*:0]const u8, buf: *c.Stat, flags: u32) c_int; + + extern "c" fn __fstat50(fd: c.fd_t, buf: *c.Stat) c_int; + + extern "c" fn readdir(dir: *c.DIR) ?*c.dirent; + extern "c" fn @"readdir$INODE64"(dir: *c.DIR) ?*c.dirent; + + extern "c" fn realpath(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8; + extern "c" fn @"realpath$DARWIN_EXTSN"(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8; +}; diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index a80c65f76c..d598c7d7b8 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -169,31 +169,8 @@ pub const COPYFILE_DATA = 1 << 3; pub const copyfile_state_t = *opaque {}; pub extern "c" fn fcopyfile(from: fd_t, to: fd_t, state: ?copyfile_state_t, flags: u32) c_int; -pub extern "c" fn @"realpath$DARWIN_EXTSN"(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8; -pub const realpath = @"realpath$DARWIN_EXTSN"; - pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) isize; -const private = struct { - extern "c" fn fstat(fd: fd_t, buf: *Stat) c_int; - /// On x86_64 Darwin, fstat has to be manually linked with $INODE64 suffix to - /// force 64bit version. - /// Note that this is fixed on aarch64 and no longer necessary. - extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int; - - extern "c" fn fstatat(dirfd: fd_t, path: [*:0]const u8, stat_buf: *Stat, flags: u32) c_int; - /// On x86_64 Darwin, fstatat has to be manually linked with $INODE64 suffix to - /// force 64bit version. - /// Note that this is fixed on aarch64 and no longer necessary. - extern "c" fn @"fstatat$INODE64"(dirfd: fd_t, path_name: [*:0]const u8, buf: *Stat, flags: u32) c_int; - - extern "c" fn readdir(dir: *std.c.DIR) ?*dirent; - extern "c" fn @"readdir$INODE64"(dir: *std.c.DIR) ?*dirent; -}; -pub const fstat = if (native_arch == .aarch64) private.fstat else private.@"fstat$INODE64"; -pub const fstatat = if (native_arch == .aarch64) private.fstatat else private.@"fstatat$INODE64"; -pub const readdir = if (native_arch == .aarch64) private.readdir else private.@"readdir$INODE64"; - pub extern "c" fn mach_absolute_time() u64; pub extern "c" fn mach_continuous_time() u64; pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) kern_return_t; @@ -866,21 +843,7 @@ pub const qos_class_t = enum(c_uint) { QOS_CLASS_UNSPECIFIED = 0x00, }; -pub const pthread_mutex_t = extern struct { - __sig: c_long = 0x32AAABA7, - __opaque: [__PTHREAD_MUTEX_SIZE__]u8 = [_]u8{0} ** __PTHREAD_MUTEX_SIZE__, -}; -pub const pthread_cond_t = extern struct { - __sig: c_long = 0x3CB0B1BB, - __opaque: [__PTHREAD_COND_SIZE__]u8 = [_]u8{0} ** __PTHREAD_COND_SIZE__, -}; -pub const pthread_rwlock_t = extern struct { - __sig: c_long = 0x2DA8B3B4, - __opaque: [192]u8 = [_]u8{0} ** 192, -}; pub const sem_t = c_int; -const __PTHREAD_MUTEX_SIZE__ = if (@sizeOf(usize) == 8) 56 else 40; -const __PTHREAD_COND_SIZE__ = if (@sizeOf(usize) == 8) 40 else 24; pub const pthread_attr_t = extern struct { __sig: c_long, @@ -1202,16 +1165,12 @@ pub const Sigaction = extern struct { }; pub const dirent = extern struct { - d_ino: u64, - d_seekoff: u64, - d_reclen: u16, - d_namlen: u16, - d_type: u8, - d_name: [1024]u8, - - pub fn reclen(self: dirent) u16 { - return self.d_reclen; - } + ino: u64, + seekoff: u64, + reclen: u16, + namlen: u16, + type: u8, + name: [1024]u8, }; /// Renamed from `kevent` to `Kevent` to avoid conflict with function name. @@ -1346,49 +1305,6 @@ pub const X_OK = 1; pub const W_OK = 2; pub const R_OK = 4; -pub const O = struct { - pub const PATH = 0x0000; - /// open for reading only - pub const RDONLY = 0x0000; - /// open for writing only - pub const WRONLY = 0x0001; - /// open for reading and writing - pub const RDWR = 0x0002; - /// do not block on open or for data to become available - pub const NONBLOCK = 0x0004; - /// append on each write - pub const APPEND = 0x0008; - /// create file if it does not exist - pub const CREAT = 0x0200; - /// truncate size to 0 - pub const TRUNC = 0x0400; - /// error if CREAT and the file exists - pub const EXCL = 0x0800; - /// atomically obtain a shared lock - pub const SHLOCK = 0x0010; - /// atomically obtain an exclusive lock - pub const EXLOCK = 0x0020; - /// do not follow symlinks - pub const NOFOLLOW = 0x0100; - /// allow open of symlinks - pub const SYMLINK = 0x200000; - /// descriptor requested for event notifications only - pub const EVTONLY = 0x8000; - /// mark as close-on-exec - pub const CLOEXEC = 0x1000000; - pub const ACCMODE = 3; - pub const ALERT = 536870912; - pub const ASYNC = 64; - pub const DIRECTORY = 1048576; - pub const DP_GETRAWENCRYPTED = 1; - pub const DP_GETRAWUNENCRYPTED = 2; - pub const DSYNC = 4194304; - pub const FSYNC = SYNC; - pub const NOCTTY = 131072; - pub const POPUP = 2147483648; - pub const SYNC = 128; -}; - pub const SEEK = struct { pub const SET = 0x0; pub const CUR = 0x1; @@ -2529,18 +2445,6 @@ pub const S = struct { pub const HOST_NAME_MAX = 72; -pub const AT = struct { - pub const FDCWD = -2; - /// Use effective ids in access check - pub const EACCESS = 0x0010; - /// Act on the symlink itself not the target - pub const SYMLINK_NOFOLLOW = 0x0020; - /// Act on target of symlink - pub const SYMLINK_FOLLOW = 0x0040; - /// Path refers to directory - pub const REMOVEDIR = 0x0080; -}; - pub const addrinfo = extern struct { flags: i32, family: i32, diff --git a/lib/std/c/dragonfly.zig b/lib/std/c/dragonfly.zig index 5474d79d38..80f583b497 100644 --- a/lib/std/c/dragonfly.zig +++ b/lib/std/c/dragonfly.zig @@ -22,22 +22,11 @@ pub extern "c" fn lwp_gettid() c_int; pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int; -pub const pthread_mutex_t = extern struct { - inner: ?*anyopaque = null, -}; -pub const pthread_cond_t = extern struct { - inner: ?*anyopaque = null, -}; - pub const pthread_attr_t = extern struct { // copied from freebsd __size: [56]u8, __align: c_long, }; -pub const pthread_rwlock_t = extern struct { - ptr: ?*anyopaque = null, -}; - pub const sem_t = ?*opaque {}; pub extern "c" fn pthread_setname_np(thread: std.c.pthread_t, name: [*:0]const u8) E; @@ -394,35 +383,6 @@ pub const X_OK = 1; // test for execute or search permission pub const W_OK = 2; // test for write permission pub const R_OK = 4; // test for read permission -pub const O = struct { - pub const RDONLY = 0; - pub const NDELAY = NONBLOCK; - pub const WRONLY = 1; - pub const RDWR = 2; - pub const ACCMODE = 3; - pub const NONBLOCK = 4; - pub const APPEND = 8; - pub const SHLOCK = 16; - pub const EXLOCK = 32; - pub const ASYNC = 64; - pub const FSYNC = 128; - pub const SYNC = 128; - pub const NOFOLLOW = 256; - pub const CREAT = 512; - pub const TRUNC = 1024; - pub const EXCL = 2048; - pub const NOCTTY = 32768; - pub const DIRECT = 65536; - pub const CLOEXEC = 131072; - pub const FBLOCKING = 262144; - pub const FNONBLOCKING = 524288; - pub const FAPPEND = 1048576; - pub const FOFFSET = 2097152; - pub const FSYNCWRITE = 4194304; - pub const FASYNCWRITE = 8388608; - pub const DIRECTORY = 134217728; -}; - pub const SEEK = struct { pub const SET = 0; pub const CUR = 1; @@ -458,24 +418,16 @@ pub const F = struct { pub const FD_CLOEXEC = 1; -pub const AT = struct { - pub const FDCWD = -328243; - pub const SYMLINK_NOFOLLOW = 1; - pub const REMOVEDIR = 2; - pub const EACCESS = 4; - pub const SYMLINK_FOLLOW = 8; -}; - pub const dirent = extern struct { - d_fileno: c_ulong, - d_namlen: u16, - d_type: u8, - d_unused1: u8, - d_unused2: u32, - d_name: [256]u8, + fileno: c_ulong, + namlen: u16, + type: u8, + unused1: u8, + unused2: u32, + name: [256]u8, pub fn reclen(self: dirent) u16 { - return (@offsetOf(dirent, "d_name") + self.d_namlen + 1 + 7) & ~@as(u16, 7); + return (@offsetOf(dirent, "name") + self.namlen + 1 + 7) & ~@as(u16, 7); } }; diff --git a/lib/std/c/emscripten.zig b/lib/std/c/emscripten.zig index ca64473016..ae72928eb9 100644 --- a/lib/std/c/emscripten.zig +++ b/lib/std/c/emscripten.zig @@ -3,7 +3,6 @@ const maxInt = std.math.maxInt; const emscripten = std.os.emscripten; pub const AF = emscripten.AF; -pub const AT = emscripten.AT; pub const CLOCK = emscripten.CLOCK; pub const CPU_COUNT = emscripten.CPU_COUNT; pub const E = emscripten.E; @@ -19,7 +18,6 @@ pub const MADV = emscripten.MADV; pub const MSF = emscripten.MSF; pub const MSG = emscripten.MSG; pub const NAME_MAX = emscripten.NAME_MAX; -pub const O = emscripten.O; pub const PATH_MAX = emscripten.PATH_MAX; pub const POLL = emscripten.POLL; pub const PROT = emscripten.PROT; @@ -159,19 +157,6 @@ pub const pthread_attr_t = extern struct { __align: c_long, }; -pub const pthread_mutex_t = extern struct { - size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(4) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T, -}; -pub const pthread_cond_t = extern struct { - size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, -}; -pub const pthread_rwlock_t = extern struct { - size: [32]u8 align(4) = [_]u8{0} ** 32, -}; - -const __SIZEOF_PTHREAD_COND_T = 48; -const __SIZEOF_PTHREAD_MUTEX_T = 24; - pub const pthread_key_t = c_uint; pub const sem_t = extern struct { __size: [__SIZEOF_SEM_T]u8 align(@alignOf(usize)), @@ -189,9 +174,9 @@ pub const RTLD = struct { }; pub const dirent = struct { - d_ino: c_uint, - d_off: c_uint, - d_reclen: c_ushort, - d_type: u8, - d_name: [256]u8, + ino: c_uint, + off: c_uint, + reclen: c_ushort, + type: u8, + name: [256]u8, }; diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index a1b64893b8..d544bc92c8 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -44,16 +44,6 @@ pub extern "c" fn sendfile( pub const dl_iterate_phdr_callback = *const fn (info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.C) c_int; pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*anyopaque) c_int; -pub const pthread_mutex_t = extern struct { - inner: ?*anyopaque = null, -}; -pub const pthread_cond_t = extern struct { - inner: ?*anyopaque = null, -}; -pub const pthread_rwlock_t = extern struct { - ptr: ?*anyopaque = null, -}; - pub const pthread_attr_t = extern struct { inner: ?*anyopaque = null, }; @@ -376,23 +366,19 @@ pub const timeval = extern struct { pub const dirent = extern struct { /// File number of entry. - d_fileno: ino_t, + fileno: ino_t, /// Directory offset of entry. - d_off: off_t, + off: off_t, /// Length of this record. - d_reclen: u16, + reclen: u16, /// File type, one of DT_. - d_type: u8, - _d_pad0: u8, - /// Length of the d_name member. - d_namlen: u16, - _d_pad1: u16, + type: u8, + pad0: u8 = 0, + /// Length of the name member. + namlen: u16, + pad1: u16 = 0, /// Name of entry. - d_name: [255:0]u8, - - pub fn reclen(self: dirent) u16 { - return self.d_reclen; - } + name: [255:0]u8, }; pub const in_port_t = u16; @@ -746,36 +732,6 @@ pub const X_OK = 1; // test for execute or search permission pub const W_OK = 2; // test for write permission pub const R_OK = 4; // test for read permission -pub const O = struct { - pub const RDONLY = 0x0000; - pub const WRONLY = 0x0001; - pub const RDWR = 0x0002; - pub const ACCMODE = 0x0003; - - pub const SHLOCK = 0x0010; - pub const EXLOCK = 0x0020; - - pub const CREAT = 0x0200; - pub const EXCL = 0x0800; - pub const NOCTTY = 0x8000; - pub const TRUNC = 0x0400; - pub const APPEND = 0x0008; - pub const NONBLOCK = 0x0004; - pub const DSYNC = 0o10000; - pub const SYNC = 0x0080; - pub const RSYNC = 0o4010000; - pub const DIRECTORY = 0x20000; - pub const NOFOLLOW = 0x0100; - pub const CLOEXEC = 0x00100000; - - pub const ASYNC = 0x0040; - pub const DIRECT = 0x00010000; - pub const NOATIME = 0o1000000; - pub const PATH = 0o10000000; - pub const TMPFILE = 0o20200000; - pub const NDELAY = NONBLOCK; -}; - /// Command flags for fcntl(2). pub const F = struct { /// Duplicate file descriptor. @@ -1573,23 +1529,6 @@ pub const S = struct { pub const HOST_NAME_MAX = 255; -pub const AT = struct { - /// Magic value that specify the use of the current working directory - /// to determine the target of relative file paths in the openat() and - /// similar syscalls. - pub const FDCWD = -100; - /// Check access using effective user and group ID - pub const EACCESS = 0x0100; - /// Do not follow symbolic links - pub const SYMLINK_NOFOLLOW = 0x0200; - /// Follow symbolic link - pub const SYMLINK_FOLLOW = 0x0400; - /// Remove directory instead of file - pub const REMOVEDIR = 0x0800; - /// Fail if not under dirfd - pub const BENEATH = 0x1000; -}; - pub const addrinfo = extern struct { flags: i32, family: i32, diff --git a/lib/std/c/fuchsia.zig b/lib/std/c/fuchsia.zig deleted file mode 100644 index af6c4756b9..0000000000 --- a/lib/std/c/fuchsia.zig +++ /dev/null @@ -1,11 +0,0 @@ -pub const pthread_mutex_t = extern struct { - size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T, -}; -pub const pthread_cond_t = extern struct { - size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, -}; -pub const pthread_rwlock_t = extern struct { - size: [56]u8 align(@alignOf(usize)) = [_]u8{0} ** 56, -}; -const __SIZEOF_PTHREAD_COND_T = 48; -const __SIZEOF_PTHREAD_MUTEX_T = 40; diff --git a/lib/std/c/haiku.zig b/lib/std/c/haiku.zig index c8cc16563a..63bfa6b82e 100644 --- a/lib/std/c/haiku.zig +++ b/lib/std/c/haiku.zig @@ -45,22 +45,6 @@ pub const pthread_attr_t = extern struct { __stack_address: ?*anyopaque, }; -pub const pthread_mutex_t = extern struct { - flags: u32 = 0, - lock: i32 = 0, - unused: i32 = -42, - owner: i32 = -1, - owner_count: i32 = 0, -}; - -pub const pthread_cond_t = extern struct { - flags: u32 = 0, - unused: i32 = -42, - mutex: ?*anyopaque = null, - waiter_count: i32 = 0, - lock: i32 = 0, -}; - pub const EAI = enum(c_int) { /// address family for hostname not supported ADDRFAMILY = 1, @@ -238,16 +222,12 @@ pub const timespec = extern struct { }; pub const dirent = extern struct { - d_dev: i32, - d_pdev: i32, - d_ino: i64, - d_pino: i64, - d_reclen: u16, - d_name: [256]u8, - - pub fn reclen(self: dirent) u16 { - return self.d_reclen; - } + dev: i32, + pdev: i32, + ino: i64, + pino: i64, + reclen: u16, + name: [256]u8, }; pub const B_OS_NAME_LENGTH = 32; // OS.h @@ -510,32 +490,6 @@ pub const X_OK = 1; // test for execute or search permission pub const W_OK = 2; // test for write permission pub const R_OK = 4; // test for read permission -pub const O = struct { - pub const RDONLY = 0x0000; - pub const WRONLY = 0x0001; - pub const RDWR = 0x0002; - pub const ACCMODE = 0x0003; - pub const RWMASK = ACCMODE; - - pub const EXCL = 0x0100; - pub const CREAT = 0x0200; - pub const TRUNC = 0x0400; - pub const NOCTTY = 0x1000; - pub const NOTRAVERSE = 0x2000; - - pub const CLOEXEC = 0x00000040; - pub const NONBLOCK = 0x00000080; - pub const NDELAY = NONBLOCK; - pub const APPEND = 0x00000800; - pub const SYNC = 0x00010000; - pub const RSYNC = 0x00020000; - pub const DSYNC = 0x00040000; - pub const NOFOLLOW = 0x00080000; - pub const DIRECT = 0x00100000; - pub const NOCACHE = DIRECT; - pub const DIRECTORY = 0x00200000; -}; - pub const F = struct { pub const DUPFD = 0x0001; pub const GETFD = 0x0002; @@ -923,14 +877,6 @@ pub const S = struct { pub const HOST_NAME_MAX = 255; -pub const AT = struct { - pub const FDCWD = -1; - pub const SYMLINK_NOFOLLOW = 0x01; - pub const SYMLINK_FOLLOW = 0x02; - pub const REMOVEDIR = 0x04; - pub const EACCESS = 0x08; -}; - pub const addrinfo = extern struct { flags: i32, family: i32, diff --git a/lib/std/c/hermit.zig b/lib/std/c/hermit.zig deleted file mode 100644 index 879346ba13..0000000000 --- a/lib/std/c/hermit.zig +++ /dev/null @@ -1,12 +0,0 @@ -const std = @import("std"); -const maxInt = std.math.maxInt; - -pub const pthread_mutex_t = extern struct { - inner: usize = ~@as(usize, 0), -}; -pub const pthread_cond_t = extern struct { - inner: usize = ~@as(usize, 0), -}; -pub const pthread_rwlock_t = extern struct { - ptr: usize = maxInt(usize), -}; diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig index c847d58bef..dd26c9ccc3 100644 --- a/lib/std/c/linux.zig +++ b/lib/std/c/linux.zig @@ -9,7 +9,6 @@ const FILE = std.c.FILE; pub const AF = linux.AF; pub const ARCH = linux.ARCH; -pub const AT = linux.AT; pub const CLOCK = linux.CLOCK; pub const CPU_COUNT = linux.CPU_COUNT; pub const E = linux.E; @@ -28,7 +27,6 @@ pub const MSF = linux.MSF; pub const MMAP2_UNIT = linux.MMAP2_UNIT; pub const MSG = linux.MSG; pub const NAME_MAX = linux.NAME_MAX; -pub const O = linux.O; pub const PATH_MAX = linux.PATH_MAX; pub const POLL = linux.POLL; pub const PROT = linux.PROT; @@ -241,8 +239,8 @@ pub extern "c" fn ftruncate64(fd: c_int, length: off_t) c_int; pub extern "c" fn getrlimit64(resource: rlimit_resource, rlim: *rlimit) c_int; pub extern "c" fn lseek64(fd: fd_t, offset: i64, whence: c_int) i64; pub extern "c" fn mmap64(addr: ?*align(std.mem.page_size) anyopaque, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: i64) *anyopaque; -pub extern "c" fn open64(path: [*:0]const u8, oflag: c_uint, ...) c_int; -pub extern "c" fn openat64(fd: c_int, path: [*:0]const u8, oflag: c_uint, ...) c_int; +pub extern "c" fn open64(path: [*:0]const u8, oflag: linux.O, ...) c_int; +pub extern "c" fn openat64(fd: c_int, path: [*:0]const u8, oflag: linux.O, ...) c_int; pub extern "c" fn pread64(fd: fd_t, buf: [*]u8, nbyte: usize, offset: i64) isize; pub extern "c" fn preadv64(fd: c_int, iov: [*]const iovec, iovcnt: c_uint, offset: i64) isize; pub extern "c" fn pwrite64(fd: fd_t, buf: [*]const u8, nbyte: usize, offset: i64) isize; @@ -277,7 +275,7 @@ pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*an pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; pub extern "c" fn memfd_create(name: [*:0]const u8, flags: c_uint) c_int; -pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int; +pub extern "c" fn pipe2(fds: *[2]fd_t, flags: linux.O) c_int; pub extern "c" fn fallocate(fd: fd_t, mode: c_int, offset: off_t, len: off_t) c_int; @@ -313,43 +311,11 @@ pub const pthread_attr_t = extern struct { __align: c_long, }; -pub const pthread_mutex_t = extern struct { - size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T, -}; -pub const pthread_cond_t = extern struct { - size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, -}; -pub const pthread_rwlock_t = switch (native_abi) { - .android => switch (@sizeOf(usize)) { - 4 => extern struct { - size: [40]u8 align(@alignOf(usize)) = [_]u8{0} ** 40, - }, - 8 => extern struct { - size: [56]u8 align(@alignOf(usize)) = [_]u8{0} ** 56, - }, - else => @compileError("impossible pointer size"), - }, - else => extern struct { - size: [56]u8 align(@alignOf(usize)) = [_]u8{0} ** 56, - }, -}; pub const pthread_key_t = c_uint; pub const sem_t = extern struct { __size: [__SIZEOF_SEM_T]u8 align(@alignOf(usize)), }; -const __SIZEOF_PTHREAD_COND_T = 48; -const __SIZEOF_PTHREAD_MUTEX_T = switch (native_abi) { - .musl, .musleabi, .musleabihf => if (@sizeOf(usize) == 8) 40 else 24, - .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (native_arch) { - .aarch64 => 48, - .x86_64 => if (native_abi == .gnux32) 40 else 32, - .mips64, .powerpc64, .powerpc64le, .sparc64 => 40, - else => if (@sizeOf(usize) == 8) 40 else 24, - }, - .android => if (@sizeOf(usize) == 8) 40 else 4, - else => @compileError("unsupported ABI"), -}; const __SIZEOF_SEM_T = 4 * @sizeOf(usize); pub extern "c" fn pthread_setname_np(thread: std.c.pthread_t, name: [*:0]const u8) E; @@ -365,16 +331,16 @@ pub const RTLD = struct { }; pub const dirent = struct { - d_ino: c_uint, - d_off: c_uint, - d_reclen: c_ushort, - d_type: u8, - d_name: [256]u8, + ino: c_uint, + off: c_uint, + reclen: c_ushort, + type: u8, + name: [256]u8, }; pub const dirent64 = struct { - d_ino: c_ulong, - d_off: c_ulong, - d_reclen: c_ushort, - d_type: u8, - d_name: [256]u8, + ino: c_ulong, + off: c_ulong, + reclen: c_ushort, + type: u8, + name: [256]u8, }; diff --git a/lib/std/c/minix.zig b/lib/std/c/minix.zig deleted file mode 100644 index 62cefc14fb..0000000000 --- a/lib/std/c/minix.zig +++ /dev/null @@ -1,18 +0,0 @@ -const builtin = @import("builtin"); -pub const pthread_mutex_t = extern struct { - size: [__SIZEOF_PTHREAD_MUTEX_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_MUTEX_T, -}; -pub const pthread_cond_t = extern struct { - size: [__SIZEOF_PTHREAD_COND_T]u8 align(@alignOf(usize)) = [_]u8{0} ** __SIZEOF_PTHREAD_COND_T, -}; -const __SIZEOF_PTHREAD_COND_T = 48; -const __SIZEOF_PTHREAD_MUTEX_T = switch (builtin.abi) { - .musl, .musleabi, .musleabihf => if (@sizeOf(usize) == 8) 40 else 24, - .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => switch (builtin.cpu.arch) { - .aarch64 => 48, - .x86_64 => if (builtin.abi == .gnux32) 40 else 32, - .mips64, .powerpc64, .powerpc64le, .sparc64 => 40, - else => if (@sizeOf(usize) == 8) 40 else 24, - }, - else => unreachable, -}; diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index 2c100e2c37..b062090dd4 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -18,9 +18,6 @@ pub extern "c" fn _lwp_self() lwpid_t; pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int; pub extern "c" fn arc4random_buf(buf: [*]u8, len: usize) void; -pub extern "c" fn __fstat50(fd: fd_t, buf: *Stat) c_int; -pub const fstat = __fstat50; - pub extern "c" fn __stat50(path: [*:0]const u8, buf: *Stat) c_int; pub const stat = __stat50; @@ -62,41 +59,6 @@ pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: u pub extern "c" fn __msync13(addr: *align(std.mem.page_size) const anyopaque, len: usize, flags: c_int) c_int; pub const msync = __msync13; -pub const pthread_mutex_t = extern struct { - magic: u32 = 0x33330003, - errorcheck: padded_pthread_spin_t = 0, - ceiling: padded_pthread_spin_t = 0, - owner: usize = 0, - waiters: ?*u8 = null, - recursed: u32 = 0, - spare2: ?*anyopaque = null, -}; - -pub const pthread_cond_t = extern struct { - magic: u32 = 0x55550005, - lock: pthread_spin_t = 0, - waiters_first: ?*u8 = null, - waiters_last: ?*u8 = null, - mutex: ?*pthread_mutex_t = null, - private: ?*anyopaque = null, -}; - -pub const pthread_rwlock_t = extern struct { - magic: c_uint = 0x99990009, - interlock: switch (builtin.cpu.arch) { - .aarch64, .sparc, .x86_64, .x86 => u8, - .arm, .powerpc => c_int, - else => unreachable, - } = 0, - rblocked_first: ?*u8 = null, - rblocked_last: ?*u8 = null, - wblocked_first: ?*u8 = null, - wblocked_last: ?*u8 = null, - nreaders: c_uint = 0, - owner: ?std.c.pthread_t = null, - private: ?*anyopaque = null, -}; - const pthread_spin_t = switch (builtin.cpu.arch) { .aarch64, .aarch64_be, .aarch64_32 => u8, .mips, .mipsel, .mips64, .mips64el => u32, @@ -337,15 +299,11 @@ pub const timeval = extern struct { pub const MAXNAMLEN = 511; pub const dirent = extern struct { - d_fileno: ino_t, - d_reclen: u16, - d_namlen: u16, - d_type: u8, - d_name: [MAXNAMLEN + 1]u8, - - pub fn reclen(self: dirent) u16 { - return self.d_reclen; - } + fileno: ino_t, + reclen: u16, + namlen: u16, + type: u8, + name: [MAXNAMLEN + 1]u8, }; pub const SOCK = struct { @@ -630,53 +588,6 @@ pub const X_OK = 1; // test for execute or search permission pub const W_OK = 2; // test for write permission pub const R_OK = 4; // test for read permission -pub const O = struct { - /// open for reading only - pub const RDONLY = 0x00000000; - /// open for writing only - pub const WRONLY = 0x00000001; - /// open for reading and writing - pub const RDWR = 0x00000002; - /// mask for above modes - pub const ACCMODE = 0x00000003; - /// no delay - pub const NONBLOCK = 0x00000004; - /// set append mode - pub const APPEND = 0x00000008; - /// open with shared file lock - pub const SHLOCK = 0x00000010; - /// open with exclusive file lock - pub const EXLOCK = 0x00000020; - /// signal pgrp when data ready - pub const ASYNC = 0x00000040; - /// synchronous writes - pub const SYNC = 0x00000080; - /// don't follow symlinks on the last - pub const NOFOLLOW = 0x00000100; - /// create if nonexistent - pub const CREAT = 0x00000200; - /// truncate to zero length - pub const TRUNC = 0x00000400; - /// error if already exists - pub const EXCL = 0x00000800; - /// don't assign controlling terminal - pub const NOCTTY = 0x00008000; - /// write: I/O data completion - pub const DSYNC = 0x00010000; - /// read: I/O completion as for write - pub const RSYNC = 0x00020000; - /// use alternate i/o semantics - pub const ALT_IO = 0x00040000; - /// direct I/O hint - pub const DIRECT = 0x00080000; - /// fail if not a directory - pub const DIRECTORY = 0x00200000; - /// set close on exec - pub const CLOEXEC = 0x00400000; - /// skip search permission checks - pub const SEARCH = 0x00800000; -}; - pub const F = struct { pub const DUPFD = 0; pub const GETFD = 1; @@ -1466,21 +1377,6 @@ pub const S = struct { } }; -pub const AT = struct { - /// Magic value that specify the use of the current working directory - /// to determine the target of relative file paths in the openat() and - /// similar syscalls. - pub const FDCWD = -100; - /// Check access using effective user and group ID - pub const EACCESS = 0x0100; - /// Do not follow symbolic links - pub const SYMLINK_NOFOLLOW = 0x0200; - /// Follow symbolic link - pub const SYMLINK_FOLLOW = 0x0400; - /// Remove directory instead of file - pub const REMOVEDIR = 0x0800; -}; - pub const HOST_NAME_MAX = 255; pub const IPPROTO = struct { diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig index a5e275913b..adf37ce48e 100644 --- a/lib/std/c/openbsd.zig +++ b/lib/std/c/openbsd.zig @@ -19,15 +19,6 @@ pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int; pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) c_int; pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; -pub const pthread_mutex_t = extern struct { - inner: ?*anyopaque = null, -}; -pub const pthread_cond_t = extern struct { - inner: ?*anyopaque = null, -}; -pub const pthread_rwlock_t = extern struct { - ptr: ?*anyopaque = null, -}; pub const pthread_spinlock_t = extern struct { inner: ?*anyopaque = null, }; @@ -336,17 +327,13 @@ pub const timezone = extern struct { pub const MAXNAMLEN = 255; pub const dirent = extern struct { - d_fileno: ino_t, - d_off: off_t, - d_reclen: u16, - d_type: u8, - d_namlen: u8, - __d_padding: [4]u8, - d_name: [MAXNAMLEN + 1]u8, - - pub fn reclen(self: dirent) u16 { - return self.d_reclen; - } + fileno: ino_t, + off: off_t, + reclen: u16, + type: u8, + namlen: u8, + _: u32 align(1) = 0, + name: [MAXNAMLEN + 1]u8, }; pub const in_port_t = u16; @@ -489,47 +476,6 @@ pub const X_OK = 1; // test for execute or search permission pub const W_OK = 2; // test for write permission pub const R_OK = 4; // test for read permission -pub const O = struct { - /// open for reading only - pub const RDONLY = 0x00000000; - /// open for writing only - pub const WRONLY = 0x00000001; - /// open for reading and writing - pub const RDWR = 0x00000002; - /// mask for above modes - pub const ACCMODE = 0x00000003; - /// no delay - pub const NONBLOCK = 0x00000004; - /// set append mode - pub const APPEND = 0x00000008; - /// open with shared file lock - pub const SHLOCK = 0x00000010; - /// open with exclusive file lock - pub const EXLOCK = 0x00000020; - /// signal pgrp when data ready - pub const ASYNC = 0x00000040; - /// synchronous writes - pub const SYNC = 0x00000080; - /// don't follow symlinks on the last - pub const NOFOLLOW = 0x00000100; - /// create if nonexistent - pub const CREAT = 0x00000200; - /// truncate to zero length - pub const TRUNC = 0x00000400; - /// error if already exists - pub const EXCL = 0x00000800; - /// don't assign controlling terminal - pub const NOCTTY = 0x00008000; - /// write: I/O data completion - pub const DSYNC = SYNC; - /// read: I/O completion as for write - pub const RSYNC = SYNC; - /// fail if not a directory - pub const DIRECTORY = 0x20000; - /// set close on exec - pub const CLOEXEC = 0x10000; -}; - pub const F = struct { pub const DUPFD = 0; pub const GETFD = 1; @@ -1312,21 +1258,6 @@ pub const S = struct { } }; -pub const AT = struct { - /// Magic value that specify the use of the current working directory - /// to determine the target of relative file paths in the openat() and - /// similar syscalls. - pub const FDCWD = -100; - /// Check access using effective user and group ID - pub const EACCESS = 0x01; - /// Do not follow symbolic links - pub const SYMLINK_NOFOLLOW = 0x02; - /// Follow symbolic link - pub const SYMLINK_FOLLOW = 0x04; - /// Remove directory instead of file - pub const REMOVEDIR = 0x08; -}; - pub const HOST_NAME_MAX = 255; pub const IPPROTO = struct { diff --git a/lib/std/c/solaris.zig b/lib/std/c/solaris.zig index c533ef2ca5..db4afbcced 100644 --- a/lib/std/c/solaris.zig +++ b/lib/std/c/solaris.zig @@ -21,29 +21,6 @@ pub extern "c" fn sysconf(sc: c_int) i64; pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) c_int; pub extern "c" fn madvise(address: [*]u8, len: usize, advise: u32) c_int; -pub const pthread_mutex_t = extern struct { - flag1: u16 = 0, - flag2: u8 = 0, - ceiling: u8 = 0, - type: u16 = 0, - magic: u16 = 0x4d58, - lock: u64 = 0, - data: u64 = 0, -}; -pub const pthread_cond_t = extern struct { - flag: [4]u8 = [_]u8{0} ** 4, - type: u16 = 0, - magic: u16 = 0x4356, - data: u64 = 0, -}; -pub const pthread_rwlock_t = extern struct { - readers: i32 = 0, - type: u16 = 0, - magic: u16 = 0x5257, - mutex: pthread_mutex_t = .{}, - readercv: pthread_cond_t = .{}, - writercv: pthread_cond_t = .{}, -}; pub const pthread_attr_t = extern struct { mutexattr: ?*anyopaque = null, }; @@ -266,17 +243,13 @@ pub const MAXNAMLEN = 511; pub const dirent = extern struct { /// Inode number of entry. - d_ino: ino_t, + ino: ino_t, /// Offset of this entry on disk. - d_off: off_t, + off: off_t, /// Length of this record. - d_reclen: u16, + reclen: u16, /// File name. - d_name: [MAXNAMLEN:0]u8, - - pub fn reclen(self: dirent) u16 { - return self.d_reclen; - } + name: [MAXNAMLEN:0]u8, }; pub const SOCK = struct { @@ -708,32 +681,6 @@ pub const F = struct { pub const RMDNY = 0x4; }; -pub const O = struct { - pub const RDONLY = 0; - pub const WRONLY = 1; - pub const RDWR = 2; - pub const SEARCH = 0x200000; - pub const EXEC = 0x400000; - pub const NDELAY = 0x04; - pub const APPEND = 0x08; - pub const SYNC = 0x10; - pub const DSYNC = 0x40; - pub const RSYNC = 0x8000; - pub const NONBLOCK = 0x80; - pub const LARGEFILE = 0x2000; - - pub const CREAT = 0x100; - pub const TRUNC = 0x200; - pub const EXCL = 0x400; - pub const NOCTTY = 0x800; - pub const XATTR = 0x4000; - pub const NOFOLLOW = 0x20000; - pub const NOLINKS = 0x40000; - pub const CLOEXEC = 0x800000; - pub const DIRECTORY = 0x1000000; - pub const DIRECT = 0x2000000; -}; - pub const LOCK = struct { pub const SH = 1; pub const EX = 2; @@ -1430,23 +1377,6 @@ pub const S = struct { } }; -pub const AT = struct { - /// Magic value that specify the use of the current working directory - /// to determine the target of relative file paths in the openat() and - /// similar syscalls. - pub const FDCWD = @as(fd_t, @bitCast(@as(u32, 0xffd19553))); - - /// Do not follow symbolic links - pub const SYMLINK_NOFOLLOW = 0x1000; - /// Follow symbolic link - pub const SYMLINK_FOLLOW = 0x2000; - /// Remove directory instead of file - pub const REMOVEDIR = 0x1; - pub const TRIGGER = 0x2; - /// Check access using effective user and group ID - pub const EACCESS = 0x4; -}; - pub const POSIX_FADV = struct { pub const NORMAL = 0; pub const RANDOM = 1; diff --git a/lib/std/c/wasi.zig b/lib/std/c/wasi.zig index e1940054b6..95558787ae 100644 --- a/lib/std/c/wasi.zig +++ b/lib/std/c/wasi.zig @@ -1,6 +1,6 @@ +const builtin = @import("builtin"); const std = @import("../std.zig"); const wasi = std.os.wasi; -const FDFLAG = wasi.FDFLAG; extern threadlocal var errno: c_int; @@ -8,42 +8,82 @@ pub fn _errno() *c_int { return &errno; } -pub const AT = wasi.AT; -pub const CLOCK = wasi.CLOCK; -pub const E = wasi.E; -pub const IOV_MAX = wasi.IOV_MAX; -pub const LOCK = wasi.LOCK; -pub const S = wasi.S; -pub const STDERR_FILENO = wasi.STDERR_FILENO; -pub const STDIN_FILENO = wasi.STDIN_FILENO; -pub const STDOUT_FILENO = wasi.STDOUT_FILENO; +pub const mode_t = u32; +pub const time_t = i64; + +pub const timespec = extern struct { + tv_sec: time_t, + tv_nsec: isize, + + pub fn fromTimestamp(tm: wasi.timestamp_t) timespec { + const tv_sec: wasi.timestamp_t = tm / 1_000_000_000; + const tv_nsec = tm - tv_sec * 1_000_000_000; + return .{ + .tv_sec = @as(time_t, @intCast(tv_sec)), + .tv_nsec = @as(isize, @intCast(tv_nsec)), + }; + } + + pub fn toTimestamp(ts: timespec) wasi.timestamp_t { + return @as(wasi.timestamp_t, @intCast(ts.tv_sec * 1_000_000_000)) + + @as(wasi.timestamp_t, @intCast(ts.tv_nsec)); + } +}; + +pub const STDIN_FILENO = 0; +pub const STDOUT_FILENO = 1; +pub const STDERR_FILENO = 2; + +pub const E = wasi.errno_t; + +pub const CLOCK = wasi.clockid_t; +pub const IOV_MAX = 1024; +pub const LOCK = struct { + pub const SH = 0x1; + pub const EX = 0x2; + pub const NB = 0x4; + pub const UN = 0x8; +}; +pub const S = struct { + pub const IEXEC = @compileError("TODO audit this"); + pub const IFBLK = 0x6000; + pub const IFCHR = 0x2000; + pub const IFDIR = 0x4000; + pub const IFIFO = 0xc000; + pub const IFLNK = 0xa000; + pub const IFMT = IFBLK | IFCHR | IFDIR | IFIFO | IFLNK | IFREG | IFSOCK; + pub const IFREG = 0x8000; + /// There's no concept of UNIX domain socket but we define this value here + /// in order to line with other OSes. + pub const IFSOCK = 0x1; +}; pub const fd_t = wasi.fd_t; pub const pid_t = c_int; pub const uid_t = u32; pub const gid_t = u32; pub const off_t = i64; -pub const ino_t = wasi.ino_t; -pub const mode_t = wasi.mode_t; -pub const time_t = wasi.time_t; -pub const timespec = wasi.timespec; +pub const ino_t = wasi.inode_t; +pub const dev_t = wasi.device_t; +pub const nlink_t = c_ulonglong; +pub const blksize_t = c_long; +pub const blkcnt_t = c_longlong; pub const Stat = extern struct { - dev: i32, + dev: dev_t, ino: ino_t, - nlink: u64, - + nlink: nlink_t, mode: mode_t, uid: uid_t, gid: gid_t, - __pad0: isize, - rdev: i32, + __pad0: c_uint = 0, + rdev: dev_t, size: off_t, - blksize: i32, - blocks: i64, - + blksize: blksize_t, + blocks: blkcnt_t, atim: timespec, mtim: timespec, ctim: timespec, + __reserved: [3]c_longlong = [3]c_longlong{ 0, 0, 0 }, pub fn atime(self: @This()) timespec { return self.atim; @@ -56,30 +96,35 @@ pub const Stat = extern struct { pub fn ctime(self: @This()) timespec { return self.ctim; } -}; -/// Derived from -/// https://github.com/WebAssembly/wasi-libc/blob/main/expected/wasm32-wasi/predefined-macros.txt -pub const O = struct { - pub const ACCMODE = (EXEC | RDWR | SEARCH); - pub const APPEND = @as(u32, FDFLAG.APPEND); - pub const CLOEXEC = (0); - pub const CREAT = ((1 << 0) << 12); // = __WASI_OFLAGS_CREAT << 12 - pub const DIRECTORY = ((1 << 1) << 12); // = __WASI_OFLAGS_DIRECTORY << 12 - pub const DSYNC = @as(u32, FDFLAG.DSYNC); - pub const EXCL = ((1 << 2) << 12); // = __WASI_OFLAGS_EXCL << 12 - pub const EXEC = (0x02000000); - pub const NOCTTY = (0); - pub const NOFOLLOW = (0x01000000); - pub const NONBLOCK = @as(u32, FDFLAG.NONBLOCK); - pub const RDONLY = (0x04000000); - pub const RDWR = (RDONLY | WRONLY); - pub const RSYNC = @as(u32, FDFLAG.RSYNC); - pub const SEARCH = (0x08000000); - pub const SYNC = @as(u32, FDFLAG.SYNC); - pub const TRUNC = ((1 << 3) << 12); // = __WASI_OFLAGS_TRUNC << 12 - pub const TTY_INIT = (0); - pub const WRONLY = (0x10000000); + pub fn fromFilestat(stat: wasi.filestat_t) Stat { + return .{ + .dev = stat.dev, + .ino = stat.ino, + .mode = switch (stat.filetype) { + .UNKNOWN => 0, + .BLOCK_DEVICE => S.IFBLK, + .CHARACTER_DEVICE => S.IFCHR, + .DIRECTORY => S.IFDIR, + .REGULAR_FILE => S.IFREG, + .SOCKET_DGRAM => S.IFSOCK, + .SOCKET_STREAM => S.IFIFO, + .SYMBOLIC_LINK => S.IFLNK, + _ => 0, + }, + .nlink = stat.nlink, + .size = @intCast(stat.size), + .atim = timespec.fromTimestamp(stat.atim), + .mtim = timespec.fromTimestamp(stat.mtim), + .ctim = timespec.fromTimestamp(stat.ctim), + + .uid = 0, + .gid = 0, + .rdev = 0, + .blksize = 0, + .blocks = 0, + }; + } }; pub const F = struct { diff --git a/lib/std/c/windows.zig b/lib/std/c/windows.zig index b6ce03d21e..758f3dbadc 100644 --- a/lib/std/c/windows.zig +++ b/lib/std/c/windows.zig @@ -11,7 +11,6 @@ pub extern "c" fn _msize(memblock: ?*anyopaque) usize; // need to verify which of these is actually supported on windows pub extern "c" fn clock_getres(clk_id: c_int, tp: *timespec) c_int; pub extern "c" fn clock_gettime(clk_id: c_int, tp: *timespec) c_int; -pub extern "c" fn fstat(fd: fd_t, buf: *Stat) c_int; pub extern "c" fn getrusage(who: c_int, usage: *rusage) c_int; pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; @@ -200,11 +199,6 @@ pub const STRUNCATE = 80; pub const F_OK = 0; -/// Remove directory instead of unlinking file -pub const AT = struct { - pub const REMOVEDIR = 0x200; -}; - pub const in_port_t = u16; pub const sa_family_t = ws2_32.ADDRESS_FAMILY; pub const socklen_t = ws2_32.socklen_t; @@ -229,31 +223,4 @@ pub const SOL = ws2_32.SOL; pub const SO = ws2_32.SO; pub const PVD_CONFIG = ws2_32.PVD_CONFIG; -pub const O = struct { - pub const RDONLY = 0o0; - pub const WRONLY = 0o1; - pub const RDWR = 0o2; - - pub const CREAT = 0o100; - pub const EXCL = 0o200; - pub const NOCTTY = 0o400; - pub const TRUNC = 0o1000; - pub const APPEND = 0o2000; - pub const NONBLOCK = 0o4000; - pub const DSYNC = 0o10000; - pub const SYNC = 0o4010000; - pub const RSYNC = 0o4010000; - pub const DIRECTORY = 0o200000; - pub const NOFOLLOW = 0o400000; - pub const CLOEXEC = 0o2000000; - - pub const ASYNC = 0o20000; - pub const DIRECT = 0o40000; - pub const LARGEFILE = 0; - pub const NOATIME = 0o1000000; - pub const PATH = 0o10000000; - pub const TMPFILE = 0o20200000; - pub const NDELAY = NONBLOCK; -}; - pub const IFNAMESIZE = 30; diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index e006fc8c45..0071bd275e 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -495,7 +495,7 @@ pub const ChildProcess = struct { } fn spawnPosix(self: *ChildProcess) SpawnError!void { - const pipe_flags = 0; + const pipe_flags: os.O = .{}; const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe2(pipe_flags) else undefined; errdefer if (self.stdin_behavior == StdIo.Pipe) { destroyPipe(stdin_pipe); @@ -513,7 +513,7 @@ pub const ChildProcess = struct { const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); const dev_null_fd = if (any_ignore) - os.openZ("/dev/null", os.O.RDWR, 0) catch |err| switch (err) { + os.openZ("/dev/null", .{ .ACCMODE = .RDWR }, 0) catch |err| switch (err) { error.PathAlreadyExists => unreachable, error.NoSpaceLeft => unreachable, error.FileTooBig => unreachable, @@ -572,7 +572,7 @@ pub const ChildProcess = struct { // end with eventfd break :blk [2]os.fd_t{ fd, fd }; } else { - break :blk try os.pipe2(os.O.CLOEXEC); + break :blk try os.pipe2(.{ .CLOEXEC = true }); } }; errdefer destroyPipe(err_pipe); diff --git a/lib/std/compress/deflate/deflate_fast.zig b/lib/std/compress/deflate/deflate_fast.zig index 3a2668762e..4fda56bfb7 100644 --- a/lib/std/compress/deflate/deflate_fast.zig +++ b/lib/std/compress/deflate/deflate_fast.zig @@ -354,6 +354,10 @@ pub const DeflateFast = struct { }; test "best speed match 1/3" { + if (@import("builtin").os.tag == .wasi) { + // https://github.com/ziglang/zig/issues/18885 + return error.SkipZigTest; + } const expectEqual = std.testing.expectEqual; { @@ -450,6 +454,10 @@ test "best speed match 1/3" { } test "best speed match 2/3" { + if (@import("builtin").os.tag == .wasi) { + // https://github.com/ziglang/zig/issues/18885 + return error.SkipZigTest; + } const expectEqual = std.testing.expectEqual; { diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig index ee2b905aeb..ebfe8fe0ee 100644 --- a/lib/std/dynamic_library.zig +++ b/lib/std/dynamic_library.zig @@ -115,7 +115,7 @@ pub const ElfDynLib = struct { /// Trusts the file. Malicious file will be able to execute arbitrary code. pub fn open(path: []const u8) !ElfDynLib { - const fd = try os.open(path, 0, os.O.RDONLY | os.O.CLOEXEC); + const fd = try os.open(path, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); defer os.close(fd); const stat = try os.fstat(fd); diff --git a/lib/std/fs/Dir.zig b/lib/std/fs/Dir.zig index 8449d88d75..9ff22a3df9 100644 --- a/lib/std/fs/Dir.zig +++ b/lib/std/fs/Dir.zig @@ -62,16 +62,16 @@ pub const Iterator = switch (builtin.os.tag) { self.end_index = @as(usize, @intCast(rc)); } const darwin_entry = @as(*align(1) posix.system.dirent, @ptrCast(&self.buf[self.index])); - const next_index = self.index + darwin_entry.reclen(); + const next_index = self.index + darwin_entry.reclen; self.index = next_index; - const name = @as([*]u8, @ptrCast(&darwin_entry.d_name))[0..darwin_entry.d_namlen]; + const name = @as([*]u8, @ptrCast(&darwin_entry.name))[0..darwin_entry.namlen]; - if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..") or (darwin_entry.d_ino == 0)) { + if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..") or (darwin_entry.ino == 0)) { continue :start_over; } - const entry_kind: Entry.Kind = switch (darwin_entry.d_type) { + const entry_kind: Entry.Kind = switch (darwin_entry.type) { posix.DT.BLK => .block_device, posix.DT.CHR => .character_device, posix.DT.DIR => .directory, @@ -110,14 +110,14 @@ pub const Iterator = switch (builtin.os.tag) { self.end_index = @as(usize, @intCast(rc)); } const entry = @as(*align(1) posix.system.dirent, @ptrCast(&self.buf[self.index])); - const next_index = self.index + entry.reclen(); + const next_index = self.index + entry.reclen; self.index = next_index; - const name = mem.sliceTo(@as([*:0]u8, @ptrCast(&entry.d_name)), 0); + const name = mem.sliceTo(@as([*:0]u8, @ptrCast(&entry.name)), 0); if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) continue :start_over; - // Solaris dirent doesn't expose d_type, so we have to call stat to get it. + // Solaris dirent doesn't expose type, so we have to call stat to get it. const stat_info = posix.fstatat( self.dir.fd, name, @@ -174,23 +174,23 @@ pub const Iterator = switch (builtin.os.tag) { self.end_index = @as(usize, @intCast(rc)); } const bsd_entry = @as(*align(1) posix.system.dirent, @ptrCast(&self.buf[self.index])); - const next_index = self.index + bsd_entry.reclen(); + const next_index = self.index + bsd_entry.reclen; self.index = next_index; - const name = @as([*]u8, @ptrCast(&bsd_entry.d_name))[0..bsd_entry.d_namlen]; + const name = @as([*]u8, @ptrCast(&bsd_entry.name))[0..bsd_entry.namlen]; const skip_zero_fileno = switch (builtin.os.tag) { - // d_fileno=0 is used to mark invalid entries or deleted files. + // fileno=0 is used to mark invalid entries or deleted files. .openbsd, .netbsd => true, else => false, }; if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..") or - (skip_zero_fileno and bsd_entry.d_fileno == 0)) + (skip_zero_fileno and bsd_entry.fileno == 0)) { continue :start_over; } - const entry_kind: Entry.Kind = switch (bsd_entry.d_type) { + const entry_kind: Entry.Kind = switch (bsd_entry.type) { posix.DT.BLK => .block_device, posix.DT.CHR => .character_device, posix.DT.DIR => .directory, @@ -256,18 +256,18 @@ pub const Iterator = switch (builtin.os.tag) { self.end_index = @as(usize, @intCast(rc)); } const haiku_entry = @as(*align(1) posix.system.dirent, @ptrCast(&self.buf[self.index])); - const next_index = self.index + haiku_entry.reclen(); + const next_index = self.index + haiku_entry.reclen; self.index = next_index; - const name = mem.sliceTo(@as([*:0]u8, @ptrCast(&haiku_entry.d_name)), 0); + const name = mem.sliceTo(@as([*:0]u8, @ptrCast(&haiku_entry.name)), 0); - if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..") or (haiku_entry.d_ino == 0)) { + if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..") or (haiku_entry.ino == 0)) { continue :start_over; } var stat_info: posix.Stat = undefined; const rc = posix.system._kern_read_stat( self.dir.fd, - &haiku_entry.d_name, + &haiku_entry.name, false, &stat_info, 0, @@ -359,17 +359,17 @@ pub const Iterator = switch (builtin.os.tag) { self.end_index = rc; } const linux_entry = @as(*align(1) linux.dirent64, @ptrCast(&self.buf[self.index])); - const next_index = self.index + linux_entry.reclen(); + const next_index = self.index + linux_entry.reclen; self.index = next_index; - const name = mem.sliceTo(@as([*:0]u8, @ptrCast(&linux_entry.d_name)), 0); + const name = mem.sliceTo(@as([*:0]u8, @ptrCast(&linux_entry.name)), 0); // skip . and .. entries if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { continue :start_over; } - const entry_kind: Entry.Kind = switch (linux_entry.d_type) { + const entry_kind: Entry.Kind = switch (linux_entry.type) { linux.DT.BLK => .block_device, linux.DT.CHR => .character_device, linux.DT.DIR => .directory, @@ -525,23 +525,23 @@ pub const Iterator = switch (builtin.os.tag) { const entry = @as(*align(1) w.dirent_t, @ptrCast(&self.buf[self.index])); const entry_size = @sizeOf(w.dirent_t); const name_index = self.index + entry_size; - if (name_index + entry.d_namlen > self.end_index) { + if (name_index + entry.namlen > self.end_index) { // This case, the name is truncated, so we need to call readdir to store the entire name. self.end_index = self.index; // Force fd_readdir in the next loop. continue :start_over; } - const name = self.buf[name_index .. name_index + entry.d_namlen]; + const name = self.buf[name_index .. name_index + entry.namlen]; - const next_index = name_index + entry.d_namlen; + const next_index = name_index + entry.namlen; self.index = next_index; - self.cookie = entry.d_next; + self.cookie = entry.next; // skip . and .. entries if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { continue :start_over; } - const entry_kind: Entry.Kind = switch (entry.d_type) { + const entry_kind: Entry.Kind = switch (entry.type) { .BLOCK_DEVICE => .block_device, .CHARACTER_DEVICE => .character_device, .DIRECTORY => .directory, @@ -764,81 +764,79 @@ pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.Ope const path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path); return self.openFileW(path_w.span(), flags); } - if (builtin.os.tag == .wasi and !builtin.link_libc) { - return self.openFileWasi(sub_path, flags); + if (builtin.os.tag == .wasi) { + var base: std.os.wasi.rights_t = .{}; + if (flags.isRead()) { + base.FD_READ = true; + base.FD_TELL = true; + base.FD_SEEK = true; + base.FD_FILESTAT_GET = true; + } + if (flags.isWrite()) { + base.FD_WRITE = true; + base.FD_TELL = true; + base.FD_SEEK = true; + base.FD_DATASYNC = true; + base.FD_FDSTAT_SET_FLAGS = true; + base.FD_SYNC = true; + base.FD_ALLOCATE = true; + base.FD_ADVISE = true; + base.FD_FILESTAT_SET_TIMES = true; + base.FD_FILESTAT_SET_SIZE = true; + } + const fd = try posix.openatWasi(self.fd, sub_path, .{}, .{}, .{}, base, .{}); + return .{ .handle = fd }; } const path_c = try posix.toPosixPath(sub_path); return self.openFileZ(&path_c, flags); } -/// Same as `openFile` but WASI only. -pub fn openFileWasi(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { - const w = std.os.wasi; - var fdflags: w.fdflags_t = 0x0; - var base: w.rights_t = 0x0; - if (flags.isRead()) { - base |= w.RIGHT.FD_READ | w.RIGHT.FD_TELL | w.RIGHT.FD_SEEK | w.RIGHT.FD_FILESTAT_GET; - } - if (flags.isWrite()) { - fdflags |= w.FDFLAG.APPEND; - base |= w.RIGHT.FD_WRITE | - w.RIGHT.FD_TELL | - w.RIGHT.FD_SEEK | - w.RIGHT.FD_DATASYNC | - w.RIGHT.FD_FDSTAT_SET_FLAGS | - w.RIGHT.FD_SYNC | - w.RIGHT.FD_ALLOCATE | - w.RIGHT.FD_ADVISE | - w.RIGHT.FD_FILESTAT_SET_TIMES | - w.RIGHT.FD_FILESTAT_SET_SIZE; - } - const fd = try posix.openatWasi(self.fd, sub_path, 0x0, 0x0, fdflags, base, 0x0); - return File{ .handle = fd }; -} - /// Same as `openFile` but the path parameter is null-terminated. pub fn openFileZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File { - if (builtin.os.tag == .windows) { - const path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, sub_path); - return self.openFileW(path_w.span(), flags); + switch (builtin.os.tag) { + .windows => { + const path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, sub_path); + return self.openFileW(path_w.span(), flags); + }, + .wasi => { + return openFile(self, mem.sliceTo(sub_path, 0), flags); + }, + else => {}, } - var os_flags: u32 = 0; - if (@hasDecl(posix.O, "CLOEXEC")) os_flags = posix.O.CLOEXEC; + var os_flags: posix.O = .{ + .ACCMODE = switch (flags.mode) { + .read_only => .RDONLY, + .write_only => .WRONLY, + .read_write => .RDWR, + }, + }; + if (@hasField(posix.O, "CLOEXEC")) os_flags.CLOEXEC = true; + if (@hasField(posix.O, "LARGEFILE")) os_flags.LARGEFILE = true; + if (@hasField(posix.O, "NOCTTY")) os_flags.NOCTTY = !flags.allow_ctty; // Use the O locking flags if the os supports them to acquire the lock // atomically. - const has_flock_open_flags = @hasDecl(posix.O, "EXLOCK"); + const has_flock_open_flags = @hasField(posix.O, "EXLOCK"); if (has_flock_open_flags) { - // Note that the O.NONBLOCK flag is removed after the openat() call + // Note that the NONBLOCK flag is removed after the openat() call // is successful. - const nonblocking_lock_flag: u32 = if (flags.lock_nonblocking) - posix.O.NONBLOCK - else - 0; - os_flags |= switch (flags.lock) { - .none => @as(u32, 0), - .shared => posix.O.SHLOCK | nonblocking_lock_flag, - .exclusive => posix.O.EXLOCK | nonblocking_lock_flag, - }; + switch (flags.lock) { + .none => {}, + .shared => { + os_flags.SHLOCK = true; + os_flags.NONBLOCK = flags.lock_nonblocking; + }, + .exclusive => { + os_flags.EXLOCK = true; + os_flags.NONBLOCK = flags.lock_nonblocking; + }, + } } - if (@hasDecl(posix.O, "LARGEFILE")) { - os_flags |= posix.O.LARGEFILE; - } - if (@hasDecl(posix.O, "NOCTTY") and !flags.allow_ctty) { - os_flags |= posix.O.NOCTTY; - } - os_flags |= switch (flags.mode) { - .read_only => @as(u32, posix.O.RDONLY), - .write_only => @as(u32, posix.O.WRONLY), - .read_write => @as(u32, posix.O.RDWR), - }; const fd = try posix.openatZ(self.fd, sub_path, os_flags, 0); errdefer posix.close(fd); - // WASI doesn't have posix.flock so we intetinally check OS prior to the inner if block - // since it is not compiltime-known and we need to avoid undefined symbol in Wasm. - if (@hasDecl(posix.system, "LOCK") and builtin.target.os.tag != .wasi) { + if (@hasDecl(posix.system, "LOCK")) { if (!has_flock_open_flags and flags.lock != .none) { // TODO: integrate async I/O const lock_nonblocking: i32 = if (flags.lock_nonblocking) posix.LOCK.NB else 0; @@ -859,7 +857,7 @@ pub fn openFileZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File error.LockedRegionLimitExceeded => unreachable, else => |e| return e, }; - fl_flags &= ~@as(usize, posix.O.NONBLOCK); + fl_flags &= ~@as(usize, 1 << @bitOffsetOf(posix.O, "NONBLOCK")); _ = posix.fcntl(fd, posix.F.SETFL, fl_flags) catch |err| switch (err) { error.FileBusy => unreachable, error.Locked => unreachable, @@ -870,7 +868,7 @@ pub fn openFileZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File }; } - return File{ .handle = fd }; + return .{ .handle = fd }; } /// Same as `openFile` but Windows-only and the path parameter is @@ -918,83 +916,81 @@ pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File const path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sub_path); return self.createFileW(path_w.span(), flags); } - if (builtin.os.tag == .wasi and !builtin.link_libc) { - return self.createFileWasi(sub_path, flags); + if (builtin.os.tag == .wasi) { + return .{ + .handle = try posix.openatWasi(self.fd, sub_path, .{}, .{ + .CREAT = true, + .TRUNC = flags.truncate, + .EXCL = flags.exclusive, + }, .{}, .{ + .FD_READ = flags.read, + .FD_WRITE = true, + .FD_DATASYNC = true, + .FD_SEEK = true, + .FD_TELL = true, + .FD_FDSTAT_SET_FLAGS = true, + .FD_SYNC = true, + .FD_ALLOCATE = true, + .FD_ADVISE = true, + .FD_FILESTAT_SET_TIMES = true, + .FD_FILESTAT_SET_SIZE = true, + .FD_FILESTAT_GET = true, + }, .{}), + }; } const path_c = try posix.toPosixPath(sub_path); return self.createFileZ(&path_c, flags); } -/// Same as `createFile` but WASI only. -pub fn createFileWasi(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { - const w = std.os.wasi; - var oflags = w.O.CREAT; - var base: w.rights_t = w.RIGHT.FD_WRITE | - w.RIGHT.FD_DATASYNC | - w.RIGHT.FD_SEEK | - w.RIGHT.FD_TELL | - w.RIGHT.FD_FDSTAT_SET_FLAGS | - w.RIGHT.FD_SYNC | - w.RIGHT.FD_ALLOCATE | - w.RIGHT.FD_ADVISE | - w.RIGHT.FD_FILESTAT_SET_TIMES | - w.RIGHT.FD_FILESTAT_SET_SIZE | - w.RIGHT.FD_FILESTAT_GET; - if (flags.read) { - base |= w.RIGHT.FD_READ; - } - if (flags.truncate) { - oflags |= w.O.TRUNC; - } - if (flags.exclusive) { - oflags |= w.O.EXCL; - } - const fd = try posix.openatWasi(self.fd, sub_path, 0x0, oflags, 0x0, base, 0x0); - return File{ .handle = fd }; -} - /// Same as `createFile` but the path parameter is null-terminated. pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File { - if (builtin.os.tag == .windows) { - const path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, sub_path_c); - return self.createFileW(path_w.span(), flags); + switch (builtin.os.tag) { + .windows => { + const path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, sub_path_c); + return self.createFileW(path_w.span(), flags); + }, + .wasi => { + return createFile(self, mem.sliceTo(sub_path_c, 0), flags); + }, + else => {}, } - // Use the O locking flags if the os supports them to acquire the lock - // atomically. - const has_flock_open_flags = @hasDecl(posix.O, "EXLOCK"); - // Note that the O.NONBLOCK flag is removed after the openat() call - // is successful. - const nonblocking_lock_flag: u32 = if (has_flock_open_flags and flags.lock_nonblocking) - posix.O.NONBLOCK - else - 0; - const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) { - .none => @as(u32, 0), - .shared => posix.O.SHLOCK | nonblocking_lock_flag, - .exclusive => posix.O.EXLOCK | nonblocking_lock_flag, - } else 0; + var os_flags: std.os.O = .{ + .ACCMODE = if (flags.read) .RDWR else .WRONLY, + .CREAT = true, + .TRUNC = flags.truncate, + .EXCL = flags.exclusive, + }; + if (@hasField(posix.O, "LARGEFILE")) os_flags.LARGEFILE = true; + if (@hasField(posix.O, "CLOEXEC")) os_flags.CLOEXEC = true; + + // Use the O locking flags if the os supports them to acquire the lock + // atomically. Note that the NONBLOCK flag is removed after the openat() + // call is successful. + const has_flock_open_flags = @hasField(posix.O, "EXLOCK"); + if (has_flock_open_flags) switch (flags.lock) { + .none => {}, + .shared => { + os_flags.SHLOCK = true; + os_flags.NONBLOCK = flags.lock_nonblocking; + }, + .exclusive => { + os_flags.EXLOCK = true; + os_flags.NONBLOCK = flags.lock_nonblocking; + }, + }; - const O_LARGEFILE = if (@hasDecl(posix.O, "LARGEFILE")) posix.O.LARGEFILE else 0; - const os_flags = lock_flag | O_LARGEFILE | posix.O.CREAT | posix.O.CLOEXEC | - (if (flags.truncate) @as(u32, posix.O.TRUNC) else 0) | - (if (flags.read) @as(u32, posix.O.RDWR) else posix.O.WRONLY) | - (if (flags.exclusive) @as(u32, posix.O.EXCL) else 0); const fd = try posix.openatZ(self.fd, sub_path_c, os_flags, flags.mode); errdefer posix.close(fd); - // WASI doesn't have posix.flock so we intetinally check OS prior to the inner if block - // since it is not compiltime-known and we need to avoid undefined symbol in Wasm. - if (builtin.target.os.tag != .wasi) { - if (!has_flock_open_flags and flags.lock != .none) { - // TODO: integrate async I/O - const lock_nonblocking: i32 = if (flags.lock_nonblocking) posix.LOCK.NB else 0; - try posix.flock(fd, switch (flags.lock) { - .none => unreachable, - .shared => posix.LOCK.SH | lock_nonblocking, - .exclusive => posix.LOCK.EX | lock_nonblocking, - }); - } + if (!has_flock_open_flags and flags.lock != .none) { + // TODO: integrate async I/O + const lock_nonblocking: i32 = if (flags.lock_nonblocking) posix.LOCK.NB else 0; + try posix.flock(fd, switch (flags.lock) { + .none => unreachable, + .shared => posix.LOCK.SH | lock_nonblocking, + .exclusive => posix.LOCK.EX | lock_nonblocking, + }); } if (has_flock_open_flags and flags.lock_nonblocking) { @@ -1006,7 +1002,7 @@ pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags error.LockedRegionLimitExceeded => unreachable, else => |e| return e, }; - fl_flags &= ~@as(usize, posix.O.NONBLOCK); + fl_flags &= ~@as(usize, 1 << @bitOffsetOf(posix.O, "NONBLOCK")); _ = posix.fcntl(fd, posix.F.SETFL, fl_flags) catch |err| switch (err) { error.FileBusy => unreachable, error.Locked => unreachable, @@ -1017,7 +1013,7 @@ pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags }; } - return File{ .handle = fd }; + return .{ .handle = fd }; } /// Same as `createFile` but Windows-only and the path parameter is @@ -1210,10 +1206,18 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) ![]u8 { return self.realpathW(pathname_w.span(), out_buffer); } - const flags = if (builtin.os.tag == .linux) - posix.O.PATH | posix.O.NONBLOCK | posix.O.CLOEXEC - else - posix.O.NONBLOCK | posix.O.CLOEXEC; + const flags: posix.O = switch (builtin.os.tag) { + .linux => .{ + .NONBLOCK = true, + .CLOEXEC = true, + .PATH = true, + }, + else => .{ + .NONBLOCK = true, + .CLOEXEC = true, + }, + }; + const fd = posix.openatZ(self.fd, pathname, flags, 0) catch |err| switch (err) { error.FileLocksNotSupported => unreachable, else => |e| return e, @@ -1334,76 +1338,85 @@ pub const OpenDirOptions = struct { /// /// Asserts that the path parameter has no null bytes. pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir { - if (builtin.os.tag == .windows) { - const sub_path_w = try posix.windows.sliceToPrefixedFileW(self.fd, sub_path); - return self.openDirW(sub_path_w.span().ptr, args); - } else if (builtin.os.tag == .wasi and !builtin.link_libc) { - return self.openDirWasi(sub_path, args); - } else { - const sub_path_c = try posix.toPosixPath(sub_path); - return self.openDirZ(&sub_path_c, args); - } -} + switch (builtin.os.tag) { + .windows => { + const sub_path_w = try posix.windows.sliceToPrefixedFileW(self.fd, sub_path); + return self.openDirW(sub_path_w.span().ptr, args); + }, + .wasi => { + var base: std.os.wasi.rights_t = .{ + .FD_FILESTAT_GET = true, + .FD_FDSTAT_SET_FLAGS = true, + .FD_FILESTAT_SET_TIMES = true, + }; + if (args.access_sub_paths) { + base.FD_READDIR = true; + base.PATH_CREATE_DIRECTORY = true; + base.PATH_CREATE_FILE = true; + base.PATH_LINK_SOURCE = true; + base.PATH_LINK_TARGET = true; + base.PATH_OPEN = true; + base.PATH_READLINK = true; + base.PATH_RENAME_SOURCE = true; + base.PATH_RENAME_TARGET = true; + base.PATH_FILESTAT_GET = true; + base.PATH_FILESTAT_SET_SIZE = true; + base.PATH_FILESTAT_SET_TIMES = true; + base.PATH_SYMLINK = true; + base.PATH_REMOVE_DIRECTORY = true; + base.PATH_UNLINK_FILE = true; + } -/// Same as `openDir` except only WASI. -pub fn openDirWasi(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir { - const w = std.os.wasi; - var base: w.rights_t = w.RIGHT.FD_FILESTAT_GET | w.RIGHT.FD_FDSTAT_SET_FLAGS | w.RIGHT.FD_FILESTAT_SET_TIMES; - if (args.access_sub_paths) { - base |= w.RIGHT.FD_READDIR | - w.RIGHT.PATH_CREATE_DIRECTORY | - w.RIGHT.PATH_CREATE_FILE | - w.RIGHT.PATH_LINK_SOURCE | - w.RIGHT.PATH_LINK_TARGET | - w.RIGHT.PATH_OPEN | - w.RIGHT.PATH_READLINK | - w.RIGHT.PATH_RENAME_SOURCE | - w.RIGHT.PATH_RENAME_TARGET | - w.RIGHT.PATH_FILESTAT_GET | - w.RIGHT.PATH_FILESTAT_SET_SIZE | - w.RIGHT.PATH_FILESTAT_SET_TIMES | - w.RIGHT.PATH_SYMLINK | - w.RIGHT.PATH_REMOVE_DIRECTORY | - w.RIGHT.PATH_UNLINK_FILE; + const result = posix.openatWasi( + self.fd, + sub_path, + .{ .SYMLINK_FOLLOW = !args.no_follow }, + .{ .DIRECTORY = true }, + .{}, + base, + base, + ); + const fd = result catch |err| switch (err) { + error.FileTooBig => unreachable, // can't happen for directories + error.IsDir => unreachable, // we're setting DIRECTORY + error.NoSpaceLeft => unreachable, // not setting CREAT + error.PathAlreadyExists => unreachable, // not setting CREAT + error.FileLocksNotSupported => unreachable, // locking folders is not supported + error.WouldBlock => unreachable, // can't happen for directories + error.FileBusy => unreachable, // can't happen for directories + else => |e| return e, + }; + return .{ .fd = fd }; + }, + else => { + const sub_path_c = try posix.toPosixPath(sub_path); + return self.openDirZ(&sub_path_c, args); + }, } - const symlink_flags: w.lookupflags_t = if (args.no_follow) 0x0 else w.LOOKUP_SYMLINK_FOLLOW; - // TODO do we really need all the rights here? - const inheriting: w.rights_t = w.RIGHT.ALL ^ w.RIGHT.SOCK_SHUTDOWN; - - const result = posix.openatWasi( - self.fd, - sub_path, - symlink_flags, - w.O.DIRECTORY, - 0x0, - base, - inheriting, - ); - const fd = result catch |err| switch (err) { - error.FileTooBig => unreachable, // can't happen for directories - error.IsDir => unreachable, // we're providing O.DIRECTORY - error.NoSpaceLeft => unreachable, // not providing O.CREAT - error.PathAlreadyExists => unreachable, // not providing O.CREAT - error.FileLocksNotSupported => unreachable, // locking folders is not supported - error.WouldBlock => unreachable, // can't happen for directories - error.FileBusy => unreachable, // can't happen for directories - else => |e| return e, - }; - return Dir{ .fd = fd }; } /// Same as `openDir` except the parameter is null-terminated. pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir { - if (builtin.os.tag == .windows) { - const sub_path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, sub_path_c); - return self.openDirW(sub_path_w.span().ptr, args); - } - const symlink_flags: u32 = if (args.no_follow) posix.O.NOFOLLOW else 0x0; - if (!args.iterate) { - const O_PATH = if (@hasDecl(posix.O, "PATH")) posix.O.PATH else 0; - return self.openDirFlagsZ(sub_path_c, posix.O.DIRECTORY | posix.O.RDONLY | posix.O.CLOEXEC | O_PATH | symlink_flags); - } else { - return self.openDirFlagsZ(sub_path_c, posix.O.DIRECTORY | posix.O.RDONLY | posix.O.CLOEXEC | symlink_flags); + switch (builtin.os.tag) { + .windows => { + const sub_path_w = try std.os.windows.cStrToPrefixedFileW(self.fd, sub_path_c); + return self.openDirW(sub_path_w.span().ptr, args); + }, + .wasi => { + return openDir(self, mem.sliceTo(sub_path_c, 0), args); + }, + else => { + var symlink_flags: posix.O = .{ + .ACCMODE = .RDONLY, + .NOFOLLOW = args.no_follow, + .DIRECTORY = true, + .CLOEXEC = true, + }; + if (@hasField(posix.O, "PATH") and !args.iterate) + symlink_flags.PATH = true; + + return self.openDirFlagsZ(sub_path_c, symlink_flags); + }, } } @@ -1422,13 +1435,14 @@ pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenDirOptions) Ope return dir; } -/// `flags` must contain `posix.O.DIRECTORY`. -fn openDirFlagsZ(self: Dir, sub_path_c: [*:0]const u8, flags: u32) OpenError!Dir { +/// Asserts `flags` has `DIRECTORY` set. +fn openDirFlagsZ(self: Dir, sub_path_c: [*:0]const u8, flags: posix.O) OpenError!Dir { + assert(flags.DIRECTORY); const fd = posix.openatZ(self.fd, sub_path_c, flags, 0) catch |err| switch (err) { error.FileTooBig => unreachable, // can't happen for directories - error.IsDir => unreachable, // we're providing O.DIRECTORY - error.NoSpaceLeft => unreachable, // not providing O.CREAT - error.PathAlreadyExists => unreachable, // not providing O.CREAT + error.IsDir => unreachable, // we're setting DIRECTORY + error.NoSpaceLeft => unreachable, // not setting CREAT + error.PathAlreadyExists => unreachable, // not setting CREAT error.FileLocksNotSupported => unreachable, // locking folders is not supported error.WouldBlock => unreachable, // can't happen for directories error.FileBusy => unreachable, // can't happen for directories @@ -2446,8 +2460,8 @@ pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat { return file.stat(); } if (builtin.os.tag == .wasi and !builtin.link_libc) { - const st = try posix.fstatatWasi(self.fd, sub_path, posix.wasi.LOOKUP_SYMLINK_FOLLOW); - return Stat.fromSystem(st); + const st = try posix.fstatat_wasi(self.fd, sub_path, .{ .SYMLINK_FOLLOW = true }); + return Stat.fromWasi(st); } const st = try posix.fstatat(self.fd, sub_path, 0); return Stat.fromSystem(st); @@ -2507,3 +2521,4 @@ const posix = std.os; const mem = std.mem; const fs = std.fs; const Allocator = std.mem.Allocator; +const assert = std.debug.assert; diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 576222ce73..4371cc4768 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -290,49 +290,59 @@ pub const Stat = struct { /// Last status/metadata change time in nanoseconds, relative to UTC 1970-01-01. ctime: i128, - pub fn fromSystem(st: posix.system.Stat) Stat { + pub fn fromSystem(st: posix.Stat) Stat { const atime = st.atime(); const mtime = st.mtime(); const ctime = st.ctime(); - const kind: Kind = if (builtin.os.tag == .wasi and !builtin.link_libc) switch (st.filetype) { - .BLOCK_DEVICE => .block_device, - .CHARACTER_DEVICE => .character_device, - .DIRECTORY => .directory, - .SYMBOLIC_LINK => .sym_link, - .REGULAR_FILE => .file, - .SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket, - else => .unknown, - } else blk: { - const m = st.mode & posix.S.IFMT; - switch (m) { - posix.S.IFBLK => break :blk .block_device, - posix.S.IFCHR => break :blk .character_device, - posix.S.IFDIR => break :blk .directory, - posix.S.IFIFO => break :blk .named_pipe, - posix.S.IFLNK => break :blk .sym_link, - posix.S.IFREG => break :blk .file, - posix.S.IFSOCK => break :blk .unix_domain_socket, - else => {}, - } - if (builtin.os.tag.isSolarish()) switch (m) { - posix.S.IFDOOR => break :blk .door, - posix.S.IFPORT => break :blk .event_port, - else => {}, - }; - - break :blk .unknown; - }; - - return Stat{ + return .{ .inode = st.ino, - .size = @as(u64, @bitCast(st.size)), + .size = @bitCast(st.size), .mode = st.mode, - .kind = kind, + .kind = k: { + const m = st.mode & posix.S.IFMT; + switch (m) { + posix.S.IFBLK => break :k .block_device, + posix.S.IFCHR => break :k .character_device, + posix.S.IFDIR => break :k .directory, + posix.S.IFIFO => break :k .named_pipe, + posix.S.IFLNK => break :k .sym_link, + posix.S.IFREG => break :k .file, + posix.S.IFSOCK => break :k .unix_domain_socket, + else => {}, + } + if (builtin.os.tag.isSolarish()) switch (m) { + posix.S.IFDOOR => break :k .door, + posix.S.IFPORT => break :k .event_port, + else => {}, + }; + + break :k .unknown; + }, .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec, .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec, .ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec, }; } + + pub fn fromWasi(st: std.os.wasi.filestat_t) Stat { + return .{ + .inode = st.ino, + .size = @bitCast(st.size), + .mode = 0, + .kind = switch (st.filetype) { + .BLOCK_DEVICE => .block_device, + .CHARACTER_DEVICE => .character_device, + .DIRECTORY => .directory, + .SYMBOLIC_LINK => .sym_link, + .REGULAR_FILE => .file, + .SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket, + else => .unknown, + }, + .atime = st.atim, + .mtime = st.mtim, + .ctime = st.ctim, + }; + } }; pub const StatError = posix.FStatError; @@ -355,7 +365,7 @@ pub fn stat(self: File) StatError!Stat { .ACCESS_DENIED => return error.AccessDenied, else => return windows.unexpectedStatus(rc), } - return Stat{ + return .{ .inode = info.InternalInformation.IndexNumber, .size = @as(u64, @bitCast(info.StandardInformation.EndOfFile)), .mode = 0, @@ -385,6 +395,11 @@ pub fn stat(self: File) StatError!Stat { }; } + if (builtin.os.tag == .wasi and !builtin.link_libc) { + const st = try posix.fstat_wasi(self.handle); + return Stat.fromWasi(st); + } + const st = try posix.fstat(self.handle); return Stat.fromSystem(st); } @@ -576,10 +591,11 @@ pub fn setPermissions(self: File, permissions: Permissions) SetPermissionsError! /// Cross-platform representation of file metadata. /// Platform-specific functionality is available through the `inner` field. pub const Metadata = struct { - /// You may use the `inner` field to use platform-specific functionality + /// Exposes platform-specific functionality. inner: switch (builtin.os.tag) { .windows => MetadataWindows, .linux => MetadataLinux, + .wasi => MetadataWasi, else => MetadataUnix, }, @@ -628,12 +644,12 @@ pub const MetadataUnix = struct { /// Returns the size of the file pub fn size(self: Self) u64 { - return @as(u64, @intCast(self.stat.size)); + return @intCast(self.stat.size); } /// Returns a `Permissions` struct, representing the permissions on the file pub fn permissions(self: Self) Permissions { - return Permissions{ .inner = PermissionsUnix{ .mode = self.stat.mode } }; + return .{ .inner = .{ .mode = self.stat.mode } }; } /// Returns the `Kind` of the file @@ -756,6 +772,42 @@ pub const MetadataLinux = struct { } }; +pub const MetadataWasi = struct { + stat: std.os.wasi.filestat_t, + + pub fn size(self: @This()) u64 { + return self.stat.size; + } + + pub fn permissions(self: @This()) Permissions { + return .{ .inner = .{ .mode = self.stat.mode } }; + } + + pub fn kind(self: @This()) Kind { + return switch (self.stat.filetype) { + .BLOCK_DEVICE => .block_device, + .CHARACTER_DEVICE => .character_device, + .DIRECTORY => .directory, + .SYMBOLIC_LINK => .sym_link, + .REGULAR_FILE => .file, + .SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket, + else => .unknown, + }; + } + + pub fn accessed(self: @This()) i128 { + return self.stat.atim; + } + + pub fn modified(self: @This()) i128 { + return self.stat.mtim; + } + + pub fn created(self: @This()) ?i128 { + return self.stat.ctim; + } +}; + pub const MetadataWindows = struct { attributes: windows.DWORD, reparse_tag: windows.DWORD, @@ -773,7 +825,7 @@ pub const MetadataWindows = struct { /// Returns a `Permissions` struct, representing the permissions on the file pub fn permissions(self: Self) Permissions { - return Permissions{ .inner = PermissionsWindows{ .attributes = self.attributes } }; + return .{ .inner = .{ .attributes = self.attributes } }; } /// Returns the `Kind` of the file. @@ -811,7 +863,7 @@ pub const MetadataWindows = struct { pub const MetadataError = posix.FStatError; pub fn metadata(self: File) MetadataError!Metadata { - return Metadata{ + return .{ .inner = switch (builtin.os.tag) { .windows => blk: { var io_status_block: windows.IO_STATUS_BLOCK = undefined; @@ -846,7 +898,7 @@ pub fn metadata(self: File) MetadataError!Metadata { break :reparse_blk 0; }; - break :blk MetadataWindows{ + break :blk .{ .attributes = info.BasicInformation.FileAttributes, .reparse_tag = reparse_tag, ._size = @as(u64, @bitCast(info.StandardInformation.EndOfFile)), @@ -887,16 +939,12 @@ pub fn metadata(self: File) MetadataError!Metadata { else => |err| return posix.unexpectedErrno(err), } - break :blk MetadataLinux{ + break :blk .{ .statx = stx, }; }, - else => blk: { - const st = try posix.fstat(self.handle); - break :blk MetadataUnix{ - .stat = st, - }; - }, + .wasi => .{ .stat = try posix.fstat_wasi(self.handle) }, + else => .{ .stat = try posix.fstat(self.handle) }, }, }; } diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index b84d0ce0f4..312e0c398f 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -242,7 +242,12 @@ test "File.stat on a File that is a symlink returns Kind.sym_link" { const sub_path_c = try os.toPosixPath("symlink"); // the O_NOFOLLOW | O_PATH combination can obtain a fd to a symlink // note that if O_DIRECTORY is set, then this will error with ENOTDIR - const flags = os.O.NOFOLLOW | os.O.PATH | os.O.RDONLY | os.O.CLOEXEC; + const flags: os.O = .{ + .NOFOLLOW = true, + .PATH = true, + .ACCMODE = .RDONLY, + .CLOEXEC = true, + }; const fd = try os.openatZ(ctx.dir.fd, &sub_path_c, flags, 0); break :linux_symlink Dir{ .fd = fd }; }, diff --git a/lib/std/os.zig b/lib/std/os.zig index c963ca7f3b..a5c8d8d35e 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -24,7 +24,6 @@ const elf = std.elf; const fs = std.fs; const dl = @import("dynamic_library.zig"); const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES; -const is_windows = builtin.os.tag == .windows; pub const darwin = std.c; pub const dragonfly = std.c; @@ -62,16 +61,21 @@ test { /// When not linking libc, it is the OS-specific system interface. pub const system = if (@hasDecl(root, "os") and root.os != @This()) root.os.system -else if (builtin.link_libc or is_windows) +else if (use_libc) std.c else switch (builtin.os.tag) { .linux => linux, .plan9 => plan9, - .wasi => wasi, .uefi => uefi, else => struct {}, }; +/// Whether to use libc for the POSIX API layer. +const use_libc = builtin.link_libc or switch (builtin.os.tag) { + .windows, .wasi => true, + else => false, +}; + pub const AF = system.AF; pub const AF_SUN = system.AF_SUN; pub const ARCH = system.ARCH; @@ -87,10 +91,7 @@ pub const F = system.F; pub const FD_CLOEXEC = system.FD_CLOEXEC; pub const Flock = system.Flock; pub const HOST_NAME_MAX = system.HOST_NAME_MAX; -pub const HW = switch (builtin.os.tag) { - .openbsd => system.HW, - else => .{}, -}; +pub const HW = system.HW; pub const IFNAMESIZE = system.IFNAMESIZE; pub const IOV_MAX = system.IOV_MAX; pub const IPPROTO = system.IPPROTO; @@ -105,19 +106,13 @@ pub const MFD = system.MFD; pub const MMAP2_UNIT = system.MMAP2_UNIT; pub const MSG = system.MSG; pub const NAME_MAX = system.NAME_MAX; -pub const O = switch (builtin.os.tag) { - // We want to expose the POSIX-like OFLAGS, so we use std.c.wasi.O instead - // of std.os.wasi.O, which is for non-POSIX-like `wasi.path_open`, etc. - .wasi => std.c.O, - else => system.O, -}; +pub const O = system.O; pub const PATH_MAX = system.PATH_MAX; pub const POLL = system.POLL; pub const POSIX_FADV = system.POSIX_FADV; pub const PR = system.PR; pub const PROT = system.PROT; pub const REG = system.REG; -pub const RIGHT = system.RIGHT; pub const RLIM = system.RLIM; pub const RR = system.RR; pub const S = system.S; @@ -151,12 +146,9 @@ pub const dl_phdr_info = system.dl_phdr_info; pub const empty_sigset = system.empty_sigset; pub const filled_sigset = system.filled_sigset; pub const fd_t = system.fd_t; -pub const fdflags_t = system.fdflags_t; -pub const fdstat_t = system.fdstat_t; pub const gid_t = system.gid_t; pub const ifreq = system.ifreq; pub const ino_t = system.ino_t; -pub const lookupflags_t = system.lookupflags_t; pub const mcontext_t = system.mcontext_t; pub const mode_t = system.mode_t; pub const msghdr = system.msghdr; @@ -164,14 +156,12 @@ pub const msghdr_const = system.msghdr_const; pub const nfds_t = system.nfds_t; pub const nlink_t = system.nlink_t; pub const off_t = system.off_t; -pub const oflags_t = system.oflags_t; pub const pid_t = system.pid_t; pub const pollfd = system.pollfd; pub const port_t = system.port_t; pub const port_event = system.port_event; pub const port_notify = system.port_notify; pub const file_obj = system.file_obj; -pub const rights_t = system.rights_t; pub const rlim_t = system.rlim_t; pub const rlimit = system.rlimit; pub const rlimit_resource = system.rlimit_resource; @@ -209,6 +199,12 @@ pub const iovec_const = extern struct { iov_len: usize, }; +pub const ACCMODE = enum(u2) { + RDONLY = 0, + WRONLY = 1, + RDWR = 2, +}; + pub const LOG = struct { /// system is unusable pub const EMERG = 0; @@ -460,13 +456,13 @@ fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtEr // Fallback to changing permissions using procfs: // - // 1. Open `path` as an `O.PATH` descriptor. + // 1. Open `path` as a `PATH` descriptor. // 2. Stat the fd and check if it isn't a symbolic link. // 3. Generate the procfs reference to the fd via `/proc/self/fd/{fd}`. // 4. Pass the procfs path to `chmod` with the `mode`. var pathfd: fd_t = undefined; while (true) { - const rc = system.openat(dirfd, &path_c, O.PATH | O.NOFOLLOW | O.CLOEXEC, @as(mode_t, 0)); + const rc = system.openat(dirfd, &path_c, .{ .PATH = true, .NOFOLLOW = true, .CLOEXEC = true }, @as(mode_t, 0)); switch (system.getErrno(rc)) { .SUCCESS => { pathfd = @as(fd_t, @intCast(rc)); @@ -536,8 +532,10 @@ pub const FChownError = error{ /// any group of which the owner is a member. If the owner or group is /// specified as `null`, the ID is not changed. pub fn fchown(fd: fd_t, owner: ?uid_t, group: ?gid_t) FChownError!void { - if (builtin.os.tag == .windows or builtin.os.tag == .wasi) - @compileError("Unsupported OS"); + switch (builtin.os.tag) { + .windows, .wasi => @compileError("Unsupported OS"), + else => {}, + } while (true) { const res = system.fchown(fd, owner orelse @as(u32, 0) -% 1, group orelse @as(u32, 0) -% 1); @@ -675,7 +673,7 @@ pub fn getrandom(buffer: []u8) GetRandomError!void { } fn getRandomBytesDevURandom(buf: []u8) !void { - const fd = try openZ("/dev/urandom", O.RDONLY | O.CLOEXEC, 0); + const fd = try openZ("/dev/urandom", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); defer close(fd); const st = try fstat(fd); @@ -1590,18 +1588,18 @@ pub const OpenError = error{ /// for 64-bit targets, as well as when opening directories. FileTooBig, - /// The path refers to directory but the `O.DIRECTORY` flag was not provided. + /// The path refers to directory but the `DIRECTORY` flag was not provided. IsDir, /// A new path cannot be created because the device has no room for the new file. - /// This error is only reachable when the `O.CREAT` flag is provided. + /// This error is only reachable when the `CREAT` flag is provided. NoSpaceLeft, /// A component used as a directory in the path was not, in fact, a directory, or - /// `O.DIRECTORY` was specified and the path was not a directory. + /// `DIRECTORY` was specified and the path was not a directory. NotDir, - /// The path already exists and the `O.CREAT` and `O.EXCL` flags were provided. + /// The path already exists and the `CREAT` and `EXCL` flags were provided. PathAlreadyExists, DeviceBusy, @@ -1629,12 +1627,11 @@ pub const OpenError = error{ /// Open and possibly create a file. Keeps trying if it gets interrupted. /// See also `openZ`. -pub fn open(file_path: []const u8, flags: u32, perm: mode_t) OpenError!fd_t { +pub fn open(file_path: []const u8, flags: O, perm: mode_t) OpenError!fd_t { if (builtin.os.tag == .windows) { - const file_path_w = try windows.sliceToPrefixedFileW(null, file_path); - return openW(file_path_w.span(), flags, perm); + @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API"); } else if (builtin.os.tag == .wasi and !builtin.link_libc) { - return openat(wasi.AT.FDCWD, file_path, flags, perm); + return openat(AT.FDCWD, file_path, flags, perm); } const file_path_c = try toPosixPath(file_path); return openZ(&file_path_c, flags, perm); @@ -1642,10 +1639,9 @@ pub fn open(file_path: []const u8, flags: u32, perm: mode_t) OpenError!fd_t { /// Open and possibly create a file. Keeps trying if it gets interrupted. /// See also `open`. -pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t { +pub fn openZ(file_path: [*:0]const u8, flags: O, perm: mode_t) OpenError!fd_t { if (builtin.os.tag == .windows) { - const file_path_w = try windows.cStrToPrefixedFileW(null, file_path); - return openW(file_path_w.span(), flags, perm); + @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API"); } else if (builtin.os.tag == .wasi and !builtin.link_libc) { return open(mem.sliceTo(file_path, 0), flags, perm); } @@ -1681,64 +1677,15 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t } } -fn openOptionsFromFlagsWindows(flags: u32) windows.OpenFileOptions { - const w = windows; - - var access_mask: w.ULONG = w.READ_CONTROL | w.FILE_WRITE_ATTRIBUTES | w.SYNCHRONIZE; - if (flags & O.RDWR != 0) { - access_mask |= w.GENERIC_READ | w.GENERIC_WRITE; - } else if (flags & O.WRONLY != 0) { - access_mask |= w.GENERIC_WRITE; - } else { - access_mask |= w.GENERIC_READ | w.GENERIC_WRITE; - } - - const filter: windows.OpenFileOptions.Filter = if (flags & O.DIRECTORY != 0) .dir_only else .file_only; - const follow_symlinks: bool = flags & O.NOFOLLOW == 0; - - const creation: w.ULONG = blk: { - if (flags & O.CREAT != 0) { - if (flags & O.EXCL != 0) { - break :blk w.FILE_CREATE; - } - } - break :blk w.FILE_OPEN; - }; - - return .{ - .access_mask = access_mask, - .creation = creation, - .filter = filter, - .follow_symlinks = follow_symlinks, - }; -} - -/// Windows-only. The path parameter is -/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. -/// Translates the POSIX open API call to a Windows API call. -/// TODO currently, this function does not handle all flag combinations -/// or makes use of perm argument. -pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t { - _ = perm; - var options = openOptionsFromFlagsWindows(flags); - options.dir = std.fs.cwd().fd; - return windows.OpenFile(file_path_w, options) catch |err| switch (err) { - error.WouldBlock => unreachable, - error.PipeBusy => unreachable, - else => |e| return e, - }; -} - /// Open and possibly create a file. Keeps trying if it gets interrupted. /// `file_path` is relative to the open directory handle `dir_fd`. /// See also `openatZ`. -pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t { +pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: O, mode: mode_t) OpenError!fd_t { if (builtin.os.tag == .windows) { - const file_path_w = try windows.sliceToPrefixedFileW(dir_fd, file_path); - return openatW(dir_fd, file_path_w.span(), flags, mode); + @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API"); } else if (builtin.os.tag == .wasi and !builtin.link_libc) { // `mode` is ignored on WASI, which does not support unix-style file permissions - const opts = try openOptionsFromFlagsWasi(dir_fd, flags); + const opts = try openOptionsFromFlagsWasi(flags); const fd = try openatWasi( dir_fd, file_path, @@ -1750,8 +1697,8 @@ pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) Ope ); errdefer close(fd); - if (flags & O.WRONLY != 0) { - const info = try fstat(fd); + if (flags.write) { + const info = try fstat_wasi(fd); if (info.filetype == .DIRECTORY) return error.IsDir; } @@ -1762,6 +1709,37 @@ pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) Ope return openatZ(dir_fd, &file_path_c, flags, mode); } +pub const CommonOpenFlags = packed struct { + ACCMODE: ACCMODE = .RDONLY, + CREAT: bool = false, + EXCL: bool = false, + LARGEFILE: bool = false, + DIRECTORY: bool = false, + CLOEXEC: bool = false, + NONBLOCK: bool = false, + + pub fn lower(cof: CommonOpenFlags) O { + if (builtin.os.tag == .wasi) return .{ + .read = cof.ACCMODE != .WRONLY, + .write = cof.ACCMODE != .RDONLY, + .CREAT = cof.CREAT, + .EXCL = cof.EXCL, + .DIRECTORY = cof.DIRECTORY, + .NONBLOCK = cof.NONBLOCK, + }; + var result: O = .{ + .ACCMODE = cof.ACCMODE, + .CREAT = cof.CREAT, + .EXCL = cof.EXCL, + .DIRECTORY = cof.DIRECTORY, + .NONBLOCK = cof.NONBLOCK, + .CLOEXEC = cof.CLOEXEC, + }; + if (@hasField(O, "LARGEFILE")) result.LARGEFILE = cof.LARGEFILE; + return result; + } +}; + /// A struct to contain all lookup/rights flags accepted by `wasi.path_open` const WasiOpenOptions = struct { oflags: wasi.oflags_t, @@ -1772,42 +1750,38 @@ const WasiOpenOptions = struct { }; /// Compute rights + flags corresponding to the provided POSIX access mode. -fn openOptionsFromFlagsWasi(fd: fd_t, oflag: u32) OpenError!WasiOpenOptions { +fn openOptionsFromFlagsWasi(oflag: O) OpenError!WasiOpenOptions { const w = std.os.wasi; - // First, discover the rights that we can derive from `fd` - var fsb_cur: wasi.fdstat_t = undefined; - _ = switch (w.fd_fdstat_get(fd, &fsb_cur)) { - .SUCCESS => .{}, - .BADF => return error.InvalidHandle, - else => |err| return unexpectedErrno(err), - }; - // Next, calculate the read/write rights to request, depending on the // provided POSIX access mode - var rights: w.rights_t = 0; - if (oflag & O.RDONLY != 0) { - rights |= w.RIGHT.FD_READ | w.RIGHT.FD_READDIR; + var rights: w.rights_t = .{}; + if (oflag.read) { + rights.FD_READ = true; + rights.FD_READDIR = true; } - if (oflag & O.WRONLY != 0) { - rights |= w.RIGHT.FD_DATASYNC | w.RIGHT.FD_WRITE | - w.RIGHT.FD_ALLOCATE | w.RIGHT.FD_FILESTAT_SET_SIZE; + if (oflag.write) { + rights.FD_DATASYNC = true; + rights.FD_WRITE = true; + rights.FD_ALLOCATE = true; + rights.FD_FILESTAT_SET_SIZE = true; } - // Request all other rights unconditionally - rights |= ~(w.RIGHT.FD_DATASYNC | w.RIGHT.FD_READ | - w.RIGHT.FD_WRITE | w.RIGHT.FD_ALLOCATE | - w.RIGHT.FD_READDIR | w.RIGHT.FD_FILESTAT_SET_SIZE); + // https://github.com/ziglang/zig/issues/18882 + const flag_bits: u32 = @bitCast(oflag); + const oflags_int: u16 = @as(u12, @truncate(flag_bits >> 12)); + const fs_flags_int: u16 = @as(u12, @truncate(flag_bits)); - // But only take rights that we can actually inherit - rights &= fsb_cur.fs_rights_inheriting; - - return WasiOpenOptions{ - .oflags = @as(w.oflags_t, @truncate((oflag >> 12))) & 0xfff, - .lookup_flags = if (oflag & O.NOFOLLOW == 0) w.LOOKUP_SYMLINK_FOLLOW else 0, + return .{ + // https://github.com/ziglang/zig/issues/18882 + .oflags = @bitCast(oflags_int), + .lookup_flags = .{ + .SYMLINK_FOLLOW = !oflag.NOFOLLOW, + }, .fs_rights_base = rights, - .fs_rights_inheriting = fsb_cur.fs_rights_inheriting, - .fs_flags = @as(w.fdflags_t, @truncate(oflag & 0xfff)), + .fs_rights_inheriting = rights, + // https://github.com/ziglang/zig/issues/18882 + .fs_flags = @bitCast(fs_flags_int), }; } @@ -1815,11 +1789,11 @@ fn openOptionsFromFlagsWasi(fd: fd_t, oflag: u32) OpenError!WasiOpenOptions { pub fn openatWasi( dir_fd: fd_t, file_path: []const u8, - lookup_flags: lookupflags_t, - oflags: oflags_t, - fdflags: fdflags_t, - base: rights_t, - inheriting: rights_t, + lookup_flags: wasi.lookupflags_t, + oflags: wasi.oflags_t, + fdflags: wasi.fdflags_t, + base: wasi.rights_t, + inheriting: wasi.rights_t, ) OpenError!fd_t { while (true) { var fd: fd_t = undefined; @@ -1829,6 +1803,7 @@ pub fn openatWasi( .FAULT => unreachable, .INVAL => unreachable, + .BADF => unreachable, .ACCES => return error.AccessDenied, .FBIG => return error.FileTooBig, .OVERFLOW => return error.FileTooBig, @@ -1854,10 +1829,9 @@ pub fn openatWasi( /// Open and possibly create a file. Keeps trying if it gets interrupted. /// `file_path` is relative to the open directory handle `dir_fd`. /// See also `openat`. -pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t { +pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: O, mode: mode_t) OpenError!fd_t { if (builtin.os.tag == .windows) { - const file_path_w = try windows.cStrToPrefixedFileW(dir_fd, file_path); - return openatW(dir_fd, file_path_w.span(), flags, mode); + @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API"); } else if (builtin.os.tag == .wasi and !builtin.link_libc) { return openat(dir_fd, mem.sliceTo(file_path, 0), flags, mode); } @@ -1897,21 +1871,6 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) } } -/// Windows-only. Similar to `openat` but with pathname argument null-terminated -/// WTF16 encoded. -/// TODO currently, this function does not handle all flag combinations -/// or makes use of perm argument. -pub fn openatW(dir_fd: fd_t, file_path_w: []const u16, flags: u32, mode: mode_t) OpenError!fd_t { - _ = mode; - var options = openOptionsFromFlagsWindows(flags); - options.dir = dir_fd; - return windows.OpenFile(file_path_w, options) catch |err| switch (err) { - error.WouldBlock => unreachable, - error.PipeBusy => unreachable, - else => |e| return e, - }; -} - pub fn dup(old_fd: fd_t) !fd_t { const rc = system.dup(old_fd); return switch (errno(rc)) { @@ -2403,42 +2362,43 @@ pub fn linkat( if (builtin.os.tag == .wasi and !builtin.link_libc) { const old: RelativePathWasi = .{ .dir_fd = olddir, .relative_path = oldpath }; const new: RelativePathWasi = .{ .dir_fd = newdir, .relative_path = newpath }; - return linkatWasi(old, new, flags); + const old_flags: wasi.lookupflags_t = .{ + .SYMLINK_FOLLOW = (flags & AT.SYMLINK_FOLLOW) != 0, + }; + switch (wasi.path_link( + old.dir_fd, + old_flags, + old.relative_path.ptr, + old.relative_path.len, + new.dir_fd, + new.relative_path.ptr, + new.relative_path.len, + )) { + .SUCCESS => return, + .ACCES => return error.AccessDenied, + .DQUOT => return error.DiskQuota, + .EXIST => return error.PathAlreadyExists, + .FAULT => unreachable, + .IO => return error.FileSystem, + .LOOP => return error.SymLinkLoop, + .MLINK => return error.LinkQuotaExceeded, + .NAMETOOLONG => return error.NameTooLong, + .NOENT => return error.FileNotFound, + .NOMEM => return error.SystemResources, + .NOSPC => return error.NoSpaceLeft, + .NOTDIR => return error.NotDir, + .PERM => return error.AccessDenied, + .ROFS => return error.ReadOnlyFileSystem, + .XDEV => return error.NotSameFileSystem, + .INVAL => unreachable, + else => |err| return unexpectedErrno(err), + } } const old = try toPosixPath(oldpath); const new = try toPosixPath(newpath); return try linkatZ(olddir, &old, newdir, &new, flags); } -/// WASI-only. The same as `linkat` but targeting WASI. -/// See also `linkat`. -pub fn linkatWasi(old: RelativePathWasi, new: RelativePathWasi, flags: i32) LinkatError!void { - var old_flags: wasi.lookupflags_t = 0; - // TODO: Why is this not defined in wasi-libc? - if (flags & linux.AT.SYMLINK_FOLLOW != 0) old_flags |= wasi.LOOKUP_SYMLINK_FOLLOW; - - switch (wasi.path_link(old.dir_fd, old_flags, old.relative_path.ptr, old.relative_path.len, new.dir_fd, new.relative_path.ptr, new.relative_path.len)) { - .SUCCESS => return, - .ACCES => return error.AccessDenied, - .DQUOT => return error.DiskQuota, - .EXIST => return error.PathAlreadyExists, - .FAULT => unreachable, - .IO => return error.FileSystem, - .LOOP => return error.SymLinkLoop, - .MLINK => return error.LinkQuotaExceeded, - .NAMETOOLONG => return error.NameTooLong, - .NOENT => return error.FileNotFound, - .NOMEM => return error.SystemResources, - .NOSPC => return error.NoSpaceLeft, - .NOTDIR => return error.NotDir, - .PERM => return error.AccessDenied, - .ROFS => return error.ReadOnlyFileSystem, - .XDEV => return error.NotSameFileSystem, - .INVAL => unreachable, - else => |err| return unexpectedErrno(err), - } -} - pub const UnlinkError = error{ FileNotFound, @@ -3404,20 +3364,16 @@ pub fn isatty(handle: fd_t) bool { return system.isatty(handle) != 0; } if (builtin.os.tag == .wasi) { - var statbuf: fdstat_t = undefined; - const err = system.fd_fdstat_get(handle, &statbuf); - if (err != .SUCCESS) { - // errno = err; + var statbuf: wasi.fdstat_t = undefined; + const err = wasi.fd_fdstat_get(handle, &statbuf); + if (err != .SUCCESS) return false; - } // A tty is a character device that we can't seek or tell on. - if (statbuf.fs_filetype != .CHARACTER_DEVICE or - (statbuf.fs_rights_base & (RIGHT.FD_SEEK | RIGHT.FD_TELL)) != 0) - { - // errno = ENOTTY; + if (statbuf.fs_filetype != .CHARACTER_DEVICE) + return false; + if (statbuf.fs_rights_base.FD_SEEK or statbuf.fs_rights_base.FD_TELL) return false; - } return true; } @@ -3838,11 +3794,11 @@ pub fn accept( /// will return a value greater than was supplied to the call. addr_size: ?*socklen_t, /// The following values can be bitwise ORed in flags to obtain different behavior: - /// * `SOCK.NONBLOCK` - Set the `O.NONBLOCK` file status flag on the open file description (see `open`) + /// * `SOCK.NONBLOCK` - Set the `NONBLOCK` file status flag on the open file description (see `open`) /// referred to by the new file descriptor. Using this flag saves extra calls to `fcntl` to achieve /// the same result. /// * `SOCK.CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the - /// description of the `O.CLOEXEC` flag in `open` for reasons why this may be useful. + /// description of the `CLOEXEC` flag in `open` for reasons why this may be useful. flags: u32, ) AcceptError!socket_t { const have_accept4 = comptime !(builtin.target.isDarwin() or builtin.os.tag == .windows); @@ -4278,16 +4234,7 @@ pub const FStatError = error{ /// Return information about a file descriptor. pub fn fstat(fd: fd_t) FStatError!Stat { if (builtin.os.tag == .wasi and !builtin.link_libc) { - var stat: wasi.filestat_t = undefined; - switch (wasi.fd_filestat_get(fd, &stat)) { - .SUCCESS => return Stat.fromFilestat(stat), - .INVAL => unreachable, - .BADF => unreachable, // Always a race condition. - .NOMEM => return error.SystemResources, - .ACCES => return error.AccessDenied, - .NOTCAPABLE => return error.AccessDenied, - else => |err| return unexpectedErrno(err), - } + return Stat.fromFilestat(try fstat_wasi(fd)); } if (builtin.os.tag == .windows) { @compileError("fstat is not yet implemented on Windows"); @@ -4306,15 +4253,30 @@ pub fn fstat(fd: fd_t) FStatError!Stat { } } +pub fn fstat_wasi(fd: fd_t) FStatError!wasi.filestat_t { + var stat: wasi.filestat_t = undefined; + switch (wasi.fd_filestat_get(fd, &stat)) { + .SUCCESS => return stat, + .INVAL => unreachable, + .BADF => unreachable, // Always a race condition. + .NOMEM => return error.SystemResources, + .ACCES => return error.AccessDenied, + .NOTCAPABLE => return error.AccessDenied, + else => |err| return unexpectedErrno(err), + } +} + pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound, SymLinkLoop }; /// Similar to `fstat`, but returns stat of a resource pointed to by `pathname` /// which is relative to `dirfd` handle. -/// See also `fstatatZ` and `fstatatWasi`. +/// See also `fstatatZ` and `fstatat_wasi`. pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat { if (builtin.os.tag == .wasi and !builtin.link_libc) { - const wasi_flags = if (flags & linux.AT.SYMLINK_NOFOLLOW == 0) wasi.LOOKUP_SYMLINK_FOLLOW else 0; - return fstatatWasi(dirfd, pathname, wasi_flags); + const filestat = try fstatat_wasi(dirfd, pathname, .{ + .SYMLINK_FOLLOW = (flags & AT.SYMLINK_NOFOLLOW) == 0, + }); + return Stat.fromFilestat(filestat); } else if (builtin.os.tag == .windows) { @compileError("fstatat is not yet implemented on Windows"); } else { @@ -4325,10 +4287,10 @@ pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat /// WASI-only. Same as `fstatat` but targeting WASI. /// See also `fstatat`. -pub fn fstatatWasi(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat { +pub fn fstatat_wasi(dirfd: fd_t, pathname: []const u8, flags: wasi.lookupflags_t) FStatAtError!wasi.filestat_t { var stat: wasi.filestat_t = undefined; switch (wasi.path_filestat_get(dirfd, flags, pathname.ptr, pathname.len, &stat)) { - .SUCCESS => return Stat.fromFilestat(stat), + .SUCCESS => return stat, .INVAL => unreachable, .BADF => unreachable, // Always a race condition. .NOMEM => return error.SystemResources, @@ -4346,7 +4308,10 @@ pub fn fstatatWasi(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!S /// See also `fstatat`. pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat { if (builtin.os.tag == .wasi and !builtin.link_libc) { - return fstatatWasi(dirfd, mem.sliceTo(pathname), flags); + const filestat = try fstatat_wasi(dirfd, mem.sliceTo(pathname, 0), .{ + .SYMLINK_FOLLOW = (flags & AT.SYMLINK_NOFOLLOW) == 0, + }); + return Stat.fromFilestat(filestat); } const fstatat_sym = if (lfs64_abi) system.fstatat64 else system.fstatat; @@ -4627,7 +4592,7 @@ pub const MMapError = error{ /// A file descriptor refers to a non-regular file. Or a file mapping was requested, /// but the file descriptor is not open for reading. Or `MAP.SHARED` was requested - /// and `PROT_WRITE` is set, but the file descriptor is not open in `O.RDWR` mode. + /// and `PROT_WRITE` is set, but the file descriptor is not open in `RDWR` mode. /// Or `PROT_WRITE` is set, but the file is append-only. AccessDenied, @@ -4795,10 +4760,12 @@ pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessErr const path_w = try windows.sliceToPrefixedFileW(dirfd, path); return faccessatW(dirfd, path_w.span().ptr, mode, flags); } else if (builtin.os.tag == .wasi and !builtin.link_libc) { - const resolved = RelativePathWasi{ .dir_fd = dirfd, .relative_path = path }; + const resolved: RelativePathWasi = .{ .dir_fd = dirfd, .relative_path = path }; - const file = blk: { - break :blk fstatat(dirfd, path, flags); + const st = blk: { + break :blk fstatat_wasi(dirfd, path, .{ + .SYMLINK_FOLLOW = (flags & AT.SYMLINK_NOFOLLOW) == 0, + }); } catch |err| switch (err) { error.AccessDenied => return error.PermissionDenied, else => |e| return e, @@ -4810,19 +4777,23 @@ pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessErr return error.PermissionDenied; } - var rights: wasi.rights_t = 0; + var rights: wasi.rights_t = .{}; if (mode & R_OK != 0) { - rights |= if (file.filetype == .DIRECTORY) - wasi.RIGHT.FD_READDIR - else - wasi.RIGHT.FD_READ; + if (st.filetype == .DIRECTORY) { + rights.FD_READDIR = true; + } else { + rights.FD_READ = true; + } } if (mode & W_OK != 0) { - rights |= wasi.RIGHT.FD_WRITE; + rights.FD_WRITE = true; } // No validation for X_OK - if ((rights & directory.fs_rights_inheriting) != rights) { + // https://github.com/ziglang/zig/issues/18882 + const rights_int: u64 = @bitCast(rights); + const inheriting_int: u64 = @bitCast(directory.fs_rights_inheriting); + if ((rights_int & inheriting_int) != rights_int) { return error.PermissionDenied; } } @@ -4915,7 +4886,7 @@ pub fn pipe() PipeError![2]fd_t { } } -pub fn pipe2(flags: u32) PipeError![2]fd_t { +pub fn pipe2(flags: O) PipeError![2]fd_t { if (@hasDecl(system, "pipe2")) { var fds: [2]fd_t = undefined; switch (errno(system.pipe2(&fds, flags))) { @@ -4934,12 +4905,13 @@ pub fn pipe2(flags: u32) PipeError![2]fd_t { close(fds[1]); } - if (flags == 0) + // https://github.com/ziglang/zig/issues/18882 + if (@as(u32, @bitCast(flags)) == 0) return fds; - // O.CLOEXEC is special, it's a file descriptor flag and must be set using + // CLOEXEC is special, it's a file descriptor flag and must be set using // F.SETFD. - if (flags & O.CLOEXEC != 0) { + if (flags.CLOEXEC) { for (fds) |fd| { switch (errno(system.fcntl(fd, F.SETFD, @as(u32, FD_CLOEXEC)))) { .SUCCESS => {}, @@ -4950,7 +4922,11 @@ pub fn pipe2(flags: u32) PipeError![2]fd_t { } } - const new_flags = flags & ~@as(u32, O.CLOEXEC); + const new_flags: u32 = f: { + var new_flags = flags; + new_flags.CLOEXEC = false; + break :f @bitCast(new_flags); + }; // Set every other flag affecting the file status using F.SETFL. if (new_flags != 0) { for (fds) |fd| { @@ -5289,7 +5265,7 @@ fn setSockFlags(sock: socket_t, flags: u32) !void { error.LockedRegionLimitExceeded => unreachable, else => |e| return e, }; - fl_flags |= O.NONBLOCK; + fl_flags |= 1 << @bitOffsetOf(O, "NONBLOCK"); _ = fcntl(sock, F.SETFL, fl_flags) catch |err| switch (err) { error.FileBusy => unreachable, error.Locked => unreachable, @@ -5389,7 +5365,17 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP return realpath(mem.sliceTo(pathname, 0), out_buffer); } if (!builtin.link_libc) { - const flags = if (builtin.os.tag == .linux) O.PATH | O.NONBLOCK | O.CLOEXEC else O.NONBLOCK | O.CLOEXEC; + const flags: O = switch (builtin.os.tag) { + .linux => .{ + .NONBLOCK = true, + .CLOEXEC = true, + .PATH = true, + }, + else => .{ + .NONBLOCK = true, + .CLOEXEC = true, + }, + }; const fd = openZ(pathname, flags, 0) catch |err| switch (err) { error.FileLocksNotSupported => unreachable, error.WouldBlock => unreachable, @@ -5893,7 +5879,10 @@ pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void { // this here, but we should really handle it somehow. const atim = times[0].toTimestamp(); const mtim = times[1].toTimestamp(); - switch (wasi.fd_filestat_set_times(fd, atim, mtim, wasi.FILESTAT_SET_ATIM | wasi.FILESTAT_SET_MTIM)) { + switch (wasi.fd_filestat_set_times(fd, atim, mtim, .{ + .ATIM = true, + .MTIM = true, + })) { .SUCCESS => return, .ACCES => return error.AccessDenied, .PERM => return error.PermissionDenied, @@ -6390,7 +6379,7 @@ pub fn sendfile( // * Descriptor is not valid or locked // * an mmap(2)-like operation is not available for in_fd // * count is negative - // * out_fd has the O.APPEND flag set + // * out_fd has the APPEND flag set // Because of the "mmap(2)-like operation" possibility, we fall back to doing read/write // manually, the same as ENOSYS. break :sf; @@ -6588,7 +6577,7 @@ pub const CopyFileRangeError = error{ FileTooBig, InputOutput, /// `fd_in` is not open for reading; or `fd_out` is not open for writing; - /// or the `O.APPEND` flag is set for `fd_out`. + /// or the `APPEND` flag is set for `fd_out`. FilesOpenedWithWrongFlags, IsDir, OutOfMemory, @@ -7425,7 +7414,7 @@ pub const TimerFdCreateError = error{ pub const TimerFdGetError = error{InvalidHandle} || UnexpectedError; pub const TimerFdSetError = TimerFdGetError || error{Canceled}; -pub fn timerfd_create(clokid: i32, flags: u32) TimerFdCreateError!fd_t { +pub fn timerfd_create(clokid: i32, flags: linux.TFD) TimerFdCreateError!fd_t { const rc = linux.timerfd_create(clokid, flags); return switch (errno(rc)) { .SUCCESS => @as(fd_t, @intCast(rc)), @@ -7439,7 +7428,12 @@ pub fn timerfd_create(clokid: i32, flags: u32) TimerFdCreateError!fd_t { }; } -pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const linux.itimerspec, old_value: ?*linux.itimerspec) TimerFdSetError!void { +pub fn timerfd_settime( + fd: i32, + flags: linux.TFD.TIMER, + new_value: *const linux.itimerspec, + old_value: ?*linux.itimerspec, +) TimerFdSetError!void { const rc = linux.timerfd_settime(fd, flags, new_value, old_value); return switch (errno(rc)) { .SUCCESS => {}, diff --git a/lib/std/os/emscripten.zig b/lib/std/os/emscripten.zig index 883136b39a..d5ab74273b 100644 --- a/lib/std/os/emscripten.zig +++ b/lib/std/os/emscripten.zig @@ -127,20 +127,6 @@ pub const AF = struct { pub const MAX = PF.MAX; }; -pub const AT = struct { - pub const FDCWD = -100; - pub const SYMLINK_NOFOLLOW = 0x100; - pub const REMOVEDIR = 0x200; - pub const SYMLINK_FOLLOW = 0x400; - pub const NO_AUTOMOUNT = 0x800; - pub const EMPTY_PATH = 0x1000; - pub const STATX_SYNC_TYPE = 0x6000; - pub const STATX_SYNC_AS_STAT = 0x0000; - pub const STATX_FORCE_SYNC = 0x2000; - pub const STATX_DONT_SYNC = 0x4000; - pub const RECURSIVE = 0x8000; -}; - pub const CLOCK = struct { pub const REALTIME = 0; pub const MONOTONIC = 1; @@ -479,33 +465,6 @@ pub const MSG = struct { pub const CMSG_CLOEXEC = 0x40000000; }; -pub const O = struct { - pub const RDONLY = 0o0; - pub const WRONLY = 0o1; - pub const RDWR = 0o2; - - pub const CREAT = 0o100; - pub const EXCL = 0o200; - pub const NOCTTY = 0o400; - pub const TRUNC = 0o1000; - pub const APPEND = 0o2000; - pub const NONBLOCK = 0o4000; - pub const DSYNC = 0o10000; - pub const SYNC = 0o4010000; - pub const RSYNC = 0o4010000; - pub const DIRECTORY = 0o200000; - pub const NOFOLLOW = 0o400000; - pub const CLOEXEC = 0o2000000; - - pub const ASYNC = 0o20000; - pub const DIRECT = 0o40000; - pub const LARGEFILE = 0o100000; - pub const NOATIME = 0o1000000; - pub const PATH = 0o10000000; - pub const TMPFILE = 0o20200000; - pub const NDELAY = NONBLOCK; -}; - pub const POLL = struct { pub const IN = 0x001; pub const PRI = 0x002; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 0a6e4fee1d..432070a5d5 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -20,6 +20,7 @@ const is_ppc64 = native_arch.isPPC64(); const is_sparc = native_arch.isSPARC(); const iovec = std.os.iovec; const iovec_const = std.os.iovec_const; +const ACCMODE = std.os.ACCMODE; test { if (builtin.os.tag == .linux) { @@ -241,12 +242,145 @@ pub const MAP = switch (native_arch) { else => @compileError("missing std.os.linux.MAP constants for this architecture"), }; -pub const O = struct { - pub usingnamespace arch_bits.O; - - pub const RDONLY = 0o0; - pub const WRONLY = 0o1; - pub const RDWR = 0o2; +pub const O = switch (native_arch) { + .x86_64 => packed struct(u32) { + ACCMODE: ACCMODE = .RDONLY, + _2: u4 = 0, + CREAT: bool = false, + EXCL: bool = false, + NOCTTY: bool = false, + TRUNC: bool = false, + APPEND: bool = false, + NONBLOCK: bool = false, + DSYNC: bool = false, + ASYNC: bool = false, + DIRECT: bool = false, + _15: u1 = 0, + DIRECTORY: bool = false, + NOFOLLOW: bool = false, + NOATIME: bool = false, + CLOEXEC: bool = false, + SYNC: bool = false, + PATH: bool = false, + TMPFILE: bool = false, + _: u9 = 0, + }, + .x86, .riscv64 => packed struct(u32) { + ACCMODE: ACCMODE = .RDONLY, + _2: u4 = 0, + CREAT: bool = false, + EXCL: bool = false, + NOCTTY: bool = false, + TRUNC: bool = false, + APPEND: bool = false, + NONBLOCK: bool = false, + DSYNC: bool = false, + ASYNC: bool = false, + DIRECT: bool = false, + LARGEFILE: bool = false, + DIRECTORY: bool = false, + NOFOLLOW: bool = false, + NOATIME: bool = false, + CLOEXEC: bool = false, + SYNC: bool = false, + PATH: bool = false, + TMPFILE: bool = false, + _: u9 = 0, + }, + .aarch64, .aarch64_be, .arm, .thumb => packed struct(u32) { + ACCMODE: ACCMODE = .RDONLY, + _2: u4 = 0, + CREAT: bool = false, + EXCL: bool = false, + NOCTTY: bool = false, + TRUNC: bool = false, + APPEND: bool = false, + NONBLOCK: bool = false, + DSYNC: bool = false, + ASYNC: bool = false, + DIRECTORY: bool = false, + NOFOLLOW: bool = false, + DIRECT: bool = false, + LARGEFILE: bool = false, + NOATIME: bool = false, + CLOEXEC: bool = false, + SYNC: bool = false, + PATH: bool = false, + TMPFILE: bool = false, + _: u9 = 0, + }, + .sparc64 => packed struct(u32) { + ACCMODE: ACCMODE = .RDONLY, + _2: u1 = 0, + APPEND: bool = false, + _4: u2 = 0, + ASYNC: bool = false, + _7: u2 = 0, + CREAT: bool = false, + TRUNC: bool = false, + EXCL: bool = false, + _12: u1 = 0, + DSYNC: bool = false, + NONBLOCK: bool = false, + NOCTTY: bool = false, + DIRECTORY: bool = false, + NOFOLLOW: bool = false, + _18: u2 = 0, + DIRECT: bool = false, + NOATIME: bool = false, + CLOEXEC: bool = false, + SYNC: bool = false, + PATH: bool = false, + TMPFILE: bool = false, + _: u6 = 0, + }, + .mips, .mipsel, .mips64, .mips64el => packed struct(u32) { + ACCMODE: ACCMODE = .RDONLY, + _2: u1 = 0, + APPEND: bool = false, + DSYNC: bool = false, + _5: u2 = 0, + NONBLOCK: bool = false, + CREAT: bool = false, + TRUNC: bool = false, + EXCL: bool = false, + NOCTTY: bool = false, + ASYNC: bool = false, + LARGEFILE: bool = false, + SYNC: bool = false, + DIRECT: bool = false, + DIRECTORY: bool = false, + NOFOLLOW: bool = false, + NOATIME: bool = false, + CLOEXEC: bool = false, + _20: u1 = 0, + PATH: bool = false, + TMPFILE: bool = false, + _: u9 = 0, + }, + .powerpc, .powerpcle, .powerpc64, .powerpc64le => packed struct(u32) { + ACCMODE: ACCMODE = .RDONLY, + _2: u4 = 0, + CREAT: bool = false, + EXCL: bool = false, + NOCTTY: bool = false, + TRUNC: bool = false, + APPEND: bool = false, + NONBLOCK: bool = false, + DSYNC: bool = false, + ASYNC: bool = false, + DIRECTORY: bool = false, + NOFOLLOW: bool = false, + LARGEFILE: bool = false, + DIRECT: bool = false, + NOATIME: bool = false, + CLOEXEC: bool = false, + SYNC: bool = false, + PATH: bool = false, + TMPFILE: bool = false, + _: u9 = 0, + }, + else => @compileError("missing std.os.linux.O constants for this architecture"), }; pub usingnamespace @import("linux/io_uring.zig"); @@ -620,20 +754,20 @@ pub fn umount2(special: [*:0]const u8, flags: u32) usize { return syscall2(.umount2, @intFromPtr(special), flags); } -pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: i64) usize { +pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: MAP, fd: i32, offset: i64) usize { if (@hasField(SYS, "mmap2")) { // Make sure the offset is also specified in multiples of page size if ((offset & (MMAP2_UNIT - 1)) != 0) - return @as(usize, @bitCast(-@as(isize, @intFromEnum(E.INVAL)))); + return @bitCast(-@as(isize, @intFromEnum(E.INVAL))); return syscall6( .mmap2, @intFromPtr(address), length, prot, - flags, - @as(usize, @bitCast(@as(isize, fd))), - @as(usize, @truncate(@as(u64, @bitCast(offset)) / MMAP2_UNIT)), + @as(u32, @bitCast(flags)), + @bitCast(@as(isize, fd)), + @truncate(@as(u64, @bitCast(offset)) / MMAP2_UNIT), ); } else { return syscall6( @@ -641,8 +775,8 @@ pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, of @intFromPtr(address), length, prot, - flags, - @as(usize, @bitCast(@as(isize, fd))), + @as(u32, @bitCast(flags)), + @bitCast(@as(isize, fd)), @as(u64, @bitCast(offset)), ); } @@ -840,12 +974,12 @@ pub fn pipe(fd: *[2]i32) usize { } } -pub fn pipe2(fd: *[2]i32, flags: u32) usize { - return syscall2(.pipe2, @intFromPtr(fd), flags); +pub fn pipe2(fd: *[2]i32, flags: O) usize { + return syscall2(.pipe2, @intFromPtr(fd), @as(u32, @bitCast(flags))); } pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { - return syscall3(.write, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(buf), count); + return syscall3(.write, @bitCast(@as(isize, fd)), @intFromPtr(buf), count); } pub fn ftruncate(fd: i32, length: i64) usize { @@ -958,15 +1092,15 @@ pub fn renameat2(oldfd: i32, oldpath: [*:0]const u8, newfd: i32, newpath: [*:0]c ); } -pub fn open(path: [*:0]const u8, flags: u32, perm: mode_t) usize { +pub fn open(path: [*:0]const u8, flags: O, perm: mode_t) usize { if (@hasField(SYS, "open")) { - return syscall3(.open, @intFromPtr(path), flags, perm); + return syscall3(.open, @intFromPtr(path), @as(u32, @bitCast(flags)), perm); } else { return syscall4( .openat, - @as(usize, @bitCast(@as(isize, AT.FDCWD))), + @bitCast(@as(isize, AT.FDCWD)), @intFromPtr(path), - flags, + @as(u32, @bitCast(flags)), perm, ); } @@ -976,9 +1110,9 @@ pub fn create(path: [*:0]const u8, perm: mode_t) usize { return syscall2(.creat, @intFromPtr(path), perm); } -pub fn openat(dirfd: i32, path: [*:0]const u8, flags: u32, mode: mode_t) usize { +pub fn openat(dirfd: i32, path: [*:0]const u8, flags: O, mode: mode_t) usize { // dirfd could be negative, for example AT.FDCWD is -100 - return syscall4(.openat, @as(usize, @bitCast(@as(isize, dirfd))), @intFromPtr(path), flags, mode); + return syscall4(.openat, @bitCast(@as(isize, dirfd)), @intFromPtr(path), @as(u32, @bitCast(flags)), mode); } /// See also `clone` (from the arch-specific include) @@ -1800,8 +1934,8 @@ pub fn eventfd(count: u32, flags: u32) usize { return syscall2(.eventfd2, count, flags); } -pub fn timerfd_create(clockid: i32, flags: u32) usize { - return syscall2(.timerfd_create, @as(usize, @bitCast(@as(isize, clockid))), flags); +pub fn timerfd_create(clockid: i32, flags: TFD) usize { + return syscall2(.timerfd_create, @bitCast(@as(isize, clockid)), @as(u32, @bitCast(flags))); } pub const itimerspec = extern struct { @@ -1810,11 +1944,11 @@ pub const itimerspec = extern struct { }; pub fn timerfd_gettime(fd: i32, curr_value: *itimerspec) usize { - return syscall2(.timerfd_gettime, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(curr_value)); + return syscall2(.timerfd_gettime, @bitCast(@as(isize, fd)), @intFromPtr(curr_value)); } -pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const itimerspec, old_value: ?*itimerspec) usize { - return syscall4(.timerfd_settime, @as(usize, @bitCast(@as(isize, fd))), flags, @intFromPtr(new_value), @intFromPtr(old_value)); +pub fn timerfd_settime(fd: i32, flags: TFD.TIMER, new_value: *const itimerspec, old_value: ?*itimerspec) usize { + return syscall4(.timerfd_settime, @bitCast(@as(isize, fd)), @as(u32, @bitCast(flags)), @intFromPtr(new_value), @intFromPtr(old_value)); } // Flags for the 'setitimer' system call @@ -2478,19 +2612,10 @@ pub const SIG = if (is_mips) struct { pub const kernel_rwf = u32; pub const RWF = struct { - /// high priority request, poll if possible pub const HIPRI: kernel_rwf = 0x00000001; - - /// per-IO O.DSYNC pub const DSYNC: kernel_rwf = 0x00000002; - - /// per-IO O.SYNC pub const SYNC: kernel_rwf = 0x00000004; - - /// per-IO, return -EAGAIN if operation would block pub const NOWAIT: kernel_rwf = 0x00000008; - - /// per-IO O.APPEND pub const APPEND: kernel_rwf = 0x00000010; }; @@ -3257,7 +3382,7 @@ pub const T = struct { }; pub const EPOLL = struct { - pub const CLOEXEC = O.CLOEXEC; + pub const CLOEXEC = 1 << @bitOffsetOf(O, "CLOEXEC"); pub const CTL_ADD = 1; pub const CTL_DEL = 2; @@ -3338,8 +3463,8 @@ pub const CLONE = struct { pub const EFD = struct { pub const SEMAPHORE = 1; - pub const CLOEXEC = O.CLOEXEC; - pub const NONBLOCK = O.NONBLOCK; + pub const CLOEXEC = 1 << @bitOffsetOf(O, "CLOEXEC"); + pub const NONBLOCK = 1 << @bitOffsetOf(O, "NONBLOCK"); }; pub const MS = struct { @@ -3388,8 +3513,8 @@ pub const MNT = struct { pub const UMOUNT_NOFOLLOW = 8; pub const IN = struct { - pub const CLOEXEC = O.CLOEXEC; - pub const NONBLOCK = O.NONBLOCK; + pub const CLOEXEC = 1 << @bitOffsetOf(O, "CLOEXEC"); + pub const NONBLOCK = 1 << @bitOffsetOf(O, "NONBLOCK"); pub const ACCESS = 0x00000001; pub const MODIFY = 0x00000002; @@ -3534,12 +3659,40 @@ pub const UTIME = struct { pub const OMIT = 0x3ffffffe; }; -pub const TFD = struct { - pub const NONBLOCK = O.NONBLOCK; - pub const CLOEXEC = O.CLOEXEC; +const TFD_TIMER = packed struct(u32) { + ABSTIME: bool = false, + CANCEL_ON_SET: bool = false, + _: u30 = 0, +}; - pub const TIMER_ABSTIME = 1; - pub const TIMER_CANCEL_ON_SET = (1 << 1); +pub const TFD = switch (native_arch) { + .sparc64 => packed struct(u32) { + _0: u14 = 0, + NONBLOCK: bool = false, + _15: u7 = 0, + CLOEXEC: bool = false, + _: u9 = 0, + + pub const TIMER = TFD_TIMER; + }, + .mips, .mipsel, .mips64, .mips64el => packed struct(u32) { + _0: u7 = 0, + NONBLOCK: bool = false, + _8: u11 = 0, + CLOEXEC: bool = false, + _: u12 = 0, + + pub const TIMER = TFD_TIMER; + }, + else => packed struct(u32) { + _0: u11 = 0, + NONBLOCK: bool = false, + _12: u7 = 0, + CLOEXEC: bool = false, + _: u12 = 0, + + pub const TIMER = TFD_TIMER; + }, }; pub const winsize = extern struct { @@ -3603,8 +3756,8 @@ pub const empty_sigset = [_]u32{0} ** sigset_len; pub const filled_sigset = [_]u32{(1 << (31 & (usize_bits - 1))) - 1} ++ [_]u32{0} ** (sigset_len - 1); pub const SFD = struct { - pub const CLOEXEC = O.CLOEXEC; - pub const NONBLOCK = O.NONBLOCK; + pub const CLOEXEC = 1 << @bitOffsetOf(O, "CLOEXEC"); + pub const NONBLOCK = 1 << @bitOffsetOf(O, "NONBLOCK"); }; pub const signalfd_siginfo = extern struct { @@ -3865,15 +4018,11 @@ pub const inotify_event = extern struct { }; pub const dirent64 = extern struct { - d_ino: u64, - d_off: u64, - d_reclen: u16, - d_type: u8, - d_name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/173 - - pub fn reclen(self: dirent64) u16 { - return self.d_reclen; - } + ino: u64, + off: u64, + reclen: u16, + type: u8, + name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/173 }; pub const dl_phdr_info = extern struct { diff --git a/lib/std/os/linux/arm-eabi.zig b/lib/std/os/linux/arm-eabi.zig index 74c381f496..68575c3344 100644 --- a/lib/std/os/linux/arm-eabi.zig +++ b/lib/std/os/linux/arm-eabi.zig @@ -141,29 +141,6 @@ pub fn restore_rt() callconv(.Naked) noreturn { pub const MMAP2_UNIT = 4096; -pub const O = struct { - pub const CREAT = 0o100; - pub const EXCL = 0o200; - pub const NOCTTY = 0o400; - pub const TRUNC = 0o1000; - pub const APPEND = 0o2000; - pub const NONBLOCK = 0o4000; - pub const DSYNC = 0o10000; - pub const SYNC = 0o4010000; - pub const RSYNC = 0o4010000; - pub const DIRECTORY = 0o40000; - pub const NOFOLLOW = 0o100000; - pub const CLOEXEC = 0o2000000; - - pub const ASYNC = 0o20000; - pub const DIRECT = 0o200000; - pub const LARGEFILE = 0o400000; - pub const NOATIME = 0o1000000; - pub const PATH = 0o10000000; - pub const TMPFILE = 0o20040000; - pub const NDELAY = NONBLOCK; -}; - pub const F = struct { pub const DUPFD = 0; pub const GETFD = 1; diff --git a/lib/std/os/linux/arm64.zig b/lib/std/os/linux/arm64.zig index 40dad5656e..f2331c1309 100644 --- a/lib/std/os/linux/arm64.zig +++ b/lib/std/os/linux/arm64.zig @@ -123,29 +123,6 @@ pub fn restore_rt() callconv(.Naked) noreturn { } } -pub const O = struct { - pub const CREAT = 0o100; - pub const EXCL = 0o200; - pub const NOCTTY = 0o400; - pub const TRUNC = 0o1000; - pub const APPEND = 0o2000; - pub const NONBLOCK = 0o4000; - pub const DSYNC = 0o10000; - pub const SYNC = 0o4010000; - pub const RSYNC = 0o4010000; - pub const DIRECTORY = 0o40000; - pub const NOFOLLOW = 0o100000; - pub const CLOEXEC = 0o2000000; - - pub const ASYNC = 0o20000; - pub const DIRECT = 0o200000; - pub const LARGEFILE = 0o400000; - pub const NOATIME = 0o1000000; - pub const PATH = 0o10000000; - pub const TMPFILE = 0o20040000; - pub const NDELAY = NONBLOCK; -}; - pub const F = struct { pub const DUPFD = 0; pub const GETFD = 1; diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index 77e134feec..bec7f44ba6 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -760,7 +760,7 @@ pub const IO_Uring = struct { user_data: u64, fd: os.fd_t, path: [*:0]const u8, - flags: u32, + flags: linux.O, mode: os.mode_t, ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); @@ -785,7 +785,7 @@ pub const IO_Uring = struct { user_data: u64, fd: os.fd_t, path: [*:0]const u8, - flags: u32, + flags: linux.O, mode: os.mode_t, file_index: u32, ) !*linux.io_uring_sqe { @@ -1658,18 +1658,18 @@ pub fn io_uring_prep_openat( sqe: *linux.io_uring_sqe, fd: os.fd_t, path: [*:0]const u8, - flags: u32, + flags: linux.O, mode: os.mode_t, ) void { io_uring_prep_rw(.OPENAT, sqe, fd, @intFromPtr(path), mode, 0); - sqe.rw_flags = flags; + sqe.rw_flags = @bitCast(flags); } pub fn io_uring_prep_openat_direct( sqe: *linux.io_uring_sqe, fd: os.fd_t, path: [*:0]const u8, - flags: u32, + flags: linux.O, mode: os.mode_t, file_index: u32, ) void { @@ -2054,7 +2054,7 @@ test "readv" { }; defer ring.deinit(); - const fd = try os.openZ("/dev/zero", os.O.RDONLY | os.O.CLOEXEC, 0); + const fd = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); defer os.close(fd); // Linux Kernel 5.4 supports IORING_REGISTER_FILES but not sparse fd sets (i.e. an fd of -1). @@ -2361,7 +2361,7 @@ test "openat" { break :p @intFromPtr(workaround); } else @intFromPtr(path); - const flags: u32 = os.O.CLOEXEC | os.O.RDWR | os.O.CREAT; + const flags: linux.O = .{ .CLOEXEC = true, .ACCMODE = .RDWR, .CREAT = true }; const mode: os.mode_t = 0o666; const sqe_openat = try ring.openat(0x33333333, tmp.dir.fd, path, flags, mode); try testing.expectEqual(linux.io_uring_sqe{ @@ -2372,7 +2372,7 @@ test "openat" { .off = 0, .addr = path_addr, .len = mode, - .rw_flags = flags, + .rw_flags = @bitCast(flags), .user_data = 0x33333333, .buf_index = 0, .personality = 0, @@ -2888,7 +2888,7 @@ test "register_files_update" { }; defer ring.deinit(); - const fd = try os.openZ("/dev/zero", os.O.RDONLY | os.O.CLOEXEC, 0); + const fd = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); defer os.close(fd); var registered_fds = [_]os.fd_t{0} ** 2; @@ -2906,7 +2906,7 @@ test "register_files_update" { // Test IORING_REGISTER_FILES_UPDATE // Only available since Linux 5.5 - const fd2 = try os.openZ("/dev/zero", os.O.RDONLY | os.O.CLOEXEC, 0); + const fd2 = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); defer os.close(fd2); registered_fds[fd_index] = fd2; @@ -3311,7 +3311,7 @@ test "provide_buffers: read" { }; defer ring.deinit(); - const fd = try os.openZ("/dev/zero", os.O.RDONLY | os.O.CLOEXEC, 0); + const fd = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); defer os.close(fd); const group_id = 1337; @@ -3443,7 +3443,7 @@ test "remove_buffers" { }; defer ring.deinit(); - const fd = try os.openZ("/dev/zero", os.O.RDONLY | os.O.CLOEXEC, 0); + const fd = try os.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); defer os.close(fd); const group_id = 1337; @@ -4113,7 +4113,7 @@ test "openat_direct/close_direct" { var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); const path = "test_io_uring_close_direct"; - const flags: u32 = os.O.RDWR | os.O.CREAT; + const flags: linux.O = .{ .ACCMODE = .RDWR, .CREAT = true }; const mode: os.mode_t = 0o666; const user_data: u64 = 0; diff --git a/lib/std/os/linux/mips.zig b/lib/std/os/linux/mips.zig index 17c6c8a150..896757f1f6 100644 --- a/lib/std/os/linux/mips.zig +++ b/lib/std/os/linux/mips.zig @@ -213,29 +213,6 @@ pub fn restore_rt() callconv(.Naked) noreturn { ); } -pub const O = struct { - pub const CREAT = 0o0400; - pub const EXCL = 0o02000; - pub const NOCTTY = 0o04000; - pub const TRUNC = 0o01000; - pub const APPEND = 0o0010; - pub const NONBLOCK = 0o0200; - pub const DSYNC = 0o0020; - pub const SYNC = 0o040020; - pub const RSYNC = 0o040020; - pub const DIRECTORY = 0o0200000; - pub const NOFOLLOW = 0o0400000; - pub const CLOEXEC = 0o02000000; - - pub const ASYNC = 0o010000; - pub const DIRECT = 0o0100000; - pub const LARGEFILE = 0o020000; - pub const NOATIME = 0o01000000; - pub const PATH = 0o010000000; - pub const TMPFILE = 0o020200000; - pub const NDELAY = NONBLOCK; -}; - pub const F = struct { pub const DUPFD = 0; pub const GETFD = 1; diff --git a/lib/std/os/linux/mips64.zig b/lib/std/os/linux/mips64.zig index 09499cbf9f..4a34f30dd9 100644 --- a/lib/std/os/linux/mips64.zig +++ b/lib/std/os/linux/mips64.zig @@ -198,29 +198,6 @@ pub fn restore_rt() callconv(.Naked) noreturn { ); } -pub const O = struct { - pub const CREAT = 0o0400; - pub const EXCL = 0o02000; - pub const NOCTTY = 0o04000; - pub const TRUNC = 0o01000; - pub const APPEND = 0o0010; - pub const NONBLOCK = 0o0200; - pub const DSYNC = 0o0020; - pub const SYNC = 0o040020; - pub const RSYNC = 0o040020; - pub const DIRECTORY = 0o0200000; - pub const NOFOLLOW = 0o0400000; - pub const CLOEXEC = 0o02000000; - - pub const ASYNC = 0o010000; - pub const DIRECT = 0o0100000; - pub const LARGEFILE = 0o020000; - pub const NOATIME = 0o01000000; - pub const PATH = 0o010000000; - pub const TMPFILE = 0o020200000; - pub const NDELAY = NONBLOCK; -}; - pub const F = struct { pub const DUPFD = 0; pub const GETFD = 1; diff --git a/lib/std/os/linux/powerpc.zig b/lib/std/os/linux/powerpc.zig index 5f07370489..4d13e90166 100644 --- a/lib/std/os/linux/powerpc.zig +++ b/lib/std/os/linux/powerpc.zig @@ -142,29 +142,6 @@ pub fn restore_rt() callconv(.Naked) noreturn { ); } -pub const O = struct { - pub const CREAT = 0o100; - pub const EXCL = 0o200; - pub const NOCTTY = 0o400; - pub const TRUNC = 0o1000; - pub const APPEND = 0o2000; - pub const NONBLOCK = 0o4000; - pub const DSYNC = 0o10000; - pub const SYNC = 0o4010000; - pub const RSYNC = 0o4010000; - pub const DIRECTORY = 0o40000; - pub const NOFOLLOW = 0o100000; - pub const CLOEXEC = 0o2000000; - - pub const ASYNC = 0o20000; - pub const DIRECT = 0o400000; - pub const LARGEFILE = 0o200000; - pub const NOATIME = 0o1000000; - pub const PATH = 0o10000000; - pub const TMPFILE = 0o20040000; - pub const NDELAY = NONBLOCK; -}; - pub const F = struct { pub const DUPFD = 0; pub const GETFD = 1; diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig index 99e52fb5ad..c81ef382c2 100644 --- a/lib/std/os/linux/powerpc64.zig +++ b/lib/std/os/linux/powerpc64.zig @@ -142,29 +142,6 @@ pub fn restore_rt() callconv(.Naked) noreturn { ); } -pub const O = struct { - pub const CREAT = 0o100; - pub const EXCL = 0o200; - pub const NOCTTY = 0o400; - pub const TRUNC = 0o1000; - pub const APPEND = 0o2000; - pub const NONBLOCK = 0o4000; - pub const DSYNC = 0o10000; - pub const SYNC = 0o4010000; - pub const RSYNC = 0o4010000; - pub const DIRECTORY = 0o40000; - pub const NOFOLLOW = 0o100000; - pub const CLOEXEC = 0o2000000; - - pub const ASYNC = 0o20000; - pub const DIRECT = 0o400000; - pub const LARGEFILE = 0o200000; - pub const NOATIME = 0o1000000; - pub const PATH = 0o10000000; - pub const TMPFILE = 0o20200000; - pub const NDELAY = NONBLOCK; -}; - pub const F = struct { pub const DUPFD = 0; pub const GETFD = 1; diff --git a/lib/std/os/linux/riscv64.zig b/lib/std/os/linux/riscv64.zig index 45821ddefa..c23fc5e4df 100644 --- a/lib/std/os/linux/riscv64.zig +++ b/lib/std/os/linux/riscv64.zig @@ -110,29 +110,6 @@ pub fn restore_rt() callconv(.Naked) noreturn { ); } -pub const O = struct { - pub const CREAT = 0o100; - pub const EXCL = 0o200; - pub const NOCTTY = 0o400; - pub const TRUNC = 0o1000; - pub const APPEND = 0o2000; - pub const NONBLOCK = 0o4000; - pub const DSYNC = 0o10000; - pub const SYNC = 0o4010000; - pub const RSYNC = 0o4010000; - pub const DIRECTORY = 0o200000; - pub const NOFOLLOW = 0o400000; - pub const CLOEXEC = 0o2000000; - - pub const ASYNC = 0o20000; - pub const DIRECT = 0o40000; - pub const LARGEFILE = 0o100000; - pub const NOATIME = 0o1000000; - pub const PATH = 0o10000000; - pub const TMPFILE = 0o20200000; - pub const NDELAY = NONBLOCK; -}; - pub const F = struct { pub const DUPFD = 0; pub const GETFD = 1; diff --git a/lib/std/os/linux/sparc64.zig b/lib/std/os/linux/sparc64.zig index ef4e1281b5..0a344e2bf4 100644 --- a/lib/std/os/linux/sparc64.zig +++ b/lib/std/os/linux/sparc64.zig @@ -195,29 +195,6 @@ pub fn restore_rt() callconv(.C) void { ); } -pub const O = struct { - pub const CREAT = 0x200; - pub const EXCL = 0x800; - pub const NOCTTY = 0x8000; - pub const TRUNC = 0x400; - pub const APPEND = 0x8; - pub const NONBLOCK = 0x4000; - pub const SYNC = 0x802000; - pub const DSYNC = 0x2000; - pub const RSYNC = SYNC; - pub const DIRECTORY = 0x10000; - pub const NOFOLLOW = 0x20000; - pub const CLOEXEC = 0x400000; - - pub const ASYNC = 0x40; - pub const DIRECT = 0x100000; - pub const LARGEFILE = 0; - pub const NOATIME = 0x200000; - pub const PATH = 0x1000000; - pub const TMPFILE = 0x2010000; - pub const NDELAY = NONBLOCK | 0x4; -}; - pub const F = struct { pub const DUPFD = 0; pub const GETFD = 1; diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig index 170bde6334..e831f11a5f 100644 --- a/lib/std/os/linux/test.zig +++ b/lib/std/os/linux/test.zig @@ -37,7 +37,7 @@ test "timer" { var err: linux.E = linux.getErrno(epoll_fd); try expect(err == .SUCCESS); - const timer_fd = linux.timerfd_create(linux.CLOCK.MONOTONIC, 0); + const timer_fd = linux.timerfd_create(linux.CLOCK.MONOTONIC, .{}); try expect(linux.getErrno(timer_fd) == .SUCCESS); const time_interval = linux.timespec{ @@ -50,7 +50,7 @@ test "timer" { .it_value = time_interval, }; - err = linux.getErrno(linux.timerfd_settime(@as(i32, @intCast(timer_fd)), 0, &new_time, null)); + err = linux.getErrno(linux.timerfd_settime(@as(i32, @intCast(timer_fd)), .{}, &new_time, null)); try expect(err == .SUCCESS); var event = linux.epoll_event{ diff --git a/lib/std/os/linux/x86.zig b/lib/std/os/linux/x86.zig index a6be4ac380..44ee45d316 100644 --- a/lib/std/os/linux/x86.zig +++ b/lib/std/os/linux/x86.zig @@ -159,29 +159,6 @@ pub fn restore_rt() callconv(.Naked) noreturn { } } -pub const O = struct { - pub const CREAT = 0o100; - pub const EXCL = 0o200; - pub const NOCTTY = 0o400; - pub const TRUNC = 0o1000; - pub const APPEND = 0o2000; - pub const NONBLOCK = 0o4000; - pub const DSYNC = 0o10000; - pub const SYNC = 0o4010000; - pub const RSYNC = 0o4010000; - pub const DIRECTORY = 0o200000; - pub const NOFOLLOW = 0o400000; - pub const CLOEXEC = 0o2000000; - - pub const ASYNC = 0o20000; - pub const DIRECT = 0o40000; - pub const LARGEFILE = 0o100000; - pub const NOATIME = 0o1000000; - pub const PATH = 0o10000000; - pub const TMPFILE = 0o20200000; - pub const NDELAY = NONBLOCK; -}; - pub const F = struct { pub const DUPFD = 0; pub const GETFD = 1; diff --git a/lib/std/os/linux/x86_64.zig b/lib/std/os/linux/x86_64.zig index 6d4ab11abb..2d69d539ae 100644 --- a/lib/std/os/linux/x86_64.zig +++ b/lib/std/os/linux/x86_64.zig @@ -131,29 +131,6 @@ pub const nlink_t = usize; pub const blksize_t = isize; pub const blkcnt_t = isize; -pub const O = struct { - pub const CREAT = 0o100; - pub const EXCL = 0o200; - pub const NOCTTY = 0o400; - pub const TRUNC = 0o1000; - pub const APPEND = 0o2000; - pub const NONBLOCK = 0o4000; - pub const DSYNC = 0o10000; - pub const SYNC = 0o4010000; - pub const RSYNC = 0o4010000; - pub const DIRECTORY = 0o200000; - pub const NOFOLLOW = 0o400000; - pub const CLOEXEC = 0o2000000; - - pub const ASYNC = 0o20000; - pub const DIRECT = 0o40000; - pub const LARGEFILE = 0; - pub const NOATIME = 0o1000000; - pub const PATH = 0o10000000; - pub const TMPFILE = 0o20200000; - pub const NDELAY = NONBLOCK; -}; - pub const F = struct { pub const DUPFD = 0; pub const GETFD = 1; diff --git a/lib/std/os/plan9.zig b/lib/std/os/plan9.zig index b42fd52245..354e05e570 100644 --- a/lib/std/os/plan9.zig +++ b/lib/std/os/plan9.zig @@ -242,17 +242,23 @@ pub fn close(fd: i32) usize { return syscall_bits.syscall1(.CLOSE, @bitCast(@as(isize, fd))); } pub const mode_t = i32; -pub const O = struct { - pub const READ = 0; // open for read - pub const RDONLY = 0; - pub const WRITE = 1; // write - pub const WRONLY = 1; - pub const RDWR = 2; // read and write - pub const EXEC = 3; // execute, == read but check execute permission - pub const TRUNC = 16; // or'ed in (except for exec), truncate file first - pub const CEXEC = 32; // or'ed in (per file descriptor), close on exec - pub const RCLOSE = 64; // or'ed in, remove on close - pub const EXCL = 0x1000; // or'ed in, exclusive create + +pub const AccessMode = enum(u2) { + RDONLY, + WRONLY, + RDWR, + EXEC, +}; + +pub const O = packed struct(u32) { + access: AccessMode, + _2: u2 = 0, + TRUNC: bool = false, + CEXEC: bool = false, + RCLOSE: bool = false, + _7: u5 = 0, + EXCL: bool = false, + _: u19 = 0, }; pub const ExecData = struct { diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 3a170d6aec..5fee5dcc7f 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -87,6 +87,7 @@ test "chdir smoke test" { test "open smoke test" { if (native_os == .wasi) return error.SkipZigTest; + if (native_os == .windows) return error.SkipZigTest; // TODO verify file attributes using `fstat` @@ -109,21 +110,21 @@ test "open smoke test" { // Create some file using `open`. file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - fd = try os.open(file_path, os.O.RDWR | os.O.CREAT | os.O.EXCL, mode); + fd = try os.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode); os.close(fd); // Try this again with the same flags. This op should fail with error.PathAlreadyExists. file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - try expectError(error.PathAlreadyExists, os.open(file_path, os.O.RDWR | os.O.CREAT | os.O.EXCL, mode)); + try expectError(error.PathAlreadyExists, os.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode)); - // Try opening without `O.EXCL` flag. + // Try opening without `EXCL` flag. file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - fd = try os.open(file_path, os.O.RDWR | os.O.CREAT, mode); + fd = try os.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true }, mode); os.close(fd); // Try opening as a directory which should fail. file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - try expectError(error.NotDir, os.open(file_path, os.O.RDWR | os.O.DIRECTORY, mode)); + try expectError(error.NotDir, os.open(file_path, .{ .ACCMODE = .RDWR, .DIRECTORY = true }, mode)); // Create some directory file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); @@ -131,16 +132,17 @@ test "open smoke test" { // Open dir using `open` file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - fd = try os.open(file_path, os.O.RDONLY | os.O.DIRECTORY, mode); + fd = try os.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode); os.close(fd); // Try opening as file which should fail. file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - try expectError(error.IsDir, os.open(file_path, os.O.RDWR, mode)); + try expectError(error.IsDir, os.open(file_path, .{ .ACCMODE = .RDWR }, mode)); } test "openat smoke test" { if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; + if (native_os == .windows) return error.SkipZigTest; // TODO verify file attributes using `fstatat` @@ -151,28 +153,47 @@ test "openat smoke test" { const mode: os.mode_t = if (native_os == .windows) 0 else 0o666; // Create some file using `openat`. - fd = try os.openat(tmp.dir.fd, "some_file", os.O.RDWR | os.O.CREAT | os.O.EXCL, mode); + fd = try os.openat(tmp.dir.fd, "some_file", os.CommonOpenFlags.lower(.{ + .ACCMODE = .RDWR, + .CREAT = true, + .EXCL = true, + }), mode); os.close(fd); // Try this again with the same flags. This op should fail with error.PathAlreadyExists. - try expectError(error.PathAlreadyExists, os.openat(tmp.dir.fd, "some_file", os.O.RDWR | os.O.CREAT | os.O.EXCL, mode)); + try expectError(error.PathAlreadyExists, os.openat(tmp.dir.fd, "some_file", os.CommonOpenFlags.lower(.{ + .ACCMODE = .RDWR, + .CREAT = true, + .EXCL = true, + }), mode)); - // Try opening without `O.EXCL` flag. - fd = try os.openat(tmp.dir.fd, "some_file", os.O.RDWR | os.O.CREAT, mode); + // Try opening without `EXCL` flag. + fd = try os.openat(tmp.dir.fd, "some_file", os.CommonOpenFlags.lower(.{ + .ACCMODE = .RDWR, + .CREAT = true, + }), mode); os.close(fd); // Try opening as a directory which should fail. - try expectError(error.NotDir, os.openat(tmp.dir.fd, "some_file", os.O.RDWR | os.O.DIRECTORY, mode)); + try expectError(error.NotDir, os.openat(tmp.dir.fd, "some_file", os.CommonOpenFlags.lower(.{ + .ACCMODE = .RDWR, + .DIRECTORY = true, + }), mode)); // Create some directory try os.mkdirat(tmp.dir.fd, "some_dir", mode); // Open dir using `open` - fd = try os.openat(tmp.dir.fd, "some_dir", os.O.RDONLY | os.O.DIRECTORY, mode); + fd = try os.openat(tmp.dir.fd, "some_dir", os.CommonOpenFlags.lower(.{ + .ACCMODE = .RDONLY, + .DIRECTORY = true, + }), mode); os.close(fd); // Try opening as file which should fail. - try expectError(error.IsDir, os.openat(tmp.dir.fd, "some_dir", os.O.RDWR, mode)); + try expectError(error.IsDir, os.openat(tmp.dir.fd, "some_dir", os.CommonOpenFlags.lower(.{ + .ACCMODE = .RDWR, + }), mode)); } test "symlink with relative paths" { @@ -688,7 +709,7 @@ test "fcntl" { tmp.dir.deleteFile(test_out_file) catch {}; } - // Note: The test assumes createFile opens the file with O.CLOEXEC + // Note: The test assumes createFile opens the file with CLOEXEC { const flags = try os.fcntl(file.handle, os.F.GETFD, 0); try expect((flags & os.FD_CLOEXEC) != 0); @@ -987,6 +1008,7 @@ test "POSIX file locking with fcntl" { test "rename smoke test" { if (native_os == .wasi) return error.SkipZigTest; + if (native_os == .windows) return error.SkipZigTest; var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -1007,7 +1029,7 @@ test "rename smoke test" { // Create some file using `open`. file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - fd = try os.open(file_path, os.O.RDWR | os.O.CREAT | os.O.EXCL, mode); + fd = try os.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode); os.close(fd); // Rename the file @@ -1016,12 +1038,12 @@ test "rename smoke test" { // Try opening renamed file file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_file" }); - fd = try os.open(file_path, os.O.RDWR, mode); + fd = try os.open(file_path, .{ .ACCMODE = .RDWR }, mode); os.close(fd); // Try opening original file - should fail with error.FileNotFound file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - try expectError(error.FileNotFound, os.open(file_path, os.O.RDWR, mode)); + try expectError(error.FileNotFound, os.open(file_path, .{ .ACCMODE = .RDWR }, mode)); // Create some directory file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); @@ -1033,16 +1055,17 @@ test "rename smoke test" { // Try opening renamed directory file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_other_dir" }); - fd = try os.open(file_path, os.O.RDONLY | os.O.DIRECTORY, mode); + fd = try os.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode); os.close(fd); // Try opening original directory - should fail with error.FileNotFound file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_dir" }); - try expectError(error.FileNotFound, os.open(file_path, os.O.RDONLY | os.O.DIRECTORY, mode)); + try expectError(error.FileNotFound, os.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode)); } test "access smoke test" { if (native_os == .wasi) return error.SkipZigTest; + if (native_os == .windows) return error.SkipZigTest; var tmp = tmpDir(.{}); defer tmp.cleanup(); @@ -1063,7 +1086,7 @@ test "access smoke test" { // Create some file using `open`. file_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "some_file" }); - fd = try os.open(file_path, os.O.RDWR | os.O.CREAT | os.O.EXCL, mode); + fd = try os.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode); os.close(fd); // Try to access() the file @@ -1088,16 +1111,15 @@ test "access smoke test" { } test "timerfd" { - if (native_os != .linux) - return error.SkipZigTest; + if (native_os != .linux) return error.SkipZigTest; const linux = os.linux; - const tfd = try os.timerfd_create(linux.CLOCK.MONOTONIC, linux.TFD.CLOEXEC); + const tfd = try os.timerfd_create(linux.CLOCK.MONOTONIC, .{ .CLOEXEC = true }); defer os.close(tfd); // Fire event 10_000_000ns = 10ms after the os.timerfd_settime call. var sit: linux.itimerspec = .{ .it_interval = .{ .tv_sec = 0, .tv_nsec = 0 }, .it_value = .{ .tv_sec = 0, .tv_nsec = 10 * (1000 * 1000) } }; - try os.timerfd_settime(tfd, 0, &sit, null); + try os.timerfd_settime(tfd, .{}, &sit, null); var fds: [1]os.pollfd = .{.{ .fd = tfd, .events = os.linux.POLL.IN, .revents = 0 }}; try expectEqual(@as(usize, 1), try os.poll(&fds, -1)); // -1 => infinite waiting @@ -1232,7 +1254,7 @@ test "fchmodat smoke test" { const fd = try os.openat( tmp.dir.fd, "regfile", - os.O.WRONLY | os.O.CREAT | os.O.EXCL | os.O.TRUNC, + .{ .ACCMODE = .WRONLY, .CREAT = true, .EXCL = true, .TRUNC = true }, 0o644, ); os.close(fd); diff --git a/lib/std/os/wasi.zig b/lib/std/os/wasi.zig index e286c5b947..016ce38a9f 100644 --- a/lib/std/os/wasi.zig +++ b/lib/std/os/wasi.zig @@ -1,6 +1,7 @@ -// wasi_snapshot_preview1 spec available (in witx format) here: -// * typenames -- https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/witx/typenames.witx -// * module -- https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/witx/wasi_snapshot_preview1.witx +//! wasi_snapshot_preview1 spec available (in witx format) here: +//! * typenames -- https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/witx/typenames.witx +//! * module -- https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/witx/wasi_snapshot_preview1.witx +//! Note that libc API does *not* go in this file. wasi libc API goes into std/c/wasi.zig instead. const builtin = @import("builtin"); const std = @import("std"); const assert = std.debug.assert; @@ -16,11 +17,6 @@ comptime { // assert(@alignOf(u64) == 8); } -pub const F_OK = 0; -pub const X_OK = 1; -pub const W_OK = 2; -pub const R_OK = 4; - pub const iovec_t = std.os.iovec; pub const ciovec_t = std.os.iovec_const; @@ -82,106 +78,22 @@ pub extern "wasi_snapshot_preview1" fn sock_recv(sock: fd_t, ri_data: [*]iovec_t pub extern "wasi_snapshot_preview1" fn sock_send(sock: fd_t, si_data: [*]const ciovec_t, si_data_len: usize, si_flags: siflags_t, so_datalen: *usize) errno_t; pub extern "wasi_snapshot_preview1" fn sock_shutdown(sock: fd_t, how: sdflags_t) errno_t; -/// Get the errno from a syscall return value, or 0 for no error. -pub fn getErrno(r: errno_t) errno_t { - return r; -} - -pub const STDIN_FILENO = 0; -pub const STDOUT_FILENO = 1; -pub const STDERR_FILENO = 2; - -pub const mode_t = u32; - -pub const time_t = i64; // match https://github.com/CraneStation/wasi-libc - -pub const timespec = extern struct { - tv_sec: time_t, - tv_nsec: isize, - - pub fn fromTimestamp(tm: timestamp_t) timespec { - const tv_sec: timestamp_t = tm / 1_000_000_000; - const tv_nsec = tm - tv_sec * 1_000_000_000; - return timespec{ - .tv_sec = @as(time_t, @intCast(tv_sec)), - .tv_nsec = @as(isize, @intCast(tv_nsec)), - }; - } - - pub fn toTimestamp(ts: timespec) timestamp_t { - const tm = @as(timestamp_t, @intCast(ts.tv_sec * 1_000_000_000)) + @as(timestamp_t, @intCast(ts.tv_nsec)); - return tm; - } -}; - -pub const Stat = struct { - dev: device_t, - ino: inode_t, - mode: mode_t, - filetype: filetype_t, - nlink: linkcount_t, - size: filesize_t, - atim: timespec, - mtim: timespec, - ctim: timespec, - - const Self = @This(); - - pub fn fromFilestat(stat: filestat_t) Self { - return Self{ - .dev = stat.dev, - .ino = stat.ino, - .mode = 0, - .filetype = stat.filetype, - .nlink = stat.nlink, - .size = stat.size, - .atim = stat.atime(), - .mtim = stat.mtime(), - .ctim = stat.ctime(), - }; - } - - pub fn atime(self: Self) timespec { - return self.atim; - } - - pub fn mtime(self: Self) timespec { - return self.mtim; - } - - pub fn ctime(self: Self) timespec { - return self.ctim; - } -}; - -pub const IOV_MAX = 1024; - -pub const AT = struct { - pub const REMOVEDIR: u32 = 0x4; - /// When linking libc, we follow their convention and use -2 for current working directory. - /// However, without libc, Zig does a different convention: it assumes the - /// current working directory is the first preopen. This behavior can be - /// overridden with a public function called `wasi_cwd` in the root source - /// file. - pub const FDCWD: fd_t = if (builtin.link_libc) -2 else 3; -}; - // As defined in the wasi_snapshot_preview1 spec file: // https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/witx/typenames.witx -pub const advice_t = u8; -pub const ADVICE_NORMAL: advice_t = 0; -pub const ADVICE_SEQUENTIAL: advice_t = 1; -pub const ADVICE_RANDOM: advice_t = 2; -pub const ADVICE_WILLNEED: advice_t = 3; -pub const ADVICE_DONTNEED: advice_t = 4; -pub const ADVICE_NOREUSE: advice_t = 5; +pub const advice_t = enum(u8) { + NORMAL = 0, + SEQUENTIAL = 1, + RANDOM = 2, + WILLNEED = 3, + DONTNEED = 4, + NOREUSE = 5, +}; -pub const clockid_t = u32; -pub const CLOCK = struct { - pub const REALTIME: clockid_t = 0; - pub const MONOTONIC: clockid_t = 1; - pub const PROCESS_CPUTIME_ID: clockid_t = 2; - pub const THREAD_CPUTIME_ID: clockid_t = 3; +pub const clockid_t = enum(u32) { + REALTIME = 0, + MONOTONIC = 1, + PROCESS_CPUTIME_ID = 2, + THREAD_CPUTIME_ID = 3, }; pub const device_t = u64; @@ -192,10 +104,10 @@ pub const DIRCOOKIE_START: dircookie_t = 0; pub const dirnamlen_t = u32; pub const dirent_t = extern struct { - d_next: dircookie_t, - d_ino: inode_t, - d_namlen: dirnamlen_t, - d_type: filetype_t, + next: dircookie_t, + ino: inode_t, + namlen: dirnamlen_t, + type: filetype_t, }; pub const errno_t = enum(u16) { @@ -280,7 +192,6 @@ pub const errno_t = enum(u16) { NOTCAPABLE = 76, _, }; -pub const E = errno_t; pub const event_t = extern struct { userdata: userdata_t, @@ -297,22 +208,23 @@ pub const eventfdreadwrite_t = extern struct { pub const eventrwflags_t = u16; pub const EVENT_FD_READWRITE_HANGUP: eventrwflags_t = 0x0001; -pub const eventtype_t = u8; -pub const EVENTTYPE_CLOCK: eventtype_t = 0; -pub const EVENTTYPE_FD_READ: eventtype_t = 1; -pub const EVENTTYPE_FD_WRITE: eventtype_t = 2; +pub const eventtype_t = enum(u8) { + CLOCK = 0, + FD_READ = 1, + FD_WRITE = 2, +}; pub const exitcode_t = u32; pub const fd_t = i32; -pub const fdflags_t = u16; -pub const FDFLAG = struct { - pub const APPEND: fdflags_t = 0x0001; - pub const DSYNC: fdflags_t = 0x0002; - pub const NONBLOCK: fdflags_t = 0x0004; - pub const RSYNC: fdflags_t = 0x0008; - pub const SYNC: fdflags_t = 0x0010; +pub const fdflags_t = packed struct(u16) { + APPEND: bool = false, + DSYNC: bool = false, + NONBLOCK: bool = false, + RSYNC: bool = false, + SYNC: bool = false, + _: u11 = 0, }; pub const fdstat_t = extern struct { @@ -335,21 +247,8 @@ pub const filestat_t = extern struct { atim: timestamp_t, mtim: timestamp_t, ctim: timestamp_t, - - pub fn atime(self: filestat_t) timespec { - return timespec.fromTimestamp(self.atim); - } - - pub fn mtime(self: filestat_t) timespec { - return timespec.fromTimestamp(self.mtim); - } - - pub fn ctime(self: filestat_t) timespec { - return timespec.fromTimestamp(self.ctim); - } }; -/// Also known as `FILETYPE`. pub const filetype_t = enum(u8) { UNKNOWN, BLOCK_DEVICE, @@ -362,26 +261,29 @@ pub const filetype_t = enum(u8) { _, }; -pub const fstflags_t = u16; -pub const FILESTAT_SET_ATIM: fstflags_t = 0x0001; -pub const FILESTAT_SET_ATIM_NOW: fstflags_t = 0x0002; -pub const FILESTAT_SET_MTIM: fstflags_t = 0x0004; -pub const FILESTAT_SET_MTIM_NOW: fstflags_t = 0x0008; +pub const fstflags_t = packed struct(u16) { + ATIM: bool = false, + ATIM_NOW: bool = false, + MTIM: bool = false, + MTIM_NOW: bool = false, + _: u12 = 0, +}; pub const inode_t = u64; -pub const ino_t = inode_t; pub const linkcount_t = u64; -pub const lookupflags_t = u32; -pub const LOOKUP_SYMLINK_FOLLOW: lookupflags_t = 0x00000001; +pub const lookupflags_t = packed struct(u32) { + SYMLINK_FOLLOW: bool = false, + _: u31 = 0, +}; -pub const oflags_t = u16; -pub const O = struct { - pub const CREAT: oflags_t = 0x0001; - pub const DIRECTORY: oflags_t = 0x0002; - pub const EXCL: oflags_t = 0x0004; - pub const TRUNC: oflags_t = 0x0008; +pub const oflags_t = packed struct(u16) { + CREAT: bool = false, + DIRECTORY: bool = false, + EXCL: bool = false, + TRUNC: bool = false, + _: u12 = 0, }; pub const preopentype_t = u8; @@ -410,110 +312,81 @@ pub const SOCK = struct { pub const RECV_DATA_TRUNCATED: roflags_t = 0x0001; }; -pub const rights_t = u64; -pub const RIGHT = struct { - pub const FD_DATASYNC: rights_t = 0x0000000000000001; - pub const FD_READ: rights_t = 0x0000000000000002; - pub const FD_SEEK: rights_t = 0x0000000000000004; - pub const FD_FDSTAT_SET_FLAGS: rights_t = 0x0000000000000008; - pub const FD_SYNC: rights_t = 0x0000000000000010; - pub const FD_TELL: rights_t = 0x0000000000000020; - pub const FD_WRITE: rights_t = 0x0000000000000040; - pub const FD_ADVISE: rights_t = 0x0000000000000080; - pub const FD_ALLOCATE: rights_t = 0x0000000000000100; - pub const PATH_CREATE_DIRECTORY: rights_t = 0x0000000000000200; - pub const PATH_CREATE_FILE: rights_t = 0x0000000000000400; - pub const PATH_LINK_SOURCE: rights_t = 0x0000000000000800; - pub const PATH_LINK_TARGET: rights_t = 0x0000000000001000; - pub const PATH_OPEN: rights_t = 0x0000000000002000; - pub const FD_READDIR: rights_t = 0x0000000000004000; - pub const PATH_READLINK: rights_t = 0x0000000000008000; - pub const PATH_RENAME_SOURCE: rights_t = 0x0000000000010000; - pub const PATH_RENAME_TARGET: rights_t = 0x0000000000020000; - pub const PATH_FILESTAT_GET: rights_t = 0x0000000000040000; - pub const PATH_FILESTAT_SET_SIZE: rights_t = 0x0000000000080000; - pub const PATH_FILESTAT_SET_TIMES: rights_t = 0x0000000000100000; - pub const FD_FILESTAT_GET: rights_t = 0x0000000000200000; - pub const FD_FILESTAT_SET_SIZE: rights_t = 0x0000000000400000; - pub const FD_FILESTAT_SET_TIMES: rights_t = 0x0000000000800000; - pub const PATH_SYMLINK: rights_t = 0x0000000001000000; - pub const PATH_REMOVE_DIRECTORY: rights_t = 0x0000000002000000; - pub const PATH_UNLINK_FILE: rights_t = 0x0000000004000000; - pub const POLL_FD_READWRITE: rights_t = 0x0000000008000000; - pub const SOCK_SHUTDOWN: rights_t = 0x0000000010000000; - pub const SOCK_ACCEPT: rights_t = 0x0000000020000000; - pub const ALL: rights_t = FD_DATASYNC | - FD_READ | - FD_SEEK | - FD_FDSTAT_SET_FLAGS | - FD_SYNC | - FD_TELL | - FD_WRITE | - FD_ADVISE | - FD_ALLOCATE | - PATH_CREATE_DIRECTORY | - PATH_CREATE_FILE | - PATH_LINK_SOURCE | - PATH_LINK_TARGET | - PATH_OPEN | - FD_READDIR | - PATH_READLINK | - PATH_RENAME_SOURCE | - PATH_RENAME_TARGET | - PATH_FILESTAT_GET | - PATH_FILESTAT_SET_SIZE | - PATH_FILESTAT_SET_TIMES | - FD_FILESTAT_GET | - FD_FILESTAT_SET_SIZE | - FD_FILESTAT_SET_TIMES | - PATH_SYMLINK | - PATH_REMOVE_DIRECTORY | - PATH_UNLINK_FILE | - POLL_FD_READWRITE | - SOCK_SHUTDOWN | - SOCK_ACCEPT; +pub const rights_t = packed struct(u64) { + FD_DATASYNC: bool = false, + FD_READ: bool = false, + FD_SEEK: bool = false, + FD_FDSTAT_SET_FLAGS: bool = false, + FD_SYNC: bool = false, + FD_TELL: bool = false, + FD_WRITE: bool = false, + FD_ADVISE: bool = false, + FD_ALLOCATE: bool = false, + PATH_CREATE_DIRECTORY: bool = false, + PATH_CREATE_FILE: bool = false, + PATH_LINK_SOURCE: bool = false, + PATH_LINK_TARGET: bool = false, + PATH_OPEN: bool = false, + FD_READDIR: bool = false, + PATH_READLINK: bool = false, + PATH_RENAME_SOURCE: bool = false, + PATH_RENAME_TARGET: bool = false, + PATH_FILESTAT_GET: bool = false, + PATH_FILESTAT_SET_SIZE: bool = false, + PATH_FILESTAT_SET_TIMES: bool = false, + FD_FILESTAT_GET: bool = false, + FD_FILESTAT_SET_SIZE: bool = false, + FD_FILESTAT_SET_TIMES: bool = false, + PATH_SYMLINK: bool = false, + PATH_REMOVE_DIRECTORY: bool = false, + PATH_UNLINK_FILE: bool = false, + POLL_FD_READWRITE: bool = false, + SOCK_SHUTDOWN: bool = false, + SOCK_ACCEPT: bool = false, + _: u34 = 0, }; -pub const sdflags_t = u8; -pub const SHUT = struct { - pub const RD: sdflags_t = 0x01; - pub const WR: sdflags_t = 0x02; +pub const sdflags_t = packed struct(u8) { + RD: bool = false, + WR: bool = false, + _: u6 = 0, }; pub const siflags_t = u16; -pub const signal_t = u8; -pub const SIGNONE: signal_t = 0; -pub const SIGHUP: signal_t = 1; -pub const SIGINT: signal_t = 2; -pub const SIGQUIT: signal_t = 3; -pub const SIGILL: signal_t = 4; -pub const SIGTRAP: signal_t = 5; -pub const SIGABRT: signal_t = 6; -pub const SIGBUS: signal_t = 7; -pub const SIGFPE: signal_t = 8; -pub const SIGKILL: signal_t = 9; -pub const SIGUSR1: signal_t = 10; -pub const SIGSEGV: signal_t = 11; -pub const SIGUSR2: signal_t = 12; -pub const SIGPIPE: signal_t = 13; -pub const SIGALRM: signal_t = 14; -pub const SIGTERM: signal_t = 15; -pub const SIGCHLD: signal_t = 16; -pub const SIGCONT: signal_t = 17; -pub const SIGSTOP: signal_t = 18; -pub const SIGTSTP: signal_t = 19; -pub const SIGTTIN: signal_t = 20; -pub const SIGTTOU: signal_t = 21; -pub const SIGURG: signal_t = 22; -pub const SIGXCPU: signal_t = 23; -pub const SIGXFSZ: signal_t = 24; -pub const SIGVTALRM: signal_t = 25; -pub const SIGPROF: signal_t = 26; -pub const SIGWINCH: signal_t = 27; -pub const SIGPOLL: signal_t = 28; -pub const SIGPWR: signal_t = 29; -pub const SIGSYS: signal_t = 30; +pub const signal_t = enum(u8) { + NONE = 0, + HUP = 1, + INT = 2, + QUIT = 3, + ILL = 4, + TRAP = 5, + ABRT = 6, + BUS = 7, + FPE = 8, + KILL = 9, + USR1 = 10, + SEGV = 11, + USR2 = 12, + PIPE = 13, + ALRM = 14, + TERM = 15, + CHLD = 16, + CONT = 17, + STOP = 18, + TSTP = 19, + TTIN = 20, + TTOU = 21, + URG = 22, + XCPU = 23, + XFSZ = 24, + VTALRM = 25, + PROF = 26, + WINCH = 27, + POLL = 28, + PWR = 29, + SYS = 30, +}; pub const subclockflags_t = u16; pub const SUBSCRIPTION_CLOCK_ABSTIME: subclockflags_t = 0x0001; @@ -545,29 +418,9 @@ pub const subscription_u_u_t = extern union { fd_write: subscription_fd_readwrite_t, }; +/// Nanoseconds. pub const timestamp_t = u64; pub const userdata_t = u64; -/// Also known as `WHENCE`. pub const whence_t = enum(u8) { SET, CUR, END }; - -pub const S = struct { - pub const IEXEC = @compileError("TODO audit this"); - pub const IFBLK = 0x6000; - pub const IFCHR = 0x2000; - pub const IFDIR = 0x4000; - pub const IFIFO = 0xc000; - pub const IFLNK = 0xa000; - pub const IFMT = IFBLK | IFCHR | IFDIR | IFIFO | IFLNK | IFREG | IFSOCK; - pub const IFREG = 0x8000; - // There's no concept of UNIX domain socket but we define this value here in order to line with other OSes. - pub const IFSOCK = 0x1; -}; - -pub const LOCK = struct { - pub const SH = 0x1; - pub const EX = 0x2; - pub const NB = 0x4; - pub const UN = 0x8; -}; diff --git a/lib/std/std.zig b/lib/std/std.zig index 34ed3c0037..98a1bba0dd 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -142,7 +142,10 @@ pub const meta = @import("meta.zig"); /// Networking. pub const net = @import("net.zig"); -/// Wrappers around OS-specific APIs. +/// POSIX-like API layer. +pub const posix = @import("os.zig"); + +/// Non-portable Operating System-specific API. pub const os = @import("os.zig"); pub const once = @import("once.zig").once; diff --git a/lib/std/time.zig b/lib/std/time.zig index 012dc838f8..425e028e01 100644 --- a/lib/std/time.zig +++ b/lib/std/time.zig @@ -19,19 +19,17 @@ pub fn sleep(nanoseconds: u64) void { if (builtin.os.tag == .wasi) { const w = std.os.wasi; const userdata: w.userdata_t = 0x0123_45678; - const clock = w.subscription_clock_t{ - .id = w.CLOCK.MONOTONIC, + const clock: w.subscription_clock_t = .{ + .id = .MONOTONIC, .timeout = nanoseconds, .precision = 0, .flags = 0, }; - const in = w.subscription_t{ + const in: w.subscription_t = .{ .userdata = userdata, - .u = w.subscription_u_t{ - .tag = w.EVENTTYPE_CLOCK, - .u = w.subscription_u_u_t{ - .clock = clock, - }, + .u = .{ + .tag = .CLOCK, + .u = .{ .clock = clock }, }, }; @@ -92,35 +90,36 @@ pub fn microTimestamp() i64 { /// before the epoch. /// See `std.os.clock_gettime` for a POSIX timestamp. pub fn nanoTimestamp() i128 { - if (builtin.os.tag == .windows) { - // FileTime has a granularity of 100 nanoseconds and uses the NTFS/Windows epoch, - // which is 1601-01-01. - const epoch_adj = epoch.windows * (ns_per_s / 100); - var ft: os.windows.FILETIME = undefined; - os.windows.kernel32.GetSystemTimeAsFileTime(&ft); - const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime; - return @as(i128, @as(i64, @bitCast(ft64)) + epoch_adj) * 100; + switch (builtin.os.tag) { + .windows => { + // FileTime has a granularity of 100 nanoseconds and uses the NTFS/Windows epoch, + // which is 1601-01-01. + const epoch_adj = epoch.windows * (ns_per_s / 100); + var ft: os.windows.FILETIME = undefined; + os.windows.kernel32.GetSystemTimeAsFileTime(&ft); + const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime; + return @as(i128, @as(i64, @bitCast(ft64)) + epoch_adj) * 100; + }, + .wasi => { + var ns: os.wasi.timestamp_t = undefined; + const err = os.wasi.clock_time_get(.REALTIME, 1, &ns); + assert(err == .SUCCESS); + return ns; + }, + .uefi => { + var value: std.os.uefi.Time = undefined; + const status = std.os.uefi.system_table.runtime_services.getTime(&value, null); + assert(status == .Success); + return value.toEpoch(); + }, + else => { + var ts: os.timespec = undefined; + os.clock_gettime(os.CLOCK.REALTIME, &ts) catch |err| switch (err) { + error.UnsupportedClock, error.Unexpected => return 0, // "Precision of timing depends on hardware and OS". + }; + return (@as(i128, ts.tv_sec) * ns_per_s) + ts.tv_nsec; + }, } - - if (builtin.os.tag == .wasi and !builtin.link_libc) { - var ns: os.wasi.timestamp_t = undefined; - const err = os.wasi.clock_time_get(os.wasi.CLOCK.REALTIME, 1, &ns); - assert(err == .SUCCESS); - return ns; - } - - if (builtin.os.tag == .uefi) { - var value: std.os.uefi.Time = undefined; - const status = std.os.uefi.system_table.runtime_services.getTime(&value, null); - assert(status == .Success); - return value.toEpoch(); - } - - var ts: os.timespec = undefined; - os.clock_gettime(os.CLOCK.REALTIME, &ts) catch |err| switch (err) { - error.UnsupportedClock, error.Unexpected => return 0, // "Precision of timing depends on hardware and OS". - }; - return (@as(i128, ts.tv_sec) * ns_per_s) + ts.tv_nsec; } test "timestamp" { @@ -177,43 +176,43 @@ pub const Instant = struct { // true if we should use clock_gettime() const is_posix = switch (builtin.os.tag) { - .wasi => builtin.link_libc, - .windows, .uefi => false, + .windows, .uefi, .wasi => false, else => true, }; /// Queries the system for the current moment of time as an Instant. - /// This is not guaranteed to be monotonic or steadily increasing, but for most implementations it is. + /// This is not guaranteed to be monotonic or steadily increasing, but for + /// most implementations it is. /// Returns `error.Unsupported` when a suitable clock is not detected. pub fn now() error{Unsupported}!Instant { - // QPC on windows doesn't fail on >= XP/2000 and includes time suspended. - if (builtin.os.tag == .windows) { - return Instant{ .timestamp = os.windows.QueryPerformanceCounter() }; - } - - // On WASI without libc, use clock_time_get directly. - if (builtin.os.tag == .wasi and !builtin.link_libc) { - var ns: os.wasi.timestamp_t = undefined; - const rc = os.wasi.clock_time_get(os.wasi.CLOCK.MONOTONIC, 1, &ns); - if (rc != .SUCCESS) return error.Unsupported; - return Instant{ .timestamp = ns }; - } - - if (builtin.os.tag == .uefi) { - var value: std.os.uefi.Time = undefined; - const status = std.os.uefi.system_table.runtime_services.getTime(&value, null); - if (status != .Success) return error.Unsupported; - return Instant{ .timestamp = value.toEpoch() }; - } - - // On darwin, use UPTIME_RAW instead of MONOTONIC as it ticks while suspended. - // On linux, use BOOTTIME instead of MONOTONIC as it ticks while suspended. - // On freebsd derivatives, use MONOTONIC_FAST as currently there's no precision tradeoff. - // On other posix systems, MONOTONIC is generally the fastest and ticks while suspended. const clock_id = switch (builtin.os.tag) { + .windows => { + // QPC on windows doesn't fail on >= XP/2000 and includes time suspended. + return Instant{ .timestamp = os.windows.QueryPerformanceCounter() }; + }, + .wasi => { + var ns: os.wasi.timestamp_t = undefined; + const rc = os.wasi.clock_time_get(.MONOTONIC, 1, &ns); + if (rc != .SUCCESS) return error.Unsupported; + return .{ .timestamp = ns }; + }, + .uefi => { + var value: std.os.uefi.Time = undefined; + const status = std.os.uefi.system_table.runtime_services.getTime(&value, null); + if (status != .Success) return error.Unsupported; + return Instant{ .timestamp = value.toEpoch() }; + }, + // On darwin, use UPTIME_RAW instead of MONOTONIC as it ticks while + // suspended. .macos, .ios, .tvos, .watchos => os.CLOCK.UPTIME_RAW, + // On freebsd derivatives, use MONOTONIC_FAST as currently there's + // no precision tradeoff. .freebsd, .dragonfly => os.CLOCK.MONOTONIC_FAST, + // On linux, use BOOTTIME instead of MONOTONIC as it ticks while + // suspended. .linux => os.CLOCK.BOOTTIME, + // On other posix systems, MONOTONIC is generally the fastest and + // ticks while suspended. else => os.CLOCK.MONOTONIC, }; @@ -262,7 +261,7 @@ pub const Instant = struct { } // WASI timestamps are directly in nanoseconds - if (builtin.os.tag == .wasi and !builtin.link_libc) { + if (builtin.os.tag == .wasi) { return self.timestamp - earlier.timestamp; }