Fix At flags for real

The fix got reverted in the merge conflict but now properly fix `At`

Update posix `At` to use packed struct (I was holding back from touching
`posix` and `c` but I might as well just get this done instead of using
the deprecated constants since the diff is already big and At is worth
while)

Signed-off-by: Bernard Assan <mega.alpha100@gmail.com>
This commit is contained in:
Bernard Assan 2025-11-14 14:17:13 +00:00
parent 62fb6db2cd
commit 0a0ca8d6f0
No known key found for this signature in database
GPG key ID: C2A2C53574321095
8 changed files with 263 additions and 122 deletions

View file

@ -24,7 +24,7 @@ pub fn cwd() Dir {
return switch (native_os) { return switch (native_os) {
.windows => .{ .handle = std.os.windows.peb().ProcessParameters.CurrentDirectory.Handle }, .windows => .{ .handle = std.os.windows.peb().ProcessParameters.CurrentDirectory.Handle },
.wasi => .{ .handle = std.options.wasiCwd() }, .wasi => .{ .handle = std.options.wasiCwd() },
else => .{ .handle = std.posix.AT.FDCWD }, else => .{ .handle = std.posix.At.fdcwd },
}; };
} }

View file

@ -1339,12 +1339,12 @@ fn dirStatPathPosix(
var path_buffer: [posix.PATH_MAX]u8 = undefined; var path_buffer: [posix.PATH_MAX]u8 = undefined;
const sub_path_posix = try pathToPosix(sub_path, &path_buffer); const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0; const flags: posix.At = .{ .symlink_nofollow = if (!options.follow_symlinks) true else false };
while (true) { while (true) {
try t.checkCancel(); try t.checkCancel();
var stat = std.mem.zeroes(posix.Stat); var stat = std.mem.zeroes(posix.Stat);
switch (posix.errno(fstatat_sym(dir.handle, sub_path_posix, &stat, flags))) { switch (posix.errno(fstatat_sym(dir.handle, sub_path_posix, &stat, @bitCast(flags)))) {
.SUCCESS => return statFromPosix(&stat), .SUCCESS => return statFromPosix(&stat),
.INTR => continue, .INTR => continue,
.CANCELED => return error.Canceled, .CANCELED => return error.Canceled,
@ -1568,7 +1568,7 @@ fn dirAccessPosix(
var path_buffer: [posix.PATH_MAX]u8 = undefined; var path_buffer: [posix.PATH_MAX]u8 = undefined;
const sub_path_posix = try pathToPosix(sub_path, &path_buffer); const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
const flags: u32 = @as(u32, if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0); const flags: posix.At = .{ .symlink_nofollow = if (!options.follow_symlinks) true else false };
const mode: u32 = const mode: u32 =
@as(u32, if (options.read) posix.R_OK else 0) | @as(u32, if (options.read) posix.R_OK else 0) |
@ -1577,7 +1577,7 @@ fn dirAccessPosix(
while (true) { while (true) {
try t.checkCancel(); try t.checkCancel();
switch (posix.errno(posix.system.faccessat(dir.handle, sub_path_posix, mode, flags))) { switch (posix.errno(posix.system.faccessat(dir.handle, sub_path_posix, mode, @bitCast(flags)))) {
.SUCCESS => return, .SUCCESS => return,
.INTR => continue, .INTR => continue,
.CANCELED => return error.Canceled, .CANCELED => return error.Canceled,
@ -2837,7 +2837,7 @@ fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekErr
fn openSelfExe(userdata: ?*anyopaque, flags: Io.File.OpenFlags) Io.File.OpenSelfExeError!Io.File { fn openSelfExe(userdata: ?*anyopaque, flags: Io.File.OpenFlags) Io.File.OpenSelfExeError!Io.File {
const t: *Threaded = @ptrCast(@alignCast(userdata)); const t: *Threaded = @ptrCast(@alignCast(userdata));
switch (native_os) { switch (native_os) {
.linux, .serenity => return dirOpenFilePosix(t, .{ .handle = posix.AT.FDCWD }, "/proc/self/exe", flags), .linux, .serenity => return dirOpenFilePosix(t, .{ .handle = posix.At.fdcwd }, "/proc/self/exe", flags),
.windows => { .windows => {
// If ImagePathName is a symlink, then it will contain the path of the symlink, // If ImagePathName is a symlink, then it will contain the path of the symlink,
// not the path that the symlink points to. However, because we are opening // not the path that the symlink points to. However, because we are opening

View file

@ -8375,128 +8375,267 @@ pub const port_event = switch (native_os) {
else => void, else => void,
}; };
pub const AT = switch (native_os) { /// Deprecated Alias to `At`
.linux => linux.AT, pub const AT = At;
.windows => struct {
pub const At = switch (native_os) {
.emscripten, .linux => linux.At,
.windows => packed struct(u32) {
_: u9 = 0,
/// Remove directory instead of unlinking file /// Remove directory instead of unlinking file
pub const REMOVEDIR = 0x200; removedir: bool = false,
_11: u22 = 0,
/// DEPRECATED aliase to `removedir`
pub const REMOVEDIR: u32 = @bitCast(At{ .removedir = true });
}, },
.driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => struct { .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => packed struct(u32) {
pub const FDCWD = -2; _: u4 = 0,
/// Use effective ids in access check /// Use effective ids in access check
pub const EACCESS = 0x0010; eaccess: bool = false,
/// Act on the symlink itself not the target /// Act on the symlink itself not the target
pub const SYMLINK_NOFOLLOW = 0x0020; symlink_nofollow: bool = false,
/// Act on target of symlink /// Act on target of symlink
pub const SYMLINK_FOLLOW = 0x0040; symlink_follow: bool = false,
/// Path refers to directory /// Path refers to directory
pub const REMOVEDIR = 0x0080; removedir: bool = false,
_9: u24 = 0,
pub const fdcwd: fd_t = -2;
/// DEPRECATED: use `eaccess`
pub const EACCESS: u32 = @bitCast(At{ .eaccess = true });
/// DEPRECATED: use `symlink_nofollow`
pub const SYMLINK_NOFOLLOW: u32 = @bitCast(At{ .symlink_nofollow = true });
/// DEPRECATED: use `symlink_follow`
pub const SYMLINK_FOLLOW: u32 = @bitCast(At{ .symlink_follow = true });
/// DEPRECATED: use `removedir`
pub const REMOVEDIR: u32 = @bitCast(At{ .removedir = true });
/// DEPRECATED: use `fdcwd`
pub const FDCWD = fdcwd;
}, },
.freebsd => struct { .freebsd => packed struct(u32) {
/// Magic value that specify the use of the current working directory _: u8 = 0,
/// 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 /// Check access using effective user and group ID
pub const EACCESS = 0x0100; eaccess: bool = false,
/// Do not follow symbolic links /// Do not follow symbolic links
pub const SYMLINK_NOFOLLOW = 0x0200; symlink_nofollow: bool = false,
/// Follow symbolic link /// Follow symbolic link
pub const SYMLINK_FOLLOW = 0x0400; symlink_follow: bool = false,
/// Remove directory instead of file /// Remove directory instead of file
pub const REMOVEDIR = 0x0800; removedir: bool = false,
/// Fail if not under dirfd /// Fail if not under dirfd
pub const BENEATH = 0x1000; beneath: bool = false,
}, _14: u19 = 0,
.netbsd => struct {
/// Magic value that specify the use of the current working directory /// Magic value that specify the use of the current working directory
/// to determine the target of relative file paths in the openat() and /// to determine the target of relative file paths in the openat() and
/// similar syscalls. /// similar syscalls.
pub const FDCWD = -100; pub const fdcwd: fd_t = -100;
/// DEPRECATED: use `eaccess`
pub const EACCESS: u32 = @bitCast(At{ .eaccess = true });
/// DEPRECATED: use `symlink_nofollow`
pub const SYMLINK_NOFOLLOW: u32 = @bitCast(At{ .symlink_nofollow = true });
/// DEPRECATED: use `symlink_follow`
pub const SYMLINK_FOLLOW: u32 = @bitCast(At{ .symlink_follow = true });
/// DEPRECATED: use `removedir`
pub const REMOVEDIR: u32 = @bitCast(At{ .removedir = true });
/// DEPRECATED: use `beneath`
pub const BENEATH: u32 = @bitCast(At{ .beneath = true });
/// DEPRECATED: use `fdcwd`
pub const FDCWD = fdcwd;
},
.netbsd => packed struct(u32) {
_: u8 = 0,
/// Check access using effective user and group ID /// Check access using effective user and group ID
pub const EACCESS = 0x0100; eaccess: bool = false,
/// Do not follow symbolic links /// Do not follow symbolic links
pub const SYMLINK_NOFOLLOW = 0x0200; symlink_nofollow: bool = false,
/// Follow symbolic link /// Follow symbolic link
pub const SYMLINK_FOLLOW = 0x0400; symlink_follow: bool = false,
/// Remove directory instead of file /// Remove directory instead of file
pub const REMOVEDIR = 0x0800; removedir: bool = false,
}, _12: u20 = 0,
.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 /// Magic value that specify the use of the current working directory
/// to determine the target of relative file paths in the openat() and /// to determine the target of relative file paths in the openat() and
/// similar syscalls. /// similar syscalls.
pub const FDCWD = -100; pub const fdcwd: fd_t = -100;
/// Check access using effective user and group ID
pub const EACCESS = 0x01; /// DEPRECATED: use `eaccess`
pub const EACCESS: u32 = @bitCast(At{ .eaccess = true });
/// DEPRECATED: use `symlink_nofollow`
pub const SYMLINK_NOFOLLOW: u32 = @bitCast(At{ .symlink_nofollow = true });
/// DEPRECATED: use `symlink_follow`
pub const SYMLINK_FOLLOW: u32 = @bitCast(At{ .symlink_follow = true });
/// DEPRECATED: use `removedir`
pub const REMOVEDIR: u32 = @bitCast(At{ .removedir = true });
/// DEPRECATED: use `fdcwd`
pub const FDCWD = fdcwd;
},
.dragonfly => packed struct(u32) {
/// Do not follow symbolic links /// Do not follow symbolic links
pub const SYMLINK_NOFOLLOW = 0x02; symlink_nofollow: bool = false,
/// Follow symbolic link
pub const SYMLINK_FOLLOW = 0x04;
/// Remove directory instead of file /// Remove directory instead of file
pub const REMOVEDIR = 0x08; removedir: bool = false,
/// Check access using effective user and group ID
eaccess: bool = false,
/// Follow symbolic link
symlink_follow: bool = false,
_: u28 = 0,
pub const fdcwd: fd_t = -328243;
/// DEPRECATED: use `symlink_nofollow`
pub const SYMLINK_NOFOLLOW: u32 = @bitCast(At{ .symlink_nofollow = true });
/// DEPRECATED: use `removedir`
pub const REMOVEDIR: u32 = @bitCast(At{ .removedir = true });
/// DEPRECATED: use `eaccess`
pub const EACCESS: u32 = @bitCast(At{ .eaccess = true });
/// DEPRECATED: use `symlink_follow`
pub const SYMLINK_FOLLOW: u32 = @bitCast(At{ .symlink_follow = true });
/// DEPRECATED: use `fdcwd`
pub const FDCWD = fdcwd;
}, },
.haiku => struct { .openbsd => packed struct(u32) {
pub const FDCWD = -1; /// Check access using effective user and group ID
pub const SYMLINK_NOFOLLOW = 0x01; eaccess: bool = false,
pub const SYMLINK_FOLLOW = 0x02; /// Do not follow symbolic links
pub const REMOVEDIR = 0x04; symlink_nofollow: bool = false,
pub const EACCESS = 0x08; /// Follow symbolic link
}, symlink_follow: bool = false,
.illumos => struct { /// Remove directory instead of file
removedir: bool = false,
_: u28 = 0,
/// Magic value that specify the use of the current working directory /// Magic value that specify the use of the current working directory
/// to determine the target of relative file paths in the openat() and /// to determine the target of relative file paths in the openat() and
/// similar syscalls. /// similar syscalls.
pub const FDCWD: fd_t = @bitCast(@as(u32, 0xffd19553)); pub const fdcwd: fd_t = -100;
/// DEPRECATED: use `eaccess`
pub const EACCESS: u32 = @bitCast(At{ .eaccess = true });
/// DEPRECATED: use `symlink_nofollow`
pub const SYMLINK_NOFOLLOW: u32 = @bitCast(At{ .symlink_nofollow = true });
/// DEPRECATED: use `symlink_follow`
pub const SYMLINK_FOLLOW: u32 = @bitCast(At{ .symlink_follow = true });
/// DEPRECATED: use `removedir`
pub const REMOVEDIR: u32 = @bitCast(At{ .removedir = true });
pub const FDCWD = fdcwd;
},
.haiku => packed struct(u32) {
/// Do not follow symbolic links /// Do not follow symbolic links
pub const SYMLINK_NOFOLLOW = 0x1000; symlink_nofollow: bool = false,
/// Follow symbolic link /// Follow symbolic link
pub const SYMLINK_FOLLOW = 0x2000; symlink_follow: bool = false,
/// Remove directory instead of file /// Remove directory instead of file
pub const REMOVEDIR = 0x1; removedir: bool = false,
pub const TRIGGER = 0x2;
/// Check access using effective user and group ID /// Check access using effective user and group ID
pub const EACCESS = 0x4; eaccess: bool = false,
_: u28 = 0,
pub const fdcwd: fd_t = -1;
/// DEPRECATED: use `symlink_nofollow`
pub const SYMLINK_NOFOLLOW: u32 = @bitCast(At{ .symlink_nofollow = true });
/// DEPRECATED: use `symlink_follow`
pub const SYMLINK_FOLLOW: u32 = @bitCast(At{ .symlink_follow = true });
/// DEPRECATED: use `removedir`
pub const REMOVEDIR: u32 = @bitCast(At{ .removedir = true });
/// DEPRECATED: use `eaccess`
pub const EACCESS: u32 = @bitCast(At{ .eaccess = true });
/// DEPRECATED: use `fdcwd`
pub const FDCWD = fdcwd;
}, },
.emscripten => struct { .illumos => packed struct(u32) {
pub const FDCWD = -100; /// Remove directory instead of file
pub const SYMLINK_NOFOLLOW = 0x100; removedir: bool = false,
pub const REMOVEDIR = 0x200; trigger: bool = false,
pub const SYMLINK_FOLLOW = 0x400; /// Check access using effective user and group ID
pub const NO_AUTOMOUNT = 0x800; eaccess: bool = false,
pub const EMPTY_PATH = 0x1000; _4: u9 = 0,
pub const STATX_SYNC_TYPE = 0x6000; /// Do not follow symbolic links
pub const STATX_SYNC_AS_STAT = 0x0000; symlink_nofollow: bool = false,
pub const STATX_FORCE_SYNC = 0x2000; /// Follow symbolic link
pub const STATX_DONT_SYNC = 0x4000; symlink_follow: bool = false,
pub const RECURSIVE = 0x8000; _15: u18 = 0,
/// 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: fd_t = @bitCast(@as(u32, 0xffd19553));
/// DEPRECATED: use `removedir`
pub const REMOVEDIR: u32 = @bitCast(At{ .removedir = true });
/// DEPRECATED: use `trigger`
pub const TRIGGER: u32 = @bitCast(At{ .trigger = true });
/// DEPRECATED: use `eaccess`
pub const EACCESS: u32 = @bitCast(At{ .eaccess = true });
/// DEPRECATED: use `symlink_nofollow`
pub const SYMLINK_NOFOLLOW: u32 = @bitCast(At{ .symlink_nofollow = true });
/// DEPRECATED: use `symlink_follow`
pub const SYMLINK_FOLLOW: u32 = @bitCast(At{ .symlink_follow = true });
/// DEPRECATED: use `fdcwd`
pub const FDCWD = fdcwd;
}, },
.wasi => struct { // Match `AT_*` constants in lib/libc/include/wasm-wasi-musl/__header_fcntl.h
// Match `AT_*` constants in lib/libc/include/wasm-wasi-musl/__header_fcntl.h .wasi => packed struct(u32) {
pub const EACCESS = 0x0; /// Do not follow symbolic links
pub const SYMLINK_NOFOLLOW = 0x1; symlink_nofollow: bool = false,
pub const SYMLINK_FOLLOW = 0x2; /// Follow symbolic link
pub const REMOVEDIR = 0x4; symlink_follow: bool = false,
/// When linking libc, we follow their convention and use -2 for current working directory. /// Remove directory instead of file
/// However, without libc, Zig does a different convention: it assumes the removedir: bool = false,
/// current working directory is the first preopen. This behavior can be _: u29 = 0,
/// overridden with a public function called `wasi_cwd` in the root source
/// file. pub const eaccess: u32 = @bitCast(At{});
pub const FDCWD: fd_t = if (builtin.link_libc) -2 else 3;
/// 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;
/// DEPRECATED: use `symlink_nofollow`
pub const SYMLINK_NOFOLLOW: u32 = @bitCast(At{ .symlink_nofollow = true });
/// DEPRECATED: use `symlink_follow`
pub const SYMLINK_FOLLOW: u32 = @bitCast(At{ .symlink_follow = true });
/// DEPRECATED: use `removedir`
pub const REMOVEDIR: u32 = @bitCast(At{ .removedir = true });
/// DEPRECATED: use `fdcwd`
pub const FDCWD = fdcwd;
/// DEPRECATED: use `eaccess`
pub const EACCESS: u32 = eaccess;
}, },
// https://github.com/SerenityOS/serenity/blob/2808b0376406a40e31293bb3bcb9170374e90506/Kernel/API/POSIX/fcntl.h#L49-L52 // https://github.com/SerenityOS/serenity/blob/2808b0376406a40e31293bb3bcb9170374e90506/Kernel/API/POSIX/fcntl.h#L49-L52
.serenity => struct { .serenity => packed struct(u32) {
pub const FDCWD = -100; _: u8 = 0,
pub const SYMLINK_NOFOLLOW = 0x100; /// Do not follow symbolic links
pub const REMOVEDIR = 0x200; symlink_nofollow: bool = false,
pub const EACCESS = 0x400; /// Remove directory instead of file
removedir: bool = false,
/// Check access using effective user and group ID
eaccess: bool = false,
_11: u21 = 0,
pub const fdcwd: fd_t = -100;
/// DEPRECATED: use `symlink_nofollow`
pub const SYMLINK_NOFOLLOW: u32 = @bitCast(At{ .symlink_nofollow = true });
/// DEPRECATED: use `removedir`
pub const REMOVEDIR: u32 = @bitCast(At{ .removedir = true });
/// DEPRECATED: use `eaccess`
pub const EACCESS: u32 = @bitCast(At{ .eaccess = true });
/// DEPRECATED: use `fdcwd`
pub const FDCWD = fdcwd;
}, },
else => void, else => void,
}; };
@ -10485,7 +10624,7 @@ pub extern "c" fn inotify_add_watch(fd: fd_t, pathname: [*:0]const u8, mask: u32
pub extern "c" fn inotify_rm_watch(fd: fd_t, wd: c_int) c_int; pub extern "c" fn inotify_rm_watch(fd: fd_t, wd: c_int) c_int;
pub extern "c" fn fstat64(fd: fd_t, buf: *Stat) c_int; pub extern "c" fn fstat64(fd: fd_t, buf: *Stat) c_int;
pub extern "c" fn fstatat64(dirfd: fd_t, noalias path: [*:0]const u8, noalias stat_buf: *Stat, flags: u32) c_int; pub extern "c" fn fstatat64(dirfd: fd_t, noalias path: [*:0]const u8, noalias stat_buf: *Stat, flags: At) c_int;
pub extern "c" fn fallocate64(fd: fd_t, mode: c_int, offset: off_t, len: off_t) c_int; pub extern "c" fn fallocate64(fd: fd_t, mode: c_int, offset: off_t, len: off_t) c_int;
pub extern "c" fn fopen64(noalias filename: [*:0]const u8, noalias modes: [*:0]const u8) ?*FILE; pub extern "c" fn fopen64(noalias filename: [*:0]const u8, noalias modes: [*:0]const u8) ?*FILE;
pub extern "c" fn ftruncate64(fd: c_int, length: off_t) c_int; pub extern "c" fn ftruncate64(fd: c_int, length: off_t) c_int;

View file

@ -187,7 +187,7 @@ pub fn cwd() Dir {
} else if (native_os == .wasi) { } else if (native_os == .wasi) {
return .{ .fd = std.options.wasiCwd() }; return .{ .fd = std.options.wasiCwd() };
} else { } else {
return .{ .fd = posix.AT.fdcwd }; return .{ .fd = posix.At.fdcwd };
} }
} }

View file

@ -148,7 +148,7 @@ pub const Iterator = switch (native_os) {
const stat_info = posix.fstatat( const stat_info = posix.fstatat(
self.dir.fd, self.dir.fd,
name, name,
posix.AT.SYMLINK_NOFOLLOW, .{ .symlink_nofollow = true },
) catch |err| switch (err) { ) catch |err| switch (err) {
error.NameTooLong => unreachable, error.NameTooLong => unreachable,
error.SymLinkLoop => unreachable, error.SymLinkLoop => unreachable,
@ -1112,7 +1112,7 @@ pub fn deleteFileZ(self: Dir, sub_path_c: [*:0]const u8) DeleteFileError!void {
// directory, so we need to handle that case specifically and translate the error // directory, so we need to handle that case specifically and translate the error
.driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos, .freebsd, .netbsd, .dragonfly, .openbsd, .illumos => { .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos, .freebsd, .netbsd, .dragonfly, .openbsd, .illumos => {
// Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them) // Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them)
const fstat = posix.fstatatZ(self.fd, sub_path_c, posix.AT.SYMLINK_NOFOLLOW) catch return e; const fstat = posix.fstatatZ(self.fd, sub_path_c, .{ .symlink_nofollow = true }) catch return e;
const is_dir = fstat.mode & posix.S.IFMT == posix.S.IFDIR; const is_dir = fstat.mode & posix.S.IFMT == posix.S.IFDIR;
return if (is_dir) error.IsDir else e; return if (is_dir) error.IsDir else e;
}, },
@ -1163,7 +1163,7 @@ pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path); const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
return self.deleteDirW(sub_path_w.span()); return self.deleteDirW(sub_path_w.span());
} else if (native_os == .wasi and !builtin.link_libc) { } else if (native_os == .wasi and !builtin.link_libc) {
posix.unlinkat(self.fd, sub_path, posix.AT.REMOVEDIR) catch |err| switch (err) { posix.unlinkat(self.fd, sub_path, posix.At.REMOVEDIR) catch |err| switch (err) {
error.IsDir => unreachable, // not possible since we pass AT.REMOVEDIR error.IsDir => unreachable, // not possible since we pass AT.REMOVEDIR
else => |e| return e, else => |e| return e,
}; };
@ -1175,7 +1175,7 @@ pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
/// Same as `deleteDir` except the parameter is null-terminated. /// Same as `deleteDir` except the parameter is null-terminated.
pub fn deleteDirZ(self: Dir, sub_path_c: [*:0]const u8) DeleteDirError!void { pub fn deleteDirZ(self: Dir, sub_path_c: [*:0]const u8) DeleteDirError!void {
posix.unlinkatZ(self.fd, sub_path_c, posix.AT.REMOVEDIR) catch |err| switch (err) { posix.unlinkatZ(self.fd, sub_path_c, posix.At.REMOVEDIR) catch |err| switch (err) {
error.IsDir => unreachable, // not possible since we pass AT.REMOVEDIR error.IsDir => unreachable, // not possible since we pass AT.REMOVEDIR
else => |e| return e, else => |e| return e,
}; };
@ -1184,7 +1184,7 @@ pub fn deleteDirZ(self: Dir, sub_path_c: [*:0]const u8) DeleteDirError!void {
/// Same as `deleteDir` except the parameter is WTF16LE, NT prefixed. /// Same as `deleteDir` except the parameter is WTF16LE, NT prefixed.
/// This function is Windows-only. /// This function is Windows-only.
pub fn deleteDirW(self: Dir, sub_path_w: []const u16) DeleteDirError!void { pub fn deleteDirW(self: Dir, sub_path_w: []const u16) DeleteDirError!void {
posix.unlinkatW(self.fd, sub_path_w, posix.AT.REMOVEDIR) catch |err| switch (err) { posix.unlinkatW(self.fd, sub_path_w, posix.At.REMOVEDIR) catch |err| switch (err) {
error.IsDir => unreachable, // not possible since we pass AT.REMOVEDIR error.IsDir => unreachable, // not possible since we pass AT.REMOVEDIR
else => |e| return e, else => |e| return e,
}; };

View file

@ -1253,7 +1253,7 @@ pub fn rmdir(path: [*:0]const u8) usize {
if (@hasField(SYS, "rmdir")) { if (@hasField(SYS, "rmdir")) {
return syscall1(.rmdir, @intFromPtr(path)); return syscall1(.rmdir, @intFromPtr(path));
} else { } else {
return syscall3(.unlinkat, @as(usize, @bitCast(@as(isize, At.fdcwd))), @intFromPtr(path), @as(u32, @bitCast(At{ .removedir_or_handle_fid = .{ .removedir = true } }))); return syscall3(.unlinkat, @as(usize, @bitCast(@as(isize, At.fdcwd))), @intFromPtr(path), @as(u32, @bitCast(At{ .removedir = true })));
} }
} }
@ -3573,7 +3573,7 @@ pub const STDERR_FILENO = 2;
pub const AT = At; pub const AT = At;
/// matches AT_* and AT_STATX_* /// matches AT_* and AT_STATX_*
pub const At = packed struct(u32) { pub const At = packed struct(u32) {
_u1: u8 = 0, _1: u8 = 0,
/// Do not follow symbolic links /// Do not follow symbolic links
symlink_nofollow: bool = false, symlink_nofollow: bool = false,
/// Remove directory instead of unlinking file /// Remove directory instead of unlinking file

View file

@ -63,7 +63,9 @@ pub const AF = system.AF;
pub const AF_SUN = system.AF_SUN; pub const AF_SUN = system.AF_SUN;
pub const AI = system.AI; pub const AI = system.AI;
pub const ARCH = system.ARCH; pub const ARCH = system.ARCH;
pub const AT = system.AT; /// DEPRECATED: use `At`
pub const AT = At;
pub const At = system.At;
pub const AT_SUN = system.AT_SUN; pub const AT_SUN = system.AT_SUN;
pub const CLOCK = system.CLOCK; pub const CLOCK = system.CLOCK;
pub const CPU_COUNT = system.CPU_COUNT; pub const CPU_COUNT = system.CPU_COUNT;
@ -480,7 +482,7 @@ fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtEr
} }
defer close(pathfd); defer close(pathfd);
const stat = fstatatZ(pathfd, "", AT.EMPTY_PATH) catch |err| switch (err) { const stat = fstatatZ(pathfd, "", .{ .empty_path = true }) catch |err| switch (err) {
error.NameTooLong => unreachable, error.NameTooLong => unreachable,
error.FileNotFound => unreachable, error.FileNotFound => unreachable,
error.Streaming => unreachable, error.Streaming => unreachable,
@ -1553,7 +1555,7 @@ pub fn open(file_path: []const u8, flags: O, perm: mode_t) OpenError!fd_t {
if (native_os == .windows) { if (native_os == .windows) {
@compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API"); @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API");
} else if (native_os == .wasi and !builtin.link_libc) { } else if (native_os == .wasi and !builtin.link_libc) {
return openat(AT.FDCWD, file_path, flags, perm); return openat(At.fdcwd, file_path, flags, perm);
} }
const file_path_c = try toPosixPath(file_path); const file_path_c = try toPosixPath(file_path);
return openZ(&file_path_c, flags, perm); return openZ(&file_path_c, flags, perm);
@ -1943,7 +1945,7 @@ pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!
if (native_os == .windows) { if (native_os == .windows) {
@compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead"); @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
} else if (native_os == .wasi and !builtin.link_libc) { } else if (native_os == .wasi and !builtin.link_libc) {
return symlinkat(target_path, AT.FDCWD, sym_link_path); return symlinkat(target_path, At.fdcwd, sym_link_path);
} }
const target_path_c = try toPosixPath(target_path); const target_path_c = try toPosixPath(target_path);
const sym_link_path_c = try toPosixPath(sym_link_path); const sym_link_path_c = try toPosixPath(sym_link_path);
@ -2103,7 +2105,7 @@ pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8) LinkError!void {
/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding. /// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
pub fn link(oldpath: []const u8, newpath: []const u8) LinkError!void { pub fn link(oldpath: []const u8, newpath: []const u8) LinkError!void {
if (native_os == .wasi and !builtin.link_libc) { if (native_os == .wasi and !builtin.link_libc) {
return linkat(AT.FDCWD, oldpath, AT.FDCWD, newpath, 0) catch |err| switch (err) { return linkat(At.fdcwd, oldpath, At.fdcwd, newpath, 0) catch |err| switch (err) {
error.NotDir => unreachable, // link() does not support directories error.NotDir => unreachable, // link() does not support directories
else => |e| return e, else => |e| return e,
}; };
@ -2163,7 +2165,7 @@ pub fn linkat(
const old: RelativePathWasi = .{ .dir_fd = olddir, .relative_path = oldpath }; const old: RelativePathWasi = .{ .dir_fd = olddir, .relative_path = oldpath };
const new: RelativePathWasi = .{ .dir_fd = newdir, .relative_path = newpath }; const new: RelativePathWasi = .{ .dir_fd = newdir, .relative_path = newpath };
const old_flags: wasi.lookupflags_t = .{ const old_flags: wasi.lookupflags_t = .{
.SYMLINK_FOLLOW = (flags & AT.SYMLINK_FOLLOW) != 0, .SYMLINK_FOLLOW = (flags & At.SYMLINK_FOLLOW) != 0,
}; };
switch (wasi.path_link( switch (wasi.path_link(
old.dir_fd, old.dir_fd,
@ -2234,7 +2236,7 @@ pub const UnlinkError = error{
/// See also `unlinkZ`. /// See also `unlinkZ`.
pub fn unlink(file_path: []const u8) UnlinkError!void { pub fn unlink(file_path: []const u8) UnlinkError!void {
if (native_os == .wasi and !builtin.link_libc) { if (native_os == .wasi and !builtin.link_libc) {
return unlinkat(AT.FDCWD, file_path, 0) catch |err| switch (err) { return unlinkat(At.fdcwd, file_path, 0) catch |err| switch (err) {
error.DirNotEmpty => unreachable, // only occurs when targeting directories error.DirNotEmpty => unreachable, // only occurs when targeting directories
else => |e| return e, else => |e| return e,
}; };
@ -2308,7 +2310,7 @@ pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!vo
/// WASI-only. Same as `unlinkat` but targeting WASI. /// WASI-only. Same as `unlinkat` but targeting WASI.
/// See also `unlinkat`. /// See also `unlinkat`.
pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
const remove_dir = (flags & AT.REMOVEDIR) != 0; const remove_dir = (flags & At.REMOVEDIR) != 0;
const res = if (remove_dir) const res = if (remove_dir)
wasi.path_remove_directory(dirfd, file_path.ptr, file_path.len) wasi.path_remove_directory(dirfd, file_path.ptr, file_path.len)
else else
@ -2373,7 +2375,7 @@ pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatEr
/// Same as `unlinkat` but `sub_path_w` is WTF16LE, NT prefixed. Windows only. /// Same as `unlinkat` but `sub_path_w` is WTF16LE, NT prefixed. Windows only.
pub fn unlinkatW(dirfd: fd_t, sub_path_w: []const u16, flags: u32) UnlinkatError!void { pub fn unlinkatW(dirfd: fd_t, sub_path_w: []const u16, flags: u32) UnlinkatError!void {
const remove_dir = (flags & AT.REMOVEDIR) != 0; const remove_dir = (flags & At.REMOVEDIR) != 0;
return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir }); return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir });
} }
@ -2421,7 +2423,7 @@ pub const RenameError = error{
/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding. /// On other platforms, both paths are an opaque sequence of bytes with no particular encoding.
pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void { pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
if (native_os == .wasi and !builtin.link_libc) { if (native_os == .wasi and !builtin.link_libc) {
return renameat(AT.FDCWD, old_path, AT.FDCWD, new_path); return renameat(At.fdcwd, old_path, At.FDCWD, new_path);
} else if (native_os == .windows) { } else if (native_os == .windows) {
const old_path_w = try windows.sliceToPrefixedFileW(null, old_path); const old_path_w = try windows.sliceToPrefixedFileW(null, old_path);
const new_path_w = try windows.sliceToPrefixedFileW(null, new_path); const new_path_w = try windows.sliceToPrefixedFileW(null, new_path);
@ -2747,7 +2749,7 @@ pub const MakeDirError = std.Io.Dir.MakeError;
/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
pub fn mkdir(dir_path: []const u8, mode: mode_t) MakeDirError!void { pub fn mkdir(dir_path: []const u8, mode: mode_t) MakeDirError!void {
if (native_os == .wasi and !builtin.link_libc) { if (native_os == .wasi and !builtin.link_libc) {
return mkdirat(AT.FDCWD, dir_path, mode); return mkdirat(At.fdcwd, dir_path, mode);
} else if (native_os == .windows) { } else if (native_os == .windows) {
const dir_path_w = try windows.sliceToPrefixedFileW(null, dir_path); const dir_path_w = try windows.sliceToPrefixedFileW(null, dir_path);
return mkdirW(dir_path_w.span(), mode); return mkdirW(dir_path_w.span(), mode);
@ -2832,7 +2834,7 @@ pub const DeleteDirError = error{
/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. /// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding.
pub fn rmdir(dir_path: []const u8) DeleteDirError!void { pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
if (native_os == .wasi and !builtin.link_libc) { if (native_os == .wasi and !builtin.link_libc) {
return unlinkat(AT.FDCWD, dir_path, AT.REMOVEDIR) catch |err| switch (err) { return unlinkat(At.fdcwd, dir_path, At.REMOVEDIR) catch |err| switch (err) {
error.FileSystem => unreachable, // only occurs when targeting files error.FileSystem => unreachable, // only occurs when targeting files
error.IsDir => unreachable, // only occurs when targeting files error.IsDir => unreachable, // only occurs when targeting files
else => |e| return e, else => |e| return e,
@ -2959,7 +2961,7 @@ pub const FchdirError = error{
} || UnexpectedError; } || UnexpectedError;
pub fn fchdir(dirfd: fd_t) FchdirError!void { pub fn fchdir(dirfd: fd_t) FchdirError!void {
if (dirfd == AT.FDCWD) return; if (dirfd == At.fdcwd) return;
while (true) { while (true) {
switch (errno(system.fchdir(dirfd))) { switch (errno(system.fchdir(dirfd))) {
.SUCCESS => return, .SUCCESS => return,
@ -3012,7 +3014,7 @@ pub const ReadLinkError = error{
/// On other platforms, the result is an opaque sequence of bytes with no particular encoding. /// On other platforms, the result is an opaque sequence of bytes with no particular encoding.
pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
if (native_os == .wasi and !builtin.link_libc) { if (native_os == .wasi and !builtin.link_libc) {
return readlinkat(AT.FDCWD, file_path, out_buffer); return readlinkat(At.fdcwd, file_path, out_buffer);
} else if (native_os == .windows) { } else if (native_os == .windows) {
var file_path_w = try windows.sliceToPrefixedFileW(null, file_path); var file_path_w = try windows.sliceToPrefixedFileW(null, file_path);
const result_w = try readlinkW(file_path_w.span(), &file_path_w.data); const result_w = try readlinkW(file_path_w.span(), &file_path_w.data);
@ -3972,7 +3974,7 @@ pub const FStatAtError = FStatError || error{
/// On WASI, `pathname` should be encoded as valid UTF-8. /// On WASI, `pathname` should be encoded as valid UTF-8.
/// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding. /// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding.
/// See also `fstatatZ`. /// See also `fstatatZ`.
pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat { pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: At) FStatAtError!Stat {
if (native_os == .wasi and !builtin.link_libc) { if (native_os == .wasi and !builtin.link_libc) {
@compileError("use std.Io instead"); @compileError("use std.Io instead");
} else if (native_os == .windows) { } else if (native_os == .windows) {
@ -3985,7 +3987,7 @@ pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat
/// Same as `fstatat` but `pathname` is null-terminated. /// Same as `fstatat` but `pathname` is null-terminated.
/// See also `fstatat`. /// See also `fstatat`.
pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat { pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: At) FStatAtError!Stat {
if (native_os == .wasi and !builtin.link_libc) { if (native_os == .wasi and !builtin.link_libc) {
@compileError("use std.Io instead"); @compileError("use std.Io instead");
} }

View file

@ -37,7 +37,7 @@ test "check WASI CWD" {
if (!builtin.link_libc) { if (!builtin.link_libc) {
// WASI without-libc hardcodes fd 3 as the FDCWD token so it can be passed directly to WASI calls // 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); try expectEqual(3, posix.At.fdcwd);
} }
} }
} }
@ -907,7 +907,7 @@ test "pwrite with empty buffer" {
} }
fn expectMode(dir: posix.fd_t, file: []const u8, mode: posix.mode_t) !void { fn expectMode(dir: posix.fd_t, file: []const u8, mode: posix.mode_t) !void {
const st = try posix.fstatat(dir, file, posix.AT.SYMLINK_NOFOLLOW); const st = try posix.fstatat(dir, file, .{ .symlink_nofollow = true });
try expectEqual(mode, st.mode & 0b111_111_111); try expectEqual(mode, st.mode & 0b111_111_111);
} }
@ -932,13 +932,13 @@ test "fchmodat smoke test" {
try posix.symlinkat("regfile", tmp.dir.fd, "symlink"); try posix.symlinkat("regfile", tmp.dir.fd, "symlink");
const sym_mode = blk: { const sym_mode = blk: {
const st = try posix.fstatat(tmp.dir.fd, "symlink", posix.AT.SYMLINK_NOFOLLOW); const st = try posix.fstatat(tmp.dir.fd, "symlink", .{ .symlink_nofollow = true });
break :blk st.mode & 0b111_111_111; break :blk st.mode & 0b111_111_111;
}; };
try posix.fchmodat(tmp.dir.fd, "regfile", 0o640, 0); try posix.fchmodat(tmp.dir.fd, "regfile", 0o640, 0);
try expectMode(tmp.dir.fd, "regfile", 0o640); try expectMode(tmp.dir.fd, "regfile", 0o640);
try posix.fchmodat(tmp.dir.fd, "regfile", 0o600, posix.AT.SYMLINK_NOFOLLOW); try posix.fchmodat(tmp.dir.fd, "regfile", 0o600, posix.At.SYMLINK_NOFOLLOW);
try expectMode(tmp.dir.fd, "regfile", 0o600); try expectMode(tmp.dir.fd, "regfile", 0o600);
try posix.fchmodat(tmp.dir.fd, "symlink", 0o640, 0); try posix.fchmodat(tmp.dir.fd, "symlink", 0o640, 0);
@ -946,7 +946,7 @@ test "fchmodat smoke test" {
try expectMode(tmp.dir.fd, "symlink", sym_mode); try expectMode(tmp.dir.fd, "symlink", sym_mode);
var test_link = true; var test_link = true;
posix.fchmodat(tmp.dir.fd, "symlink", 0o600, posix.AT.SYMLINK_NOFOLLOW) catch |err| switch (err) { posix.fchmodat(tmp.dir.fd, "symlink", 0o600, posix.At.SYMLINK_NOFOLLOW) catch |err| switch (err) {
error.OperationNotSupported => test_link = false, error.OperationNotSupported => test_link = false,
else => |e| return e, else => |e| return e,
}; };