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 {
668668 .sa = &saAttr,
669669 .creation = windows.OPEN_EXISTING,
670670 }) catch |err| switch (err) {
671 error.PathAlreadyExists => unreachable, // not possible for "NUL"
672 error.PipeBusy => unreachable, // not possible for "NUL"
673 error.FileNotFound => unreachable, // not possible for "NUL"
674 error.AccessDenied => unreachable, // not possible for "NUL"
675 error.NameTooLong => unreachable, // not possible for "NUL"
676 error.WouldBlock => unreachable, // not possible for "NUL"
677 error.NetworkNotFound => unreachable, // not possible for "NUL"
671 error.PathAlreadyExists => return error.Unexpected, // not possible for "NUL"
672 error.PipeBusy => return error.Unexpected, // not possible for "NUL"
673 error.FileNotFound => return error.Unexpected, // not possible for "NUL"
674 error.AccessDenied => return error.Unexpected, // not possible for "NUL"
675 error.NameTooLong => return error.Unexpected, // not possible for "NUL"
676 error.WouldBlock => return error.Unexpected, // not possible for "NUL"
677 error.NetworkNotFound => return error.Unexpected, // not possible for "NUL"
678 error.AntivirusInterference => return error.Unexpected, // not possible for "NUL"
678679 else => |e| return e,
679680 }
680681 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
11791179 };
11801180}
11811181
1182pub const RealPathError = posix.RealPathError;
1183
11821184/// This function returns the canonicalized absolute pathname of
11831185/// `pathname` relative to this `Dir`. If `pathname` is absolute, ignores this
11841186/// `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
11861188/// This function is not universally supported by all platforms.
11871189/// Currently supported hosts are: Linux, macOS, and Windows.
11881190/// 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 {
11901192 if (builtin.os.tag == .wasi) {
11911193 @compileError("realpath is not available on WASI");
11921194 }
......@@ -1200,7 +1202,7 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) ![]u8 {
12001202
12011203/// Same as `Dir.realpath` except `pathname` is null-terminated.
12021204/// 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 {
12041206 if (builtin.os.tag == .windows) {
12051207 const pathname_w = try posix.windows.cStrToPrefixedFileW(self.fd, pathname);
12061208 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 {
12191221 };
12201222
12211223 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,
12231227 else => |e| return e,
12241228 };
12251229 defer posix.close(fd);
......@@ -1244,7 +1248,7 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) ![]u8 {
12441248
12451249/// Windows-only. Same as `Dir.realpath` except `pathname` is WTF16 encoded.
12461250/// 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 {
12481252 const w = std.os.windows;
12491253
12501254 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
......@@ -1265,27 +1269,31 @@ pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) ![]u8 {
12651269 };
12661270 defer w.CloseHandle(h_file);
12671271
1268 // Use of MAX_PATH_BYTES here is valid as the realpath function does not
1269 // have a variant that takes an arbitrary-size buffer.
1270 // TODO(#4812): Consider reimplementing realpath or using the POSIX.1-2008
1271 // NULL out parameter (GNU's canonicalize_file_name) to handle overelong
1272 // paths. musl supports passing NULL but restricts the output to PATH_MAX
1273 // anyway.
1274 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
1275 const out_path = try posix.getFdPath(h_file, &buffer);
1276
1277 if (out_path.len > out_buffer.len) {
1272 var wide_buf: [w.PATH_MAX_WIDE]u16 = undefined;
1273 const wide_slice = try w.GetFinalPathNameByHandle(h_file, .{}, &wide_buf);
1274 var big_out_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
1275 const end_index = std.unicode.utf16leToUtf8(&big_out_buf, wide_slice) catch |e| switch (e) {
1276 // TODO: Windows file paths can be arbitrary arrays of u16 values and
1277 // must not fail with InvalidUtf8.
1278 error.DanglingSurrogateHalf,
1279 error.ExpectedSecondSurrogateHalf,
1280 error.UnexpectedSecondSurrogateHalf,
1281 error.CodepointTooLarge,
1282 error.Utf8CannotEncodeSurrogateHalf,
1283 => return error.InvalidUtf8,
1284 };
1285 if (end_index > out_buffer.len)
12781286 return error.NameTooLong;
1279 }
1280
1281 const result = out_buffer[0..out_path.len];
1282 @memcpy(result, out_path);
1287 const result = out_buffer[0..end_index];
1288 @memcpy(result, big_out_buf[0..end_index]);
12831289 return result;
12841290}
12851291
1292pub const RealPathAllocError = RealPathError || Allocator.Error;
1293
12861294/// Same as `Dir.realpath` except caller must free the returned memory.
12871295/// 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 {
12891297 // Use of MAX_PATH_BYTES here is valid as the realpath function does not
12901298 // have a variant that takes an arbitrary-size buffer.
12911299 // 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{
4848 Unexpected,
4949 /// On Windows, `\\server` or `\\server\share` was not found.
5050 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,
5157} || posix.OpenError || posix.FlockError;
5258
5359pub const OpenMode = enum {
lib/std/os.zig+30-11
......@@ -2602,6 +2602,12 @@ pub const RenameError = error{
26022602 PipeBusy,
26032603 /// On Windows, `\\server` or `\\server\share` was not found.
26042604 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,
26052611} || UnexpectedError;
26062612
26072613/// 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
29272933 .creation = windows.FILE_CREATE,
29282934 .filter = .dir_only,
29292935 }) catch |err| switch (err) {
2930 error.IsDir => unreachable,
2931 error.PipeBusy => unreachable,
2932 error.WouldBlock => unreachable,
2936 error.IsDir => return error.Unexpected,
2937 error.PipeBusy => return error.Unexpected,
2938 error.WouldBlock => return error.Unexpected,
2939 error.AntivirusInterference => return error.Unexpected,
29332940 else => |e| return e,
29342941 };
29352942 windows.CloseHandle(sub_dir_handle);
......@@ -3006,9 +3013,10 @@ pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void {
30063013 .creation = windows.FILE_CREATE,
30073014 .filter = .dir_only,
30083015 }) catch |err| switch (err) {
3009 error.IsDir => unreachable,
3010 error.PipeBusy => unreachable,
3011 error.WouldBlock => unreachable,
3016 error.IsDir => return error.Unexpected,
3017 error.PipeBusy => return error.Unexpected,
3018 error.WouldBlock => return error.Unexpected,
3019 error.AntivirusInterference => return error.Unexpected,
30123020 else => |e| return e,
30133021 };
30143022 windows.CloseHandle(sub_dir_handle);
......@@ -5347,6 +5355,13 @@ pub const RealPathError = error{
53475355 NetworkNotFound,
53485356
53495357 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,
53505365} || UnexpectedError;
53515366
53525367/// Return the canonicalized absolute pathname.
......@@ -5441,15 +5456,17 @@ pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPat
54415456
54425457pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool {
54435458 return switch (os.tag) {
5444 // zig fmt: off
54455459 .windows,
5446 .macos, .ios, .watchos, .tvos,
5460 .macos,
5461 .ios,
5462 .watchos,
5463 .tvos,
54475464 .linux,
54485465 .solaris,
54495466 .illumos,
54505467 .freebsd,
54515468 => true,
5452 // zig fmt: on
5469
54535470 .dragonfly => os.version_range.semver.max.order(.{ .major = 6, .minor = 0, .patch = 0 }) != .lt,
54545471 .netbsd => os.version_range.semver.max.order(.{ .major = 10, .minor = 0, .patch = 0 }) != .lt,
54555472 else => false,
......@@ -5469,8 +5486,10 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
54695486 var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
54705487 const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]);
54715488
5472 // Trust that Windows gives us valid UTF-16LE.
5473 const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch unreachable;
5489 // TODO: Windows file paths can be arbitrary arrays of u16 values
5490 // and must not fail with InvalidUtf8.
5491 const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch
5492 return error.InvalidUtf8;
54745493 return out_buffer[0..end_index];
54755494 },
54765495 .macos, .ios, .watchos, .tvos => {
lib/std/os/windows.zig+15-11
......@@ -41,6 +41,7 @@ pub const OpenError = error{
4141 NameTooLong,
4242 WouldBlock,
4343 NetworkNotFound,
44 AntivirusInterference,
4445};
4546
4647pub const OpenFileOptions = struct {
......@@ -145,6 +146,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
145146 std.time.sleep(std.time.ns_per_ms);
146147 continue;
147148 },
149 .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,
148150 else => return unexpectedStatus(rc),
149151 }
150152 }
......@@ -637,9 +639,10 @@ pub fn CreateSymbolicLink(
637639 .filter = if (is_directory) .dir_only else .file_only,
638640 }) catch |err| switch (err) {
639641 error.IsDir => return error.PathAlreadyExists,
640 error.NotDir => unreachable,
641 error.WouldBlock => unreachable,
642 error.PipeBusy => unreachable,
642 error.NotDir => return error.Unexpected,
643 error.WouldBlock => return error.Unexpected,
644 error.PipeBusy => return error.Unexpected,
645 error.AntivirusInterference => return error.Unexpected,
643646 else => |e| return e,
644647 };
645648 defer CloseHandle(symlink_handle);
......@@ -1158,14 +1161,15 @@ pub fn GetFinalPathNameByHandle(
11581161 .share_access = FILE_SHARE_READ | FILE_SHARE_WRITE,
11591162 .creation = FILE_OPEN,
11601163 }) catch |err| switch (err) {
1161 error.IsDir => unreachable,
1162 error.NotDir => unreachable,
1163 error.NoDevice => unreachable,
1164 error.AccessDenied => unreachable,
1165 error.PipeBusy => unreachable,
1166 error.PathAlreadyExists => unreachable,
1167 error.WouldBlock => unreachable,
1168 error.NetworkNotFound => unreachable,
1164 error.IsDir => return error.Unexpected,
1165 error.NotDir => return error.Unexpected,
1166 error.NoDevice => return error.Unexpected,
1167 error.AccessDenied => return error.Unexpected,
1168 error.PipeBusy => return error.Unexpected,
1169 error.PathAlreadyExists => return error.Unexpected,
1170 error.WouldBlock => return error.Unexpected,
1171 error.NetworkNotFound => return error.Unexpected,
1172 error.AntivirusInterference => return error.Unexpected,
11691173 else => |e| return e,
11701174 };
11711175 defer CloseHandle(mgmt_handle);
lib/std/zig/system.zig+2
......@@ -766,6 +766,7 @@ fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion {
766766 error.PipeBusy => unreachable, // Windows-only
767767 error.SharingViolation => unreachable, // Windows-only
768768 error.NetworkNotFound => unreachable, // Windows-only
769 error.AntivirusInterference => unreachable, // Windows-only
769770 error.FileLocksNotSupported => unreachable, // No lock requested.
770771 error.NoSpaceLeft => unreachable, // read-only
771772 error.PathAlreadyExists => unreachable, // read-only
......@@ -1003,6 +1004,7 @@ fn detectAbiAndDynamicLinker(
10031004 error.FileLocksNotSupported => unreachable,
10041005 error.WouldBlock => unreachable,
10051006 error.FileBusy => unreachable, // opened without write permissions
1007 error.AntivirusInterference => unreachable, // Windows-only error
10061008
10071009 error.IsDir,
10081010 error.NotDir,