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