1use std::os::fd::{AsRawFd as _, BorrowedFd, IntoRawFd as _, OwnedFd};
2
3use crate::{
4 Errno,
5 io::fd,
6 kernel::{self, errno},
7};
8
9pub mod raw;
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum Failure {
13 Get(Errno),
14 Set(Errno),
15}
16
17pub trait Flags: From<Failure> + Sized {
18 const READ: libc::c_int;
19 const WRITE: libc::c_int;
20 const BIT: libc::c_int;
21}
22
23pub trait Fd: errno::Source {
25 fn try_limit() -> Result<(u64, u64), Errno> {
26 let mut limit = std::mem::MaybeUninit::uninit();
27 let result = unsafe { libc::getrlimit(libc::RLIMIT_NOFILE, limit.as_mut_ptr()) };
29 unsafe { kernel::raw::Status::new(result).try_zero::<Self, Errno>() }?;
31 let limit = unsafe { limit.assume_init() };
33 Ok((limit.rlim_cur.into(), limit.rlim_max.into()))
34 }
35
36 fn update<E: Flags>(file: BorrowedFd<'_>) -> Result<(), E> {
38 update::<Self, E, true, true>(file)
39 }
40
41 fn remove<E: Flags>(file: BorrowedFd<'_>) -> Result<(), E> {
43 update::<Self, E, true, false>(file)
44 }
45
46 fn try_update<E: Flags>(file: BorrowedFd<'_>) -> Result<(), E> {
48 update::<Self, E, false, true>(file)
49 }
50
51 fn close<E: From<Errno>>(file: OwnedFd) -> Result<(), E> {
54 let raw = unsafe { libc::close(file.into_raw_fd()) };
56 unsafe { kernel::raw::Status::new(raw).try_zero::<Self, E>() }
58 }
59
60 fn duplicate<E: From<Errno>>(file: BorrowedFd<'_>) -> Result<OwnedFd, E> {
61 let raw = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_DUPFD_CLOEXEC, 0) };
63 unsafe { <OwnedFd as fd::raw::Adopt>::try_adopt::<Self, E>(raw) }
65 }
66
67 fn set<const COMMAND: libc::c_int, E: From<Errno>>(
68 file: BorrowedFd<'_>,
69 flags: libc::c_int,
70 ) -> Result<(), E> {
71 set::<Self, E, COMMAND, false>(file, flags, E::from)
72 }
73
74 fn set_retry<const COMMAND: libc::c_int, E: From<Errno>>(
76 file: BorrowedFd<'_>,
77 flags: libc::c_int,
78 ) -> Result<(), E> {
79 set::<Self, E, COMMAND, true>(file, flags, E::from)
80 }
81 fn read<'a, E: From<Errno>>(
82 file: BorrowedFd<'_>,
83 output: &'a mut [u8],
84 ) -> Result<&'a mut [u8], E> {
85 let raw = unsafe { libc::read(file.as_raw_fd(), output.as_mut_ptr().cast(), output.len()) };
87 unsafe { crate::io::raw::Buffer::try_native::<Self, E>(output, raw) }
89 }
90
91 fn write<'a, E: From<Errno>>(file: BorrowedFd<'_>, input: &'a [u8]) -> Result<&'a [u8], E> {
92 let raw = unsafe { libc::write(file.as_raw_fd(), input.as_ptr().cast(), input.len()) };
94 unsafe { crate::io::raw::Buffer::try_native::<Self, E>(input, raw) }
96 }
97}
98
99impl<T: errno::Source> Fd for T {}
100
101fn update<S: Fd + ?Sized, E: Flags, const RETRY: bool, const ENABLED: bool>(
102 file: BorrowedFd<'_>,
103) -> Result<(), E> {
104 const {
105 assert!(
106 (E::READ == libc::F_GETFD && E::WRITE == libc::F_SETFD)
107 || (E::READ == libc::F_GETFL && E::WRITE == libc::F_SETFL)
108 );
109 }
110 let flags = unsafe {
113 kernel::raw::try_retry::<_, E, RETRY>(
114 || {
115 kernel::raw::Status::new(libc::fcntl(file.as_raw_fd(), E::READ))
116 .try_value::<S, Errno>()
117 },
118 |error| E::from(Failure::Get(error)),
119 )
120 }?
121 .get();
122 let flags = if ENABLED {
123 flags | E::BIT
124 } else {
125 flags & !E::BIT
126 };
127 if E::WRITE == libc::F_SETFD {
128 set::<S, E, { libc::F_SETFD }, RETRY>(file, flags, |error| E::from(Failure::Set(error)))
129 } else {
130 set::<S, E, { libc::F_SETFL }, RETRY>(file, flags, |error| E::from(Failure::Set(error)))
131 }
132}
133
134fn set<S: Fd + ?Sized, E, const COMMAND: libc::c_int, const RETRY: bool>(
135 file: BorrowedFd<'_>,
136 flags: libc::c_int,
137 classify: impl FnOnce(Errno) -> E,
138) -> Result<(), E> {
139 const { assert!(COMMAND == libc::F_SETFD || COMMAND == libc::F_SETFL) };
140 unsafe {
143 kernel::raw::try_retry::<_, E, RETRY>(
144 || {
145 kernel::raw::Status::new(libc::fcntl(file.as_raw_fd(), COMMAND, flags))
146 .try_zero::<S, Errno>()
147 },
148 classify,
149 )
150 }
151}