diff --git a/apps/Cat/Cat.v3 b/apps/Cat/Cat.v3 new file mode 100644 index 000000000..61bc28189 --- /dev/null +++ b/apps/Cat/Cat.v3 @@ -0,0 +1,16 @@ +// Copyright 2022 Ben L. Titzer. All rights reserved. +// See LICENSE for details of Apache 2.0 license. + +def main(args: Array) -> int { + for (i = 0; i < args.length; i++) { + var data = System.fileLoad(args[i]); + if (data == null) { + System.puts("cat: "); + System.puts(args[i]); + System.puts(": no such file or directory\n"); + } else { + System.puts(data); + } + } + return 0; +} diff --git a/apps/Cat/DEPS b/apps/Cat/DEPS new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/apps/Cat/DEPS @@ -0,0 +1 @@ + diff --git a/apps/Cat/foo.txt b/apps/Cat/foo.txt new file mode 100644 index 000000000..091744b9c --- /dev/null +++ b/apps/Cat/foo.txt @@ -0,0 +1,2 @@ +Foobartext +Foobartext2 diff --git a/rt/wasm-wasi1/System.v3 b/rt/wasm-wasi1/System.v3 index c58e068c4..a53a7ab8b 100644 --- a/rt/wasm-wasi1/System.v3 +++ b/rt/wasm-wasi1/System.v3 @@ -12,7 +12,11 @@ component System { } // open a file def fileOpen(path: string, read: bool) -> int { - var r = wasi_snapshot_preview1.path_open(3, 0, Pointer.atContents(path), path.length, 0, 0, 0, 0, Pointer.atContents(retbuf)); + var pre_fd = WasiPreopens.find(path); + if (pre_fd < 0) return -1; + var rights = (1 << if(read, wasi_rights.fd_read, wasi_rights.fd_write).tag) | + (1 << wasi_rights.fd_seek.tag); + var r = wasi_snapshot_preview1.path_open(pre_fd, 0, Pointer.atContents(path), path.length, 0, rights, rights, 0, Pointer.atContents(retbuf)); if (r != 0) return -1; return Pointer.atContents(retbuf).load(); } @@ -47,13 +51,14 @@ component System { def fileLoad(path: string) -> Array { var fd = fileOpen(path, true); if (fd < 0) return null; - var r = wasi_snapshot_preview1.fd_seek(fd, 0, wasi_whence.END.tag, Pointer.atContents(retbuf)); + var bufptr = aligned_sizeptr(); + var r = wasi_snapshot_preview1.fd_seek(fd, 0, wasi_whence.END.tag, bufptr); if (r < 0) { wasi_snapshot_preview1.fd_close(fd); return null; } - var len = int.!(Pointer.atContents(retbuf).load()); - r = wasi_snapshot_preview1.fd_seek(fd, 0, wasi_whence.SET.tag, Pointer.atContents(retbuf)); + var len = int.!(bufptr.load()); + r = wasi_snapshot_preview1.fd_seek(fd, 0, wasi_whence.SET.tag, bufptr); if (r < 0) { wasi_snapshot_preview1.fd_close(fd); return null; @@ -134,6 +139,7 @@ component System { def STDIN = 0; def STDOUT = 1; def STDERR = 2; +def MAX_PREOPEN = 10; // @thread-local @lazy buffer for write integers and chars to System.out def numbuf = Array.new(16); @@ -164,3 +170,8 @@ def fs_read(fd: int, data: Pointer, len: int) -> int { var r = wasi_snapshot_preview1.fd_read(fd, p, 1, sizeptr); return if(r == 0, sizeptr.load()); } +def aligned_sizeptr() -> Pointer { + var addr = Pointer.atContents(retbuf) - Pointer.NULL; + addr = (addr + 7) & ~7; + return Pointer.NULL + addr; +} \ No newline at end of file diff --git a/rt/wasm-wasi1/WasiPreopens.v3 b/rt/wasm-wasi1/WasiPreopens.v3 new file mode 100644 index 000000000..9137bbafc --- /dev/null +++ b/rt/wasm-wasi1/WasiPreopens.v3 @@ -0,0 +1,50 @@ +// Copyright 2024 Virgil authors. All rights reserved. +// See LICENSE for details of Apache 2.0 license. + +def FIRST_PREOPEN = 3; +def MAX_PREOPENS = 10; +var preopenCount = -1; +var relativeFd = -1; +var preopens = Array<(string, int)>.new(MAX_PREOPENS); +def buf = Array.new(2); + +// Scans the preopens and populates the array of (name, fd) pairs. +def init() { + preopenCount = 0; + for (i = FIRST_PREOPEN; i < (FIRST_PREOPEN + MAX_PREOPENS); i++) { + var r = wasi_snapshot_preview1.fd_prestat_get(i, Pointer.atContents(buf)); + if (r != 0) break; + var len = buf[1]; + if (len == 0) break; + var name = Array.new(len); + r = wasi_snapshot_preview1.fd_prestat_dir_name(i, Pointer.atContents(name), name.length); + if (r != 0) break; + preopens[preopenCount++] = (name, i); + if (len == 1 && name[0] == '.') relativeFd = i; + } +} + +// Lazily scans WASI pre-opened directories and matches paths to preopens. +component WasiPreopens { + def find(path: string) -> int { + if (path.length == 0) return -1; + if (preopenCount < 0) init(); + // If the path is not absolute and there is a '.' preopened, use that. + if (path[0] != '/' && relativeFd >= 0) return relativeFd; + // Search for an absolute or relative match + for (i < preopenCount) { + var t = preopens[i]; + if (checkPrefix(t.0, path)) return t.1; + } + return -1; + } +} +def checkPrefix(prefix: string, path: string) -> bool { + if (prefix.length > path.length) return false; + for (i < prefix.length) { + if (path[i] != prefix[i]) return false; + } + if (prefix[prefix.length - 1] == '/') return true; + if (path.length > prefix.length && path[prefix.length] == '/') return true; + return false; +}