diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 944800a7f468..29e603e1b3c2 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -1490,13 +1490,18 @@ pub const Dir = struct { } } - fn openDirAccessMaskW(self: Dir, sub_path: []const u8, access_mask: u32, no_follow: bool) OpenError!Dir { + /// Calls makeOpenDirAccessMaskW recursively to make an entire path + /// (i.e. falling back if the parent directory does not exist). Opens the dir if the path + /// already exists and is a directory. + /// This function is not atomic, and if it returns an error, the file system may + /// have been modified regardless. + fn makeOpenPathAccessMaskW(self: Dir, sub_path: []const u8, access_mask: u32, no_follow: bool) OpenError!Dir { const w = os.windows; var end_index: usize = sub_path.len; return while (true) { const sub_path_w = try w.sliceToPrefixedFileW(sub_path[0..end_index]); - const result = self.openDirWithAccessAndCreationW(sub_path_w.span().ptr, access_mask, .{ + const result = self.makeOpenDirAccessMaskW(sub_path_w.span().ptr, access_mask, .{ .no_follow = no_follow, .create_disposition = if (end_index == sub_path.len) w.FILE_OPEN_IF else w.FILE_CREATE, }) catch |err| switch (err) { @@ -1524,6 +1529,7 @@ pub const Dir = struct { /// This function performs `makePath`, followed by `openDir`. /// If supported by the OS, this operation is atomic. It is not atomic on /// all operating systems. + /// On Windows, this function preforms `makeOpenPathAccessMaskW`. pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOptions) !Dir { return switch (builtin.os.tag) { .windows => { @@ -1531,12 +1537,15 @@ pub const Dir = struct { const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | w.SYNCHRONIZE | w.FILE_TRAVERSE; - return self.openDirAccessMaskW(sub_path, base_flags, open_dir_options.no_follow); + return self.makeOpenPathAccessMaskW(sub_path, base_flags, open_dir_options.no_follow); }, else => { - return self.openDir(sub_path, open_dir_options) catch { - try self.makePath(sub_path); - return self.openDir(sub_path, open_dir_options); + return self.openDir(sub_path, open_dir_options) catch |err| switch (err) { + error.FileNotFound => { + try self.makePath(sub_path); + return self.openDir(sub_path, open_dir_options); + }, + else => |e| return e, }; }, }; @@ -1553,13 +1562,16 @@ pub const Dir = struct { w.SYNCHRONIZE | w.FILE_TRAVERSE | w.FILE_LIST_DIRECTORY; return IterableDir{ - .dir = try self.openDirAccessMaskW(sub_path, base_flags, open_dir_options.no_follow), + .dir = try self.makeOpenPathAccessMaskW(sub_path, base_flags, open_dir_options.no_follow), }; }, else => { - return self.openIterableDir(sub_path, open_dir_options) catch { - try self.makePath(sub_path); - return self.openIterableDir(sub_path, open_dir_options); + return self.openIterableDir(sub_path, open_dir_options) catch |err| switch (err) { + error.FileNotFound => { + try self.makePath(sub_path); + return self.openIterableDir(sub_path, open_dir_options); + }, + else => |e| return e, }; }, }; @@ -1816,7 +1828,7 @@ pub const Dir = struct { const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | w.SYNCHRONIZE | w.FILE_TRAVERSE; const flags: u32 = if (iterable) base_flags | w.FILE_LIST_DIRECTORY else base_flags; - var dir = try self.openDirWithAccessAndCreationW(sub_path_w, flags, .{ + var dir = try self.makeOpenDirAccessMaskW(sub_path_w, flags, .{ .no_follow = args.no_follow, .create_disposition = w.FILE_OPEN, }); @@ -1842,12 +1854,12 @@ pub const Dir = struct { return Dir{ .fd = fd }; } - const OpenDirWithAccessAndCreationWOptions = struct { + const MakeOpenDirAccessMaskWOptions = struct { no_follow: bool, create_disposition: u32, }; - fn openDirWithAccessAndCreationW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32, flags: OpenDirWithAccessAndCreationWOptions) OpenError!Dir { + fn makeOpenDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32, flags: MakeOpenDirAccessMaskWOptions) OpenError!Dir { const w = os.windows; var result = Dir{ diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 4c51e512b678..3fc3aaa1499e 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -475,6 +475,13 @@ test "file operations on directories" { dir.close(); } +test "makeOpenPath parent dirs do not exist" { + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + var dir = try fs.cwd().makeOpenPath("root_dir/parent_dir/some_dir", .{}); + defer dir.close(); +} + test "deleteDir" { var tmp_dir = tmpDir(.{}); defer tmp_dir.cleanup();