]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/wasi/ext/fs.rs
Move to intra-doc links for wasi/ext/fs.rs, os_str_bytes.rs, primitive_docs.rs &...
[rust.git] / library / std / src / sys / wasi / ext / fs.rs
1 //! WASI-specific extensions to primitives in the `std::fs` module.
2
3 #![unstable(feature = "wasi_ext", issue = "none")]
4
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};
10
11 /// WASI-specific extensions to [`File`].
12 pub trait FileExt {
13     /// Reads a number of bytes starting from a given offset.
14     ///
15     /// Returns the number of bytes read.
16     ///
17     /// The offset is relative to the start of the file and thus independent
18     /// from the current cursor.
19     ///
20     /// The current file cursor is not affected by this function.
21     ///
22     /// Note that similar to [`File::read`], it is not an error to return with a
23     /// short read.
24     fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
25         let bufs = &mut [IoSliceMut::new(buf)];
26         self.read_vectored_at(bufs, offset)
27     }
28
29     /// Reads a number of bytes starting from a given offset.
30     ///
31     /// Returns the number of bytes read.
32     ///
33     /// The offset is relative to the start of the file and thus independent
34     /// from the current cursor.
35     ///
36     /// The current file cursor is not affected by this function.
37     ///
38     /// Note that similar to [`File::read_vectored`], it is not an error to
39     /// return with a short read.
40     fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize>;
41
42     /// Reads the exact number of byte required to fill `buf` from the given offset.
43     ///
44     /// The offset is relative to the start of the file and thus independent
45     /// from the current cursor.
46     ///
47     /// The current file cursor is not affected by this function.
48     ///
49     /// Similar to [`Read::read_exact`] but uses [`read_at`] instead of `read`.
50     ///
51     /// [`read_at`]: FileExt::read_at
52     ///
53     /// # Errors
54     ///
55     /// If this function encounters an error of the kind
56     /// [`ErrorKind::Interrupted`] then the error is ignored and the operation
57     /// will continue.
58     ///
59     /// If this function encounters an "end of file" before completely filling
60     /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`].
61     /// The contents of `buf` are unspecified in this case.
62     ///
63     /// If any other read error is encountered then this function immediately
64     /// returns. The contents of `buf` are unspecified in this case.
65     ///
66     /// If this function returns an error, it is unspecified how many bytes it
67     /// has read, but it will never read more than would be necessary to
68     /// completely fill the buffer.
69     #[stable(feature = "rw_exact_all_at", since = "1.33.0")]
70     fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
71         while !buf.is_empty() {
72             match self.read_at(buf, offset) {
73                 Ok(0) => break,
74                 Ok(n) => {
75                     let tmp = buf;
76                     buf = &mut tmp[n..];
77                     offset += n as u64;
78                 }
79                 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
80                 Err(e) => return Err(e),
81             }
82         }
83         if !buf.is_empty() {
84             Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
85         } else {
86             Ok(())
87         }
88     }
89
90     /// Writes a number of bytes starting from a given offset.
91     ///
92     /// Returns the number of bytes written.
93     ///
94     /// The offset is relative to the start of the file and thus independent
95     /// from the current cursor.
96     ///
97     /// The current file cursor is not affected by this function.
98     ///
99     /// When writing beyond the end of the file, the file is appropriately
100     /// extended and the intermediate bytes are initialized with the value 0.
101     ///
102     /// Note that similar to [`File::write`], it is not an error to return a
103     /// short write.
104     fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
105         let bufs = &[IoSlice::new(buf)];
106         self.write_vectored_at(bufs, offset)
107     }
108
109     /// Writes a number of bytes starting from a given offset.
110     ///
111     /// Returns the number of bytes written.
112     ///
113     /// The offset is relative to the start of the file and thus independent
114     /// from the current cursor.
115     ///
116     /// The current file cursor is not affected by this function.
117     ///
118     /// When writing beyond the end of the file, the file is appropriately
119     /// extended and the intermediate bytes are initialized with the value 0.
120     ///
121     /// Note that similar to [`File::write_vectored`], it is not an error to return a
122     /// short write.
123     fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize>;
124
125     /// Attempts to write an entire buffer starting from a given offset.
126     ///
127     /// The offset is relative to the start of the file and thus independent
128     /// from the current cursor.
129     ///
130     /// The current file cursor is not affected by this function.
131     ///
132     /// This method will continuously call [`write_at`] until there is no more data
133     /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is
134     /// returned. This method will not return until the entire buffer has been
135     /// successfully written or such an error occurs. The first error that is
136     /// not of [`ErrorKind::Interrupted`] kind generated from this method will be
137     /// returned.
138     ///
139     /// # Errors
140     ///
141     /// This function will return the first error of
142     /// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns.
143     ///
144     /// [`write_at`]: FileExt::write_at
145     #[stable(feature = "rw_exact_all_at", since = "1.33.0")]
146     fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
147         while !buf.is_empty() {
148             match self.write_at(buf, offset) {
149                 Ok(0) => {
150                     return Err(io::Error::new(
151                         io::ErrorKind::WriteZero,
152                         "failed to write whole buffer",
153                     ));
154                 }
155                 Ok(n) => {
156                     buf = &buf[n..];
157                     offset += n as u64
158                 }
159                 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
160                 Err(e) => return Err(e),
161             }
162         }
163         Ok(())
164     }
165
166     /// Returns the current position within the file.
167     ///
168     /// This corresponds to the `fd_tell` syscall and is similar to
169     /// `seek` where you offset 0 bytes from the current position.
170     fn tell(&self) -> io::Result<u64>;
171
172     /// Adjust the flags associated with this file.
173     ///
174     /// This corresponds to the `fd_fdstat_set_flags` syscall.
175     fn fdstat_set_flags(&self, flags: u16) -> io::Result<()>;
176
177     /// Adjust the rights associated with this file.
178     ///
179     /// This corresponds to the `fd_fdstat_set_rights` syscall.
180     fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()>;
181
182     /// Provide file advisory information on a file descriptor.
183     ///
184     /// This corresponds to the `fd_advise` syscall.
185     fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()>;
186
187     /// Force the allocation of space in a file.
188     ///
189     /// This corresponds to the `fd_allocate` syscall.
190     fn allocate(&self, offset: u64, len: u64) -> io::Result<()>;
191
192     /// Create a directory.
193     ///
194     /// This corresponds to the `path_create_directory` syscall.
195     fn create_directory<P: AsRef<Path>>(&self, dir: P) -> io::Result<()>;
196
197     /// Read the contents of a symbolic link.
198     ///
199     /// This corresponds to the `path_readlink` syscall.
200     fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf>;
201
202     /// Return the attributes of a file or directory.
203     ///
204     /// This corresponds to the `path_filestat_get` syscall.
205     fn metadata_at<P: AsRef<Path>>(&self, lookup_flags: u32, path: P) -> io::Result<Metadata>;
206
207     /// Unlink a file.
208     ///
209     /// This corresponds to the `path_unlink_file` syscall.
210     fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()>;
211
212     /// Remove a directory.
213     ///
214     /// This corresponds to the `path_remove_directory` syscall.
215     fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()>;
216 }
217
218 // FIXME: bind fd_fdstat_get - need to define a custom return type
219 // FIXME: bind fd_readdir - can't return `ReadDir` since we only have entry name
220 // FIXME: bind fd_filestat_set_times maybe? - on crates.io for unix
221 // FIXME: bind path_filestat_set_times maybe? - on crates.io for unix
222 // FIXME: bind poll_oneoff maybe? - probably should wait for I/O to settle
223 // FIXME: bind random_get maybe? - on crates.io for unix
224
225 impl FileExt for fs::File {
226     fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
227         self.as_inner().fd().pread(bufs, offset)
228     }
229
230     fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
231         self.as_inner().fd().pwrite(bufs, offset)
232     }
233
234     fn tell(&self) -> io::Result<u64> {
235         self.as_inner().fd().tell()
236     }
237
238     fn fdstat_set_flags(&self, flags: u16) -> io::Result<()> {
239         self.as_inner().fd().set_flags(flags)
240     }
241
242     fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()> {
243         self.as_inner().fd().set_rights(rights, inheriting)
244     }
245
246     fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> {
247         self.as_inner().fd().advise(offset, len, advice)
248     }
249
250     fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
251         self.as_inner().fd().allocate(offset, len)
252     }
253
254     fn create_directory<P: AsRef<Path>>(&self, dir: P) -> io::Result<()> {
255         self.as_inner().fd().create_directory(osstr2str(dir.as_ref().as_ref())?)
256     }
257
258     fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf> {
259         self.as_inner().read_link(path.as_ref())
260     }
261
262     fn metadata_at<P: AsRef<Path>>(&self, lookup_flags: u32, path: P) -> io::Result<Metadata> {
263         let m = self.as_inner().metadata_at(lookup_flags, path.as_ref())?;
264         Ok(FromInner::from_inner(m))
265     }
266
267     fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
268         self.as_inner().fd().unlink_file(osstr2str(path.as_ref().as_ref())?)
269     }
270
271     fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
272         self.as_inner().fd().remove_directory(osstr2str(path.as_ref().as_ref())?)
273     }
274 }
275
276 /// WASI-specific extensions to [`fs::OpenOptions`].
277 pub trait OpenOptionsExt {
278     /// Pass custom `dirflags` argument to `path_open`.
279     ///
280     /// This option configures the `dirflags` argument to the
281     /// `path_open` syscall which `OpenOptions` will eventually call. The
282     /// `dirflags` argument configures how the file is looked up, currently
283     /// primarily affecting whether symlinks are followed or not.
284     ///
285     /// By default this value is `__WASI_LOOKUP_SYMLINK_FOLLOW`, or symlinks are
286     /// followed. You can call this method with 0 to disable following symlinks
287     fn lookup_flags(&mut self, flags: u32) -> &mut Self;
288
289     /// Indicates whether `OpenOptions` must open a directory or not.
290     ///
291     /// This method will configure whether the `__WASI_O_DIRECTORY` flag is
292     /// passed when opening a file. When passed it will require that the opened
293     /// path is a directory.
294     ///
295     /// This option is by default `false`
296     fn directory(&mut self, dir: bool) -> &mut Self;
297
298     /// Indicates whether `__WASI_FDFLAG_DSYNC` is passed in the `fs_flags`
299     /// field of `path_open`.
300     ///
301     /// This option is by default `false`
302     fn dsync(&mut self, dsync: bool) -> &mut Self;
303
304     /// Indicates whether `__WASI_FDFLAG_NONBLOCK` is passed in the `fs_flags`
305     /// field of `path_open`.
306     ///
307     /// This option is by default `false`
308     fn nonblock(&mut self, nonblock: bool) -> &mut Self;
309
310     /// Indicates whether `__WASI_FDFLAG_RSYNC` is passed in the `fs_flags`
311     /// field of `path_open`.
312     ///
313     /// This option is by default `false`
314     fn rsync(&mut self, rsync: bool) -> &mut Self;
315
316     /// Indicates whether `__WASI_FDFLAG_SYNC` is passed in the `fs_flags`
317     /// field of `path_open`.
318     ///
319     /// This option is by default `false`
320     fn sync(&mut self, sync: bool) -> &mut Self;
321
322     /// Indicates the value that should be passed in for the `fs_rights_base`
323     /// parameter of `path_open`.
324     ///
325     /// This option defaults based on the `read` and `write` configuration of
326     /// this `OpenOptions` builder. If this method is called, however, the
327     /// exact mask passed in will be used instead.
328     fn fs_rights_base(&mut self, rights: u64) -> &mut Self;
329
330     /// Indicates the value that should be passed in for the
331     /// `fs_rights_inheriting` parameter of `path_open`.
332     ///
333     /// The default for this option is the same value as what will be passed
334     /// for the `fs_rights_base` parameter but if this method is called then
335     /// the specified value will be used instead.
336     fn fs_rights_inheriting(&mut self, rights: u64) -> &mut Self;
337
338     /// Open a file or directory.
339     ///
340     /// This corresponds to the `path_open` syscall.
341     fn open_at<P: AsRef<Path>>(&self, file: &File, path: P) -> io::Result<File>;
342 }
343
344 impl OpenOptionsExt for OpenOptions {
345     fn lookup_flags(&mut self, flags: u32) -> &mut OpenOptions {
346         self.as_inner_mut().lookup_flags(flags);
347         self
348     }
349
350     fn directory(&mut self, dir: bool) -> &mut OpenOptions {
351         self.as_inner_mut().directory(dir);
352         self
353     }
354
355     fn dsync(&mut self, enabled: bool) -> &mut OpenOptions {
356         self.as_inner_mut().dsync(enabled);
357         self
358     }
359
360     fn nonblock(&mut self, enabled: bool) -> &mut OpenOptions {
361         self.as_inner_mut().nonblock(enabled);
362         self
363     }
364
365     fn rsync(&mut self, enabled: bool) -> &mut OpenOptions {
366         self.as_inner_mut().rsync(enabled);
367         self
368     }
369
370     fn sync(&mut self, enabled: bool) -> &mut OpenOptions {
371         self.as_inner_mut().sync(enabled);
372         self
373     }
374
375     fn fs_rights_base(&mut self, rights: u64) -> &mut OpenOptions {
376         self.as_inner_mut().fs_rights_base(rights);
377         self
378     }
379
380     fn fs_rights_inheriting(&mut self, rights: u64) -> &mut OpenOptions {
381         self.as_inner_mut().fs_rights_inheriting(rights);
382         self
383     }
384
385     fn open_at<P: AsRef<Path>>(&self, file: &File, path: P) -> io::Result<File> {
386         let inner = file.as_inner().open_at(path.as_ref(), self.as_inner())?;
387         Ok(File::from_inner(inner))
388     }
389 }
390
391 /// WASI-specific extensions to [`fs::Metadata`].
392 pub trait MetadataExt {
393     /// Returns the `st_dev` field of the internal `filestat_t`
394     fn dev(&self) -> u64;
395     /// Returns the `st_ino` field of the internal `filestat_t`
396     fn ino(&self) -> u64;
397     /// Returns the `st_nlink` field of the internal `filestat_t`
398     fn nlink(&self) -> u64;
399     /// Returns the `st_atim` field of the internal `filestat_t`
400     fn atim(&self) -> u64;
401     /// Returns the `st_mtim` field of the internal `filestat_t`
402     fn mtim(&self) -> u64;
403     /// Returns the `st_ctim` field of the internal `filestat_t`
404     fn ctim(&self) -> u64;
405 }
406
407 impl MetadataExt for fs::Metadata {
408     fn dev(&self) -> u64 {
409         self.as_inner().as_wasi().dev
410     }
411     fn ino(&self) -> u64 {
412         self.as_inner().as_wasi().ino
413     }
414     fn nlink(&self) -> u64 {
415         self.as_inner().as_wasi().nlink
416     }
417     fn atim(&self) -> u64 {
418         self.as_inner().as_wasi().atim
419     }
420     fn mtim(&self) -> u64 {
421         self.as_inner().as_wasi().mtim
422     }
423     fn ctim(&self) -> u64 {
424         self.as_inner().as_wasi().ctim
425     }
426 }
427
428 /// WASI-specific extensions for [`FileType`].
429 ///
430 /// Adds support for special WASI file types such as block/character devices,
431 /// pipes, and sockets.
432 pub trait FileTypeExt {
433     /// Returns `true` if this file type is a block device.
434     fn is_block_device(&self) -> bool;
435     /// Returns `true` if this file type is a character device.
436     fn is_character_device(&self) -> bool;
437     /// Returns `true` if this file type is a socket datagram.
438     fn is_socket_dgram(&self) -> bool;
439     /// Returns `true` if this file type is a socket stream.
440     fn is_socket_stream(&self) -> bool;
441 }
442
443 impl FileTypeExt for fs::FileType {
444     fn is_block_device(&self) -> bool {
445         self.as_inner().bits() == wasi::FILETYPE_BLOCK_DEVICE
446     }
447     fn is_character_device(&self) -> bool {
448         self.as_inner().bits() == wasi::FILETYPE_CHARACTER_DEVICE
449     }
450     fn is_socket_dgram(&self) -> bool {
451         self.as_inner().bits() == wasi::FILETYPE_SOCKET_DGRAM
452     }
453     fn is_socket_stream(&self) -> bool {
454         self.as_inner().bits() == wasi::FILETYPE_SOCKET_STREAM
455     }
456 }
457
458 /// WASI-specific extension methods for [`fs::DirEntry`].
459 pub trait DirEntryExt {
460     /// Returns the underlying `d_ino` field of the `dirent_t`
461     fn ino(&self) -> u64;
462 }
463
464 impl DirEntryExt for fs::DirEntry {
465     fn ino(&self) -> u64 {
466         self.as_inner().ino()
467     }
468 }
469
470 /// Create a hard link.
471 ///
472 /// This corresponds to the `path_link` syscall.
473 pub fn link<P: AsRef<Path>, U: AsRef<Path>>(
474     old_fd: &File,
475     old_flags: u32,
476     old_path: P,
477     new_fd: &File,
478     new_path: U,
479 ) -> io::Result<()> {
480     old_fd.as_inner().fd().link(
481         old_flags,
482         osstr2str(old_path.as_ref().as_ref())?,
483         new_fd.as_inner().fd(),
484         osstr2str(new_path.as_ref().as_ref())?,
485     )
486 }
487
488 /// Rename a file or directory.
489 ///
490 /// This corresponds to the `path_rename` syscall.
491 pub fn rename<P: AsRef<Path>, U: AsRef<Path>>(
492     old_fd: &File,
493     old_path: P,
494     new_fd: &File,
495     new_path: U,
496 ) -> io::Result<()> {
497     old_fd.as_inner().fd().rename(
498         osstr2str(old_path.as_ref().as_ref())?,
499         new_fd.as_inner().fd(),
500         osstr2str(new_path.as_ref().as_ref())?,
501     )
502 }
503
504 /// Create a symbolic link.
505 ///
506 /// This corresponds to the `path_symlink` syscall.
507 pub fn symlink<P: AsRef<Path>, U: AsRef<Path>>(
508     old_path: P,
509     fd: &File,
510     new_path: U,
511 ) -> io::Result<()> {
512     fd.as_inner()
513         .fd()
514         .symlink(osstr2str(old_path.as_ref().as_ref())?, osstr2str(new_path.as_ref().as_ref())?)
515 }