authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-13 14:04:00-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-14 11:14:43-08:00
log07c1dd3d1d6c9a1c4fec931621dfd93328bad1e4
tree6d3365d931b58a4335e30d6be31e478153634e67
parenta23ab331a28d865e3ea636c9033db4de345f8653

std.os.windows.OpenFile: add missing error

Encountered in a recent CI run on an aarch64-windows dev kit. Pretty sure I disabled the virus scanner but it looks like it turned itself back on with a Windows Update. Rather than marking the new error code as unreachable in the places where it is unexpected, this commit makes it return `error.Unexpected`.

6 files changed, 88 insertions(+), 48 deletions(-)

lib/std/child_process.zig+8-7
...@@ -668,13 +668,14 @@ pub const ChildProcess = struct {...@@ -668,13 +668,14 @@ pub const ChildProcess = struct {
668 .sa = &saAttr,668 .sa = &saAttr,
669 .creation = windows.OPEN_EXISTING,669 .creation = windows.OPEN_EXISTING,
670 }) catch |err| switch (err) {670 }) catch |err| switch (err) {
671 error.PathAlreadyExists => unreachable, // not possible for "NUL"671 error.PathAlreadyExists => return error.Unexpected, // not possible for "NUL"
672 error.PipeBusy => unreachable, // not possible for "NUL"672 error.PipeBusy => return error.Unexpected, // not possible for "NUL"
673 error.FileNotFound => unreachable, // not possible for "NUL"673 error.FileNotFound => return error.Unexpected, // not possible for "NUL"
674 error.AccessDenied => unreachable, // not possible for "NUL"674 error.AccessDenied => return error.Unexpected, // not possible for "NUL"
675 error.NameTooLong => unreachable, // not possible for "NUL"675 error.NameTooLong => return error.Unexpected, // not possible for "NUL"
676 error.WouldBlock => unreachable, // not possible for "NUL"676 error.WouldBlock => return error.Unexpected, // not possible for "NUL"
677 error.NetworkNotFound => unreachable, // not possible for "NUL"677 error.NetworkNotFound => return error.Unexpected, // not possible for "NUL"
678 error.AntivirusInterference => return error.Unexpected, // not possible for "NUL"
678 else => |e| return e,679 else => |e| return e,
679 }680 }
680 else681 else
lib/std/fs/Dir.zig+27-19
...@@ -1179,6 +1179,8 @@ pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOp...@@ -1179,6 +1179,8 @@ pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOp
1179 };1179 };
1180}1180}
11811181
1182pub const RealPathError = posix.RealPathError;
1183
1182/// This function returns the canonicalized absolute pathname of1184/// This function returns the canonicalized absolute pathname of
1183/// `pathname` relative to this `Dir`. If `pathname` is absolute, ignores this1185/// `pathname` relative to this `Dir`. If `pathname` is absolute, ignores this
1184/// `Dir` handle and returns the canonicalized absolute pathname of `pathname`1186/// `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...@@ -1186,7 +1188,7 @@ pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOp
1186/// This function is not universally supported by all platforms.1188/// This function is not universally supported by all platforms.
1187/// Currently supported hosts are: Linux, macOS, and Windows.1189/// Currently supported hosts are: Linux, macOS, and Windows.
1188/// See also `Dir.realpathZ`, `Dir.realpathW`, and `Dir.realpathAlloc`.1190/// See also `Dir.realpathZ`, `Dir.realpathW`, and `Dir.realpathAlloc`.
1189pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) ![]u8 {1191pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError![]u8 {
1190 if (builtin.os.tag == .wasi) {1192 if (builtin.os.tag == .wasi) {
1191 @compileError("realpath is not available on WASI");1193 @compileError("realpath is not available on WASI");
1192 }1194 }
...@@ -1200,7 +1202,7 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) ![]u8 {...@@ -1200,7 +1202,7 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) ![]u8 {
12001202
1201/// Same as `Dir.realpath` except `pathname` is null-terminated.1203/// Same as `Dir.realpath` except `pathname` is null-terminated.
1202/// See also `Dir.realpath`, `realpathZ`.1204/// See also `Dir.realpath`, `realpathZ`.
1203pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) ![]u8 {1205pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathError![]u8 {
1204 if (builtin.os.tag == .windows) {1206 if (builtin.os.tag == .windows) {
1205 const pathname_w = try posix.windows.cStrToPrefixedFileW(self.fd, pathname);1207 const pathname_w = try posix.windows.cStrToPrefixedFileW(self.fd, pathname);
1206 return self.realpathW(pathname_w.span(), out_buffer);1208 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 {...@@ -1219,7 +1221,9 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) ![]u8 {
1219 };1221 };
12201222
1221 const fd = posix.openatZ(self.fd, pathname, flags, 0) catch |err| switch (err) {1223 const fd = posix.openatZ(self.fd, pathname, flags, 0) catch |err| switch (err) {
1222 error.FileLocksNotSupported => unreachable,1224 error.FileLocksNotSupported => return error.Unexpected,
1225 error.FileBusy => return error.Unexpected,
1226 error.WouldBlock => return error.Unexpected,
1223 else => |e| return e,1227 else => |e| return e,
1224 };1228 };
1225 defer posix.close(fd);1229 defer posix.close(fd);
...@@ -1244,7 +1248,7 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) ![]u8 {...@@ -1244,7 +1248,7 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) ![]u8 {
12441248
1245/// Windows-only. Same as `Dir.realpath` except `pathname` is WTF16 encoded.1249/// Windows-only. Same as `Dir.realpath` except `pathname` is WTF16 encoded.
1246/// See also `Dir.realpath`, `realpathW`.1250/// See also `Dir.realpath`, `realpathW`.
1247pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) ![]u8 {1251pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) RealPathError![]u8 {
1248 const w = std.os.windows;1252 const w = std.os.windows;
12491253
1250 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;1254 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
...@@ -1265,27 +1269,31 @@ pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) ![]u8 {...@@ -1265,27 +1269,31 @@ pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) ![]u8 {
1265 };1269 };
1266 defer w.CloseHandle(h_file);1270 defer w.CloseHandle(h_file);
12671271
1268 // Use of MAX_PATH_BYTES here is valid as the realpath function does not1272 var wide_buf: [w.PATH_MAX_WIDE]u16 = undefined;
1269 // have a variant that takes an arbitrary-size buffer.1273 const wide_slice = try w.GetFinalPathNameByHandle(h_file, .{}, &wide_buf);
1270 // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-20081274 var big_out_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
1271 // NULL out parameter (GNU's canonicalize_file_name) to handle overelong1275 const end_index = std.unicode.utf16leToUtf8(&big_out_buf, wide_slice) catch |e| switch (e) {
1272 // paths. musl supports passing NULL but restricts the output to PATH_MAX1276 // TODO: Windows file paths can be arbitrary arrays of u16 values and
1273 // anyway.1277 // must not fail with InvalidUtf8.
1274 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;1278 error.DanglingSurrogateHalf,
1275 const out_path = try posix.getFdPath(h_file, &buffer);1279 error.ExpectedSecondSurrogateHalf,
12761280 error.UnexpectedSecondSurrogateHalf,
1277 if (out_path.len > out_buffer.len) {1281 error.CodepointTooLarge,
1282 error.Utf8CannotEncodeSurrogateHalf,
1283 => return error.InvalidUtf8,
1284 };
1285 if (end_index > out_buffer.len)
1278 return error.NameTooLong;1286 return error.NameTooLong;
1279 }1287 const result = out_buffer[0..end_index];
12801288 @memcpy(result, big_out_buf[0..end_index]);
1281 const result = out_buffer[0..out_path.len];
1282 @memcpy(result, out_path);
1283 return result;1289 return result;
1284}1290}
12851291
1292pub const RealPathAllocError = RealPathError || Allocator.Error;
1293
1286/// Same as `Dir.realpath` except caller must free the returned memory.1294/// Same as `Dir.realpath` except caller must free the returned memory.
1287/// See also `Dir.realpath`.1295/// See also `Dir.realpath`.
1288pub fn realpathAlloc(self: Dir, allocator: Allocator, pathname: []const u8) ![]u8 {1296pub fn realpathAlloc(self: Dir, allocator: Allocator, pathname: []const u8) RealPathAllocError![]u8 {
1289 // Use of MAX_PATH_BYTES here is valid as the realpath function does not1297 // Use of MAX_PATH_BYTES here is valid as the realpath function does not
1290 // have a variant that takes an arbitrary-size buffer.1298 // have a variant that takes an arbitrary-size buffer.
1291 // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-20081299 // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008
lib/std/fs/File.zig+6
...@@ -48,6 +48,12 @@ pub const OpenError = error{...@@ -48,6 +48,12 @@ pub const OpenError = error{
48 Unexpected,48 Unexpected,
49 /// On Windows, `\\server` or `\\server\share` was not found.49 /// On Windows, `\\server` or `\\server\share` was not found.
50 NetworkNotFound,50 NetworkNotFound,
51 /// On Windows, antivirus software is enabled by default. It can be
52 /// disabled, but Windows Update sometimes ignores the user's preference
53 /// and re-enables it. When enabled, antivirus software on Windows
54 /// intercepts file system operations and makes them significantly slower
55 /// in addition to possibly failing with this error code.
56 AntivirusInterference,
51} || posix.OpenError || posix.FlockError;57} || posix.OpenError || posix.FlockError;
5258
53pub const OpenMode = enum {59pub const OpenMode = enum {
lib/std/os.zig+30-11
...@@ -2602,6 +2602,12 @@ pub const RenameError = error{...@@ -2602,6 +2602,12 @@ pub const RenameError = error{
2602 PipeBusy,2602 PipeBusy,
2603 /// On Windows, `\\server` or `\\server\share` was not found.2603 /// On Windows, `\\server` or `\\server\share` was not found.
2604 NetworkNotFound,2604 NetworkNotFound,
2605 /// On Windows, antivirus software is enabled by default. It can be
2606 /// disabled, but Windows Update sometimes ignores the user's preference
2607 /// and re-enables it. When enabled, antivirus software on Windows
2608 /// intercepts file system operations and makes them significantly slower
2609 /// in addition to possibly failing with this error code.
2610 AntivirusInterference,
2605} || UnexpectedError;2611} || UnexpectedError;
26062612
2607/// Change the name or location of a file.2613/// 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...@@ -2927,9 +2933,10 @@ pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!v
2927 .creation = windows.FILE_CREATE,2933 .creation = windows.FILE_CREATE,
2928 .filter = .dir_only,2934 .filter = .dir_only,
2929 }) catch |err| switch (err) {2935 }) catch |err| switch (err) {
2930 error.IsDir => unreachable,2936 error.IsDir => return error.Unexpected,
2931 error.PipeBusy => unreachable,2937 error.PipeBusy => return error.Unexpected,
2932 error.WouldBlock => unreachable,2938 error.WouldBlock => return error.Unexpected,
2939 error.AntivirusInterference => return error.Unexpected,
2933 else => |e| return e,2940 else => |e| return e,
2934 };2941 };
2935 windows.CloseHandle(sub_dir_handle);2942 windows.CloseHandle(sub_dir_handle);
...@@ -3006,9 +3013,10 @@ pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void {...@@ -3006,9 +3013,10 @@ pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void {
3006 .creation = windows.FILE_CREATE,3013 .creation = windows.FILE_CREATE,
3007 .filter = .dir_only,3014 .filter = .dir_only,
3008 }) catch |err| switch (err) {3015 }) catch |err| switch (err) {
3009 error.IsDir => unreachable,3016 error.IsDir => return error.Unexpected,
3010 error.PipeBusy => unreachable,3017 error.PipeBusy => return error.Unexpected,
3011 error.WouldBlock => unreachable,3018 error.WouldBlock => return error.Unexpected,
3019 error.AntivirusInterference => return error.Unexpected,
3012 else => |e| return e,3020 else => |e| return e,
3013 };3021 };
3014 windows.CloseHandle(sub_dir_handle);3022 windows.CloseHandle(sub_dir_handle);
...@@ -5347,6 +5355,13 @@ pub const RealPathError = error{...@@ -5347,6 +5355,13 @@ pub const RealPathError = error{
5347 NetworkNotFound,5355 NetworkNotFound,
53485356
5349 PathAlreadyExists,5357 PathAlreadyExists,
5358
5359 /// On Windows, antivirus software is enabled by default. It can be
5360 /// disabled, but Windows Update sometimes ignores the user's preference
5361 /// and re-enables it. When enabled, antivirus software on Windows
5362 /// intercepts file system operations and makes them significantly slower
5363 /// in addition to possibly failing with this error code.
5364 AntivirusInterference,
5350} || UnexpectedError;5365} || UnexpectedError;
53515366
5352/// Return the canonicalized absolute pathname.5367/// Return the canonicalized absolute pathname.
...@@ -5441,15 +5456,17 @@ pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPat...@@ -5441,15 +5456,17 @@ pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPat
54415456
5442pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool {5457pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool {
5443 return switch (os.tag) {5458 return switch (os.tag) {
5444 // zig fmt: off
5445 .windows,5459 .windows,
5446 .macos, .ios, .watchos, .tvos,5460 .macos,
5461 .ios,
5462 .watchos,
5463 .tvos,
5447 .linux,5464 .linux,
5448 .solaris,5465 .solaris,
5449 .illumos,5466 .illumos,
5450 .freebsd,5467 .freebsd,
5451 => true,5468 => true,
5452 // zig fmt: on5469
5453 .dragonfly => os.version_range.semver.max.order(.{ .major = 6, .minor = 0, .patch = 0 }) != .lt,5470 .dragonfly => os.version_range.semver.max.order(.{ .major = 6, .minor = 0, .patch = 0 }) != .lt,
5454 .netbsd => os.version_range.semver.max.order(.{ .major = 10, .minor = 0, .patch = 0 }) != .lt,5471 .netbsd => os.version_range.semver.max.order(.{ .major = 10, .minor = 0, .patch = 0 }) != .lt,
5455 else => false,5472 else => false,
...@@ -5469,8 +5486,10 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {...@@ -5469,8 +5486,10 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
5469 var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;5486 var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
5470 const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]);5487 const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]);
54715488
5472 // Trust that Windows gives us valid UTF-16LE.5489 // TODO: Windows file paths can be arbitrary arrays of u16 values
5473 const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch unreachable;5490 // and must not fail with InvalidUtf8.
5491 const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch
5492 return error.InvalidUtf8;
5474 return out_buffer[0..end_index];5493 return out_buffer[0..end_index];
5475 },5494 },
5476 .macos, .ios, .watchos, .tvos => {5495 .macos, .ios, .watchos, .tvos => {
lib/std/os/windows.zig+15-11
...@@ -41,6 +41,7 @@ pub const OpenError = error{...@@ -41,6 +41,7 @@ pub const OpenError = error{
41 NameTooLong,41 NameTooLong,
42 WouldBlock,42 WouldBlock,
43 NetworkNotFound,43 NetworkNotFound,
44 AntivirusInterference,
44};45};
4546
46pub const OpenFileOptions = struct {47pub const OpenFileOptions = struct {
...@@ -145,6 +146,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN...@@ -145,6 +146,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
145 std.time.sleep(std.time.ns_per_ms);146 std.time.sleep(std.time.ns_per_ms);
146 continue;147 continue;
147 },148 },
149 .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,
148 else => return unexpectedStatus(rc),150 else => return unexpectedStatus(rc),
149 }151 }
150 }152 }
...@@ -637,9 +639,10 @@ pub fn CreateSymbolicLink(...@@ -637,9 +639,10 @@ pub fn CreateSymbolicLink(
637 .filter = if (is_directory) .dir_only else .file_only,639 .filter = if (is_directory) .dir_only else .file_only,
638 }) catch |err| switch (err) {640 }) catch |err| switch (err) {
639 error.IsDir => return error.PathAlreadyExists,641 error.IsDir => return error.PathAlreadyExists,
640 error.NotDir => unreachable,642 error.NotDir => return error.Unexpected,
641 error.WouldBlock => unreachable,643 error.WouldBlock => return error.Unexpected,
642 error.PipeBusy => unreachable,644 error.PipeBusy => return error.Unexpected,
645 error.AntivirusInterference => return error.Unexpected,
643 else => |e| return e,646 else => |e| return e,
644 };647 };
645 defer CloseHandle(symlink_handle);648 defer CloseHandle(symlink_handle);
...@@ -1158,14 +1161,15 @@ pub fn GetFinalPathNameByHandle(...@@ -1158,14 +1161,15 @@ pub fn GetFinalPathNameByHandle(
1158 .share_access = FILE_SHARE_READ | FILE_SHARE_WRITE,1161 .share_access = FILE_SHARE_READ | FILE_SHARE_WRITE,
1159 .creation = FILE_OPEN,1162 .creation = FILE_OPEN,
1160 }) catch |err| switch (err) {1163 }) catch |err| switch (err) {
1161 error.IsDir => unreachable,1164 error.IsDir => return error.Unexpected,
1162 error.NotDir => unreachable,1165 error.NotDir => return error.Unexpected,
1163 error.NoDevice => unreachable,1166 error.NoDevice => return error.Unexpected,
1164 error.AccessDenied => unreachable,1167 error.AccessDenied => return error.Unexpected,
1165 error.PipeBusy => unreachable,1168 error.PipeBusy => return error.Unexpected,
1166 error.PathAlreadyExists => unreachable,1169 error.PathAlreadyExists => return error.Unexpected,
1167 error.WouldBlock => unreachable,1170 error.WouldBlock => return error.Unexpected,
1168 error.NetworkNotFound => unreachable,1171 error.NetworkNotFound => return error.Unexpected,
1172 error.AntivirusInterference => return error.Unexpected,
1169 else => |e| return e,1173 else => |e| return e,
1170 };1174 };
1171 defer CloseHandle(mgmt_handle);1175 defer CloseHandle(mgmt_handle);
lib/std/zig/system.zig+2
...@@ -766,6 +766,7 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {...@@ -766,6 +766,7 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
766 error.PipeBusy => unreachable, // Windows-only766 error.PipeBusy => unreachable, // Windows-only
767 error.SharingViolation => unreachable, // Windows-only767 error.SharingViolation => unreachable, // Windows-only
768 error.NetworkNotFound => unreachable, // Windows-only768 error.NetworkNotFound => unreachable, // Windows-only
769 error.AntivirusInterference => unreachable, // Windows-only
769 error.FileLocksNotSupported => unreachable, // No lock requested.770 error.FileLocksNotSupported => unreachable, // No lock requested.
770 error.NoSpaceLeft => unreachable, // read-only771 error.NoSpaceLeft => unreachable, // read-only
771 error.PathAlreadyExists => unreachable, // read-only772 error.PathAlreadyExists => unreachable, // read-only
...@@ -1003,6 +1004,7 @@ fn detectAbiAndDynamicLinker(...@@ -1003,6 +1004,7 @@ fn detectAbiAndDynamicLinker(
1003 error.FileLocksNotSupported => unreachable,1004 error.FileLocksNotSupported => unreachable,
1004 error.WouldBlock => unreachable,1005 error.WouldBlock => unreachable,
1005 error.FileBusy => unreachable, // opened without write permissions1006 error.FileBusy => unreachable, // opened without write permissions
1007 error.AntivirusInterference => unreachable, // Windows-only error
10061008
1007 error.IsDir,1009 error.IsDir,
1008 error.NotDir,1010 error.NotDir,