diff --git a/lib/std/Io/Dir.zig b/lib/std/Io/Dir.zig index 8ee450bf34..79824c56a6 100644 --- a/lib/std/Io/Dir.zig +++ b/lib/std/Io/Dir.zig @@ -155,8 +155,6 @@ pub const MakeError = error{ NoSpaceLeft, NotDir, ReadOnlyFileSystem, - /// WASI-only; file paths must be valid UTF-8. - InvalidUtf8, /// Windows-only; file paths provided by the user must be valid WTF-8. /// https://simonsapin.github.io/wtf-8/ InvalidWtf8, @@ -164,6 +162,8 @@ pub const MakeError = error{ NoDevice, /// On Windows, `\\server` or `\\server\share` was not found. NetworkNotFound, + /// File system cannot encode the requested file name bytes. + InvalidFileName, } || Io.Cancelable || Io.UnexpectedError; /// Creates a single directory with a relative or absolute path. diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index c5f1634f97..cab383b7dd 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -160,7 +160,11 @@ pub fn io(pool: *Pool) Io { .conditionWait = conditionWait, .conditionWake = conditionWake, - .dirMake = dirMake, + .dirMake = switch (builtin.os.tag) { + .windows => @panic("TODO"), + .wasi => @panic("TODO"), + else => dirMakePosix, + }, .dirStat = dirStat, .dirStatPath = dirStatPath, .fileStat = switch (builtin.os.tag) { @@ -759,14 +763,35 @@ fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition. } } -fn dirMake(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void { +fn dirMakePosix(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void { const pool: *Pool = @ptrCast(@alignCast(userdata)); - try pool.checkCancel(); - - _ = dir; - _ = sub_path; - _ = mode; - @panic("TODO"); + var path_buffer: [posix.PATH_MAX]u8 = undefined; + const sub_path_posix = try toPosixPath(sub_path, &path_buffer); + while (true) { + try pool.checkCancel(); + switch (posix.errno(posix.system.mkdirat(dir.handle, sub_path_posix, mode))) { + .SUCCESS => return, + .INTR => continue, + .ACCES => return error.AccessDenied, + .BADF => |err| return errnoBug(err), + .PERM => return error.PermissionDenied, + .DQUOT => return error.DiskQuota, + .EXIST => return error.PathAlreadyExists, + .FAULT => |err| return errnoBug(err), + .LOOP => return error.SymLinkLoop, + .MLINK => return error.LinkQuotaExceeded, + .NAMETOOLONG => return error.NameTooLong, + .NOENT => return error.FileNotFound, + .NOMEM => return error.SystemResources, + .NOSPC => return error.NoSpaceLeft, + .NOTDIR => return error.NotDir, + .ROFS => return error.ReadOnlyFileSystem, + // dragonfly: when dir_fd is unlinked from filesystem + .NOTCONN => return error.FileNotFound, + .ILSEQ => return error.InvalidFileName, + else => |err| return posix.unexpectedErrno(err), + } + } } fn dirStat(userdata: ?*anyopaque, dir: Io.Dir) Io.Dir.StatError!Io.Dir.Stat { @@ -2267,3 +2292,12 @@ fn timestampToPosix(nanoseconds: i96) std.posix.timespec { .nsec = @intCast(@mod(nanoseconds, std.time.ns_per_s)), }; } + +fn toPosixPath(file_path: []const u8, buffer: *[posix.PATH_MAX]u8) error{ NameTooLong, InvalidFileName }![:0]u8 { + if (std.mem.containsAtLeastScalar2(u8, file_path, 0, 1)) return error.InvalidFileName; + // >= rather than > to make room for the null byte + if (file_path.len >= buffer.len) return error.NameTooLong; + @memcpy(buffer[0..file_path.len], file_path); + buffer[file_path.len] = 0; + return buffer[0..file_path.len :0]; +}