1 //! linux_raw syscalls supporting `rustix::io`.
2 //!
3 //! # Safety
4 //!
5 //! See the `rustix::backend` module documentation for details.
6 #![allow(unsafe_code)]
7 #![allow(clippy::undocumented_unsafe_blocks)]
8 
9 #[cfg(target_pointer_width = "64")]
10 use crate::backend::conv::loff_t_from_u64;
11 #[cfg(all(
12     target_pointer_width = "32",
13     any(target_arch = "arm", target_arch = "mips", target_arch = "mips32r6"),
14 ))]
15 use crate::backend::conv::zero;
16 use crate::backend::conv::{
17     c_uint, pass_usize, raw_fd, ret, ret_c_int, ret_c_uint, ret_discarded_fd, ret_owned_fd,
18     ret_usize, slice,
19 };
20 #[cfg(target_pointer_width = "32")]
21 use crate::backend::conv::{hi, lo};
22 use crate::backend::{c, MAX_IOV};
23 use crate::fd::{AsFd, BorrowedFd, OwnedFd, RawFd};
24 use crate::io::{self, DupFlags, FdFlags, IoSlice, IoSliceMut, ReadWriteFlags};
25 use crate::ioctl::{IoctlOutput, RawOpcode};
26 #[cfg(all(feature = "fs", feature = "net"))]
27 use crate::net::{RecvFlags, SendFlags};
28 use core::cmp;
29 use linux_raw_sys::general::{F_DUPFD_CLOEXEC, F_GETFD, F_SETFD};
30 
31 #[inline]
read(fd: BorrowedFd<'_>, buf: *mut u8, len: usize) -> io::Result<usize>32 pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: *mut u8, len: usize) -> io::Result<usize> {
33     ret_usize(syscall!(__NR_read, fd, buf, pass_usize(len)))
34 }
35 
36 #[inline]
pread( fd: BorrowedFd<'_>, buf: *mut u8, len: usize, pos: u64, ) -> io::Result<usize>37 pub(crate) unsafe fn pread(
38     fd: BorrowedFd<'_>,
39     buf: *mut u8,
40     len: usize,
41     pos: u64,
42 ) -> io::Result<usize> {
43     // <https://github.com/torvalds/linux/blob/fcadab740480e0e0e9fa9bd272acd409884d431a/arch/arm64/kernel/sys32.c#L75>
44     #[cfg(all(
45         target_pointer_width = "32",
46         any(target_arch = "arm", target_arch = "mips", target_arch = "mips32r6"),
47     ))]
48     {
49         ret_usize(syscall!(
50             __NR_pread64,
51             fd,
52             buf,
53             pass_usize(len),
54             zero(),
55             hi(pos),
56             lo(pos)
57         ))
58     }
59     #[cfg(all(
60         target_pointer_width = "32",
61         not(any(target_arch = "arm", target_arch = "mips", target_arch = "mips32r6")),
62     ))]
63     {
64         ret_usize(syscall!(
65             __NR_pread64,
66             fd,
67             buf,
68             pass_usize(len),
69             hi(pos),
70             lo(pos)
71         ))
72     }
73     #[cfg(target_pointer_width = "64")]
74     ret_usize(syscall!(
75         __NR_pread64,
76         fd,
77         buf,
78         pass_usize(len),
79         loff_t_from_u64(pos)
80     ))
81 }
82 
83 #[inline]
readv(fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize>84 pub(crate) fn readv(fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
85     let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]);
86 
87     unsafe { ret_usize(syscall!(__NR_readv, fd, bufs_addr, bufs_len)) }
88 }
89 
90 #[inline]
preadv( fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut<'_>], pos: u64, ) -> io::Result<usize>91 pub(crate) fn preadv(
92     fd: BorrowedFd<'_>,
93     bufs: &mut [IoSliceMut<'_>],
94     pos: u64,
95 ) -> io::Result<usize> {
96     let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]);
97 
98     // Unlike the plain "p" functions, the "pv" functions pass their offset in
99     // an endian-independent way, and always in two registers.
100     unsafe {
101         ret_usize(syscall!(
102             __NR_preadv,
103             fd,
104             bufs_addr,
105             bufs_len,
106             pass_usize(pos as usize),
107             pass_usize((pos >> 32) as usize)
108         ))
109     }
110 }
111 
112 #[inline]
preadv2( fd: BorrowedFd<'_>, bufs: &mut [IoSliceMut<'_>], pos: u64, flags: ReadWriteFlags, ) -> io::Result<usize>113 pub(crate) fn preadv2(
114     fd: BorrowedFd<'_>,
115     bufs: &mut [IoSliceMut<'_>],
116     pos: u64,
117     flags: ReadWriteFlags,
118 ) -> io::Result<usize> {
119     let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]);
120 
121     // Unlike the plain "p" functions, the "pv" functions pass their offset in
122     // an endian-independent way, and always in two registers.
123     unsafe {
124         ret_usize(syscall!(
125             __NR_preadv2,
126             fd,
127             bufs_addr,
128             bufs_len,
129             pass_usize(pos as usize),
130             pass_usize((pos >> 32) as usize),
131             flags
132         ))
133     }
134 }
135 
136 #[inline]
write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize>137 pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize> {
138     let (buf_addr, buf_len) = slice(buf);
139 
140     unsafe { ret_usize(syscall_readonly!(__NR_write, fd, buf_addr, buf_len)) }
141 }
142 
143 #[inline]
pwrite(fd: BorrowedFd<'_>, buf: &[u8], pos: u64) -> io::Result<usize>144 pub(crate) fn pwrite(fd: BorrowedFd<'_>, buf: &[u8], pos: u64) -> io::Result<usize> {
145     let (buf_addr, buf_len) = slice(buf);
146 
147     // <https://github.com/torvalds/linux/blob/fcadab740480e0e0e9fa9bd272acd409884d431a/arch/arm64/kernel/sys32.c#L81-L83>
148     #[cfg(all(
149         target_pointer_width = "32",
150         any(target_arch = "arm", target_arch = "mips", target_arch = "mips32r6"),
151     ))]
152     unsafe {
153         ret_usize(syscall_readonly!(
154             __NR_pwrite64,
155             fd,
156             buf_addr,
157             buf_len,
158             zero(),
159             hi(pos),
160             lo(pos)
161         ))
162     }
163     #[cfg(all(
164         target_pointer_width = "32",
165         not(any(target_arch = "arm", target_arch = "mips", target_arch = "mips32r6")),
166     ))]
167     unsafe {
168         ret_usize(syscall_readonly!(
169             __NR_pwrite64,
170             fd,
171             buf_addr,
172             buf_len,
173             hi(pos),
174             lo(pos)
175         ))
176     }
177     #[cfg(target_pointer_width = "64")]
178     unsafe {
179         ret_usize(syscall_readonly!(
180             __NR_pwrite64,
181             fd,
182             buf_addr,
183             buf_len,
184             loff_t_from_u64(pos)
185         ))
186     }
187 }
188 
189 #[inline]
writev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>]) -> io::Result<usize>190 pub(crate) fn writev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
191     let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]);
192 
193     unsafe { ret_usize(syscall_readonly!(__NR_writev, fd, bufs_addr, bufs_len)) }
194 }
195 
196 #[inline]
pwritev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>], pos: u64) -> io::Result<usize>197 pub(crate) fn pwritev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>], pos: u64) -> io::Result<usize> {
198     let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]);
199 
200     // Unlike the plain "p" functions, the "pv" functions pass their offset in
201     // an endian-independent way, and always in two registers.
202     unsafe {
203         ret_usize(syscall_readonly!(
204             __NR_pwritev,
205             fd,
206             bufs_addr,
207             bufs_len,
208             pass_usize(pos as usize),
209             pass_usize((pos >> 32) as usize)
210         ))
211     }
212 }
213 
214 #[inline]
pwritev2( fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>], pos: u64, flags: ReadWriteFlags, ) -> io::Result<usize>215 pub(crate) fn pwritev2(
216     fd: BorrowedFd<'_>,
217     bufs: &[IoSlice<'_>],
218     pos: u64,
219     flags: ReadWriteFlags,
220 ) -> io::Result<usize> {
221     let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]);
222 
223     // Unlike the plain "p" functions, the "pv" functions pass their offset in
224     // an endian-independent way, and always in two registers.
225     unsafe {
226         ret_usize(syscall_readonly!(
227             __NR_pwritev2,
228             fd,
229             bufs_addr,
230             bufs_len,
231             pass_usize(pos as usize),
232             pass_usize((pos >> 32) as usize),
233             flags
234         ))
235     }
236 }
237 
238 #[inline]
close(fd: RawFd)239 pub(crate) unsafe fn close(fd: RawFd) {
240     // See the documentation for [`io::close`] for why errors are ignored.
241     syscall_readonly!(__NR_close, raw_fd(fd)).decode_void();
242 }
243 
244 #[inline]
ioctl( fd: BorrowedFd<'_>, request: RawOpcode, arg: *mut c::c_void, ) -> io::Result<IoctlOutput>245 pub(crate) unsafe fn ioctl(
246     fd: BorrowedFd<'_>,
247     request: RawOpcode,
248     arg: *mut c::c_void,
249 ) -> io::Result<IoctlOutput> {
250     ret_c_int(syscall!(__NR_ioctl, fd, c_uint(request), arg))
251 }
252 
253 #[inline]
ioctl_readonly( fd: BorrowedFd<'_>, request: RawOpcode, arg: *mut c::c_void, ) -> io::Result<IoctlOutput>254 pub(crate) unsafe fn ioctl_readonly(
255     fd: BorrowedFd<'_>,
256     request: RawOpcode,
257     arg: *mut c::c_void,
258 ) -> io::Result<IoctlOutput> {
259     ret_c_int(syscall_readonly!(__NR_ioctl, fd, c_uint(request), arg))
260 }
261 
262 #[cfg(all(feature = "fs", feature = "net"))]
is_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)>263 pub(crate) fn is_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> {
264     let (mut read, mut write) = crate::fs::fd::_is_file_read_write(fd)?;
265     let mut not_socket = false;
266     if read {
267         // Do a `recv` with `PEEK` and `DONTWAIT` for 1 byte. A 0 indicates
268         // the read side is shut down; an `EWOULDBLOCK` indicates the read
269         // side is still open.
270         let mut buf = [core::mem::MaybeUninit::<u8>::uninit()];
271         match unsafe {
272             crate::backend::net::syscalls::recv(
273                 fd,
274                 buf.as_mut_ptr() as *mut u8,
275                 1,
276                 RecvFlags::PEEK | RecvFlags::DONTWAIT,
277             )
278         } {
279             Ok(0) => read = false,
280             Err(err) => {
281                 #[allow(unreachable_patterns)] // `EAGAIN` may equal `EWOULDBLOCK`
282                 match err {
283                     io::Errno::AGAIN | io::Errno::WOULDBLOCK => (),
284                     io::Errno::NOTSOCK => not_socket = true,
285                     _ => return Err(err),
286                 }
287             }
288             Ok(_) => (),
289         }
290     }
291     if write && !not_socket {
292         // Do a `send` with `DONTWAIT` for 0 bytes. An `EPIPE` indicates
293         // the write side is shut down.
294         #[allow(unreachable_patterns)] // `EAGAIN` equals `EWOULDBLOCK`
295         match crate::backend::net::syscalls::send(fd, &[], SendFlags::DONTWAIT) {
296             Err(io::Errno::AGAIN | io::Errno::WOULDBLOCK | io::Errno::NOTSOCK) => (),
297             Err(io::Errno::PIPE) => write = false,
298             Err(err) => return Err(err),
299             Ok(_) => (),
300         }
301     }
302     Ok((read, write))
303 }
304 
305 #[inline]
dup(fd: BorrowedFd<'_>) -> io::Result<OwnedFd>306 pub(crate) fn dup(fd: BorrowedFd<'_>) -> io::Result<OwnedFd> {
307     unsafe { ret_owned_fd(syscall_readonly!(__NR_dup, fd)) }
308 }
309 
310 #[allow(clippy::needless_pass_by_ref_mut)]
311 #[inline]
dup2(fd: BorrowedFd<'_>, new: &mut OwnedFd) -> io::Result<()>312 pub(crate) fn dup2(fd: BorrowedFd<'_>, new: &mut OwnedFd) -> io::Result<()> {
313     #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
314     {
315         // We don't need to worry about the difference between `dup2` and
316         // `dup3` when the file descriptors are equal because we have an
317         // `&mut OwnedFd` which means `fd` doesn't alias it.
318         dup3(fd, new, DupFlags::empty())
319     }
320 
321     #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
322     unsafe {
323         ret_discarded_fd(syscall_readonly!(__NR_dup2, fd, new.as_fd()))
324     }
325 }
326 
327 #[allow(clippy::needless_pass_by_ref_mut)]
328 #[inline]
dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, flags: DupFlags) -> io::Result<()>329 pub(crate) fn dup3(fd: BorrowedFd<'_>, new: &mut OwnedFd, flags: DupFlags) -> io::Result<()> {
330     unsafe { ret_discarded_fd(syscall_readonly!(__NR_dup3, fd, new.as_fd(), flags)) }
331 }
332 
333 #[inline]
fcntl_getfd(fd: BorrowedFd<'_>) -> io::Result<FdFlags>334 pub(crate) fn fcntl_getfd(fd: BorrowedFd<'_>) -> io::Result<FdFlags> {
335     #[cfg(target_pointer_width = "32")]
336     unsafe {
337         ret_c_uint(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETFD)))
338             .map(FdFlags::from_bits_retain)
339     }
340     #[cfg(target_pointer_width = "64")]
341     unsafe {
342         ret_c_uint(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETFD)))
343             .map(FdFlags::from_bits_retain)
344     }
345 }
346 
347 #[inline]
fcntl_setfd(fd: BorrowedFd<'_>, flags: FdFlags) -> io::Result<()>348 pub(crate) fn fcntl_setfd(fd: BorrowedFd<'_>, flags: FdFlags) -> io::Result<()> {
349     #[cfg(target_pointer_width = "32")]
350     unsafe {
351         ret(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_SETFD), flags))
352     }
353     #[cfg(target_pointer_width = "64")]
354     unsafe {
355         ret(syscall_readonly!(__NR_fcntl, fd, c_uint(F_SETFD), flags))
356     }
357 }
358 
359 #[inline]
fcntl_dupfd_cloexec(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd>360 pub(crate) fn fcntl_dupfd_cloexec(fd: BorrowedFd<'_>, min: RawFd) -> io::Result<OwnedFd> {
361     #[cfg(target_pointer_width = "32")]
362     unsafe {
363         ret_owned_fd(syscall_readonly!(
364             __NR_fcntl64,
365             fd,
366             c_uint(F_DUPFD_CLOEXEC),
367             raw_fd(min)
368         ))
369     }
370     #[cfg(target_pointer_width = "64")]
371     unsafe {
372         ret_owned_fd(syscall_readonly!(
373             __NR_fcntl,
374             fd,
375             c_uint(F_DUPFD_CLOEXEC),
376             raw_fd(min)
377         ))
378     }
379 }
380