1 //! linux_raw syscalls supporting `rustix::fs`.
2 //!
3 //! # Safety
4 //!
5 //! See the `rustix::backend` module documentation for details.
6 #![allow(unsafe_code)]
7 #![allow(dead_code)]
8 #![allow(clippy::undocumented_unsafe_blocks)]
9
10 use super::super::c;
11 use super::super::conv::{
12 by_ref, c_int, c_uint, dev_t, oflags_for_open_how, opt_mut, pass_usize, raw_fd, ret, ret_c_int,
13 ret_c_uint, ret_owned_fd, ret_usize, size_of, slice_mut, zero,
14 };
15 #[cfg(target_pointer_width = "64")]
16 use super::super::conv::{loff_t, loff_t_from_u64, ret_u64};
17 #[cfg(any(
18 target_arch = "aarch64",
19 target_arch = "riscv64",
20 target_arch = "mips64",
21 target_pointer_width = "32",
22 ))]
23 use crate::fd::AsFd;
24 use crate::fd::{BorrowedFd, OwnedFd};
25 use crate::ffi::CStr;
26 use crate::fs::{
27 Access, Advice, AtFlags, FallocateFlags, FileType, FlockOperation, MemfdFlags, Mode, OFlags,
28 RenameFlags, ResolveFlags, SealFlags, Stat, StatFs, StatVfs, StatVfsMountFlags, StatxFlags,
29 Timestamps,
30 };
31 use crate::io::{self, SeekFrom};
32 use crate::process::{Gid, Uid};
33 use core::convert::TryInto;
34 use core::mem::MaybeUninit;
35 #[cfg(target_arch = "mips64")]
36 use linux_raw_sys::general::stat as linux_stat64;
37 use linux_raw_sys::general::{
38 __kernel_fsid_t, __kernel_timespec, open_how, statx, AT_EACCESS, AT_FDCWD, AT_REMOVEDIR,
39 AT_SYMLINK_NOFOLLOW, F_ADD_SEALS, F_GETFL, F_GETLEASE, F_GETOWN, F_GETPIPE_SZ, F_GETSIG,
40 F_GET_SEALS, F_SETFL, F_SETPIPE_SZ, SEEK_CUR, SEEK_END, SEEK_SET, STATX__RESERVED,
41 };
42 #[cfg(target_pointer_width = "32")]
43 use {
44 super::super::conv::{hi, lo, slice_just_addr},
45 linux_raw_sys::general::stat64 as linux_stat64,
46 linux_raw_sys::general::timespec as __kernel_old_timespec,
47 };
48
49 #[inline]
open(filename: &CStr, flags: OFlags, mode: Mode) -> io::Result<OwnedFd>50 pub(crate) fn open(filename: &CStr, flags: OFlags, mode: Mode) -> io::Result<OwnedFd> {
51 #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
52 {
53 openat(crate::fs::cwd().as_fd(), filename, flags, mode)
54 }
55 #[cfg(all(
56 target_pointer_width = "32",
57 not(any(target_arch = "aarch64", target_arch = "riscv64")),
58 ))]
59 unsafe {
60 ret_owned_fd(syscall_readonly!(__NR_open, filename, flags, mode))
61 }
62 #[cfg(all(
63 target_pointer_width = "64",
64 not(any(target_arch = "aarch64", target_arch = "riscv64")),
65 ))]
66 unsafe {
67 ret_owned_fd(syscall_readonly!(__NR_open, filename, flags, mode))
68 }
69 }
70
71 #[inline]
openat( dirfd: BorrowedFd<'_>, filename: &CStr, flags: OFlags, mode: Mode, ) -> io::Result<OwnedFd>72 pub(crate) fn openat(
73 dirfd: BorrowedFd<'_>,
74 filename: &CStr,
75 flags: OFlags,
76 mode: Mode,
77 ) -> io::Result<OwnedFd> {
78 #[cfg(target_pointer_width = "32")]
79 unsafe {
80 ret_owned_fd(syscall_readonly!(__NR_openat, dirfd, filename, flags, mode))
81 }
82 #[cfg(target_pointer_width = "64")]
83 unsafe {
84 ret_owned_fd(syscall_readonly!(__NR_openat, dirfd, filename, flags, mode))
85 }
86 }
87
88 #[inline]
openat2( dirfd: BorrowedFd<'_>, pathname: &CStr, flags: OFlags, mode: Mode, resolve: ResolveFlags, ) -> io::Result<OwnedFd>89 pub(crate) fn openat2(
90 dirfd: BorrowedFd<'_>,
91 pathname: &CStr,
92 flags: OFlags,
93 mode: Mode,
94 resolve: ResolveFlags,
95 ) -> io::Result<OwnedFd> {
96 #[cfg(target_pointer_width = "32")]
97 unsafe {
98 ret_owned_fd(syscall_readonly!(
99 __NR_openat2,
100 dirfd,
101 pathname,
102 by_ref(&open_how {
103 flags: oflags_for_open_how(flags),
104 mode: u64::from(mode.bits()),
105 resolve: resolve.bits(),
106 }),
107 size_of::<open_how, _>()
108 ))
109 }
110 #[cfg(target_pointer_width = "64")]
111 unsafe {
112 ret_owned_fd(syscall_readonly!(
113 __NR_openat2,
114 dirfd,
115 pathname,
116 by_ref(&open_how {
117 flags: oflags_for_open_how(flags),
118 mode: u64::from(mode.bits()),
119 resolve: resolve.bits(),
120 }),
121 size_of::<open_how, _>()
122 ))
123 }
124 }
125
126 #[inline]
chmod(filename: &CStr, mode: Mode) -> io::Result<()>127 pub(crate) fn chmod(filename: &CStr, mode: Mode) -> io::Result<()> {
128 unsafe {
129 ret(syscall_readonly!(
130 __NR_fchmodat,
131 raw_fd(AT_FDCWD),
132 filename,
133 mode
134 ))
135 }
136 }
137
138 #[inline]
chmodat(dirfd: BorrowedFd<'_>, filename: &CStr, mode: Mode) -> io::Result<()>139 pub(crate) fn chmodat(dirfd: BorrowedFd<'_>, filename: &CStr, mode: Mode) -> io::Result<()> {
140 unsafe { ret(syscall_readonly!(__NR_fchmodat, dirfd, filename, mode)) }
141 }
142
143 #[inline]
fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()>144 pub(crate) fn fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()> {
145 unsafe { ret(syscall_readonly!(__NR_fchmod, fd, mode)) }
146 }
147
148 #[inline]
chownat( dirfd: BorrowedFd<'_>, filename: &CStr, owner: Option<Uid>, group: Option<Gid>, flags: AtFlags, ) -> io::Result<()>149 pub(crate) fn chownat(
150 dirfd: BorrowedFd<'_>,
151 filename: &CStr,
152 owner: Option<Uid>,
153 group: Option<Gid>,
154 flags: AtFlags,
155 ) -> io::Result<()> {
156 unsafe {
157 let (ow, gr) = crate::process::translate_fchown_args(owner, group);
158 ret(syscall_readonly!(
159 __NR_fchownat,
160 dirfd,
161 filename,
162 c_uint(ow),
163 c_uint(gr),
164 flags
165 ))
166 }
167 }
168
169 #[inline]
fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()>170 pub(crate) fn fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> {
171 unsafe {
172 let (ow, gr) = crate::process::translate_fchown_args(owner, group);
173 ret(syscall_readonly!(__NR_fchown, fd, c_uint(ow), c_uint(gr)))
174 }
175 }
176
177 #[inline]
mknodat( dirfd: BorrowedFd<'_>, filename: &CStr, file_type: FileType, mode: Mode, dev: u64, ) -> io::Result<()>178 pub(crate) fn mknodat(
179 dirfd: BorrowedFd<'_>,
180 filename: &CStr,
181 file_type: FileType,
182 mode: Mode,
183 dev: u64,
184 ) -> io::Result<()> {
185 #[cfg(target_pointer_width = "32")]
186 unsafe {
187 ret(syscall_readonly!(
188 __NR_mknodat,
189 dirfd,
190 filename,
191 (mode, file_type),
192 dev_t(dev)?
193 ))
194 }
195 #[cfg(target_pointer_width = "64")]
196 unsafe {
197 ret(syscall_readonly!(
198 __NR_mknodat,
199 dirfd,
200 filename,
201 (mode, file_type),
202 dev_t(dev)
203 ))
204 }
205 }
206
207 #[inline]
seek(fd: BorrowedFd<'_>, pos: SeekFrom) -> io::Result<u64>208 pub(crate) fn seek(fd: BorrowedFd<'_>, pos: SeekFrom) -> io::Result<u64> {
209 let (whence, offset) = match pos {
210 SeekFrom::Start(pos) => {
211 let pos: u64 = pos;
212 // Silently cast; we'll get `EINVAL` if the value is negative.
213 (SEEK_SET, pos as i64)
214 }
215 SeekFrom::End(offset) => (SEEK_END, offset),
216 SeekFrom::Current(offset) => (SEEK_CUR, offset),
217 };
218 _seek(fd, offset, whence)
219 }
220
221 #[inline]
_seek(fd: BorrowedFd<'_>, offset: i64, whence: c::c_uint) -> io::Result<u64>222 pub(crate) fn _seek(fd: BorrowedFd<'_>, offset: i64, whence: c::c_uint) -> io::Result<u64> {
223 #[cfg(target_pointer_width = "32")]
224 unsafe {
225 let mut result = MaybeUninit::<u64>::uninit();
226 ret(syscall!(
227 __NR__llseek,
228 fd,
229 // Don't use the hi/lo functions here because Linux's llseek
230 // takes its 64-bit argument differently from everything else.
231 pass_usize((offset >> 32) as usize),
232 pass_usize(offset as usize),
233 &mut result,
234 c_uint(whence)
235 ))?;
236 Ok(result.assume_init())
237 }
238 #[cfg(target_pointer_width = "64")]
239 unsafe {
240 ret_u64(syscall_readonly!(
241 __NR_lseek,
242 fd,
243 loff_t(offset),
244 c_uint(whence)
245 ))
246 }
247 }
248
249 #[inline]
tell(fd: BorrowedFd<'_>) -> io::Result<u64>250 pub(crate) fn tell(fd: BorrowedFd<'_>) -> io::Result<u64> {
251 _seek(fd, 0, SEEK_CUR).map(|x| x as u64)
252 }
253
254 #[inline]
ftruncate(fd: BorrowedFd<'_>, length: u64) -> io::Result<()>255 pub(crate) fn ftruncate(fd: BorrowedFd<'_>, length: u64) -> io::Result<()> {
256 // <https://github.com/torvalds/linux/blob/fcadab740480e0e0e9fa9bd272acd409884d431a/arch/arm64/kernel/sys32.c#L81-L83>
257 #[cfg(all(
258 target_pointer_width = "32",
259 any(target_arch = "arm", target_arch = "mips", target_arch = "powerpc"),
260 ))]
261 unsafe {
262 ret(syscall_readonly!(
263 __NR_ftruncate64,
264 fd,
265 zero(),
266 hi(length),
267 lo(length)
268 ))
269 }
270 #[cfg(all(
271 target_pointer_width = "32",
272 not(any(target_arch = "arm", target_arch = "mips", target_arch = "powerpc")),
273 ))]
274 unsafe {
275 ret(syscall_readonly!(
276 __NR_ftruncate64,
277 fd,
278 hi(length),
279 lo(length)
280 ))
281 }
282 #[cfg(target_pointer_width = "64")]
283 unsafe {
284 ret(syscall_readonly!(
285 __NR_ftruncate,
286 fd,
287 loff_t_from_u64(length)
288 ))
289 }
290 }
291
292 #[inline]
fallocate( fd: BorrowedFd<'_>, mode: FallocateFlags, offset: u64, len: u64, ) -> io::Result<()>293 pub(crate) fn fallocate(
294 fd: BorrowedFd<'_>,
295 mode: FallocateFlags,
296 offset: u64,
297 len: u64,
298 ) -> io::Result<()> {
299 #[cfg(target_pointer_width = "32")]
300 unsafe {
301 ret(syscall_readonly!(
302 __NR_fallocate,
303 fd,
304 mode,
305 hi(offset),
306 lo(offset),
307 hi(len),
308 lo(len)
309 ))
310 }
311 #[cfg(target_pointer_width = "64")]
312 unsafe {
313 ret(syscall_readonly!(
314 __NR_fallocate,
315 fd,
316 mode,
317 loff_t_from_u64(offset),
318 loff_t_from_u64(len)
319 ))
320 }
321 }
322
323 #[inline]
fadvise(fd: BorrowedFd<'_>, pos: u64, len: u64, advice: Advice) -> io::Result<()>324 pub(crate) fn fadvise(fd: BorrowedFd<'_>, pos: u64, len: u64, advice: Advice) -> io::Result<()> {
325 // On ARM, the arguments are reordered so that the len and pos argument
326 // pairs are aligned. And ARM has a custom syscall code for this.
327 #[cfg(target_arch = "arm")]
328 unsafe {
329 ret(syscall_readonly!(
330 __NR_arm_fadvise64_64,
331 fd,
332 advice,
333 hi(pos),
334 lo(pos),
335 hi(len),
336 lo(len)
337 ))
338 }
339
340 // On powerpc, the arguments are reordered as on ARM.
341 #[cfg(target_arch = "powerpc")]
342 unsafe {
343 ret(syscall_readonly!(
344 __NR_fadvise64_64,
345 fd,
346 advice,
347 hi(pos),
348 lo(pos),
349 hi(len),
350 lo(len)
351 ))
352 }
353 // On mips, the arguments are not reordered, and padding is inserted
354 // instead to ensure alignment.
355 #[cfg(target_arch = "mips")]
356 unsafe {
357 ret(syscall_readonly!(
358 __NR_fadvise64,
359 fd,
360 zero(),
361 hi(pos),
362 lo(pos),
363 hi(len),
364 lo(len),
365 advice
366 ))
367 }
368 #[cfg(all(
369 target_pointer_width = "32",
370 not(any(target_arch = "arm", target_arch = "mips", target_arch = "powerpc")),
371 ))]
372 unsafe {
373 ret(syscall_readonly!(
374 __NR_fadvise64_64,
375 fd,
376 hi(pos),
377 lo(pos),
378 hi(len),
379 lo(len),
380 advice
381 ))
382 }
383 #[cfg(target_pointer_width = "64")]
384 unsafe {
385 ret(syscall_readonly!(
386 __NR_fadvise64,
387 fd,
388 loff_t_from_u64(pos),
389 loff_t_from_u64(len),
390 advice
391 ))
392 }
393 }
394
395 #[inline]
fsync(fd: BorrowedFd<'_>) -> io::Result<()>396 pub(crate) fn fsync(fd: BorrowedFd<'_>) -> io::Result<()> {
397 unsafe { ret(syscall_readonly!(__NR_fsync, fd)) }
398 }
399
400 #[inline]
fdatasync(fd: BorrowedFd<'_>) -> io::Result<()>401 pub(crate) fn fdatasync(fd: BorrowedFd<'_>) -> io::Result<()> {
402 unsafe { ret(syscall_readonly!(__NR_fdatasync, fd)) }
403 }
404
405 #[inline]
flock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()>406 pub(crate) fn flock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> {
407 unsafe { ret(syscall!(__NR_flock, fd, c_uint(operation as c::c_uint))) }
408 }
409
410 #[inline]
fstat(fd: BorrowedFd<'_>) -> io::Result<Stat>411 pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result<Stat> {
412 #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
413 {
414 match statx(fd, cstr!(""), AtFlags::EMPTY_PATH, StatxFlags::BASIC_STATS) {
415 Ok(x) => statx_to_stat(x),
416 Err(io::Errno::NOSYS) => fstat_old(fd),
417 Err(err) => Err(err),
418 }
419 }
420
421 #[cfg(all(target_pointer_width = "64", not(target_arch = "mips64")))]
422 unsafe {
423 let mut result = MaybeUninit::<Stat>::uninit();
424 ret(syscall!(__NR_fstat, fd, &mut result))?;
425 Ok(result.assume_init())
426 }
427 }
428
429 #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
fstat_old(fd: BorrowedFd<'_>) -> io::Result<Stat>430 fn fstat_old(fd: BorrowedFd<'_>) -> io::Result<Stat> {
431 let mut result = MaybeUninit::<linux_stat64>::uninit();
432
433 #[cfg(target_arch = "mips64")]
434 unsafe {
435 ret(syscall!(__NR_fstat, fd, &mut result))?;
436 stat_to_stat(result.assume_init())
437 }
438
439 #[cfg(target_pointer_width = "32")]
440 unsafe {
441 ret(syscall!(__NR_fstat64, fd, &mut result))?;
442 stat_to_stat(result.assume_init())
443 }
444 }
445
446 #[inline]
stat(filename: &CStr) -> io::Result<Stat>447 pub(crate) fn stat(filename: &CStr) -> io::Result<Stat> {
448 #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
449 {
450 match statx(
451 crate::fs::cwd().as_fd(),
452 filename,
453 AtFlags::empty(),
454 StatxFlags::BASIC_STATS,
455 ) {
456 Ok(x) => statx_to_stat(x),
457 Err(io::Errno::NOSYS) => stat_old(filename),
458 Err(err) => Err(err),
459 }
460 }
461
462 #[cfg(all(target_pointer_width = "64", not(target_arch = "mips64")))]
463 unsafe {
464 let mut result = MaybeUninit::<Stat>::uninit();
465 ret(syscall!(
466 __NR_newfstatat,
467 raw_fd(AT_FDCWD),
468 filename,
469 &mut result,
470 c_uint(0)
471 ))?;
472 Ok(result.assume_init())
473 }
474 }
475
476 #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
stat_old(filename: &CStr) -> io::Result<Stat>477 fn stat_old(filename: &CStr) -> io::Result<Stat> {
478 let mut result = MaybeUninit::<linux_stat64>::uninit();
479
480 #[cfg(target_arch = "mips64")]
481 unsafe {
482 ret(syscall!(
483 __NR_newfstatat,
484 raw_fd(AT_FDCWD),
485 filename,
486 &mut result,
487 c_uint(0)
488 ))?;
489 stat_to_stat(result.assume_init())
490 }
491
492 #[cfg(target_pointer_width = "32")]
493 unsafe {
494 ret(syscall!(
495 __NR_fstatat64,
496 raw_fd(AT_FDCWD),
497 filename,
498 &mut result,
499 c_uint(0)
500 ))?;
501 stat_to_stat(result.assume_init())
502 }
503 }
504
505 #[inline]
statat(dirfd: BorrowedFd<'_>, filename: &CStr, flags: AtFlags) -> io::Result<Stat>506 pub(crate) fn statat(dirfd: BorrowedFd<'_>, filename: &CStr, flags: AtFlags) -> io::Result<Stat> {
507 #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
508 {
509 match statx(dirfd, filename, flags, StatxFlags::BASIC_STATS) {
510 Ok(x) => statx_to_stat(x),
511 Err(io::Errno::NOSYS) => statat_old(dirfd, filename, flags),
512 Err(err) => Err(err),
513 }
514 }
515
516 #[cfg(all(target_pointer_width = "64", not(target_arch = "mips64")))]
517 unsafe {
518 let mut result = MaybeUninit::<Stat>::uninit();
519 ret(syscall!(
520 __NR_newfstatat,
521 dirfd,
522 filename,
523 &mut result,
524 flags
525 ))?;
526 Ok(result.assume_init())
527 }
528 }
529
530 #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
statat_old(dirfd: BorrowedFd<'_>, filename: &CStr, flags: AtFlags) -> io::Result<Stat>531 fn statat_old(dirfd: BorrowedFd<'_>, filename: &CStr, flags: AtFlags) -> io::Result<Stat> {
532 let mut result = MaybeUninit::<linux_stat64>::uninit();
533
534 #[cfg(target_arch = "mips64")]
535 unsafe {
536 ret(syscall!(
537 __NR_newfstatat,
538 dirfd,
539 filename,
540 &mut result,
541 flags
542 ))?;
543 stat_to_stat(result.assume_init())
544 }
545
546 #[cfg(target_pointer_width = "32")]
547 unsafe {
548 ret(syscall!(
549 __NR_fstatat64,
550 dirfd,
551 filename,
552 &mut result,
553 flags
554 ))?;
555 stat_to_stat(result.assume_init())
556 }
557 }
558
559 #[inline]
lstat(filename: &CStr) -> io::Result<Stat>560 pub(crate) fn lstat(filename: &CStr) -> io::Result<Stat> {
561 #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
562 {
563 match statx(
564 crate::fs::cwd().as_fd(),
565 filename,
566 AtFlags::SYMLINK_NOFOLLOW,
567 StatxFlags::BASIC_STATS,
568 ) {
569 Ok(x) => statx_to_stat(x),
570 Err(io::Errno::NOSYS) => lstat_old(filename),
571 Err(err) => Err(err),
572 }
573 }
574
575 #[cfg(all(target_pointer_width = "64", not(target_arch = "mips64")))]
576 unsafe {
577 let mut result = MaybeUninit::<Stat>::uninit();
578 ret(syscall!(
579 __NR_newfstatat,
580 raw_fd(AT_FDCWD),
581 filename,
582 &mut result,
583 c_uint(AT_SYMLINK_NOFOLLOW)
584 ))?;
585 Ok(result.assume_init())
586 }
587 }
588
589 #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
lstat_old(filename: &CStr) -> io::Result<Stat>590 fn lstat_old(filename: &CStr) -> io::Result<Stat> {
591 let mut result = MaybeUninit::<linux_stat64>::uninit();
592
593 #[cfg(target_arch = "mips64")]
594 unsafe {
595 ret(syscall!(
596 __NR_newfstatat,
597 raw_fd(AT_FDCWD),
598 filename,
599 &mut result,
600 c_uint(AT_SYMLINK_NOFOLLOW)
601 ))?;
602 stat_to_stat(result.assume_init())
603 }
604
605 #[cfg(target_pointer_width = "32")]
606 unsafe {
607 ret(syscall!(
608 __NR_fstatat64,
609 raw_fd(AT_FDCWD),
610 filename,
611 &mut result,
612 c_uint(AT_SYMLINK_NOFOLLOW)
613 ))?;
614 stat_to_stat(result.assume_init())
615 }
616 }
617
618 /// Convert from a Linux `statx` value to rustix's `Stat`.
619 #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat>620 fn statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat> {
621 Ok(Stat {
622 st_dev: crate::fs::makedev(x.stx_dev_major, x.stx_dev_minor),
623 st_mode: x.stx_mode.into(),
624 st_nlink: x.stx_nlink.into(),
625 st_uid: x.stx_uid.into(),
626 st_gid: x.stx_gid.into(),
627 st_rdev: crate::fs::makedev(x.stx_rdev_major, x.stx_rdev_minor),
628 st_size: x.stx_size.try_into().map_err(|_| io::Errno::OVERFLOW)?,
629 st_blksize: x.stx_blksize.into(),
630 st_blocks: x.stx_blocks.into(),
631 st_atime: x
632 .stx_atime
633 .tv_sec
634 .try_into()
635 .map_err(|_| io::Errno::OVERFLOW)?,
636 st_atime_nsec: x.stx_atime.tv_nsec.into(),
637 st_mtime: x
638 .stx_mtime
639 .tv_sec
640 .try_into()
641 .map_err(|_| io::Errno::OVERFLOW)?,
642 st_mtime_nsec: x.stx_mtime.tv_nsec.into(),
643 st_ctime: x
644 .stx_ctime
645 .tv_sec
646 .try_into()
647 .map_err(|_| io::Errno::OVERFLOW)?,
648 st_ctime_nsec: x.stx_ctime.tv_nsec.into(),
649 st_ino: x.stx_ino.into(),
650 })
651 }
652
653 /// Convert from a Linux `stat64` value to rustix's `Stat`.
654 #[cfg(target_pointer_width = "32")]
stat_to_stat(s64: linux_raw_sys::general::stat64) -> io::Result<Stat>655 fn stat_to_stat(s64: linux_raw_sys::general::stat64) -> io::Result<Stat> {
656 Ok(Stat {
657 st_dev: s64.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?,
658 st_mode: s64.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?,
659 st_nlink: s64.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?,
660 st_uid: s64.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?,
661 st_gid: s64.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?,
662 st_rdev: s64.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?,
663 st_size: s64.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?,
664 st_blksize: s64.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?,
665 st_blocks: s64.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?,
666 st_atime: s64.st_atime.try_into().map_err(|_| io::Errno::OVERFLOW)?,
667 st_atime_nsec: s64
668 .st_atime_nsec
669 .try_into()
670 .map_err(|_| io::Errno::OVERFLOW)?,
671 st_mtime: s64.st_mtime.try_into().map_err(|_| io::Errno::OVERFLOW)?,
672 st_mtime_nsec: s64
673 .st_mtime_nsec
674 .try_into()
675 .map_err(|_| io::Errno::OVERFLOW)?,
676 st_ctime: s64.st_ctime.try_into().map_err(|_| io::Errno::OVERFLOW)?,
677 st_ctime_nsec: s64
678 .st_ctime_nsec
679 .try_into()
680 .map_err(|_| io::Errno::OVERFLOW)?,
681 st_ino: s64.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?,
682 })
683 }
684
685 /// Convert from a Linux `stat` value to rustix's `Stat`.
686 #[cfg(target_arch = "mips64")]
stat_to_stat(s: linux_raw_sys::general::stat) -> io::Result<Stat>687 fn stat_to_stat(s: linux_raw_sys::general::stat) -> io::Result<Stat> {
688 Ok(Stat {
689 st_dev: s.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?,
690 st_mode: s.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?,
691 st_nlink: s.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?,
692 st_uid: s.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?,
693 st_gid: s.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?,
694 st_rdev: s.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?,
695 st_size: s.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?,
696 st_blksize: s.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?,
697 st_blocks: s.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?,
698 st_atime: s.st_atime.try_into().map_err(|_| io::Errno::OVERFLOW)?,
699 st_atime_nsec: s
700 .st_atime_nsec
701 .try_into()
702 .map_err(|_| io::Errno::OVERFLOW)?,
703 st_mtime: s.st_mtime.try_into().map_err(|_| io::Errno::OVERFLOW)?,
704 st_mtime_nsec: s
705 .st_mtime_nsec
706 .try_into()
707 .map_err(|_| io::Errno::OVERFLOW)?,
708 st_ctime: s.st_ctime.try_into().map_err(|_| io::Errno::OVERFLOW)?,
709 st_ctime_nsec: s
710 .st_ctime_nsec
711 .try_into()
712 .map_err(|_| io::Errno::OVERFLOW)?,
713 st_ino: s.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?,
714 })
715 }
716
717 #[inline]
statx( dirfd: BorrowedFd<'_>, pathname: &CStr, flags: AtFlags, mask: StatxFlags, ) -> io::Result<statx>718 pub(crate) fn statx(
719 dirfd: BorrowedFd<'_>,
720 pathname: &CStr,
721 flags: AtFlags,
722 mask: StatxFlags,
723 ) -> io::Result<statx> {
724 // If a future Linux kernel adds more fields to `struct statx` and users
725 // passing flags unknown to rustix in `StatxFlags`, we could end up
726 // writing outside of the buffer. To prevent this possibility, we mask off
727 // any flags that we don't know about.
728 //
729 // This includes `STATX__RESERVED`, which has a value that we know, but
730 // which could take on arbitrary new meaning in the future. Linux currently
731 // rejects this flag with `EINVAL`, so we do the same.
732 //
733 // This doesn't rely on `STATX_ALL` because [it's deprecated] and already
734 // doesn't represent all the known flags.
735 //
736 // [it's deprecated]: https://patchwork.kernel.org/project/linux-fsdevel/patch/20200505095915.11275-7-mszeredi@redhat.com/
737 if (mask.bits() & STATX__RESERVED) == STATX__RESERVED {
738 return Err(io::Errno::INVAL);
739 }
740 let mask = mask & StatxFlags::all();
741
742 unsafe {
743 let mut statx_buf = MaybeUninit::<statx>::uninit();
744 ret(syscall!(
745 __NR_statx,
746 dirfd,
747 pathname,
748 flags,
749 mask,
750 &mut statx_buf
751 ))?;
752 Ok(statx_buf.assume_init())
753 }
754 }
755
756 #[inline]
is_statx_available() -> bool757 pub(crate) fn is_statx_available() -> bool {
758 unsafe {
759 // Call `statx` with null pointers so that if it fails for any reason
760 // other than `EFAULT`, we know it's not supported.
761 matches!(
762 ret(syscall!(
763 __NR_statx,
764 raw_fd(AT_FDCWD),
765 zero(),
766 zero(),
767 zero(),
768 zero()
769 )),
770 Err(io::Errno::FAULT)
771 )
772 }
773 }
774
775 #[inline]
fstatfs(fd: BorrowedFd<'_>) -> io::Result<StatFs>776 pub(crate) fn fstatfs(fd: BorrowedFd<'_>) -> io::Result<StatFs> {
777 #[cfg(target_pointer_width = "32")]
778 unsafe {
779 let mut result = MaybeUninit::<StatFs>::uninit();
780 ret(syscall!(
781 __NR_fstatfs64,
782 fd,
783 size_of::<StatFs, _>(),
784 &mut result
785 ))?;
786 Ok(result.assume_init())
787 }
788
789 #[cfg(target_pointer_width = "64")]
790 unsafe {
791 let mut result = MaybeUninit::<StatFs>::uninit();
792 ret(syscall!(__NR_fstatfs, fd, &mut result))?;
793 Ok(result.assume_init())
794 }
795 }
796
797 #[inline]
fstatvfs(fd: BorrowedFd<'_>) -> io::Result<StatVfs>798 pub(crate) fn fstatvfs(fd: BorrowedFd<'_>) -> io::Result<StatVfs> {
799 // Linux doesn't have an `fstatvfs` syscall; we have to do `fstatfs` and
800 // translate the fields as best we can.
801 let statfs = fstatfs(fd)?;
802
803 Ok(statfs_to_statvfs(statfs))
804 }
805
806 #[inline]
statfs(filename: &CStr) -> io::Result<StatFs>807 pub(crate) fn statfs(filename: &CStr) -> io::Result<StatFs> {
808 #[cfg(target_pointer_width = "32")]
809 unsafe {
810 let mut result = MaybeUninit::<StatFs>::uninit();
811 ret(syscall!(
812 __NR_statfs64,
813 filename,
814 size_of::<StatFs, _>(),
815 &mut result
816 ))?;
817 Ok(result.assume_init())
818 }
819 #[cfg(target_pointer_width = "64")]
820 unsafe {
821 let mut result = MaybeUninit::<StatFs>::uninit();
822 ret(syscall!(__NR_statfs, filename, &mut result))?;
823 Ok(result.assume_init())
824 }
825 }
826
827 #[inline]
statvfs(filename: &CStr) -> io::Result<StatVfs>828 pub(crate) fn statvfs(filename: &CStr) -> io::Result<StatVfs> {
829 // Linux doesn't have a `statvfs` syscall; we have to do `statfs` and
830 // translate the fields as best we can.
831 let statfs = statfs(filename)?;
832
833 Ok(statfs_to_statvfs(statfs))
834 }
835
statfs_to_statvfs(statfs: StatFs) -> StatVfs836 fn statfs_to_statvfs(statfs: StatFs) -> StatVfs {
837 let __kernel_fsid_t { val } = statfs.f_fsid;
838 let [f_fsid_val0, f_fsid_val1]: [i32; 2] = val;
839
840 StatVfs {
841 f_bsize: statfs.f_bsize as u64,
842 f_frsize: if statfs.f_frsize != 0 {
843 statfs.f_frsize
844 } else {
845 statfs.f_bsize
846 } as u64,
847 f_blocks: statfs.f_blocks as u64,
848 f_bfree: statfs.f_bfree as u64,
849 f_bavail: statfs.f_bavail as u64,
850 f_files: statfs.f_files as u64,
851 f_ffree: statfs.f_ffree as u64,
852 f_favail: statfs.f_ffree as u64,
853 f_fsid: f_fsid_val0 as u32 as u64 | ((f_fsid_val1 as u32 as u64) << 32),
854 f_flag: unsafe { StatVfsMountFlags::from_bits_unchecked(statfs.f_flags as u64) },
855 f_namemax: statfs.f_namelen as u64,
856 }
857 }
858
859 #[inline]
readlink(path: &CStr, buf: &mut [u8]) -> io::Result<usize>860 pub(crate) fn readlink(path: &CStr, buf: &mut [u8]) -> io::Result<usize> {
861 let (buf_addr_mut, buf_len) = slice_mut(buf);
862 unsafe {
863 ret_usize(syscall!(
864 __NR_readlinkat,
865 raw_fd(AT_FDCWD),
866 path,
867 buf_addr_mut,
868 buf_len
869 ))
870 }
871 }
872
873 #[inline]
readlinkat(dirfd: BorrowedFd<'_>, path: &CStr, buf: &mut [u8]) -> io::Result<usize>874 pub(crate) fn readlinkat(dirfd: BorrowedFd<'_>, path: &CStr, buf: &mut [u8]) -> io::Result<usize> {
875 let (buf_addr_mut, buf_len) = slice_mut(buf);
876 unsafe {
877 ret_usize(syscall!(
878 __NR_readlinkat,
879 dirfd,
880 path,
881 buf_addr_mut,
882 buf_len
883 ))
884 }
885 }
886
887 #[inline]
fcntl_getfl(fd: BorrowedFd<'_>) -> io::Result<OFlags>888 pub(crate) fn fcntl_getfl(fd: BorrowedFd<'_>) -> io::Result<OFlags> {
889 #[cfg(target_pointer_width = "32")]
890 unsafe {
891 ret_c_uint(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETFL)))
892 .map(OFlags::from_bits_truncate)
893 }
894 #[cfg(target_pointer_width = "64")]
895 unsafe {
896 ret_c_uint(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETFL)))
897 .map(OFlags::from_bits_truncate)
898 }
899 }
900
901 #[inline]
fcntl_setfl(fd: BorrowedFd<'_>, flags: OFlags) -> io::Result<()>902 pub(crate) fn fcntl_setfl(fd: BorrowedFd<'_>, flags: OFlags) -> io::Result<()> {
903 #[cfg(target_pointer_width = "32")]
904 unsafe {
905 ret(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_SETFL), flags))
906 }
907 #[cfg(target_pointer_width = "64")]
908 unsafe {
909 ret(syscall_readonly!(__NR_fcntl, fd, c_uint(F_SETFL), flags))
910 }
911 }
912
913 #[inline]
fcntl_getlease(fd: BorrowedFd<'_>) -> io::Result<c::c_int>914 pub(crate) fn fcntl_getlease(fd: BorrowedFd<'_>) -> io::Result<c::c_int> {
915 #[cfg(target_pointer_width = "32")]
916 unsafe {
917 ret_c_int(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETLEASE)))
918 }
919 #[cfg(target_pointer_width = "64")]
920 unsafe {
921 ret_c_int(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETLEASE)))
922 }
923 }
924
925 #[inline]
fcntl_getown(fd: BorrowedFd<'_>) -> io::Result<c::c_int>926 pub(crate) fn fcntl_getown(fd: BorrowedFd<'_>) -> io::Result<c::c_int> {
927 #[cfg(target_pointer_width = "32")]
928 unsafe {
929 ret_c_int(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETOWN)))
930 }
931 #[cfg(target_pointer_width = "64")]
932 unsafe {
933 ret_c_int(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETOWN)))
934 }
935 }
936
937 #[inline]
fcntl_getsig(fd: BorrowedFd<'_>) -> io::Result<c::c_int>938 pub(crate) fn fcntl_getsig(fd: BorrowedFd<'_>) -> io::Result<c::c_int> {
939 #[cfg(target_pointer_width = "32")]
940 unsafe {
941 ret_c_int(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETSIG)))
942 }
943 #[cfg(target_pointer_width = "64")]
944 unsafe {
945 ret_c_int(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETSIG)))
946 }
947 }
948
949 #[inline]
fcntl_getpipe_sz(fd: BorrowedFd<'_>) -> io::Result<usize>950 pub(crate) fn fcntl_getpipe_sz(fd: BorrowedFd<'_>) -> io::Result<usize> {
951 #[cfg(target_pointer_width = "32")]
952 unsafe {
953 ret_usize(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETPIPE_SZ)))
954 }
955 #[cfg(target_pointer_width = "64")]
956 unsafe {
957 ret_usize(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETPIPE_SZ)))
958 }
959 }
960
961 #[inline]
fcntl_setpipe_sz(fd: BorrowedFd<'_>, size: c::c_int) -> io::Result<usize>962 pub(crate) fn fcntl_setpipe_sz(fd: BorrowedFd<'_>, size: c::c_int) -> io::Result<usize> {
963 #[cfg(target_pointer_width = "32")]
964 unsafe {
965 ret_usize(syscall_readonly!(
966 __NR_fcntl64,
967 fd,
968 c_uint(F_SETPIPE_SZ),
969 c_int(size)
970 ))
971 }
972 #[cfg(target_pointer_width = "64")]
973 unsafe {
974 ret_usize(syscall_readonly!(
975 __NR_fcntl,
976 fd,
977 c_uint(F_SETPIPE_SZ),
978 c_int(size)
979 ))
980 }
981 }
982
983 #[inline]
fcntl_get_seals(fd: BorrowedFd<'_>) -> io::Result<SealFlags>984 pub(crate) fn fcntl_get_seals(fd: BorrowedFd<'_>) -> io::Result<SealFlags> {
985 #[cfg(target_pointer_width = "32")]
986 unsafe {
987 ret_c_int(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GET_SEALS)))
988 .map(|seals| SealFlags::from_bits_unchecked(seals as u32))
989 }
990 #[cfg(target_pointer_width = "64")]
991 unsafe {
992 ret_c_int(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GET_SEALS)))
993 .map(|seals| SealFlags::from_bits_unchecked(seals as u32))
994 }
995 }
996
997 #[inline]
fcntl_add_seals(fd: BorrowedFd<'_>, seals: SealFlags) -> io::Result<()>998 pub(crate) fn fcntl_add_seals(fd: BorrowedFd<'_>, seals: SealFlags) -> io::Result<()> {
999 #[cfg(target_pointer_width = "32")]
1000 unsafe {
1001 ret(syscall_readonly!(
1002 __NR_fcntl64,
1003 fd,
1004 c_uint(F_ADD_SEALS),
1005 seals
1006 ))
1007 }
1008 #[cfg(target_pointer_width = "64")]
1009 unsafe {
1010 ret(syscall_readonly!(
1011 __NR_fcntl,
1012 fd,
1013 c_uint(F_ADD_SEALS),
1014 seals
1015 ))
1016 }
1017 }
1018
1019 #[inline]
rename(oldname: &CStr, newname: &CStr) -> io::Result<()>1020 pub(crate) fn rename(oldname: &CStr, newname: &CStr) -> io::Result<()> {
1021 #[cfg(target_arch = "riscv64")]
1022 unsafe {
1023 ret(syscall_readonly!(
1024 __NR_renameat2,
1025 raw_fd(AT_FDCWD),
1026 oldname,
1027 raw_fd(AT_FDCWD),
1028 newname,
1029 c_uint(0)
1030 ))
1031 }
1032 #[cfg(not(target_arch = "riscv64"))]
1033 unsafe {
1034 ret(syscall_readonly!(
1035 __NR_renameat,
1036 raw_fd(AT_FDCWD),
1037 oldname,
1038 raw_fd(AT_FDCWD),
1039 newname
1040 ))
1041 }
1042 }
1043
1044 #[inline]
renameat( old_dirfd: BorrowedFd<'_>, oldname: &CStr, new_dirfd: BorrowedFd<'_>, newname: &CStr, ) -> io::Result<()>1045 pub(crate) fn renameat(
1046 old_dirfd: BorrowedFd<'_>,
1047 oldname: &CStr,
1048 new_dirfd: BorrowedFd<'_>,
1049 newname: &CStr,
1050 ) -> io::Result<()> {
1051 #[cfg(target_arch = "riscv64")]
1052 unsafe {
1053 ret(syscall_readonly!(
1054 __NR_renameat2,
1055 old_dirfd,
1056 oldname,
1057 new_dirfd,
1058 newname,
1059 c_uint(0)
1060 ))
1061 }
1062 #[cfg(not(target_arch = "riscv64"))]
1063 unsafe {
1064 ret(syscall_readonly!(
1065 __NR_renameat,
1066 old_dirfd,
1067 oldname,
1068 new_dirfd,
1069 newname
1070 ))
1071 }
1072 }
1073
1074 #[inline]
renameat2( old_dirfd: BorrowedFd<'_>, oldname: &CStr, new_dirfd: BorrowedFd<'_>, newname: &CStr, flags: RenameFlags, ) -> io::Result<()>1075 pub(crate) fn renameat2(
1076 old_dirfd: BorrowedFd<'_>,
1077 oldname: &CStr,
1078 new_dirfd: BorrowedFd<'_>,
1079 newname: &CStr,
1080 flags: RenameFlags,
1081 ) -> io::Result<()> {
1082 unsafe {
1083 ret(syscall_readonly!(
1084 __NR_renameat2,
1085 old_dirfd,
1086 oldname,
1087 new_dirfd,
1088 newname,
1089 flags
1090 ))
1091 }
1092 }
1093
1094 #[inline]
unlink(pathname: &CStr) -> io::Result<()>1095 pub(crate) fn unlink(pathname: &CStr) -> io::Result<()> {
1096 unsafe {
1097 ret(syscall_readonly!(
1098 __NR_unlinkat,
1099 raw_fd(AT_FDCWD),
1100 pathname,
1101 c_uint(0)
1102 ))
1103 }
1104 }
1105
1106 #[inline]
unlinkat(dirfd: BorrowedFd<'_>, pathname: &CStr, flags: AtFlags) -> io::Result<()>1107 pub(crate) fn unlinkat(dirfd: BorrowedFd<'_>, pathname: &CStr, flags: AtFlags) -> io::Result<()> {
1108 unsafe { ret(syscall_readonly!(__NR_unlinkat, dirfd, pathname, flags)) }
1109 }
1110
1111 #[inline]
rmdir(pathname: &CStr) -> io::Result<()>1112 pub(crate) fn rmdir(pathname: &CStr) -> io::Result<()> {
1113 unsafe {
1114 ret(syscall_readonly!(
1115 __NR_unlinkat,
1116 raw_fd(AT_FDCWD),
1117 pathname,
1118 c_uint(AT_REMOVEDIR)
1119 ))
1120 }
1121 }
1122
1123 #[inline]
link(oldname: &CStr, newname: &CStr) -> io::Result<()>1124 pub(crate) fn link(oldname: &CStr, newname: &CStr) -> io::Result<()> {
1125 unsafe {
1126 ret(syscall_readonly!(
1127 __NR_linkat,
1128 raw_fd(AT_FDCWD),
1129 oldname,
1130 raw_fd(AT_FDCWD),
1131 newname,
1132 c_uint(0)
1133 ))
1134 }
1135 }
1136
1137 #[inline]
linkat( old_dirfd: BorrowedFd<'_>, oldname: &CStr, new_dirfd: BorrowedFd<'_>, newname: &CStr, flags: AtFlags, ) -> io::Result<()>1138 pub(crate) fn linkat(
1139 old_dirfd: BorrowedFd<'_>,
1140 oldname: &CStr,
1141 new_dirfd: BorrowedFd<'_>,
1142 newname: &CStr,
1143 flags: AtFlags,
1144 ) -> io::Result<()> {
1145 unsafe {
1146 ret(syscall_readonly!(
1147 __NR_linkat,
1148 old_dirfd,
1149 oldname,
1150 new_dirfd,
1151 newname,
1152 flags
1153 ))
1154 }
1155 }
1156
1157 #[inline]
symlink(oldname: &CStr, newname: &CStr) -> io::Result<()>1158 pub(crate) fn symlink(oldname: &CStr, newname: &CStr) -> io::Result<()> {
1159 unsafe {
1160 ret(syscall_readonly!(
1161 __NR_symlinkat,
1162 oldname,
1163 raw_fd(AT_FDCWD),
1164 newname
1165 ))
1166 }
1167 }
1168
1169 #[inline]
symlinkat(oldname: &CStr, dirfd: BorrowedFd<'_>, newname: &CStr) -> io::Result<()>1170 pub(crate) fn symlinkat(oldname: &CStr, dirfd: BorrowedFd<'_>, newname: &CStr) -> io::Result<()> {
1171 unsafe { ret(syscall_readonly!(__NR_symlinkat, oldname, dirfd, newname)) }
1172 }
1173
1174 #[inline]
mkdir(pathname: &CStr, mode: Mode) -> io::Result<()>1175 pub(crate) fn mkdir(pathname: &CStr, mode: Mode) -> io::Result<()> {
1176 unsafe {
1177 ret(syscall_readonly!(
1178 __NR_mkdirat,
1179 raw_fd(AT_FDCWD),
1180 pathname,
1181 mode
1182 ))
1183 }
1184 }
1185
1186 #[inline]
mkdirat(dirfd: BorrowedFd<'_>, pathname: &CStr, mode: Mode) -> io::Result<()>1187 pub(crate) fn mkdirat(dirfd: BorrowedFd<'_>, pathname: &CStr, mode: Mode) -> io::Result<()> {
1188 unsafe { ret(syscall_readonly!(__NR_mkdirat, dirfd, pathname, mode)) }
1189 }
1190
1191 #[inline]
getdents(fd: BorrowedFd<'_>, dirent: &mut [u8]) -> io::Result<usize>1192 pub(crate) fn getdents(fd: BorrowedFd<'_>, dirent: &mut [u8]) -> io::Result<usize> {
1193 let (dirent_addr_mut, dirent_len) = slice_mut(dirent);
1194
1195 unsafe { ret_usize(syscall!(__NR_getdents64, fd, dirent_addr_mut, dirent_len)) }
1196 }
1197
1198 #[inline]
getdents_uninit( fd: BorrowedFd<'_>, dirent: &mut [MaybeUninit<u8>], ) -> io::Result<usize>1199 pub(crate) fn getdents_uninit(
1200 fd: BorrowedFd<'_>,
1201 dirent: &mut [MaybeUninit<u8>],
1202 ) -> io::Result<usize> {
1203 let (dirent_addr_mut, dirent_len) = slice_mut(dirent);
1204
1205 unsafe { ret_usize(syscall!(__NR_getdents64, fd, dirent_addr_mut, dirent_len)) }
1206 }
1207
1208 #[inline]
utimensat( dirfd: BorrowedFd<'_>, pathname: &CStr, times: &Timestamps, flags: AtFlags, ) -> io::Result<()>1209 pub(crate) fn utimensat(
1210 dirfd: BorrowedFd<'_>,
1211 pathname: &CStr,
1212 times: &Timestamps,
1213 flags: AtFlags,
1214 ) -> io::Result<()> {
1215 _utimensat(dirfd, Some(pathname), times, flags)
1216 }
1217
1218 #[inline]
_utimensat( dirfd: BorrowedFd<'_>, pathname: Option<&CStr>, times: &Timestamps, flags: AtFlags, ) -> io::Result<()>1219 fn _utimensat(
1220 dirfd: BorrowedFd<'_>,
1221 pathname: Option<&CStr>,
1222 times: &Timestamps,
1223 flags: AtFlags,
1224 ) -> io::Result<()> {
1225 // Assert that `Timestamps` has the expected layout.
1226 let _ = unsafe { core::mem::transmute::<Timestamps, [__kernel_timespec; 2]>(times.clone()) };
1227
1228 #[cfg(target_pointer_width = "32")]
1229 unsafe {
1230 match ret(syscall_readonly!(
1231 __NR_utimensat_time64,
1232 dirfd,
1233 pathname,
1234 by_ref(times),
1235 flags
1236 )) {
1237 Err(io::Errno::NOSYS) => _utimensat_old(dirfd, pathname, times, flags),
1238 otherwise => otherwise,
1239 }
1240 }
1241 #[cfg(target_pointer_width = "64")]
1242 unsafe {
1243 ret(syscall_readonly!(
1244 __NR_utimensat,
1245 dirfd,
1246 pathname,
1247 by_ref(times),
1248 flags
1249 ))
1250 }
1251 }
1252
1253 #[cfg(target_pointer_width = "32")]
_utimensat_old( dirfd: BorrowedFd<'_>, pathname: Option<&CStr>, times: &Timestamps, flags: AtFlags, ) -> io::Result<()>1254 unsafe fn _utimensat_old(
1255 dirfd: BorrowedFd<'_>,
1256 pathname: Option<&CStr>,
1257 times: &Timestamps,
1258 flags: AtFlags,
1259 ) -> io::Result<()> {
1260 // See the comments in `rustix_clock_gettime_via_syscall` about
1261 // emulation.
1262 let old_times = [
1263 __kernel_old_timespec {
1264 tv_sec: times
1265 .last_access
1266 .tv_sec
1267 .try_into()
1268 .map_err(|_| io::Errno::OVERFLOW)?,
1269 tv_nsec: times
1270 .last_access
1271 .tv_nsec
1272 .try_into()
1273 .map_err(|_| io::Errno::INVAL)?,
1274 },
1275 __kernel_old_timespec {
1276 tv_sec: times
1277 .last_modification
1278 .tv_sec
1279 .try_into()
1280 .map_err(|_| io::Errno::OVERFLOW)?,
1281 tv_nsec: times
1282 .last_modification
1283 .tv_nsec
1284 .try_into()
1285 .map_err(|_| io::Errno::INVAL)?,
1286 },
1287 ];
1288 // The length of the array is fixed and not passed into the syscall.
1289 let old_times_addr = slice_just_addr(&old_times);
1290 ret(syscall_readonly!(
1291 __NR_utimensat,
1292 dirfd,
1293 pathname,
1294 old_times_addr,
1295 flags
1296 ))
1297 }
1298
1299 #[inline]
futimens(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()>1300 pub(crate) fn futimens(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()> {
1301 _utimensat(fd, None, times, AtFlags::empty())
1302 }
1303
accessat( dirfd: BorrowedFd<'_>, path: &CStr, access: Access, flags: AtFlags, ) -> io::Result<()>1304 pub(crate) fn accessat(
1305 dirfd: BorrowedFd<'_>,
1306 path: &CStr,
1307 access: Access,
1308 flags: AtFlags,
1309 ) -> io::Result<()> {
1310 // Linux's `faccessat` doesn't have a flags parameter. If we have
1311 // `AT_EACCESS` and we're not setuid or setgid, we can emulate it.
1312 if flags.is_empty()
1313 || (flags.bits() == AT_EACCESS
1314 && crate::process::getuid() == crate::process::geteuid()
1315 && crate::process::getgid() == crate::process::getegid())
1316 {
1317 return unsafe { ret(syscall_readonly!(__NR_faccessat, dirfd, path, access)) };
1318 }
1319
1320 if flags.bits() != AT_EACCESS {
1321 return Err(io::Errno::INVAL);
1322 }
1323
1324 // TODO: Use faccessat2 in newer Linux versions.
1325 Err(io::Errno::NOSYS)
1326 }
1327
1328 #[inline]
copy_file_range( fd_in: BorrowedFd<'_>, off_in: Option<&mut u64>, fd_out: BorrowedFd<'_>, off_out: Option<&mut u64>, len: u64, ) -> io::Result<u64>1329 pub(crate) fn copy_file_range(
1330 fd_in: BorrowedFd<'_>,
1331 off_in: Option<&mut u64>,
1332 fd_out: BorrowedFd<'_>,
1333 off_out: Option<&mut u64>,
1334 len: u64,
1335 ) -> io::Result<u64> {
1336 let len: usize = len.try_into().unwrap_or(usize::MAX);
1337 _copy_file_range(fd_in, off_in, fd_out, off_out, len, 0).map(|result| result as u64)
1338 }
1339
1340 #[inline]
_copy_file_range( fd_in: BorrowedFd<'_>, off_in: Option<&mut u64>, fd_out: BorrowedFd<'_>, off_out: Option<&mut u64>, len: usize, flags: c::c_uint, ) -> io::Result<usize>1341 fn _copy_file_range(
1342 fd_in: BorrowedFd<'_>,
1343 off_in: Option<&mut u64>,
1344 fd_out: BorrowedFd<'_>,
1345 off_out: Option<&mut u64>,
1346 len: usize,
1347 flags: c::c_uint,
1348 ) -> io::Result<usize> {
1349 unsafe {
1350 ret_usize(syscall!(
1351 __NR_copy_file_range,
1352 fd_in,
1353 opt_mut(off_in),
1354 fd_out,
1355 opt_mut(off_out),
1356 pass_usize(len),
1357 c_uint(flags)
1358 ))
1359 }
1360 }
1361
1362 #[inline]
memfd_create(name: &CStr, flags: MemfdFlags) -> io::Result<OwnedFd>1363 pub(crate) fn memfd_create(name: &CStr, flags: MemfdFlags) -> io::Result<OwnedFd> {
1364 unsafe { ret_owned_fd(syscall_readonly!(__NR_memfd_create, name, flags)) }
1365 }
1366
1367 #[inline]
sendfile( out_fd: BorrowedFd<'_>, in_fd: BorrowedFd<'_>, offset: Option<&mut u64>, count: usize, ) -> io::Result<usize>1368 pub(crate) fn sendfile(
1369 out_fd: BorrowedFd<'_>,
1370 in_fd: BorrowedFd<'_>,
1371 offset: Option<&mut u64>,
1372 count: usize,
1373 ) -> io::Result<usize> {
1374 #[cfg(target_pointer_width = "32")]
1375 unsafe {
1376 ret_usize(syscall!(
1377 __NR_sendfile64,
1378 out_fd,
1379 in_fd,
1380 opt_mut(offset),
1381 pass_usize(count)
1382 ))
1383 }
1384 #[cfg(target_pointer_width = "64")]
1385 unsafe {
1386 ret_usize(syscall!(
1387 __NR_sendfile,
1388 out_fd,
1389 in_fd,
1390 opt_mut(offset),
1391 pass_usize(count)
1392 ))
1393 }
1394 }
1395
1396 #[inline]
1397 #[cfg(any(target_os = "android", target_os = "linux"))]
mount( source: Option<&CStr>, target: &CStr, file_system_type: Option<&CStr>, flags: super::types::MountFlagsArg, data: Option<&CStr>, ) -> io::Result<()>1398 pub(crate) fn mount(
1399 source: Option<&CStr>,
1400 target: &CStr,
1401 file_system_type: Option<&CStr>,
1402 flags: super::types::MountFlagsArg,
1403 data: Option<&CStr>,
1404 ) -> io::Result<()> {
1405 unsafe {
1406 ret(syscall_readonly!(
1407 __NR_mount,
1408 source,
1409 target,
1410 file_system_type,
1411 flags,
1412 data
1413 ))
1414 }
1415 }
1416