diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-12-31 16:35:01 -0800 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2022-01-02 16:58:05 -0800 |
| commit | d3f87f8ac01039722197a13a12342fc747a90567 (patch) | |
| tree | 226dfc7c6e61533b34a083f810b3e1429390e679 /lib/std | |
| parent | b4d6e85a339e971829404dc1a260bde4062735f8 (diff) | |
| download | zig-d3f87f8ac01039722197a13a12342fc747a90567.tar.gz zig-d3f87f8ac01039722197a13a12342fc747a90567.zip | |
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.
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/fs.zig | 2 | ||||
| -rw-r--r-- | lib/std/fs/watch.zig | 2 | ||||
| -rw-r--r-- | lib/std/os.zig | 11 | ||||
| -rw-r--r-- | lib/std/os/windows.zig | 23 |
4 files changed, 26 insertions, 12 deletions
diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 6e1821178f..a09f9f2ed2 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -1361,7 +1361,7 @@ pub const Dir = struct { .share_access = share_access, .creation = creation, .io_mode = .blocking, - .open_dir = true, + .filter = .dir_only, }) catch |er| switch (er) { error.WouldBlock => unreachable, else => |e2| return e2, diff --git a/lib/std/fs/watch.zig b/lib/std/fs/watch.zig index c103925bdd..e2ec8b8061 100644 --- a/lib/std/fs/watch.zig +++ b/lib/std/fs/watch.zig @@ -401,7 +401,7 @@ pub fn Watch(comptime V: type) type { .access_mask = windows.FILE_LIST_DIRECTORY, .creation = windows.FILE_OPEN, .io_mode = .evented, - .open_dir = true, + .filter = .dir_only, }); errdefer windows.CloseHandle(dir_handle); diff --git a/lib/std/os.zig b/lib/std/os.zig index 1728c2ac0d..7be8825fcc 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1353,7 +1353,7 @@ fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions { access_mask |= w.GENERIC_READ | w.GENERIC_WRITE; } - const open_dir: bool = flags & O.DIRECTORY != 0; + const filter: windows.OpenFileOptions.Filter = if (flags & O.DIRECTORY != 0) .dir_only else .file_only; const follow_symlinks: bool = flags & O.NOFOLLOW == 0; const creation: w.ULONG = blk: { @@ -1369,7 +1369,7 @@ fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions { .access_mask = access_mask, .io_mode = .blocking, .creation = creation, - .open_dir = open_dir, + .filter = filter, .follow_symlinks = follow_symlinks, }; } @@ -2324,6 +2324,7 @@ pub fn renameatW( .access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE, .creation = windows.FILE_OPEN, .io_mode = .blocking, + .filter = .any, // This function is supposed to rename both files and directories. }) catch |err| switch (err) { error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`. else => |e| return e, @@ -2435,7 +2436,7 @@ pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!v .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE, .creation = windows.FILE_CREATE, .io_mode = .blocking, - .open_dir = true, + .filter = .dir_only, }) catch |err| switch (err) { error.IsDir => unreachable, error.PipeBusy => unreachable, @@ -2511,7 +2512,7 @@ pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void { .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE, .creation = windows.FILE_CREATE, .io_mode = .blocking, - .open_dir = true, + .filter = .dir_only, }) catch |err| switch (err) { error.IsDir => unreachable, error.PipeBusy => unreachable, @@ -4693,7 +4694,7 @@ pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPat .share_access = share_access, .creation = creation, .io_mode = .blocking, - .open_dir = true, + .filter = .dir_only, }) catch |er| switch (er) { error.WouldBlock => unreachable, else => |e2| return e2, diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 59e65ed54c..0d9907893c 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -53,17 +53,26 @@ pub const OpenFileOptions = struct { io_mode: std.io.ModeOverride, /// If true, tries to open path as a directory. /// Defaults to false. - open_dir: bool = false, + filter: Filter = .file_only, /// If false, tries to open path as a reparse point without dereferencing it. /// Defaults to true. follow_symlinks: bool = true, + + pub const Filter = enum { + /// Causes `OpenFile` to return `error.IsDir` if the opened handle would be a directory. + file_only, + /// Causes `OpenFile` to return `error.NotDir` if the opened handle would be a file. + dir_only, + /// `OpenFile` does not discriminate between opening files and directories. + any, + }; }; pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE { - if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and !options.open_dir) { + if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and options.filter == .file_only) { return error.IsDir; } - if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and !options.open_dir) { + if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and options.filter == .file_only) { return error.IsDir; } @@ -87,7 +96,11 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN }; var io: IO_STATUS_BLOCK = undefined; const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0; - const file_or_dir_flag: ULONG = if (options.open_dir) FILE_DIRECTORY_FILE else FILE_NON_DIRECTORY_FILE; + const file_or_dir_flag: ULONG = switch (options.filter) { + .file_only => FILE_NON_DIRECTORY_FILE, + .dir_only => FILE_DIRECTORY_FILE, + .any => 0, + }; // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT. const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT; @@ -695,7 +708,7 @@ pub fn CreateSymbolicLink( .dir = dir, .creation = FILE_CREATE, .io_mode = .blocking, - .open_dir = is_directory, + .filter = if (is_directory) .dir_only else .file_only, }) catch |err| switch (err) { error.IsDir => return error.PathAlreadyExists, error.NotDir => unreachable, |
