diff --git a/src/fd.ts b/src/fd.ts index 435685a..771fbe2 100644 --- a/src/fd.ts +++ b/src/fd.ts @@ -100,14 +100,15 @@ export abstract class Fd { ): number { return wasi.ERRNO_NOTSUP; } - path_link( - old_fd: number, - old_flags: number, - old_path: string, - new_path: string, - ): number { + path_link(path: string, inode: Inode): number { return wasi.ERRNO_NOTSUP; } + path_lookup( + path: string, + dirflags: number, + ): { ret: number; inode_obj: Inode | null } { + return { ret: wasi.ERRNO_NOTSUP, inode_obj: null }; + } path_open( dirflags: number, path: string, @@ -134,3 +135,13 @@ export abstract class Fd { return wasi.ERRNO_NOTSUP; } } + +export abstract class Inode { + abstract path_open( + oflags: number, + fs_rights_base: bigint, + fd_flags: number, + ): { ret: number; fd_obj: Fd | null }; + + abstract stat(): wasi.Filestat; +} diff --git a/src/fs_core.ts b/src/fs_core.ts index c0b54d3..fd09dc8 100644 --- a/src/fs_core.ts +++ b/src/fs_core.ts @@ -1,7 +1,6 @@ import { debug } from "./debug.js"; import * as wasi from "./wasi_defs.js"; -import { Fd } from "./fd.js"; -import { Inode } from "./inode.js"; +import { Fd, Inode } from "./fd.js"; export class OpenFile extends Fd { file: File; @@ -349,6 +348,23 @@ export class OpenDirectory extends Fd { return { ret: 0, filestat: entry.stat() }; } + path_lookup( + path_str: string, + dirflags: number, + ): { ret: number; inode_obj: Inode | null } { + let { ret: path_ret, path } = Path.from(path_str); + if (path == null) { + return { ret: path_ret, inode_obj: null }; + } + + let { ret, entry } = this.dir.get_entry_for_path(path); + if (entry == null) { + return { ret, inode_obj: null }; + } + + return { ret: wasi.ERRNO_SUCCESS, inode_obj: entry }; + } + path_open( dirflags: number, path_str: string, @@ -406,6 +422,39 @@ export class OpenDirectory extends Fd { ).ret; } + path_link(path_str: string, inode: Inode): number { + let { ret: path_ret, path } = Path.from(path_str); + if (path_str == null) { + return path_ret; + } + + if (path.is_dir) { + return wasi.ERRNO_NOENT; + } + + const { + ret: parent_ret, + parent_entry, + filename, + entry, + } = this.dir.get_parent_dir_and_entry_for_path(path, true); + if (parent_entry == null || filename == null) { + return parent_ret; + } + + if (entry != null) { + return wasi.ERRNO_EXIST; + } + + if (inode.stat().filetype == wasi.FILETYPE_DIRECTORY) { + return wasi.ERRNO_PERM; + } + + parent_entry.contents.set(filename, inode); + + return wasi.ERRNO_SUCCESS; + } + path_unlink_file(path_str: string): number { let { ret: path_ret, path } = Path.from(path_str); if (path == null) { diff --git a/src/index.ts b/src/index.ts index 5b4081c..11bb0c6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,7 @@ import WASI from "./wasi.js"; export { WASI }; -export { Fd } from "./fd.js"; -export { Inode } from "./inode.js"; +export { Fd, Inode } from "./fd.js"; export { File, SyncOPFSFile, diff --git a/src/inode.ts b/src/inode.ts deleted file mode 100644 index af04ff2..0000000 --- a/src/inode.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* eslint @typescript-eslint/no-unused-vars:0 */ -import * as wasi from "./wasi_defs.js"; -import { Fd } from "./fd.js"; - -export abstract class Inode { - abstract path_open( - oflags: number, - fs_rights_base: bigint, - fd_flags: number, - ): { ret: number; fd_obj: Fd | null }; - - abstract stat(): wasi.Filestat; -} diff --git a/src/wasi.ts b/src/wasi.ts index 04973c2..2390579 100644 --- a/src/wasi.ts +++ b/src/wasi.ts @@ -592,12 +592,14 @@ export default class WASI { const new_path = new TextDecoder("utf-8").decode( buffer8.slice(new_path_ptr, new_path_ptr + new_path_len), ); - return self.fds[new_fd].path_link( - old_fd, - old_flags, + const { ret, inode_obj } = self.fds[old_fd].path_lookup( old_path, - new_path, + old_flags, ); + if (inode_obj == null) { + return ret; + } + return self.fds[new_fd].path_link(new_path, inode_obj); } else { return wasi.ERRNO_BADF; } diff --git a/test/skip.json b/test/skip.json index 7ebf4d6..fdc8d6b 100644 --- a/test/skip.json +++ b/test/skip.json @@ -20,7 +20,6 @@ "path_link": "fail", "fd_fdstat_set_rights": "fail", "readlink": "fail", - "unlink_file_trailing_slashes": "fail", "path_symlink_trailing_slashes": "fail", "poll_oneoff_stdio": "fail", "dangling_symlink": "fail",