fs.go

  1package sys
  2
  3import (
  4	"io/fs"
  5
  6	"github.com/tetratelabs/wazero/sys"
  7)
  8
  9// FS is a writeable fs.FS bridge backed by syscall functions needed for ABI
 10// including WASI.
 11//
 12// Implementations should embed UnimplementedFS for forward compatibility. Any
 13// unsupported method or parameter should return ENO
 14//
 15// # Errors
 16//
 17// All methods that can return an error return a Errno, which is zero
 18// on success.
 19//
 20// Restricting to Errno matches current WebAssembly host functions,
 21// which are constrained to well-known error codes. For example, WASI maps syscall
 22// errors to u32 numeric values.
 23//
 24// # Notes
 25//
 26// A writable filesystem abstraction is not yet implemented as of Go 1.20. See
 27// https://github.com/golang/go/issues/45757
 28type FS interface {
 29	// OpenFile opens a file. It should be closed via Close on File.
 30	//
 31	// # Errors
 32	//
 33	// A zero Errno is success. The below are expected otherwise:
 34	//   - ENOSYS: the implementation does not support this function.
 35	//   - EINVAL: `path` or `flag` is invalid.
 36	//   - EISDIR: the path was a directory, but flag included O_RDWR or
 37	//     O_WRONLY
 38	//   - ENOENT: `path` doesn't exist and `flag` doesn't contain O_CREAT.
 39	//
 40	// # Constraints on the returned file
 41	//
 42	// Implementations that can read flags should enforce them regardless of
 43	// the type returned. For example, while os.File implements io.Writer,
 44	// attempts to write to a directory or a file opened with O_RDONLY fail
 45	// with a EBADF.
 46	//
 47	// Some implementations choose whether to enforce read-only opens, namely
 48	// fs.FS. While fs.FS is supported (Adapt), wazero cannot runtime enforce
 49	// open flags. Instead, we encourage good behavior and test our built-in
 50	// implementations.
 51	//
 52	// # Notes
 53	//
 54	//   - This is like os.OpenFile, except the path is relative to this file
 55	//     system, and Errno is returned instead of os.PathError.
 56	//   - Implications of permissions when O_CREAT are described in Chmod notes.
 57	//   - This is like `open` in POSIX. See
 58	//     https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html
 59	OpenFile(path string, flag Oflag, perm fs.FileMode) (File, Errno)
 60
 61	// Lstat gets file status without following symbolic links.
 62	//
 63	// # Errors
 64	//
 65	// A zero Errno is success. The below are expected otherwise:
 66	//   - ENOSYS: the implementation does not support this function.
 67	//   - ENOENT: `path` doesn't exist.
 68	//
 69	// # Notes
 70	//
 71	//   - This is like syscall.Lstat, except the `path` is relative to this
 72	//     file system.
 73	//   - This is like `lstat` in POSIX. See
 74	//     https://pubs.opengroup.org/onlinepubs/9699919799/functions/lstat.html
 75	//   - An fs.FileInfo backed implementation sets atim, mtim and ctim to the
 76	//     same value.
 77	//   - When the path is a symbolic link, the stat returned is for the link,
 78	//     not the file it refers to.
 79	Lstat(path string) (sys.Stat_t, Errno)
 80
 81	// Stat gets file status.
 82	//
 83	// # Errors
 84	//
 85	// A zero Errno is success. The below are expected otherwise:
 86	//   - ENOSYS: the implementation does not support this function.
 87	//   - ENOENT: `path` doesn't exist.
 88	//
 89	// # Notes
 90	//
 91	//   - This is like syscall.Stat, except the `path` is relative to this
 92	//     file system.
 93	//   - This is like `stat` in POSIX. See
 94	//     https://pubs.opengroup.org/onlinepubs/9699919799/functions/stat.html
 95	//   - An fs.FileInfo backed implementation sets atim, mtim and ctim to the
 96	//     same value.
 97	//   - When the path is a symbolic link, the stat returned is for the file
 98	//     it refers to.
 99	Stat(path string) (sys.Stat_t, Errno)
100
101	// Mkdir makes a directory.
102	//
103	// # Errors
104	//
105	// A zero Errno is success. The below are expected otherwise:
106	//   - ENOSYS: the implementation does not support this function.
107	//   - EINVAL: `path` is invalid.
108	//   - EEXIST: `path` exists and is a directory.
109	//   - ENOTDIR: `path` exists and is a file.
110	//
111	// # Notes
112	//
113	//   - This is like syscall.Mkdir, except the `path` is relative to this
114	//     file system.
115	//   - This is like `mkdir` in POSIX. See
116	//     https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html
117	//   - Implications of permissions are described in Chmod notes.
118	Mkdir(path string, perm fs.FileMode) Errno
119
120	// Chmod changes the mode of the file.
121	//
122	// # Errors
123	//
124	// A zero Errno is success. The below are expected otherwise:
125	//   - ENOSYS: the implementation does not support this function.
126	//   - EINVAL: `path` is invalid.
127	//   - ENOENT: `path` does not exist.
128	//
129	// # Notes
130	//
131	//   - This is like syscall.Chmod, except the `path` is relative to this
132	//     file system.
133	//   - This is like `chmod` in POSIX. See
134	//     https://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html
135	//   - Windows ignores the execute bit, and any permissions come back as
136	//     group and world. For example, chmod of 0400 reads back as 0444, and
137	//     0700 0666. Also, permissions on directories aren't supported at all.
138	Chmod(path string, perm fs.FileMode) Errno
139
140	// Rename renames file or directory.
141	//
142	// # Errors
143	//
144	// A zero Errno is success. The below are expected otherwise:
145	//   - ENOSYS: the implementation does not support this function.
146	//   - EINVAL: `from` or `to` is invalid.
147	//   - ENOENT: `from` or `to` don't exist.
148	//   - ENOTDIR: `from` is a directory and `to` exists as a file.
149	//   - EISDIR: `from` is a file and `to` exists as a directory.
150	//   - ENOTEMPTY: `both from` and `to` are existing directory, but
151	//    `to` is not empty.
152	//
153	// # Notes
154	//
155	//   - This is like syscall.Rename, except the paths are relative to this
156	//     file system.
157	//   - This is like `rename` in POSIX. See
158	//     https://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html
159	//   -  Windows doesn't let you overwrite an existing directory.
160	Rename(from, to string) Errno
161
162	// Rmdir removes a directory.
163	//
164	// # Errors
165	//
166	// A zero Errno is success. The below are expected otherwise:
167	//   - ENOSYS: the implementation does not support this function.
168	//   - EINVAL: `path` is invalid.
169	//   - ENOENT: `path` doesn't exist.
170	//   - ENOTDIR: `path` exists, but isn't a directory.
171	//   - ENOTEMPTY: `path` exists, but isn't empty.
172	//
173	// # Notes
174	//
175	//   - This is like syscall.Rmdir, except the `path` is relative to this
176	//     file system.
177	//   - This is like `rmdir` in POSIX. See
178	//     https://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html
179	//   - As of Go 1.19, Windows maps ENOTDIR to ENOENT.
180	Rmdir(path string) Errno
181
182	// Unlink removes a directory entry.
183	//
184	// # Errors
185	//
186	// A zero Errno is success. The below are expected otherwise:
187	//   - ENOSYS: the implementation does not support this function.
188	//   - EINVAL: `path` is invalid.
189	//   - ENOENT: `path` doesn't exist.
190	//   - EISDIR: `path` exists, but is a directory.
191	//
192	// # Notes
193	//
194	//   - This is like syscall.Unlink, except the `path` is relative to this
195	//     file system.
196	//   - This is like `unlink` in POSIX. See
197	//     https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html
198	//   - On Windows, syscall.Unlink doesn't delete symlink to directory unlike other platforms. Implementations might
199	//     want to combine syscall.RemoveDirectory with syscall.Unlink in order to delete such links on Windows.
200	//     See https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-removedirectorya
201	Unlink(path string) Errno
202
203	// Link creates a "hard" link from oldPath to newPath, in contrast to a
204	// soft link (via Symlink).
205	//
206	// # Errors
207	//
208	// A zero Errno is success. The below are expected otherwise:
209	//   - ENOSYS: the implementation does not support this function.
210	//   - EPERM: `oldPath` is invalid.
211	//   - ENOENT: `oldPath` doesn't exist.
212	//   - EISDIR: `newPath` exists, but is a directory.
213	//
214	// # Notes
215	//
216	//   - This is like syscall.Link, except the `oldPath` is relative to this
217	//     file system.
218	//   - This is like `link` in POSIX. See
219	//     https://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html
220	Link(oldPath, newPath string) Errno
221
222	// Symlink creates a "soft" link from oldPath to newPath, in contrast to a
223	// hard link (via Link).
224	//
225	// # Errors
226	//
227	// A zero Errno is success. The below are expected otherwise:
228	//   - ENOSYS: the implementation does not support this function.
229	//   - EPERM: `oldPath` or `newPath` is invalid.
230	//   - EEXIST: `newPath` exists.
231	//
232	// # Notes
233	//
234	//   - This is like syscall.Symlink, except the `oldPath` is relative to
235	//     this file system.
236	//   - This is like `symlink` in POSIX. See
237	//     https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlink.html
238	//   - Only `newPath` is relative to this file system and `oldPath` is kept
239	//     as-is. That is because the link is only resolved relative to the
240	//     directory when dereferencing it (e.g. ReadLink).
241	//     See https://github.com/bytecodealliance/cap-std/blob/v1.0.4/cap-std/src/fs/dir.rs#L404-L409
242	//     for how others implement this.
243	//   - Symlinks in Windows requires `SeCreateSymbolicLinkPrivilege`.
244	//     Otherwise, EPERM results.
245	//     See https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links
246	Symlink(oldPath, linkName string) Errno
247
248	// Readlink reads the contents of a symbolic link.
249	//
250	// # Errors
251	//
252	// A zero Errno is success. The below are expected otherwise:
253	//   - ENOSYS: the implementation does not support this function.
254	//   - EINVAL: `path` is invalid.
255	//
256	// # Notes
257	//
258	//   - This is like syscall.Readlink, except the path is relative to this
259	//     filesystem.
260	//   - This is like `readlink` in POSIX. See
261	//     https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html
262	//   - On Windows, the path separator is different from other platforms,
263	//     but to provide consistent results to Wasm, this normalizes to a "/"
264	//     separator.
265	Readlink(path string) (string, Errno)
266
267	// Utimens set file access and modification times on a path relative to
268	// this file system, at nanosecond precision.
269	//
270	// # Parameters
271	//
272	// If the path is a symbolic link, the target of expanding that link is
273	// updated.
274	//
275	// The `atim` and `mtim` parameters refer to access and modification time
276	// stamps as defined in sys.Stat_t. To retain one or the other, substitute
277	// it with the pseudo-timestamp UTIME_OMIT.
278	//
279	// # Errors
280	//
281	// A zero Errno is success. The below are expected otherwise:
282	//   - ENOSYS: the implementation does not support this function.
283	//   - EINVAL: `path` is invalid.
284	//   - EEXIST: `path` exists and is a directory.
285	//   - ENOTDIR: `path` exists and is a file.
286	//
287	// # Notes
288	//
289	//   - This is like syscall.UtimesNano and `utimensat` with `AT_FDCWD` in
290	//     POSIX. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html
291	Utimens(path string, atim, mtim int64) Errno
292}