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 { kernel::raw::Status::new(raw).try_zero::<Self, E>() }
47 }
48
49 fn duplicate<E: From<Errno>>(file: BorrowedFd<'_>) -> Result<OwnedFd, E> {
50 let raw = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_DUPFD_CLOEXEC, 0) };
52 unsafe { <OwnedFd as fd::raw::Adopt>::try_adopt::<Self, E>(raw) }
54 }
55
56 fn set<const COMMAND: libc::c_int, E: From<Errno>>(
57 file: BorrowedFd<'_>,
58 flags: libc::c_int,
59 ) -> Result<(), E> {
60 set::<Self, E, COMMAND, false>(file, flags, E::from)
61 }
62
63 fn set_retry<const COMMAND: libc::c_int, E: From<Errno>>(
65 file: BorrowedFd<'_>,
66 flags: libc::c_int,
67 ) -> Result<(), E> {
68 set::<Self, E, COMMAND, true>(file, flags, E::from)
69 }
70 fn read<'a, E: From<Errno>>(
71 file: BorrowedFd<'_>,
72 output: &'a mut [u8],
73 ) -> Result<&'a mut [u8], E> {
74 let raw = unsafe { libc::read(file.as_raw_fd(), output.as_mut_ptr().cast(), output.len()) };
76 unsafe { crate::io::raw::Buffer::try_native::<Self, E>(output, raw) }
78 }
79
80 fn write<'a, E: From<Errno>>(file: BorrowedFd<'_>, input: &'a [u8]) -> Result<&'a [u8], E> {
81 let raw = unsafe { libc::write(file.as_raw_fd(), input.as_ptr().cast(), input.len()) };
83 unsafe { crate::io::raw::Buffer::try_native::<Self, E>(input, raw) }
85 }
86}
87
88impl<T: errno::Source> Fd for T {}
89
90fn update<S: Fd + ?Sized, E: Flags, const RETRY: bool, const ENABLED: bool>(
91 file: BorrowedFd<'_>,
92) -> Result<(), E> {
93 const {
94 assert!(
95 (E::READ == libc::F_GETFD && E::WRITE == libc::F_SETFD)
96 || (E::READ == libc::F_GETFL && E::WRITE == libc::F_SETFL)
97 );
98 }
99 let flags = unsafe {
102 kernel::raw::try_retry::<_, E, RETRY>(
103 || {
104 kernel::raw::Status::new(libc::fcntl(file.as_raw_fd(), E::READ))
105 .try_value::<S, Errno>()
106 },
107 |error| E::from(Failure::Get(error)),
108 )
109 }?
110 .get();
111 let flags = if ENABLED {
112 flags | E::BIT
113 } else {
114 flags & !E::BIT
115 };
116 if E::WRITE == libc::F_SETFD {
117 set::<S, E, { libc::F_SETFD }, RETRY>(file, flags, |error| E::from(Failure::Set(error)))
118 } else {
119 set::<S, E, { libc::F_SETFL }, RETRY>(file, flags, |error| E::from(Failure::Set(error)))
120 }
121}
122
123fn set<S: Fd + ?Sized, E, const COMMAND: libc::c_int, const RETRY: bool>(
124 file: BorrowedFd<'_>,
125 flags: libc::c_int,
126 classify: impl FnOnce(Errno) -> E,
127) -> Result<(), E> {
128 const { assert!(COMMAND == libc::F_SETFD || COMMAND == libc::F_SETFL) };
129 unsafe {
132 kernel::raw::try_retry::<_, E, RETRY>(
133 || {
134 kernel::raw::Status::new(libc::fcntl(file.as_raw_fd(), COMMAND, flags))
135 .try_zero::<S, Errno>()
136 },
137 classify,
138 )
139 }
140}