filedesc/
unix.rs

1#![deny(unsafe_op_in_unsafe_fn)]
2
3use std::os::raw::c_int;
4use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
5use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd};
6
7#[derive(Debug)]
8/// Thin wrapper around an open file descriptor.
9///
10/// The wrapped file descriptor will be closed
11/// when the wrapper is dropped.
12pub struct FileDesc {
13	fd: OwnedFd,
14}
15
16impl FileDesc {
17	/// Create [`FileDesc`] from an owned file descriptor.
18	///
19	/// This does not do anything to the file descriptor other than wrap it.
20	/// Notably, it does not set the `close-on-exec` flag.
21	pub fn new(fd: OwnedFd) -> Self {
22		Self { fd }
23	}
24
25	/// Wrap a raw file descriptor in a [`FileDesc`].
26	///
27	/// This does not do anything to the file descriptor other than wrapping it.
28	/// Notably, it does not set the `close-on-exec` flag.
29	///
30	/// # Safety
31	/// The input must be a valid file descriptor.
32	/// The file descriptor must not be closed as long as it is managed by the created [`FileDesc`].
33	pub unsafe fn from_raw_fd(fd: RawFd) -> Self {
34		unsafe {
35			Self::new(OwnedFd::from_raw_fd(fd))
36		}
37	}
38
39	/// Duplicate a file descriptor from an object that has a file descriptor.
40	///
41	/// The new file descriptor will have the `close-on-exec` flag set.
42	/// If the platform supports it, the flag will be set atomically.
43	///
44	/// The duplicated [`FileDesc`] will be the sole owner of the new file descriptor, but it will share ownership of the underlying kernel object.
45	pub fn duplicate_from<T: AsFd>(other: T) -> std::io::Result<Self> {
46		Ok(Self::new(other.as_fd().try_clone_to_owned()?))
47	}
48
49	/// Duplicate a raw file descriptor and wrap it in a [`FileDesc`].
50	///
51	/// The new file descriptor will have the `close-on-exec` flag set.
52	/// If the platform supports it, the flag will be set atomically.
53	///
54	/// The duplicated [`FileDesc`] will be the sole owner of the new file descriptor, but it will share ownership of the underlying kernel object.
55	///
56	/// # Safety
57	/// The file descriptor must be valid,
58	/// and duplicating it must not violate the safety requirements of any object already using the file descriptor.
59	pub unsafe fn duplicate_raw_fd(fd: RawFd) -> std::io::Result<Self> {
60		unsafe {
61			Self::duplicate_from(BorrowedFd::borrow_raw(fd))
62		}
63	}
64
65	/// Get the file descriptor.
66	///
67	/// This function does not release ownership of the underlying file descriptor.
68	/// The file descriptor will still be closed when the [`FileDesc`] is dropped.
69	pub fn as_fd(&self) -> BorrowedFd {
70		self.fd.as_fd()
71	}
72
73	/// Release and get the raw file descriptor.
74	///
75	/// This function releases ownership of the underlying file descriptor.
76	/// The file descriptor will not be closed.
77	pub fn into_fd(self) -> OwnedFd {
78		self.fd
79	}
80
81	/// Get the raw file descriptor.
82	///
83	/// This function does not release ownership of the underlying file descriptor.
84	/// The file descriptor will still be closed when the [`FileDesc`] is dropped.
85	pub fn as_raw_fd(&self) -> RawFd {
86		self.fd.as_raw_fd()
87	}
88
89	/// Release and get the raw file descriptor.
90	///
91	/// This function releases ownership of the underlying file descriptor.
92	/// The file descriptor will not be closed.
93	pub fn into_raw_fd(self) -> RawFd {
94		self.fd.into_raw_fd()
95	}
96
97	/// Try to duplicate the file descriptor.
98	///
99	/// The duplicated [`FileDesc`] will be the sole owner of the new file descriptor, but it will share ownership of the underlying kernel object.
100	///
101	/// The new file descriptor will have the `close-on-exec` flag set.
102	/// If the platform supports it, the flag will be set atomically.
103	pub fn duplicate(&self) -> std::io::Result<Self> {
104		Self::duplicate_from(self)
105	}
106
107	/// Change the close-on-exec flag of the file descriptor.
108	///
109	/// You should always try to create file descriptors with the close-on-exec flag already set atomically instead of changing it later with this function.
110	/// Setting the flag later on introduces a race condition if another thread forks before the call to `set_close_on_exec` finishes.
111	///
112	/// You can use this without any race condition to disable the `close-on-exec` flag *after* forking but before executing a new program.
113	pub fn set_close_on_exec(&self, close_on_exec: bool) -> std::io::Result<()> {
114		unsafe {
115			// TODO: Are there platforms where we need to preserve other bits?
116			let arg = if close_on_exec { libc::FD_CLOEXEC } else { 0 };
117			check_ret(libc::fcntl(self.fd.as_raw_fd(), libc::F_SETFD, arg))?;
118			Ok(())
119		}
120	}
121
122	/// Check the close-on-exec flag of the file descriptor.
123	pub fn get_close_on_exec(&self) -> std::io::Result<bool> {
124		unsafe {
125			let ret = check_ret(libc::fcntl(self.fd.as_raw_fd(), libc::F_GETFD, 0))?;
126			Ok(ret & libc::FD_CLOEXEC != 0)
127		}
128	}
129}
130
131impl AsFd for FileDesc {
132	fn as_fd(&self) -> BorrowedFd<'_> {
133		self.fd.as_fd()
134	}
135}
136
137impl From<OwnedFd> for FileDesc {
138	fn from(value: OwnedFd) -> Self {
139		Self::new(value)
140	}
141}
142
143impl From<FileDesc> for OwnedFd {
144	fn from(value: FileDesc) -> Self {
145		value.fd
146	}
147}
148
149impl FromRawFd for FileDesc {
150	unsafe fn from_raw_fd(fd: RawFd) -> Self {
151		unsafe {
152			Self::from_raw_fd(fd)
153		}
154	}
155}
156
157impl AsRawFd for FileDesc {
158	fn as_raw_fd(&self) -> RawFd {
159		self.as_raw_fd()
160	}
161}
162
163impl AsRawFd for &'_ FileDesc {
164	fn as_raw_fd(&self) -> RawFd {
165		(*self).as_raw_fd()
166	}
167}
168
169impl IntoRawFd for FileDesc {
170	fn into_raw_fd(self) -> RawFd {
171		self.into_raw_fd()
172	}
173}
174
175/// Wrap the return value of a libc function in an [`std::io::Result`].
176///
177/// If the return value is -1, [`last_os_error()`](std::io::Error::last_os_error) is returned.
178/// Otherwise, the return value is returned wrapped as [`Ok`].
179fn check_ret(ret: c_int) -> std::io::Result<c_int> {
180	if ret == -1 {
181		Err(std::io::Error::last_os_error())
182	} else {
183		Ok(ret)
184	}
185}