1use std::io;
2
3#[cfg(unix)]
4use std::os::fd::BorrowedFd;
5
6use crate::os::CheckLibcResult;
7
8pub use libc::{F_GETFD, F_GETFL, F_SETFD, F_SETFL, FD_CLOEXEC};
9
10#[cfg(any(
11 target_os = "android",
12 target_os = "dragonfly",
13 target_os = "freebsd",
14 target_os = "linux",
15 target_os = "macos",
16 target_os = "netbsd",
17 target_os = "redox"
18))]
19pub use libc::{F_LOCK, F_TEST, F_TLOCK, F_ULOCK};
20
21#[cfg(not(target_os = "wasi"))]
22pub use libc::{F_DUPFD, F_DUPFD_CLOEXEC, F_GETLK, F_SETLK, F_SETLKW};
23
24#[cfg(not(any(target_os = "wasi", target_os = "redox")))]
25pub use libc::{F_GETOWN, F_RDLCK, F_SETOWN, F_UNLCK, F_WRLCK, LOCK_EX, LOCK_NB, LOCK_SH, LOCK_UN};
26
27#[cfg(target_vendor = "apple")]
28pub use libc::{F_FULLFSYNC, F_NOCACHE};
29
30#[cfg(target_os = "freebsd")]
31pub use libc::{F_DUP2FD, F_DUP2FD_CLOEXEC};
32
33#[cfg(any(target_os = "android", target_os = "linux", target_vendor = "apple"))]
34pub use libc::{F_OFD_GETLK, F_OFD_SETLK, F_OFD_SETLKW};
35
36#[cfg(not(target_os = "wasi"))]
38pub const FASYNC: libc::c_int = libc::O_ASYNC;
39
40#[cfg(target_vendor = "apple")]
41pub use libc::F_RDAHEAD;
42
43#[cfg(target_vendor = "apple")]
45mod darwin_fcntl {
46 pub const F_SETNOSIGPIPE: libc::c_int = 73;
47 pub const F_GETNOSIGPIPE: libc::c_int = 74;
48 pub const F_SETLEASE: libc::c_int = 106;
49 pub const F_GETLEASE: libc::c_int = 107;
50}
51
52#[cfg(target_vendor = "apple")]
53pub use darwin_fcntl::{F_GETLEASE, F_GETNOSIGPIPE, F_SETLEASE, F_SETNOSIGPIPE};
54
55#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
56pub use libc::{
57 F_ADD_SEALS, F_GET_SEALS, F_GETLEASE, F_GETPIPE_SZ, F_NOTIFY, F_SEAL_GROW, F_SEAL_SEAL,
58 F_SEAL_SHRINK, F_SEAL_WRITE, F_SETLEASE, F_SETPIPE_SZ,
59};
60
61#[cfg(any(target_os = "dragonfly", target_os = "netbsd", target_vendor = "apple"))]
62pub use libc::F_GETPATH;
63
64pub fn normalize_ioctl_request(request: i64) -> libc::c_ulong {
65 (request as u32) as libc::c_ulong
66}
67
68pub fn fcntl_int(fd: i32, cmd: i32, arg: i32) -> io::Result<i32> {
69 unsafe { libc::fcntl(fd, cmd, arg) }.check_libc_neg()
70}
71
72pub fn validate_fd(fd: i32) -> io::Result<()> {
73 fcntl_int(fd, libc::F_GETFD, 0).map(|_| ())
74}
75
76#[cfg(unix)]
77pub fn get_inheritable(fd: BorrowedFd<'_>) -> io::Result<bool> {
78 use nix::fcntl as nix_fcntl;
79
80 let flags = nix_fcntl::FdFlag::from_bits_truncate(
81 nix_fcntl::fcntl(fd, nix_fcntl::FcntlArg::F_GETFD).map_err(io::Error::from)?,
82 );
83 Ok(!flags.contains(nix_fcntl::FdFlag::FD_CLOEXEC))
84}
85
86#[cfg(unix)]
87pub fn get_blocking(fd: BorrowedFd<'_>) -> io::Result<bool> {
88 use nix::fcntl as nix_fcntl;
89
90 let flags = nix_fcntl::OFlag::from_bits_truncate(
91 nix_fcntl::fcntl(fd, nix_fcntl::FcntlArg::F_GETFL).map_err(io::Error::from)?,
92 );
93 Ok(!flags.contains(nix_fcntl::OFlag::O_NONBLOCK))
94}
95
96#[cfg(unix)]
97pub fn set_blocking(fd: BorrowedFd<'_>, blocking: bool) -> io::Result<()> {
98 use nix::fcntl as nix_fcntl;
99
100 let flags = nix_fcntl::OFlag::from_bits_truncate(
101 nix_fcntl::fcntl(fd, nix_fcntl::FcntlArg::F_GETFL).map_err(io::Error::from)?,
102 );
103 let mut new_flags = flags;
104 new_flags.set(nix_fcntl::OFlag::O_NONBLOCK, !blocking);
105 if flags != new_flags {
106 nix_fcntl::fcntl(fd, nix_fcntl::FcntlArg::F_SETFL(new_flags)).map_err(io::Error::from)?;
107 }
108 Ok(())
109}
110
111pub fn fcntl_with_bytes(fd: i32, cmd: i32, arg: &mut [u8]) -> io::Result<i32> {
112 unsafe { libc::fcntl(fd, cmd, arg.as_mut_ptr()) }.check_libc_neg()
113}
114
115pub unsafe fn ioctl_ptr(
120 fd: i32,
121 request: libc::c_ulong,
122 arg: *mut libc::c_void,
123) -> io::Result<i32> {
124 unsafe { libc::ioctl(fd, request as _, arg) }.check_libc_neg()
125}
126
127pub fn ioctl_int(fd: i32, request: libc::c_ulong, arg: i32) -> io::Result<i32> {
128 unsafe { libc::ioctl(fd, request as _, arg) }.check_libc_neg()
129}
130
131#[cfg(not(any(target_os = "wasi", target_os = "redox")))]
132pub fn flock(fd: i32, operation: i32) -> io::Result<i32> {
133 unsafe { libc::flock(fd, operation) }.check_libc_neg()
134}
135
136#[cfg(not(any(target_os = "wasi", target_os = "redox")))]
137pub enum LockfError {
138 InvalidCmd,
139 Overflow(String),
140 Io(io::Error),
141}
142
143#[cfg(not(any(target_os = "wasi", target_os = "redox")))]
144pub fn lockf(fd: i32, cmd: i32, len: i64, start: i64, whence: i32) -> Result<i32, LockfError> {
145 fn convert_field<T, U>(value: T) -> Result<U, LockfError>
146 where
147 T: TryInto<U>,
148 T::Error: core::fmt::Display,
149 {
150 value
151 .try_into()
152 .map_err(|err| LockfError::Overflow(err.to_string()))
153 }
154
155 let l_type = if cmd == libc::LOCK_UN {
156 libc::F_UNLCK
157 } else if (cmd & libc::LOCK_SH) != 0 {
158 libc::F_RDLCK
159 } else if (cmd & libc::LOCK_EX) != 0 {
160 libc::F_WRLCK
161 } else {
162 return Err(LockfError::InvalidCmd);
163 };
164
165 let lock = libc::flock {
166 l_type: convert_field(l_type)?,
167 l_whence: convert_field(whence)?,
168 l_start: convert_field(start)?,
169 l_len: convert_field(len)?,
170 ..unsafe { core::mem::zeroed() }
171 };
172
173 unsafe {
174 libc::fcntl(
175 fd,
176 if (cmd & libc::LOCK_NB) != 0 {
177 libc::F_SETLK
178 } else {
179 libc::F_SETLKW
180 },
181 &lock,
182 )
183 }
184 .check_libc_neg()
185 .map_err(LockfError::Io)
186}