authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-08 22:26:18-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:49-07:00
logeadfefa002b642d1b9173fa7d9a281a70a61e233
treed7736bbbbadff49aa9bb995ec0a4483029d20142
parente85df854aa86c4f41c3a64d2af1858aed3482fbc

std.Io: implement dirMake

In the future, it might be nice to introduce a type for file system path names. This would be a way to avoid having InvalidFileName in the error set, since construction of such type could validate it above the interface.

2 files changed, 44 insertions(+), 10 deletions(-)

lib/std/Io/Dir.zig+2-2
...@@ -155,8 +155,6 @@ pub const MakeError = error{...@@ -155,8 +155,6 @@ pub const MakeError = error{
155 NoSpaceLeft,155 NoSpaceLeft,
156 NotDir,156 NotDir,
157 ReadOnlyFileSystem,157 ReadOnlyFileSystem,
158 /// WASI-only; file paths must be valid UTF-8.
159 InvalidUtf8,
160 /// Windows-only; file paths provided by the user must be valid WTF-8.158 /// Windows-only; file paths provided by the user must be valid WTF-8.
161 /// https://simonsapin.github.io/wtf-8/159 /// https://simonsapin.github.io/wtf-8/
162 InvalidWtf8,160 InvalidWtf8,
...@@ -164,6 +162,8 @@ pub const MakeError = error{...@@ -164,6 +162,8 @@ pub const MakeError = error{
164 NoDevice,162 NoDevice,
165 /// On Windows, `\\server` or `\\server\share` was not found.163 /// On Windows, `\\server` or `\\server\share` was not found.
166 NetworkNotFound,164 NetworkNotFound,
165 /// File system cannot encode the requested file name bytes.
166 InvalidFileName,
167} || Io.Cancelable || Io.UnexpectedError;167} || Io.Cancelable || Io.UnexpectedError;
168168
169/// Creates a single directory with a relative or absolute path.169/// Creates a single directory with a relative or absolute path.
lib/std/Io/Threaded.zig+42-8
...@@ -160,7 +160,11 @@ pub fn io(pool: *Pool) Io {...@@ -160,7 +160,11 @@ pub fn io(pool: *Pool) Io {
160 .conditionWait = conditionWait,160 .conditionWait = conditionWait,
161 .conditionWake = conditionWake,161 .conditionWake = conditionWake,
162162
163 .dirMake = dirMake,163 .dirMake = switch (builtin.os.tag) {
164 .windows => @panic("TODO"),
165 .wasi => @panic("TODO"),
166 else => dirMakePosix,
167 },
164 .dirStat = dirStat,168 .dirStat = dirStat,
165 .dirStatPath = dirStatPath,169 .dirStatPath = dirStatPath,
166 .fileStat = switch (builtin.os.tag) {170 .fileStat = switch (builtin.os.tag) {
...@@ -759,14 +763,35 @@ fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition....@@ -759,14 +763,35 @@ fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.
759 }763 }
760}764}
761765
762fn dirMake(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void {766fn dirMakePosix(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void {
763 const pool: *Pool = @ptrCast(@alignCast(userdata));767 const pool: *Pool = @ptrCast(@alignCast(userdata));
764 try pool.checkCancel();768 var path_buffer: [posix.PATH_MAX]u8 = undefined;
765769 const sub_path_posix = try toPosixPath(sub_path, &path_buffer);
766 _ = dir;770 while (true) {
767 _ = sub_path;771 try pool.checkCancel();
768 _ = mode;772 switch (posix.errno(posix.system.mkdirat(dir.handle, sub_path_posix, mode))) {
769 @panic("TODO");773 .SUCCESS => return,
774 .INTR => continue,
775 .ACCES => return error.AccessDenied,
776 .BADF => |err| return errnoBug(err),
777 .PERM => return error.PermissionDenied,
778 .DQUOT => return error.DiskQuota,
779 .EXIST => return error.PathAlreadyExists,
780 .FAULT => |err| return errnoBug(err),
781 .LOOP => return error.SymLinkLoop,
782 .MLINK => return error.LinkQuotaExceeded,
783 .NAMETOOLONG => return error.NameTooLong,
784 .NOENT => return error.FileNotFound,
785 .NOMEM => return error.SystemResources,
786 .NOSPC => return error.NoSpaceLeft,
787 .NOTDIR => return error.NotDir,
788 .ROFS => return error.ReadOnlyFileSystem,
789 // dragonfly: when dir_fd is unlinked from filesystem
790 .NOTCONN => return error.FileNotFound,
791 .ILSEQ => return error.InvalidFileName,
792 else => |err| return posix.unexpectedErrno(err),
793 }
794 }
770}795}
771796
772fn dirStat(userdata: ?*anyopaque, dir: Io.Dir) Io.Dir.StatError!Io.Dir.Stat {797fn dirStat(userdata: ?*anyopaque, dir: Io.Dir) Io.Dir.StatError!Io.Dir.Stat {
...@@ -2267,3 +2292,12 @@ fn timestampToPosix(nanoseconds: i96) std.posix.timespec {...@@ -2267,3 +2292,12 @@ fn timestampToPosix(nanoseconds: i96) std.posix.timespec {
2267 .nsec = @intCast(@mod(nanoseconds, std.time.ns_per_s)),2292 .nsec = @intCast(@mod(nanoseconds, std.time.ns_per_s)),
2268 };2293 };
2269}2294}
2295
2296fn toPosixPath(file_path: []const u8, buffer: *[posix.PATH_MAX]u8) error{ NameTooLong, InvalidFileName }![:0]u8 {
2297 if (std.mem.containsAtLeastScalar2(u8, file_path, 0, 1)) return error.InvalidFileName;
2298 // >= rather than > to make room for the null byte
2299 if (file_path.len >= buffer.len) return error.NameTooLong;
2300 @memcpy(buffer[0..file_path.len], file_path);
2301 buffer[file_path.len] = 0;
2302 return buffer[0..file_path.len :0];
2303}