mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +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,
|
dirMake: *const fn (?*anyopaque, Dir, sub_path: []const u8, mode: Dir.Mode) Dir.MakeError!void,
|
||||||
dirStat: *const fn (?*anyopaque, Dir) Dir.StatError!Dir.Stat,
|
dirStat: *const fn (?*anyopaque, Dir) Dir.StatError!Dir.Stat,
|
||||||
dirStatPath: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.StatPathOptions) Dir.StatPathError!File.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,
|
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,
|
dirOpenFile: *const fn (?*anyopaque, Dir, sub_path: []const u8, File.OpenFlags) File.OpenError!File,
|
||||||
fileStat: *const fn (?*anyopaque, File) File.StatError!File.Stat,
|
fileStat: *const fn (?*anyopaque, File) File.StatError!File.Stat,
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,37 @@ pub const PathNameError = error{
|
||||||
BadPathName,
|
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{
|
pub const OpenError = error{
|
||||||
FileNotFound,
|
FileNotFound,
|
||||||
NotDir,
|
NotDir,
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,67 @@ pub fn stat(file: File, io: Io) StatError!Stat {
|
||||||
return io.vtable.fileStat(io.userdata, file);
|
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 CreateFlags = std.fs.File.CreateFlags;
|
||||||
|
|
||||||
pub const OpenError = error{
|
pub const OpenError = error{
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,11 @@ pub fn io(t: *Threaded) Io {
|
||||||
.wasi => fileStatWasi,
|
.wasi => fileStatWasi,
|
||||||
else => fileStatPosix,
|
else => fileStatPosix,
|
||||||
},
|
},
|
||||||
|
.dirAccess = switch (builtin.os.tag) {
|
||||||
|
.windows => @panic("TODO"),
|
||||||
|
.wasi => dirAccessWasi,
|
||||||
|
else => dirAccessPosix,
|
||||||
|
},
|
||||||
.dirCreateFile = switch (builtin.os.tag) {
|
.dirCreateFile = switch (builtin.os.tag) {
|
||||||
.windows => @panic("TODO"),
|
.windows => @panic("TODO"),
|
||||||
.wasi => @panic("TODO"),
|
.wasi => @panic("TODO"),
|
||||||
|
|
@ -992,7 +997,6 @@ fn dirStatPathWasi(
|
||||||
) Io.Dir.StatPathError!Io.File.Stat {
|
) Io.Dir.StatPathError!Io.File.Stat {
|
||||||
if (builtin.link_libc) return dirStatPathPosix(userdata, dir, sub_path, options);
|
if (builtin.link_libc) return dirStatPathPosix(userdata, dir, sub_path, options);
|
||||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||||
const dir_fd = dir.handle;
|
|
||||||
const wasi = std.os.wasi;
|
const wasi = std.os.wasi;
|
||||||
const flags: wasi.lookupflags_t = .{
|
const flags: wasi.lookupflags_t = .{
|
||||||
.SYMLINK_FOLLOW = @intFromBool(options.follow_symlinks),
|
.SYMLINK_FOLLOW = @intFromBool(options.follow_symlinks),
|
||||||
|
|
@ -1000,16 +1004,16 @@ fn dirStatPathWasi(
|
||||||
var stat: wasi.filestat_t = undefined;
|
var stat: wasi.filestat_t = undefined;
|
||||||
while (true) {
|
while (true) {
|
||||||
try t.checkCancel();
|
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),
|
.SUCCESS => return statFromWasi(stat),
|
||||||
.INTR => continue,
|
.INTR => continue,
|
||||||
.CANCELED => return error.Canceled,
|
.CANCELED => return error.Canceled,
|
||||||
|
|
||||||
.INVAL => |err| errnoBug(err),
|
.INVAL => |err| return errnoBug(err),
|
||||||
.BADF => |err| errnoBug(err), // Always a race condition.
|
.BADF => |err| return errnoBug(err), // Always a race condition.
|
||||||
.NOMEM => return error.SystemResources,
|
.NOMEM => return error.SystemResources,
|
||||||
.ACCES => return error.AccessDenied,
|
.ACCES => return error.AccessDenied,
|
||||||
.FAULT => |err| errnoBug(err),
|
.FAULT => |err| return errnoBug(err),
|
||||||
.NAMETOOLONG => return error.NameTooLong,
|
.NAMETOOLONG => return error.NameTooLong,
|
||||||
.NOENT => return error.FileNotFound,
|
.NOENT => return error.FileNotFound,
|
||||||
.NOTDIR => 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 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;
|
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(
|
fn dirCreateFilePosix(
|
||||||
userdata: ?*anyopaque,
|
userdata: ?*anyopaque,
|
||||||
dir: Io.Dir,
|
dir: Io.Dir,
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
//! File System.
|
//! File System.
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const native_os = builtin.os.tag;
|
||||||
|
|
||||||
const std = @import("std.zig");
|
const std = @import("std.zig");
|
||||||
const builtin = @import("builtin");
|
const Io = std.Io;
|
||||||
const root = @import("root");
|
const root = @import("root");
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const base64 = std.base64;
|
const base64 = std.base64;
|
||||||
const crypto = std.crypto;
|
const crypto = std.crypto;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const native_os = builtin.os.tag;
|
|
||||||
const posix = std.posix;
|
const posix = std.posix;
|
||||||
const windows = std.os.windows;
|
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 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 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.
|
/// 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));
|
assert(path.isAbsolute(absolute_path));
|
||||||
try cwd().access(absolute_path, flags);
|
try cwd().access(absolute_path, flags);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2353,47 +2353,14 @@ pub fn writeFile(self: Dir, options: WriteFileOptions) WriteFileError!void {
|
||||||
try file.writeAll(options.data);
|
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`.
|
/// Deprecated in favor of `Io.Dir.access`.
|
||||||
/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
|
pub fn access(self: Dir, sub_path: []const u8, options: Io.Dir.AccessOptions) AccessError!void {
|
||||||
/// On WASI, `sub_path` should be encoded as valid UTF-8.
|
var threaded: Io.Threaded = .init_single_threaded;
|
||||||
/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
|
const io = threaded.io();
|
||||||
/// Be careful of Time-Of-Check-Time-Of-Use race conditions when using this function.
|
return Io.Dir.access(self.adaptToNewApi(), io, sub_path, options);
|
||||||
/// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const CopyFileOptions = struct {
|
pub const CopyFileOptions = struct {
|
||||||
|
|
|
||||||
|
|
@ -40,65 +40,12 @@ pub const default_mode = switch (builtin.os.tag) {
|
||||||
|
|
||||||
/// Deprecated in favor of `Io.File.OpenError`.
|
/// Deprecated in favor of `Io.File.OpenError`.
|
||||||
pub const OpenError = Io.File.OpenError || error{WouldBlock};
|
pub const OpenError = Io.File.OpenError || error{WouldBlock};
|
||||||
|
/// Deprecated in favor of `Io.File.OpenMode`.
|
||||||
pub const OpenMode = enum {
|
pub const OpenMode = Io.File.OpenMode;
|
||||||
read_only,
|
/// Deprecated in favor of `Io.File.Lock`.
|
||||||
write_only,
|
pub const Lock = Io.File.Lock;
|
||||||
read_write,
|
/// Deprecated in favor of `Io.File.OpenFlags`.
|
||||||
};
|
pub const OpenFlags = Io.File.OpenFlags;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const CreateFlags = struct {
|
pub const CreateFlags = struct {
|
||||||
/// Whether the file will be created with read access.
|
/// Whether the file will be created with read access.
|
||||||
|
|
|
||||||
|
|
@ -4360,8 +4360,7 @@ pub const FStatAtError = FStatError || error{
|
||||||
NameTooLong,
|
NameTooLong,
|
||||||
FileNotFound,
|
FileNotFound,
|
||||||
SymLinkLoop,
|
SymLinkLoop,
|
||||||
/// WASI-only; file paths must be valid UTF-8.
|
BadPathName,
|
||||||
InvalidUtf8,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Similar to `fstat`, but returns stat of a resource pointed to by `pathname`
|
/// 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);
|
_ = try windows.GetFileAttributesW(path_w.span().ptr);
|
||||||
return;
|
return;
|
||||||
} else if (native_os == .wasi and !builtin.link_libc) {
|
} 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);
|
const path_c = try toPosixPath(path);
|
||||||
return accessZ(&path_c, mode);
|
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{
|
pub const PipeError = error{
|
||||||
SystemFdQuotaExceeded,
|
SystemFdQuotaExceeded,
|
||||||
ProcessFdQuotaExceeded,
|
ProcessFdQuotaExceeded,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue