Skip to content

Commit a13c054

Browse files
committed
wasi: split with-libc and without-libc implementations
Make a clearer separation between the Posix defines for WASI without a libc (the `system` defined in posix.zig) and the Posix defines for WASI with a libc (which should mirror the Musl-based wasi-libc implementation, and are defined in c.zig). Make WASI `posix.O` look more POSIX-y when compiling without libc. These structures and constants do not need to mirror the with-libc Wasi API (e.g., can use `.ACCMODE`). This removes some Wasi-specific code in Dir.zig and posix.zig by making the structure use names consistent with other platforms. Define `.S`, `.timespec`, and `.AT` structs for Wasi-without-libc in posix.zig. These are based on the wasi-with-libc structures, but cleaned up to remove cruft like padding or wholly unsupported fields. Fix `.S` and `.AT` constants on wasi-with-libc (defined in c.zig) to match wasi-libc headers. Define `mode_t` type as `void` for wasi targets because file permissions are not supported via the Posix APIS on these platforms. Use `mode_t` as the parameter type in Zig's mkdir\* and openat\* wrappers. Re-enable a lot of tests that have accumulated stale not-on-wasi guards, and simplify some tests (e.g., dropping unnecessary absolute paths) so they can run on wasi targets.
1 parent eadc0c0 commit a13c054

File tree

10 files changed

+439
-244
lines changed

10 files changed

+439
-244
lines changed

lib/std/Build/Step/Run.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1088,7 +1088,7 @@ fn runCommand(
10881088
// the `--` before the module name. This appears to work for both old and
10891089
// new Wasmtime versions.
10901090
try interp_argv.append(bin_name);
1091-
try interp_argv.append("--dir=.");
1091+
try interp_argv.append("--dir=."); // Preopen '.' at file descriptor 3 for cwd
10921092
try interp_argv.append("--");
10931093
try interp_argv.append(argv[0]);
10941094
try interp_argv.appendSlice(argv[1..]);

lib/std/c.zig

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,11 @@ pub const dev_t = switch (native_os) {
133133
pub const mode_t = switch (native_os) {
134134
.linux => linux.mode_t,
135135
.emscripten => emscripten.mode_t,
136-
.openbsd, .haiku, .netbsd, .solaris, .illumos, .wasi => u32,
136+
.openbsd, .haiku, .netbsd, .solaris, .illumos => u32,
137137
.freebsd, .macos, .ios, .tvos, .watchos, .visionos => u16,
138-
else => u0,
138+
.wasi => void,
139+
.windows => u0,
140+
else => u0, // TODO: should be void?
139141
};
140142

141143
pub const nlink_t = switch (native_os) {
@@ -1702,17 +1704,15 @@ pub const S = switch (native_os) {
17021704
.linux => linux.S,
17031705
.emscripten => emscripten.S,
17041706
.wasi => struct {
1705-
pub const IEXEC = @compileError("TODO audit this");
1707+
// Match wasi-libc's libc-bottom-half/headers/public/__mode_t.h
17061708
pub const IFBLK = 0x6000;
17071709
pub const IFCHR = 0x2000;
17081710
pub const IFDIR = 0x4000;
1709-
pub const IFIFO = 0xc000;
1711+
pub const IFIFO = 0x1000;
17101712
pub const IFLNK = 0xa000;
1711-
pub const IFMT = IFBLK | IFCHR | IFDIR | IFIFO | IFLNK | IFREG | IFSOCK;
17121713
pub const IFREG = 0x8000;
1713-
/// There's no concept of UNIX domain socket but we define this value here
1714-
/// in order to line with other OSes.
1715-
pub const IFSOCK = 0x1;
1714+
pub const IFSOCK = 0xc000;
1715+
pub const IFMT = IFBLK | IFCHR | IFDIR | IFIFO | IFLNK | IFREG | IFSOCK;
17161716
},
17171717
.macos, .ios, .tvos, .watchos, .visionos => struct {
17181718
pub const IFMT = 0o170000;
@@ -6362,6 +6362,7 @@ pub const Stat = switch (native_os) {
63626362
},
63636363
.emscripten => emscripten.Stat,
63646364
.wasi => extern struct {
6365+
// Matches wasi-libc's libc-bottom-half/headers/public/__struct_stat.h
63656366
dev: dev_t,
63666367
ino: ino_t,
63676368
nlink: nlink_t,
@@ -7061,17 +7062,13 @@ pub const AT = switch (native_os) {
70617062
pub const RECURSIVE = 0x8000;
70627063
},
70637064
.wasi => struct {
7064-
pub const SYMLINK_NOFOLLOW = 0x100;
7065-
pub const SYMLINK_FOLLOW = 0x400;
7066-
pub const REMOVEDIR: u32 = 0x4;
7067-
/// When linking libc, we follow their convention and use -2 for current working directory.
7068-
/// However, without libc, Zig does a different convention: it assumes the
7069-
/// current working directory is the first preopen. This behavior can be
7070-
/// overridden with a public function called `wasi_cwd` in the root source
7071-
/// file.
7072-
pub const FDCWD: fd_t = if (builtin.link_libc) -2 else 3;
7065+
// Match AT_* constants in wasi-libc libc-bottom-half/headers/public/__header_fcntl.h
7066+
pub const FDCWD = -2;
7067+
pub const EACCESS = 0x0;
7068+
pub const SYMLINK_NOFOLLOW = 0x1;
7069+
pub const SYMLINK_FOLLOW = 0x2;
7070+
pub const REMOVEDIR = 0x4;
70737071
},
7074-
70757072
else => void,
70767073
};
70777074

@@ -7100,6 +7097,7 @@ pub const O = switch (native_os) {
71007097
_: u9 = 0,
71017098
},
71027099
.wasi => packed struct(u32) {
7100+
// Match layout from wasi-libc libc-bottom-half/headers/public/__header_fcntl.h
71037101
APPEND: bool = false,
71047102
DSYNC: bool = false,
71057103
NONBLOCK: bool = false,
@@ -7112,10 +7110,17 @@ pub const O = switch (native_os) {
71127110
TRUNC: bool = false,
71137111
_16: u8 = 0,
71147112
NOFOLLOW: bool = false,
7113+
7114+
// Logical O_ACCMODE is the following 4 bits, note O_SEARCH is between RD and WR,
7115+
// so can't easily reuse std.posix.ACCMODE in this struct.
71157116
EXEC: bool = false,
7116-
read: bool = false,
7117+
RDONLY: bool = true, // equivalent to "ACCMODE = .RDONLY" default
71177118
SEARCH: bool = false,
7118-
write: bool = false,
7119+
WRONLY: bool = false,
7120+
7121+
// CLOEXEC, TTY_ININT, NOCTTY are mapped 0, so they're silently
7122+
// ignored in C code.
7123+
71197124
_: u3 = 0,
71207125
},
71217126
.solaris, .illumos => packed struct(u32) {
@@ -8945,6 +8950,7 @@ pub const gettimeofday = switch (native_os) {
89458950

89468951
pub const msync = switch (native_os) {
89478952
.netbsd => private.__msync13,
8953+
.wasi => {},
89488954
else => private.msync,
89498955
};
89508956

@@ -9087,8 +9093,8 @@ pub const fork = switch (native_os) {
90879093
pub extern "c" fn access(path: [*:0]const u8, mode: c_uint) c_int;
90889094
pub extern "c" fn faccessat(dirfd: fd_t, path: [*:0]const u8, mode: c_uint, flags: c_uint) c_int;
90899095
pub extern "c" fn pipe(fds: *[2]fd_t) c_int;
9090-
pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int;
9091-
pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: u32) c_int;
9096+
pub extern "c" fn mkdir(path: [*:0]const u8, mode: usize) c_int;
9097+
pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: usize) c_int;
90929098
pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int;
90939099
pub extern "c" fn symlinkat(oldpath: [*:0]const u8, newdirfd: fd_t, newpath: [*:0]const u8) c_int;
90949100
pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int;

lib/std/debug.zig

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,7 +1030,7 @@ test printLineFromFileAnyOs {
10301030
output.clearRetainingCapacity();
10311031
}
10321032
{
1033-
const path = try fs.path.join(allocator, &.{ test_dir_path, "three_lines.zig" });
1033+
const path = try join(allocator, &.{ test_dir_path, "three_lines.zig" });
10341034
defer allocator.free(path);
10351035
try test_dir.dir.writeFile(.{
10361036
.sub_path = "three_lines.zig",
@@ -1052,7 +1052,7 @@ test printLineFromFileAnyOs {
10521052
{
10531053
const file = try test_dir.dir.createFile("line_overlaps_page_boundary.zig", .{});
10541054
defer file.close();
1055-
const path = try fs.path.join(allocator, &.{ test_dir_path, "line_overlaps_page_boundary.zig" });
1055+
const path = try join(allocator, &.{ test_dir_path, "line_overlaps_page_boundary.zig" });
10561056
defer allocator.free(path);
10571057

10581058
const overlap = 10;
@@ -1068,7 +1068,7 @@ test printLineFromFileAnyOs {
10681068
{
10691069
const file = try test_dir.dir.createFile("file_ends_on_page_boundary.zig", .{});
10701070
defer file.close();
1071-
const path = try fs.path.join(allocator, &.{ test_dir_path, "file_ends_on_page_boundary.zig" });
1071+
const path = try join(allocator, &.{ test_dir_path, "file_ends_on_page_boundary.zig" });
10721072
defer allocator.free(path);
10731073

10741074
var writer = file.writer();
@@ -1081,7 +1081,7 @@ test printLineFromFileAnyOs {
10811081
{
10821082
const file = try test_dir.dir.createFile("very_long_first_line_spanning_multiple_pages.zig", .{});
10831083
defer file.close();
1084-
const path = try fs.path.join(allocator, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" });
1084+
const path = try join(allocator, &.{ test_dir_path, "very_long_first_line_spanning_multiple_pages.zig" });
10851085
defer allocator.free(path);
10861086

10871087
var writer = file.writer();
@@ -1106,7 +1106,7 @@ test printLineFromFileAnyOs {
11061106
{
11071107
const file = try test_dir.dir.createFile("file_of_newlines.zig", .{});
11081108
defer file.close();
1109-
const path = try fs.path.join(allocator, &.{ test_dir_path, "file_of_newlines.zig" });
1109+
const path = try join(allocator, &.{ test_dir_path, "file_of_newlines.zig" });
11101110
defer allocator.free(path);
11111111

11121112
var writer = file.writer();

lib/std/fs/Dir.zig

Lines changed: 38 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ fd: Handle,
22

33
pub const Handle = posix.fd_t;
44

5-
pub const default_mode = 0o755;
5+
/// Default mode bits for a new directory.
6+
pub const default_mode = switch (posix.mode_t) {
7+
void => {}, // wasi-without-libc has no mode suppport
8+
u0 => 0, // Zig's Posix layer doesn't support modes on Windows
9+
else => 0o755,
10+
};
611

712
pub const Entry = struct {
813
name: []const u8,
@@ -831,32 +836,19 @@ pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.Ope
831836

832837
/// Same as `openFile` but the path parameter is null-terminated.
833838
pub fn openFileZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File {
834-
switch (native_os) {
835-
.windows => {
836-
const path_w = try windows.cStrToPrefixedFileW(self.fd, sub_path);
837-
return self.openFileW(path_w.span(), flags);
838-
},
839-
// Use the libc API when libc is linked because it implements things
840-
// such as opening absolute file paths.
841-
.wasi => if (!builtin.link_libc) {
842-
return openFile(self, mem.sliceTo(sub_path, 0), flags);
843-
},
844-
else => {},
839+
if (native_os == .windows) {
840+
const path_w = try windows.cStrToPrefixedFileW(self.fd, sub_path);
841+
return self.openFileW(path_w.span(), flags);
842+
}
843+
if (native_os == .wasi and !builtin.link_libc) {
844+
return openFile(self, mem.sliceTo(sub_path, 0), flags);
845845
}
846846

847-
var os_flags: posix.O = switch (native_os) {
848-
.wasi => .{
849-
.read = flags.mode != .write_only,
850-
.write = flags.mode != .read_only,
851-
},
852-
else => .{
853-
.ACCMODE = switch (flags.mode) {
854-
.read_only => .RDONLY,
855-
.write_only => .WRONLY,
856-
.read_write => .RDWR,
857-
},
858-
},
859-
};
847+
var os_flags: posix.O = std.posix.makeOFlags(switch (flags.mode) {
848+
.read_only => .RDONLY,
849+
.write_only => .WRONLY,
850+
.read_write => .RDWR,
851+
}, .{});
860852
if (@hasField(posix.O, "CLOEXEC")) os_flags.CLOEXEC = true;
861853
if (@hasField(posix.O, "LARGEFILE")) os_flags.LARGEFILE = true;
862854
if (@hasField(posix.O, "NOCTTY")) os_flags.NOCTTY = !flags.allow_ctty;
@@ -879,7 +871,8 @@ pub fn openFileZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File
879871
},
880872
}
881873
}
882-
const fd = try posix.openatZ(self.fd, sub_path, os_flags, 0);
874+
const mode = if (posix.mode_t == void) {} else 0;
875+
const fd = try posix.openatZ(self.fd, sub_path, os_flags, mode);
883876
errdefer posix.close(fd);
884877

885878
if (have_flock and !has_flock_open_flags and flags.lock != .none) {
@@ -1002,12 +995,11 @@ pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags
1002995
else => {},
1003996
}
1004997

1005-
var os_flags: posix.O = .{
1006-
.ACCMODE = if (flags.read) .RDWR else .WRONLY,
998+
var os_flags: posix.O = std.posix.makeOFlags(if (flags.read) .RDWR else .WRONLY, .{
1007999
.CREAT = true,
10081000
.TRUNC = flags.truncate,
10091001
.EXCL = flags.exclusive,
1010-
};
1002+
});
10111003
if (@hasField(posix.O, "LARGEFILE")) os_flags.LARGEFILE = true;
10121004
if (@hasField(posix.O, "CLOEXEC")) os_flags.CLOEXEC = true;
10131005

@@ -1270,7 +1262,7 @@ pub const RealPathError = posix.RealPathError;
12701262
/// See also `Dir.realpathZ`, `Dir.realpathW`, and `Dir.realpathAlloc`.
12711263
pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError![]u8 {
12721264
if (native_os == .wasi) {
1273-
@compileError("realpath is not available on WASI");
1265+
@compileError("WASI does not support absolute paths");
12741266
}
12751267
if (native_os == .windows) {
12761268
const pathname_w = try windows.sliceToPrefixedFileW(self.fd, pathname);
@@ -1283,22 +1275,18 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError
12831275
/// Same as `Dir.realpath` except `pathname` is null-terminated.
12841276
/// See also `Dir.realpath`, `realpathZ`.
12851277
pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathError![]u8 {
1278+
if (native_os == .wasi) {
1279+
@compileError("WASI does not support absolute paths");
1280+
}
12861281
if (native_os == .windows) {
12871282
const pathname_w = try windows.cStrToPrefixedFileW(self.fd, pathname);
12881283
return self.realpathW(pathname_w.span(), out_buffer);
12891284
}
12901285

1291-
const flags: posix.O = switch (native_os) {
1292-
.linux => .{
1293-
.NONBLOCK = true,
1294-
.CLOEXEC = true,
1295-
.PATH = true,
1296-
},
1297-
else => .{
1298-
.NONBLOCK = true,
1299-
.CLOEXEC = true,
1300-
},
1301-
};
1286+
var flags: posix.O = .{};
1287+
if (@hasField(posix.O, "NONBLOCK")) flags.NONBLOCK = true;
1288+
if (@hasField(posix.O, "CLOEXEC")) flags.CLOEXEC = true;
1289+
if (@hasField(posix.O, "PATH")) flags.PATH = true;
13021290

13031291
const fd = posix.openatZ(self.fd, pathname, flags, 0) catch |err| switch (err) {
13041292
error.FileLocksNotSupported => return error.Unexpected,
@@ -1511,19 +1499,13 @@ pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenOptions) OpenErr
15111499
else => {},
15121500
}
15131501

1514-
var symlink_flags: posix.O = switch (native_os) {
1515-
.wasi => .{
1516-
.read = true,
1517-
.NOFOLLOW = args.no_follow,
1518-
.DIRECTORY = true,
1519-
},
1520-
else => .{
1521-
.ACCMODE = .RDONLY,
1522-
.NOFOLLOW = args.no_follow,
1523-
.DIRECTORY = true,
1524-
.CLOEXEC = true,
1525-
},
1526-
};
1502+
var symlink_flags: posix.O = std.posix.makeOFlags(.RDONLY, .{
1503+
.NOFOLLOW = args.no_follow,
1504+
.DIRECTORY = true,
1505+
});
1506+
1507+
if (@hasField(posix.O, "CLOEXEC"))
1508+
symlink_flags.CLOEXEC = true;
15271509

15281510
if (@hasField(posix.O, "PATH") and !args.iterate)
15291511
symlink_flags.PATH = true;
@@ -1556,7 +1538,8 @@ pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenOptions) OpenEr
15561538
/// Asserts `flags` has `DIRECTORY` set.
15571539
fn openDirFlagsZ(self: Dir, sub_path_c: [*:0]const u8, flags: posix.O) OpenError!Dir {
15581540
assert(flags.DIRECTORY);
1559-
const fd = posix.openatZ(self.fd, sub_path_c, flags, 0) catch |err| switch (err) {
1541+
const mode = if (posix.mode_t == void) {} else 0;
1542+
const fd = posix.openatZ(self.fd, sub_path_c, flags, mode) catch |err| switch (err) {
15601543
error.FileTooBig => unreachable, // can't happen for directories
15611544
error.IsDir => unreachable, // we're setting DIRECTORY
15621545
error.NoSpaceLeft => unreachable, // not setting CREAT

lib/std/fs/File.zig

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ pub const Kind = enum {
2727
/// the `touch` command, which would correspond to `0o644`. However, POSIX
2828
/// libc implementations use `0o666` inside `fopen` and then rely on the
2929
/// process-scoped "umask" setting to adjust this number for file creation.
30-
pub const default_mode = switch (builtin.os.tag) {
31-
.windows => 0,
32-
.wasi => 0,
30+
pub const default_mode = switch (posix.mode_t) {
31+
void => {}, // WASI-without-libc has no mode support
32+
u0 => 0, // Zig's Posix layer doesn't support modes for Windows
3333
else => 0o666,
3434
};
3535

@@ -381,7 +381,7 @@ pub const Stat = struct {
381381
/// is unique to each filesystem.
382382
inode: INode,
383383
size: u64,
384-
/// This is available on POSIX systems and is always 0 otherwise.
384+
/// This is available on POSIX systems and is {} otherwise.
385385
mode: Mode,
386386
kind: Kind,
387387

@@ -399,7 +399,7 @@ pub const Stat = struct {
399399
return .{
400400
.inode = st.ino,
401401
.size = @bitCast(st.size),
402-
.mode = st.mode,
402+
.mode = if (Mode == void) {} else st.mode,
403403
.kind = k: {
404404
const m = st.mode & posix.S.IFMT;
405405
switch (m) {
@@ -455,7 +455,7 @@ pub const Stat = struct {
455455
return .{
456456
.inode = st.ino,
457457
.size = @bitCast(st.size),
458-
.mode = 0,
458+
.mode = {},
459459
.kind = switch (st.filetype) {
460460
.BLOCK_DEVICE => .block_device,
461461
.CHARACTER_DEVICE => .character_device,

0 commit comments

Comments
 (0)