diff --git a/lib/std/Build/Watch.zig b/lib/std/Build/Watch.zig index 947468de549f..e11198d42644 100644 --- a/lib/std/Build/Watch.zig +++ b/lib/std/Build/Watch.zig @@ -340,19 +340,55 @@ const Os = switch (builtin.os.tag) { const reaction_set = rs: { const gop = try w.dir_table.getOrPut(gpa, path); if (!gop.found_existing) { - var realpath_buf: [std.fs.max_path_bytes]u8 = undefined; - const realpath = try path.root_dir.handle.realpath(path.sub_path, &realpath_buf); - const realpath_w = try windows.sliceToPrefixedFileW(null, realpath); - const dir_handle = windows.kernel32.CreateFileW( - realpath_w.span().ptr, - windows.GENERIC_READ, - windows.FILE_SHARE_DELETE | windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE, + // The following code is a drawn out NtCreateFile call. (mostly adapted from std.fs.Dir.makeOpenDirAccessMaskW) + // It's necessary in order to get the flags are required when calling ReadDirectoryChangesW. + const root_fd = path.root_dir.handle.fd; + const sub_path = path.subPathOrDot(); + const sub_path_w = try windows.sliceToPrefixedFileW(root_fd, sub_path); + const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong; + + var nt_name = windows.UNICODE_STRING{ + .Length = @intCast(path_len_bytes), + .MaximumLength = @intCast(path_len_bytes), + .Buffer = @constCast(sub_path_w.span().ptr), + }; + var attr = windows.OBJECT_ATTRIBUTES{ + .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), + .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w.span())) null else root_fd, + .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. + .ObjectName = &nt_name, + .SecurityDescriptor = null, + .SecurityQualityOfService = null, + }; + var io: windows.IO_STATUS_BLOCK = undefined; + var dir_handle: windows.HANDLE = undefined; + const rc = windows.ntdll.NtCreateFile( + &dir_handle, + windows.SYNCHRONIZE | windows.GENERIC_READ | windows.FILE_LIST_DIRECTORY, + &attr, + &io, null, - windows.OPEN_EXISTING, - windows.FILE_FLAG_BACKUP_SEMANTICS | windows.FILE_FLAG_OVERLAPPED, + 0, + windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_DELETE, + windows.FILE_OPEN, + windows.FILE_DIRECTORY_FILE | windows.FILE_OPEN_FOR_BACKUP_INTENT, null, + 0, ); + switch (rc) { + .SUCCESS => {}, + .OBJECT_NAME_INVALID => return error.BadPathName, + .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, + .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, + .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, + .NOT_A_DIRECTORY => return error.NotDir, + // This can happen if the directory has 'List folder contents' permission set to 'Deny' + .ACCESS_DENIED => return error.AccessDenied, + .INVALID_PARAMETER => unreachable, + else => return windows.unexpectedStatus(rc), + } + assert(dir_handle != windows.INVALID_HANDLE_VALUE); // `dir_handle` may already be present in the table in