dir_view/
dir.rs

1use crate::{ReadDirView, ViewKind};
2#[cfg(feature = "cap-fs-ext")]
3use cap_fs_ext::{AccessType, SystemTimeSpec};
4use cap_std::fs::{Dir, DirBuilder, File, Metadata, OpenOptions, Permissions};
5use cap_std::io_lifetimes::AsFilelike;
6#[cfg(unix)]
7use cap_std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};
8use cap_std::AmbientAuthority;
9#[cfg(target_os = "wasi")]
10use rustix::fs::OpenOptionsExt;
11use std::path::{Path, PathBuf};
12use std::{fmt, io};
13
14/// A view of a [`Dir`].
15///
16/// This provides the same API as `Dir`, but imposes restrictions according
17/// to the view kind.
18pub struct DirView {
19    pub(crate) dir: Dir,
20    pub(crate) view_kind: ViewKind,
21}
22
23impl DirView {
24    /// Constructs a new instance of `Self` from the given [`Dir`] and
25    /// [`ViewKind`].
26    #[inline]
27    pub fn from_dir(dir: Dir, view_kind: ViewKind) -> Self {
28        Self { dir, view_kind }
29    }
30
31    /// Attempts to open a file in read-only mode.
32    ///
33    /// This corresponds to [`std::fs::File::open`], but only accesses paths
34    /// relative to `self`.
35    #[inline]
36    pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
37        self.dir.open(path)
38    }
39
40    /// Opens a file at `path` with the options specified by `options`.
41    ///
42    /// This corresponds to [`std::fs::OpenOptions::open`].
43    ///
44    /// Instead of being a method on `OpenOptions`, this is a method on `Dir`,
45    /// and it only accesses paths relative to `self`.
46    #[inline]
47    pub fn open_with<P: AsRef<Path>>(&self, path: P, options: &OpenOptions) -> io::Result<File> {
48        let mut options = options.clone();
49        match self.view_kind {
50            ViewKind::Full => {}
51            ViewKind::Readonly => {
52                // Override any flag that allows writing.
53                options.append(false);
54                options.truncate(false);
55                options.write(false);
56                options.create(false);
57                options.create_new(false);
58            }
59        }
60        self.dir.open_with(path, &options)
61    }
62
63    /// Attempts to open a directory.
64    #[inline]
65    pub fn open_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<Self> {
66        Ok(Self {
67            dir: self.dir.open_dir(path)?,
68            view_kind: self.view_kind,
69        })
70    }
71
72    /// Creates a new, empty directory at the provided path.
73    ///
74    /// This corresponds to [`std::fs::create_dir`], but only accesses paths
75    /// relative to `self`.
76    #[inline]
77    pub fn create_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
78        self.check_mutation()?;
79        self.dir.create_dir(path)
80    }
81
82    /// Recursively create a directory and all of its parent components if they
83    /// are missing.
84    ///
85    /// This corresponds to [`std::fs::create_dir_all`], but only accesses
86    /// paths relative to `self`.
87    #[inline]
88    pub fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
89        self.check_mutation()?;
90        self.dir.create_dir_all(path)
91    }
92
93    /// Creates the specified directory with the options configured in this
94    /// builder.
95    ///
96    /// This corresponds to [`std::fs::DirBuilder::create`].
97    #[cfg(not(target_os = "wasi"))]
98    #[inline]
99    pub fn create_dir_with<P: AsRef<Path>>(
100        &self,
101        path: P,
102        dir_builder: &DirBuilder,
103    ) -> io::Result<()> {
104        self.check_mutation()?;
105        self.dir.create_dir_with(path, dir_builder)
106    }
107
108    /// Opens a file in write-only mode.
109    ///
110    /// This corresponds to [`std::fs::File::create`], but only accesses paths
111    /// relative to `self`.
112    #[inline]
113    pub fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
114        self.check_mutation()?;
115        self.dir.create(path)
116    }
117
118    /// Returns the canonical form of a path with all intermediate components
119    /// normalized and symbolic links resolved.
120    ///
121    /// This corresponds to [`std::fs::canonicalize`], but instead of returning
122    /// an absolute path, returns a path relative to the directory
123    /// represented by `self`.
124    #[inline]
125    pub fn canonicalize<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf> {
126        self.dir.canonicalize(path)
127    }
128
129    /// Copies the contents of one file to another. This function will also
130    /// copy the permission bits of the original file to the destination
131    /// file.
132    ///
133    /// This corresponds to [`std::fs::copy`], but only accesses paths
134    /// relative to `self`.
135    #[inline]
136    pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(
137        &self,
138        from: P,
139        to_dir: &Self,
140        to: Q,
141    ) -> io::Result<u64> {
142        to_dir.check_mutation()?;
143        self.dir.copy(from, &to_dir.dir, to)
144    }
145
146    /// Creates a new hard link on a filesystem.
147    ///
148    /// This corresponds to [`std::fs::hard_link`], but only accesses paths
149    /// relative to `self`.
150    #[inline]
151    pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(
152        &self,
153        src: P,
154        dst_dir: &Self,
155        dst: Q,
156    ) -> io::Result<()> {
157        self.check_mutation()?;
158        dst_dir.check_mutation()?;
159        self.dir.hard_link(src, &dst_dir.dir, dst)
160    }
161
162    /// Given a path, query the file system to get information about a file,
163    /// directory, etc.
164    ///
165    /// This corresponds to [`std::fs::metadata`], but only accesses paths
166    /// relative to `self`.
167    #[inline]
168    pub fn metadata<P: AsRef<Path>>(&self, path: P) -> io::Result<cap_std::fs::Metadata> {
169        self.dir.metadata(path)
170    }
171
172    /// Queries metadata about the underlying directory.
173    ///
174    /// This is similar to [`std::fs::File::metadata`], but for `Dir` rather
175    /// than for `File`.
176    #[inline]
177    pub fn dir_metadata(&self) -> io::Result<Metadata> {
178        self.dir.dir_metadata()
179    }
180
181    /// Returns an iterator over the entries within `self`.
182    #[inline]
183    pub fn entries(&self) -> io::Result<ReadDirView> {
184        Ok(ReadDirView {
185            read_dir: self.dir.entries()?,
186            view_kind: self.view_kind,
187        })
188    }
189
190    /// Returns an iterator over the entries within a directory.
191    ///
192    /// This corresponds to [`std::fs::read_dir`], but only accesses paths
193    /// relative to `self`.
194    #[inline]
195    pub fn read_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<ReadDirView> {
196        Ok(ReadDirView {
197            read_dir: self.dir.read_dir(path)?,
198            view_kind: self.view_kind,
199        })
200    }
201
202    /// Read the entire contents of a file into a bytes vector.
203    ///
204    /// This corresponds to [`std::fs::read`], but only accesses paths
205    /// relative to `self`.
206    #[inline]
207    pub fn read<P: AsRef<Path>>(&self, path: P) -> io::Result<Vec<u8>> {
208        self.dir.read(path)
209    }
210
211    /// Reads a symbolic link, returning the file that the link points to.
212    ///
213    /// This corresponds to [`std::fs::read_link`], but only accesses paths
214    /// relative to `self`.
215    #[inline]
216    pub fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf> {
217        self.dir.read_link(path)
218    }
219
220    /// Read the entire contents of a file into a string.
221    ///
222    /// This corresponds to [`std::fs::read_to_string`], but only accesses
223    /// paths relative to `self`.
224    #[inline]
225    pub fn read_to_string<P: AsRef<Path>>(&self, path: P) -> io::Result<String> {
226        self.dir.read_to_string(path)
227    }
228
229    /// Removes an empty directory.
230    ///
231    /// This corresponds to [`std::fs::remove_dir`], but only accesses paths
232    /// relative to `self`.
233    #[inline]
234    pub fn remove_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
235        self.check_mutation()?;
236        self.dir.remove_dir(path)
237    }
238
239    /// Removes a directory at this path, after removing all its contents. Use
240    /// carefully!
241    ///
242    /// This corresponds to [`std::fs::remove_dir_all`], but only accesses
243    /// paths relative to `self`.
244    #[inline]
245    pub fn remove_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
246        self.check_mutation()?;
247        self.dir.remove_dir_all(path)
248    }
249
250    /// Remove the directory referenced by `self` and consume `self`.
251    ///
252    /// Even though this implementation works in terms of handles as much as
253    /// possible, removal is not guaranteed to be atomic with respect to a
254    /// concurrent rename of the directory.
255    #[inline]
256    pub fn remove_open_dir(self) -> io::Result<()> {
257        self.check_mutation()?;
258        self.dir.remove_open_dir()
259    }
260
261    /// Removes the directory referenced by `self`, after removing all its
262    /// contents, and consume `self`. Use carefully!
263    ///
264    /// Even though this implementation works in terms of handles as much as
265    /// possible, removal is not guaranteed to be atomic with respect to a
266    /// concurrent rename of the directory.
267    #[inline]
268    pub fn remove_open_dir_all(self) -> io::Result<()> {
269        self.check_mutation()?;
270        self.dir.remove_open_dir_all()
271    }
272
273    /// Removes a file from a filesystem.
274    ///
275    /// This corresponds to [`std::fs::remove_file`], but only accesses paths
276    /// relative to `self`.
277    #[inline]
278    pub fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
279        self.check_mutation()?;
280        self.dir.remove_file(path)
281    }
282
283    /// Rename a file or directory to a new name, replacing the original file
284    /// if to already exists.
285    ///
286    /// This corresponds to [`std::fs::rename`], but only accesses paths
287    /// relative to `self`.
288    #[inline]
289    pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(
290        &self,
291        from: P,
292        to_dir: &Self,
293        to: Q,
294    ) -> io::Result<()> {
295        self.check_mutation()?;
296        to_dir.check_mutation()?;
297        self.dir.rename(from, &to_dir.dir, to)
298    }
299
300    /// Changes the permissions found on a file or a directory.
301    ///
302    /// This corresponds to [`std::fs::set_permissions`], but only accesses
303    /// paths relative to `self`. Also, on some platforms, this function
304    /// may fail if the file or directory cannot be opened for reading or
305    /// writing first.
306    #[cfg(not(target_os = "wasi"))]
307    #[inline]
308    pub fn set_permissions<P: AsRef<Path>>(&self, path: P, perm: Permissions) -> io::Result<()> {
309        self.check_mutation()?;
310        self.dir.set_permissions(path, perm)
311    }
312
313    /// Query the metadata about a file without following symlinks.
314    ///
315    /// This corresponds to [`std::fs::symlink_metadata`], but only accesses
316    /// paths relative to `self`.
317    #[inline]
318    pub fn symlink_metadata<P: AsRef<Path>>(&self, path: P) -> io::Result<Metadata> {
319        self.dir.symlink_metadata(path)
320    }
321
322    /// Write a slice as the entire contents of a file.
323    ///
324    /// This corresponds to [`std::fs::write`], but only accesses paths
325    /// relative to `self`.
326    #[inline]
327    pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(&self, path: P, contents: C) -> io::Result<()> {
328        self.check_mutation()?;
329        self.dir.write(path, contents)
330    }
331
332    /// Creates a new symbolic link on a filesystem.
333    ///
334    /// The `original` argument provides the target of the symlink. The `link`
335    /// argument provides the name of the created symlink.
336    ///
337    /// Despite the argument ordering, `original` is not resolved relative to
338    /// `self` here. `link` is resolved relative to `self`, and `original` is
339    /// not resolved within this function.
340    ///
341    /// The `link` path is resolved when the symlink is dereferenced, relative
342    /// to the directory that contains it.
343    ///
344    /// This corresponds to [`std::os::unix::fs::symlink`], but only accesses
345    /// paths relative to `self`.
346    ///
347    /// [`std::os::unix::fs::symlink`]: https://doc.rust-lang.org/std/os/unix/fs/fn.symlink.html
348    #[cfg(not(windows))]
349    #[inline]
350    pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(&self, original: P, link: Q) -> io::Result<()> {
351        self.check_mutation()?;
352        self.dir.symlink(original, link)
353    }
354
355    /// Creates a new file symbolic link on a filesystem.
356    ///
357    /// The `original` argument provides the target of the symlink. The `link`
358    /// argument provides the name of the created symlink.
359    ///
360    /// Despite the argument ordering, `original` is not resolved relative to
361    /// `self` here. `link` is resolved relative to `self`, and `original` is
362    /// not resolved within this function.
363    ///
364    /// The `link` path is resolved when the symlink is dereferenced, relative
365    /// to the directory that contains it.
366    ///
367    /// This corresponds to [`std::os::windows::fs::symlink_file`], but only
368    /// accesses paths relative to `self`.
369    ///
370    /// [`std::os::windows::fs::symlink_file`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html
371    #[cfg(windows)]
372    #[inline]
373    pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(
374        &self,
375        original: P,
376        link: Q,
377    ) -> io::Result<()> {
378        self.check_mutation()?;
379        self.dir.symlink_file(original, link)
380    }
381
382    /// Creates a new directory symlink on a filesystem.
383    ///
384    /// The `original` argument provides the target of the symlink. The `link`
385    /// argument provides the name of the created symlink.
386    ///
387    /// Despite the argument ordering, `original` is not resolved relative to
388    /// `self` here. `link` is resolved relative to `self`, and `original` is
389    /// not resolved within this function.
390    ///
391    /// The `link` path is resolved when the symlink is dereferenced, relative
392    /// to the directory that contains it.
393    ///
394    /// This corresponds to [`std::os::windows::fs::symlink_dir`], but only
395    /// accesses paths relative to `self`.
396    ///
397    /// [`std::os::windows::fs::symlink_dir`]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html
398    #[cfg(windows)]
399    #[inline]
400    pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(
401        &self,
402        original: P,
403        link: Q,
404    ) -> io::Result<()> {
405        self.check_mutation()?;
406        self.dir.symlink_dir(original, link)
407    }
408
409    /// Creates a new `UnixListener` bound to the specified socket.
410    ///
411    /// This corresponds to [`std::os::unix::net::UnixListener::bind`], but
412    /// only accesses paths relative to `self`.
413    ///
414    /// XXX: This function is not yet implemented.
415    ///
416    /// [`std::os::unix::net::UnixListener::bind`]: https://doc.rust-lang.org/std/os/unix/net/struct.UnixListener.html#method.bind
417    #[cfg(unix)]
418    #[inline]
419    pub fn bind_unix_listener<P: AsRef<Path>>(&self, path: P) -> io::Result<UnixListener> {
420        self.dir.bind_unix_listener(path)
421    }
422
423    /// Connects to the socket named by path.
424    ///
425    /// This corresponds to [`std::os::unix::net::UnixStream::connect`], but
426    /// only accesses paths relative to `self`.
427    ///
428    /// XXX: This function is not yet implemented.
429    ///
430    /// [`std::os::unix::net::UnixStream::connect`]: https://doc.rust-lang.org/std/os/unix/net/struct.UnixStream.html#method.connect
431    #[cfg(unix)]
432    #[inline]
433    pub fn connect_unix_stream<P: AsRef<Path>>(&self, path: P) -> io::Result<UnixStream> {
434        self.dir.connect_unix_stream(path)
435    }
436
437    /// Creates a Unix datagram socket bound to the given path.
438    ///
439    /// This corresponds to [`std::os::unix::net::UnixDatagram::bind`], but
440    /// only accesses paths relative to `self`.
441    ///
442    /// XXX: This function is not yet implemented.
443    ///
444    /// [`std::os::unix::net::UnixDatagram::bind`]: https://doc.rust-lang.org/std/os/unix/net/struct.UnixDatagram.html#method.bind
445    #[cfg(unix)]
446    #[inline]
447    pub fn bind_unix_datagram<P: AsRef<Path>>(&self, path: P) -> io::Result<UnixDatagram> {
448        self.dir.bind_unix_datagram(path)
449    }
450
451    /// Connects the socket to the specified address.
452    ///
453    /// This corresponds to [`std::os::unix::net::UnixDatagram::connect`], but
454    /// only accesses paths relative to `self`.
455    ///
456    /// XXX: This function is not yet implemented.
457    ///
458    /// [`std::os::unix::net::UnixDatagram::connect`]: https://doc.rust-lang.org/std/os/unix/net/struct.UnixDatagram.html#method.connect
459    #[cfg(unix)]
460    #[inline]
461    pub fn connect_unix_datagram<P: AsRef<Path>>(
462        &self,
463        unix_datagram: &UnixDatagram,
464        path: P,
465    ) -> io::Result<()> {
466        self.dir.connect_unix_datagram(unix_datagram, path)
467    }
468
469    /// Sends data on the socket to the specified address.
470    ///
471    /// This corresponds to [`std::os::unix::net::UnixDatagram::send_to`], but
472    /// only accesses paths relative to `self`.
473    ///
474    /// XXX: This function is not yet implemented.
475    ///
476    /// [`std::os::unix::net::UnixDatagram::send_to`]: https://doc.rust-lang.org/std/os/unix/net/struct.UnixDatagram.html#method.send_to
477    #[cfg(unix)]
478    #[inline]
479    pub fn send_to_unix_datagram_addr<P: AsRef<Path>>(
480        &self,
481        unix_datagram: &UnixDatagram,
482        buf: &[u8],
483        path: P,
484    ) -> io::Result<usize> {
485        self.dir
486            .send_to_unix_datagram_addr(unix_datagram, buf, path)
487    }
488
489    /// Creates a new `Dir` instance that shares the same underlying file
490    /// handle as the existing `Dir` instance.
491    #[inline]
492    pub fn try_clone(&self) -> io::Result<Self> {
493        Ok(Self {
494            dir: self.dir.try_clone()?,
495            view_kind: self.view_kind,
496        })
497    }
498
499    /// Returns `true` if the path points at an existing entity.
500    ///
501    /// This corresponds to [`std::path::Path::exists`], but only
502    /// accesses paths relative to `self`.
503    #[inline]
504    pub fn exists<P: AsRef<Path>>(&self, path: P) -> bool {
505        self.dir.exists(path)
506    }
507
508    /// Returns `true` if the path points at an existing entity.
509    ///
510    /// This corresponds to [`std::fs::try_exists`], but only
511    /// accesses paths relative to `self`.
512    ///
513    /// # API correspondence with `std`
514    ///
515    /// This API is not yet stable in `std`, but is likely to be. For more
516    /// information, see the [tracker issue](https://github.com/rust-lang/rust/issues/83186).
517    #[inline]
518    pub fn try_exists<P: AsRef<Path>>(&self, path: P) -> io::Result<bool> {
519        self.dir.try_exists(path)
520    }
521
522    /// Returns `true` if the path exists on disk and is pointing at a regular
523    /// file.
524    ///
525    /// This corresponds to [`std::path::Path::is_file`], but only
526    /// accesses paths relative to `self`.
527    #[inline]
528    pub fn is_file<P: AsRef<Path>>(&self, path: P) -> bool {
529        self.dir.is_file(path)
530    }
531
532    /// Checks if `path` is a directory.
533    ///
534    /// This is similar to [`std::path::Path::is_dir`] in that it checks if
535    /// `path` relative to `Dir` is a directory. This function will
536    /// traverse symbolic links to query information about the destination
537    /// file. In case of broken symbolic links, this will return `false`.
538    #[inline]
539    pub fn is_dir<P: AsRef<Path>>(&self, path: P) -> bool {
540        self.dir.is_dir(path)
541    }
542
543    /// Constructs a new instance of `Self` by opening the given path as a
544    /// directory using the host process' ambient authority.
545    ///
546    /// # Ambient Authority
547    ///
548    /// This function is not sandboxed and may access any path that the host
549    /// process has access to.
550    #[inline]
551    pub fn open_ambient_dir<P: AsRef<Path>>(
552        path: P,
553        view_kind: ViewKind,
554        ambient_authority: AmbientAuthority,
555    ) -> io::Result<Self> {
556        Ok(Self {
557            dir: Dir::open_ambient_dir(path, ambient_authority)?,
558            view_kind,
559        })
560    }
561
562    /// Constructs a new instance of `Self` by opening the parent directory
563    /// (aka "..") of `self`, using the host process' ambient authority.
564    ///
565    /// # Ambient Authority
566    ///
567    /// This function accesses a directory outside of the `self` subtree.
568    #[inline]
569    pub fn open_parent_dir(
570        &self,
571        view_kind: ViewKind,
572        ambient_authority: AmbientAuthority,
573    ) -> io::Result<Self> {
574        Ok(Self {
575            dir: self.dir.open_parent_dir(ambient_authority)?,
576            view_kind,
577        })
578    }
579
580    /// Construct a new instance of `Self` from existing directory file
581    /// descriptor.
582    ///
583    /// This can be useful when interacting with other libraries and or C/C++
584    /// code which has invoked `openat(..., O_DIRECTORY)` external to this
585    /// crate.
586    pub fn reopen_dir<Filelike: AsFilelike>(
587        dir: &Filelike,
588        view_kind: ViewKind,
589    ) -> io::Result<Self> {
590        Ok(Self {
591            dir: Dir::reopen_dir(dir)?,
592            view_kind,
593        })
594    }
595
596    fn check_mutation(&self) -> io::Result<()> {
597        match self.view_kind {
598            ViewKind::Full => Ok(()),
599            ViewKind::Readonly => Err(Self::readonly()),
600        }
601    }
602
603    fn readonly() -> io::Error {
604        io::Error::new(
605            io::ErrorKind::PermissionDenied,
606            "attempt to modify a directory tree through a read-only `DirView`",
607        )
608    }
609}
610
611impl fmt::Debug for DirView {
612    // Like libstd's version, but doesn't print the path.
613    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
614        let mut b = f.debug_struct("DirView");
615        b.field("dir", &self.dir);
616        #[cfg(windows)]
617        b.field("view_kind", &self.view_kind);
618        b.finish()
619    }
620}
621
622#[cfg(feature = "cap-fs-ext")]
623impl cap_fs_ext::DirExt for DirView {
624    fn set_atime<P: AsRef<Path>>(&self, path: P, atime: SystemTimeSpec) -> io::Result<()> {
625        self.check_mutation()?;
626        cap_fs_ext::DirExt::set_atime(&self.dir, path, atime)
627    }
628
629    fn set_mtime<P: AsRef<Path>>(&self, path: P, mtime: SystemTimeSpec) -> io::Result<()> {
630        self.check_mutation()?;
631        cap_fs_ext::DirExt::set_mtime(&self.dir, path, mtime)
632    }
633
634    fn set_times<P: AsRef<Path>>(
635        &self,
636        path: P,
637        atime: Option<SystemTimeSpec>,
638        mtime: Option<SystemTimeSpec>,
639    ) -> io::Result<()> {
640        self.check_mutation()?;
641        cap_fs_ext::DirExt::set_times(&self.dir, path, atime, mtime)
642    }
643
644    fn set_symlink_times<P: AsRef<Path>>(
645        &self,
646        path: P,
647        atime: Option<SystemTimeSpec>,
648        mtime: Option<SystemTimeSpec>,
649    ) -> io::Result<()> {
650        self.check_mutation()?;
651        cap_fs_ext::DirExt::set_symlink_times(&self.dir, path, atime, mtime)
652    }
653
654    fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
655        self.check_mutation()?;
656        cap_fs_ext::DirExt::symlink(&self.dir, src, dst)
657    }
658
659    fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
660        self.check_mutation()?;
661        cap_fs_ext::DirExt::symlink_file(&self.dir, src, dst)
662    }
663
664    fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
665        self.check_mutation()?;
666        cap_fs_ext::DirExt::symlink_dir(&self.dir, src, dst)
667    }
668
669    fn open_dir_nofollow<P: AsRef<Path>>(&self, path: P) -> io::Result<Self>
670    where
671        Self: Sized,
672    {
673        Ok(Self {
674            dir: self.dir.open_dir_nofollow(path)?,
675            view_kind: self.view_kind,
676        })
677    }
678
679    fn remove_file_or_symlink<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
680        self.check_mutation()?;
681        cap_fs_ext::DirExt::remove_file_or_symlink(&self.dir, path)
682    }
683
684    fn access<P: AsRef<Path>>(&self, path: P, type_: AccessType) -> io::Result<()> {
685        cap_fs_ext::DirExt::access(&self.dir, path, type_)
686    }
687
688    fn access_symlink<P: AsRef<Path>>(&self, path: P, type_: AccessType) -> io::Result<()> {
689        cap_fs_ext::DirExt::access_symlink(&self.dir, path, type_)
690    }
691
692    fn set_symlink_permissions<P: AsRef<Path>>(
693        &self,
694        path: P,
695        perm: Permissions,
696    ) -> io::Result<()> {
697        self.check_mutation()?;
698        cap_fs_ext::DirExt::set_symlink_permissions(&self.dir, path, perm)
699    }
700}