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