mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
std.Io.Threaded: implement dirMake for WASI
This commit is contained in:
parent
ec9dfc540b
commit
143127529b
@ -883,6 +883,10 @@ pub const Timestamp = struct {
|
||||
return .{ .nanoseconds = t.nanoseconds, .clock = clock };
|
||||
}
|
||||
|
||||
pub fn fromNanoseconds(x: i96) Timestamp {
|
||||
return .{ .nanoseconds = x };
|
||||
}
|
||||
|
||||
pub fn toSeconds(t: Timestamp) i64 {
|
||||
return @intCast(@divTrunc(t.nanoseconds, std.time.ns_per_s));
|
||||
}
|
||||
|
||||
@ -167,7 +167,7 @@ pub fn io(t: *Threaded) Io {
|
||||
|
||||
.dirMake = switch (builtin.os.tag) {
|
||||
.windows => @panic("TODO"),
|
||||
.wasi => @panic("TODO"),
|
||||
.wasi => dirMakeWasi,
|
||||
else => dirMakePosix,
|
||||
},
|
||||
.dirStat = dirStat,
|
||||
@ -906,6 +906,37 @@ fn dirMakePosix(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode:
|
||||
}
|
||||
}
|
||||
|
||||
fn dirMakeWasi(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void {
|
||||
if (builtin.link_libc) return dirMakePosix(userdata, dir, sub_path, mode);
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
while (true) {
|
||||
try t.checkCancel();
|
||||
switch (std.os.wasi.path_create_directory(dir.handle, sub_path.ptr, sub_path.len)) {
|
||||
.SUCCESS => return,
|
||||
.INTR => continue,
|
||||
.CANCELED => return error.Canceled,
|
||||
|
||||
.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,
|
||||
.NOTCAPABLE => return error.AccessDenied,
|
||||
.ILSEQ => return error.BadPathName,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dirStat(userdata: ?*anyopaque, dir: Io.Dir) Io.Dir.StatError!Io.Dir.Stat {
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
try t.checkCancel();
|
||||
@ -1005,13 +1036,13 @@ fn dirStatPathWasi(
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
const wasi = std.os.wasi;
|
||||
const flags: wasi.lookupflags_t = .{
|
||||
.SYMLINK_FOLLOW = @intFromBool(options.follow_symlinks),
|
||||
.SYMLINK_FOLLOW = options.follow_symlinks,
|
||||
};
|
||||
var stat: wasi.filestat_t = undefined;
|
||||
while (true) {
|
||||
try t.checkCancel();
|
||||
switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) {
|
||||
.SUCCESS => return statFromWasi(stat),
|
||||
.SUCCESS => return statFromWasi(&stat),
|
||||
.INTR => continue,
|
||||
.CANCELED => return error.Canceled,
|
||||
|
||||
@ -1166,19 +1197,19 @@ fn dirAccessWasi(
|
||||
userdata: ?*anyopaque,
|
||||
dir: Io.Dir,
|
||||
sub_path: []const u8,
|
||||
options: Io.File.OpenFlags,
|
||||
) Io.File.AccessError!void {
|
||||
options: Io.Dir.AccessOptions,
|
||||
) Io.Dir.AccessError!void {
|
||||
if (builtin.link_libc) return dirAccessPosix(userdata, dir, sub_path, options);
|
||||
const t: *Threaded = @ptrCast(@alignCast(userdata));
|
||||
const wasi = std.os.wasi;
|
||||
const flags: wasi.lookupflags_t = .{
|
||||
.SYMLINK_FOLLOW = @intFromBool(options.follow_symlinks),
|
||||
.SYMLINK_FOLLOW = options.follow_symlinks,
|
||||
};
|
||||
const stat = while (true) {
|
||||
var stat: wasi.filestat_t = undefined;
|
||||
var stat: wasi.filestat_t = undefined;
|
||||
while (true) {
|
||||
try t.checkCancel();
|
||||
switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) {
|
||||
.SUCCESS => break statFromWasi(stat),
|
||||
.SUCCESS => break,
|
||||
.INTR => continue,
|
||||
.CANCELED => return error.Canceled,
|
||||
|
||||
@ -1194,9 +1225,9 @@ fn dirAccessWasi(
|
||||
.ILSEQ => return error.BadPathName,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (!options.mode.read and !options.mode.write and !options.mode.execute)
|
||||
if (!options.read and !options.write and !options.execute)
|
||||
return;
|
||||
|
||||
var directory: wasi.fdstat_t = undefined;
|
||||
@ -1204,14 +1235,14 @@ fn dirAccessWasi(
|
||||
return error.AccessDenied;
|
||||
|
||||
var rights: wasi.rights_t = .{};
|
||||
if (options.mode.read) {
|
||||
if (options.read) {
|
||||
if (stat.filetype == .DIRECTORY) {
|
||||
rights.FD_READDIR = true;
|
||||
} else {
|
||||
rights.FD_READ = true;
|
||||
}
|
||||
}
|
||||
if (options.mode.write)
|
||||
if (options.write)
|
||||
rights.FD_WRITE = true;
|
||||
|
||||
// No validation for execution.
|
||||
@ -3262,9 +3293,9 @@ fn statFromWasi(st: *const std.os.wasi.filestat_t) Io.File.Stat {
|
||||
.SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket,
|
||||
else => .unknown,
|
||||
},
|
||||
.atime = st.atim,
|
||||
.mtime = st.mtim,
|
||||
.ctime = st.ctim,
|
||||
.atime = .fromNanoseconds(st.atim),
|
||||
.mtime = .fromNanoseconds(st.mtim),
|
||||
.ctime = .fromNanoseconds(st.ctim),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -201,7 +201,13 @@ pub fn getFdPath(fd: std.posix.fd_t, out_buffer: *[max_path_bytes]u8) std.posix.
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fstat_wasi(fd: posix.fd_t) posix.FStatError!wasi.filestat_t {
|
||||
pub const FstatError = error{
|
||||
SystemResources,
|
||||
AccessDenied,
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn fstat_wasi(fd: posix.fd_t) FstatError!wasi.filestat_t {
|
||||
var stat: wasi.filestat_t = undefined;
|
||||
switch (wasi.fd_filestat_get(fd, &stat)) {
|
||||
.SUCCESS => return stat,
|
||||
|
||||
@ -2809,37 +2809,13 @@ pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: mode_t) MakeDirErro
|
||||
const sub_dir_path_w = try windows.sliceToPrefixedFileW(dir_fd, sub_dir_path);
|
||||
return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
|
||||
} else if (native_os == .wasi and !builtin.link_libc) {
|
||||
return mkdiratWasi(dir_fd, sub_dir_path, mode);
|
||||
@compileError("use std.Io instead");
|
||||
} else {
|
||||
const sub_dir_path_c = try toPosixPath(sub_dir_path);
|
||||
return mkdiratZ(dir_fd, &sub_dir_path_c, mode);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: mode_t) MakeDirError!void {
|
||||
_ = mode;
|
||||
switch (wasi.path_create_directory(dir_fd, sub_dir_path.ptr, sub_dir_path.len)) {
|
||||
.SUCCESS => return,
|
||||
.ACCES => return error.AccessDenied,
|
||||
.BADF => unreachable,
|
||||
.PERM => return error.PermissionDenied,
|
||||
.DQUOT => return error.DiskQuota,
|
||||
.EXIST => return error.PathAlreadyExists,
|
||||
.FAULT => unreachable,
|
||||
.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,
|
||||
.NOTCAPABLE => return error.AccessDenied,
|
||||
.ILSEQ => return error.BadPathName,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `mkdirat` except the parameters are null-terminated.
|
||||
pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: mode_t) MakeDirError!void {
|
||||
if (native_os == .windows) {
|
||||
|
||||
@ -109,64 +109,6 @@ test "open smoke test" {
|
||||
}
|
||||
}
|
||||
|
||||
test "openat smoke test" {
|
||||
if (native_os == .windows) return error.SkipZigTest;
|
||||
|
||||
// TODO verify file attributes using `fstatat`
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
var fd: posix.fd_t = undefined;
|
||||
const mode: posix.mode_t = if (native_os == .windows) 0 else 0o666;
|
||||
|
||||
// Create some file using `openat`.
|
||||
fd = try posix.openat(tmp.dir.fd, "some_file", CommonOpenFlags.lower(.{
|
||||
.ACCMODE = .RDWR,
|
||||
.CREAT = true,
|
||||
.EXCL = true,
|
||||
}), mode);
|
||||
posix.close(fd);
|
||||
|
||||
// Try this again with the same flags. This op should fail with error.PathAlreadyExists.
|
||||
try expectError(error.PathAlreadyExists, posix.openat(tmp.dir.fd, "some_file", CommonOpenFlags.lower(.{
|
||||
.ACCMODE = .RDWR,
|
||||
.CREAT = true,
|
||||
.EXCL = true,
|
||||
}), mode));
|
||||
|
||||
// Try opening without `EXCL` flag.
|
||||
fd = try posix.openat(tmp.dir.fd, "some_file", CommonOpenFlags.lower(.{
|
||||
.ACCMODE = .RDWR,
|
||||
.CREAT = true,
|
||||
}), mode);
|
||||
posix.close(fd);
|
||||
|
||||
// Try opening as a directory which should fail.
|
||||
try expectError(error.NotDir, posix.openat(tmp.dir.fd, "some_file", CommonOpenFlags.lower(.{
|
||||
.ACCMODE = .RDWR,
|
||||
.DIRECTORY = true,
|
||||
}), mode));
|
||||
|
||||
// Create some directory
|
||||
try posix.mkdirat(tmp.dir.fd, "some_dir", mode);
|
||||
|
||||
// Open dir using `open`
|
||||
fd = try posix.openat(tmp.dir.fd, "some_dir", CommonOpenFlags.lower(.{
|
||||
.ACCMODE = .RDONLY,
|
||||
.DIRECTORY = true,
|
||||
}), mode);
|
||||
posix.close(fd);
|
||||
|
||||
// Try opening as file which should fail (skip on wasi+libc due to
|
||||
// https://github.com/bytecodealliance/wasmtime/issues/9054)
|
||||
if (native_os != .wasi or !builtin.link_libc) {
|
||||
try expectError(error.IsDir, posix.openat(tmp.dir.fd, "some_dir", CommonOpenFlags.lower(.{
|
||||
.ACCMODE = .RDWR,
|
||||
}), mode));
|
||||
}
|
||||
}
|
||||
|
||||
test "readlink on Windows" {
|
||||
if (native_os != .windows) return error.SkipZigTest;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user