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