1 //! libc syscalls supporting `rustix::io`.
2 
3 use crate::backend::c;
4 #[cfg(not(target_os = "wasi"))]
5 use crate::backend::conv::ret_discarded_fd;
6 use crate::backend::conv::{borrowed_fd, ret, ret_c_int, ret_owned_fd, ret_usize};
7 use crate::fd::{AsFd, BorrowedFd, OwnedFd, RawFd};
8 #[cfg(not(any(
9     target_os = "aix",
10     target_os = "espidf",
11     target_os = "nto",
12     target_os = "vita",
13     target_os = "wasi"
14 )))]
15 use crate::io::DupFlags;
16 #[cfg(feature = "linux-raw-sys")]
17 use crate::io::ReadWriteFlags;
18 use crate::io::{self, FdFlags};
19 use crate::ioctl::{IoctlOutput, RawOpcode};
20 use core::cmp::min;
21 #[cfg(all(feature = "fs", feature = "net"))]
22 use libc_errno::errno;
23 #[cfg(not(target_os = "espidf"))]
24 use {
25     crate::backend::MAX_IOV,
26     crate::io::{IoSlice, IoSliceMut},
27 };
28 
read(fd: BorrowedFd<'_>, buf: *mut u8, len: usize) -> io::Result<usize>29 pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: *mut u8, len: usize) -> io::Result<usize> {
30     ret_usize(c::read(borrowed_fd(fd), buf.cast(), min(len, READ_LIMIT)))
31 }
32 
write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize>33 pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize> {
34     unsafe {
35         ret_usize(c::write(
36             borrowed_fd(fd),
37             buf.as_ptr().cast(),
38             min(buf.len(), READ_LIMIT),
39         ))
40     }
41 }
42 
pread( fd: BorrowedFd<'_>, buf: *mut u8, len: usize, offset: u64, ) -> io::Result<usize>43 pub(crate) unsafe fn pread(
44     fd: BorrowedFd<'_>,
45     buf: *mut u8,
46     len: usize,
47     offset: u64,
48 ) -> io::Result<usize> {
49     let len = min(len, READ_LIMIT);
50 
51     // Silently cast; we'll get `EINVAL` if the value is negative.
52     let offset = offset as i64;
53 
54     // ESP-IDF and Vita don't support 64-bit offsets.
55     #[cfg(any(target_os = "espidf", target_os = "vita"))]
56     let offset: i32 = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?;
57 
58     ret_usize(c::pread(borrowed_fd(fd), buf.cast(), len, offset))
59 }
60 
pwrite(fd: BorrowedFd<'_>, buf: &[u8], offset: u64) -> io::Result<usize>61 pub(crate) fn pwrite(fd: BorrowedFd<'_>, buf: &[u8], offset: u64) -> io::Result<usize> {
62     let len = min(buf.len(), READ_LIMIT);
63 
64     // Silently cast; we'll get `EINVAL` if the value is negative.
65     let offset = offset as i64;
66 
67     // ESP-IDF and Vita don't support 64-bit offsets.
68     #[cfg(any(target_os = "espidf", target_os = "vita"))]
69     let offset: i32 = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?;
70 
71     unsafe { ret_usize(c::pwrite(borrowed_fd(fd), buf.as_ptr().cast(), len, offset)) }
72 }
73 
74 #[cfg(not(target_os = "espidf"))]
readv(fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize>75 pub(crate) fn readv(fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
76     unsafe {
77         ret_usize(c::readv(
78             borrowed_fd(fd),
79             bufs.as_ptr().cast::<c::iovec>(),
80             min(bufs.len(), MAX_IOV) as c::c_int,
81         ))
82     }
83 }
84 
85 #[cfg(not(target_os = "espidf"))]
writev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>]) -> io::Result<usize>86 pub(crate) fn writev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
87     unsafe {
88         ret_usize(c::writev(
89             borrowed_fd(fd),
90             bufs.as_ptr().cast::<c::iovec>(),
91             min(bufs.len(), MAX_IOV) as c::c_int,
92         ))
93     }
94 }
95 
96 #[cfg(not(any(
97     target_os = "espidf",
98     target_os = "haiku",
99     target_os = "nto",
100     target_os = "redox",
101     target_os = "solaris",
102     target_os = "vita"
103 )))]
preadv( fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut<'_>], offset: u64, ) -> io::Result<usize>104 pub(crate) fn preadv(
105     fd: BorrowedFd<'_>,
106     bufs: &mut [IoSliceMut<'_>],
107     offset: u64,
108 ) -> io::Result<usize> {
109     // Silently cast; we'll get `EINVAL` if the value is negative.
110     let offset = offset as i64;
111     unsafe {
112         ret_usize(c::preadv(
113             borrowed_fd(fd),
114             bufs.as_ptr().cast::<c::iovec>(),
115             min(bufs.len(), MAX_IOV) as c::c_int,
116             offset,
117         ))
118     }
119 }
120 
121 #[cfg(not(any(
122     target_os = "espidf",
123     target_os = "haiku",
124     target_os = "nto",
125     target_os = "redox",
126     target_os = "solaris",
127     target_os = "vita"
128 )))]
pwritev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize>129 pub(crate) fn pwritev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
130     // Silently cast; we'll get `EINVAL` if the value is negative.
131     let offset = offset as i64;
132     unsafe {
133         ret_usize(c::pwritev(
134             borrowed_fd(fd),
135             bufs.as_ptr().cast::<c::iovec>(),
136             min(bufs.len(), MAX_IOV) as c::c_int,
137             offset,
138         ))
139     }
140 }
141 
142 #[cfg(feature = "linux-raw-sys")]
preadv2( fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut<'_>], offset: u64, flags: ReadWriteFlags, ) -> io::Result<usize>143 pub(crate) fn preadv2(
144     fd: BorrowedFd<'_>,
145     bufs: &mut [IoSliceMut<'_>],
146     offset: u64,
147     flags: ReadWriteFlags,
148 ) -> io::Result<usize> {
149     // Silently cast; we'll get `EINVAL` if the value is negative.
150     let offset = offset as i64;
151     unsafe {
152         ret_usize(c::preadv2(
153             borrowed_fd(fd),
154             bufs.as_ptr().cast::<c::iovec>(),
155             min(bufs.len(), MAX_IOV) as c::c_int,
156             offset,
157             bitflags_bits!(flags),
158         ))
159     }
160 }
161 
162 #[cfg(feature = "linux-raw-sys")]
pwritev2( fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>], offset: u64, flags: ReadWriteFlags, ) -> io::Result<usize>163 pub(crate) fn pwritev2(
164     fd: BorrowedFd<'_>,
165     bufs: &[IoSlice<'_>],
166     offset: u64,
167     flags: ReadWriteFlags,
168 ) -> io::Result<usize> {
169     // Silently cast; we'll get `EINVAL` if the value is negative.
170     let offset = offset as i64;
171     unsafe {
172         ret_usize(c::pwritev2(
173             borrowed_fd(fd),
174             bufs.as_ptr().cast::<c::iovec>(),
175             min(bufs.len(), MAX_IOV) as c::c_int,
176             offset,
177             bitflags_bits!(flags),
178         ))
179     }
180 }
181 
182 // These functions are derived from Rust's library/std/src/sys/unix/fd.rs at
183 // revision 326ef470a8b379a180d6dc4bbef08990698a737a.
184 
185 // The maximum read limit on most POSIX-like systems is `SSIZE_MAX`, with the
186 // manual page quoting that if the count of bytes to read is greater than
187 // `SSIZE_MAX` the result is “unspecified”.
188 //
189 // On macOS, however, apparently the 64-bit libc is either buggy or
190 // intentionally showing odd behavior by rejecting any read with a size larger
191 // than or equal to `INT_MAX`. To handle both of these the read size is capped
192 // on both platforms.
193 #[cfg(target_os = "macos")]
194 const READ_LIMIT: usize = c::c_int::MAX as usize - 1;
195 #[cfg(not(target_os = "macos"))]
196 const READ_LIMIT: usize = c::ssize_t::MAX as usize;
197 
close(raw_fd: RawFd)198 pub(crate) unsafe fn close(raw_fd: RawFd) {
199     let _ = c::close(raw_fd as c::c_int);
200 }
201 
202 #[inline]
ioctl( fd: BorrowedFd<'_>, request: RawOpcode, arg: *mut c::c_void, ) -> io::Result<IoctlOutput>203 pub(crate) unsafe fn ioctl(
204     fd: BorrowedFd<'_>,
205     request: RawOpcode,
206     arg: *mut c::c_void,
207 ) -> io::Result<IoctlOutput> {
208     ret_c_int(c::ioctl(borrowed_fd(fd), request, arg))
209 }
210 
211 #[inline]
ioctl_readonly( fd: BorrowedFd<'_>, request: RawOpcode, arg: *mut c::c_void, ) -> io::Result<IoctlOutput>212 pub(crate) unsafe fn ioctl_readonly(
213     fd: BorrowedFd<'_>,
214     request: RawOpcode,
215     arg: *mut c::c_void,
216 ) -> io::Result<IoctlOutput> {
217     ioctl(fd, request, arg)
218 }
219 
220 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
221 #[cfg(all(feature = "fs", feature = "net"))]
is_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)>222 pub(crate) fn is_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> {
223     use core::mem::MaybeUninit;
224 
225     let (mut read, mut write) = crate::fs::fd::_is_file_read_write(fd)?;
226     let mut not_socket = false;
227     if read {
228         // Do a `recv` with `PEEK` and `DONTWAIT` for 1 byte. A 0 indicates
229         // the read side is shut down; an `EWOULDBLOCK` indicates the read
230         // side is still open.
231         match unsafe {
232             c::recv(
233                 borrowed_fd(fd),
234                 MaybeUninit::<[u8; 1]>::uninit()
235                     .as_mut_ptr()
236                     .cast::<c::c_void>(),
237                 1,
238                 c::MSG_PEEK | c::MSG_DONTWAIT,
239             )
240         } {
241             0 => read = false,
242             -1 => {
243                 #[allow(unreachable_patterns)] // `EAGAIN` may equal `EWOULDBLOCK`
244                 match errno().0 {
245                     c::EAGAIN | c::EWOULDBLOCK => (),
246                     c::ENOTSOCK => not_socket = true,
247                     err => return Err(io::Errno(err)),
248                 }
249             }
250             _ => (),
251         }
252     }
253     if write && !not_socket {
254         // Do a `send` with `DONTWAIT` for 0 bytes. An `EPIPE` indicates
255         // the write side is shut down.
256         if unsafe { c::send(borrowed_fd(fd), [].as_ptr(), 0, c::MSG_DONTWAIT) } == -1 {
257             #[allow(unreachable_patterns)] // `EAGAIN` may equal `EWOULDBLOCK`
258             match errno().0 {
259                 c::EAGAIN | c::EWOULDBLOCK | c::ENOTSOCK => (),
260                 c::EPIPE => write = false,
261                 err => return Err(io::Errno(err)),
262             }
263         }
264     }
265     Ok((read, write))
266 }
267 
268 #[cfg(target_os = "wasi")]
269 #[cfg(all(feature = "fs", feature = "net"))]
is_read_write(_fd: BorrowedFd<'_>) -> io::Result<(bool, bool)>270 pub(crate) fn is_read_write(_fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> {
271     todo!("Implement is_read_write for WASI in terms of fd_fdstat_get");
272 }
273 
fcntl_getfd(fd: BorrowedFd<'_>) -> io::Result<FdFlags>274 pub(crate) fn fcntl_getfd(fd: BorrowedFd<'_>) -> io::Result<FdFlags> {
275     let flags = unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GETFD))? };
276     Ok(FdFlags::from_bits_retain(bitcast!(flags)))
277 }
278 
fcntl_setfd(fd: BorrowedFd<'_>, flags: FdFlags) -> io::Result<()>279 pub(crate) fn fcntl_setfd(fd: BorrowedFd<'_>, flags: FdFlags) -> io::Result<()> {
280     unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_SETFD, flags.bits())) }
281 }
282 
283 #[cfg(not(any(target_os = "espidf", target_os = "wasi")))]
fcntl_dupfd_cloexec(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd>284 pub(crate) fn fcntl_dupfd_cloexec(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd> {
285     unsafe { ret_owned_fd(c::fcntl(borrowed_fd(fd), c::F_DUPFD_CLOEXEC, min)) }
286 }
287 
288 #[cfg(target_os = "espidf")]
fcntl_dupfd(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd>289 pub(crate) fn fcntl_dupfd(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd> {
290     unsafe { ret_owned_fd(c::fcntl(borrowed_fd(fd), c::F_DUPFD, min)) }
291 }
292 
293 #[cfg(not(target_os = "wasi"))]
dup(fd: BorrowedFd<'_>) -> io::Result<OwnedFd>294 pub(crate) fn dup(fd: BorrowedFd<'_>) -> io::Result<OwnedFd> {
295     unsafe { ret_owned_fd(c::dup(borrowed_fd(fd))) }
296 }
297 
298 #[allow(clippy::needless_pass_by_ref_mut)]
299 #[cfg(not(target_os = "wasi"))]
dup2(fd: BorrowedFd<'_>, new: &mut OwnedFd) -> io::Result<()>300 pub(crate) fn dup2(fd: BorrowedFd<'_>, new: &mut OwnedFd) -> io::Result<()> {
301     unsafe { ret_discarded_fd(c::dup2(borrowed_fd(fd), borrowed_fd(new.as_fd()))) }
302 }
303 
304 #[allow(clippy::needless_pass_by_ref_mut)]
305 #[cfg(not(any(
306     apple,
307     target_os = "aix",
308     target_os = "android",
309     target_os = "dragonfly",
310     target_os = "espidf",
311     target_os = "haiku",
312     target_os = "nto",
313     target_os = "redox",
314     target_os = "vita",
315     target_os = "wasi",
316 )))]
dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, flags: DupFlags) -> io::Result<()>317 pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, flags: DupFlags) -> io::Result<()> {
318     unsafe {
319         ret_discarded_fd(c::dup3(
320             borrowed_fd(fd),
321             borrowed_fd(new.as_fd()),
322             bitflags_bits!(flags),
323         ))
324     }
325 }
326 
327 #[cfg(any(
328     apple,
329     target_os = "android",
330     target_os = "dragonfly",
331     target_os = "haiku",
332     target_os = "redox",
333 ))]
dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, _flags: DupFlags) -> io::Result<()>334 pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, _flags: DupFlags) -> io::Result<()> {
335     // Android 5.0 has `dup3`, but libc doesn't have bindings. Emulate it
336     // using `dup2`. We don't need to worry about the difference between
337     // `dup2` and `dup3` when the file descriptors are equal because we
338     // have an `&mut OwnedFd` which means `fd` doesn't alias it.
339     dup2(fd, new)
340 }
341