Commit 81e7e9fdbb
Changed files (5)
lib
lib/std/fs/Dir.zig
@@ -1235,28 +1235,10 @@ pub fn setAsCwd(self: Dir) !void {
try posix.fchdir(self.fd);
}
-pub const OpenOptions = struct {
- /// `true` means the opened directory can be used as the `Dir` parameter
- /// for functions which operate based on an open directory handle. When `false`,
- /// such operations are Illegal Behavior.
- access_sub_paths: bool = true,
-
- /// `true` means the opened directory can be scanned for the files and sub-directories
- /// of the result. It means the `iterate` function can be called.
- iterate: bool = false,
-
- /// `true` means it won't dereference the symlinks.
- no_follow: bool = false,
-};
+/// Deprecated in favor of `Io.Dir.OpenOptions`.
+pub const OpenOptions = Io.Dir.OpenOptions;
-/// Opens a directory at the given path. The directory is a system resource that remains
-/// open until `close` is called on the result.
-/// The directory cannot be iterated unless the `iterate` option is set to `true`.
-///
-/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
-/// On WASI, `sub_path` should be encoded as valid UTF-8.
-/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
-/// Asserts that the path parameter has no null bytes.
+/// Deprecated in favor of `Io.Dir.openDir`.
pub fn openDir(self: Dir, sub_path: []const u8, args: OpenOptions) OpenError!Dir {
switch (native_os) {
.windows => {
@@ -1264,54 +1246,9 @@ pub fn openDir(self: Dir, sub_path: []const u8, args: OpenOptions) OpenError!Dir
return self.openDirW(sub_path_w.span().ptr, args);
},
.wasi => if (!builtin.link_libc) {
- var base: std.os.wasi.rights_t = .{
- .FD_FILESTAT_GET = true,
- .FD_FDSTAT_SET_FLAGS = true,
- .FD_FILESTAT_SET_TIMES = true,
- };
- if (args.access_sub_paths) {
- base.FD_READDIR = true;
- base.PATH_CREATE_DIRECTORY = true;
- base.PATH_CREATE_FILE = true;
- base.PATH_LINK_SOURCE = true;
- base.PATH_LINK_TARGET = true;
- base.PATH_OPEN = true;
- base.PATH_READLINK = true;
- base.PATH_RENAME_SOURCE = true;
- base.PATH_RENAME_TARGET = true;
- base.PATH_FILESTAT_GET = true;
- base.PATH_FILESTAT_SET_SIZE = true;
- base.PATH_FILESTAT_SET_TIMES = true;
- base.PATH_SYMLINK = true;
- base.PATH_REMOVE_DIRECTORY = true;
- base.PATH_UNLINK_FILE = true;
- }
-
- const result = posix.openatWasi(
- self.fd,
- sub_path,
- .{ .SYMLINK_FOLLOW = !args.no_follow },
- .{ .DIRECTORY = true },
- .{},
- base,
- base,
- );
- const fd = result 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,
- error.PipeBusy => unreachable,
- error.ProcessNotFound => unreachable,
- error.AntivirusInterference => unreachable,
-
- else => |e| return e,
- };
- return .{ .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));
},
else => {},
}
@@ -1358,12 +1295,12 @@ pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenOptions) OpenErr
var symlink_flags: posix.O = switch (native_os) {
.wasi => .{
.read = true,
- .NOFOLLOW = args.no_follow,
+ .NOFOLLOW = !args.follow_symlinks,
.DIRECTORY = true,
},
else => .{
.ACCMODE = .RDONLY,
- .NOFOLLOW = args.no_follow,
+ .NOFOLLOW = !args.follow_symlinks,
.DIRECTORY = true,
.CLOEXEC = true,
},
@@ -1384,7 +1321,7 @@ pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenOptions) OpenEr
w.SYNCHRONIZE | w.FILE_TRAVERSE;
const flags: u32 = if (args.iterate) base_flags | w.FILE_LIST_DIRECTORY else base_flags;
const dir = self.makeOpenDirAccessMaskW(sub_path_w, flags, .{
- .no_follow = args.no_follow,
+ .no_follow = !args.follow_symlinks,
.create_disposition = w.FILE_OPEN,
}) catch |err| switch (err) {
error.ReadOnlyFileSystem => unreachable,
@@ -1923,7 +1860,7 @@ pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void {
if (treat_as_dir) {
if (stack.unusedCapacitySlice().len >= 1) {
var iterable_dir = top.iter.dir.openDir(entry.name, .{
- .no_follow = true,
+ .follow_symlinks = false,
.iterate = true,
}) catch |err| switch (err) {
error.NotDir => {
@@ -2019,7 +1956,7 @@ pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void {
handle_entry: while (true) {
if (treat_as_dir) {
break :iterable_dir parent_dir.openDir(name, .{
- .no_follow = true,
+ .follow_symlinks = false,
.iterate = true,
}) catch |err| switch (err) {
error.NotDir => {
@@ -2125,7 +2062,7 @@ fn deleteTreeMinStackSizeWithKindHint(self: Dir, sub_path: []const u8, kind_hint
handle_entry: while (true) {
if (treat_as_dir) {
const new_dir = dir.openDir(entry.name, .{
- .no_follow = true,
+ .follow_symlinks = false,
.iterate = true,
}) catch |err| switch (err) {
error.NotDir => {
@@ -2224,7 +2161,7 @@ fn deleteTreeOpenInitialSubpath(self: Dir, sub_path: []const u8, kind_hint: File
handle_entry: while (true) {
if (treat_as_dir) {
break :iterable_dir self.openDir(sub_path, .{
- .no_follow = true,
+ .follow_symlinks = false,
.iterate = true,
}) catch |err| switch (err) {
error.NotDir => {
lib/std/Io/Dir.zig
@@ -69,6 +69,30 @@ pub const OpenError = error{
NetworkNotFound,
} || PathNameError || Io.Cancelable || Io.UnexpectedError;
+pub const OpenOptions = struct {
+ /// `true` means the opened directory can be used as the `Dir` parameter
+ /// for functions which operate based on an open directory handle. When `false`,
+ /// such operations are Illegal Behavior.
+ access_sub_paths: bool = true,
+ /// `true` means the opened directory can be scanned for the files and sub-directories
+ /// of the result. It means the `iterate` function can be called.
+ iterate: bool = false,
+ /// `false` means it won't dereference the symlinks.
+ follow_symlinks: bool = true,
+};
+
+/// Opens a directory at the given path. The directory is a system resource that remains
+/// open until `close` is called on the result.
+///
+/// The directory cannot be iterated unless the `iterate` option is set to `true`.
+///
+/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
+/// On WASI, `sub_path` should be encoded as valid UTF-8.
+/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
+pub fn openDir(dir: Dir, io: Io, sub_path: []const u8, options: OpenOptions) OpenError!Dir {
+ return io.vtable.dirOpenDir(io.userdata, dir, sub_path, options);
+}
+
pub fn openFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
return io.vtable.dirOpenFile(io.userdata, dir, sub_path, flags);
}
lib/std/Io/Threaded.zig
@@ -198,6 +198,11 @@ pub fn io(t: *Threaded) Io {
.wasi => dirOpenFileWasi,
else => dirOpenFilePosix,
},
+ .dirOpenDir = switch (builtin.os.tag) {
+ .windows => @panic("TODO"),
+ .wasi => dirOpenDirWasi,
+ else => dirOpenDirPosix,
+ },
.fileClose = fileClose,
.fileWriteStreaming = fileWriteStreaming,
.fileWritePositional = fileWritePositional,
@@ -1429,7 +1434,6 @@ fn dirCreateFileWasi(
.CANCELED => return error.Canceled,
.FAULT => |err| return errnoBug(err),
- // Provides INVAL with a linux host on a bad path name, but NOENT on Windows
.INVAL => return error.BadPathName,
.BADF => |err| return errnoBug(err), // File descriptor used after closed.
.ACCES => return error.AccessDenied,
@@ -1656,6 +1660,87 @@ fn dirOpenFileWasi(
}
}
+fn dirOpenDirPosix(
+ userdata: ?*anyopaque,
+ dir: Io.Dir,
+ sub_path: []const u8,
+ options: Io.Dir.OpenOptions,
+) Io.Dir.OpenError!Io.Dir {
+ const t: *Threaded = @ptrCast(@alignCast(userdata));
+
+ _ = t;
+ _ = dir;
+ _ = sub_path;
+ _ = options;
+ @panic("TODO");
+}
+
+fn dirOpenDirWasi(
+ userdata: ?*anyopaque,
+ dir: Io.Dir,
+ sub_path: []const u8,
+ options: Io.Dir.OpenOptions,
+) Io.Dir.OpenError!Io.Dir {
+ if (builtin.link_libc) return dirOpenDirPosix(userdata, dir, sub_path, options);
+ const t: *Threaded = @ptrCast(@alignCast(userdata));
+ const wasi = std.os.wasi;
+
+ var base: std.os.wasi.rights_t = .{
+ .FD_FILESTAT_GET = true,
+ .FD_FDSTAT_SET_FLAGS = true,
+ .FD_FILESTAT_SET_TIMES = true,
+ };
+ if (options.access_sub_paths) {
+ base.FD_READDIR = true;
+ base.PATH_CREATE_DIRECTORY = true;
+ base.PATH_CREATE_FILE = true;
+ base.PATH_LINK_SOURCE = true;
+ base.PATH_LINK_TARGET = true;
+ base.PATH_OPEN = true;
+ base.PATH_READLINK = true;
+ base.PATH_RENAME_SOURCE = true;
+ base.PATH_RENAME_TARGET = true;
+ base.PATH_FILESTAT_GET = true;
+ base.PATH_FILESTAT_SET_SIZE = true;
+ base.PATH_FILESTAT_SET_TIMES = true;
+ base.PATH_SYMLINK = true;
+ base.PATH_REMOVE_DIRECTORY = true;
+ base.PATH_UNLINK_FILE = true;
+ }
+
+ const lookup_flags: wasi.lookupflags_t = .{ .SYMLINK_FOLLOW = options.follow_symlinks };
+ const oflags: wasi.oflags_t = .{ .DIRECTORY = true };
+ const fdflags: wasi.fdflags_t = .{};
+ var fd: posix.fd_t = undefined;
+
+ while (true) {
+ try t.checkCancel();
+ switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, base, fdflags, &fd)) {
+ .SUCCESS => return .{ .handle = fd },
+ .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,
+ .NOTCAPABLE => return error.AccessDenied,
+ .ILSEQ => return error.BadPathName,
+ else => |err| return posix.unexpectedErrno(err),
+ }
+ }
+}
+
fn fileClose(userdata: ?*anyopaque, file: Io.File) void {
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
lib/std/Io.zig
@@ -666,6 +666,7 @@ pub const VTable = struct {
dirAccess: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.AccessOptions) Dir.AccessError!void,
dirCreateFile: *const fn (?*anyopaque, Dir, sub_path: []const u8, File.CreateFlags) File.OpenError!File,
dirOpenFile: *const fn (?*anyopaque, Dir, sub_path: []const u8, File.OpenFlags) File.OpenError!File,
+ dirOpenDir: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.OpenOptions) Dir.OpenError!Dir,
fileStat: *const fn (?*anyopaque, File) File.StatError!File.Stat,
fileClose: *const fn (?*anyopaque, File) void,
fileWriteStreaming: *const fn (?*anyopaque, File, buffer: [][]const u8) File.WriteStreamingError!usize,
lib/std/tar.zig
@@ -977,7 +977,7 @@ test pipeToFileSystem {
const data = @embedFile("tar/testdata/example.tar");
var reader: std.Io.Reader = .fixed(data);
- var tmp = testing.tmpDir(.{ .no_follow = true });
+ var tmp = testing.tmpDir(.{ .follow_symlinks = false });
defer tmp.cleanup();
const dir = tmp.dir;
@@ -1010,7 +1010,7 @@ test "pipeToFileSystem root_dir" {
// with strip_components = 1
{
- var tmp = testing.tmpDir(.{ .no_follow = true });
+ var tmp = testing.tmpDir(.{ .follow_symlinks = false });
defer tmp.cleanup();
var diagnostics: Diagnostics = .{ .allocator = testing.allocator };
defer diagnostics.deinit();
@@ -1032,7 +1032,7 @@ test "pipeToFileSystem root_dir" {
// with strip_components = 0
{
reader = .fixed(data);
- var tmp = testing.tmpDir(.{ .no_follow = true });
+ var tmp = testing.tmpDir(.{ .follow_symlinks = false });
defer tmp.cleanup();
var diagnostics: Diagnostics = .{ .allocator = testing.allocator };
defer diagnostics.deinit();
@@ -1084,7 +1084,7 @@ test "pipeToFileSystem strip_components" {
const data = @embedFile("tar/testdata/example.tar");
var reader: std.Io.Reader = .fixed(data);
- var tmp = testing.tmpDir(.{ .no_follow = true });
+ var tmp = testing.tmpDir(.{ .follow_symlinks = false });
defer tmp.cleanup();
var diagnostics: Diagnostics = .{ .allocator = testing.allocator };
defer diagnostics.deinit();
@@ -1145,7 +1145,7 @@ test "executable bit" {
for ([_]PipeOptions.ModeMode{ .ignore, .executable_bit_only }) |opt| {
var reader: std.Io.Reader = .fixed(data);
- var tmp = testing.tmpDir(.{ .no_follow = true });
+ var tmp = testing.tmpDir(.{ .follow_symlinks = false });
//defer tmp.cleanup();
pipeToFileSystem(tmp.dir, &reader, .{