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