Skip to main content

rustpython_host_env/
fcntl.rs

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/// `FASYNC` is what `fcntl.h` calls the flag `O_ASYNC` stands for.
37#[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/// The commands `fcntl.h` carries that libc does not name.
44#[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
115/// # Safety
116///
117/// `arg` must be a valid pointer for the `request` passed to `ioctl` and must
118/// satisfy the platform ABI requirements for that request.
119pub 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}