Skip to content

Commit 138d30b

Browse files
rootbeeralexrp
authored andcommitted
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.
1 parent 0af492a commit 138d30b

File tree

6 files changed

+84
-53
lines changed

6 files changed

+84
-53
lines changed

lib/std/c.zig

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,7 @@ pub const F = switch (native_os) {
692692
.linux => linux.F,
693693
.emscripten => emscripten.F,
694694
.wasi => struct {
695+
// Match `F_*` constants from lib/libc/include/wasm-wasi-musl/__header_fcntl.h
695696
pub const GETFD = 1;
696697
pub const SETFD = 2;
697698
pub const GETFL = 3;
@@ -1723,17 +1724,43 @@ pub const S = switch (native_os) {
17231724
.linux => linux.S,
17241725
.emscripten => emscripten.S,
17251726
.wasi => struct {
1726-
pub const IEXEC = @compileError("TODO audit this");
1727+
// Match `S_*` constants from lib/libc/include/wasm-wasi-musl/__mode_t.h
17271728
pub const IFBLK = 0x6000;
17281729
pub const IFCHR = 0x2000;
17291730
pub const IFDIR = 0x4000;
1730-
pub const IFIFO = 0xc000;
1731+
pub const IFIFO = 0x1000;
17311732
pub const IFLNK = 0xa000;
17321733
pub const IFMT = IFBLK | IFCHR | IFDIR | IFIFO | IFLNK | IFREG | IFSOCK;
17331734
pub const IFREG = 0x8000;
1734-
/// There's no concept of UNIX domain socket but we define this value here
1735-
/// in order to line with other OSes.
1736-
pub const IFSOCK = 0x1;
1735+
pub const IFSOCK = 0xc000;
1736+
1737+
pub fn ISBLK(m: u32) bool {
1738+
return m & IFMT == IFBLK;
1739+
}
1740+
1741+
pub fn ISCHR(m: u32) bool {
1742+
return m & IFMT == IFCHR;
1743+
}
1744+
1745+
pub fn ISDIR(m: u32) bool {
1746+
return m & IFMT == IFDIR;
1747+
}
1748+
1749+
pub fn ISFIFO(m: u32) bool {
1750+
return m & IFMT == IFIFO;
1751+
}
1752+
1753+
pub fn ISLNK(m: u32) bool {
1754+
return m & IFMT == IFLNK;
1755+
}
1756+
1757+
pub fn ISREG(m: u32) bool {
1758+
return m & IFMT == IFREG;
1759+
}
1760+
1761+
pub fn ISSOCK(m: u32) bool {
1762+
return m & IFMT == IFSOCK;
1763+
}
17371764
},
17381765
.macos, .ios, .tvos, .watchos, .visionos => struct {
17391766
pub const IFMT = 0o170000;
@@ -6802,6 +6829,7 @@ pub const Stat = switch (native_os) {
68026829
},
68036830
.emscripten => emscripten.Stat,
68046831
.wasi => extern struct {
6832+
// Match wasi-libc's `struct stat` in lib/libc/include/wasm-wasi-musl/__struct_stat.h
68056833
dev: dev_t,
68066834
ino: ino_t,
68076835
nlink: nlink_t,
@@ -7502,17 +7530,18 @@ pub const AT = switch (native_os) {
75027530
pub const RECURSIVE = 0x8000;
75037531
},
75047532
.wasi => struct {
7505-
pub const SYMLINK_NOFOLLOW = 0x100;
7506-
pub const SYMLINK_FOLLOW = 0x400;
7507-
pub const REMOVEDIR: u32 = 0x4;
7533+
// Match `AT_*` constants in lib/libc/include/wasm-wasi-musl/__header_fcntl.h
7534+
pub const EACCESS = 0x0;
7535+
pub const SYMLINK_NOFOLLOW = 0x1;
7536+
pub const SYMLINK_FOLLOW = 0x2;
7537+
pub const REMOVEDIR = 0x4;
75087538
/// When linking libc, we follow their convention and use -2 for current working directory.
75097539
/// However, without libc, Zig does a different convention: it assumes the
75107540
/// current working directory is the first preopen. This behavior can be
75117541
/// overridden with a public function called `wasi_cwd` in the root source
75127542
/// file.
75137543
pub const FDCWD: fd_t = if (builtin.link_libc) -2 else 3;
75147544
},
7515-
75167545
else => void,
75177546
};
75187547

@@ -7541,6 +7570,7 @@ pub const O = switch (native_os) {
75417570
_: u9 = 0,
75427571
},
75437572
.wasi => packed struct(u32) {
7573+
// Match `O_*` bits from lib/libc/include/wasm-wasi-musl/__header_fcntl.h
75447574
APPEND: bool = false,
75457575
DSYNC: bool = false,
75467576
NONBLOCK: bool = false,
@@ -7557,6 +7587,8 @@ pub const O = switch (native_os) {
75577587
read: bool = false,
75587588
SEARCH: bool = false,
75597589
write: bool = false,
7590+
// O_CLOEXEC, O_TTY_ININT, O_NOCTTY are 0 in wasi-musl, so they're silently
7591+
// ignored in C code. Thus no mapping in Zig.
75607592
_: u3 = 0,
75617593
},
75627594
.solaris, .illumos => packed struct(u32) {

lib/std/fs/Dir.zig

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,17 +1295,10 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathE
12951295
return self.realpathW(pathname_w.span(), out_buffer);
12961296
}
12971297

1298-
const flags: posix.O = switch (native_os) {
1299-
.linux => .{
1300-
.NONBLOCK = true,
1301-
.CLOEXEC = true,
1302-
.PATH = true,
1303-
},
1304-
else => .{
1305-
.NONBLOCK = true,
1306-
.CLOEXEC = true,
1307-
},
1308-
};
1298+
var flags: posix.O = .{};
1299+
if (@hasField(posix.O, "NONBLOCK")) flags.NONBLOCK = true;
1300+
if (@hasField(posix.O, "CLOEXEC")) flags.CLOEXEC = true;
1301+
if (@hasField(posix.O, "PATH")) flags.PATH = true;
13091302

13101303
const fd = posix.openatZ(self.fd, pathname, flags, 0) catch |err| switch (err) {
13111304
error.FileLocksNotSupported => return error.Unexpected,

lib/std/fs/test.zig

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -361,9 +361,12 @@ test "openDirAbsolute" {
361361
}
362362

363363
test "openDir cwd parent '..'" {
364-
if (native_os == .wasi) return error.SkipZigTest;
365-
366-
var dir = try fs.cwd().openDir("..", .{});
364+
var dir = fs.cwd().openDir("..", .{}) catch |err| {
365+
if (native_os == .wasi and err == error.AccessDenied) {
366+
return; // This is okay. WASI disallows escaping from the fs sandbox
367+
}
368+
return err;
369+
};
367370
defer dir.close();
368371
}
369372

@@ -1678,8 +1681,6 @@ test "read from locked file" {
16781681
}
16791682

16801683
test "walker" {
1681-
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
1682-
16831684
var tmp = tmpDir(.{ .iterate = true });
16841685
defer tmp.cleanup();
16851686

@@ -1731,8 +1732,6 @@ test "walker" {
17311732
}
17321733

17331734
test "walker without fully iterating" {
1734-
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
1735-
17361735
var tmp = tmpDir(.{ .iterate = true });
17371736
defer tmp.cleanup();
17381737

@@ -1754,8 +1753,6 @@ test "walker without fully iterating" {
17541753
}
17551754

17561755
test "'.' and '..' in fs.Dir functions" {
1757-
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
1758-
17591756
if (native_os == .windows and builtin.cpu.arch == .aarch64) {
17601757
// https://github.com/ziglang/zig/issues/17134
17611758
return error.SkipZigTest;

lib/std/os/wasi.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! wasi_snapshot_preview1 spec available (in witx format) here:
22
//! * typenames -- https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/witx/typenames.witx
33
//! * module -- https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/witx/wasi_snapshot_preview1.witx
4-
//! Note that libc API does *not* go in this file. wasi libc API goes into std/c/wasi.zig instead.
4+
//! Note that libc API does *not* go in this file. wasi libc API goes into std/c.zig instead.
55
const builtin = @import("builtin");
66
const std = @import("std");
77
const assert = std.debug.assert;

lib/std/posix.zig

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5143,10 +5143,10 @@ pub fn sysctl(
51435143
newlen: usize,
51445144
) SysCtlError!void {
51455145
if (native_os == .wasi) {
5146-
@panic("unsupported"); // TODO should be compile error, not panic
5146+
@compileError("sysctl not supported on WASI");
51475147
}
51485148
if (native_os == .haiku) {
5149-
@panic("unsupported"); // TODO should be compile error, not panic
5149+
@compileError("sysctl not supported on Haiku");
51505150
}
51515151

51525152
const name_len = cast(c_uint, name.len) orelse return error.NameTooLong;
@@ -5168,10 +5168,10 @@ pub fn sysctlbynameZ(
51685168
newlen: usize,
51695169
) SysCtlError!void {
51705170
if (native_os == .wasi) {
5171-
@panic("unsupported"); // TODO should be compile error, not panic
5171+
@compileError("sysctl not supported on WASI");
51725172
}
51735173
if (native_os == .haiku) {
5174-
@panic("unsupported"); // TODO should be compile error, not panic
5174+
@compileError("sysctl not supported on Haiku");
51755175
}
51765176

51775177
switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) {

lib/std/posix/test.zig

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,19 @@ test "WTF-8 to WTF-16 conversion buffer overflows" {
3131
try expectError(error.NameTooLong, posix.chdirZ(input_wtf8));
3232
}
3333

34+
test "check WASI CWD" {
35+
if (native_os == .wasi) {
36+
if (std.options.wasiCwd() != 3) {
37+
@panic("WASI code that uses cwd (like this test) needs a preopen for cwd (add '--dir=.' to wasmtime)");
38+
}
39+
40+
if (!builtin.link_libc) {
41+
// WASI without-libc hardcodes fd 3 as the FDCWD token so it can be passed directly to WASI calls
42+
try expectEqual(3, posix.AT.FDCWD);
43+
}
44+
}
45+
}
46+
3447
test "chdir smoke test" {
3548
if (native_os == .wasi) return error.SkipZigTest;
3649

@@ -151,7 +164,6 @@ test "open smoke test" {
151164
}
152165

153166
test "openat smoke test" {
154-
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
155167
if (native_os == .windows) return error.SkipZigTest;
156168

157169
// TODO verify file attributes using `fstatat`
@@ -200,10 +212,13 @@ test "openat smoke test" {
200212
}), mode);
201213
posix.close(fd);
202214

203-
// Try opening as file which should fail.
204-
try expectError(error.IsDir, posix.openat(tmp.dir.fd, "some_dir", CommonOpenFlags.lower(.{
205-
.ACCMODE = .RDWR,
206-
}), mode));
215+
// Try opening as file which should fail (skip on wasi+libc due to
216+
// https://github.com/bytecodealliance/wasmtime/issues/9054)
217+
if (native_os != .wasi or !builtin.link_libc) {
218+
try expectError(error.IsDir, posix.openat(tmp.dir.fd, "some_dir", CommonOpenFlags.lower(.{
219+
.ACCMODE = .RDWR,
220+
}), mode));
221+
}
207222
}
208223

209224
test "symlink with relative paths" {
@@ -366,8 +381,7 @@ test "fstatat" {
366381
defer file.close();
367382

368383
// now repeat but using `fstatat` instead
369-
const flags = if (native_os == .wasi) 0x0 else posix.AT.SYMLINK_NOFOLLOW;
370-
const statat = try posix.fstatat(tmp.dir.fd, "file.txt", flags);
384+
const statat = try posix.fstatat(tmp.dir.fd, "file.txt", posix.AT.SYMLINK_NOFOLLOW);
371385

372386
// s390x-linux does not have nanosecond precision for fstat(), but it does for fstatat(). As a
373387
// result, comparing the two structures is doomed to fail.
@@ -1308,22 +1322,17 @@ const CommonOpenFlags = packed struct {
13081322
NONBLOCK: bool = false,
13091323

13101324
pub fn lower(cof: CommonOpenFlags) posix.O {
1311-
if (native_os == .wasi) return .{
1325+
var result: posix.O = if (native_os == .wasi) .{
13121326
.read = cof.ACCMODE != .WRONLY,
13131327
.write = cof.ACCMODE != .RDONLY,
1314-
.CREAT = cof.CREAT,
1315-
.EXCL = cof.EXCL,
1316-
.DIRECTORY = cof.DIRECTORY,
1317-
.NONBLOCK = cof.NONBLOCK,
1318-
};
1319-
var result: posix.O = .{
1328+
} else .{
13201329
.ACCMODE = cof.ACCMODE,
1321-
.CREAT = cof.CREAT,
1322-
.EXCL = cof.EXCL,
1323-
.DIRECTORY = cof.DIRECTORY,
1324-
.NONBLOCK = cof.NONBLOCK,
1325-
.CLOEXEC = cof.CLOEXEC,
13261330
};
1331+
result.CREAT = cof.CREAT;
1332+
result.EXCL = cof.EXCL;
1333+
result.DIRECTORY = cof.DIRECTORY;
1334+
result.NONBLOCK = cof.NONBLOCK;
1335+
if (@hasField(posix.O, "CLOEXEC")) result.CLOEXEC = cof.CLOEXEC;
13271336
if (@hasField(posix.O, "LARGEFILE")) result.LARGEFILE = cof.LARGEFILE;
13281337
return result;
13291338
}

0 commit comments

Comments
 (0)