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