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}