Implement std.fs.Dir.openFileWasi

It seems that `std.os.openZ` is too POSIX-specific, so I think it
should not be a point of entry for WASI `open` call. I figure
WASI should be treated as a separate "os" that's _not_ POSIX
especially given the incoming changes in the ephemeral snapshot.
This commit is contained in:
Jakub Konka 2020-05-01 12:33:11 +02:00
parent d7ca220121
commit 8e1cd69717
2 changed files with 79 additions and 25 deletions

View file

@ -37,7 +37,7 @@ pub const Watch = @import("fs/watch.zig").Watch;
/// fit into a UTF-8 encoded array of this length. /// fit into a UTF-8 encoded array of this length.
/// The byte count includes room for a null sentinel byte. /// The byte count includes room for a null sentinel byte.
pub const MAX_PATH_BYTES = switch (builtin.os.tag) { pub const MAX_PATH_BYTES = switch (builtin.os.tag) {
.linux, .macosx, .ios, .freebsd, .netbsd, .dragonfly => os.PATH_MAX, .linux, .macosx, .ios, .freebsd, .netbsd, .dragonfly, .wasi => os.PATH_MAX,
// Each UTF-16LE character may be expanded to 3 UTF-8 bytes. // Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
// If it would require 4 UTF-8 bytes, then there would be a surrogate // If it would require 4 UTF-8 bytes, then there would be a surrogate
// pair in the UTF-16LE, and we (over)account 3 bytes for it that way. // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.
@ -584,10 +584,33 @@ pub const Dir = struct {
const path_w = try os.windows.sliceToPrefixedFileW(sub_path); const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.openFileW(path_w.span(), flags); return self.openFileW(path_w.span(), flags);
} }
if (builtin.os.tag == .wasi) {
return self.openFileWasi(sub_path, flags);
}
const path_c = try os.toPosixPath(sub_path); const path_c = try os.toPosixPath(sub_path);
return self.openFileZ(&path_c, flags); return self.openFileZ(&path_c, flags);
} }
pub fn openFileWasi(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
var fdflags: wasi.fdflag_t = 0x0;
var rights: wasi.rights_t = 0x0;
if (flags.read) {
rights |= wasi.FD_READ | wasi.FD_TELL | wasi.FD_FILESTAT_GET;
}
if (flags.write) {
fdflags |= wasi.FDFLAG_APPEND;
rights |= wasi.FD_WRITE | wasi.FD_DATASYNC | wasi.FD_SEEK | wasi.FD_FDSTAT_SET_FLAGS | wasi.FD_SYNC | wasi.FD_ALLOCATE | wasi.FD_ADVISE | wasi.FD_FILESTAT_SET_TIMES | wasi.FD_FILESTAT_SET_SIZE;
}
const fd = try os.openatWasi(self.fd, sub_path, 0x0, fdflags, rights);
return File{
.handle = fd,
.io_mode = .blocking,
.async_block_allowed = File.async_block_allowed_no,
};
}
pub const openFileC = @compileError("deprecated: renamed to openFileZ"); pub const openFileC = @compileError("deprecated: renamed to openFileZ");
/// Same as `openFile` but the path parameter is null-terminated. /// Same as `openFile` but the path parameter is null-terminated.
@ -671,12 +694,37 @@ pub const Dir = struct {
const path_w = try os.windows.sliceToPrefixedFileW(sub_path); const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.createFileW(path_w.span(), flags); return self.createFileW(path_w.span(), flags);
} }
if (builtin.os.tag == .wasi) {
return self.createFileWasi(sub_path, flags);
}
const path_c = try os.toPosixPath(sub_path); const path_c = try os.toPosixPath(sub_path);
return self.createFileZ(&path_c, flags); return self.createFileZ(&path_c, flags);
} }
pub const createFileC = @compileError("deprecated: renamed to createFileZ"); pub const createFileC = @compileError("deprecated: renamed to createFileZ");
pub fn createFileWasi(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
var oflags: wasi.oflags_t = 0x0;
var rights: wasi.rights_t = wasi.FD_WRITE | wasi.FD_DATASYNC | wasi.FD_SEEK | wasi.FD_FDSTAT_SET_FLAGS | wasi.FD_SYNC | wasi.FD_ALLOCATE | wasi.FD_ADVISE | wasi.FD_FILESTAT_SET_TIMES | wasi.FD_FILESTAT_SET_SIZE;
if (flags.read) {
rights |= wasi.FD_READ | wasi.FD_TELL | wasi.FD_FILESTAT_GET;
}
if (flags.truncate) {
oflags |= wasi.O_TRUNC;
}
if (flags.exclusive) {
oflags |= wasi.O_EXCL;
}
const fd = try os.openatWasi(self.fd, sub_path, oflags, 0x0, rights);
return File{
.handle = fd,
.io_mode = .blocking,
.async_block_allowed = File.async_block_allowed_no,
};
}
/// Same as `createFile` but the path parameter is null-terminated. /// Same as `createFile` but the path parameter is null-terminated.
pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File { pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File {
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {

View file

@ -869,6 +869,26 @@ pub fn open(file_path: []const u8, flags: u32, perm: mode_t) OpenError!fd_t {
return openZ(&file_path_c, flags, perm); return openZ(&file_path_c, flags, perm);
} }
pub fn openWasi(file_path: []const u8, oflags: wasi.oflags_t, fdflags: wasi.fdflags_t, rights: wasi.rights_t) OpenError!fd_t {
var dirfd: fd_t = undefined;
var prefix: usize = undefined;
switch (wasi.resolve_preopen(file_path, &dirfd, &prefix)) {
0 => {},
else => |err| return unexpectedErrno(err),
}
const rel_path = file_path[prefix + 1 ..];
var fd: fd_t = undefined;
switch (wasi.path_open(dirfd, 0x0, rel_path.ptr, rel_path.len, oflags, rights, 0x0, fdflags, &fd)) {
0 => {},
// TODO map errors
else => |err| return unexpectedErrno(err),
}
return fd;
}
pub const openC = @compileError("deprecated: renamed to openZ"); pub const openC = @compileError("deprecated: renamed to openZ");
/// Open and possibly create a file. Keeps trying if it gets interrupted. /// Open and possibly create a file. Keeps trying if it gets interrupted.
@ -879,30 +899,6 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t
return openW(file_path_w.span(), flags, perm); return openW(file_path_w.span(), flags, perm);
} }
while (true) { while (true) {
if (builtin.os.tag == .wasi and !builtin.link_libc) {
var dirfd: fd_t = undefined;
var prefix: usize = undefined;
const path = mem.span(file_path);
switch (wasi.resolve_preopen(path, &dirfd, &prefix)) {
0 => {},
else => |err| return unexpectedErrno(err),
}
const rel_path = path[prefix + 1 ..];
// TODO map flags to wasi.oflag_t
// TODO call wasi.fd_fdstat_get to verify rights
// TODO adjust all flags to path_open
var fd: fd_t = undefined;
switch (wasi.path_open(dirfd, 0x0, rel_path.ptr, rel_path.len, wasi.O_CREAT, 0x0, 0x0, 0x0, &fd)) {
0 => {},
else => |err| return unexpectedErrno(err),
}
return fd;
}
const rc = system.open(file_path, flags, perm); const rc = system.open(file_path, flags, perm);
switch (errno(rc)) { switch (errno(rc)) {
0 => return @intCast(fd_t, rc), 0 => return @intCast(fd_t, rc),
@ -947,6 +943,16 @@ pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) Ope
return openatZ(dir_fd, &file_path_c, flags, mode); return openatZ(dir_fd, &file_path_c, flags, mode);
} }
pub fn openatWasi(dir_fd: fd_t, file_path: []const u8, oflags: wasi.oflag_t, fdflags: wasi.fdflag_t, rights: wasi.rights_t) OpenError!fd_t {
switch (wasi.path_open(dirfd, 0x0, &file_path, file_path.len, oflags, rights, 0x0, fdflags, &fd)) {
0 => {},
// TODO map errors
else => |err| return unexpectedErrno(err),
}
return fd;
}
pub const openatC = @compileError("deprecated: renamed to openatZ"); pub const openatC = @compileError("deprecated: renamed to openatZ");
/// Open and possibly create a file. Keeps trying if it gets interrupted. /// Open and possibly create a file. Keeps trying if it gets interrupted.