cap_std/fs/
dir.rs

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