rustix/backend/linux_raw/fs/
syscalls.rs

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