| author | |
| committer | |
| log | d3f87f8ac01039722197a13a12342fc747a90567 |
| tree | 226dfc7c6e61533b34a083f810b3e1429390e679 |
| parent | b4d6e85a339e971829404dc1a260bde4062735f8 |
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 { |
| 1361 | 1361 | .share_access = share_access, |
| 1362 | 1362 | .creation = creation, |
| 1363 | 1363 | .io_mode = .blocking, |
| 1364 | .open_dir = true, | |
| 1364 | .filter = .dir_only, | |
| 1365 | 1365 | }) catch |er| switch (er) { |
| 1366 | 1366 | error.WouldBlock => unreachable, |
| 1367 | 1367 | else => |e2| return e2, |
lib/std/fs/watch.zig+1-1| ... | ... | @@ -401,7 +401,7 @@ pub fn Watch(comptime V: type) type { |
| 401 | 401 | .access_mask = windows.FILE_LIST_DIRECTORY, |
| 402 | 402 | .creation = windows.FILE_OPEN, |
| 403 | 403 | .io_mode = .evented, |
| 404 | .open_dir = true, | |
| 404 | .filter = .dir_only, | |
| 405 | 405 | }); |
| 406 | 406 | errdefer windows.CloseHandle(dir_handle); |
| 407 | 407 |
lib/std/os.zig+6-5| ... | ... | @@ -1353,7 +1353,7 @@ fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions { |
| 1353 | 1353 | access_mask |= w.GENERIC_READ | w.GENERIC_WRITE; |
| 1354 | 1354 | } |
| 1355 | 1355 | |
| 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; | |
| 1357 | 1357 | const follow_symlinks: bool = flags & O.NOFOLLOW == 0; |
| 1358 | 1358 | |
| 1359 | 1359 | const creation: w.ULONG = blk: { |
| ... | ... | @@ -1369,7 +1369,7 @@ fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions { |
| 1369 | 1369 | .access_mask = access_mask, |
| 1370 | 1370 | .io_mode = .blocking, |
| 1371 | 1371 | .creation = creation, |
| 1372 | .open_dir = open_dir, | |
| 1372 | .filter = filter, | |
| 1373 | 1373 | .follow_symlinks = follow_symlinks, |
| 1374 | 1374 | }; |
| 1375 | 1375 | } |
| ... | ... | @@ -2324,6 +2324,7 @@ pub fn renameatW( |
| 2324 | 2324 | .access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE, |
| 2325 | 2325 | .creation = windows.FILE_OPEN, |
| 2326 | 2326 | .io_mode = .blocking, |
| 2327 | .filter = .any, // This function is supposed to rename both files and directories. | |
| 2327 | 2328 | }) catch |err| switch (err) { |
| 2328 | 2329 | error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`. |
| 2329 | 2330 | else => |e| return e, |
| ... | ... | @@ -2435,7 +2436,7 @@ pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!v |
| 2435 | 2436 | .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE, |
| 2436 | 2437 | .creation = windows.FILE_CREATE, |
| 2437 | 2438 | .io_mode = .blocking, |
| 2438 | .open_dir = true, | |
| 2439 | .filter = .dir_only, | |
| 2439 | 2440 | }) catch |err| switch (err) { |
| 2440 | 2441 | error.IsDir => unreachable, |
| 2441 | 2442 | error.PipeBusy => unreachable, |
| ... | ... | @@ -2511,7 +2512,7 @@ pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void { |
| 2511 | 2512 | .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE, |
| 2512 | 2513 | .creation = windows.FILE_CREATE, |
| 2513 | 2514 | .io_mode = .blocking, |
| 2514 | .open_dir = true, | |
| 2515 | .filter = .dir_only, | |
| 2515 | 2516 | }) catch |err| switch (err) { |
| 2516 | 2517 | error.IsDir => unreachable, |
| 2517 | 2518 | error.PipeBusy => unreachable, |
| ... | ... | @@ -4693,7 +4694,7 @@ pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPat |
| 4693 | 4694 | .share_access = share_access, |
| 4694 | 4695 | .creation = creation, |
| 4695 | 4696 | .io_mode = .blocking, |
| 4696 | .open_dir = true, | |
| 4697 | .filter = .dir_only, | |
| 4697 | 4698 | }) catch |er| switch (er) { |
| 4698 | 4699 | error.WouldBlock => unreachable, |
| 4699 | 4700 | else => |e2| return e2, |
lib/std/os/windows.zig+18-5| ... | ... | @@ -53,17 +53,26 @@ pub const OpenFileOptions = struct { |
| 53 | 53 | io_mode: std.io.ModeOverride, |
| 54 | 54 | /// If true, tries to open path as a directory. |
| 55 | 55 | /// Defaults to false. |
| 56 | open_dir: bool = false, | |
| 56 | filter: Filter = .file_only, | |
| 57 | 57 | /// If false, tries to open path as a reparse point without dereferencing it. |
| 58 | 58 | /// Defaults to true. |
| 59 | 59 | 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 | }; | |
| 60 | 69 | }; |
| 61 | 70 | |
| 62 | 71 | pub 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) { | |
| 64 | 73 | return error.IsDir; |
| 65 | 74 | } |
| 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) { | |
| 67 | 76 | return error.IsDir; |
| 68 | 77 | } |
| 69 | 78 | |
| ... | ... | @@ -87,7 +96,11 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN |
| 87 | 96 | }; |
| 88 | 97 | var io: IO_STATUS_BLOCK = undefined; |
| 89 | 98 | 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 | }; | |
| 91 | 104 | // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT. |
| 92 | 105 | const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT; |
| 93 | 106 | |
| ... | ... | @@ -695,7 +708,7 @@ pub fn CreateSymbolicLink( |
| 695 | 708 | .dir = dir, |
| 696 | 709 | .creation = FILE_CREATE, |
| 697 | 710 | .io_mode = .blocking, |
| 698 | .open_dir = is_directory, | |
| 711 | .filter = if (is_directory) .dir_only else .file_only, | |
| 699 | 712 | }) catch |err| switch (err) { |
| 700 | 713 | error.IsDir => return error.PathAlreadyExists, |
| 701 | 714 | error.NotDir => unreachable, |