mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
fix std.fs.Dir.makePath silent failure (#16878)
std.fs.dir.makePath silently failed if one of the items in the path already exists. For example:
cwd.makePath("foo/bar/baz")
Silently failing is OK if "bar" is already a directory - this is the intended use of makePath (like mkdir -p). But if bar is a file then the subdirectory baz cannot be created - the end result is that makePath doesn't do anything which should be a detectable error because baz is never created.
The existing code had a TODO comment that did not specifically cover this error, but the solution for this silent failure also accomplishes the TODO task - the code now stats "foo" and returns an appropriate error. The new code also handles potential race condition if "bar" is deleted/permissions changed/etc in between the initial makeDir and statFile calls.
This commit is contained in:
parent
ed410b9c1e
commit
4cbf74bd9b
@ -1125,9 +1125,17 @@ pub fn makePath(self: Dir, sub_path: []const u8) !void {
|
|||||||
while (true) {
|
while (true) {
|
||||||
self.makeDir(component.path) catch |err| switch (err) {
|
self.makeDir(component.path) catch |err| switch (err) {
|
||||||
error.PathAlreadyExists => {
|
error.PathAlreadyExists => {
|
||||||
// TODO stat the file and return an error if it's not a directory
|
// stat the file and return an error if it's not a directory
|
||||||
// this is important because otherwise a dangling symlink
|
// this is important because otherwise a dangling symlink
|
||||||
// could cause an infinite loop
|
// could cause an infinite loop
|
||||||
|
check_dir: {
|
||||||
|
// workaround for windows, see https://github.com/ziglang/zig/issues/16738
|
||||||
|
const fstat = self.statFile(component.path) catch |stat_err| switch (stat_err) {
|
||||||
|
error.IsDir => break :check_dir,
|
||||||
|
else => |e| return e,
|
||||||
|
};
|
||||||
|
if (fstat.kind != .directory) return error.NotDir;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
error.FileNotFound => |e| {
|
error.FileNotFound => |e| {
|
||||||
component = it.previous() orelse return e;
|
component = it.previous() orelse return e;
|
||||||
|
|||||||
@ -1089,6 +1089,16 @@ test "makePath in a directory that no longer exists" {
|
|||||||
try testing.expectError(error.FileNotFound, tmp.dir.makePath("sub-path"));
|
try testing.expectError(error.FileNotFound, tmp.dir.makePath("sub-path"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "makePath but sub_path contains pre-existing file" {
|
||||||
|
var tmp = tmpDir(.{});
|
||||||
|
defer tmp.cleanup();
|
||||||
|
|
||||||
|
try tmp.dir.makeDir("foo");
|
||||||
|
try tmp.dir.writeFile("foo/bar", "");
|
||||||
|
|
||||||
|
try testing.expectError(error.NotDir, tmp.dir.makePath("foo/bar/baz"));
|
||||||
|
}
|
||||||
|
|
||||||
fn expectDir(dir: Dir, path: []const u8) !void {
|
fn expectDir(dir: Dir, path: []const u8) !void {
|
||||||
var d = try dir.openDir(path, .{});
|
var d = try dir.openDir(path, .{});
|
||||||
d.close();
|
d.close();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user