mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
std.Io: add dirAccess
This commit is contained in:
parent
3bf0ce65a5
commit
f8ea00bd6d
8 changed files with 225 additions and 226 deletions
|
|
@ -663,6 +663,7 @@ pub const VTable = struct {
|
|||
dirMake: *const fn (?*anyopaque, Dir, sub_path: []const u8, mode: Dir.Mode) Dir.MakeError!void,
|
||||
dirStat: *const fn (?*anyopaque, Dir) Dir.StatError!Dir.Stat,
|
||||
dirStatPath: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.StatPathOptions) Dir.StatPathError!File.Stat,
|
||||
dirAccess: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.AccessOptions) Dir.AccessError!void,
|
||||
dirCreateFile: *const fn (?*anyopaque, Dir, sub_path: []const u8, File.CreateFlags) File.OpenError!File,
|
||||
dirOpenFile: *const fn (?*anyopaque, Dir, sub_path: []const u8, File.OpenFlags) File.OpenError!File,
|
||||
fileStat: *const fn (?*anyopaque, File) File.StatError!File.Stat,
|
||||
|
|
|
|||
|
|
@ -23,6 +23,37 @@ pub const PathNameError = error{
|
|||
BadPathName,
|
||||
};
|
||||
|
||||
pub const AccessError = error{
|
||||
AccessDenied,
|
||||
PermissionDenied,
|
||||
FileNotFound,
|
||||
InputOutput,
|
||||
SystemResources,
|
||||
FileBusy,
|
||||
SymLinkLoop,
|
||||
ReadOnlyFileSystem,
|
||||
} || PathNameError || Io.Cancelable || Io.UnexpectedError;
|
||||
|
||||
pub const AccessOptions = packed struct {
|
||||
follow_symlinks: bool = true,
|
||||
read: bool = false,
|
||||
write: bool = false,
|
||||
execute: bool = false,
|
||||
};
|
||||
|
||||
/// Test accessing `sub_path`.
|
||||
///
|
||||
/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
|
||||
/// On WASI, `sub_path` should be encoded as valid UTF-8.
|
||||
/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
|
||||
///
|
||||
/// Be careful of Time-Of-Check-Time-Of-Use race conditions when using this
|
||||
/// function. For example, instead of testing if a file exists and then opening
|
||||
/// it, just open it and handle the error for file not found.
|
||||
pub fn access(dir: Dir, io: Io, sub_path: []const u8, options: AccessOptions) AccessError!void {
|
||||
return io.vtable.dirAccess(io.userdata, dir, sub_path, options);
|
||||
}
|
||||
|
||||
pub const OpenError = error{
|
||||
FileNotFound,
|
||||
NotDir,
|
||||
|
|
|
|||
|
|
@ -80,7 +80,67 @@ pub fn stat(file: File, io: Io) StatError!Stat {
|
|||
return io.vtable.fileStat(io.userdata, file);
|
||||
}
|
||||
|
||||
pub const OpenFlags = std.fs.File.OpenFlags;
|
||||
pub const OpenMode = enum {
|
||||
read_only,
|
||||
write_only,
|
||||
read_write,
|
||||
};
|
||||
|
||||
pub const Lock = enum {
|
||||
none,
|
||||
shared,
|
||||
exclusive,
|
||||
};
|
||||
|
||||
pub const OpenFlags = struct {
|
||||
mode: OpenMode = .read_only,
|
||||
|
||||
/// Open the file with an advisory lock to coordinate with other processes
|
||||
/// accessing it at the same time. An exclusive lock will prevent other
|
||||
/// processes from acquiring a lock. A shared lock will prevent other
|
||||
/// processes from acquiring a exclusive lock, but does not prevent
|
||||
/// other process from getting their own shared locks.
|
||||
///
|
||||
/// The lock is advisory, except on Linux in very specific circumstances[1].
|
||||
/// This means that a process that does not respect the locking API can still get access
|
||||
/// to the file, despite the lock.
|
||||
///
|
||||
/// On these operating systems, the lock is acquired atomically with
|
||||
/// opening the file:
|
||||
/// * Darwin
|
||||
/// * DragonFlyBSD
|
||||
/// * FreeBSD
|
||||
/// * Haiku
|
||||
/// * NetBSD
|
||||
/// * OpenBSD
|
||||
/// On these operating systems, the lock is acquired via a separate syscall
|
||||
/// after opening the file:
|
||||
/// * Linux
|
||||
/// * Windows
|
||||
///
|
||||
/// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
|
||||
lock: Lock = .none,
|
||||
|
||||
/// Sets whether or not to wait until the file is locked to return. If set to true,
|
||||
/// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
|
||||
/// is available to proceed.
|
||||
lock_nonblocking: bool = false,
|
||||
|
||||
/// Set this to allow the opened file to automatically become the
|
||||
/// controlling TTY for the current process.
|
||||
allow_ctty: bool = false,
|
||||
|
||||
follow_symlinks: bool = true,
|
||||
|
||||
pub fn isRead(self: OpenFlags) bool {
|
||||
return self.mode != .write_only;
|
||||
}
|
||||
|
||||
pub fn isWrite(self: OpenFlags) bool {
|
||||
return self.mode != .read_only;
|
||||
}
|
||||
};
|
||||
|
||||
pub const CreateFlags = std.fs.File.CreateFlags;
|
||||
|
||||
pub const OpenError = error{
|
||||
|
|
|
|||
|
|
@ -183,6 +183,11 @@ pub fn io(t: *Threaded) Io {
|
|||
.wasi => fileStatWasi,
|
||||
else => fileStatPosix,
|
||||
},
|
||||
.dirAccess = switch (builtin.os.tag) {
|
||||
.windows => @panic("TODO"),
|
||||
.wasi => dirAccessWasi,
|
||||
else => dirAccessPosix,
|
||||
},
|
||||
.dirCreateFile = switch (builtin.os.tag) {
|
||||
.windows => @panic("TODO"),
|
||||
.wasi => @panic("TODO"),
|
||||
|
|
@ -992,7 +997,6 @@ fn dirStatPathWasi(
|
|||
) Io.Dir.StatPathError!Io.File.Stat {
|
||||
if (builtin.link_libc) return dirStatPathPosix(userdata, dir, sub_path, options);
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
const dir_fd = dir.handle;
|
||||
const wasi = std.os.wasi;
|
||||
const flags: wasi.lookupflags_t = .{
|
||||
.SYMLINK_FOLLOW = @intFromBool(options.follow_symlinks),
|
||||
|
|
@ -1000,16 +1004,16 @@ fn dirStatPathWasi(
|
|||
var stat: wasi.filestat_t = undefined;
|
||||
while (true) {
|
||||
try t.checkCancel();
|
||||
switch (wasi.path_filestat_get(dir_fd, flags, sub_path.ptr, sub_path.len, &stat)) {
|
||||
switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) {
|
||||
.SUCCESS => return statFromWasi(stat),
|
||||
.INTR => continue,
|
||||
.CANCELED => return error.Canceled,
|
||||
|
||||
.INVAL => |err| errnoBug(err),
|
||||
.BADF => |err| errnoBug(err), // Always a race condition.
|
||||
.INVAL => |err| return errnoBug(err),
|
||||
.BADF => |err| return errnoBug(err), // Always a race condition.
|
||||
.NOMEM => return error.SystemResources,
|
||||
.ACCES => return error.AccessDenied,
|
||||
.FAULT => |err| errnoBug(err),
|
||||
.FAULT => |err| return errnoBug(err),
|
||||
.NAMETOOLONG => return error.NameTooLong,
|
||||
.NOENT => return error.FileNotFound,
|
||||
.NOTDIR => return error.FileNotFound,
|
||||
|
|
@ -1103,6 +1107,110 @@ const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.syste
|
|||
const lseek_sym = if (posix.lfs64_abi) posix.system.lseek64 else posix.system.lseek;
|
||||
const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv;
|
||||
|
||||
fn dirAccessPosix(
|
||||
userdata: ?*anyopaque,
|
||||
dir: Io.Dir,
|
||||
sub_path: []const u8,
|
||||
options: Io.Dir.AccessOptions,
|
||||
) Io.Dir.AccessError!void {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
|
||||
var path_buffer: [posix.PATH_MAX]u8 = undefined;
|
||||
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 mode: u32 =
|
||||
@as(u32, if (options.read) posix.R_OK else 0) |
|
||||
@as(u32, if (options.write) posix.W_OK else 0) |
|
||||
@as(u32, if (options.execute) posix.X_OK else 0);
|
||||
|
||||
while (true) {
|
||||
try t.checkCancel();
|
||||
switch (posix.errno(posix.system.faccessat(dir.handle, sub_path_posix, mode, flags))) {
|
||||
.SUCCESS => return,
|
||||
.INTR => continue,
|
||||
.CANCELED => return error.Canceled,
|
||||
|
||||
.ACCES => return error.AccessDenied,
|
||||
.PERM => return error.PermissionDenied,
|
||||
.ROFS => return error.ReadOnlyFileSystem,
|
||||
.LOOP => return error.SymLinkLoop,
|
||||
.TXTBSY => return error.FileBusy,
|
||||
.NOTDIR => return error.FileNotFound,
|
||||
.NOENT => return error.FileNotFound,
|
||||
.NAMETOOLONG => return error.NameTooLong,
|
||||
.INVAL => |err| return errnoBug(err),
|
||||
.FAULT => |err| return errnoBug(err),
|
||||
.IO => return error.InputOutput,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.ILSEQ => return error.BadPathName, // TODO move to wasi
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dirAccessWasi(
|
||||
userdata: ?*anyopaque,
|
||||
dir: Io.Dir,
|
||||
sub_path: []const u8,
|
||||
options: Io.File.OpenFlags,
|
||||
) Io.File.AccessError!void {
|
||||
if (builtin.link_libc) return dirAccessPosix(userdata, dir, sub_path, options);
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
const wasi = std.os.wasi;
|
||||
const flags: wasi.lookupflags_t = .{
|
||||
.SYMLINK_FOLLOW = @intFromBool(options.follow_symlinks),
|
||||
};
|
||||
const stat = while (true) {
|
||||
var stat: wasi.filestat_t = undefined;
|
||||
try t.checkCancel();
|
||||
switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) {
|
||||
.SUCCESS => break statFromWasi(stat),
|
||||
.INTR => continue,
|
||||
.CANCELED => return error.Canceled,
|
||||
|
||||
.INVAL => |err| return errnoBug(err),
|
||||
.BADF => |err| return errnoBug(err), // Always a race condition.
|
||||
.NOMEM => return error.SystemResources,
|
||||
.ACCES => return error.AccessDenied,
|
||||
.FAULT => |err| return errnoBug(err),
|
||||
.NAMETOOLONG => return error.NameTooLong,
|
||||
.NOENT => return error.FileNotFound,
|
||||
.NOTDIR => return error.FileNotFound,
|
||||
.NOTCAPABLE => return error.AccessDenied,
|
||||
.ILSEQ => return error.BadPathName,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
};
|
||||
|
||||
if (!options.mode.read and !options.mode.write and !options.mode.execute)
|
||||
return;
|
||||
|
||||
var directory: wasi.fdstat_t = undefined;
|
||||
if (wasi.fd_fdstat_get(dir.handle, &directory) != .SUCCESS)
|
||||
return error.AccessDenied;
|
||||
|
||||
var rights: wasi.rights_t = .{};
|
||||
if (options.mode.read) {
|
||||
if (stat.filetype == .DIRECTORY) {
|
||||
rights.FD_READDIR = true;
|
||||
} else {
|
||||
rights.FD_READ = true;
|
||||
}
|
||||
}
|
||||
if (options.mode.write)
|
||||
rights.FD_WRITE = true;
|
||||
|
||||
// No validation for execution.
|
||||
|
||||
// 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.AccessDenied;
|
||||
}
|
||||
|
||||
fn dirCreateFilePosix(
|
||||
userdata: ?*anyopaque,
|
||||
dir: Io.Dir,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
//! File System.
|
||||
const builtin = @import("builtin");
|
||||
const native_os = builtin.os.tag;
|
||||
|
||||
const std = @import("std.zig");
|
||||
const builtin = @import("builtin");
|
||||
const Io = std.Io;
|
||||
const root = @import("root");
|
||||
const mem = std.mem;
|
||||
const base64 = std.base64;
|
||||
const crypto = std.crypto;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
const native_os = builtin.os.tag;
|
||||
const posix = std.posix;
|
||||
const windows = std.os.windows;
|
||||
|
||||
|
|
@ -274,7 +275,7 @@ pub fn openFileAbsoluteW(absolute_path_w: []const u16, flags: File.OpenFlags) Fi
|
|||
/// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
|
||||
/// On WASI, `absolute_path` should be encoded as valid UTF-8.
|
||||
/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding.
|
||||
pub fn accessAbsolute(absolute_path: []const u8, flags: File.OpenFlags) Dir.AccessError!void {
|
||||
pub fn accessAbsolute(absolute_path: []const u8, flags: Io.Dir.AccessOptions) Dir.AccessError!void {
|
||||
assert(path.isAbsolute(absolute_path));
|
||||
try cwd().access(absolute_path, flags);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2353,47 +2353,14 @@ pub fn writeFile(self: Dir, options: WriteFileOptions) WriteFileError!void {
|
|||
try file.writeAll(options.data);
|
||||
}
|
||||
|
||||
pub const AccessError = posix.AccessError;
|
||||
/// Deprecated in favor of `Io.Dir.AccessError`.
|
||||
pub const AccessError = Io.Dir.AccessError;
|
||||
|
||||
/// Test accessing `sub_path`.
|
||||
/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
|
||||
/// On WASI, `sub_path` should be encoded as valid UTF-8.
|
||||
/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
|
||||
/// Be careful of Time-Of-Check-Time-Of-Use race conditions when using this function.
|
||||
/// For example, instead of testing if a file exists and then opening it, just
|
||||
/// open it and handle the error for file not found.
|
||||
pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void {
|
||||
if (native_os == .windows) {
|
||||
const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path);
|
||||
return self.accessW(sub_path_w.span().ptr, flags);
|
||||
}
|
||||
const path_c = try posix.toPosixPath(sub_path);
|
||||
return self.accessZ(&path_c, flags);
|
||||
}
|
||||
|
||||
/// Same as `access` except the path parameter is null-terminated.
|
||||
pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void {
|
||||
if (native_os == .windows) {
|
||||
const sub_path_w = try windows.cStrToPrefixedFileW(self.fd, sub_path);
|
||||
return self.accessW(sub_path_w.span().ptr, flags);
|
||||
}
|
||||
const os_mode = switch (flags.mode) {
|
||||
.read_only => @as(u32, posix.F_OK),
|
||||
.write_only => @as(u32, posix.W_OK),
|
||||
.read_write => @as(u32, posix.R_OK | posix.W_OK),
|
||||
};
|
||||
const result = posix.faccessatZ(self.fd, sub_path, os_mode, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Same as `access` except asserts the target OS is Windows and the path parameter is
|
||||
/// * WTF-16 LE encoded
|
||||
/// * null-terminated
|
||||
/// * relative or has the NT namespace prefix
|
||||
/// TODO currently this ignores `flags`.
|
||||
pub fn accessW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) AccessError!void {
|
||||
_ = flags;
|
||||
return posix.faccessatW(self.fd, sub_path_w);
|
||||
/// Deprecated in favor of `Io.Dir.access`.
|
||||
pub fn access(self: Dir, sub_path: []const u8, options: Io.Dir.AccessOptions) AccessError!void {
|
||||
var threaded: Io.Threaded = .init_single_threaded;
|
||||
const io = threaded.io();
|
||||
return Io.Dir.access(self.adaptToNewApi(), io, sub_path, options);
|
||||
}
|
||||
|
||||
pub const CopyFileOptions = struct {
|
||||
|
|
|
|||
|
|
@ -40,65 +40,12 @@ pub const default_mode = switch (builtin.os.tag) {
|
|||
|
||||
/// Deprecated in favor of `Io.File.OpenError`.
|
||||
pub const OpenError = Io.File.OpenError || error{WouldBlock};
|
||||
|
||||
pub const OpenMode = enum {
|
||||
read_only,
|
||||
write_only,
|
||||
read_write,
|
||||
};
|
||||
|
||||
pub const Lock = enum {
|
||||
none,
|
||||
shared,
|
||||
exclusive,
|
||||
};
|
||||
|
||||
pub const OpenFlags = struct {
|
||||
mode: OpenMode = .read_only,
|
||||
|
||||
/// Open the file with an advisory lock to coordinate with other processes
|
||||
/// accessing it at the same time. An exclusive lock will prevent other
|
||||
/// processes from acquiring a lock. A shared lock will prevent other
|
||||
/// processes from acquiring a exclusive lock, but does not prevent
|
||||
/// other process from getting their own shared locks.
|
||||
///
|
||||
/// The lock is advisory, except on Linux in very specific circumstances[1].
|
||||
/// This means that a process that does not respect the locking API can still get access
|
||||
/// to the file, despite the lock.
|
||||
///
|
||||
/// On these operating systems, the lock is acquired atomically with
|
||||
/// opening the file:
|
||||
/// * Darwin
|
||||
/// * DragonFlyBSD
|
||||
/// * FreeBSD
|
||||
/// * Haiku
|
||||
/// * NetBSD
|
||||
/// * OpenBSD
|
||||
/// On these operating systems, the lock is acquired via a separate syscall
|
||||
/// after opening the file:
|
||||
/// * Linux
|
||||
/// * Windows
|
||||
///
|
||||
/// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
|
||||
lock: Lock = .none,
|
||||
|
||||
/// Sets whether or not to wait until the file is locked to return. If set to true,
|
||||
/// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
|
||||
/// is available to proceed.
|
||||
lock_nonblocking: bool = false,
|
||||
|
||||
/// Set this to allow the opened file to automatically become the
|
||||
/// controlling TTY for the current process.
|
||||
allow_ctty: bool = false,
|
||||
|
||||
pub fn isRead(self: OpenFlags) bool {
|
||||
return self.mode != .write_only;
|
||||
}
|
||||
|
||||
pub fn isWrite(self: OpenFlags) bool {
|
||||
return self.mode != .read_only;
|
||||
}
|
||||
};
|
||||
/// Deprecated in favor of `Io.File.OpenMode`.
|
||||
pub const OpenMode = Io.File.OpenMode;
|
||||
/// Deprecated in favor of `Io.File.Lock`.
|
||||
pub const Lock = Io.File.Lock;
|
||||
/// Deprecated in favor of `Io.File.OpenFlags`.
|
||||
pub const OpenFlags = Io.File.OpenFlags;
|
||||
|
||||
pub const CreateFlags = struct {
|
||||
/// Whether the file will be created with read access.
|
||||
|
|
|
|||
|
|
@ -4360,8 +4360,7 @@ pub const FStatAtError = FStatError || error{
|
|||
NameTooLong,
|
||||
FileNotFound,
|
||||
SymLinkLoop,
|
||||
/// WASI-only; file paths must be valid UTF-8.
|
||||
InvalidUtf8,
|
||||
BadPathName,
|
||||
};
|
||||
|
||||
/// Similar to `fstat`, but returns stat of a resource pointed to by `pathname`
|
||||
|
|
@ -4900,7 +4899,7 @@ pub fn access(path: []const u8, mode: u32) AccessError!void {
|
|||
_ = try windows.GetFileAttributesW(path_w.span().ptr);
|
||||
return;
|
||||
} else if (native_os == .wasi and !builtin.link_libc) {
|
||||
return faccessat(AT.FDCWD, path, mode, 0);
|
||||
@compileError("wasi doesn't support absolute paths");
|
||||
}
|
||||
const path_c = try toPosixPath(path);
|
||||
return accessZ(&path_c, mode);
|
||||
|
|
@ -4934,121 +4933,6 @@ pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check user's permissions for a file, based on an open directory handle.
|
||||
///
|
||||
/// * On Windows, asserts `path` is valid [WTF-8](https://wtf-8.codeberg.page/).
|
||||
/// * On WASI, invalid UTF-8 passed to `path` causes `error.InvalidUtf8`.
|
||||
/// * On other platforms, `path` is an opaque sequence of bytes with no particular encoding.
|
||||
///
|
||||
/// On Windows, `mode` is ignored. This is a POSIX API that is only partially supported by
|
||||
/// Windows. See `fs` for the cross-platform file system API.
|
||||
pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void {
|
||||
if (native_os == .windows) {
|
||||
const path_w = try windows.sliceToPrefixedFileW(dirfd, path);
|
||||
return faccessatW(dirfd, path_w.span().ptr);
|
||||
} else if (native_os == .wasi and !builtin.link_libc) {
|
||||
const resolved: RelativePathWasi = .{ .dir_fd = dirfd, .relative_path = path };
|
||||
|
||||
const st = try std.os.fstatat_wasi(dirfd, path, .{
|
||||
.SYMLINK_FOLLOW = (flags & AT.SYMLINK_NOFOLLOW) == 0,
|
||||
});
|
||||
|
||||
if (mode != F_OK) {
|
||||
var directory: wasi.fdstat_t = undefined;
|
||||
if (wasi.fd_fdstat_get(resolved.dir_fd, &directory) != .SUCCESS) {
|
||||
return error.AccessDenied;
|
||||
}
|
||||
|
||||
var rights: wasi.rights_t = .{};
|
||||
if (mode & R_OK != 0) {
|
||||
if (st.filetype == .DIRECTORY) {
|
||||
rights.FD_READDIR = true;
|
||||
} else {
|
||||
rights.FD_READ = true;
|
||||
}
|
||||
}
|
||||
if (mode & W_OK != 0) {
|
||||
rights.FD_WRITE = true;
|
||||
}
|
||||
// No validation for X_OK
|
||||
|
||||
// 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.AccessDenied;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
const path_c = try toPosixPath(path);
|
||||
return faccessatZ(dirfd, &path_c, mode, flags);
|
||||
}
|
||||
|
||||
/// Same as `faccessat` except the path parameter is null-terminated.
|
||||
pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void {
|
||||
if (native_os == .windows) {
|
||||
const path_w = try windows.cStrToPrefixedFileW(dirfd, path);
|
||||
return faccessatW(dirfd, path_w.span().ptr);
|
||||
} else if (native_os == .wasi and !builtin.link_libc) {
|
||||
return faccessat(dirfd, mem.sliceTo(path, 0), mode, flags);
|
||||
}
|
||||
switch (errno(system.faccessat(dirfd, path, mode, flags))) {
|
||||
.SUCCESS => return,
|
||||
.ACCES => return error.AccessDenied,
|
||||
.PERM => return error.PermissionDenied,
|
||||
.ROFS => return error.ReadOnlyFileSystem,
|
||||
.LOOP => return error.SymLinkLoop,
|
||||
.TXTBSY => return error.FileBusy,
|
||||
.NOTDIR => return error.FileNotFound,
|
||||
.NOENT => return error.FileNotFound,
|
||||
.NAMETOOLONG => return error.NameTooLong,
|
||||
.INVAL => unreachable,
|
||||
.FAULT => unreachable,
|
||||
.IO => return error.InputOutput,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.ILSEQ => return error.BadPathName,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `faccessat` except asserts the target is Windows and the path parameter
|
||||
/// is NtDll-prefixed, null-terminated, WTF-16 encoded.
|
||||
pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16) AccessError!void {
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
|
||||
return;
|
||||
}
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const path_len_bytes = cast(u16, mem.sliceTo(sub_path_w, 0).len * 2) orelse return error.NameTooLong;
|
||||
var nt_name = windows.UNICODE_STRING{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
.Buffer = @constCast(sub_path_w),
|
||||
};
|
||||
var attr = windows.OBJECT_ATTRIBUTES{
|
||||
.Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
|
||||
.RootDirectory = if (fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd,
|
||||
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
|
||||
.ObjectName = &nt_name,
|
||||
.SecurityDescriptor = null,
|
||||
.SecurityQualityOfService = null,
|
||||
};
|
||||
var basic_info: windows.FILE_BASIC_INFORMATION = undefined;
|
||||
switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) {
|
||||
.SUCCESS => return,
|
||||
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
||||
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
|
||||
.OBJECT_NAME_INVALID => unreachable,
|
||||
.INVALID_PARAMETER => unreachable,
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
||||
else => |rc| return windows.unexpectedStatus(rc),
|
||||
}
|
||||
}
|
||||
|
||||
pub const PipeError = error{
|
||||
SystemFdQuotaExceeded,
|
||||
ProcessFdQuotaExceeded,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue