| author | |
| committer | |
| log | 8981b18fee9dc1db7faa8343d0d2151c3c0671fd |
| tree | 57fccb7375a3ef130b153ed5c12755d1f1d85de9 |
| parent | 66bbe4ec4c11839217d6a9d65771d60d45cd6bc1 |
This way, we can remove more `kernel32` calls such as `RemoveDirectoryW`
or `DeleteFileW`, and use `std.os.windows.DeleteFile` instead which
is purely NT-based.5 files changed, 115 insertions(+), 136 deletions(-)
lib/std/fs.zig+18-5| ... | ... | @@ -1117,7 +1117,7 @@ pub const Dir = struct { |
| 1117 | 1117 | pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void { |
| 1118 | 1118 | if (builtin.os.tag == .windows) { |
| 1119 | 1119 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 1120 | return self.deleteFileW(sub_path_w.span().ptr); | |
| 1120 | return self.deleteFileW(sub_path_w.span()); | |
| 1121 | 1121 | } else if (builtin.os.tag == .wasi) { |
| 1122 | 1122 | os.unlinkatWasi(self.fd, sub_path, 0) catch |err| switch (err) { |
| 1123 | 1123 | error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR |
| ... | ... | @@ -1151,7 +1151,7 @@ pub const Dir = struct { |
| 1151 | 1151 | } |
| 1152 | 1152 | |
| 1153 | 1153 | /// Same as `deleteFile` except the parameter is WTF-16 encoded. |
| 1154 | pub fn deleteFileW(self: Dir, sub_path_w: [*:0]const u16) DeleteFileError!void { | |
| 1154 | pub fn deleteFileW(self: Dir, sub_path_w: []const u16) DeleteFileError!void { | |
| 1155 | 1155 | os.unlinkatW(self.fd, sub_path_w, 0) catch |err| switch (err) { |
| 1156 | 1156 | error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR |
| 1157 | 1157 | else => |e| return e, |
| ... | ... | @@ -1180,7 +1180,7 @@ pub const Dir = struct { |
| 1180 | 1180 | pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void { |
| 1181 | 1181 | if (builtin.os.tag == .windows) { |
| 1182 | 1182 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 1183 | return self.deleteDirW(sub_path_w.span().ptr); | |
| 1183 | return self.deleteDirW(sub_path_w.span()); | |
| 1184 | 1184 | } else if (builtin.os.tag == .wasi) { |
| 1185 | 1185 | os.unlinkat(self.fd, sub_path, os.AT_REMOVEDIR) catch |err| switch (err) { |
| 1186 | 1186 | error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR |
| ... | ... | @@ -1202,7 +1202,7 @@ pub const Dir = struct { |
| 1202 | 1202 | |
| 1203 | 1203 | /// Same as `deleteDir` except the parameter is UTF16LE, NT prefixed. |
| 1204 | 1204 | /// This function is Windows-only. |
| 1205 | pub fn deleteDirW(self: Dir, sub_path_w: [*:0]const u16) DeleteDirError!void { | |
| 1205 | pub fn deleteDirW(self: Dir, sub_path_w: []const u16) DeleteDirError!void { | |
| 1206 | 1206 | os.unlinkatW(self.fd, sub_path_w, os.AT_REMOVEDIR) catch |err| switch (err) { |
| 1207 | 1207 | error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR |
| 1208 | 1208 | else => |e| return e, |
| ... | ... | @@ -1939,7 +1939,20 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { |
| 1939 | 1939 | return walker; |
| 1940 | 1940 | } |
| 1941 | 1941 | |
| 1942 | pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError || os.FlockError; | |
| 1942 | pub const OpenSelfExeError = error{ | |
| 1943 | SharingViolation, | |
| 1944 | PathAlreadyExists, | |
| 1945 | FileNotFound, | |
| 1946 | AccessDenied, | |
| 1947 | PipeBusy, | |
| 1948 | NameTooLong, | |
| 1949 | /// On Windows, file paths must be valid Unicode. | |
| 1950 | InvalidUtf8, | |
| 1951 | /// On Windows, file paths cannot contain these characters: | |
| 1952 | /// '/', '*', '?', '"', '<', '>', '|' | |
| 1953 | BadPathName, | |
| 1954 | Unexpected, | |
| 1955 | } || os.OpenError || SelfExePathError || os.FlockError; | |
| 1943 | 1956 | |
| 1944 | 1957 | pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File { |
| 1945 | 1958 | if (builtin.os.tag == .linux) { |
lib/std/fs/file.zig+14-1| ... | ... | @@ -47,7 +47,20 @@ pub const File = struct { |
| 47 | 47 | else => 0o666, |
| 48 | 48 | }; |
| 49 | 49 | |
| 50 | pub const OpenError = windows.CreateFileError || os.OpenError || os.FlockError; | |
| 50 | pub const OpenError = error{ | |
| 51 | SharingViolation, | |
| 52 | PathAlreadyExists, | |
| 53 | FileNotFound, | |
| 54 | AccessDenied, | |
| 55 | PipeBusy, | |
| 56 | NameTooLong, | |
| 57 | /// On Windows, file paths must be valid Unicode. | |
| 58 | InvalidUtf8, | |
| 59 | /// On Windows, file paths cannot contain these characters: | |
| 60 | /// '/', '*', '?', '"', '<', '>', '|' | |
| 61 | BadPathName, | |
| 62 | Unexpected, | |
| 63 | } || os.OpenError || os.FlockError; | |
| 51 | 64 | |
| 52 | 65 | pub const Lock = enum { None, Shared, Exclusive }; |
| 53 | 66 |
lib/std/fs/watch.zig+1-1| ... | ... | @@ -379,7 +379,7 @@ pub fn Watch(comptime V: type) type { |
| 379 | 379 | .access_mask = windows.FILE_LIST_DIRECTORY, |
| 380 | 380 | .creation = windows.FILE_OPEN, |
| 381 | 381 | .io_mode = .blocking, |
| 382 | .expect_dir = true, | |
| 382 | .open_dir = true, | |
| 383 | 383 | }); |
| 384 | 384 | var dir_handle_consumed = false; |
| 385 | 385 | defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle); |
lib/std/os.zig+22-67| ... | ... | @@ -1683,7 +1683,7 @@ pub fn unlink(file_path: []const u8) UnlinkError!void { |
| 1683 | 1683 | @compileError("unlink is not supported in WASI; use unlinkat instead"); |
| 1684 | 1684 | } else if (builtin.os.tag == .windows) { |
| 1685 | 1685 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); |
| 1686 | return windows.DeleteFileW(file_path_w.span().ptr); | |
| 1686 | return unlinkW(file_path_w.span()); | |
| 1687 | 1687 | } else { |
| 1688 | 1688 | const file_path_c = try toPosixPath(file_path); |
| 1689 | 1689 | return unlinkZ(&file_path_c); |
| ... | ... | @@ -1696,7 +1696,7 @@ pub const unlinkC = @compileError("deprecated: renamed to unlinkZ"); |
| 1696 | 1696 | pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void { |
| 1697 | 1697 | if (builtin.os.tag == .windows) { |
| 1698 | 1698 | const file_path_w = try windows.cStrToPrefixedFileW(file_path); |
| 1699 | return windows.DeleteFileW(file_path_w.span().ptr); | |
| 1699 | return unlinkW(file_path_w.span()); | |
| 1700 | 1700 | } |
| 1701 | 1701 | switch (errno(system.unlink(file_path))) { |
| 1702 | 1702 | 0 => return, |
| ... | ... | @@ -1717,6 +1717,11 @@ pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void { |
| 1717 | 1717 | } |
| 1718 | 1718 | } |
| 1719 | 1719 | |
| 1720 | /// Windows-only. Same as `unlink` except the parameter is null-terminated, WTF16 encoded. | |
| 1721 | pub fn unlinkW(file_path_w: []const u16) UnlinkError!void { | |
| 1722 | return windows.DeleteFile(file_path_w, .{ .dir = std.fs.cwd().fd }); | |
| 1723 | } | |
| 1724 | ||
| 1720 | 1725 | pub const UnlinkatError = UnlinkError || error{ |
| 1721 | 1726 | /// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty. |
| 1722 | 1727 | DirNotEmpty, |
| ... | ... | @@ -1727,7 +1732,7 @@ pub const UnlinkatError = UnlinkError || error{ |
| 1727 | 1732 | pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { |
| 1728 | 1733 | if (builtin.os.tag == .windows) { |
| 1729 | 1734 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); |
| 1730 | return unlinkatW(dirfd, file_path_w.span().ptr, flags); | |
| 1735 | return unlinkatW(dirfd, file_path_w.span(), flags); | |
| 1731 | 1736 | } else if (builtin.os.tag == .wasi) { |
| 1732 | 1737 | return unlinkatWasi(dirfd, file_path, flags); |
| 1733 | 1738 | } else { |
| ... | ... | @@ -1774,7 +1779,7 @@ pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatErro |
| 1774 | 1779 | pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void { |
| 1775 | 1780 | if (builtin.os.tag == .windows) { |
| 1776 | 1781 | const file_path_w = try windows.cStrToPrefixedFileW(file_path_c); |
| 1777 | return unlinkatW(dirfd, file_path_w.span().ptr, flags); | |
| 1782 | return unlinkatW(dirfd, file_path_w.span(), flags); | |
| 1778 | 1783 | } |
| 1779 | 1784 | switch (errno(system.unlinkat(dirfd, file_path_c, flags))) { |
| 1780 | 1785 | 0 => return, |
| ... | ... | @@ -1800,67 +1805,9 @@ pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatEr |
| 1800 | 1805 | } |
| 1801 | 1806 | |
| 1802 | 1807 | /// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only. |
| 1803 | pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*:0]const u16, flags: u32) UnlinkatError!void { | |
| 1804 | const w = windows; | |
| 1805 | ||
| 1806 | const want_rmdir_behavior = (flags & AT_REMOVEDIR) != 0; | |
| 1807 | const create_options_flags = if (want_rmdir_behavior) | |
| 1808 | @as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_DIRECTORY_FILE | w.FILE_OPEN_REPARSE_POINT) | |
| 1809 | else | |
| 1810 | @as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_NON_DIRECTORY_FILE | w.FILE_OPEN_REPARSE_POINT); // would we ever want to delete the target instead? | |
| 1811 | ||
| 1812 | const path_len_bytes = @intCast(u16, mem.lenZ(sub_path_w) * 2); | |
| 1813 | var nt_name = w.UNICODE_STRING{ | |
| 1814 | .Length = path_len_bytes, | |
| 1815 | .MaximumLength = path_len_bytes, | |
| 1816 | // The Windows API makes this mutable, but it will not mutate here. | |
| 1817 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), | |
| 1818 | }; | |
| 1819 | ||
| 1820 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { | |
| 1821 | // Windows does not recognize this, but it does work with empty string. | |
| 1822 | nt_name.Length = 0; | |
| 1823 | } | |
| 1824 | if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { | |
| 1825 | // Can't remove the parent directory with an open handle. | |
| 1826 | return error.FileBusy; | |
| 1827 | } | |
| 1828 | ||
| 1829 | var attr = w.OBJECT_ATTRIBUTES{ | |
| 1830 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | |
| 1831 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd, | |
| 1832 | .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. | |
| 1833 | .ObjectName = &nt_name, | |
| 1834 | .SecurityDescriptor = null, | |
| 1835 | .SecurityQualityOfService = null, | |
| 1836 | }; | |
| 1837 | var io: w.IO_STATUS_BLOCK = undefined; | |
| 1838 | var tmp_handle: w.HANDLE = undefined; | |
| 1839 | var rc = w.ntdll.NtCreateFile( | |
| 1840 | &tmp_handle, | |
| 1841 | w.SYNCHRONIZE | w.DELETE, | |
| 1842 | &attr, | |
| 1843 | &io, | |
| 1844 | null, | |
| 1845 | 0, | |
| 1846 | w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE, | |
| 1847 | w.FILE_OPEN, | |
| 1848 | create_options_flags, | |
| 1849 | null, | |
| 1850 | 0, | |
| 1851 | ); | |
| 1852 | if (rc == .SUCCESS) { | |
| 1853 | rc = w.ntdll.NtClose(tmp_handle); | |
| 1854 | } | |
| 1855 | switch (rc) { | |
| 1856 | .SUCCESS => return, | |
| 1857 | .OBJECT_NAME_INVALID => unreachable, | |
| 1858 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, | |
| 1859 | .INVALID_PARAMETER => unreachable, | |
| 1860 | .FILE_IS_A_DIRECTORY => return error.IsDir, | |
| 1861 | .NOT_A_DIRECTORY => return error.NotDir, | |
| 1862 | else => return w.unexpectedStatus(rc), | |
| 1863 | } | |
| 1808 | pub fn unlinkatW(dirfd: fd_t, sub_path_w: []const u16, flags: u32) UnlinkatError!void { | |
| 1809 | const remove_dir = (flags & AT_REMOVEDIR) != 0; | |
| 1810 | return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir }); | |
| 1864 | 1811 | } |
| 1865 | 1812 | |
| 1866 | 1813 | const RenameError = error{ |
| ... | ... | @@ -2256,7 +2203,7 @@ pub fn rmdir(dir_path: []const u8) DeleteDirError!void { |
| 2256 | 2203 | @compileError("rmdir is not supported in WASI; use unlinkat instead"); |
| 2257 | 2204 | } else if (builtin.os.tag == .windows) { |
| 2258 | 2205 | const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); |
| 2259 | return windows.RemoveDirectoryW(dir_path_w.span().ptr); | |
| 2206 | return rmdirW(dir_path_w.span()); | |
| 2260 | 2207 | } else { |
| 2261 | 2208 | const dir_path_c = try toPosixPath(dir_path); |
| 2262 | 2209 | return rmdirZ(&dir_path_c); |
| ... | ... | @@ -2269,7 +2216,7 @@ pub const rmdirC = @compileError("deprecated: renamed to rmdirZ"); |
| 2269 | 2216 | pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void { |
| 2270 | 2217 | if (builtin.os.tag == .windows) { |
| 2271 | 2218 | const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); |
| 2272 | return windows.RemoveDirectoryW(dir_path_w.span().ptr); | |
| 2219 | return rmdirW(dir_path_w.span()); | |
| 2273 | 2220 | } |
| 2274 | 2221 | switch (errno(system.rmdir(dir_path))) { |
| 2275 | 2222 | 0 => return, |
| ... | ... | @@ -2290,6 +2237,14 @@ pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void { |
| 2290 | 2237 | } |
| 2291 | 2238 | } |
| 2292 | 2239 | |
| 2240 | /// Windows-only. Same as `rmdir` except the parameter is null-terminated, WTF16 encoded. | |
| 2241 | pub fn rmdirW(dir_path_w: []const u16) DeleteDirError!void { | |
| 2242 | return windows.DeleteFile(dir_path_w, .{ .dir = std.fs.cwd().fd, .remove_dir = true }) catch |err| switch (err) { | |
| 2243 | error.IsDir => unreachable, | |
| 2244 | else => |e| return e, | |
| 2245 | }; | |
| 2246 | } | |
| 2247 | ||
| 2293 | 2248 | pub const ChangeCurDirError = error{ |
| 2294 | 2249 | AccessDenied, |
| 2295 | 2250 | FileSystem, |
lib/std/os/windows.zig+60-62| ... | ... | @@ -25,30 +25,6 @@ pub usingnamespace @import("windows/bits.zig"); |
| 25 | 25 | |
| 26 | 26 | pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize)); |
| 27 | 27 | |
| 28 | pub const CreateFileError = error{ | |
| 29 | SharingViolation, | |
| 30 | PathAlreadyExists, | |
| 31 | ||
| 32 | /// When any of the path components can not be found or the file component can not | |
| 33 | /// be found. Some operating systems distinguish between path components not found and | |
| 34 | /// file components not found, but they are collapsed into FileNotFound to gain | |
| 35 | /// consistency across operating systems. | |
| 36 | FileNotFound, | |
| 37 | ||
| 38 | AccessDenied, | |
| 39 | PipeBusy, | |
| 40 | NameTooLong, | |
| 41 | ||
| 42 | /// On Windows, file paths must be valid Unicode. | |
| 43 | InvalidUtf8, | |
| 44 | ||
| 45 | /// On Windows, file paths cannot contain these characters: | |
| 46 | /// '/', '*', '?', '"', '<', '>', '|' | |
| 47 | BadPathName, | |
| 48 | ||
| 49 | Unexpected, | |
| 50 | }; | |
| 51 | ||
| 52 | 28 | pub const OpenError = error{ |
| 53 | 29 | IsDir, |
| 54 | 30 | FileNotFound, |
| ... | ... | @@ -729,24 +705,69 @@ pub const DeleteFileError = error{ |
| 729 | 705 | NameTooLong, |
| 730 | 706 | FileBusy, |
| 731 | 707 | Unexpected, |
| 708 | NotDir, | |
| 709 | IsDir, | |
| 732 | 710 | }; |
| 733 | 711 | |
| 734 | pub fn DeleteFile(filename: []const u8) DeleteFileError!void { | |
| 735 | const filename_w = try sliceToPrefixedFileW(filename); | |
| 736 | return DeleteFileW(filename_w.span().ptr); | |
| 737 | } | |
| 712 | pub const DeleteFileOptions = struct { | |
| 713 | dir: ?HANDLE, | |
| 714 | remove_dir: bool = false, | |
| 715 | }; | |
| 738 | 716 | |
| 739 | pub fn DeleteFileW(filename: [*:0]const u16) DeleteFileError!void { | |
| 740 | if (kernel32.DeleteFileW(filename) == 0) { | |
| 741 | switch (kernel32.GetLastError()) { | |
| 742 | .FILE_NOT_FOUND => return error.FileNotFound, | |
| 743 | .PATH_NOT_FOUND => return error.FileNotFound, | |
| 744 | .ACCESS_DENIED => return error.AccessDenied, | |
| 745 | .FILENAME_EXCED_RANGE => return error.NameTooLong, | |
| 746 | .INVALID_PARAMETER => return error.NameTooLong, | |
| 747 | .SHARING_VIOLATION => return error.FileBusy, | |
| 748 | else => |err| return unexpectedError(err), | |
| 749 | } | |
| 717 | pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFileError!void { | |
| 718 | const create_options_flags: ULONG = if (options.remove_dir) | |
| 719 | FILE_DELETE_ON_CLOSE | FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT | |
| 720 | else | |
| 721 | FILE_DELETE_ON_CLOSE | FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT; // would we ever want to delete the target instead? | |
| 722 | ||
| 723 | const path_len_bytes = @intCast(u16, sub_path_w.len * 2); | |
| 724 | var nt_name = UNICODE_STRING{ | |
| 725 | .Length = path_len_bytes, | |
| 726 | .MaximumLength = path_len_bytes, | |
| 727 | // The Windows API makes this mutable, but it will not mutate here. | |
| 728 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)), | |
| 729 | }; | |
| 730 | ||
| 731 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { | |
| 732 | // Windows does not recognize this, but it does work with empty string. | |
| 733 | nt_name.Length = 0; | |
| 734 | } | |
| 735 | if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { | |
| 736 | // Can't remove the parent directory with an open handle. | |
| 737 | return error.FileBusy; | |
| 738 | } | |
| 739 | ||
| 740 | var attr = OBJECT_ATTRIBUTES{ | |
| 741 | .Length = @sizeOf(OBJECT_ATTRIBUTES), | |
| 742 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else options.dir, | |
| 743 | .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. | |
| 744 | .ObjectName = &nt_name, | |
| 745 | .SecurityDescriptor = null, | |
| 746 | .SecurityQualityOfService = null, | |
| 747 | }; | |
| 748 | var io: IO_STATUS_BLOCK = undefined; | |
| 749 | var tmp_handle: HANDLE = undefined; | |
| 750 | var rc = ntdll.NtCreateFile( | |
| 751 | &tmp_handle, | |
| 752 | SYNCHRONIZE | DELETE, | |
| 753 | &attr, | |
| 754 | &io, | |
| 755 | null, | |
| 756 | 0, | |
| 757 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
| 758 | FILE_OPEN, | |
| 759 | create_options_flags, | |
| 760 | null, | |
| 761 | 0, | |
| 762 | ); | |
| 763 | switch (rc) { | |
| 764 | .SUCCESS => return CloseHandle(tmp_handle), | |
| 765 | .OBJECT_NAME_INVALID => unreachable, | |
| 766 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, | |
| 767 | .INVALID_PARAMETER => unreachable, | |
| 768 | .FILE_IS_A_DIRECTORY => return error.IsDir, | |
| 769 | .NOT_A_DIRECTORY => return error.NotDir, | |
| 770 | else => return unexpectedStatus(rc), | |
| 750 | 771 | } |
| 751 | 772 | } |
| 752 | 773 | |
| ... | ... | @@ -766,29 +787,6 @@ pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DW |
| 766 | 787 | } |
| 767 | 788 | } |
| 768 | 789 | |
| 769 | pub const RemoveDirectoryError = error{ | |
| 770 | FileNotFound, | |
| 771 | DirNotEmpty, | |
| 772 | Unexpected, | |
| 773 | NotDir, | |
| 774 | }; | |
| 775 | ||
| 776 | pub fn RemoveDirectory(dir_path: []const u8) RemoveDirectoryError!void { | |
| 777 | const dir_path_w = try sliceToPrefixedFileW(dir_path); | |
| 778 | return RemoveDirectoryW(dir_path_w.span().ptr); | |
| 779 | } | |
| 780 | ||
| 781 | pub fn RemoveDirectoryW(dir_path_w: [*:0]const u16) RemoveDirectoryError!void { | |
| 782 | if (kernel32.RemoveDirectoryW(dir_path_w) == 0) { | |
| 783 | switch (kernel32.GetLastError()) { | |
| 784 | .PATH_NOT_FOUND => return error.FileNotFound, | |
| 785 | .DIR_NOT_EMPTY => return error.DirNotEmpty, | |
| 786 | .DIRECTORY => return error.NotDir, | |
| 787 | else => |err| return unexpectedError(err), | |
| 788 | } | |
| 789 | } | |
| 790 | } | |
| 791 | ||
| 792 | 790 | pub const GetStdHandleError = error{ |
| 793 | 791 | NoStandardHandleAttached, |
| 794 | 792 | Unexpected, |