std.Io.Threaded: implement dirOpenDir

This commit is contained in:
Andrew Kelley 2025-10-20 06:13:39 -07:00
parent dc6a4f3bf1
commit 62c0496d0a
2 changed files with 103 additions and 110 deletions

View file

@ -208,6 +208,7 @@ pub fn io(t: *Threaded) Io {
.dirOpenDir = switch (builtin.os.tag) {
.windows => dirOpenDirWindows,
.wasi => dirOpenDirWasi,
.haiku => dirOpenDirHaiku,
else => dirOpenDirPosix,
},
.dirClose = dirClose,
@ -1784,22 +1785,20 @@ fn dirOpenFilePosix(
if (@hasField(posix.O, "NOCTTY")) os_flags.NOCTTY = !flags.allow_ctty;
// Use the O locking flags if the os supports them to acquire the lock
// atomically.
if (have_flock_open_flags) {
// Note that the NONBLOCK flag is removed after the openat() call
// is successful.
switch (flags.lock) {
.none => {},
.shared => {
os_flags.SHLOCK = true;
os_flags.NONBLOCK = flags.lock_nonblocking;
},
.exclusive => {
os_flags.EXLOCK = true;
os_flags.NONBLOCK = flags.lock_nonblocking;
},
}
}
// atomically. Note that the NONBLOCK flag is removed after the openat()
// call is successful.
if (have_flock_open_flags) switch (flags.lock) {
.none => {},
.shared => {
os_flags.SHLOCK = true;
os_flags.NONBLOCK = flags.lock_nonblocking;
},
.exclusive => {
os_flags.EXLOCK = true;
os_flags.NONBLOCK = flags.lock_nonblocking;
},
};
const fd: posix.fd_t = while (true) {
try t.checkCancel();
const rc = openat_sym(dir.handle, sub_path_posix, os_flags, @as(posix.mode_t, 0));
@ -2008,11 +2007,92 @@ fn dirOpenDirPosix(
) Io.Dir.OpenError!Io.Dir {
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
_ = dir;
_ = sub_path;
var path_buffer: [posix.PATH_MAX]u8 = undefined;
const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
var flags: posix.O = switch (native_os) {
.wasi => .{
.read = true,
.NOFOLLOW = !options.follow_symlinks,
.DIRECTORY = true,
},
else => .{
.ACCMODE = .RDONLY,
.NOFOLLOW = !options.follow_symlinks,
.DIRECTORY = true,
.CLOEXEC = true,
},
};
if (@hasField(posix.O, "PATH") and !options.iterate)
flags.PATH = true;
while (true) {
try t.checkCancel();
const rc = openat_sym(dir.handle, sub_path_posix, flags, @as(usize, 0));
switch (posix.errno(rc)) {
.SUCCESS => return .{ .handle = @intCast(rc) },
.INTR => continue,
.CANCELED => return error.Canceled,
.FAULT => |err| return errnoBug(err),
.INVAL => return error.BadPathName,
.BADF => |err| return errnoBug(err), // File descriptor used after closed.
.ACCES => return error.AccessDenied,
.LOOP => return error.SymLinkLoop,
.MFILE => return error.ProcessFdQuotaExceeded,
.NAMETOOLONG => return error.NameTooLong,
.NFILE => return error.SystemFdQuotaExceeded,
.NODEV => return error.NoDevice,
.NOENT => return error.FileNotFound,
.NOMEM => return error.SystemResources,
.NOTDIR => return error.NotDir,
.PERM => return error.PermissionDenied,
.BUSY => return error.DeviceBusy,
.NXIO => return error.NoDevice,
.ILSEQ => return error.BadPathName,
else => |err| return posix.unexpectedErrno(err),
}
}
}
fn dirOpenDirHaiku(
userdata: ?*anyopaque,
dir: Io.Dir,
sub_path: []const u8,
options: Io.Dir.OpenOptions,
) Io.Dir.OpenError!Io.Dir {
const t: *Threaded = @ptrCast(@alignCast(userdata));
var path_buffer: [posix.PATH_MAX]u8 = undefined;
const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
_ = options;
@panic("TODO");
while (true) {
try t.checkCancel();
const rc = posix.system._kern_open_dir(dir.handle, sub_path_posix);
if (rc >= 0) return .{ .handle = rc };
switch (@as(posix.E, @enumFromInt(rc))) {
.INTR => continue,
.CANCELED => return error.Canceled,
.FAULT => |err| return errnoBug(err),
.INVAL => |err| return errnoBug(err),
.BADF => |err| return errnoBug(err), // File descriptor used after closed.
.ACCES => return error.AccessDenied,
.LOOP => return error.SymLinkLoop,
.MFILE => return error.ProcessFdQuotaExceeded,
.NAMETOOLONG => return error.NameTooLong,
.NFILE => return error.SystemFdQuotaExceeded,
.NODEV => return error.NoDevice,
.NOENT => return error.FileNotFound,
.NOMEM => return error.SystemResources,
.NOTDIR => return error.NotDir,
.PERM => return error.PermissionDenied,
.BUSY => return error.DeviceBusy,
else => |err| return posix.unexpectedErrno(err),
}
}
}
fn dirOpenDirWindows(

View file

@ -1069,96 +1069,9 @@ pub const OpenOptions = Io.Dir.OpenOptions;
/// Deprecated in favor of `Io.Dir.openDir`.
pub fn openDir(self: Dir, sub_path: []const u8, args: OpenOptions) OpenError!Dir {
switch (native_os) {
.windows => {
var threaded: Io.Threaded = .init_single_threaded;
const io = threaded.io();
return .adaptFromNewApi(try Io.Dir.openDir(.{ .handle = self.fd }, io, sub_path, args));
},
.wasi => if (!builtin.link_libc) {
var threaded: Io.Threaded = .init_single_threaded;
const io = threaded.io();
return .adaptFromNewApi(try Io.Dir.openDir(.{ .handle = self.fd }, io, sub_path, args));
},
else => {},
}
const sub_path_c = try posix.toPosixPath(sub_path);
return self.openDirZ(&sub_path_c, args);
}
/// Same as `openDir` except the parameter is null-terminated.
pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenOptions) OpenError!Dir {
switch (native_os) {
.windows => {
@compileError("use std.Io instead");
},
// Use the libc API when libc is linked because it implements things
// such as opening absolute directory paths.
.wasi => if (!builtin.link_libc) {
return openDir(self, mem.sliceTo(sub_path_c, 0), args);
},
.haiku => {
const rc = posix.system._kern_open_dir(self.fd, sub_path_c);
if (rc >= 0) return .{ .fd = rc };
switch (@as(posix.E, @enumFromInt(rc))) {
.FAULT => unreachable,
.INVAL => unreachable,
.BADF => unreachable,
.ACCES => return error.AccessDenied,
.LOOP => return error.SymLinkLoop,
.MFILE => return error.ProcessFdQuotaExceeded,
.NAMETOOLONG => return error.NameTooLong,
.NFILE => return error.SystemFdQuotaExceeded,
.NODEV => return error.NoDevice,
.NOENT => return error.FileNotFound,
.NOMEM => return error.SystemResources,
.NOTDIR => return error.NotDir,
.PERM => return error.PermissionDenied,
.BUSY => return error.DeviceBusy,
else => |err| return posix.unexpectedErrno(err),
}
},
else => {},
}
var symlink_flags: posix.O = switch (native_os) {
.wasi => .{
.read = true,
.NOFOLLOW = !args.follow_symlinks,
.DIRECTORY = true,
},
else => .{
.ACCMODE = .RDONLY,
.NOFOLLOW = !args.follow_symlinks,
.DIRECTORY = true,
.CLOEXEC = true,
},
};
if (@hasField(posix.O, "PATH") and !args.iterate)
symlink_flags.PATH = true;
return self.openDirFlagsZ(sub_path_c, symlink_flags);
}
/// Asserts `flags` has `DIRECTORY` set.
fn openDirFlagsZ(self: Dir, sub_path_c: [*:0]const u8, flags: posix.O) OpenError!Dir {
assert(flags.DIRECTORY);
const fd = posix.openatZ(self.fd, sub_path_c, flags, 0) catch |err| switch (err) {
error.FileTooBig => unreachable, // can't happen for directories
error.IsDir => unreachable, // we're setting DIRECTORY
error.NoSpaceLeft => unreachable, // not setting CREAT
error.PathAlreadyExists => unreachable, // not setting CREAT
error.FileLocksNotSupported => unreachable, // locking folders is not supported
error.WouldBlock => unreachable, // can't happen for directories
error.FileBusy => unreachable, // can't happen for directories
error.SharingViolation => unreachable, // can't happen for directories
error.PipeBusy => unreachable, // can't happen for directories
error.AntivirusInterference => unreachable, // can't happen for directories
error.ProcessNotFound => unreachable, // can't happen for directories
else => |e| return e,
};
return Dir{ .fd = fd };
var threaded: Io.Threaded = .init_single_threaded;
const io = threaded.io();
return .adaptFromNewApi(try Io.Dir.openDir(.{ .handle = self.fd }, io, sub_path, args));
}
pub const DeleteFileError = posix.UnlinkError;