mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
wasi: fix wasm-wasi-musl constants
Zig's copy of the `SYMLINK_{NO,}FOLLOW` constants from wasi-musl was
wrong, as were the `IFIFO` and `IFSOCK` file type flags. Fix these up,
and add comments pointing to exactly where they come from (as the
wasi-musl source has lots of unused, different definitions of these
constants).
Add tests for the Zig convention that WASM preopen 3 is the current
working directory. This is true for WASM with or without libc.
Enable several fs and posix tests that are now passing (not necessarily
because of this change) on wasm targets.
Fixes #20890.
This commit is contained in:
parent
0af492a272
commit
138d30bb47
@ -692,6 +692,7 @@ pub const F = switch (native_os) {
|
|||||||
.linux => linux.F,
|
.linux => linux.F,
|
||||||
.emscripten => emscripten.F,
|
.emscripten => emscripten.F,
|
||||||
.wasi => struct {
|
.wasi => struct {
|
||||||
|
// Match `F_*` constants from lib/libc/include/wasm-wasi-musl/__header_fcntl.h
|
||||||
pub const GETFD = 1;
|
pub const GETFD = 1;
|
||||||
pub const SETFD = 2;
|
pub const SETFD = 2;
|
||||||
pub const GETFL = 3;
|
pub const GETFL = 3;
|
||||||
@ -1723,17 +1724,43 @@ pub const S = switch (native_os) {
|
|||||||
.linux => linux.S,
|
.linux => linux.S,
|
||||||
.emscripten => emscripten.S,
|
.emscripten => emscripten.S,
|
||||||
.wasi => struct {
|
.wasi => struct {
|
||||||
pub const IEXEC = @compileError("TODO audit this");
|
// Match `S_*` constants from lib/libc/include/wasm-wasi-musl/__mode_t.h
|
||||||
pub const IFBLK = 0x6000;
|
pub const IFBLK = 0x6000;
|
||||||
pub const IFCHR = 0x2000;
|
pub const IFCHR = 0x2000;
|
||||||
pub const IFDIR = 0x4000;
|
pub const IFDIR = 0x4000;
|
||||||
pub const IFIFO = 0xc000;
|
pub const IFIFO = 0x1000;
|
||||||
pub const IFLNK = 0xa000;
|
pub const IFLNK = 0xa000;
|
||||||
pub const IFMT = IFBLK | IFCHR | IFDIR | IFIFO | IFLNK | IFREG | IFSOCK;
|
pub const IFMT = IFBLK | IFCHR | IFDIR | IFIFO | IFLNK | IFREG | IFSOCK;
|
||||||
pub const IFREG = 0x8000;
|
pub const IFREG = 0x8000;
|
||||||
/// There's no concept of UNIX domain socket but we define this value here
|
pub const IFSOCK = 0xc000;
|
||||||
/// in order to line with other OSes.
|
|
||||||
pub const IFSOCK = 0x1;
|
pub fn ISBLK(m: u32) bool {
|
||||||
|
return m & IFMT == IFBLK;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ISCHR(m: u32) bool {
|
||||||
|
return m & IFMT == IFCHR;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ISDIR(m: u32) bool {
|
||||||
|
return m & IFMT == IFDIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ISFIFO(m: u32) bool {
|
||||||
|
return m & IFMT == IFIFO;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ISLNK(m: u32) bool {
|
||||||
|
return m & IFMT == IFLNK;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ISREG(m: u32) bool {
|
||||||
|
return m & IFMT == IFREG;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ISSOCK(m: u32) bool {
|
||||||
|
return m & IFMT == IFSOCK;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
.macos, .ios, .tvos, .watchos, .visionos => struct {
|
.macos, .ios, .tvos, .watchos, .visionos => struct {
|
||||||
pub const IFMT = 0o170000;
|
pub const IFMT = 0o170000;
|
||||||
@ -6802,6 +6829,7 @@ pub const Stat = switch (native_os) {
|
|||||||
},
|
},
|
||||||
.emscripten => emscripten.Stat,
|
.emscripten => emscripten.Stat,
|
||||||
.wasi => extern struct {
|
.wasi => extern struct {
|
||||||
|
// Match wasi-libc's `struct stat` in lib/libc/include/wasm-wasi-musl/__struct_stat.h
|
||||||
dev: dev_t,
|
dev: dev_t,
|
||||||
ino: ino_t,
|
ino: ino_t,
|
||||||
nlink: nlink_t,
|
nlink: nlink_t,
|
||||||
@ -7502,9 +7530,11 @@ pub const AT = switch (native_os) {
|
|||||||
pub const RECURSIVE = 0x8000;
|
pub const RECURSIVE = 0x8000;
|
||||||
},
|
},
|
||||||
.wasi => struct {
|
.wasi => struct {
|
||||||
pub const SYMLINK_NOFOLLOW = 0x100;
|
// Match `AT_*` constants in lib/libc/include/wasm-wasi-musl/__header_fcntl.h
|
||||||
pub const SYMLINK_FOLLOW = 0x400;
|
pub const EACCESS = 0x0;
|
||||||
pub const REMOVEDIR: u32 = 0x4;
|
pub const SYMLINK_NOFOLLOW = 0x1;
|
||||||
|
pub const SYMLINK_FOLLOW = 0x2;
|
||||||
|
pub const REMOVEDIR = 0x4;
|
||||||
/// When linking libc, we follow their convention and use -2 for current working directory.
|
/// 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
|
/// However, without libc, Zig does a different convention: it assumes the
|
||||||
/// current working directory is the first preopen. This behavior can be
|
/// current working directory is the first preopen. This behavior can be
|
||||||
@ -7512,7 +7542,6 @@ pub const AT = switch (native_os) {
|
|||||||
/// file.
|
/// file.
|
||||||
pub const FDCWD: fd_t = if (builtin.link_libc) -2 else 3;
|
pub const FDCWD: fd_t = if (builtin.link_libc) -2 else 3;
|
||||||
},
|
},
|
||||||
|
|
||||||
else => void,
|
else => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -7541,6 +7570,7 @@ pub const O = switch (native_os) {
|
|||||||
_: u9 = 0,
|
_: u9 = 0,
|
||||||
},
|
},
|
||||||
.wasi => packed struct(u32) {
|
.wasi => packed struct(u32) {
|
||||||
|
// Match `O_*` bits from lib/libc/include/wasm-wasi-musl/__header_fcntl.h
|
||||||
APPEND: bool = false,
|
APPEND: bool = false,
|
||||||
DSYNC: bool = false,
|
DSYNC: bool = false,
|
||||||
NONBLOCK: bool = false,
|
NONBLOCK: bool = false,
|
||||||
@ -7557,6 +7587,8 @@ pub const O = switch (native_os) {
|
|||||||
read: bool = false,
|
read: bool = false,
|
||||||
SEARCH: bool = false,
|
SEARCH: bool = false,
|
||||||
write: bool = false,
|
write: bool = false,
|
||||||
|
// O_CLOEXEC, O_TTY_ININT, O_NOCTTY are 0 in wasi-musl, so they're silently
|
||||||
|
// ignored in C code. Thus no mapping in Zig.
|
||||||
_: u3 = 0,
|
_: u3 = 0,
|
||||||
},
|
},
|
||||||
.solaris, .illumos => packed struct(u32) {
|
.solaris, .illumos => packed struct(u32) {
|
||||||
|
|||||||
@ -1295,17 +1295,10 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathE
|
|||||||
return self.realpathW(pathname_w.span(), out_buffer);
|
return self.realpathW(pathname_w.span(), out_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
const flags: posix.O = switch (native_os) {
|
var flags: posix.O = .{};
|
||||||
.linux => .{
|
if (@hasField(posix.O, "NONBLOCK")) flags.NONBLOCK = true;
|
||||||
.NONBLOCK = true,
|
if (@hasField(posix.O, "CLOEXEC")) flags.CLOEXEC = true;
|
||||||
.CLOEXEC = true,
|
if (@hasField(posix.O, "PATH")) flags.PATH = true;
|
||||||
.PATH = true,
|
|
||||||
},
|
|
||||||
else => .{
|
|
||||||
.NONBLOCK = true,
|
|
||||||
.CLOEXEC = true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const fd = posix.openatZ(self.fd, pathname, flags, 0) catch |err| switch (err) {
|
const fd = posix.openatZ(self.fd, pathname, flags, 0) catch |err| switch (err) {
|
||||||
error.FileLocksNotSupported => return error.Unexpected,
|
error.FileLocksNotSupported => return error.Unexpected,
|
||||||
|
|||||||
@ -361,9 +361,12 @@ test "openDirAbsolute" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "openDir cwd parent '..'" {
|
test "openDir cwd parent '..'" {
|
||||||
if (native_os == .wasi) return error.SkipZigTest;
|
var dir = fs.cwd().openDir("..", .{}) catch |err| {
|
||||||
|
if (native_os == .wasi and err == error.AccessDenied) {
|
||||||
var dir = try fs.cwd().openDir("..", .{});
|
return; // This is okay. WASI disallows escaping from the fs sandbox
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
};
|
||||||
defer dir.close();
|
defer dir.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1678,8 +1681,6 @@ test "read from locked file" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "walker" {
|
test "walker" {
|
||||||
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
|
|
||||||
|
|
||||||
var tmp = tmpDir(.{ .iterate = true });
|
var tmp = tmpDir(.{ .iterate = true });
|
||||||
defer tmp.cleanup();
|
defer tmp.cleanup();
|
||||||
|
|
||||||
@ -1731,8 +1732,6 @@ test "walker" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "walker without fully iterating" {
|
test "walker without fully iterating" {
|
||||||
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
|
|
||||||
|
|
||||||
var tmp = tmpDir(.{ .iterate = true });
|
var tmp = tmpDir(.{ .iterate = true });
|
||||||
defer tmp.cleanup();
|
defer tmp.cleanup();
|
||||||
|
|
||||||
@ -1754,8 +1753,6 @@ test "walker without fully iterating" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "'.' and '..' in fs.Dir functions" {
|
test "'.' and '..' in fs.Dir functions" {
|
||||||
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
|
|
||||||
|
|
||||||
if (native_os == .windows and builtin.cpu.arch == .aarch64) {
|
if (native_os == .windows and builtin.cpu.arch == .aarch64) {
|
||||||
// https://github.com/ziglang/zig/issues/17134
|
// https://github.com/ziglang/zig/issues/17134
|
||||||
return error.SkipZigTest;
|
return error.SkipZigTest;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
//! wasi_snapshot_preview1 spec available (in witx format) here:
|
//! wasi_snapshot_preview1 spec available (in witx format) here:
|
||||||
//! * typenames -- https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/witx/typenames.witx
|
//! * 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
|
//! * 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.
|
//! Note that libc API does *not* go in this file. wasi libc API goes into std/c.zig instead.
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
|||||||
@ -5143,10 +5143,10 @@ pub fn sysctl(
|
|||||||
newlen: usize,
|
newlen: usize,
|
||||||
) SysCtlError!void {
|
) SysCtlError!void {
|
||||||
if (native_os == .wasi) {
|
if (native_os == .wasi) {
|
||||||
@panic("unsupported"); // TODO should be compile error, not panic
|
@compileError("sysctl not supported on WASI");
|
||||||
}
|
}
|
||||||
if (native_os == .haiku) {
|
if (native_os == .haiku) {
|
||||||
@panic("unsupported"); // TODO should be compile error, not panic
|
@compileError("sysctl not supported on Haiku");
|
||||||
}
|
}
|
||||||
|
|
||||||
const name_len = cast(c_uint, name.len) orelse return error.NameTooLong;
|
const name_len = cast(c_uint, name.len) orelse return error.NameTooLong;
|
||||||
@ -5168,10 +5168,10 @@ pub fn sysctlbynameZ(
|
|||||||
newlen: usize,
|
newlen: usize,
|
||||||
) SysCtlError!void {
|
) SysCtlError!void {
|
||||||
if (native_os == .wasi) {
|
if (native_os == .wasi) {
|
||||||
@panic("unsupported"); // TODO should be compile error, not panic
|
@compileError("sysctl not supported on WASI");
|
||||||
}
|
}
|
||||||
if (native_os == .haiku) {
|
if (native_os == .haiku) {
|
||||||
@panic("unsupported"); // TODO should be compile error, not panic
|
@compileError("sysctl not supported on Haiku");
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) {
|
switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) {
|
||||||
|
|||||||
@ -31,6 +31,19 @@ test "WTF-8 to WTF-16 conversion buffer overflows" {
|
|||||||
try expectError(error.NameTooLong, posix.chdirZ(input_wtf8));
|
try expectError(error.NameTooLong, posix.chdirZ(input_wtf8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "check WASI CWD" {
|
||||||
|
if (native_os == .wasi) {
|
||||||
|
if (std.options.wasiCwd() != 3) {
|
||||||
|
@panic("WASI code that uses cwd (like this test) needs a preopen for cwd (add '--dir=.' to wasmtime)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!builtin.link_libc) {
|
||||||
|
// WASI without-libc hardcodes fd 3 as the FDCWD token so it can be passed directly to WASI calls
|
||||||
|
try expectEqual(3, posix.AT.FDCWD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "chdir smoke test" {
|
test "chdir smoke test" {
|
||||||
if (native_os == .wasi) return error.SkipZigTest;
|
if (native_os == .wasi) return error.SkipZigTest;
|
||||||
|
|
||||||
@ -151,7 +164,6 @@ test "open smoke test" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "openat smoke test" {
|
test "openat smoke test" {
|
||||||
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
|
|
||||||
if (native_os == .windows) return error.SkipZigTest;
|
if (native_os == .windows) return error.SkipZigTest;
|
||||||
|
|
||||||
// TODO verify file attributes using `fstatat`
|
// TODO verify file attributes using `fstatat`
|
||||||
@ -200,11 +212,14 @@ test "openat smoke test" {
|
|||||||
}), mode);
|
}), mode);
|
||||||
posix.close(fd);
|
posix.close(fd);
|
||||||
|
|
||||||
// Try opening as file which should fail.
|
// Try opening as file which should fail (skip on wasi+libc due to
|
||||||
|
// https://github.com/bytecodealliance/wasmtime/issues/9054)
|
||||||
|
if (native_os != .wasi or !builtin.link_libc) {
|
||||||
try expectError(error.IsDir, posix.openat(tmp.dir.fd, "some_dir", CommonOpenFlags.lower(.{
|
try expectError(error.IsDir, posix.openat(tmp.dir.fd, "some_dir", CommonOpenFlags.lower(.{
|
||||||
.ACCMODE = .RDWR,
|
.ACCMODE = .RDWR,
|
||||||
}), mode));
|
}), mode));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "symlink with relative paths" {
|
test "symlink with relative paths" {
|
||||||
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
|
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||||
@ -366,8 +381,7 @@ test "fstatat" {
|
|||||||
defer file.close();
|
defer file.close();
|
||||||
|
|
||||||
// now repeat but using `fstatat` instead
|
// now repeat but using `fstatat` instead
|
||||||
const flags = if (native_os == .wasi) 0x0 else posix.AT.SYMLINK_NOFOLLOW;
|
const statat = try posix.fstatat(tmp.dir.fd, "file.txt", posix.AT.SYMLINK_NOFOLLOW);
|
||||||
const statat = try posix.fstatat(tmp.dir.fd, "file.txt", flags);
|
|
||||||
|
|
||||||
// s390x-linux does not have nanosecond precision for fstat(), but it does for fstatat(). As a
|
// s390x-linux does not have nanosecond precision for fstat(), but it does for fstatat(). As a
|
||||||
// result, comparing the two structures is doomed to fail.
|
// result, comparing the two structures is doomed to fail.
|
||||||
@ -1308,22 +1322,17 @@ const CommonOpenFlags = packed struct {
|
|||||||
NONBLOCK: bool = false,
|
NONBLOCK: bool = false,
|
||||||
|
|
||||||
pub fn lower(cof: CommonOpenFlags) posix.O {
|
pub fn lower(cof: CommonOpenFlags) posix.O {
|
||||||
if (native_os == .wasi) return .{
|
var result: posix.O = if (native_os == .wasi) .{
|
||||||
.read = cof.ACCMODE != .WRONLY,
|
.read = cof.ACCMODE != .WRONLY,
|
||||||
.write = cof.ACCMODE != .RDONLY,
|
.write = cof.ACCMODE != .RDONLY,
|
||||||
.CREAT = cof.CREAT,
|
} else .{
|
||||||
.EXCL = cof.EXCL,
|
|
||||||
.DIRECTORY = cof.DIRECTORY,
|
|
||||||
.NONBLOCK = cof.NONBLOCK,
|
|
||||||
};
|
|
||||||
var result: posix.O = .{
|
|
||||||
.ACCMODE = cof.ACCMODE,
|
.ACCMODE = cof.ACCMODE,
|
||||||
.CREAT = cof.CREAT,
|
|
||||||
.EXCL = cof.EXCL,
|
|
||||||
.DIRECTORY = cof.DIRECTORY,
|
|
||||||
.NONBLOCK = cof.NONBLOCK,
|
|
||||||
.CLOEXEC = cof.CLOEXEC,
|
|
||||||
};
|
};
|
||||||
|
result.CREAT = cof.CREAT;
|
||||||
|
result.EXCL = cof.EXCL;
|
||||||
|
result.DIRECTORY = cof.DIRECTORY;
|
||||||
|
result.NONBLOCK = cof.NONBLOCK;
|
||||||
|
if (@hasField(posix.O, "CLOEXEC")) result.CLOEXEC = cof.CLOEXEC;
|
||||||
if (@hasField(posix.O, "LARGEFILE")) result.LARGEFILE = cof.LARGEFILE;
|
if (@hasField(posix.O, "LARGEFILE")) result.LARGEFILE = cof.LARGEFILE;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user