Commit 07c1dd3d1d
Changed files (6)
lib
lib/std/fs/Dir.zig
@@ -1179,6 +1179,8 @@ pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOp
};
}
+pub const RealPathError = posix.RealPathError;
+
/// This function returns the canonicalized absolute pathname of
/// `pathname` relative to this `Dir`. If `pathname` is absolute, ignores this
/// `Dir` handle and returns the canonicalized absolute pathname of `pathname`
@@ -1186,7 +1188,7 @@ pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOp
/// This function is not universally supported by all platforms.
/// Currently supported hosts are: Linux, macOS, and Windows.
/// See also `Dir.realpathZ`, `Dir.realpathW`, and `Dir.realpathAlloc`.
-pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) ![]u8 {
+pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError![]u8 {
if (builtin.os.tag == .wasi) {
@compileError("realpath is not available on WASI");
}
@@ -1200,7 +1202,7 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) ![]u8 {
/// Same as `Dir.realpath` except `pathname` is null-terminated.
/// See also `Dir.realpath`, `realpathZ`.
-pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) ![]u8 {
+pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathError![]u8 {
if (builtin.os.tag == .windows) {
const pathname_w = try posix.windows.cStrToPrefixedFileW(self.fd, pathname);
return self.realpathW(pathname_w.span(), out_buffer);
@@ -1219,7 +1221,9 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) ![]u8 {
};
const fd = posix.openatZ(self.fd, pathname, flags, 0) catch |err| switch (err) {
- error.FileLocksNotSupported => unreachable,
+ error.FileLocksNotSupported => return error.Unexpected,
+ error.FileBusy => return error.Unexpected,
+ error.WouldBlock => return error.Unexpected,
else => |e| return e,
};
defer posix.close(fd);
@@ -1244,7 +1248,7 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) ![]u8 {
/// Windows-only. Same as `Dir.realpath` except `pathname` is WTF16 encoded.
/// See also `Dir.realpath`, `realpathW`.
-pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) ![]u8 {
+pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) RealPathError![]u8 {
const w = std.os.windows;
const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
@@ -1265,27 +1269,31 @@ pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) ![]u8 {
};
defer w.CloseHandle(h_file);
- // Use of MAX_PATH_BYTES here is valid as the realpath function does not
- // have a variant that takes an arbitrary-size buffer.
- // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008
- // NULL out parameter (GNU's canonicalize_file_name) to handle overelong
- // paths. musl supports passing NULL but restricts the output to PATH_MAX
- // anyway.
- var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
- const out_path = try posix.getFdPath(h_file, &buffer);
-
- if (out_path.len > out_buffer.len) {
+ var wide_buf: [w.PATH_MAX_WIDE]u16 = undefined;
+ const wide_slice = try w.GetFinalPathNameByHandle(h_file, .{}, &wide_buf);
+ var big_out_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
+ const end_index = std.unicode.utf16leToUtf8(&big_out_buf, wide_slice) catch |e| switch (e) {
+ // TODO: Windows file paths can be arbitrary arrays of u16 values and
+ // must not fail with InvalidUtf8.
+ error.DanglingSurrogateHalf,
+ error.ExpectedSecondSurrogateHalf,
+ error.UnexpectedSecondSurrogateHalf,
+ error.CodepointTooLarge,
+ error.Utf8CannotEncodeSurrogateHalf,
+ => return error.InvalidUtf8,
+ };
+ if (end_index > out_buffer.len)
return error.NameTooLong;
- }
-
- const result = out_buffer[0..out_path.len];
- @memcpy(result, out_path);
+ const result = out_buffer[0..end_index];
+ @memcpy(result, big_out_buf[0..end_index]);
return result;
}
+pub const RealPathAllocError = RealPathError || Allocator.Error;
+
/// Same as `Dir.realpath` except caller must free the returned memory.
/// See also `Dir.realpath`.
-pub fn realpathAlloc(self: Dir, allocator: Allocator, pathname: []const u8) ![]u8 {
+pub fn realpathAlloc(self: Dir, allocator: Allocator, pathname: []const u8) RealPathAllocError![]u8 {
// Use of MAX_PATH_BYTES here is valid as the realpath function does not
// have a variant that takes an arbitrary-size buffer.
// TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008
lib/std/fs/File.zig
@@ -48,6 +48,12 @@ pub const OpenError = error{
Unexpected,
/// On Windows, `\\server` or `\\server\share` was not found.
NetworkNotFound,
+ /// 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;
pub const OpenMode = enum {
lib/std/os/windows.zig
@@ -41,6 +41,7 @@ pub const OpenError = error{
NameTooLong,
WouldBlock,
NetworkNotFound,
+ AntivirusInterference,
};
pub const OpenFileOptions = struct {
@@ -145,6 +146,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
std.time.sleep(std.time.ns_per_ms);
continue;
},
+ .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,
else => return unexpectedStatus(rc),
}
}
@@ -637,9 +639,10 @@ pub fn CreateSymbolicLink(
.filter = if (is_directory) .dir_only else .file_only,
}) catch |err| switch (err) {
error.IsDir => return error.PathAlreadyExists,
- error.NotDir => unreachable,
- error.WouldBlock => unreachable,
- error.PipeBusy => unreachable,
+ error.NotDir => return error.Unexpected,
+ error.WouldBlock => return error.Unexpected,
+ error.PipeBusy => return error.Unexpected,
+ error.AntivirusInterference => return error.Unexpected,
else => |e| return e,
};
defer CloseHandle(symlink_handle);
@@ -1158,14 +1161,15 @@ pub fn GetFinalPathNameByHandle(
.share_access = FILE_SHARE_READ | FILE_SHARE_WRITE,
.creation = FILE_OPEN,
}) catch |err| switch (err) {
- error.IsDir => unreachable,
- error.NotDir => unreachable,
- error.NoDevice => unreachable,
- error.AccessDenied => unreachable,
- error.PipeBusy => unreachable,
- error.PathAlreadyExists => unreachable,
- error.WouldBlock => unreachable,
- error.NetworkNotFound => unreachable,
+ error.IsDir => return error.Unexpected,
+ error.NotDir => return error.Unexpected,
+ error.NoDevice => return error.Unexpected,
+ error.AccessDenied => return error.Unexpected,
+ error.PipeBusy => return error.Unexpected,
+ error.PathAlreadyExists => return error.Unexpected,
+ error.WouldBlock => return error.Unexpected,
+ error.NetworkNotFound => return error.Unexpected,
+ error.AntivirusInterference => return error.Unexpected,
else => |e| return e,
};
defer CloseHandle(mgmt_handle);
lib/std/zig/system.zig
@@ -766,6 +766,7 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
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
@@ -1003,6 +1004,7 @@ fn detectAbiAndDynamicLinker(
error.FileLocksNotSupported => unreachable,
error.WouldBlock => unreachable,
error.FileBusy => unreachable, // opened without write permissions
+ error.AntivirusInterference => unreachable, // Windows-only error
error.IsDir,
error.NotDir,
lib/std/child_process.zig
@@ -668,13 +668,14 @@ pub const ChildProcess = struct {
.sa = &saAttr,
.creation = windows.OPEN_EXISTING,
}) catch |err| switch (err) {
- error.PathAlreadyExists => unreachable, // not possible for "NUL"
- error.PipeBusy => unreachable, // not possible for "NUL"
- error.FileNotFound => unreachable, // not possible for "NUL"
- error.AccessDenied => unreachable, // not possible for "NUL"
- error.NameTooLong => unreachable, // not possible for "NUL"
- error.WouldBlock => unreachable, // not possible for "NUL"
- error.NetworkNotFound => unreachable, // not possible for "NUL"
+ error.PathAlreadyExists => return error.Unexpected, // not possible for "NUL"
+ error.PipeBusy => return error.Unexpected, // not possible for "NUL"
+ error.FileNotFound => return error.Unexpected, // not possible for "NUL"
+ error.AccessDenied => return error.Unexpected, // not possible for "NUL"
+ error.NameTooLong => return error.Unexpected, // not possible for "NUL"
+ error.WouldBlock => return error.Unexpected, // not possible for "NUL"
+ error.NetworkNotFound => return error.Unexpected, // not possible for "NUL"
+ error.AntivirusInterference => return error.Unexpected, // not possible for "NUL"
else => |e| return e,
}
else
lib/std/os.zig
@@ -2602,6 +2602,12 @@ pub const RenameError = error{
PipeBusy,
/// On Windows, `\\server` or `\\server\share` was not found.
NetworkNotFound,
+ /// 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,
} || UnexpectedError;
/// Change the name or location of a file.
@@ -2927,9 +2933,10 @@ pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!v
.creation = windows.FILE_CREATE,
.filter = .dir_only,
}) catch |err| switch (err) {
- error.IsDir => unreachable,
- error.PipeBusy => unreachable,
- error.WouldBlock => unreachable,
+ error.IsDir => return error.Unexpected,
+ error.PipeBusy => return error.Unexpected,
+ error.WouldBlock => return error.Unexpected,
+ error.AntivirusInterference => return error.Unexpected,
else => |e| return e,
};
windows.CloseHandle(sub_dir_handle);
@@ -3006,9 +3013,10 @@ pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void {
.creation = windows.FILE_CREATE,
.filter = .dir_only,
}) catch |err| switch (err) {
- error.IsDir => unreachable,
- error.PipeBusy => unreachable,
- error.WouldBlock => unreachable,
+ error.IsDir => return error.Unexpected,
+ error.PipeBusy => return error.Unexpected,
+ error.WouldBlock => return error.Unexpected,
+ error.AntivirusInterference => return error.Unexpected,
else => |e| return e,
};
windows.CloseHandle(sub_dir_handle);
@@ -5347,6 +5355,13 @@ pub const RealPathError = error{
NetworkNotFound,
PathAlreadyExists,
+
+ /// 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,
} || UnexpectedError;
/// Return the canonicalized absolute pathname.
@@ -5441,15 +5456,17 @@ pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPat
pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool {
return switch (os.tag) {
- // zig fmt: off
.windows,
- .macos, .ios, .watchos, .tvos,
+ .macos,
+ .ios,
+ .watchos,
+ .tvos,
.linux,
.solaris,
.illumos,
.freebsd,
=> true,
- // zig fmt: on
+
.dragonfly => os.version_range.semver.max.order(.{ .major = 6, .minor = 0, .patch = 0 }) != .lt,
.netbsd => os.version_range.semver.max.order(.{ .major = 10, .minor = 0, .patch = 0 }) != .lt,
else => false,
@@ -5469,8 +5486,10 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]);
- // Trust that Windows gives us valid UTF-16LE.
- const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch unreachable;
+ // TODO: Windows file paths can be arbitrary arrays of u16 values
+ // and must not fail with InvalidUtf8.
+ const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch
+ return error.InvalidUtf8;
return out_buffer[0..end_index];
},
.macos, .ios, .watchos, .tvos => {