1use std::fs::File;
13use std::io;
14use std::mem::ManuallyDrop;
15use std::ops::{Deref, DerefMut};
16use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
17
18pub const FIRST_HIGH_FD: RawFd = 10;
22
23pub struct AutoClosePipes {
29 pub read: OwnedFd,
30 pub write: OwnedFd,
31}
32
33pub fn make_autoclose_pipes() -> io::Result<AutoClosePipes> {
40 let (read_fd, write_fd) =
41 nix::unistd::pipe().map_err(|e| io::Error::from_raw_os_error(e as i32))?;
42
43 let read_fd = heightenize_fd(read_fd)?;
45 let write_fd = heightenize_fd(write_fd)?;
46
47 Ok(AutoClosePipes {
48 read: read_fd,
49 write: write_fd,
50 })
51}
52
53fn heightenize_fd(fd: OwnedFd) -> io::Result<OwnedFd> {
58 let raw_fd = fd.as_raw_fd();
59
60 if raw_fd >= FIRST_HIGH_FD {
61 set_cloexec(raw_fd, true)?;
62 return Ok(fd);
63 }
64
65 let new_fd = nix::fcntl::fcntl(raw_fd, nix::fcntl::FcntlArg::F_DUPFD_CLOEXEC(FIRST_HIGH_FD))
67 .map_err(|e| io::Error::from_raw_os_error(e as i32))?;
68
69 Ok(unsafe { OwnedFd::from_raw_fd(new_fd) })
70}
71
72pub fn set_cloexec(fd: RawFd, should_set: bool) -> io::Result<()> {
78 let flags = unsafe { libc::fcntl(fd, libc::F_GETFD, 0) };
79 if flags < 0 {
80 return Err(io::Error::last_os_error());
81 }
82
83 let new_flags = if should_set {
84 flags | libc::FD_CLOEXEC
85 } else {
86 flags & !libc::FD_CLOEXEC
87 };
88
89 if flags != new_flags {
90 let result = unsafe { libc::fcntl(fd, libc::F_SETFD, new_flags) };
91 if result < 0 {
92 return Err(io::Error::last_os_error());
93 }
94 }
95
96 Ok(())
97}
98
99pub fn make_fd_nonblocking(fd: RawFd) -> io::Result<()> {
103 let flags = unsafe { libc::fcntl(fd, libc::F_GETFL, 0) };
104 if flags < 0 {
105 return Err(io::Error::last_os_error());
106 }
107
108 if (flags & libc::O_NONBLOCK) == 0 {
109 let result = unsafe { libc::fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK) };
110 if result < 0 {
111 return Err(io::Error::last_os_error());
112 }
113 }
114
115 Ok(())
116}
117
118pub fn make_fd_blocking(fd: RawFd) -> io::Result<()> {
122 let flags = unsafe { libc::fcntl(fd, libc::F_GETFL, 0) };
123 if flags < 0 {
124 return Err(io::Error::last_os_error());
125 }
126
127 if (flags & libc::O_NONBLOCK) != 0 {
128 let result = unsafe { libc::fcntl(fd, libc::F_SETFL, flags & !libc::O_NONBLOCK) };
129 if result < 0 {
130 return Err(io::Error::last_os_error());
131 }
132 }
133
134 Ok(())
135}
136
137pub fn close_fd(fd: RawFd) {
141 if fd < 0 {
142 return;
143 }
144 loop {
145 let result = unsafe { libc::close(fd) };
146 if result == 0 {
147 break;
148 }
149 let err = io::Error::last_os_error();
150 if err.raw_os_error() != Some(libc::EINTR) {
151 break;
152 }
153 }
154}
155
156pub fn dup_fd(fd: RawFd) -> io::Result<RawFd> {
161 let new_fd = unsafe { libc::dup(fd) };
162 if new_fd < 0 {
163 Err(io::Error::last_os_error())
164 } else {
165 Ok(new_fd)
166 }
167}
168
169pub fn dup2_fd(src: RawFd, dst: RawFd) -> io::Result<()> {
173 if src == dst {
174 return Ok(());
175 }
176 let result = unsafe { libc::dup2(src, dst) };
177 if result < 0 {
178 Err(io::Error::last_os_error())
179 } else {
180 Ok(())
181 }
182}
183
184pub struct BorrowedFdFile(ManuallyDrop<File>);
190
191impl Deref for BorrowedFdFile {
192 type Target = File;
193 fn deref(&self) -> &Self::Target {
194 &self.0
195 }
196}
197
198impl DerefMut for BorrowedFdFile {
199 fn deref_mut(&mut self) -> &mut Self::Target {
200 &mut self.0
201 }
202}
203
204impl FromRawFd for BorrowedFdFile {
205 unsafe fn from_raw_fd(fd: RawFd) -> Self {
206 Self(ManuallyDrop::new(unsafe { File::from_raw_fd(fd) }))
207 }
208}
209
210impl AsRawFd for BorrowedFdFile {
211 fn as_raw_fd(&self) -> RawFd {
212 self.0.as_raw_fd()
213 }
214}
215
216impl IntoRawFd for BorrowedFdFile {
217 fn into_raw_fd(self) -> RawFd {
218 ManuallyDrop::into_inner(self.0).into_raw_fd()
219 }
220}
221
222impl Clone for BorrowedFdFile {
223 fn clone(&self) -> Self {
224 unsafe { Self::from_raw_fd(self.as_raw_fd()) }
225 }
226}
227
228impl BorrowedFdFile {
229 pub fn stdin() -> Self {
230 unsafe { Self::from_raw_fd(libc::STDIN_FILENO) }
231 }
232
233 pub fn stdout() -> Self {
234 unsafe { Self::from_raw_fd(libc::STDOUT_FILENO) }
235 }
236
237 pub fn stderr() -> Self {
238 unsafe { Self::from_raw_fd(libc::STDERR_FILENO) }
239 }
240}
241
242impl io::Read for BorrowedFdFile {
243 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
244 self.deref_mut().read(buf)
245 }
246}
247
248impl io::Write for BorrowedFdFile {
249 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
250 self.deref_mut().write(buf)
251 }
252
253 fn flush(&mut self) -> io::Result<()> {
254 self.deref_mut().flush()
255 }
256}
257
258#[derive(Debug, Clone, Copy, PartialEq, Eq)]
267pub enum FdType {
268 Unused = 0,
269 External = 1,
270 Internal = 2,
271 Module = 3,
272 Flock = 4,
273 FlockExec = 5,
274 Proc = 6,
275}
276
277pub fn movefd(fd: RawFd) -> RawFd {
283 if !(0..FIRST_HIGH_FD).contains(&fd) {
284 return fd;
285 }
286
287 unsafe {
288 let new_fd = libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, FIRST_HIGH_FD);
289 if new_fd != -1 {
290 libc::close(fd);
291 return new_fd;
292 }
293 }
294 fd
295}
296
297pub fn redup(x: RawFd, y: RawFd) -> RawFd {
302 if x < 0 {
303 unsafe { libc::close(y) };
304 return y;
305 }
306
307 if x == y {
308 return y;
309 }
310
311 let result = unsafe { libc::dup2(x, y) };
312 if result == -1 {
313 return -1;
314 }
315
316 unsafe { libc::close(x) };
317 y
318}
319
320pub fn zclose(fd: RawFd) -> i32 {
325 if fd >= 0 {
326 unsafe { libc::close(fd) }
327 } else {
328 -1
329 }
330}
331
332pub fn zdup(fd: RawFd) -> RawFd {
337 if fd < 0 {
338 return -1;
339 }
340 unsafe { libc::dup(fd) }
341}
342
343pub fn fd_is_open(fd: RawFd) -> bool {
348 if fd < 0 {
349 return false;
350 }
351 unsafe { libc::fcntl(fd, libc::F_GETFD, 0) >= 0 }
352}
353
354#[cfg(test)]
359mod tests {
360 use super::*;
361
362 #[test]
363 fn test_make_autoclose_pipes() {
364 let pipes = make_autoclose_pipes().expect("Failed to create pipes");
365
366 assert!(pipes.read.as_raw_fd() >= FIRST_HIGH_FD);
368 assert!(pipes.write.as_raw_fd() >= FIRST_HIGH_FD);
369
370 let read_flags = unsafe { libc::fcntl(pipes.read.as_raw_fd(), libc::F_GETFD, 0) };
372 let write_flags = unsafe { libc::fcntl(pipes.write.as_raw_fd(), libc::F_GETFD, 0) };
373
374 assert!(read_flags >= 0);
375 assert!(write_flags >= 0);
376 assert_ne!(read_flags & libc::FD_CLOEXEC, 0);
377 assert_ne!(write_flags & libc::FD_CLOEXEC, 0);
378 }
379
380 #[test]
381 fn test_set_cloexec() {
382 let file = std::fs::File::open("/dev/null").unwrap();
383 let fd = file.as_raw_fd();
384
385 set_cloexec(fd, true).unwrap();
387 let flags = unsafe { libc::fcntl(fd, libc::F_GETFD, 0) };
388 assert_ne!(flags & libc::FD_CLOEXEC, 0);
389
390 set_cloexec(fd, false).unwrap();
392 let flags = unsafe { libc::fcntl(fd, libc::F_GETFD, 0) };
393 assert_eq!(flags & libc::FD_CLOEXEC, 0);
394 }
395
396 #[test]
397 fn test_nonblocking() {
398 let file = std::fs::File::open("/dev/null").unwrap();
399 let fd = file.as_raw_fd();
400
401 make_fd_nonblocking(fd).unwrap();
403 let flags = unsafe { libc::fcntl(fd, libc::F_GETFL, 0) };
404 assert_ne!(flags & libc::O_NONBLOCK, 0);
405
406 make_fd_blocking(fd).unwrap();
408 let flags = unsafe { libc::fcntl(fd, libc::F_GETFL, 0) };
409 assert_eq!(flags & libc::O_NONBLOCK, 0);
410 }
411
412 #[test]
413 fn test_borrowed_fd_file_does_not_close() {
414 let file = std::fs::File::open("/dev/null").unwrap();
415 let fd = file.as_raw_fd();
416
417 {
424 let _borrowed = unsafe { BorrowedFdFile::from_raw_fd(fd) };
425 }
426
427 let flags = unsafe { libc::fcntl(fd, libc::F_GETFD, 0) };
429 assert!(
430 flags >= 0,
431 "fd should still be valid after dropping BorrowedFdFile"
432 );
433
434 drop(file);
436
437 let flags = unsafe { libc::fcntl(fd, libc::F_GETFD, 0) };
439 assert!(
440 flags < 0,
441 "fd should be invalid after dropping original File"
442 );
443 }
444}