cap_async_std/fs/
dir.rs

1use crate::fs::{DirBuilder, File, Metadata, OpenOptions, ReadDir};
2#[cfg(target_os = "wasi")]
3use async_std::os::wasi::{
4    fs::OpenOptionsExt,
5    io::{AsRawFd, IntoRawFd},
6};
7use async_std::path::{Path, PathBuf};
8use async_std::task::spawn_blocking;
9use async_std::{fs, io};
10use cap_primitives::fs::{
11    canonicalize, copy, create_dir, hard_link, open, open_ambient_dir, open_dir, open_parent_dir,
12    read_base_dir, read_dir, read_link, remove_dir, remove_dir_all, remove_file, remove_open_dir,
13    remove_open_dir_all, rename, set_permissions, stat, DirOptions, FollowSymlinks, Permissions,
14};
15use cap_primitives::AmbientAuthority;
16use io_lifetimes::raw::{AsRawFilelike, FromRawFilelike};
17#[cfg(not(windows))]
18use io_lifetimes::{AsFd, BorrowedFd, OwnedFd};
19use io_lifetimes::{AsFilelike, FromFilelike};
20#[cfg(windows)]
21use io_lifetimes::{AsHandle, BorrowedHandle, OwnedHandle};
22use std::fmt;
23use std::mem::ManuallyDrop;
24#[cfg(unix)]
25use {
26    crate::os::unix::net::{UnixDatagram, UnixListener, UnixStream},
27    async_std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd},
28    cap_primitives::fs::symlink,
29};
30#[cfg(windows)]
31use {
32    async_std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle},
33    cap_primitives::fs::{symlink_dir, symlink_file},
34    io_extras::os::windows::{
35        AsHandleOrSocket, AsRawHandleOrSocket, BorrowedHandleOrSocket, IntoRawHandleOrSocket,
36        OwnedHandleOrSocket, RawHandleOrSocket,
37    },
38};
39
40/// A reference to an open directory on a filesystem.
41///
42/// This does not directly correspond to anything in `async_std`, however its
43/// methods correspond to the [functions in `async_std::fs`] and the
44/// constructor methods for [`async_std::fs::File`].
45///
46/// Unlike `async_std::fs`, this API's `canonicalize` returns a relative path
47/// since absolute paths don't interoperate well with the capability model.
48///
49/// [functions in `async_std::fs`]: https://docs.rs/async-std/latest/async_std/fs/index.html#functions
50#[derive(Clone)]
51pub struct Dir {
52    std_file: fs::File,
53}
54
55impl Dir {
56    /// Constructs a new instance of `Self` from the given
57    /// `async_std::fs::File`.
58    ///
59    /// To prevent race conditions on Windows, the file must be opened without
60    /// `FILE_SHARE_DELETE`.
61    ///
62    /// This grants access the resources the `async_std::fs::File` instance
63    /// already has access to.
64    #[inline]
65    pub fn from_std_file(std_file: fs::File) -> Self {
66        Self { std_file }
67    }
68
69    /// Consumes `self` and returns an `async_std::fs::File`.
70    #[inline]
71    pub fn into_std_file(self) -> fs::File {
72        self.std_file
73    }
74
75    /// Attempts to open a file in read-only mode.
76    ///
77    /// This corresponds to [`async_std::fs::File::open`], but only accesses
78    /// paths relative to `self`.
79    #[inline]
80    pub async fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
81        self.open_with(path, OpenOptions::new().read(true)).await
82    }
83
84    /// Opens a file at `path` with the options specified by `options`.
85    ///
86    /// This corresponds to [`async_std::fs::OpenOptions::open`].
87    ///
88    /// Instead of being a method on `OpenOptions`, this is a method on `Dir`,
89    /// and it only accesses paths relative to `self`.
90    #[inline]
91    pub async fn open_with<P: AsRef<Path>>(
92        &self,
93        path: P,
94        options: &OpenOptions,
95    ) -> io::Result<File> {
96        self._open_with(path.as_ref(), options).await
97    }
98
99    #[cfg(not(target_os = "wasi"))]
100    async fn _open_with(&self, path: &Path, options: &OpenOptions) -> io::Result<File> {
101        let path = path.to_path_buf();
102        let clone = self.clone();
103        let options = options.clone();
104        let file = spawn_blocking(move || {
105            open(
106                &*clone.as_filelike_view::<std::fs::File>(),
107                path.as_ref(),
108                &options,
109            )
110        })
111        .await?
112        .into();
113        Ok(File::from_std(file))
114    }
115
116    #[cfg(target_os = "wasi")]
117    async fn _open_with(
118        file: &std::fs::File,
119        path: &Path,
120        options: &OpenOptions,
121    ) -> io::Result<File> {
122        let file = options.open_at(&self.std_file, path)?.into();
123        Ok(File::from_std(file))
124    }
125
126    /// Attempts to open a directory.
127    #[inline]
128    pub async fn open_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<Self> {
129        let path = path.as_ref().to_path_buf();
130        let clone = self.clone();
131        let dir = spawn_blocking(move || {
132            open_dir(&clone.as_filelike_view::<std::fs::File>(), path.as_ref())
133        })
134        .await?
135        .into();
136        Ok(Self::from_std_file(dir))
137    }
138
139    /// Creates a new, empty directory at the provided path.
140    ///
141    /// This corresponds to [`async_std::fs::create_dir`], but only accesses
142    /// paths relative to `self`.
143    ///
144    /// TODO: async: fix this when we fix <https://github.com/bytecodealliance/cap-std/issues/51>
145    #[inline]
146    pub fn create_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
147        self._create_dir_one(path.as_ref(), &DirOptions::new())
148    }
149
150    /// Recursively create a directory and all of its parent components if they
151    /// are missing.
152    ///
153    /// This corresponds to [`async_std::fs::create_dir_all`], but only
154    /// accesses paths relative to `self`.
155    ///
156    /// TODO: async: fix this when we fix <https://github.com/bytecodealliance/cap-std/issues/51>
157    #[inline]
158    pub fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
159        self._create_dir_all(path.as_ref(), &DirOptions::new())
160    }
161
162    /// Creates the specified directory with the options configured in this
163    /// builder.
164    ///
165    /// This corresponds to [`async_std::fs::DirBuilder::create`].
166    ///
167    /// TODO: async: fix this when we fix <https://github.com/bytecodealliance/cap-std/issues/51>
168    #[inline]
169    pub fn create_dir_with<P: AsRef<Path>>(
170        &self,
171        path: P,
172        dir_builder: &DirBuilder,
173    ) -> io::Result<()> {
174        let options = dir_builder.options();
175        if dir_builder.is_recursive() {
176            self._create_dir_all(path.as_ref(), options)
177        } else {
178            self._create_dir_one(path.as_ref(), options)
179        }
180    }
181
182    #[inline]
183    fn _create_dir_one(&self, path: &Path, dir_options: &DirOptions) -> io::Result<()> {
184        create_dir(
185            &self.as_filelike_view::<std::fs::File>(),
186            path.as_ref(),
187            dir_options,
188        )
189    }
190
191    fn _create_dir_all(&self, path: &Path, dir_options: &DirOptions) -> io::Result<()> {
192        if path == Path::new("") {
193            return Ok(());
194        }
195
196        match self._create_dir_one(path, dir_options) {
197            Ok(()) => return Ok(()),
198            Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
199            Err(_) if self.is_dir_blocking(path) => return Ok(()),
200            Err(e) => return Err(e),
201        }
202        match path.parent() {
203            Some(p) => self._create_dir_all(p, dir_options)?,
204            None => {
205                return Err(io::Error::new(
206                    io::ErrorKind::Other,
207                    "failed to create whole tree",
208                ))
209            }
210        }
211        match self._create_dir_one(path, dir_options) {
212            Ok(()) => Ok(()),
213            Err(_) if self.is_dir_blocking(path) => Ok(()),
214            Err(e) => Err(e),
215        }
216    }
217
218    /// Opens a file in write-only mode.
219    ///
220    /// This corresponds to [`async_std::fs::File::create`], but only accesses
221    /// paths relative to `self`.
222    #[inline]
223    pub async fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
224        self.open_with(
225            path,
226            OpenOptions::new().write(true).create(true).truncate(true),
227        )
228        .await
229    }
230
231    /// Returns the canonical form of a path with all intermediate components
232    /// normalized and symbolic links resolved.
233    ///
234    /// This corresponds to [`async_std::fs::canonicalize`], but instead of
235    /// returning an absolute path, returns a path relative to the
236    /// directory represented by `self`.
237    #[inline]
238    pub async fn canonicalize<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf> {
239        let path = path.as_ref().to_path_buf();
240        let clone = self.clone();
241        spawn_blocking(move || {
242            canonicalize(&clone.as_filelike_view::<std::fs::File>(), path.as_ref())
243        })
244        .await
245        .map(PathBuf::from)
246    }
247
248    /// Copies the contents of one file to another. This function will also
249    /// copy the permission bits of the original file to the destination
250    /// file.
251    ///
252    /// This corresponds to [`async_std::fs::copy`], but only accesses paths
253    /// relative to `self`.
254    #[inline]
255    pub async fn copy<P: AsRef<Path>, Q: AsRef<Path>>(
256        &self,
257        from: P,
258        to_dir: &Self,
259        to: Q,
260    ) -> io::Result<u64> {
261        let from = from.as_ref().to_path_buf();
262        let to = to.as_ref().to_path_buf();
263        let from_clone = self.clone();
264        let to_clone = to_dir.clone();
265        spawn_blocking(move || {
266            copy(
267                &from_clone.as_filelike_view::<std::fs::File>(),
268                from.as_ref(),
269                &to_clone.as_filelike_view::<std::fs::File>(),
270                to.as_ref(),
271            )
272        })
273        .await
274    }
275
276    /// Creates a new hard link on a filesystem.
277    ///
278    /// This corresponds to [`async_std::fs::hard_link`], but only accesses
279    /// paths relative to `self`.
280    #[inline]
281    pub async fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(
282        &self,
283        src: P,
284        dst_dir: &Self,
285        dst: Q,
286    ) -> io::Result<()> {
287        let dst = dst.as_ref().to_path_buf();
288        let src = src.as_ref().to_path_buf();
289        let src_clone = self.clone();
290        let dst_clone = dst_dir.clone();
291        spawn_blocking(move || {
292            hard_link(
293                &src_clone.as_filelike_view::<std::fs::File>(),
294                src.as_ref(),
295                &dst_clone.as_filelike_view::<std::fs::File>(),
296                dst.as_ref(),
297            )
298        })
299        .await
300    }
301
302    /// Given a path, query the file system to get information about a file,
303    /// directory, etc.
304    ///
305    /// This corresponds to [`async_std::fs::metadata`], but only accesses
306    /// paths relative to `self`.
307    #[inline]
308    pub async fn metadata<P: AsRef<Path>>(&self, path: P) -> io::Result<Metadata> {
309        let path = path.as_ref().to_path_buf();
310        let clone = self.clone();
311        spawn_blocking(move || {
312            stat(
313                &clone.as_filelike_view::<std::fs::File>(),
314                path.as_ref(),
315                FollowSymlinks::Yes,
316            )
317        })
318        .await
319    }
320
321    /// TODO: Remove this once `create_dir` and friends are async.
322    #[inline]
323    fn metadata_blocking<P: AsRef<Path>>(&self, path: P) -> io::Result<Metadata> {
324        let path = path.as_ref().to_path_buf();
325        stat(
326            &self.as_filelike_view::<std::fs::File>(),
327            path.as_ref(),
328            FollowSymlinks::Yes,
329        )
330    }
331
332    /// Queries metadata about the underlying directory.
333    ///
334    /// This is similar to [`std::fs::File::metadata`], but for `Dir` rather
335    /// than for `File`.
336    #[inline]
337    pub async fn dir_metadata(&self) -> io::Result<Metadata> {
338        let clone = self.clone();
339        spawn_blocking(move || metadata_from(&*clone.as_filelike_view::<std::fs::File>())).await
340    }
341
342    /// Returns an iterator over the entries within `self`.
343    #[inline]
344    pub async fn entries(&self) -> io::Result<ReadDir> {
345        let clone = self.clone();
346        spawn_blocking(move || read_base_dir(&clone.as_filelike_view::<std::fs::File>()))
347            .await
348            .map(|inner| ReadDir { inner })
349    }
350
351    /// Returns an iterator over the entries within a directory.
352    ///
353    /// This corresponds to [`async_std::fs::read_dir`], but only accesses
354    /// paths relative to `self`.
355    #[inline]
356    pub async fn read_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<ReadDir> {
357        let path = path.as_ref().to_path_buf();
358        let clone = self.clone();
359        spawn_blocking(move || read_dir(&clone.as_filelike_view::<std::fs::File>(), path.as_ref()))
360            .await
361            .map(|inner| ReadDir { inner })
362    }
363
364    /// Read the entire contents of a file into a bytes vector.
365    ///
366    /// This corresponds to [`async_std::fs::read`], but only accesses paths
367    /// relative to `self`.
368    #[inline]
369    pub async fn read<P: AsRef<Path>>(&self, path: P) -> io::Result<Vec<u8>> {
370        use async_std::prelude::*;
371        let mut file = self.open(path).await?;
372        let mut bytes = Vec::with_capacity(initial_buffer_size(&file).await);
373        file.read_to_end(&mut bytes).await?;
374        Ok(bytes)
375    }
376
377    /// Reads a symbolic link, returning the file that the link points to.
378    ///
379    /// This corresponds to [`async_std::fs::read_link`], but only accesses
380    /// paths relative to `self`.
381    #[inline]
382    pub async fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf> {
383        let path = path.as_ref().to_path_buf();
384        let clone = self.clone();
385        spawn_blocking(move || read_link(&clone.as_filelike_view::<std::fs::File>(), path.as_ref()))
386            .await
387            .map(PathBuf::from)
388    }
389
390    /// Read the entire contents of a file into a string.
391    ///
392    /// This corresponds to [`async_std::fs::read_to_string`], but only
393    /// accesses paths relative to `self`.
394    #[inline]
395    pub async fn read_to_string<P: AsRef<Path>>(&self, path: P) -> io::Result<String> {
396        use async_std::prelude::*;
397        let mut s = String::new();
398        self.open(path).await?.read_to_string(&mut s).await?;
399        Ok(s)
400    }
401
402    /// Removes an empty directory.
403    ///
404    /// This corresponds to [`async_std::fs::remove_dir`], but only accesses
405    /// paths relative to `self`.
406    #[inline]
407    pub async fn remove_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
408        let path = path.as_ref().to_path_buf();
409        let clone = self.clone();
410        spawn_blocking(move || {
411            remove_dir(&clone.as_filelike_view::<std::fs::File>(), path.as_ref())
412        })
413        .await
414    }
415
416    /// Removes a directory at this path, after removing all its contents. Use
417    /// carefully!
418    ///
419    /// This corresponds to [`async_std::fs::remove_dir_all`], but only
420    /// accesses paths relative to `self`.
421    #[inline]
422    pub async fn remove_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
423        let path = path.as_ref().to_path_buf();
424        let clone = self.clone();
425        spawn_blocking(move || {
426            remove_dir_all(&clone.as_filelike_view::<std::fs::File>(), path.as_ref())
427        })
428        .await
429    }
430
431    /// Remove the directory referenced by `self` and consume `self`.
432    ///
433    /// Even though this implementation works in terms of handles as much as
434    /// possible, removal is not guaranteed to be atomic with respect to a
435    /// concurrent rename of the directory.
436    #[inline]
437    pub async fn remove_open_dir(self) -> io::Result<()> {
438        let file = std::fs::File::from_into_filelike(self.std_file);
439        spawn_blocking(move || remove_open_dir(file)).await
440    }
441
442    /// Removes the directory referenced by `self`, after removing all its
443    /// contents, and consume `self`. Use carefully!
444    ///
445    /// Even though this implementation works in terms of handles as much as
446    /// possible, removal is not guaranteed to be atomic with respect to a
447    /// concurrent rename of the directory.
448    #[inline]
449    pub async fn remove_open_dir_all(self) -> io::Result<()> {
450        let file = std::fs::File::from_into_filelike(self.std_file);
451        spawn_blocking(move || remove_open_dir_all(file)).await
452    }
453
454    /// Removes a file from a filesystem.
455    ///
456    /// This corresponds to [`async_std::fs::remove_file`], but only accesses
457    /// paths relative to `self`.
458    #[inline]
459    pub async fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
460        let path = path.as_ref().to_path_buf();
461        let clone = self.clone();
462        spawn_blocking(move || {
463            remove_file(&clone.as_filelike_view::<std::fs::File>(), path.as_ref())
464        })
465        .await
466    }
467
468    /// Rename a file or directory to a new name, replacing the original file
469    /// if to already exists.
470    ///
471    /// This corresponds to [`async_std::fs::rename`], but only accesses paths
472    /// relative to `self`.
473    #[inline]
474    pub async fn rename<P: AsRef<Path>, Q: AsRef<Path>>(
475        &self,
476        from: P,
477        to_dir: &Self,
478        to: Q,
479    ) -> io::Result<()> {
480        let from = from.as_ref().to_path_buf();
481        let to = to.as_ref().to_path_buf();
482        let clone = self.clone();
483        let to_clone = to_dir.clone();
484        spawn_blocking(move || {
485            rename(
486                &clone.as_filelike_view::<std::fs::File>(),
487                from.as_ref(),
488                &to_clone.as_filelike_view::<std::fs::File>(),
489                to.as_ref(),
490            )
491        })
492        .await
493    }
494
495    /// Changes the permissions found on a file or a directory.
496    ///
497    /// This corresponds to [`async_std::fs::set_permissions`], but only
498    /// accesses paths relative to `self`. Also, on some platforms, this
499    /// function may fail if the file or directory cannot be opened for
500    /// reading or writing first.
501    pub async fn set_permissions<P: AsRef<Path>>(
502        &self,
503        path: P,
504        perm: Permissions,
505    ) -> io::Result<()> {
506        let path = path.as_ref().to_path_buf();
507        let clone = self.clone();
508        spawn_blocking(move || {
509            set_permissions(
510                &clone.as_filelike_view::<std::fs::File>(),
511                path.as_ref(),
512                perm,
513            )
514        })
515        .await
516    }
517
518    /// Query the metadata about a file without following symlinks.
519    ///
520    /// This corresponds to [`async_std::fs::symlink_metadata`], but only
521    /// accesses paths relative to `self`.
522    #[inline]
523    pub async fn symlink_metadata<P: AsRef<Path>>(&self, path: P) -> io::Result<Metadata> {
524        let path = path.as_ref().to_path_buf();
525        let clone = self.clone();
526        spawn_blocking(move || {
527            stat(
528                &clone.as_filelike_view::<std::fs::File>(),
529                path.as_ref(),
530                FollowSymlinks::No,
531            )
532        })
533        .await
534    }
535
536    /// Write a slice as the entire contents of a file.
537    ///
538    /// This corresponds to [`async_std::fs::write`], but only accesses paths
539    /// relative to `self`.
540    #[inline]
541    pub async fn write<P: AsRef<Path>, C: AsRef<[u8]>>(
542        &self,
543        path: P,
544        contents: C,
545    ) -> io::Result<()> {
546        use async_std::prelude::*;
547        let mut file = self.create(path).await?;
548        file.write_all(contents.as_ref()).await
549    }
550
551    /// Creates a new symbolic link on a filesystem.
552    ///
553    /// The `original` argument provides the target of the symlink. The `link`
554    /// argument provides the name of the created symlink.
555    ///
556    /// Despite the argument ordering, `original` is not resolved relative to
557    /// `self` here. `link` is resolved relative to `self`, and `original` is
558    /// not resolved within this function.
559    ///
560    /// The `link` path is resolved when the symlink is dereferenced, relative
561    /// to the directory that contains it.
562    ///
563    /// This corresponds to [`async_std::os::unix::fs::symlink`], but only
564    /// accesses paths relative to `self`.
565    ///
566    /// [`async_std::os::unix::fs::symlink`]: https://docs.rs/async-std/latest/async_std/os/unix/fs/fn.symlink.html
567    #[cfg(not(windows))]
568    #[inline]
569    pub async fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(
570        &self,
571        original: P,
572        link: Q,
573    ) -> io::Result<()> {
574        let original = original.as_ref().to_path_buf();
575        let link = link.as_ref().to_path_buf();
576        let clone = self.clone();
577        spawn_blocking(move || {
578            symlink(
579                original.as_ref(),
580                &clone.as_filelike_view::<std::fs::File>(),
581                link.as_ref(),
582            )
583        })
584        .await
585    }
586
587    /// Creates a new file symbolic link on a filesystem.
588    ///
589    /// The `original` argument provides the target of the symlink. The `link`
590    /// argument provides the name of the created symlink.
591    ///
592    /// Despite the argument ordering, `original` is not resolved relative to
593    /// `self` here. `link` is resolved relative to `self`, and `original` is
594    /// not resolved within this function.
595    ///
596    /// The `link` path is resolved when the symlink is dereferenced, relative
597    /// to the directory that contains it.
598    ///
599    /// This corresponds to [`async_std::os::windows::fs::symlink_file`], but
600    /// only accesses paths relative to `self`.
601    ///
602    /// [`async_std::os::windows::fs::symlink_file`]: https://docs.rs/async-std/latest/async_std/os/windows/fs/fn.symlink_file.html
603    #[cfg(windows)]
604    #[inline]
605    pub async fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(
606        &self,
607        original: P,
608        link: Q,
609    ) -> io::Result<()> {
610        let original = original.as_ref().to_path_buf();
611        let link = link.as_ref().to_path_buf();
612        let clone = self.clone();
613        spawn_blocking(move || {
614            symlink_file(
615                original.as_ref(),
616                &clone.as_filelike_view::<std::fs::File>(),
617                link.as_ref(),
618            )
619        })
620        .await
621    }
622
623    /// Creates a new directory symlink on a filesystem.
624    ///
625    /// The `original` argument provides the target of the symlink. The `link`
626    /// argument provides the name of the created symlink.
627    ///
628    /// Despite the argument ordering, `original` is not resolved relative to
629    /// `self` here. `link` is resolved relative to `self`, and `original` is
630    /// not resolved within this function.
631    ///
632    /// The `link` path is resolved when the symlink is dereferenced, relative
633    /// to the directory that contains it.
634    ///
635    /// This corresponds to [`async_std::os::windows::fs::symlink_dir`], but
636    /// only accesses paths relative to `self`.
637    ///
638    /// [`async_std::os::windows::fs::symlink_dir`]: https://docs.rs/async-std/latest/async_std/os/windows/fs/fn.symlink_dir.html
639    #[cfg(windows)]
640    #[inline]
641    pub async fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(
642        &self,
643        original: P,
644        link: Q,
645    ) -> io::Result<()> {
646        let original = original.as_ref().to_path_buf();
647        let link = link.as_ref().to_path_buf();
648        let clone = self.clone();
649        spawn_blocking(move || {
650            symlink_dir(
651                original.as_ref(),
652                &clone.as_filelike_view::<std::fs::File>(),
653                link.as_ref(),
654            )
655        })
656        .await
657    }
658
659    /// Creates a new `UnixListener` bound to the specified socket.
660    ///
661    /// This corresponds to [`async_std::os::unix::net::UnixListener::bind`],
662    /// but only accesses paths relative to `self`.
663    ///
664    /// XXX: This function is not yet implemented.
665    ///
666    /// [`async_std::os::unix::net::UnixListener::bind`]: https://docs.rs/async-std/latest/async_std/os/unix/net/struct.UnixListener.html#method.bind
667    #[doc(alias = "bind")]
668    #[cfg(unix)]
669    #[inline]
670    pub async fn bind_unix_listener<P: AsRef<Path>>(&self, path: P) -> io::Result<UnixListener> {
671        todo!(
672            "Dir::bind_unix_listener({:?}, {})",
673            self.std_file,
674            path.as_ref().display()
675        )
676    }
677
678    /// Connects to the socket named by path.
679    ///
680    /// This corresponds to [`async_std::os::unix::net::UnixStream::connect`],
681    /// but only accesses paths relative to `self`.
682    ///
683    /// XXX: This function is not yet implemented.
684    ///
685    /// [`async_std::os::unix::net::UnixStream::connect`]: https://docs.rs/async-std/latest/async_std/os/unix/net/struct.UnixStream.html#method.connect
686    #[doc(alias = "connect")]
687    #[cfg(unix)]
688    #[inline]
689    pub async fn connect_unix_stream<P: AsRef<Path>>(&self, path: P) -> io::Result<UnixStream> {
690        todo!(
691            "Dir::connect_unix_stream({:?}, {})",
692            self.std_file,
693            path.as_ref().display()
694        )
695    }
696
697    /// Creates a Unix datagram socket bound to the given path.
698    ///
699    /// This corresponds to [`async_std::os::unix::net::UnixDatagram::bind`],
700    /// but only accesses paths relative to `self`.
701    ///
702    /// XXX: This function is not yet implemented.
703    ///
704    /// [`async_std::os::unix::net::UnixDatagram::bind`]: https://docs.rs/async-std/latest/async_std/os/unix/net/struct.UnixDatagram.html#method.bind
705    #[doc(alias = "bind")]
706    #[cfg(unix)]
707    #[inline]
708    pub async fn bind_unix_datagram<P: AsRef<Path>>(&self, path: P) -> io::Result<UnixDatagram> {
709        todo!(
710            "Dir::bind_unix_datagram({:?}, {})",
711            self.std_file,
712            path.as_ref().display()
713        )
714    }
715
716    /// Connects the socket to the specified address.
717    ///
718    /// This corresponds to
719    /// [`async_std::os::unix::net::UnixDatagram::connect`], but only
720    /// accesses paths relative to `self`.
721    ///
722    /// XXX: This function is not yet implemented.
723    ///
724    /// [`async_std::os::unix::net::UnixDatagram::connect`]: https://docs.rs/async-std/latest/async_std/os/unix/net/struct.UnixDatagram.html#method.connect
725    #[doc(alias = "connect")]
726    #[cfg(unix)]
727    #[inline]
728    pub async fn connect_unix_datagram<P: AsRef<Path>>(
729        &self,
730        _unix_datagram: &UnixDatagram,
731        path: P,
732    ) -> io::Result<()> {
733        todo!(
734            "Dir::connect_unix_datagram({:?}, {})",
735            self.std_file,
736            path.as_ref().display()
737        )
738    }
739
740    /// Sends data on the socket to the specified address.
741    ///
742    /// This corresponds to
743    /// [`async_std::os::unix::net::UnixDatagram::send_to`], but only
744    /// accesses paths relative to `self`.
745    ///
746    /// XXX: This function is not yet implemented.
747    ///
748    /// [`async_std::os::unix::net::UnixDatagram::send_to`]: https://docs.rs/async-std/latest/async_std/os/unix/net/struct.UnixDatagram.html#method.send_to
749    #[doc(alias = "send_to")]
750    #[cfg(unix)]
751    #[inline]
752    pub async fn send_to_unix_datagram_addr<P: AsRef<Path>>(
753        &self,
754        _unix_datagram: &UnixDatagram,
755        buf: &[u8],
756        path: P,
757    ) -> io::Result<usize> {
758        todo!(
759            "Dir::send_to_unix_datagram_addr({:?}, {:?}, {})",
760            self.std_file,
761            buf,
762            path.as_ref().display()
763        )
764    }
765
766    // async_std doesn't have `try_clone`.
767
768    /// Returns `true` if the path points at an existing entity.
769    ///
770    /// This corresponds to [`async_std::path::Path::exists`], but only
771    /// accesses paths relative to `self`.
772    #[inline]
773    pub async fn exists<P: AsRef<Path>>(&self, path: P) -> bool {
774        self.metadata(path).await.is_ok()
775    }
776
777    /// Returns `true` if the path points at an existing entity.
778    ///
779    /// This is an asynchronous version of [`std::fs::try_exists`], and also
780    /// only accesses paths relative to `self`.
781    ///
782    /// NOTE: This API is not yet part of `async_std`.
783    #[inline]
784    pub async fn try_exists<P: AsRef<Path>>(&self, path: P) -> io::Result<bool> {
785        match self.metadata(path.as_ref()).await {
786            Ok(_) => Ok(true),
787            Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false),
788            Err(e) => Err(e),
789        }
790    }
791
792    /// Returns `true` if the path exists on disk and is pointing at a regular
793    /// file.
794    ///
795    /// This corresponds to [`async_std::path::Path::is_file`], but only
796    /// accesses paths relative to `self`.
797    #[inline]
798    pub async fn is_file<P: AsRef<Path>>(&self, path: P) -> bool {
799        self.metadata(path)
800            .await
801            .map(|m| m.is_file())
802            .unwrap_or(false)
803    }
804
805    /// Checks if `path` is a directory.
806    ///
807    /// This is similar to [`async_std::path::Path::is_dir`] in that it checks
808    /// if `path` relative to `Dir` is a directory. This function will traverse
809    /// symbolic links to query information about the destination file. In case
810    /// of broken symbolic links, this will return `false`.
811    #[inline]
812    pub async fn is_dir<P: AsRef<Path>>(&self, path: P) -> bool {
813        self.metadata(path)
814            .await
815            .map(|m| m.is_dir())
816            .unwrap_or(false)
817    }
818
819    /// TODO: Remove this once `create_dir` and friends are async.
820    #[inline]
821    fn is_dir_blocking<P: AsRef<Path>>(&self, path: P) -> bool {
822        self.metadata_blocking(path)
823            .map(|m| m.is_dir())
824            .unwrap_or(false)
825    }
826
827    /// Constructs a new instance of `Self` by opening the given path as a
828    /// directory using the host process' ambient authority.
829    ///
830    /// # Ambient Authority
831    ///
832    /// This function is not sandboxed and may access any path that the host
833    /// process has access to.
834    #[inline]
835    pub async fn open_ambient_dir<P: AsRef<Path>>(
836        path: P,
837        ambient_authority: AmbientAuthority,
838    ) -> io::Result<Self> {
839        let path = path.as_ref().to_path_buf();
840        spawn_blocking(move || open_ambient_dir(path.as_ref(), ambient_authority))
841            .await
842            .map(|f| Self::from_std_file(f.into()))
843    }
844
845    /// Constructs a new instance of `Self` by opening the parent directory
846    /// (aka "..") of `self`, using the host process' ambient authority.
847    ///
848    /// # Ambient Authority
849    ///
850    /// This function accesses a directory outside of the `self` subtree.
851    #[inline]
852    pub async fn open_parent_dir(&self, ambient_authority: AmbientAuthority) -> io::Result<Self> {
853        let clone = self.clone();
854        let dir = spawn_blocking(move || {
855            open_parent_dir(
856                &*clone.as_filelike_view::<std::fs::File>(),
857                ambient_authority,
858            )
859        })
860        .await?
861        .into();
862        Ok(Self::from_std_file(dir))
863    }
864
865    /// Recursively create a directory and all of its parent components if they
866    /// are missing, using the host process' ambient authority.
867    ///
868    /// # Ambient Authority
869    ///
870    /// This function is not sandboxed and may access any path that the host
871    /// process has access to.
872    #[inline]
873    pub async fn create_ambient_dir_all<P: AsRef<Path>>(
874        path: P,
875        ambient_authority: AmbientAuthority,
876    ) -> io::Result<()> {
877        let _ = ambient_authority;
878        let path = path.as_ref().to_path_buf();
879        fs::create_dir_all(path).await
880    }
881
882    /// Construct a new instance of `Self` from existing directory file
883    /// descriptor.
884    ///
885    /// This can be useful when interacting with other libraries and or C/C++
886    /// code which has invoked `openat(..., O_DIRECTORY)` external to this
887    /// crate.
888    pub async fn reopen_dir<Filelike: AsFilelike + Send>(dir: &Filelike) -> io::Result<Self> {
889        // Our public API has a `&Filelike` here, which prevents us from doing
890        // a `clone` as we usually do. So instead, we use the raw filelike, which
891        // we can clone and depend on it remaining open until we return.
892        let raw_filelike = dir.as_filelike_view::<std::fs::File>().as_raw_filelike();
893        // SAFETY: `raw_filelike` remains open for the duration of the
894        // `reopen_dir` call.
895        let file = ManuallyDrop::new(unsafe { std::fs::File::from_raw_filelike(raw_filelike) });
896        let dir = spawn_blocking(move || {
897            cap_primitives::fs::open_dir(&*file, std::path::Component::CurDir.as_ref())
898        })
899        .await?
900        .into();
901        Ok(Self::from_std_file(dir))
902    }
903}
904
905#[cfg(not(target_os = "wasi"))]
906#[inline]
907fn metadata_from(file: &std::fs::File) -> io::Result<Metadata> {
908    Metadata::from_file(file)
909}
910
911#[cfg(target_os = "wasi")]
912#[inline]
913fn metadata_from(file: &std::fs::File) -> io::Result<Metadata> {
914    file.metadata()
915}
916
917// Safety: `FilelikeViewType` is implemented for `std::fs::File`.
918unsafe impl io_lifetimes::views::FilelikeViewType for Dir {}
919
920#[cfg(not(windows))]
921impl FromRawFd for Dir {
922    #[inline]
923    unsafe fn from_raw_fd(fd: RawFd) -> Self {
924        Self::from_std_file(fs::File::from_raw_fd(fd))
925    }
926}
927
928#[cfg(not(windows))]
929impl From<OwnedFd> for Dir {
930    #[inline]
931    fn from(fd: OwnedFd) -> Self {
932        Self::from_std_file(fs::File::from(fd))
933    }
934}
935
936#[cfg(windows)]
937impl FromRawHandle for Dir {
938    /// To prevent race conditions on Windows, the handle must be opened
939    /// without `FILE_SHARE_DELETE`.
940    #[inline]
941    unsafe fn from_raw_handle(handle: RawHandle) -> Self {
942        Self::from_std_file(fs::File::from_raw_handle(handle))
943    }
944}
945
946#[cfg(windows)]
947impl From<OwnedHandle> for Dir {
948    #[inline]
949    fn from(handle: OwnedHandle) -> Self {
950        Self::from_std_file(fs::File::from(handle))
951    }
952}
953
954#[cfg(not(windows))]
955impl AsRawFd for Dir {
956    #[inline]
957    fn as_raw_fd(&self) -> RawFd {
958        self.std_file.as_raw_fd()
959    }
960}
961
962#[cfg(not(windows))]
963impl AsFd for Dir {
964    #[inline]
965    fn as_fd(&self) -> BorrowedFd<'_> {
966        self.std_file.as_fd()
967    }
968}
969
970#[cfg(windows)]
971impl AsRawHandle for Dir {
972    #[inline]
973    fn as_raw_handle(&self) -> RawHandle {
974        self.std_file.as_raw_handle()
975    }
976}
977
978#[cfg(windows)]
979impl AsHandle for Dir {
980    #[inline]
981    fn as_handle(&self) -> BorrowedHandle<'_> {
982        self.std_file.as_handle()
983    }
984}
985
986#[cfg(windows)]
987impl AsRawHandleOrSocket for Dir {
988    #[inline]
989    fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
990        self.std_file.as_raw_handle_or_socket()
991    }
992}
993
994#[cfg(windows)]
995impl AsHandleOrSocket for Dir {
996    #[inline]
997    fn as_handle_or_socket(&self) -> BorrowedHandleOrSocket<'_> {
998        self.std_file.as_handle_or_socket()
999    }
1000}
1001
1002#[cfg(not(windows))]
1003impl IntoRawFd for Dir {
1004    #[inline]
1005    fn into_raw_fd(self) -> RawFd {
1006        self.std_file.into_raw_fd()
1007    }
1008}
1009
1010#[cfg(not(windows))]
1011impl From<Dir> for OwnedFd {
1012    #[inline]
1013    fn from(dir: Dir) -> OwnedFd {
1014        dir.std_file.into()
1015    }
1016}
1017
1018#[cfg(windows)]
1019impl IntoRawHandle for Dir {
1020    #[inline]
1021    fn into_raw_handle(self) -> RawHandle {
1022        self.std_file.into_raw_handle()
1023    }
1024}
1025
1026#[cfg(windows)]
1027impl From<Dir> for OwnedHandle {
1028    #[inline]
1029    fn from(dir: Dir) -> OwnedHandle {
1030        dir.std_file.into()
1031    }
1032}
1033
1034#[cfg(windows)]
1035impl IntoRawHandleOrSocket for Dir {
1036    #[inline]
1037    fn into_raw_handle_or_socket(self) -> RawHandleOrSocket {
1038        self.std_file.into_raw_handle_or_socket()
1039    }
1040}
1041
1042#[cfg(windows)]
1043impl From<Dir> for OwnedHandleOrSocket {
1044    #[inline]
1045    fn from(dir: Dir) -> Self {
1046        dir.std_file.into()
1047    }
1048}
1049
1050/// Indicates how large a buffer to pre-allocate before reading the entire
1051/// file.
1052///
1053/// Derived from the function of the same name in Rust's library/std/src/fs.rs
1054/// at revision 108e90ca78f052c0c1c49c42a22c85620be19712.
1055async fn initial_buffer_size(file: &File) -> usize {
1056    // Allocate one extra byte so the buffer doesn't need to grow before the
1057    // final `read` call at the end of the file. Don't worry about `usize`
1058    // overflow because reading will fail regardless in that case.
1059    file.metadata()
1060        .await
1061        .map(|m| m.len() as usize + 1)
1062        .unwrap_or(0)
1063}
1064
1065impl fmt::Debug for Dir {
1066    // Like libstd's version, but doesn't print the path.
1067    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1068        let mut b = f.debug_struct("Dir");
1069        #[cfg(not(windows))]
1070        b.field("fd", &self.std_file.as_raw_fd());
1071        #[cfg(windows)]
1072        b.field("handle", &self.std_file.as_raw_handle());
1073        b.finish()
1074    }
1075}