brush_core/sys/unix/fd.rs
1//! File descriptor utilities.
2
3use std::os::fd::RawFd;
4
5use crate::{ShellFd, error, openfiles};
6
7cfg_if::cfg_if! {
8 if #[cfg(any(target_os = "linux", target_os = "android"))] {
9 const FD_DIR_PATH: &str = "/proc/self/fd";
10 } else if #[cfg(any(
11 target_os = "freebsd",
12 target_os = "macos",
13 target_os = "netbsd",
14 target_os = "openbsd"
15 ))] {
16 const FD_DIR_PATH: &str = "/dev/fd";
17 } else {
18 /// Returns an iterator over all open file descriptors for the shell.
19 pub fn iter_fds()
20 -> Result<impl Iterator<Item = (ShellFd, openfiles::OpenFile)>, error::Error> {
21 Ok(std::iter::empty())
22 }
23 }
24}
25
26/// Makes a best-effort attempt to iterate over all open file descriptors
27/// for the current process.
28///
29/// If the platform does not support enumerating file descriptors, an empty iterator
30/// is returned. This function will skip any file descriptors that cannot be opened.
31#[cfg(any(
32 target_os = "linux",
33 target_os = "android",
34 target_os = "freebsd",
35 target_os = "macos",
36 target_os = "netbsd",
37 target_os = "openbsd"
38))]
39pub fn try_iter_open_fds() -> impl Iterator<Item = (ShellFd, openfiles::OpenFile)> {
40 std::fs::read_dir(FD_DIR_PATH)
41 .into_iter()
42 .flatten()
43 .filter_map(Result::ok)
44 .filter_map(|entry| {
45 let fd: RawFd = entry.file_name().to_str()?.parse().ok()?;
46
47 // SAFETY:
48 // We are trying to open the file descriptor we found listed
49 // in the filesystem, but there's a risk that it's not the same one
50 // that we enumerated or that it's since been closed. For the purposes
51 // of this function, either of those outcomes are acceptable. We
52 // simply skip any fds that we can't open, and the function's purpose
53 // is to make a best-effort attempt to open all available fds.
54 let file = unsafe { open_file_by_fd(fd) }.ok()?;
55
56 Some((fd, file))
57 })
58}
59
60/// Attempts to retrieve an `OpenFile` representation for the given already-open file descriptor.
61///
62/// If the file descriptor cannot be opened, `None` is returned. Note that there is no guarantee
63/// that the returned file matches the original file descriptor, as the fd may have been closed
64/// and potentially re-used in the meantime.
65///
66/// # Arguments
67///
68/// * `fd` - The file descriptor to open.
69pub fn try_get_file_for_open_fd(fd: RawFd) -> Option<openfiles::OpenFile> {
70 // SAFETY:
71 // We are trying to open the file descriptor provided by the caller. There's a risk that the fd
72 // is invalid or has been closed since it was enumerated. For the purposes of this function,
73 // we simply return None if we can't open it. There's also a risk that the fd has been closed
74 // and re-used for a different file; again, for the purposes of this function, we accept that
75 // risk and document it as part of the function's contract.
76 unsafe { open_file_by_fd(fd).ok() }
77}
78
79unsafe fn open_file_by_fd(fd: RawFd) -> Result<openfiles::OpenFile, error::Error> {
80 // SAFETY: We are creating a BorrowedFd from a file descriptor. Callers typically
81 // enumerate available file descriptors from procfs, devfs, or similar, but there's
82 // still a risk that the fd has become invalid or closed since then -- or that this
83 // function gets used incorrectly.
84 let borrowed_fd = unsafe { std::os::fd::BorrowedFd::borrow_raw(fd) };
85 let owned_fd = borrowed_fd.try_clone_to_owned()?;
86 Ok(std::fs::File::from(owned_fd).into())
87}