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 update<E: Flags>(file: BorrowedFd<'_>) -> Result<(), E> {
27 update::<Self, E, true, true>(file)
28 }
29
30 fn remove<E: Flags>(file: BorrowedFd<'_>) -> Result<(), E> {
32 update::<Self, E, true, false>(file)
33 }
34
35 fn try_update<E: Flags>(file: BorrowedFd<'_>) -> Result<(), E> {
37 update::<Self, E, false, true>(file)
38 }
39
40 fn close<E: From<Errno>>(file: OwnedFd) -> Result<(), E> {
43 let raw = unsafe { libc::close(file.into_raw_fd()) };
45 unsafe {
47 kernel::raw::Status::new(raw).try_zero(|| kernel::raw::Code::last::<Self>().into())
48 }
49 }
50
51 fn duplicate<E: From<Errno>>(file: BorrowedFd<'_>) -> Result<OwnedFd, E> {
52 let raw = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_DUPFD_CLOEXEC, 0) };
54 unsafe {
56 <OwnedFd as fd::raw::Adopt>::try_adopt(raw, || kernel::raw::Code::last::<Self>().into())
57 }
58 }
59
60 fn set<const COMMAND: libc::c_int, E: From<Errno>>(
61 file: BorrowedFd<'_>,
62 flags: libc::c_int,
63 ) -> Result<(), E> {
64 set::<Self, E, COMMAND, false>(file, flags, E::from)
65 }
66
67 fn set_retry<const COMMAND: libc::c_int, E: From<Errno>>(
69 file: BorrowedFd<'_>,
70 flags: libc::c_int,
71 ) -> Result<(), E> {
72 set::<Self, E, COMMAND, true>(file, flags, E::from)
73 }
74 fn read<'a, E: From<Errno>>(
75 file: BorrowedFd<'_>,
76 output: &'a mut [u8],
77 ) -> Result<&'a mut [u8], E> {
78 let raw = unsafe { libc::read(file.as_raw_fd(), output.as_mut_ptr().cast(), output.len()) };
80 unsafe {
82 crate::io::raw::Buffer::try_native(output, raw, || {
83 kernel::raw::Code::last::<Self>().into()
84 })
85 }
86 }
87
88 fn write<'a, E: From<Errno>>(file: BorrowedFd<'_>, input: &'a [u8]) -> Result<&'a [u8], E> {
89 let raw = unsafe { libc::write(file.as_raw_fd(), input.as_ptr().cast(), input.len()) };
91 unsafe {
93 crate::io::raw::Buffer::try_native(input, raw, || {
94 kernel::raw::Code::last::<Self>().into()
95 })
96 }
97 }
98}
99
100impl<T: errno::Source> Fd for T {}
101
102fn update<S: Fd + ?Sized, E: Flags, const RETRY: bool, const ENABLED: bool>(
103 file: BorrowedFd<'_>,
104) -> Result<(), E> {
105 const {
106 assert!(
107 (E::READ == libc::F_GETFD && E::WRITE == libc::F_SETFD)
108 || (E::READ == libc::F_GETFL && E::WRITE == libc::F_SETFL)
109 );
110 }
111 let flags = unsafe {
114 kernel::raw::try_retry::<_, E, RETRY>(
115 || {
116 kernel::raw::Status::new(libc::fcntl(file.as_raw_fd(), E::READ))
117 .try_value::<Errno>(|| kernel::raw::Code::last::<S>().into())
118 },
119 |error| E::from(Failure::Get(error)),
120 )
121 }?
122 .get();
123 let flags = if ENABLED {
124 flags | E::BIT
125 } else {
126 flags & !E::BIT
127 };
128 if E::WRITE == libc::F_SETFD {
129 set::<S, E, { libc::F_SETFD }, RETRY>(file, flags, |error| E::from(Failure::Set(error)))
130 } else {
131 set::<S, E, { libc::F_SETFL }, RETRY>(file, flags, |error| E::from(Failure::Set(error)))
132 }
133}
134
135fn set<S: Fd + ?Sized, E, const COMMAND: libc::c_int, const RETRY: bool>(
136 file: BorrowedFd<'_>,
137 flags: libc::c_int,
138 classify: impl FnOnce(Errno) -> E,
139) -> Result<(), E> {
140 const { assert!(COMMAND == libc::F_SETFD || COMMAND == libc::F_SETFL) };
141 unsafe {
144 kernel::raw::try_retry::<_, E, RETRY>(
145 || {
146 kernel::raw::Status::new(libc::fcntl(file.as_raw_fd(), COMMAND, flags))
147 .try_zero::<Errno>(|| kernel::raw::Code::last::<S>().into())
148 },
149 classify,
150 )
151 }
152}