authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-11-25 06:36:49-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-23 22:15:07-08:00
log89bda60d28de5a44f445eaf056db45735176c1e3
tree4a2cd6740444f24aaf30c5ee6800849aedacdb69
parentc3f2de5e519926eb0029062fe8e782a6f9df9c05

std.Io.Threaded: implement makePath


3 files changed, 47 insertions(+), 55 deletions(-)

lib/std/Io.zig+1-1
...@@ -663,7 +663,7 @@ pub const VTable = struct {...@@ -663,7 +663,7 @@ pub const VTable = struct {
663 futexWake: *const fn (?*anyopaque, ptr: *const u32, max_waiters: u32) void,663 futexWake: *const fn (?*anyopaque, ptr: *const u32, max_waiters: u32) void,
664664
665 dirMake: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.Mode) Dir.MakeError!void,665 dirMake: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.Mode) Dir.MakeError!void,
666 dirMakePath: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.Mode) Dir.MakeError!void,666 dirMakePath: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.Mode) Dir.MakePathError!Dir.MakePathStatus,
667 dirMakeOpenPath: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.OpenOptions) Dir.MakeOpenPathError!Dir,667 dirMakeOpenPath: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.OpenOptions) Dir.MakeOpenPathError!Dir,
668 dirStat: *const fn (?*anyopaque, Dir) Dir.StatError!Dir.Stat,668 dirStat: *const fn (?*anyopaque, Dir) Dir.StatError!Dir.Stat,
669 dirStatPath: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.StatPathOptions) Dir.StatPathError!File.Stat,669 dirStatPath: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.StatPathOptions) Dir.StatPathError!File.Stat,
lib/std/Io/Dir.zig+12-35
...@@ -206,7 +206,7 @@ pub fn updateFile(...@@ -206,7 +206,7 @@ pub fn updateFile(
206 }206 }
207207
208 if (std.fs.path.dirname(dest_path)) |dirname| {208 if (std.fs.path.dirname(dest_path)) |dirname| {
209 try dest_dir.makePath(io, dirname);209 try dest_dir.makePathMode(io, dirname, default_mode);
210 }210 }
211211
212 var buffer: [1000]u8 = undefined; // Used only when direct fd-to-fd is not available.212 var buffer: [1000]u8 = undefined; // Used only when direct fd-to-fd is not available.
...@@ -287,13 +287,17 @@ pub fn makeDir(dir: Dir, io: Io, sub_path: []const u8) MakeError!void {...@@ -287,13 +287,17 @@ pub fn makeDir(dir: Dir, io: Io, sub_path: []const u8) MakeError!void {
287287
288pub const MakePathError = MakeError || StatPathError;288pub const MakePathError = MakeError || StatPathError;
289289
290/// Calls makeDir iteratively to make an entire path, creating any parent290/// Same as `makePathMode` but passes `default_mode`.
291/// directories that do not exist.291pub fn makePath(dir: Dir, io: Io, sub_path: []const u8) MakePathError!void {
292 _ = try io.vtable.dirMakePath(io.userdata, dir, sub_path, default_mode);
293}
294
295/// Creates parent directories as necessary to ensure `sub_path` exists as a directory.
292///296///
293/// Returns success if the path already exists and is a directory.297/// Returns success if the path already exists and is a directory.
294///298///
295/// This function is not atomic, and if it returns an error, the file system299/// This function may not be atomic. If it returns an error, the file system
296/// may have been modified regardless.300/// may have been modified.
297///301///
298/// Fails on an empty path with `error.BadPathName` as that is not a path that302/// Fails on an empty path with `error.BadPathName` as that is not a path that
299/// can be created.303/// can be created.
...@@ -309,8 +313,8 @@ pub const MakePathError = MakeError || StatPathError;...@@ -309,8 +313,8 @@ pub const MakePathError = MakeError || StatPathError;
309/// - On other platforms, `..` are not resolved before the path is passed to `mkdirat`,313/// - On other platforms, `..` are not resolved before the path is passed to `mkdirat`,
310/// meaning a `sub_path` like "first/../second" will create both a `./first`314/// meaning a `sub_path` like "first/../second" will create both a `./first`
311/// and a `./second` directory.315/// and a `./second` directory.
312pub fn makePath(dir: Dir, io: Io, sub_path: []const u8) MakePathError!void {316pub fn makePathMode(dir: Dir, io: Io, sub_path: []const u8, mode: Mode) MakePathError!void {
313 _ = try makePathStatus(dir, io, sub_path);317 _ = try io.vtable.dirMakePath(io.userdata, dir, sub_path, mode);
314}318}
315319
316pub const MakePathStatus = enum { existed, created };320pub const MakePathStatus = enum { existed, created };
...@@ -318,34 +322,7 @@ pub const MakePathStatus = enum { existed, created };...@@ -318,34 +322,7 @@ pub const MakePathStatus = enum { existed, created };
318/// Same as `makePath` except returns whether the path already existed or was322/// Same as `makePath` except returns whether the path already existed or was
319/// successfully created.323/// successfully created.
320pub fn makePathStatus(dir: Dir, io: Io, sub_path: []const u8) MakePathError!MakePathStatus {324pub fn makePathStatus(dir: Dir, io: Io, sub_path: []const u8) MakePathError!MakePathStatus {
321 var it = std.fs.path.componentIterator(sub_path);325 return io.vtable.dirMakePath(io.userdata, dir, sub_path, default_mode);
322 var status: MakePathStatus = .existed;
323 var component = it.last() orelse return error.BadPathName;
324 while (true) {
325 if (makeDir(dir, io, component.path)) {
326 status = .created;
327 } else |err| switch (err) {
328 error.PathAlreadyExists => {
329 // stat the file and return an error if it's not a directory
330 // this is important because otherwise a dangling symlink
331 // could cause an infinite loop
332 check_dir: {
333 // workaround for windows, see https://github.com/ziglang/zig/issues/16738
334 const fstat = statPath(dir, io, component.path, .{}) catch |stat_err| switch (stat_err) {
335 error.IsDir => break :check_dir,
336 else => |e| return e,
337 };
338 if (fstat.kind != .directory) return error.NotDir;
339 }
340 },
341 error.FileNotFound => |e| {
342 component = it.previous() orelse return e;
343 continue;
344 },
345 else => |e| return e,
346 }
347 component = it.next() orelse return status;
348 }
349}326}
350327
351pub const MakeOpenPathError = MakeError || OpenError || StatPathError;328pub const MakeOpenPathError = MakeError || OpenError || StatPathError;
lib/std/Io/Threaded.zig+34-19
...@@ -1455,27 +1455,42 @@ fn dirMakeWindows(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode...@@ -1455,27 +1455,42 @@ fn dirMakeWindows(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode
1455 windows.CloseHandle(sub_dir_handle);1455 windows.CloseHandle(sub_dir_handle);
1456}1456}
14571457
1458const dirMakePath = switch (native_os) {1458fn dirMakePath(
1459 .windows => dirMakePathWindows,1459 userdata: ?*anyopaque,
1460 else => dirMakePathPosix,1460 dir: Io.Dir,
1461};1461 sub_path: []const u8,
14621462 mode: Io.Dir.Mode,
1463fn dirMakePathPosix(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void {1463) Io.Dir.MakePathError!Io.Dir.MakePathStatus {
1464 const t: *Threaded = @ptrCast(@alignCast(userdata));1464 const t: *Threaded = @ptrCast(@alignCast(userdata));
1465 _ = t;
1466 _ = dir;
1467 _ = sub_path;
1468 _ = mode;
1469 @panic("TODO implement dirMakePathPosix");
1470}
14711465
1472fn dirMakePathWindows(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void {1466 var it = std.fs.path.componentIterator(sub_path);
1473 const t: *Threaded = @ptrCast(@alignCast(userdata));1467 var status: Io.Dir.MakePathStatus = .existed;
1474 _ = t;1468 var component = it.last() orelse return error.BadPathName;
1475 _ = dir;1469 while (true) {
1476 _ = sub_path;1470 if (dirMake(t, dir, component.path, mode)) |_| {
1477 _ = mode;1471 status = .created;
1478 @panic("TODO implement dirMakePathWindows");1472 } else |err| switch (err) {
1473 error.PathAlreadyExists => {
1474 // stat the file and return an error if it's not a directory
1475 // this is important because otherwise a dangling symlink
1476 // could cause an infinite loop
1477 check_dir: {
1478 // workaround for windows, see https://github.com/ziglang/zig/issues/16738
1479 const fstat = dirStatPath(t, dir, component.path, .{}) catch |stat_err| switch (stat_err) {
1480 error.IsDir => break :check_dir,
1481 else => |e| return e,
1482 };
1483 if (fstat.kind != .directory) return error.NotDir;
1484 }
1485 },
1486 error.FileNotFound => |e| {
1487 component = it.previous() orelse return e;
1488 continue;
1489 },
1490 else => |e| return e,
1491 }
1492 component = it.next() orelse return status;
1493 }
1479}1494}
14801495
1481const dirMakeOpenPath = switch (native_os) {1496const dirMakeOpenPath = switch (native_os) {