1 //! WASI-specific extensions to primitives in the `std::fs` module.
3 #![unstable(feature = "wasi_ext", issue = "none")]
5 use crate::fs::{self, File, Metadata, OpenOptions};
6 use crate::io::{self, IoSlice, IoSliceMut};
7 use crate::path::{Path, PathBuf};
8 use crate::sys::fs::osstr2str;
9 use crate::sys_common::{AsInner, AsInnerMut, FromInner};
11 /// WASI-specific extensions to [`File`].
13 /// [`File`]: ../../../../std/fs/struct.File.html
15 /// Reads a number of bytes starting from a given offset.
17 /// Returns the number of bytes read.
19 /// The offset is relative to the start of the file and thus independent
20 /// from the current cursor.
22 /// The current file cursor is not affected by this function.
24 /// Note that similar to [`File::read`], it is not an error to return with a
27 /// [`File::read`]: ../../../../std/fs/struct.File.html#method.read
28 fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
29 let bufs = &mut [IoSliceMut::new(buf)];
30 self.read_vectored_at(bufs, offset)
33 /// Reads a number of bytes starting from a given offset.
35 /// Returns the number of bytes read.
37 /// The offset is relative to the start of the file and thus independent
38 /// from the current cursor.
40 /// The current file cursor is not affected by this function.
42 /// Note that similar to [`File::read_vectored`], it is not an error to
43 /// return with a short read.
45 /// [`File::read`]: ../../../../std/fs/struct.File.html#method.read_vectored
46 fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize>;
48 /// Reads the exact number of byte required to fill `buf` from the given offset.
50 /// The offset is relative to the start of the file and thus independent
51 /// from the current cursor.
53 /// The current file cursor is not affected by this function.
55 /// Similar to [`Read::read_exact`] but uses [`read_at`] instead of `read`.
57 /// [`Read::read_exact`]: ../../../../std/io/trait.Read.html#method.read_exact
58 /// [`read_at`]: #tymethod.read_at
62 /// If this function encounters an error of the kind
63 /// [`ErrorKind::Interrupted`] then the error is ignored and the operation
66 /// If this function encounters an "end of file" before completely filling
67 /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`].
68 /// The contents of `buf` are unspecified in this case.
70 /// If any other read error is encountered then this function immediately
71 /// returns. The contents of `buf` are unspecified in this case.
73 /// If this function returns an error, it is unspecified how many bytes it
74 /// has read, but it will never read more than would be necessary to
75 /// completely fill the buffer.
77 /// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted
78 /// [`ErrorKind::UnexpectedEof`]: ../../../../std/io/enum.ErrorKind.html#variant.UnexpectedEof
79 #[stable(feature = "rw_exact_all_at", since = "1.33.0")]
80 fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
81 while !buf.is_empty() {
82 match self.read_at(buf, offset) {
89 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
90 Err(e) => return Err(e),
94 Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
100 /// Writes a number of bytes starting from a given offset.
102 /// Returns the number of bytes written.
104 /// The offset is relative to the start of the file and thus independent
105 /// from the current cursor.
107 /// The current file cursor is not affected by this function.
109 /// When writing beyond the end of the file, the file is appropriately
110 /// extended and the intermediate bytes are initialized with the value 0.
112 /// Note that similar to [`File::write`], it is not an error to return a
115 /// [`File::write`]: ../../../../std/fs/struct.File.html#write.v
116 fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
117 let bufs = &[IoSlice::new(buf)];
118 self.write_vectored_at(bufs, offset)
121 /// Writes a number of bytes starting from a given offset.
123 /// Returns the number of bytes written.
125 /// The offset is relative to the start of the file and thus independent
126 /// from the current cursor.
128 /// The current file cursor is not affected by this function.
130 /// When writing beyond the end of the file, the file is appropriately
131 /// extended and the intermediate bytes are initialized with the value 0.
133 /// Note that similar to [`File::write_vectored`], it is not an error to return a
136 /// [`File::write`]: ../../../../std/fs/struct.File.html#method.write_vectored
137 fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize>;
139 /// Attempts to write an entire buffer starting from a given offset.
141 /// The offset is relative to the start of the file and thus independent
142 /// from the current cursor.
144 /// The current file cursor is not affected by this function.
146 /// This method will continuously call [`write_at`] until there is no more data
147 /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is
148 /// returned. This method will not return until the entire buffer has been
149 /// successfully written or such an error occurs. The first error that is
150 /// not of [`ErrorKind::Interrupted`] kind generated from this method will be
155 /// This function will return the first error of
156 /// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns.
158 /// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted
159 /// [`write_at`]: #tymethod.write_at
160 #[stable(feature = "rw_exact_all_at", since = "1.33.0")]
161 fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
162 while !buf.is_empty() {
163 match self.write_at(buf, offset) {
165 return Err(io::Error::new(
166 io::ErrorKind::WriteZero,
167 "failed to write whole buffer",
174 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
175 Err(e) => return Err(e),
181 /// Returns the current position within the file.
183 /// This corresponds to the `fd_tell` syscall and is similar to
184 /// `seek` where you offset 0 bytes from the current position.
185 fn tell(&self) -> io::Result<u64>;
187 /// Adjust the flags associated with this file.
189 /// This corresponds to the `fd_fdstat_set_flags` syscall.
190 fn fdstat_set_flags(&self, flags: u16) -> io::Result<()>;
192 /// Adjust the rights associated with this file.
194 /// This corresponds to the `fd_fdstat_set_rights` syscall.
195 fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()>;
197 /// Provide file advisory information on a file descriptor.
199 /// This corresponds to the `fd_advise` syscall.
200 fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()>;
202 /// Force the allocation of space in a file.
204 /// This corresponds to the `fd_allocate` syscall.
205 fn allocate(&self, offset: u64, len: u64) -> io::Result<()>;
207 /// Create a directory.
209 /// This corresponds to the `path_create_directory` syscall.
210 fn create_directory<P: AsRef<Path>>(&self, dir: P) -> io::Result<()>;
212 /// Read the contents of a symbolic link.
214 /// This corresponds to the `path_readlink` syscall.
215 fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf>;
217 /// Return the attributes of a file or directory.
219 /// This corresponds to the `path_filestat_get` syscall.
220 fn metadata_at<P: AsRef<Path>>(&self, lookup_flags: u32, path: P) -> io::Result<Metadata>;
224 /// This corresponds to the `path_unlink_file` syscall.
225 fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()>;
227 /// Remove a directory.
229 /// This corresponds to the `path_remove_directory` syscall.
230 fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()>;
233 // FIXME: bind fd_fdstat_get - need to define a custom return type
234 // FIXME: bind fd_readdir - can't return `ReadDir` since we only have entry name
235 // FIXME: bind fd_filestat_set_times maybe? - on crates.io for unix
236 // FIXME: bind path_filestat_set_times maybe? - on crates.io for unix
237 // FIXME: bind poll_oneoff maybe? - probably should wait for I/O to settle
238 // FIXME: bind random_get maybe? - on crates.io for unix
240 impl FileExt for fs::File {
241 fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
242 self.as_inner().fd().pread(bufs, offset)
245 fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
246 self.as_inner().fd().pwrite(bufs, offset)
249 fn tell(&self) -> io::Result<u64> {
250 self.as_inner().fd().tell()
253 fn fdstat_set_flags(&self, flags: u16) -> io::Result<()> {
254 self.as_inner().fd().set_flags(flags)
257 fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()> {
258 self.as_inner().fd().set_rights(rights, inheriting)
261 fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> {
262 self.as_inner().fd().advise(offset, len, advice)
265 fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
266 self.as_inner().fd().allocate(offset, len)
269 fn create_directory<P: AsRef<Path>>(&self, dir: P) -> io::Result<()> {
270 self.as_inner().fd().create_directory(osstr2str(dir.as_ref().as_ref())?)
273 fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf> {
274 self.as_inner().read_link(path.as_ref())
277 fn metadata_at<P: AsRef<Path>>(&self, lookup_flags: u32, path: P) -> io::Result<Metadata> {
278 let m = self.as_inner().metadata_at(lookup_flags, path.as_ref())?;
279 Ok(FromInner::from_inner(m))
282 fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
283 self.as_inner().fd().unlink_file(osstr2str(path.as_ref().as_ref())?)
286 fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
287 self.as_inner().fd().remove_directory(osstr2str(path.as_ref().as_ref())?)
291 /// WASI-specific extensions to [`fs::OpenOptions`].
293 /// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html
294 pub trait OpenOptionsExt {
295 /// Pass custom `dirflags` argument to `path_open`.
297 /// This option configures the `dirflags` argument to the
298 /// `path_open` syscall which `OpenOptions` will eventually call. The
299 /// `dirflags` argument configures how the file is looked up, currently
300 /// primarily affecting whether symlinks are followed or not.
302 /// By default this value is `__WASI_LOOKUP_SYMLINK_FOLLOW`, or symlinks are
303 /// followed. You can call this method with 0 to disable following symlinks
304 fn lookup_flags(&mut self, flags: u32) -> &mut Self;
306 /// Indicates whether `OpenOptions` must open a directory or not.
308 /// This method will configure whether the `__WASI_O_DIRECTORY` flag is
309 /// passed when opening a file. When passed it will require that the opened
310 /// path is a directory.
312 /// This option is by default `false`
313 fn directory(&mut self, dir: bool) -> &mut Self;
315 /// Indicates whether `__WASI_FDFLAG_DSYNC` is passed in the `fs_flags`
316 /// field of `path_open`.
318 /// This option is by default `false`
319 fn dsync(&mut self, dsync: bool) -> &mut Self;
321 /// Indicates whether `__WASI_FDFLAG_NONBLOCK` is passed in the `fs_flags`
322 /// field of `path_open`.
324 /// This option is by default `false`
325 fn nonblock(&mut self, nonblock: bool) -> &mut Self;
327 /// Indicates whether `__WASI_FDFLAG_RSYNC` is passed in the `fs_flags`
328 /// field of `path_open`.
330 /// This option is by default `false`
331 fn rsync(&mut self, rsync: bool) -> &mut Self;
333 /// Indicates whether `__WASI_FDFLAG_SYNC` is passed in the `fs_flags`
334 /// field of `path_open`.
336 /// This option is by default `false`
337 fn sync(&mut self, sync: bool) -> &mut Self;
339 /// Indicates the value that should be passed in for the `fs_rights_base`
340 /// parameter of `path_open`.
342 /// This option defaults based on the `read` and `write` configuration of
343 /// this `OpenOptions` builder. If this method is called, however, the
344 /// exact mask passed in will be used instead.
345 fn fs_rights_base(&mut self, rights: u64) -> &mut Self;
347 /// Indicates the value that should be passed in for the
348 /// `fs_rights_inheriting` parameter of `path_open`.
350 /// The default for this option is the same value as what will be passed
351 /// for the `fs_rights_base` parameter but if this method is called then
352 /// the specified value will be used instead.
353 fn fs_rights_inheriting(&mut self, rights: u64) -> &mut Self;
355 /// Open a file or directory.
357 /// This corresponds to the `path_open` syscall.
358 fn open_at<P: AsRef<Path>>(&self, file: &File, path: P) -> io::Result<File>;
361 impl OpenOptionsExt for OpenOptions {
362 fn lookup_flags(&mut self, flags: u32) -> &mut OpenOptions {
363 self.as_inner_mut().lookup_flags(flags);
367 fn directory(&mut self, dir: bool) -> &mut OpenOptions {
368 self.as_inner_mut().directory(dir);
372 fn dsync(&mut self, enabled: bool) -> &mut OpenOptions {
373 self.as_inner_mut().dsync(enabled);
377 fn nonblock(&mut self, enabled: bool) -> &mut OpenOptions {
378 self.as_inner_mut().nonblock(enabled);
382 fn rsync(&mut self, enabled: bool) -> &mut OpenOptions {
383 self.as_inner_mut().rsync(enabled);
387 fn sync(&mut self, enabled: bool) -> &mut OpenOptions {
388 self.as_inner_mut().sync(enabled);
392 fn fs_rights_base(&mut self, rights: u64) -> &mut OpenOptions {
393 self.as_inner_mut().fs_rights_base(rights);
397 fn fs_rights_inheriting(&mut self, rights: u64) -> &mut OpenOptions {
398 self.as_inner_mut().fs_rights_inheriting(rights);
402 fn open_at<P: AsRef<Path>>(&self, file: &File, path: P) -> io::Result<File> {
403 let inner = file.as_inner().open_at(path.as_ref(), self.as_inner())?;
404 Ok(File::from_inner(inner))
408 /// WASI-specific extensions to [`fs::Metadata`].
410 /// [`fs::Metadata`]: ../../../../std/fs/struct.Metadata.html
411 pub trait MetadataExt {
412 /// Returns the `st_dev` field of the internal `filestat_t`
413 fn dev(&self) -> u64;
414 /// Returns the `st_ino` field of the internal `filestat_t`
415 fn ino(&self) -> u64;
416 /// Returns the `st_nlink` field of the internal `filestat_t`
417 fn nlink(&self) -> u64;
418 /// Returns the `st_atim` field of the internal `filestat_t`
419 fn atim(&self) -> u64;
420 /// Returns the `st_mtim` field of the internal `filestat_t`
421 fn mtim(&self) -> u64;
422 /// Returns the `st_ctim` field of the internal `filestat_t`
423 fn ctim(&self) -> u64;
426 impl MetadataExt for fs::Metadata {
427 fn dev(&self) -> u64 {
428 self.as_inner().as_wasi().dev
430 fn ino(&self) -> u64 {
431 self.as_inner().as_wasi().ino
433 fn nlink(&self) -> u64 {
434 self.as_inner().as_wasi().nlink
436 fn atim(&self) -> u64 {
437 self.as_inner().as_wasi().atim
439 fn mtim(&self) -> u64 {
440 self.as_inner().as_wasi().mtim
442 fn ctim(&self) -> u64 {
443 self.as_inner().as_wasi().ctim
447 /// WASI-specific extensions for [`FileType`].
449 /// Adds support for special WASI file types such as block/character devices,
450 /// pipes, and sockets.
452 /// [`FileType`]: ../../../../std/fs/struct.FileType.html
453 pub trait FileTypeExt {
454 /// Returns `true` if this file type is a block device.
455 fn is_block_device(&self) -> bool;
456 /// Returns `true` if this file type is a character device.
457 fn is_character_device(&self) -> bool;
458 /// Returns `true` if this file type is a socket datagram.
459 fn is_socket_dgram(&self) -> bool;
460 /// Returns `true` if this file type is a socket stream.
461 fn is_socket_stream(&self) -> bool;
464 impl FileTypeExt for fs::FileType {
465 fn is_block_device(&self) -> bool {
466 self.as_inner().bits() == wasi::FILETYPE_BLOCK_DEVICE
468 fn is_character_device(&self) -> bool {
469 self.as_inner().bits() == wasi::FILETYPE_CHARACTER_DEVICE
471 fn is_socket_dgram(&self) -> bool {
472 self.as_inner().bits() == wasi::FILETYPE_SOCKET_DGRAM
474 fn is_socket_stream(&self) -> bool {
475 self.as_inner().bits() == wasi::FILETYPE_SOCKET_STREAM
479 /// WASI-specific extension methods for [`fs::DirEntry`].
481 /// [`fs::DirEntry`]: ../../../../std/fs/struct.DirEntry.html
482 pub trait DirEntryExt {
483 /// Returns the underlying `d_ino` field of the `dirent_t`
484 fn ino(&self) -> u64;
487 impl DirEntryExt for fs::DirEntry {
488 fn ino(&self) -> u64 {
489 self.as_inner().ino()
493 /// Create a hard link.
495 /// This corresponds to the `path_link` syscall.
496 pub fn link<P: AsRef<Path>, U: AsRef<Path>>(
502 ) -> io::Result<()> {
503 old_fd.as_inner().fd().link(
505 osstr2str(old_path.as_ref().as_ref())?,
506 new_fd.as_inner().fd(),
507 osstr2str(new_path.as_ref().as_ref())?,
511 /// Rename a file or directory.
513 /// This corresponds to the `path_rename` syscall.
514 pub fn rename<P: AsRef<Path>, U: AsRef<Path>>(
519 ) -> io::Result<()> {
520 old_fd.as_inner().fd().rename(
521 osstr2str(old_path.as_ref().as_ref())?,
522 new_fd.as_inner().fd(),
523 osstr2str(new_path.as_ref().as_ref())?,
527 /// Create a symbolic link.
529 /// This corresponds to the `path_symlink` syscall.
530 pub fn symlink<P: AsRef<Path>, U: AsRef<Path>>(
534 ) -> io::Result<()> {
537 .symlink(osstr2str(old_path.as_ref().as_ref())?, osstr2str(new_path.as_ref().as_ref())?)