| ... | ... | @@ -2044,26 +2044,97 @@ fn dirOpenFileWindows( |
| 2044 | 2044 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 2045 | 2045 | const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path); |
| 2046 | 2046 | const sub_path_w = sub_path_w_array.span(); |
| 2047 | | return dirOpenFileWindowsInner(t, dir, sub_path_w, flags); |
| 2047 | const dir_handle = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle; |
| 2048 | return dirOpenFileWindowsInner(t, dir_handle, sub_path_w, flags); |
| 2048 | 2049 | } |
| 2049 | 2050 | |
| 2050 | 2051 | fn dirOpenFileWindowsInner( |
| 2051 | 2052 | t: *Threaded, |
| 2052 | | dir: Io.Dir, |
| 2053 | dir_handle: ?windows.HANDLE, |
| 2053 | 2054 | sub_path_w: [:0]const u16, |
| 2054 | 2055 | flags: Io.File.OpenFlags, |
| 2055 | 2056 | ) Io.File.OpenError!Io.File { |
| 2056 | | try t.checkCancel(); |
| 2057 | if (std.mem.eql(u16, sub_path_w, &.{'.'})) return error.IsDir; |
| 2058 | if (std.mem.eql(u16, sub_path_w, &.{ '.', '.' })) return error.IsDir; |
| 2059 | const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong; |
| 2060 | |
| 2057 | 2061 | const w = windows; |
| 2058 | | const handle = try w.OpenFile(sub_path_w, .{ |
| 2059 | | .dir = dir.handle, |
| 2060 | | .access_mask = w.SYNCHRONIZE | |
| 2061 | | (if (flags.isRead()) @as(u32, w.GENERIC_READ) else 0) | |
| 2062 | | (if (flags.isWrite()) @as(u32, w.GENERIC_WRITE) else 0), |
| 2063 | | .creation = w.FILE_OPEN, |
| 2064 | | }); |
| 2065 | | errdefer w.CloseHandle(handle); |
| 2062 | |
| 2063 | var nt_name: w.UNICODE_STRING = .{ |
| 2064 | .Length = path_len_bytes, |
| 2065 | .MaximumLength = path_len_bytes, |
| 2066 | .Buffer = @constCast(sub_path_w.ptr), |
| 2067 | }; |
| 2068 | var attr: w.OBJECT_ATTRIBUTES = .{ |
| 2069 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), |
| 2070 | .RootDirectory = dir_handle, |
| 2071 | .Attributes = 0, |
| 2072 | .ObjectName = &nt_name, |
| 2073 | .SecurityDescriptor = null, |
| 2074 | .SecurityQualityOfService = null, |
| 2075 | }; |
| 2066 | 2076 | var io_status_block: w.IO_STATUS_BLOCK = undefined; |
| 2077 | const blocking_flag: w.ULONG = w.FILE_SYNCHRONOUS_IO_NONALERT; |
| 2078 | const file_or_dir_flag: w.ULONG = w.FILE_NON_DIRECTORY_FILE; |
| 2079 | // If we're not following symlinks, we need to ensure we don't pass in any |
| 2080 | // synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT. |
| 2081 | const create_file_flags: w.ULONG = file_or_dir_flag | |
| 2082 | if (flags.follow_symlinks) blocking_flag else w.FILE_OPEN_REPARSE_POINT; |
| 2083 | |
| 2084 | const handle = while (true) { |
| 2085 | try t.checkCancel(); |
| 2086 | |
| 2087 | var result: w.HANDLE = undefined; |
| 2088 | const rc = w.ntdll.NtCreateFile( |
| 2089 | &result, |
| 2090 | w.SYNCHRONIZE | |
| 2091 | (if (flags.isRead()) @as(u32, w.GENERIC_READ) else 0) | |
| 2092 | (if (flags.isWrite()) @as(u32, w.GENERIC_WRITE) else 0), |
| 2093 | &attr, |
| 2094 | &io_status_block, |
| 2095 | null, |
| 2096 | w.FILE_ATTRIBUTE_NORMAL, |
| 2097 | w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, |
| 2098 | w.FILE_OPEN, |
| 2099 | create_file_flags, |
| 2100 | null, |
| 2101 | 0, |
| 2102 | ); |
| 2103 | switch (rc) { |
| 2104 | .SUCCESS => break result, |
| 2105 | .OBJECT_NAME_INVALID => return error.BadPathName, |
| 2106 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, |
| 2107 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, |
| 2108 | .BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found |
| 2109 | .BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't |
| 2110 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, |
| 2111 | .INVALID_PARAMETER => |err| return w.statusBug(err), |
| 2112 | .SHARING_VIOLATION => return error.AccessDenied, |
| 2113 | .ACCESS_DENIED => return error.AccessDenied, |
| 2114 | .PIPE_BUSY => return error.PipeBusy, |
| 2115 | .PIPE_NOT_AVAILABLE => return error.NoDevice, |
| 2116 | .OBJECT_PATH_SYNTAX_BAD => |err| return w.statusBug(err), |
| 2117 | .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, |
| 2118 | .FILE_IS_A_DIRECTORY => return error.IsDir, |
| 2119 | .NOT_A_DIRECTORY => return error.NotDir, |
| 2120 | .USER_MAPPED_FILE => return error.AccessDenied, |
| 2121 | .INVALID_HANDLE => |err| return w.statusBug(err), |
| 2122 | .DELETE_PENDING => { |
| 2123 | // This error means that there *was* a file in this location on |
| 2124 | // the file system, but it was deleted. However, the OS is not |
| 2125 | // finished with the deletion operation, and so this CreateFile |
| 2126 | // call has failed. There is not really a sane way to handle |
| 2127 | // this other than retrying the creation after the OS finishes |
| 2128 | // the deletion. |
| 2129 | _ = w.kernel32.SleepEx(1, w.FALSE); |
| 2130 | continue; |
| 2131 | }, |
| 2132 | .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference, |
| 2133 | else => return w.unexpectedStatus(rc), |
| 2134 | } |
| 2135 | }; |
| 2136 | errdefer w.CloseHandle(handle); |
| 2137 | |
| 2067 | 2138 | const range_off: w.LARGE_INTEGER = 0; |
| 2068 | 2139 | const range_len: w.LARGE_INTEGER = 1; |
| 2069 | 2140 | const exclusive = switch (flags.lock) { |
| ... | ... | @@ -2691,20 +2762,19 @@ fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekErr |
| 2691 | 2762 | |
| 2692 | 2763 | fn openSelfExe(userdata: ?*anyopaque, flags: Io.File.OpenFlags) Io.File.OpenSelfExeError!Io.File { |
| 2693 | 2764 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 2694 | | if (native_os == .linux or native_os == .serenity) { |
| 2695 | | return dirOpenFilePosix(t, .{ .handle = posix.AT.FDCWD }, "/proc/self/exe", flags); |
| 2696 | | } |
| 2697 | | if (is_windows) { |
| 2698 | | // If ImagePathName is a symlink, then it will contain the path of the symlink, |
| 2699 | | // not the path that the symlink points to. However, because we are opening |
| 2700 | | // the file, we can let the openFileW call follow the symlink for us. |
| 2701 | | const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName; |
| 2702 | | const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0]; |
| 2703 | | const cwd_handle = std.os.windows.peb().ProcessParameters.CurrentDirectory.Handle; |
| 2704 | | |
| 2705 | | return dirOpenFileWindowsInner(t, .{ .handle = cwd_handle }, image_path_name, flags); |
| 2765 | switch (native_os) { |
| 2766 | .linux, .serenity => return dirOpenFilePosix(t, .{ .handle = posix.AT.FDCWD }, "/proc/self/exe", flags), |
| 2767 | .windows => { |
| 2768 | // If ImagePathName is a symlink, then it will contain the path of the symlink, |
| 2769 | // not the path that the symlink points to. However, because we are opening |
| 2770 | // the file, we can let the openFileW call follow the symlink for us. |
| 2771 | const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName; |
| 2772 | const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0]; |
| 2773 | const prefixed_path_w = try windows.wToPrefixedFileW(null, image_path_name); |
| 2774 | return dirOpenFileWindowsInner(t, null, prefixed_path_w.span(), flags); |
| 2775 | }, |
| 2776 | else => @panic("TODO implement openSelfExe"), |
| 2706 | 2777 | } |
| 2707 | | @panic("TODO implement openSelfExe"); |
| 2708 | 2778 | } |
| 2709 | 2779 | |
| 2710 | 2780 | fn fileWritePositional( |
| ... | ... | @@ -2823,7 +2893,8 @@ fn sleepWindows(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { |
| 2823 | 2893 | break :ms std.math.maxInt(windows.DWORD); |
| 2824 | 2894 | break :ms std.math.lossyCast(windows.DWORD, d.raw.toMilliseconds()); |
| 2825 | 2895 | }; |
| 2826 | | windows.kernel32.Sleep(ms); |
| 2896 | // TODO: alertable true with checkCancel in a loop plus deadline |
| 2897 | _ = windows.kernel32.SleepEx(ms, windows.FALSE); |
| 2827 | 2898 | } |
| 2828 | 2899 | |
| 2829 | 2900 | fn sleepWasi(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { |