authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-31 16:35:01-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-02 16:58:05-08:00
logd3f87f8ac01039722197a13a12342fc747a90567
tree226dfc7c6e61533b34a083f810b3e1429390e679
parentb4d6e85a339e971829404dc1a260bde4062735f8

std.fs.rename: fix Windows implementation

The semantics of this function are that it moves both files and directories. Previously we had this `is_dir` boolean field of `std.os.windows.OpenFile` which required the API user to choose: are we opening a file or directory? And the other kind would either cause error.IsDir or error.NotDir. But that is not a limitation of the Windows file system API; it was self-imposed. On Windows, rename is implemented internally with `NtCreateFile` so we need to allow it to open either files or directories. This is now done by `std.os.windows.OpenFile` accepting enum{file_only,dir_only,any} instead of a boolean.

4 files changed, 26 insertions(+), 12 deletions(-)

lib/std/fs.zig+1-1
......@@ -1361,7 +1361,7 @@ pub const Dir = struct {
13611361 .share_access = share_access,
13621362 .creation = creation,
13631363 .io_mode = .blocking,
1364 .open_dir = true,
1364 .filter = .dir_only,
13651365 }) catch |er| switch (er) {
13661366 error.WouldBlock => unreachable,
13671367 else => |e2| return e2,
lib/std/fs/watch.zig+1-1
......@@ -401,7 +401,7 @@ pub fn Watch(comptime V: type) type {
401401 .access_mask = windows.FILE_LIST_DIRECTORY,
402402 .creation = windows.FILE_OPEN,
403403 .io_mode = .evented,
404 .open_dir = true,
404 .filter = .dir_only,
405405 });
406406 errdefer windows.CloseHandle(dir_handle);
407407
lib/std/os.zig+6-5
......@@ -1353,7 +1353,7 @@ fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions {
13531353 access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
13541354 }
13551355
1356 const open_dir: bool = flags & O.DIRECTORY != 0;
1356 const filter: windows.OpenFileOptions.Filter = if (flags & O.DIRECTORY != 0) .dir_only else .file_only;
13571357 const follow_symlinks: bool = flags & O.NOFOLLOW == 0;
13581358
13591359 const creation: w.ULONG = blk: {
......@@ -1369,7 +1369,7 @@ fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions {
13691369 .access_mask = access_mask,
13701370 .io_mode = .blocking,
13711371 .creation = creation,
1372 .open_dir = open_dir,
1372 .filter = filter,
13731373 .follow_symlinks = follow_symlinks,
13741374 };
13751375}
......@@ -2324,6 +2324,7 @@ pub fn renameatW(
23242324 .access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE,
23252325 .creation = windows.FILE_OPEN,
23262326 .io_mode = .blocking,
2327 .filter = .any, // This function is supposed to rename both files and directories.
23272328 }) catch |err| switch (err) {
23282329 error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`.
23292330 else => |e| return e,
......@@ -2435,7 +2436,7 @@ pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!v
24352436 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
24362437 .creation = windows.FILE_CREATE,
24372438 .io_mode = .blocking,
2438 .open_dir = true,
2439 .filter = .dir_only,
24392440 }) catch |err| switch (err) {
24402441 error.IsDir => unreachable,
24412442 error.PipeBusy => unreachable,
......@@ -2511,7 +2512,7 @@ pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void {
25112512 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
25122513 .creation = windows.FILE_CREATE,
25132514 .io_mode = .blocking,
2514 .open_dir = true,
2515 .filter = .dir_only,
25152516 }) catch |err| switch (err) {
25162517 error.IsDir => unreachable,
25172518 error.PipeBusy => unreachable,
......@@ -4693,7 +4694,7 @@ pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPat
46934694 .share_access = share_access,
46944695 .creation = creation,
46954696 .io_mode = .blocking,
4696 .open_dir = true,
4697 .filter = .dir_only,
46974698 }) catch |er| switch (er) {
46984699 error.WouldBlock => unreachable,
46994700 else => |e2| return e2,
lib/std/os/windows.zig+18-5
......@@ -53,17 +53,26 @@ pub const OpenFileOptions = struct {
5353 io_mode: std.io.ModeOverride,
5454 /// If true, tries to open path as a directory.
5555 /// Defaults to false.
56 open_dir: bool = false,
56 filter: Filter = .file_only,
5757 /// If false, tries to open path as a reparse point without dereferencing it.
5858 /// Defaults to true.
5959 follow_symlinks: bool = true,
60
61 pub const Filter = enum {
62 /// Causes `OpenFile` to return `error.IsDir` if the opened handle would be a directory.
63 file_only,
64 /// Causes `OpenFile` to return `error.NotDir` if the opened handle would be a file.
65 dir_only,
66 /// `OpenFile` does not discriminate between opening files and directories.
67 any,
68 };
6069};
6170
6271pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {
63 if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and !options.open_dir) {
72 if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and options.filter == .file_only) {
6473 return error.IsDir;
6574 }
66 if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and !options.open_dir) {
75 if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and options.filter == .file_only) {
6776 return error.IsDir;
6877 }
6978
......@@ -87,7 +96,11 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
8796 };
8897 var io: IO_STATUS_BLOCK = undefined;
8998 const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0;
90 const file_or_dir_flag: ULONG = if (options.open_dir) FILE_DIRECTORY_FILE else FILE_NON_DIRECTORY_FILE;
99 const file_or_dir_flag: ULONG = switch (options.filter) {
100 .file_only => FILE_NON_DIRECTORY_FILE,
101 .dir_only => FILE_DIRECTORY_FILE,
102 .any => 0,
103 };
91104 // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT.
92105 const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT;
93106
......@@ -695,7 +708,7 @@ pub fn CreateSymbolicLink(
695708 .dir = dir,
696709 .creation = FILE_CREATE,
697710 .io_mode = .blocking,
698 .open_dir = is_directory,
711 .filter = if (is_directory) .dir_only else .file_only,
699712 }) catch |err| switch (err) {
700713 error.IsDir => return error.PathAlreadyExists,
701714 error.NotDir => unreachable,