authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-07-30 22:47:30+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-07-31 16:31:51+02:00
log8981b18fee9dc1db7faa8343d0d2151c3c0671fd
tree57fccb7375a3ef130b153ed5c12755d1f1d85de9
parent66bbe4ec4c11839217d6a9d65771d60d45cd6bc1

Move delete file logic into windows.DeleteFile fn

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,7 +1117,7 @@ pub const Dir = struct {
1117 pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {1117 pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
1118 if (builtin.os.tag == .windows) {1118 if (builtin.os.tag == .windows) {
1119 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);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 } else if (builtin.os.tag == .wasi) {1121 } else if (builtin.os.tag == .wasi) {
1122 os.unlinkatWasi(self.fd, sub_path, 0) catch |err| switch (err) {1122 os.unlinkatWasi(self.fd, sub_path, 0) catch |err| switch (err) {
1123 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR1123 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
...@@ -1151,7 +1151,7 @@ pub const Dir = struct {...@@ -1151,7 +1151,7 @@ pub const Dir = struct {
1151 }1151 }
11521152
1153 /// Same as `deleteFile` except the parameter is WTF-16 encoded.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 os.unlinkatW(self.fd, sub_path_w, 0) catch |err| switch (err) {1155 os.unlinkatW(self.fd, sub_path_w, 0) catch |err| switch (err) {
1156 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR1156 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
1157 else => |e| return e,1157 else => |e| return e,
...@@ -1180,7 +1180,7 @@ pub const Dir = struct {...@@ -1180,7 +1180,7 @@ pub const Dir = struct {
1180 pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {1180 pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
1181 if (builtin.os.tag == .windows) {1181 if (builtin.os.tag == .windows) {
1182 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);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 } else if (builtin.os.tag == .wasi) {1184 } else if (builtin.os.tag == .wasi) {
1185 os.unlinkat(self.fd, sub_path, os.AT_REMOVEDIR) catch |err| switch (err) {1185 os.unlinkat(self.fd, sub_path, os.AT_REMOVEDIR) catch |err| switch (err) {
1186 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR1186 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
...@@ -1202,7 +1202,7 @@ pub const Dir = struct {...@@ -1202,7 +1202,7 @@ pub const Dir = struct {
12021202
1203 /// Same as `deleteDir` except the parameter is UTF16LE, NT prefixed.1203 /// Same as `deleteDir` except the parameter is UTF16LE, NT prefixed.
1204 /// This function is Windows-only.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 os.unlinkatW(self.fd, sub_path_w, os.AT_REMOVEDIR) catch |err| switch (err) {1206 os.unlinkatW(self.fd, sub_path_w, os.AT_REMOVEDIR) catch |err| switch (err) {
1207 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR1207 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
1208 else => |e| return e,1208 else => |e| return e,
...@@ -1939,7 +1939,20 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {...@@ -1939,7 +1939,20 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
1939 return walker;1939 return walker;
1940}1940}
19411941
1942pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError || os.FlockError;1942pub 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;
19431956
1944pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {1957pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {
1945 if (builtin.os.tag == .linux) {1958 if (builtin.os.tag == .linux) {
lib/std/fs/file.zig+14-1
...@@ -47,7 +47,20 @@ pub const File = struct {...@@ -47,7 +47,20 @@ pub const File = struct {
47 else => 0o666,47 else => 0o666,
48 };48 };
4949
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;
5164
52 pub const Lock = enum { None, Shared, Exclusive };65 pub const Lock = enum { None, Shared, Exclusive };
5366
lib/std/fs/watch.zig+1-1
...@@ -379,7 +379,7 @@ pub fn Watch(comptime V: type) type {...@@ -379,7 +379,7 @@ pub fn Watch(comptime V: type) type {
379 .access_mask = windows.FILE_LIST_DIRECTORY,379 .access_mask = windows.FILE_LIST_DIRECTORY,
380 .creation = windows.FILE_OPEN,380 .creation = windows.FILE_OPEN,
381 .io_mode = .blocking,381 .io_mode = .blocking,
382 .expect_dir = true,382 .open_dir = true,
383 });383 });
384 var dir_handle_consumed = false;384 var dir_handle_consumed = false;
385 defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle);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,7 +1683,7 @@ pub fn unlink(file_path: []const u8) UnlinkError!void {
1683 @compileError("unlink is not supported in WASI; use unlinkat instead");1683 @compileError("unlink is not supported in WASI; use unlinkat instead");
1684 } else if (builtin.os.tag == .windows) {1684 } else if (builtin.os.tag == .windows) {
1685 const file_path_w = try windows.sliceToPrefixedFileW(file_path);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 } else {1687 } else {
1688 const file_path_c = try toPosixPath(file_path);1688 const file_path_c = try toPosixPath(file_path);
1689 return unlinkZ(&file_path_c);1689 return unlinkZ(&file_path_c);
...@@ -1696,7 +1696,7 @@ pub const unlinkC = @compileError("deprecated: renamed to unlinkZ");...@@ -1696,7 +1696,7 @@ pub const unlinkC = @compileError("deprecated: renamed to unlinkZ");
1696pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {1696pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {
1697 if (builtin.os.tag == .windows) {1697 if (builtin.os.tag == .windows) {
1698 const file_path_w = try windows.cStrToPrefixedFileW(file_path);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 switch (errno(system.unlink(file_path))) {1701 switch (errno(system.unlink(file_path))) {
1702 0 => return,1702 0 => return,
...@@ -1717,6 +1717,11 @@ pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {...@@ -1717,6 +1717,11 @@ pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {
1717 }1717 }
1718}1718}
17191719
1720/// Windows-only. Same as `unlink` except the parameter is null-terminated, WTF16 encoded.
1721pub fn unlinkW(file_path_w: []const u16) UnlinkError!void {
1722 return windows.DeleteFile(file_path_w, .{ .dir = std.fs.cwd().fd });
1723}
1724
1720pub const UnlinkatError = UnlinkError || error{1725pub const UnlinkatError = UnlinkError || error{
1721 /// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty.1726 /// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty.
1722 DirNotEmpty,1727 DirNotEmpty,
...@@ -1727,7 +1732,7 @@ pub const UnlinkatError = UnlinkError || error{...@@ -1727,7 +1732,7 @@ pub const UnlinkatError = UnlinkError || error{
1727pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {1732pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
1728 if (builtin.os.tag == .windows) {1733 if (builtin.os.tag == .windows) {
1729 const file_path_w = try windows.sliceToPrefixedFileW(file_path);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 } else if (builtin.os.tag == .wasi) {1736 } else if (builtin.os.tag == .wasi) {
1732 return unlinkatWasi(dirfd, file_path, flags);1737 return unlinkatWasi(dirfd, file_path, flags);
1733 } else {1738 } else {
...@@ -1774,7 +1779,7 @@ pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatErro...@@ -1774,7 +1779,7 @@ pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatErro
1774pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void {1779pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void {
1775 if (builtin.os.tag == .windows) {1780 if (builtin.os.tag == .windows) {
1776 const file_path_w = try windows.cStrToPrefixedFileW(file_path_c);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 switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {1784 switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {
1780 0 => return,1785 0 => return,
...@@ -1800,67 +1805,9 @@ pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatEr...@@ -1800,67 +1805,9 @@ pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatEr
1800}1805}
18011806
1802/// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only.1807/// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only.
1803pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*:0]const u16, flags: u32) UnlinkatError!void {1808pub fn unlinkatW(dirfd: fd_t, sub_path_w: []const u16, flags: u32) UnlinkatError!void {
1804 const w = windows;1809 const remove_dir = (flags & AT_REMOVEDIR) != 0;
18051810 return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir });
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 }
1864}1811}
18651812
1866const RenameError = error{1813const RenameError = error{
...@@ -2256,7 +2203,7 @@ pub fn rmdir(dir_path: []const u8) DeleteDirError!void {...@@ -2256,7 +2203,7 @@ pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
2256 @compileError("rmdir is not supported in WASI; use unlinkat instead");2203 @compileError("rmdir is not supported in WASI; use unlinkat instead");
2257 } else if (builtin.os.tag == .windows) {2204 } else if (builtin.os.tag == .windows) {
2258 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);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 } else {2207 } else {
2261 const dir_path_c = try toPosixPath(dir_path);2208 const dir_path_c = try toPosixPath(dir_path);
2262 return rmdirZ(&dir_path_c);2209 return rmdirZ(&dir_path_c);
...@@ -2269,7 +2216,7 @@ pub const rmdirC = @compileError("deprecated: renamed to rmdirZ");...@@ -2269,7 +2216,7 @@ pub const rmdirC = @compileError("deprecated: renamed to rmdirZ");
2269pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {2216pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {
2270 if (builtin.os.tag == .windows) {2217 if (builtin.os.tag == .windows) {
2271 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);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 switch (errno(system.rmdir(dir_path))) {2221 switch (errno(system.rmdir(dir_path))) {
2275 0 => return,2222 0 => return,
...@@ -2290,6 +2237,14 @@ pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {...@@ -2290,6 +2237,14 @@ pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {
2290 }2237 }
2291}2238}
22922239
2240/// Windows-only. Same as `rmdir` except the parameter is null-terminated, WTF16 encoded.
2241pub 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
2293pub const ChangeCurDirError = error{2248pub const ChangeCurDirError = error{
2294 AccessDenied,2249 AccessDenied,
2295 FileSystem,2250 FileSystem,
lib/std/os/windows.zig+60-62
...@@ -25,30 +25,6 @@ pub usingnamespace @import("windows/bits.zig");...@@ -25,30 +25,6 @@ pub usingnamespace @import("windows/bits.zig");
2525
26pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));26pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));
2727
28pub 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
52pub const OpenError = error{28pub const OpenError = error{
53 IsDir,29 IsDir,
54 FileNotFound,30 FileNotFound,
...@@ -729,24 +705,69 @@ pub const DeleteFileError = error{...@@ -729,24 +705,69 @@ pub const DeleteFileError = error{
729 NameTooLong,705 NameTooLong,
730 FileBusy,706 FileBusy,
731 Unexpected,707 Unexpected,
708 NotDir,
709 IsDir,
732};710};
733711
734pub fn DeleteFile(filename: []const u8) DeleteFileError!void {712pub const DeleteFileOptions = struct {
735 const filename_w = try sliceToPrefixedFileW(filename);713 dir: ?HANDLE,
736 return DeleteFileW(filename_w.span().ptr);714 remove_dir: bool = false,
737}715};
738716
739pub fn DeleteFileW(filename: [*:0]const u16) DeleteFileError!void {717pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFileError!void {
740 if (kernel32.DeleteFileW(filename) == 0) {718 const create_options_flags: ULONG = if (options.remove_dir)
741 switch (kernel32.GetLastError()) {719 FILE_DELETE_ON_CLOSE | FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT
742 .FILE_NOT_FOUND => return error.FileNotFound,720 else
743 .PATH_NOT_FOUND => return error.FileNotFound,721 FILE_DELETE_ON_CLOSE | FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT; // would we ever want to delete the target instead?
744 .ACCESS_DENIED => return error.AccessDenied,722
745 .FILENAME_EXCED_RANGE => return error.NameTooLong,723 const path_len_bytes = @intCast(u16, sub_path_w.len * 2);
746 .INVALID_PARAMETER => return error.NameTooLong,724 var nt_name = UNICODE_STRING{
747 .SHARING_VIOLATION => return error.FileBusy,725 .Length = path_len_bytes,
748 else => |err| return unexpectedError(err),726 .MaximumLength = path_len_bytes,
749 }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}
752773
...@@ -766,29 +787,6 @@ pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DW...@@ -766,29 +787,6 @@ pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DW
766 }787 }
767}788}
768789
769pub const RemoveDirectoryError = error{
770 FileNotFound,
771 DirNotEmpty,
772 Unexpected,
773 NotDir,
774};
775
776pub 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
781pub 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
792pub const GetStdHandleError = error{790pub const GetStdHandleError = error{
793 NoStandardHandleAttached,791 NoStandardHandleAttached,
794 Unexpected,792 Unexpected,