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}