mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
std.Io: implement dirStatPath
This commit is contained in:
parent
750b1431bf
commit
8a1e6c8c39
14 changed files with 406 additions and 291 deletions
|
|
@ -1305,7 +1305,7 @@ fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) fs.File.PReadErro
|
|||
}
|
||||
|
||||
// Create/Write a file, close it, then grab its stat.mtime timestamp.
|
||||
fn testGetCurrentFileTimestamp(dir: fs.Dir) !i128 {
|
||||
fn testGetCurrentFileTimestamp(dir: fs.Dir) !Io.Timestamp {
|
||||
const test_out_file = "test-filetimestamp.tmp";
|
||||
|
||||
var file = try dir.createFile(test_out_file, .{
|
||||
|
|
@ -1333,8 +1333,8 @@ test "cache file and then recall it" {
|
|||
|
||||
// Wait for file timestamps to tick
|
||||
const initial_time = try testGetCurrentFileTimestamp(tmp.dir);
|
||||
while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {
|
||||
try std.Io.Duration.sleep(.fromNanoseconds(1), io);
|
||||
while ((try testGetCurrentFileTimestamp(tmp.dir)).nanoseconds == initial_time.nanoseconds) {
|
||||
try std.Io.Clock.Duration.sleep(.{ .clock = .boot, .raw = .fromNanoseconds(1) }, io);
|
||||
}
|
||||
|
||||
var digest1: HexDigest = undefined;
|
||||
|
|
@ -1399,8 +1399,8 @@ test "check that changing a file makes cache fail" {
|
|||
|
||||
// Wait for file timestamps to tick
|
||||
const initial_time = try testGetCurrentFileTimestamp(tmp.dir);
|
||||
while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {
|
||||
try std.Io.Duration.sleep(.fromNanoseconds(1), io);
|
||||
while ((try testGetCurrentFileTimestamp(tmp.dir)).nanoseconds == initial_time.nanoseconds) {
|
||||
try std.Io.Clock.Duration.sleep(.{ .clock = .boot, .raw = .fromNanoseconds(1) }, io);
|
||||
}
|
||||
|
||||
var digest1: HexDigest = undefined;
|
||||
|
|
@ -1517,8 +1517,8 @@ test "Manifest with files added after initial hash work" {
|
|||
|
||||
// Wait for file timestamps to tick
|
||||
const initial_time = try testGetCurrentFileTimestamp(tmp.dir);
|
||||
while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {
|
||||
try std.Io.Duration.sleep(.fromNanoseconds(1), io);
|
||||
while ((try testGetCurrentFileTimestamp(tmp.dir)).nanoseconds == initial_time.nanoseconds) {
|
||||
try std.Io.Clock.Duration.sleep(.{ .clock = .boot, .raw = .fromNanoseconds(1) }, io);
|
||||
}
|
||||
|
||||
var digest1: HexDigest = undefined;
|
||||
|
|
@ -1568,8 +1568,8 @@ test "Manifest with files added after initial hash work" {
|
|||
|
||||
// Wait for file timestamps to tick
|
||||
const initial_time2 = try testGetCurrentFileTimestamp(tmp.dir);
|
||||
while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time2) {
|
||||
try std.Io.Duration.sleep(.fromNanoseconds(1), io);
|
||||
while ((try testGetCurrentFileTimestamp(tmp.dir)).nanoseconds == initial_time2.nanoseconds) {
|
||||
try std.Io.Clock.Duration.sleep(.{ .clock = .boot, .raw = .fromNanoseconds(1) }, io);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -654,20 +654,20 @@ pub const VTable = struct {
|
|||
conditionWait: *const fn (?*anyopaque, cond: *Condition, mutex: *Mutex) Cancelable!void,
|
||||
conditionWake: *const fn (?*anyopaque, cond: *Condition, wake: Condition.Wake) void,
|
||||
|
||||
dirMake: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.MakeError!void,
|
||||
dirStat: *const fn (?*anyopaque, dir: Dir) Dir.StatError!Dir.Stat,
|
||||
dirStatPath: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8) Dir.StatError!File.Stat,
|
||||
fileStat: *const fn (?*anyopaque, file: File) File.StatError!File.Stat,
|
||||
createFile: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File,
|
||||
fileOpen: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File,
|
||||
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,
|
||||
fileStat: *const fn (?*anyopaque, File) File.StatError!File.Stat,
|
||||
createFile: *const fn (?*anyopaque, Dir, sub_path: []const u8, File.CreateFlags) File.OpenError!File,
|
||||
fileOpen: *const fn (?*anyopaque, Dir, sub_path: []const u8, File.OpenFlags) File.OpenError!File,
|
||||
fileClose: *const fn (?*anyopaque, File) void,
|
||||
pwrite: *const fn (?*anyopaque, file: File, buffer: []const u8, offset: std.posix.off_t) File.PWriteError!usize,
|
||||
pwrite: *const fn (?*anyopaque, File, buffer: []const u8, offset: std.posix.off_t) File.PWriteError!usize,
|
||||
/// Returns 0 on end of stream.
|
||||
fileReadStreaming: *const fn (?*anyopaque, file: File, data: [][]u8) File.ReadStreamingError!usize,
|
||||
fileReadStreaming: *const fn (?*anyopaque, File, data: [][]u8) File.ReadStreamingError!usize,
|
||||
/// Returns 0 on end of stream.
|
||||
fileReadPositional: *const fn (?*anyopaque, file: File, data: [][]u8, offset: u64) File.ReadPositionalError!usize,
|
||||
fileSeekBy: *const fn (?*anyopaque, file: File, offset: i64) File.SeekError!void,
|
||||
fileSeekTo: *const fn (?*anyopaque, file: File, offset: u64) File.SeekError!void,
|
||||
fileReadPositional: *const fn (?*anyopaque, File, data: [][]u8, offset: u64) File.ReadPositionalError!usize,
|
||||
fileSeekBy: *const fn (?*anyopaque, File, offset: i64) File.SeekError!void,
|
||||
fileSeekTo: *const fn (?*anyopaque, File, offset: u64) File.SeekError!void,
|
||||
|
||||
now: *const fn (?*anyopaque, Clock) Clock.Error!Timestamp,
|
||||
sleep: *const fn (?*anyopaque, Timeout) SleepError!void,
|
||||
|
|
@ -795,6 +795,14 @@ pub const Clock = enum {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn subDuration(from: Clock.Timestamp, duration: Clock.Duration) Clock.Timestamp {
|
||||
assert(from.clock == duration.clock);
|
||||
return .{
|
||||
.raw = from.raw.subDuration(duration.raw),
|
||||
.clock = from.clock,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn fromNow(io: Io, duration: Clock.Duration) Error!Clock.Timestamp {
|
||||
return .{
|
||||
.clock = duration.clock,
|
||||
|
|
@ -855,6 +863,10 @@ pub const Timestamp = struct {
|
|||
return .{ .nanoseconds = from.nanoseconds + duration.nanoseconds };
|
||||
}
|
||||
|
||||
pub fn subDuration(from: Timestamp, duration: Duration) Timestamp {
|
||||
return .{ .nanoseconds = from.nanoseconds - duration.nanoseconds };
|
||||
}
|
||||
|
||||
pub fn withClock(t: Timestamp, clock: Clock) Clock.Timestamp {
|
||||
return .{ .nanoseconds = t.nanoseconds, .clock = clock };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,29 @@ pub fn cwd() Dir {
|
|||
|
||||
pub const Handle = std.posix.fd_t;
|
||||
|
||||
pub const PathNameError = error{
|
||||
NameTooLong,
|
||||
/// File system cannot encode the requested file name bytes.
|
||||
/// Could be due to invalid WTF-8 on Windows, invalid UTF-8 on WASI,
|
||||
/// invalid characters on Windows, etc. Filesystem and operating specific.
|
||||
BadPathName,
|
||||
};
|
||||
|
||||
pub const OpenError = error{
|
||||
FileNotFound,
|
||||
NotDir,
|
||||
AccessDenied,
|
||||
PermissionDenied,
|
||||
SymLinkLoop,
|
||||
ProcessFdQuotaExceeded,
|
||||
SystemFdQuotaExceeded,
|
||||
NoDevice,
|
||||
SystemResources,
|
||||
DeviceBusy,
|
||||
/// On Windows, `\\server` or `\\server\share` was not found.
|
||||
NetworkNotFound,
|
||||
} || PathNameError || Io.Cancelable || Io.UnexpectedError;
|
||||
|
||||
pub fn openFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
|
||||
return io.vtable.fileOpen(io.userdata, dir, sub_path, flags);
|
||||
}
|
||||
|
|
@ -149,22 +172,15 @@ pub const MakeError = error{
|
|||
PathAlreadyExists,
|
||||
SymLinkLoop,
|
||||
LinkQuotaExceeded,
|
||||
NameTooLong,
|
||||
FileNotFound,
|
||||
SystemResources,
|
||||
NoSpaceLeft,
|
||||
NotDir,
|
||||
ReadOnlyFileSystem,
|
||||
/// Windows-only; file paths provided by the user must be valid WTF-8.
|
||||
/// https://simonsapin.github.io/wtf-8/
|
||||
InvalidWtf8,
|
||||
BadPathName,
|
||||
NoDevice,
|
||||
/// On Windows, `\\server` or `\\server\share` was not found.
|
||||
NetworkNotFound,
|
||||
/// File system cannot encode the requested file name bytes.
|
||||
InvalidFileName,
|
||||
} || Io.Cancelable || Io.UnexpectedError;
|
||||
} || PathNameError || Io.Cancelable || Io.UnexpectedError;
|
||||
|
||||
/// Creates a single directory with a relative or absolute path.
|
||||
///
|
||||
|
|
@ -225,7 +241,7 @@ pub fn makePathStatus(dir: Dir, io: Io, sub_path: []const u8) MakePathError!Make
|
|||
// could cause an infinite loop
|
||||
check_dir: {
|
||||
// workaround for windows, see https://github.com/ziglang/zig/issues/16738
|
||||
const fstat = statPath(dir, io, component.path) catch |stat_err| switch (stat_err) {
|
||||
const fstat = statPath(dir, io, component.path, .{}) catch |stat_err| switch (stat_err) {
|
||||
error.IsDir => break :check_dir,
|
||||
else => |e| return e,
|
||||
};
|
||||
|
|
@ -251,6 +267,10 @@ pub fn stat(dir: Dir, io: Io) StatError!Stat {
|
|||
|
||||
pub const StatPathError = File.OpenError || File.StatError;
|
||||
|
||||
pub const StatPathOptions = struct {
|
||||
follow_symlinks: bool = true,
|
||||
};
|
||||
|
||||
/// Returns metadata for a file inside the directory.
|
||||
///
|
||||
/// On Windows, this requires three syscalls. On other operating systems, it
|
||||
|
|
@ -263,6 +283,6 @@ pub const StatPathError = File.OpenError || File.StatError;
|
|||
/// * On Windows, `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
|
||||
/// * 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.
|
||||
pub fn statPath(dir: Dir, io: Io, sub_path: []const u8) StatPathError!Stat {
|
||||
return io.vtable.dirStatPath(io.userdata, dir, sub_path);
|
||||
pub fn statPath(dir: Dir, io: Io, sub_path: []const u8, options: StatPathOptions) StatPathError!Stat {
|
||||
return io.vtable.dirStatPath(io.userdata, dir, sub_path, options);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,63 @@ pub fn stat(file: File, io: Io) StatError!Stat {
|
|||
pub const OpenFlags = std.fs.File.OpenFlags;
|
||||
pub const CreateFlags = std.fs.File.CreateFlags;
|
||||
|
||||
pub const OpenError = std.fs.File.OpenError || Io.Cancelable;
|
||||
pub const OpenError = error{
|
||||
SharingViolation,
|
||||
PipeBusy,
|
||||
NoDevice,
|
||||
/// On Windows, `\\server` or `\\server\share` was not found.
|
||||
NetworkNotFound,
|
||||
ProcessNotFound,
|
||||
/// On Windows, antivirus software is enabled by default. It can be
|
||||
/// disabled, but Windows Update sometimes ignores the user's preference
|
||||
/// and re-enables it. When enabled, antivirus software on Windows
|
||||
/// intercepts file system operations and makes them significantly slower
|
||||
/// in addition to possibly failing with this error code.
|
||||
AntivirusInterference,
|
||||
/// In WASI, this error may occur when the file descriptor does
|
||||
/// not hold the required rights to open a new resource relative to it.
|
||||
AccessDenied,
|
||||
PermissionDenied,
|
||||
SymLinkLoop,
|
||||
ProcessFdQuotaExceeded,
|
||||
SystemFdQuotaExceeded,
|
||||
/// Either:
|
||||
/// * One of the path components does not exist.
|
||||
/// * Cwd was used, but cwd has been deleted.
|
||||
/// * The path associated with the open directory handle has been deleted.
|
||||
/// * On macOS, multiple processes or threads raced to create the same file
|
||||
/// with `O.EXCL` set to `false`.
|
||||
FileNotFound,
|
||||
/// The path exceeded `max_path_bytes` bytes.
|
||||
/// Insufficient kernel memory was available, or
|
||||
/// the named file is a FIFO and per-user hard limit on
|
||||
/// memory allocation for pipes has been reached.
|
||||
SystemResources,
|
||||
/// The file is too large to be opened. This error is unreachable
|
||||
/// for 64-bit targets, as well as when opening directories.
|
||||
FileTooBig,
|
||||
/// The path refers to directory but the `DIRECTORY` flag was not provided.
|
||||
IsDir,
|
||||
/// A new path cannot be created because the device has no room for the new file.
|
||||
/// This error is only reachable when the `CREAT` flag is provided.
|
||||
NoSpaceLeft,
|
||||
/// A component used as a directory in the path was not, in fact, a directory, or
|
||||
/// `DIRECTORY` was specified and the path was not a directory.
|
||||
NotDir,
|
||||
/// The path already exists and the `CREAT` and `EXCL` flags were provided.
|
||||
PathAlreadyExists,
|
||||
DeviceBusy,
|
||||
FileLocksNotSupported,
|
||||
/// One of these three things:
|
||||
/// * pathname refers to an executable image which is currently being
|
||||
/// executed and write access was requested.
|
||||
/// * pathname refers to a file that is currently in use as a swap
|
||||
/// file, and the O_TRUNC flag was specified.
|
||||
/// * pathname refers to a file that is currently being read by the
|
||||
/// kernel (e.g., for module/firmware loading), and write access was
|
||||
/// requested.
|
||||
FileBusy,
|
||||
} || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError;
|
||||
|
||||
pub fn close(file: File, io: Io) void {
|
||||
return io.vtable.fileClose(io.userdata, file);
|
||||
|
|
|
|||
|
|
@ -166,14 +166,23 @@ pub fn io(pool: *Pool) Io {
|
|||
else => dirMakePosix,
|
||||
},
|
||||
.dirStat = dirStat,
|
||||
.dirStatPath = dirStatPath,
|
||||
.dirStatPath = switch (builtin.os.tag) {
|
||||
.linux => dirStatPathLinux,
|
||||
.windows => @panic("TODO"),
|
||||
.wasi => @panic("TODO"),
|
||||
else => dirStatPathPosix,
|
||||
},
|
||||
.fileStat = switch (builtin.os.tag) {
|
||||
.linux => fileStatLinux,
|
||||
.windows => fileStatWindows,
|
||||
.wasi => fileStatWasi,
|
||||
else => fileStatPosix,
|
||||
},
|
||||
.createFile = createFile,
|
||||
.createFile = switch (builtin.os.tag) {
|
||||
.windows => @panic("TODO"),
|
||||
.wasi => @panic("TODO"),
|
||||
else => createFilePosix,
|
||||
},
|
||||
.fileOpen = fileOpen,
|
||||
.fileClose = fileClose,
|
||||
.pwrite = pwrite,
|
||||
|
|
@ -765,8 +774,10 @@ fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.
|
|||
|
||||
fn dirMakePosix(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void {
|
||||
const pool: *Pool = @ptrCast(@alignCast(userdata));
|
||||
|
||||
var path_buffer: [posix.PATH_MAX]u8 = undefined;
|
||||
const sub_path_posix = try toPosixPath(sub_path, &path_buffer);
|
||||
const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
|
||||
|
||||
while (true) {
|
||||
try pool.checkCancel();
|
||||
switch (posix.errno(posix.system.mkdirat(dir.handle, sub_path_posix, mode))) {
|
||||
|
|
@ -788,7 +799,7 @@ fn dirMakePosix(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode:
|
|||
.ROFS => return error.ReadOnlyFileSystem,
|
||||
// dragonfly: when dir_fd is unlinked from filesystem
|
||||
.NOTCONN => return error.FileNotFound,
|
||||
.ILSEQ => return error.InvalidFileName,
|
||||
.ILSEQ => return error.BadPathName,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
|
@ -802,13 +813,82 @@ fn dirStat(userdata: ?*anyopaque, dir: Io.Dir) Io.Dir.StatError!Io.Dir.Stat {
|
|||
@panic("TODO");
|
||||
}
|
||||
|
||||
fn dirStatPath(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8) Io.Dir.StatError!Io.File.Stat {
|
||||
fn dirStatPathLinux(
|
||||
userdata: ?*anyopaque,
|
||||
dir: Io.Dir,
|
||||
sub_path: []const u8,
|
||||
options: Io.Dir.StatPathOptions,
|
||||
) Io.Dir.StatPathError!Io.File.Stat {
|
||||
const pool: *Pool = @ptrCast(@alignCast(userdata));
|
||||
try pool.checkCancel();
|
||||
const linux = std.os.linux;
|
||||
|
||||
_ = dir;
|
||||
_ = sub_path;
|
||||
@panic("TODO");
|
||||
var path_buffer: [posix.PATH_MAX]u8 = undefined;
|
||||
const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
|
||||
|
||||
const flags: u32 = linux.AT.NO_AUTOMOUNT |
|
||||
@as(u32, if (!options.follow_symlinks) linux.AT.SYMLINK_NOFOLLOW else 0);
|
||||
|
||||
while (true) {
|
||||
try pool.checkCancel();
|
||||
var statx = std.mem.zeroes(linux.Statx);
|
||||
const rc = linux.statx(
|
||||
dir.handle,
|
||||
sub_path_posix,
|
||||
flags,
|
||||
linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME,
|
||||
&statx,
|
||||
);
|
||||
switch (linux.E.init(rc)) {
|
||||
.SUCCESS => return statFromLinux(&statx),
|
||||
.INTR => continue,
|
||||
.ACCES => return error.AccessDenied,
|
||||
.BADF => |err| return errnoBug(err),
|
||||
.FAULT => |err| return errnoBug(err),
|
||||
.INVAL => |err| return errnoBug(err),
|
||||
.LOOP => return error.SymLinkLoop,
|
||||
.NAMETOOLONG => |err| return errnoBug(err), // Handled by pathToPosix() above.
|
||||
.NOENT => return error.FileNotFound,
|
||||
.NOTDIR => return error.NotDir,
|
||||
.NOMEM => return error.SystemResources,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dirStatPathPosix(
|
||||
userdata: ?*anyopaque,
|
||||
dir: Io.Dir,
|
||||
sub_path: []const u8,
|
||||
options: Io.Dir.StatPathOptions,
|
||||
) Io.Dir.StatPathError!Io.File.Stat {
|
||||
const pool: *Pool = @ptrCast(@alignCast(userdata));
|
||||
|
||||
var path_buffer: [posix.PATH_MAX]u8 = undefined;
|
||||
const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
|
||||
|
||||
const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;
|
||||
const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.system.fstatat;
|
||||
|
||||
while (true) {
|
||||
try pool.checkCancel();
|
||||
var stat = std.mem.zeroes(posix.Stat);
|
||||
switch (posix.errno(fstatat_sym(dir.handle, sub_path_posix, &stat, flags))) {
|
||||
.SUCCESS => return statFromPosix(stat),
|
||||
.INTR => continue,
|
||||
.INVAL => |err| return errnoBug(err),
|
||||
.BADF => |err| return errnoBug(err), // Always a race condition.
|
||||
.NOMEM => return error.SystemResources,
|
||||
.ACCES => return error.AccessDenied,
|
||||
.PERM => return error.PermissionDenied,
|
||||
.FAULT => |err| return errnoBug(err),
|
||||
.NAMETOOLONG => return error.NameTooLong,
|
||||
.LOOP => return error.SymLinkLoop,
|
||||
.NOENT => return error.FileNotFound,
|
||||
.NOTDIR => return error.FileNotFound,
|
||||
.ILSEQ => return error.BadPathName,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fileStatPosix(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat {
|
||||
|
|
@ -885,17 +965,127 @@ fn fileStatWasi(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.
|
|||
}
|
||||
}
|
||||
|
||||
fn createFile(
|
||||
const have_flock = @TypeOf(posix.system.flock) != void;
|
||||
|
||||
fn createFilePosix(
|
||||
userdata: ?*anyopaque,
|
||||
dir: Io.Dir,
|
||||
sub_path: []const u8,
|
||||
flags: Io.File.CreateFlags,
|
||||
) Io.File.OpenError!Io.File {
|
||||
const pool: *Pool = @ptrCast(@alignCast(userdata));
|
||||
|
||||
var path_buffer: [posix.PATH_MAX]u8 = undefined;
|
||||
const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
|
||||
|
||||
var os_flags: posix.O = .{
|
||||
.ACCMODE = if (flags.read) .RDWR else .WRONLY,
|
||||
.CREAT = true,
|
||||
.TRUNC = flags.truncate,
|
||||
.EXCL = flags.exclusive,
|
||||
};
|
||||
if (@hasField(posix.O, "LARGEFILE")) os_flags.LARGEFILE = true;
|
||||
if (@hasField(posix.O, "CLOEXEC")) os_flags.CLOEXEC = true;
|
||||
|
||||
// Use the O locking flags if the os supports them to acquire the lock
|
||||
// atomically. Note that the NONBLOCK flag is removed after the openat()
|
||||
// call is successful.
|
||||
const has_flock_open_flags = @hasField(posix.O, "EXLOCK");
|
||||
if (has_flock_open_flags) switch (flags.lock) {
|
||||
.none => {},
|
||||
.shared => {
|
||||
os_flags.SHLOCK = true;
|
||||
os_flags.NONBLOCK = flags.lock_nonblocking;
|
||||
},
|
||||
.exclusive => {
|
||||
os_flags.EXLOCK = true;
|
||||
os_flags.NONBLOCK = flags.lock_nonblocking;
|
||||
},
|
||||
};
|
||||
|
||||
const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat;
|
||||
|
||||
const fd: posix.fd_t = while (true) {
|
||||
try pool.checkCancel();
|
||||
const fs_dir: std.fs.Dir = .{ .fd = dir.handle };
|
||||
const fs_file = try fs_dir.createFile(sub_path, flags);
|
||||
return .{ .handle = fs_file.handle };
|
||||
const rc = openat_sym(dir.handle, sub_path_posix, os_flags, flags.mode);
|
||||
switch (posix.errno(rc)) {
|
||||
.SUCCESS => break @intCast(rc),
|
||||
.INTR => continue,
|
||||
|
||||
.FAULT => |err| return errnoBug(err),
|
||||
.INVAL => return error.BadPathName,
|
||||
.BADF => |err| return errnoBug(err),
|
||||
.ACCES => return error.AccessDenied,
|
||||
.FBIG => return error.FileTooBig,
|
||||
.OVERFLOW => return error.FileTooBig,
|
||||
.ISDIR => return error.IsDir,
|
||||
.LOOP => return error.SymLinkLoop,
|
||||
.MFILE => return error.ProcessFdQuotaExceeded,
|
||||
.NAMETOOLONG => return error.NameTooLong,
|
||||
.NFILE => return error.SystemFdQuotaExceeded,
|
||||
.NODEV => return error.NoDevice,
|
||||
.NOENT => return error.FileNotFound,
|
||||
.SRCH => return error.ProcessNotFound,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.NOSPC => return error.NoSpaceLeft,
|
||||
.NOTDIR => return error.NotDir,
|
||||
.PERM => return error.PermissionDenied,
|
||||
.EXIST => return error.PathAlreadyExists,
|
||||
.BUSY => return error.DeviceBusy,
|
||||
.OPNOTSUPP => return error.FileLocksNotSupported,
|
||||
//.AGAIN => return error.WouldBlock,
|
||||
.TXTBSY => return error.FileBusy,
|
||||
.NXIO => return error.NoDevice,
|
||||
.ILSEQ => return error.BadPathName,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
};
|
||||
errdefer posix.close(fd);
|
||||
|
||||
if (have_flock and !has_flock_open_flags and flags.lock != .none) {
|
||||
const lock_nonblocking: i32 = if (flags.lock_nonblocking) posix.LOCK.NB else 0;
|
||||
const lock_flags = switch (flags.lock) {
|
||||
.none => unreachable,
|
||||
.shared => posix.LOCK.SH | lock_nonblocking,
|
||||
.exclusive => posix.LOCK.EX | lock_nonblocking,
|
||||
};
|
||||
while (true) {
|
||||
try pool.checkCancel();
|
||||
switch (posix.errno(posix.system.flock(fd, lock_flags))) {
|
||||
.SUCCESS => break,
|
||||
.INTR => continue,
|
||||
|
||||
.BADF => |err| return errnoBug(err),
|
||||
.INVAL => |err| return errnoBug(err), // invalid parameters
|
||||
.NOLCK => return error.SystemResources,
|
||||
//.AGAIN => return error.WouldBlock,
|
||||
.OPNOTSUPP => return error.FileLocksNotSupported,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (has_flock_open_flags and flags.lock_nonblocking) {
|
||||
var fl_flags: usize = while (true) {
|
||||
try pool.checkCancel();
|
||||
switch (posix.errno(posix.system.fcntl(fd, posix.F.GETFL, 0))) {
|
||||
.SUCCESS => break,
|
||||
.INTR => continue,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
};
|
||||
fl_flags &= ~@as(usize, 1 << @bitOffsetOf(posix.O, "NONBLOCK"));
|
||||
while (true) {
|
||||
try pool.checkCancel();
|
||||
switch (posix.errno(posix.fcntl(fd, posix.F.SETFL, fl_flags))) {
|
||||
.SUCCESS => break,
|
||||
.INTR => continue,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return .{ .handle = fd };
|
||||
}
|
||||
|
||||
fn fileOpen(
|
||||
|
|
@ -2293,8 +2483,8 @@ fn timestampToPosix(nanoseconds: i96) std.posix.timespec {
|
|||
};
|
||||
}
|
||||
|
||||
fn toPosixPath(file_path: []const u8, buffer: *[posix.PATH_MAX]u8) error{ NameTooLong, InvalidFileName }![:0]u8 {
|
||||
if (std.mem.containsAtLeastScalar2(u8, file_path, 0, 1)) return error.InvalidFileName;
|
||||
fn pathToPosix(file_path: []const u8, buffer: *[posix.PATH_MAX]u8) Io.Dir.PathNameError![:0]u8 {
|
||||
if (std.mem.containsAtLeastScalar2(u8, file_path, 0, 1)) return error.BadPathName;
|
||||
// >= rather than > to make room for the null byte
|
||||
if (file_path.len >= buffer.len) return error.NameTooLong;
|
||||
@memcpy(buffer[0..file_path.len], file_path);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ const native_endian = @import("builtin").target.cpu.arch.endian();
|
|||
const tmpDir = std.testing.tmpDir;
|
||||
|
||||
test "write a file, read it, then delete it" {
|
||||
const io = std.testing.io;
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
|
|
@ -45,7 +47,7 @@ test "write a file, read it, then delete it" {
|
|||
try expectEqual(expected_file_size, file_size);
|
||||
|
||||
var file_buffer: [1024]u8 = undefined;
|
||||
var file_reader = file.reader(&file_buffer);
|
||||
var file_reader = file.reader(io, &file_buffer);
|
||||
const contents = try file_reader.interface.allocRemaining(std.testing.allocator, .limited(2 * 1024));
|
||||
defer std.testing.allocator.free(contents);
|
||||
|
||||
|
|
@ -114,10 +116,10 @@ test "updateTimes" {
|
|||
const stat_old = try file.stat();
|
||||
// Set atime and mtime to 5s before
|
||||
try file.updateTimes(
|
||||
stat_old.atime - 5 * std.time.ns_per_s,
|
||||
stat_old.mtime - 5 * std.time.ns_per_s,
|
||||
stat_old.atime.subDuration(.fromSeconds(5)),
|
||||
stat_old.mtime.subDuration(.fromSeconds(5)),
|
||||
);
|
||||
const stat_new = try file.stat();
|
||||
try expect(stat_new.atime < stat_old.atime);
|
||||
try expect(stat_new.mtime < stat_old.mtime);
|
||||
try expect(stat_new.atime.nanoseconds < stat_old.atime.nanoseconds);
|
||||
try expect(stat_new.mtime.nanoseconds < stat_old.mtime.nanoseconds);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -318,10 +318,13 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
|
|||
var buf: [32]u8 = undefined;
|
||||
const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()});
|
||||
|
||||
var threaded: std.Io.Threaded = .init_single_threaded;
|
||||
const io = threaded.io();
|
||||
|
||||
const file = try std.fs.cwd().openFile(path, .{});
|
||||
defer file.close();
|
||||
|
||||
var file_reader = file.readerStreaming(&.{});
|
||||
var file_reader = file.readerStreaming(io, &.{});
|
||||
const data_len = file_reader.interface.readSliceShort(buffer_ptr[0 .. max_name_len + 1]) catch |err| switch (err) {
|
||||
error.ReadFailed => return file_reader.err.?,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -123,14 +123,9 @@ const SingleThreadedImpl = struct {
|
|||
fn wait(self: *Impl, mutex: *Mutex, timeout: ?u64) error{Timeout}!void {
|
||||
_ = self;
|
||||
_ = mutex;
|
||||
|
||||
// There are no other threads to wake us up.
|
||||
// So if we wait without a timeout we would never wake up.
|
||||
const timeout_ns = timeout orelse {
|
||||
unreachable; // deadlock detected
|
||||
};
|
||||
|
||||
std.Thread.sleep(timeout_ns);
|
||||
assert(timeout != null); // Deadlock detected.
|
||||
return error.Timeout;
|
||||
}
|
||||
|
||||
|
|
@ -323,6 +318,8 @@ test "wait and signal" {
|
|||
return error.SkipZigTest;
|
||||
}
|
||||
|
||||
const io = testing.io;
|
||||
|
||||
const num_threads = 4;
|
||||
|
||||
const MultiWait = struct {
|
||||
|
|
@ -348,7 +345,7 @@ test "wait and signal" {
|
|||
}
|
||||
|
||||
while (true) {
|
||||
std.Thread.sleep(100 * std.time.ns_per_ms);
|
||||
try std.Io.Clock.Duration.sleep(.{ .clock = .awake, .raw = .fromMilliseconds(100) }, io);
|
||||
|
||||
multi_wait.mutex.lock();
|
||||
defer multi_wait.mutex.unlock();
|
||||
|
|
@ -368,6 +365,8 @@ test signal {
|
|||
return error.SkipZigTest;
|
||||
}
|
||||
|
||||
const io = testing.io;
|
||||
|
||||
const num_threads = 4;
|
||||
|
||||
const SignalTest = struct {
|
||||
|
|
@ -405,7 +404,7 @@ test signal {
|
|||
}
|
||||
|
||||
while (true) {
|
||||
std.Thread.sleep(10 * std.time.ns_per_ms);
|
||||
try std.Io.Clock.Duration.sleep(.{ .clock = .awake, .raw = .fromMilliseconds(10) }, io);
|
||||
|
||||
signal_test.mutex.lock();
|
||||
defer signal_test.mutex.unlock();
|
||||
|
|
|
|||
|
|
@ -441,7 +441,7 @@ pub fn resolveInPlace(base: Uri, new_len: usize, aux_buf: *[]u8) ResolveInPlaceE
|
|||
};
|
||||
}
|
||||
|
||||
fn validateHost(bytes: []const u8) []const u8 {
|
||||
fn validateHost(bytes: []const u8) HostName.ValidateError![]const u8 {
|
||||
try HostName.validate(bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ const ElfDynLibError = error{
|
|||
ElfStringSectionNotFound,
|
||||
ElfSymSectionNotFound,
|
||||
ElfHashTableNotFound,
|
||||
Canceled,
|
||||
} || posix.OpenError || posix.MMapError;
|
||||
|
||||
pub const ElfDynLib = struct {
|
||||
|
|
|
|||
|
|
@ -36,10 +36,6 @@ const IteratorError = error{
|
|||
AccessDenied,
|
||||
PermissionDenied,
|
||||
SystemResources,
|
||||
/// WASI-only. The path of an entry could not be encoded as valid UTF-8.
|
||||
/// WASI is unable to handle paths that cannot be encoded as well-formed UTF-8.
|
||||
/// https://github.com/WebAssembly/wasi-filesystem/issues/17#issuecomment-1430639353
|
||||
InvalidUtf8,
|
||||
} || posix.UnexpectedError;
|
||||
|
||||
pub const Iterator = switch (native_os) {
|
||||
|
|
@ -553,7 +549,6 @@ pub const Iterator = switch (native_os) {
|
|||
.INVAL => unreachable,
|
||||
.NOENT => return error.DirNotFound, // The directory being iterated was deleted during iteration.
|
||||
.NOTCAPABLE => return error.AccessDenied,
|
||||
.ILSEQ => return error.InvalidUtf8, // An entry's name cannot be encoded as UTF-8.
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
if (bufused == 0) return null;
|
||||
|
|
@ -844,28 +839,7 @@ pub fn walk(self: Dir, allocator: Allocator) Allocator.Error!Walker {
|
|||
};
|
||||
}
|
||||
|
||||
pub const OpenError = error{
|
||||
FileNotFound,
|
||||
NotDir,
|
||||
AccessDenied,
|
||||
PermissionDenied,
|
||||
SymLinkLoop,
|
||||
ProcessFdQuotaExceeded,
|
||||
NameTooLong,
|
||||
SystemFdQuotaExceeded,
|
||||
NoDevice,
|
||||
SystemResources,
|
||||
/// WASI-only; file paths must be valid UTF-8.
|
||||
InvalidUtf8,
|
||||
/// Windows-only; file paths provided by the user must be valid WTF-8.
|
||||
/// https://wtf-8.codeberg.page/
|
||||
InvalidWtf8,
|
||||
BadPathName,
|
||||
DeviceBusy,
|
||||
/// On Windows, `\\server` or `\\server\share` was not found.
|
||||
NetworkNotFound,
|
||||
ProcessNotFound,
|
||||
} || posix.UnexpectedError;
|
||||
pub const OpenError = Io.Dir.OpenError;
|
||||
|
||||
pub fn close(self: *Dir) void {
|
||||
posix.close(self.fd);
|
||||
|
|
@ -1311,7 +1285,7 @@ pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenOptio
|
|||
};
|
||||
}
|
||||
|
||||
pub const RealPathError = posix.RealPathError;
|
||||
pub const RealPathError = posix.RealPathError || error{Canceled};
|
||||
|
||||
/// This function returns the canonicalized absolute pathname of
|
||||
/// `pathname` relative to this `Dir`. If `pathname` is absolute, ignores this
|
||||
|
|
@ -1369,7 +1343,6 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathE
|
|||
error.FileLocksNotSupported => return error.Unexpected,
|
||||
error.FileBusy => return error.Unexpected,
|
||||
error.WouldBlock => return error.Unexpected,
|
||||
error.InvalidUtf8 => unreachable, // WASI-only
|
||||
else => |e| return e,
|
||||
};
|
||||
defer posix.close(fd);
|
||||
|
|
@ -1639,6 +1612,10 @@ fn openDirFlagsZ(self: Dir, sub_path_c: [*:0]const u8, flags: posix.O) OpenError
|
|||
error.FileLocksNotSupported => unreachable, // locking folders is not supported
|
||||
error.WouldBlock => unreachable, // can't happen for directories
|
||||
error.FileBusy => unreachable, // can't happen for directories
|
||||
error.SharingViolation => unreachable, // can't happen for directories
|
||||
error.PipeBusy => unreachable, // can't happen for directories
|
||||
error.AntivirusInterference => unreachable, // can't happen for directories
|
||||
error.ProcessNotFound => unreachable, // can't happen for directories
|
||||
else => |e| return e,
|
||||
};
|
||||
return Dir{ .fd = fd };
|
||||
|
|
@ -2095,24 +2072,21 @@ pub const DeleteTreeError = error{
|
|||
FileBusy,
|
||||
DeviceBusy,
|
||||
ProcessNotFound,
|
||||
|
||||
/// One of the path components was not a directory.
|
||||
/// This error is unreachable if `sub_path` does not contain a path separator.
|
||||
NotDir,
|
||||
|
||||
/// WASI-only; file paths must be valid UTF-8.
|
||||
InvalidUtf8,
|
||||
|
||||
/// Windows-only; file paths provided by the user must be valid WTF-8.
|
||||
/// https://wtf-8.codeberg.page/
|
||||
InvalidWtf8,
|
||||
|
||||
/// On Windows, file paths cannot contain these characters:
|
||||
/// '/', '*', '?', '"', '<', '>', '|'
|
||||
BadPathName,
|
||||
|
||||
/// On Windows, `\\server` or `\\server\share` was not found.
|
||||
NetworkNotFound,
|
||||
|
||||
Canceled,
|
||||
} || posix.UnexpectedError;
|
||||
|
||||
/// Whether `sub_path` describes a symlink, file, or directory, this function
|
||||
|
|
@ -2169,17 +2143,15 @@ pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void {
|
|||
error.PermissionDenied,
|
||||
error.SymLinkLoop,
|
||||
error.ProcessFdQuotaExceeded,
|
||||
error.ProcessNotFound,
|
||||
error.NameTooLong,
|
||||
error.SystemFdQuotaExceeded,
|
||||
error.NoDevice,
|
||||
error.SystemResources,
|
||||
error.Unexpected,
|
||||
error.InvalidUtf8,
|
||||
error.InvalidWtf8,
|
||||
error.BadPathName,
|
||||
error.NetworkNotFound,
|
||||
error.DeviceBusy,
|
||||
error.Canceled,
|
||||
=> |e| return e,
|
||||
};
|
||||
stack.appendAssumeCapacity(.{
|
||||
|
|
@ -2266,18 +2238,16 @@ pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void {
|
|||
error.AccessDenied,
|
||||
error.PermissionDenied,
|
||||
error.SymLinkLoop,
|
||||
error.ProcessNotFound,
|
||||
error.ProcessFdQuotaExceeded,
|
||||
error.NameTooLong,
|
||||
error.SystemFdQuotaExceeded,
|
||||
error.NoDevice,
|
||||
error.SystemResources,
|
||||
error.Unexpected,
|
||||
error.InvalidUtf8,
|
||||
error.InvalidWtf8,
|
||||
error.BadPathName,
|
||||
error.NetworkNotFound,
|
||||
error.DeviceBusy,
|
||||
error.Canceled,
|
||||
=> |e| return e,
|
||||
};
|
||||
} else {
|
||||
|
|
@ -2374,18 +2344,16 @@ fn deleteTreeMinStackSizeWithKindHint(self: Dir, sub_path: []const u8, kind_hint
|
|||
error.AccessDenied,
|
||||
error.PermissionDenied,
|
||||
error.SymLinkLoop,
|
||||
error.ProcessNotFound,
|
||||
error.ProcessFdQuotaExceeded,
|
||||
error.NameTooLong,
|
||||
error.SystemFdQuotaExceeded,
|
||||
error.NoDevice,
|
||||
error.SystemResources,
|
||||
error.Unexpected,
|
||||
error.InvalidUtf8,
|
||||
error.InvalidWtf8,
|
||||
error.BadPathName,
|
||||
error.NetworkNotFound,
|
||||
error.DeviceBusy,
|
||||
error.Canceled,
|
||||
=> |e| return e,
|
||||
};
|
||||
if (cleanup_dir_parent) |*d| d.close();
|
||||
|
|
@ -2476,17 +2444,15 @@ fn deleteTreeOpenInitialSubpath(self: Dir, sub_path: []const u8, kind_hint: File
|
|||
error.PermissionDenied,
|
||||
error.SymLinkLoop,
|
||||
error.ProcessFdQuotaExceeded,
|
||||
error.ProcessNotFound,
|
||||
error.NameTooLong,
|
||||
error.SystemFdQuotaExceeded,
|
||||
error.NoDevice,
|
||||
error.SystemResources,
|
||||
error.Unexpected,
|
||||
error.InvalidUtf8,
|
||||
error.InvalidWtf8,
|
||||
error.BadPathName,
|
||||
error.DeviceBusy,
|
||||
error.NetworkNotFound,
|
||||
error.Canceled,
|
||||
=> |e| return e,
|
||||
};
|
||||
} else {
|
||||
|
|
@ -2589,7 +2555,7 @@ pub const CopyFileOptions = struct {
|
|||
|
||||
pub const CopyFileError = File.OpenError || File.StatError ||
|
||||
AtomicFile.InitError || AtomicFile.FinishError ||
|
||||
File.ReadError || File.WriteError;
|
||||
File.ReadError || File.WriteError || error{InvalidFileName};
|
||||
|
||||
/// Atomically creates a new file at `dest_path` within `dest_dir` with the
|
||||
/// same contents as `source_path` within `source_dir`, overwriting any already
|
||||
|
|
@ -2690,33 +2656,9 @@ pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat {
|
|||
const st = try std.os.fstatat_wasi(self.fd, sub_path, .{ .SYMLINK_FOLLOW = true });
|
||||
return Stat.fromWasi(st);
|
||||
}
|
||||
if (native_os == .linux) {
|
||||
const sub_path_c = try posix.toPosixPath(sub_path);
|
||||
var stx = std.mem.zeroes(linux.Statx);
|
||||
|
||||
const rc = linux.statx(
|
||||
self.fd,
|
||||
&sub_path_c,
|
||||
linux.AT.NO_AUTOMOUNT,
|
||||
linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME,
|
||||
&stx,
|
||||
);
|
||||
|
||||
return switch (linux.E.init(rc)) {
|
||||
.SUCCESS => Stat.fromLinux(stx),
|
||||
.ACCES => error.AccessDenied,
|
||||
.BADF => unreachable,
|
||||
.FAULT => unreachable,
|
||||
.INVAL => unreachable,
|
||||
.LOOP => error.SymLinkLoop,
|
||||
.NAMETOOLONG => unreachable, // Handled by posix.toPosixPath() above.
|
||||
.NOENT, .NOTDIR => error.FileNotFound,
|
||||
.NOMEM => error.SystemResources,
|
||||
else => |err| posix.unexpectedErrno(err),
|
||||
};
|
||||
}
|
||||
const st = try posix.fstatat(self.fd, sub_path, 0);
|
||||
return Stat.fromPosix(st);
|
||||
var threaded: Io.Threaded = .init_single_threaded;
|
||||
const io = threaded.io();
|
||||
return Io.Dir.statPath(.{ .handle = self.fd }, io, sub_path, .{});
|
||||
}
|
||||
|
||||
pub const ChmodError = File.ChmodError;
|
||||
|
|
|
|||
|
|
@ -38,33 +38,8 @@ pub const default_mode = switch (builtin.os.tag) {
|
|||
else => 0o666,
|
||||
};
|
||||
|
||||
pub const OpenError = error{
|
||||
SharingViolation,
|
||||
PathAlreadyExists,
|
||||
FileNotFound,
|
||||
AccessDenied,
|
||||
PipeBusy,
|
||||
NoDevice,
|
||||
NameTooLong,
|
||||
/// WASI-only; file paths must be valid UTF-8.
|
||||
InvalidUtf8,
|
||||
/// Windows-only; file paths provided by the user must be valid WTF-8.
|
||||
/// https://wtf-8.codeberg.page/
|
||||
InvalidWtf8,
|
||||
/// On Windows, file paths cannot contain these characters:
|
||||
/// '/', '*', '?', '"', '<', '>', '|'
|
||||
BadPathName,
|
||||
Unexpected,
|
||||
/// On Windows, `\\server` or `\\server\share` was not found.
|
||||
NetworkNotFound,
|
||||
ProcessNotFound,
|
||||
/// On Windows, antivirus software is enabled by default. It can be
|
||||
/// disabled, but Windows Update sometimes ignores the user's preference
|
||||
/// and re-enables it. When enabled, antivirus software on Windows
|
||||
/// intercepts file system operations and makes them significantly slower
|
||||
/// in addition to possibly failing with this error code.
|
||||
AntivirusInterference,
|
||||
} || posix.OpenError || posix.FlockError;
|
||||
/// Deprecated in favor of `Io.File.OpenError`.
|
||||
pub const OpenError = Io.File.OpenError || error{WouldBlock};
|
||||
|
||||
pub const OpenMode = enum {
|
||||
read_only,
|
||||
|
|
|
|||
|
|
@ -1542,81 +1542,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz
|
|||
}
|
||||
}
|
||||
|
||||
pub const OpenError = error{
|
||||
/// In WASI, this error may occur when the file descriptor does
|
||||
/// not hold the required rights to open a new resource relative to it.
|
||||
AccessDenied,
|
||||
PermissionDenied,
|
||||
SymLinkLoop,
|
||||
ProcessFdQuotaExceeded,
|
||||
SystemFdQuotaExceeded,
|
||||
NoDevice,
|
||||
/// Either:
|
||||
/// * One of the path components does not exist.
|
||||
/// * Cwd was used, but cwd has been deleted.
|
||||
/// * The path associated with the open directory handle has been deleted.
|
||||
/// * On macOS, multiple processes or threads raced to create the same file
|
||||
/// with `O.EXCL` set to `false`.
|
||||
FileNotFound,
|
||||
|
||||
/// The path exceeded `max_path_bytes` bytes.
|
||||
NameTooLong,
|
||||
|
||||
/// Insufficient kernel memory was available, or
|
||||
/// the named file is a FIFO and per-user hard limit on
|
||||
/// memory allocation for pipes has been reached.
|
||||
SystemResources,
|
||||
|
||||
/// The file is too large to be opened. This error is unreachable
|
||||
/// for 64-bit targets, as well as when opening directories.
|
||||
FileTooBig,
|
||||
|
||||
/// The path refers to directory but the `DIRECTORY` flag was not provided.
|
||||
IsDir,
|
||||
|
||||
/// A new path cannot be created because the device has no room for the new file.
|
||||
/// This error is only reachable when the `CREAT` flag is provided.
|
||||
NoSpaceLeft,
|
||||
|
||||
/// A component used as a directory in the path was not, in fact, a directory, or
|
||||
/// `DIRECTORY` was specified and the path was not a directory.
|
||||
NotDir,
|
||||
|
||||
/// The path already exists and the `CREAT` and `EXCL` flags were provided.
|
||||
PathAlreadyExists,
|
||||
DeviceBusy,
|
||||
|
||||
/// The underlying filesystem does not support file locks
|
||||
FileLocksNotSupported,
|
||||
|
||||
/// Path contains characters that are disallowed by the underlying filesystem.
|
||||
BadPathName,
|
||||
|
||||
/// WASI-only; file paths must be valid UTF-8.
|
||||
InvalidUtf8,
|
||||
|
||||
/// Windows-only; file paths provided by the user must be valid WTF-8.
|
||||
/// https://wtf-8.codeberg.page/
|
||||
InvalidWtf8,
|
||||
|
||||
/// On Windows, `\\server` or `\\server\share` was not found.
|
||||
NetworkNotFound,
|
||||
|
||||
/// This error occurs in Linux if the process to be open was not found.
|
||||
ProcessNotFound,
|
||||
|
||||
/// One of these three things:
|
||||
/// * pathname refers to an executable image which is currently being
|
||||
/// executed and write access was requested.
|
||||
/// * pathname refers to a file that is currently in use as a swap
|
||||
/// file, and the O_TRUNC flag was specified.
|
||||
/// * pathname refers to a file that is currently being read by the
|
||||
/// kernel (e.g., for module/firmware loading), and write access was
|
||||
/// requested.
|
||||
FileBusy,
|
||||
|
||||
WouldBlock,
|
||||
} || UnexpectedError;
|
||||
pub const OpenError = std.Io.File.OpenError || error{WouldBlock};
|
||||
|
||||
/// Open and possibly create a file. Keeps trying if it gets interrupted.
|
||||
/// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
|
||||
|
|
|
|||
|
|
@ -766,27 +766,23 @@ test glibcVerFromLinkName {
|
|||
|
||||
fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion {
|
||||
var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {
|
||||
error.NameTooLong => unreachable,
|
||||
error.InvalidUtf8 => unreachable, // WASI only
|
||||
error.InvalidWtf8 => unreachable, // Windows-only
|
||||
error.BadPathName => unreachable,
|
||||
error.DeviceBusy => unreachable,
|
||||
error.NetworkNotFound => unreachable, // Windows-only
|
||||
error.NameTooLong => return error.Unexpected,
|
||||
error.BadPathName => return error.Unexpected,
|
||||
error.DeviceBusy => return error.Unexpected,
|
||||
error.NetworkNotFound => return error.Unexpected, // Windows-only
|
||||
|
||||
error.FileNotFound,
|
||||
error.NotDir,
|
||||
error.AccessDenied,
|
||||
error.PermissionDenied,
|
||||
error.NoDevice,
|
||||
=> return error.GLibCNotFound,
|
||||
error.FileNotFound => return error.GLibCNotFound,
|
||||
error.NotDir => return error.GLibCNotFound,
|
||||
error.AccessDenied => return error.GLibCNotFound,
|
||||
error.PermissionDenied => return error.GLibCNotFound,
|
||||
error.NoDevice => return error.GLibCNotFound,
|
||||
|
||||
error.ProcessNotFound,
|
||||
error.ProcessFdQuotaExceeded,
|
||||
error.SystemFdQuotaExceeded,
|
||||
error.SystemResources,
|
||||
error.SymLinkLoop,
|
||||
error.Unexpected,
|
||||
=> |e| return e,
|
||||
error.ProcessFdQuotaExceeded => |e| return e,
|
||||
error.SystemFdQuotaExceeded => |e| return e,
|
||||
error.SystemResources => |e| return e,
|
||||
error.SymLinkLoop => |e| return e,
|
||||
error.Unexpected => |e| return e,
|
||||
error.Canceled => |e| return e,
|
||||
};
|
||||
defer dir.close();
|
||||
|
||||
|
|
@ -799,38 +795,34 @@ fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion {
|
|||
// that start with "GLIBC_2.".
|
||||
const glibc_so_basename = "libc.so.6";
|
||||
var file = dir.openFile(glibc_so_basename, .{}) catch |err| switch (err) {
|
||||
error.NameTooLong => unreachable,
|
||||
error.InvalidUtf8 => unreachable, // WASI only
|
||||
error.InvalidWtf8 => unreachable, // Windows only
|
||||
error.BadPathName => unreachable, // Windows only
|
||||
error.PipeBusy => unreachable, // Windows-only
|
||||
error.SharingViolation => unreachable, // Windows-only
|
||||
error.NetworkNotFound => unreachable, // Windows-only
|
||||
error.AntivirusInterference => unreachable, // Windows-only
|
||||
error.FileLocksNotSupported => unreachable, // No lock requested.
|
||||
error.NoSpaceLeft => unreachable, // read-only
|
||||
error.PathAlreadyExists => unreachable, // read-only
|
||||
error.DeviceBusy => unreachable, // read-only
|
||||
error.FileBusy => unreachable, // read-only
|
||||
error.WouldBlock => unreachable, // not using O_NONBLOCK
|
||||
error.NoDevice => unreachable, // not asking for a special device
|
||||
|
||||
error.AccessDenied,
|
||||
error.PermissionDenied,
|
||||
error.FileNotFound,
|
||||
error.NotDir,
|
||||
error.IsDir,
|
||||
=> return error.GLibCNotFound,
|
||||
|
||||
error.NameTooLong => return error.Unexpected,
|
||||
error.BadPathName => return error.Unexpected,
|
||||
error.PipeBusy => return error.Unexpected, // Windows-only
|
||||
error.SharingViolation => return error.Unexpected, // Windows-only
|
||||
error.NetworkNotFound => return error.Unexpected, // Windows-only
|
||||
error.AntivirusInterference => return error.Unexpected, // Windows-only
|
||||
error.FileLocksNotSupported => return error.Unexpected, // No lock requested.
|
||||
error.NoSpaceLeft => return error.Unexpected, // read-only
|
||||
error.PathAlreadyExists => return error.Unexpected, // read-only
|
||||
error.DeviceBusy => return error.Unexpected, // read-only
|
||||
error.FileBusy => return error.Unexpected, // read-only
|
||||
error.NoDevice => return error.Unexpected, // not asking for a special device
|
||||
error.FileTooBig => return error.Unexpected,
|
||||
error.WouldBlock => return error.Unexpected, // not opened in non-blocking
|
||||
|
||||
error.ProcessNotFound,
|
||||
error.ProcessFdQuotaExceeded,
|
||||
error.SystemFdQuotaExceeded,
|
||||
error.SystemResources,
|
||||
error.SymLinkLoop,
|
||||
error.Unexpected,
|
||||
=> |e| return e,
|
||||
error.AccessDenied => return error.GLibCNotFound,
|
||||
error.PermissionDenied => return error.GLibCNotFound,
|
||||
error.FileNotFound => return error.GLibCNotFound,
|
||||
error.NotDir => return error.GLibCNotFound,
|
||||
error.IsDir => return error.GLibCNotFound,
|
||||
|
||||
error.ProcessNotFound => |e| return e,
|
||||
error.ProcessFdQuotaExceeded => |e| return e,
|
||||
error.SystemFdQuotaExceeded => |e| return e,
|
||||
error.SystemResources => |e| return e,
|
||||
error.SymLinkLoop => |e| return e,
|
||||
error.Unexpected => |e| return e,
|
||||
error.Canceled => |e| return e,
|
||||
};
|
||||
defer file.close();
|
||||
|
||||
|
|
@ -1016,12 +1008,9 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
|
|||
error.NameTooLong => return error.Unexpected,
|
||||
error.PathAlreadyExists => return error.Unexpected,
|
||||
error.SharingViolation => return error.Unexpected,
|
||||
error.InvalidUtf8 => return error.Unexpected, // WASI only
|
||||
error.InvalidWtf8 => return error.Unexpected, // Windows only
|
||||
error.BadPathName => return error.Unexpected,
|
||||
error.PipeBusy => return error.Unexpected,
|
||||
error.FileLocksNotSupported => return error.Unexpected,
|
||||
error.WouldBlock => return error.Unexpected,
|
||||
error.FileBusy => return error.Unexpected, // opened without write permissions
|
||||
error.AntivirusInterference => return error.Unexpected, // Windows-only error
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue