]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/wasi/ext/fs.rs
Rollup merge of #74751 - GuillaumeGomez:cleanup-e0730, r=jyn514
[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 ///
13 /// [`File`]: ../../../../std/fs/struct.File.html
14 pub trait FileExt {
15     /// Reads a number of bytes starting from a given offset.
16     ///
17     /// Returns the number of bytes read.
18     ///
19     /// The offset is relative to the start of the file and thus independent
20     /// from the current cursor.
21     ///
22     /// The current file cursor is not affected by this function.
23     ///
24     /// Note that similar to [`File::read`], it is not an error to return with a
25     /// short read.
26     ///
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)
31     }
32
33     /// Reads a number of bytes starting from a given offset.
34     ///
35     /// Returns the number of bytes read.
36     ///
37     /// The offset is relative to the start of the file and thus independent
38     /// from the current cursor.
39     ///
40     /// The current file cursor is not affected by this function.
41     ///
42     /// Note that similar to [`File::read_vectored`], it is not an error to
43     /// return with a short read.
44     ///
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>;
47
48     /// Reads the exact number of byte required to fill `buf` from the given offset.
49     ///
50     /// The offset is relative to the start of the file and thus independent
51     /// from the current cursor.
52     ///
53     /// The current file cursor is not affected by this function.
54     ///
55     /// Similar to [`Read::read_exact`] but uses [`read_at`] instead of `read`.
56     ///
57     /// [`Read::read_exact`]: ../../../../std/io/trait.Read.html#method.read_exact
58     /// [`read_at`]: #tymethod.read_at
59     ///
60     /// # Errors
61     ///
62     /// If this function encounters an error of the kind
63     /// [`ErrorKind::Interrupted`] then the error is ignored and the operation
64     /// will continue.
65     ///
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.
69     ///
70     /// If any other read error is encountered then this function immediately
71     /// returns. The contents of `buf` are unspecified in this case.
72     ///
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.
76     ///
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) {
83                 Ok(0) => break,
84                 Ok(n) => {
85                     let tmp = buf;
86                     buf = &mut tmp[n..];
87                     offset += n as u64;
88                 }
89                 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
90                 Err(e) => return Err(e),
91             }
92         }
93         if !buf.is_empty() {
94             Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
95         } else {
96             Ok(())
97         }
98     }
99
100     /// Writes a number of bytes starting from a given offset.
101     ///
102     /// Returns the number of bytes written.
103     ///
104     /// The offset is relative to the start of the file and thus independent
105     /// from the current cursor.
106     ///
107     /// The current file cursor is not affected by this function.
108     ///
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.
111     ///
112     /// Note that similar to [`File::write`], it is not an error to return a
113     /// short write.
114     ///
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)
119     }
120
121     /// Writes a number of bytes starting from a given offset.
122     ///
123     /// Returns the number of bytes written.
124     ///
125     /// The offset is relative to the start of the file and thus independent
126     /// from the current cursor.
127     ///
128     /// The current file cursor is not affected by this function.
129     ///
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.
132     ///
133     /// Note that similar to [`File::write_vectored`], it is not an error to return a
134     /// short write.
135     ///
136     /// [`File::write`]: ../../../../std/fs/struct.File.html#method.write_vectored
137     fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize>;
138
139     /// Attempts to write an entire buffer starting from a given offset.
140     ///
141     /// The offset is relative to the start of the file and thus independent
142     /// from the current cursor.
143     ///
144     /// The current file cursor is not affected by this function.
145     ///
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
151     /// returned.
152     ///
153     /// # Errors
154     ///
155     /// This function will return the first error of
156     /// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns.
157     ///
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) {
164                 Ok(0) => {
165                     return Err(io::Error::new(
166                         io::ErrorKind::WriteZero,
167                         "failed to write whole buffer",
168                     ));
169                 }
170                 Ok(n) => {
171                     buf = &buf[n..];
172                     offset += n as u64
173                 }
174                 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
175                 Err(e) => return Err(e),
176             }
177         }
178         Ok(())
179     }
180
181     /// Returns the current position within the file.
182     ///
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>;
186
187     /// Adjust the flags associated with this file.
188     ///
189     /// This corresponds to the `fd_fdstat_set_flags` syscall.
190     fn fdstat_set_flags(&self, flags: u16) -> io::Result<()>;
191
192     /// Adjust the rights associated with this file.
193     ///
194     /// This corresponds to the `fd_fdstat_set_rights` syscall.
195     fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()>;
196
197     /// Provide file advisory information on a file descriptor.
198     ///
199     /// This corresponds to the `fd_advise` syscall.
200     fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()>;
201
202     /// Force the allocation of space in a file.
203     ///
204     /// This corresponds to the `fd_allocate` syscall.
205     fn allocate(&self, offset: u64, len: u64) -> io::Result<()>;
206
207     /// Create a directory.
208     ///
209     /// This corresponds to the `path_create_directory` syscall.
210     fn create_directory<P: AsRef<Path>>(&self, dir: P) -> io::Result<()>;
211
212     /// Read the contents of a symbolic link.
213     ///
214     /// This corresponds to the `path_readlink` syscall.
215     fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf>;
216
217     /// Return the attributes of a file or directory.
218     ///
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>;
221
222     /// Unlink a file.
223     ///
224     /// This corresponds to the `path_unlink_file` syscall.
225     fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()>;
226
227     /// Remove a directory.
228     ///
229     /// This corresponds to the `path_remove_directory` syscall.
230     fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()>;
231 }
232
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
239
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)
243     }
244
245     fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
246         self.as_inner().fd().pwrite(bufs, offset)
247     }
248
249     fn tell(&self) -> io::Result<u64> {
250         self.as_inner().fd().tell()
251     }
252
253     fn fdstat_set_flags(&self, flags: u16) -> io::Result<()> {
254         self.as_inner().fd().set_flags(flags)
255     }
256
257     fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()> {
258         self.as_inner().fd().set_rights(rights, inheriting)
259     }
260
261     fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> {
262         self.as_inner().fd().advise(offset, len, advice)
263     }
264
265     fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
266         self.as_inner().fd().allocate(offset, len)
267     }
268
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())?)
271     }
272
273     fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf> {
274         self.as_inner().read_link(path.as_ref())
275     }
276
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))
280     }
281
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())?)
284     }
285
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())?)
288     }
289 }
290
291 /// WASI-specific extensions to [`fs::OpenOptions`].
292 ///
293 /// [`fs::OpenOptions`]: ../../../../std/fs/struct.OpenOptions.html
294 pub trait OpenOptionsExt {
295     /// Pass custom `dirflags` argument to `path_open`.
296     ///
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.
301     ///
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;
305
306     /// Indicates whether `OpenOptions` must open a directory or not.
307     ///
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.
311     ///
312     /// This option is by default `false`
313     fn directory(&mut self, dir: bool) -> &mut Self;
314
315     /// Indicates whether `__WASI_FDFLAG_DSYNC` is passed in the `fs_flags`
316     /// field of `path_open`.
317     ///
318     /// This option is by default `false`
319     fn dsync(&mut self, dsync: bool) -> &mut Self;
320
321     /// Indicates whether `__WASI_FDFLAG_NONBLOCK` is passed in the `fs_flags`
322     /// field of `path_open`.
323     ///
324     /// This option is by default `false`
325     fn nonblock(&mut self, nonblock: bool) -> &mut Self;
326
327     /// Indicates whether `__WASI_FDFLAG_RSYNC` is passed in the `fs_flags`
328     /// field of `path_open`.
329     ///
330     /// This option is by default `false`
331     fn rsync(&mut self, rsync: bool) -> &mut Self;
332
333     /// Indicates whether `__WASI_FDFLAG_SYNC` is passed in the `fs_flags`
334     /// field of `path_open`.
335     ///
336     /// This option is by default `false`
337     fn sync(&mut self, sync: bool) -> &mut Self;
338
339     /// Indicates the value that should be passed in for the `fs_rights_base`
340     /// parameter of `path_open`.
341     ///
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;
346
347     /// Indicates the value that should be passed in for the
348     /// `fs_rights_inheriting` parameter of `path_open`.
349     ///
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;
354
355     /// Open a file or directory.
356     ///
357     /// This corresponds to the `path_open` syscall.
358     fn open_at<P: AsRef<Path>>(&self, file: &File, path: P) -> io::Result<File>;
359 }
360
361 impl OpenOptionsExt for OpenOptions {
362     fn lookup_flags(&mut self, flags: u32) -> &mut OpenOptions {
363         self.as_inner_mut().lookup_flags(flags);
364         self
365     }
366
367     fn directory(&mut self, dir: bool) -> &mut OpenOptions {
368         self.as_inner_mut().directory(dir);
369         self
370     }
371
372     fn dsync(&mut self, enabled: bool) -> &mut OpenOptions {
373         self.as_inner_mut().dsync(enabled);
374         self
375     }
376
377     fn nonblock(&mut self, enabled: bool) -> &mut OpenOptions {
378         self.as_inner_mut().nonblock(enabled);
379         self
380     }
381
382     fn rsync(&mut self, enabled: bool) -> &mut OpenOptions {
383         self.as_inner_mut().rsync(enabled);
384         self
385     }
386
387     fn sync(&mut self, enabled: bool) -> &mut OpenOptions {
388         self.as_inner_mut().sync(enabled);
389         self
390     }
391
392     fn fs_rights_base(&mut self, rights: u64) -> &mut OpenOptions {
393         self.as_inner_mut().fs_rights_base(rights);
394         self
395     }
396
397     fn fs_rights_inheriting(&mut self, rights: u64) -> &mut OpenOptions {
398         self.as_inner_mut().fs_rights_inheriting(rights);
399         self
400     }
401
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))
405     }
406 }
407
408 /// WASI-specific extensions to [`fs::Metadata`].
409 ///
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;
424 }
425
426 impl MetadataExt for fs::Metadata {
427     fn dev(&self) -> u64 {
428         self.as_inner().as_wasi().dev
429     }
430     fn ino(&self) -> u64 {
431         self.as_inner().as_wasi().ino
432     }
433     fn nlink(&self) -> u64 {
434         self.as_inner().as_wasi().nlink
435     }
436     fn atim(&self) -> u64 {
437         self.as_inner().as_wasi().atim
438     }
439     fn mtim(&self) -> u64 {
440         self.as_inner().as_wasi().mtim
441     }
442     fn ctim(&self) -> u64 {
443         self.as_inner().as_wasi().ctim
444     }
445 }
446
447 /// WASI-specific extensions for [`FileType`].
448 ///
449 /// Adds support for special WASI file types such as block/character devices,
450 /// pipes, and sockets.
451 ///
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;
462 }
463
464 impl FileTypeExt for fs::FileType {
465     fn is_block_device(&self) -> bool {
466         self.as_inner().bits() == wasi::FILETYPE_BLOCK_DEVICE
467     }
468     fn is_character_device(&self) -> bool {
469         self.as_inner().bits() == wasi::FILETYPE_CHARACTER_DEVICE
470     }
471     fn is_socket_dgram(&self) -> bool {
472         self.as_inner().bits() == wasi::FILETYPE_SOCKET_DGRAM
473     }
474     fn is_socket_stream(&self) -> bool {
475         self.as_inner().bits() == wasi::FILETYPE_SOCKET_STREAM
476     }
477 }
478
479 /// WASI-specific extension methods for [`fs::DirEntry`].
480 ///
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;
485 }
486
487 impl DirEntryExt for fs::DirEntry {
488     fn ino(&self) -> u64 {
489         self.as_inner().ino()
490     }
491 }
492
493 /// Create a hard link.
494 ///
495 /// This corresponds to the `path_link` syscall.
496 pub fn link<P: AsRef<Path>, U: AsRef<Path>>(
497     old_fd: &File,
498     old_flags: u32,
499     old_path: P,
500     new_fd: &File,
501     new_path: U,
502 ) -> io::Result<()> {
503     old_fd.as_inner().fd().link(
504         old_flags,
505         osstr2str(old_path.as_ref().as_ref())?,
506         new_fd.as_inner().fd(),
507         osstr2str(new_path.as_ref().as_ref())?,
508     )
509 }
510
511 /// Rename a file or directory.
512 ///
513 /// This corresponds to the `path_rename` syscall.
514 pub fn rename<P: AsRef<Path>, U: AsRef<Path>>(
515     old_fd: &File,
516     old_path: P,
517     new_fd: &File,
518     new_path: U,
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())?,
524     )
525 }
526
527 /// Create a symbolic link.
528 ///
529 /// This corresponds to the `path_symlink` syscall.
530 pub fn symlink<P: AsRef<Path>, U: AsRef<Path>>(
531     old_path: P,
532     fd: &File,
533     new_path: U,
534 ) -> io::Result<()> {
535     fd.as_inner()
536         .fd()
537         .symlink(osstr2str(old_path.as_ref().as_ref())?, osstr2str(new_path.as_ref().as_ref())?)
538 }