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    all(
18        target_pointer_width = "32",
19        any(target_arch = "arm", target_arch = "powerpc"),
20    )
21))]
22use crate::backend::conv::zero;
23use crate::backend::conv::{
24    by_ref, c_int, c_uint, dev_t, opt_mut, pass_usize, raw_fd, ret, ret_c_int, ret_c_uint,
25    ret_infallible, ret_owned_fd, ret_usize, size_of, slice, slice_mut,
26};
27#[cfg(target_pointer_width = "64")]
28use crate::backend::conv::{loff_t, loff_t_from_u64, ret_u64};
29use crate::fd::{BorrowedFd, OwnedFd};
30use crate::ffi::CStr;
31#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
32use crate::fs::CWD;
33use crate::fs::{
34    inotify, Access, Advice, AtFlags, FallocateFlags, FileType, FlockOperation, Fsid, Gid,
35    MemfdFlags, Mode, OFlags, RenameFlags, ResolveFlags, SealFlags, SeekFrom, Stat, StatFs,
36    StatVfs, StatVfsMountFlags, Statx, StatxFlags, Timestamps, Uid, XattrFlags,
37};
38use crate::io;
39use core::mem::MaybeUninit;
40use core::num::NonZeroU64;
41#[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))]
42use linux_raw_sys::general::stat as linux_stat64;
43use linux_raw_sys::general::{
44    open_how, AT_EACCESS, AT_FDCWD, AT_REMOVEDIR, AT_SYMLINK_NOFOLLOW, F_ADD_SEALS, F_GETFL,
45    F_GET_SEALS, F_SETFL, SEEK_CUR, SEEK_DATA, SEEK_END, SEEK_HOLE, SEEK_SET, STATX__RESERVED,
46};
47#[cfg(target_pointer_width = "32")]
48use {
49    crate::backend::conv::{hi, lo, slice_just_addr},
50    linux_raw_sys::general::stat64 as linux_stat64,
51    linux_raw_sys::general::timespec as __kernel_old_timespec,
52};
53
54#[inline]
55pub(crate) fn open(path: &CStr, flags: OFlags, mode: Mode) -> io::Result<OwnedFd> {
56    // Always enable support for large files.
57    let flags = flags | OFlags::LARGEFILE;
58
59    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
60    {
61        openat(CWD, path, flags, mode)
62    }
63    #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
64    unsafe {
65        ret_owned_fd(syscall_readonly!(__NR_open, path, flags, mode))
66    }
67}
68
69#[inline]
70pub(crate) fn openat(
71    dirfd: BorrowedFd<'_>,
72    path: &CStr,
73    flags: OFlags,
74    mode: Mode,
75) -> io::Result<OwnedFd> {
76    // Always enable support for large files.
77    let flags = flags | OFlags::LARGEFILE;
78
79    unsafe { ret_owned_fd(syscall_readonly!(__NR_openat, dirfd, path, flags, mode)) }
80}
81
82#[inline]
83pub(crate) fn openat2(
84    dirfd: BorrowedFd<'_>,
85    path: &CStr,
86    mut flags: OFlags,
87    mode: Mode,
88    resolve: ResolveFlags,
89) -> io::Result<OwnedFd> {
90    // Enable support for large files, but not with `O_PATH` because
91    // `openat2` doesn't like those flags together.
92    if !flags.contains(OFlags::PATH) {
93        flags |= OFlags::from_bits_retain(c::O_LARGEFILE);
94    }
95
96    unsafe {
97        ret_owned_fd(syscall_readonly!(
98            __NR_openat2,
99            dirfd,
100            path,
101            by_ref(&open_how {
102                flags: oflags_for_open_how(flags),
103                mode: u64::from(mode.bits()),
104                resolve: resolve.bits(),
105            }),
106            size_of::<open_how, _>()
107        ))
108    }
109}
110
111#[inline]
112pub(crate) fn chmod(path: &CStr, mode: Mode) -> io::Result<()> {
113    unsafe {
114        ret(syscall_readonly!(
115            __NR_fchmodat,
116            raw_fd(AT_FDCWD),
117            path,
118            mode
119        ))
120    }
121}
122
123#[inline]
124pub(crate) fn chmodat(
125    dirfd: BorrowedFd<'_>,
126    path: &CStr,
127    mode: Mode,
128    flags: AtFlags,
129) -> io::Result<()> {
130    if flags == AtFlags::SYMLINK_NOFOLLOW {
131        return Err(io::Errno::OPNOTSUPP);
132    }
133    if !flags.is_empty() {
134        return Err(io::Errno::INVAL);
135    }
136    unsafe { ret(syscall_readonly!(__NR_fchmodat, dirfd, path, mode)) }
137}
138
139#[inline]
140pub(crate) fn fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()> {
141    unsafe { ret(syscall_readonly!(__NR_fchmod, fd, mode)) }
142}
143
144#[inline]
145pub(crate) fn chownat(
146    dirfd: BorrowedFd<'_>,
147    path: &CStr,
148    owner: Option<Uid>,
149    group: Option<Gid>,
150    flags: AtFlags,
151) -> io::Result<()> {
152    unsafe {
153        let (ow, gr) = crate::ugid::translate_fchown_args(owner, group);
154        ret(syscall_readonly!(
155            __NR_fchownat,
156            dirfd,
157            path,
158            c_uint(ow),
159            c_uint(gr),
160            flags
161        ))
162    }
163}
164
165#[inline]
166pub(crate) fn chown(path: &CStr, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> {
167    // Most architectures have a `chown` syscall.
168    #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
169    unsafe {
170        let (ow, gr) = crate::ugid::translate_fchown_args(owner, group);
171        ret(syscall_readonly!(__NR_chown, path, c_uint(ow), c_uint(gr)))
172    }
173
174    // Aarch64 and RISC-V don't, so use `fchownat`.
175    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
176    unsafe {
177        let (ow, gr) = crate::ugid::translate_fchown_args(owner, group);
178        ret(syscall_readonly!(
179            __NR_fchownat,
180            raw_fd(AT_FDCWD),
181            path,
182            c_uint(ow),
183            c_uint(gr),
184            zero()
185        ))
186    }
187}
188
189#[inline]
190pub(crate) fn fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> {
191    unsafe {
192        let (ow, gr) = crate::ugid::translate_fchown_args(owner, group);
193        ret(syscall_readonly!(__NR_fchown, fd, c_uint(ow), c_uint(gr)))
194    }
195}
196
197#[inline]
198pub(crate) fn mknodat(
199    dirfd: BorrowedFd<'_>,
200    path: &CStr,
201    file_type: FileType,
202    mode: Mode,
203    dev: u64,
204) -> io::Result<()> {
205    #[cfg(target_pointer_width = "32")]
206    unsafe {
207        ret(syscall_readonly!(
208            __NR_mknodat,
209            dirfd,
210            path,
211            (mode, file_type),
212            dev_t(dev)?
213        ))
214    }
215    #[cfg(target_pointer_width = "64")]
216    unsafe {
217        ret(syscall_readonly!(
218            __NR_mknodat,
219            dirfd,
220            path,
221            (mode, file_type),
222            dev_t(dev)
223        ))
224    }
225}
226
227#[inline]
228pub(crate) fn seek(fd: BorrowedFd<'_>, pos: SeekFrom) -> io::Result<u64> {
229    let (whence, offset) = match pos {
230        SeekFrom::Start(pos) => {
231            let pos: u64 = pos;
232            // Silently cast; we'll get `EINVAL` if the value is negative.
233            (SEEK_SET, pos as i64)
234        }
235        SeekFrom::End(offset) => (SEEK_END, offset),
236        SeekFrom::Current(offset) => (SEEK_CUR, offset),
237        SeekFrom::Data(pos) => {
238            let pos: u64 = pos;
239            // Silently cast; we'll get `EINVAL` if the value is negative.
240            (SEEK_DATA, pos as i64)
241        }
242        SeekFrom::Hole(pos) => {
243            let pos: u64 = pos;
244            // Silently cast; we'll get `EINVAL` if the value is negative.
245            (SEEK_HOLE, pos as i64)
246        }
247    };
248    _seek(fd, offset, whence)
249}
250
251#[inline]
252pub(crate) fn _seek(fd: BorrowedFd<'_>, offset: i64, whence: c::c_uint) -> io::Result<u64> {
253    #[cfg(target_pointer_width = "32")]
254    unsafe {
255        let mut result = MaybeUninit::<u64>::uninit();
256        ret(syscall!(
257            __NR__llseek,
258            fd,
259            // Don't use the hi/lo functions here because Linux's llseek
260            // takes its 64-bit argument differently from everything else.
261            pass_usize((offset >> 32) as usize),
262            pass_usize(offset as usize),
263            &mut result,
264            c_uint(whence)
265        ))?;
266        Ok(result.assume_init())
267    }
268    #[cfg(target_pointer_width = "64")]
269    unsafe {
270        ret_u64(syscall_readonly!(
271            __NR_lseek,
272            fd,
273            loff_t(offset),
274            c_uint(whence)
275        ))
276    }
277}
278
279#[inline]
280pub(crate) fn tell(fd: BorrowedFd<'_>) -> io::Result<u64> {
281    _seek(fd, 0, SEEK_CUR).map(|x| x as u64)
282}
283
284#[inline]
285pub(crate) fn ftruncate(fd: BorrowedFd<'_>, length: u64) -> io::Result<()> {
286    // <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/kernel/sys32.c?h=v6.13#n89>
287    #[cfg(all(
288        target_pointer_width = "32",
289        any(
290            target_arch = "arm",
291            target_arch = "mips",
292            target_arch = "mips32r6",
293            target_arch = "powerpc"
294        ),
295    ))]
296    unsafe {
297        ret(syscall_readonly!(
298            __NR_ftruncate64,
299            fd,
300            zero(),
301            hi(length),
302            lo(length)
303        ))
304    }
305    #[cfg(all(
306        target_pointer_width = "32",
307        not(any(
308            target_arch = "arm",
309            target_arch = "mips",
310            target_arch = "mips32r6",
311            target_arch = "powerpc"
312        )),
313    ))]
314    unsafe {
315        ret(syscall_readonly!(
316            __NR_ftruncate64,
317            fd,
318            hi(length),
319            lo(length)
320        ))
321    }
322    #[cfg(target_pointer_width = "64")]
323    unsafe {
324        ret(syscall_readonly!(
325            __NR_ftruncate,
326            fd,
327            loff_t_from_u64(length)
328        ))
329    }
330}
331
332#[inline]
333pub(crate) fn fallocate(
334    fd: BorrowedFd<'_>,
335    mode: FallocateFlags,
336    offset: u64,
337    len: u64,
338) -> io::Result<()> {
339    #[cfg(target_pointer_width = "32")]
340    unsafe {
341        ret(syscall_readonly!(
342            __NR_fallocate,
343            fd,
344            mode,
345            hi(offset),
346            lo(offset),
347            hi(len),
348            lo(len)
349        ))
350    }
351    #[cfg(target_pointer_width = "64")]
352    unsafe {
353        ret(syscall_readonly!(
354            __NR_fallocate,
355            fd,
356            mode,
357            loff_t_from_u64(offset),
358            loff_t_from_u64(len)
359        ))
360    }
361}
362
363#[inline]
364pub(crate) fn fadvise(
365    fd: BorrowedFd<'_>,
366    pos: u64,
367    len: Option<NonZeroU64>,
368    advice: Advice,
369) -> io::Result<()> {
370    let len = match len {
371        None => 0,
372        Some(len) => len.get(),
373    };
374
375    // On ARM, the arguments are reordered so that the `len` and `pos` argument
376    // pairs are aligned. And ARM has a custom syscall code for this.
377    #[cfg(target_arch = "arm")]
378    unsafe {
379        ret(syscall_readonly!(
380            __NR_arm_fadvise64_64,
381            fd,
382            advice,
383            hi(pos),
384            lo(pos),
385            hi(len),
386            lo(len)
387        ))
388    }
389
390    // On powerpc, the arguments are reordered as on ARM.
391    #[cfg(target_arch = "powerpc")]
392    unsafe {
393        ret(syscall_readonly!(
394            __NR_fadvise64_64,
395            fd,
396            advice,
397            hi(pos),
398            lo(pos),
399            hi(len),
400            lo(len)
401        ))
402    }
403
404    // On mips, the arguments are not reordered, and padding is inserted
405    // instead to ensure alignment.
406    #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))]
407    unsafe {
408        ret(syscall_readonly!(
409            __NR_fadvise64,
410            fd,
411            zero(),
412            hi(pos),
413            lo(pos),
414            hi(len),
415            lo(len),
416            advice
417        ))
418    }
419
420    // For all other 32-bit architectures, use `fadvise64_64` so that we get a
421    // 64-bit length.
422    #[cfg(all(
423        target_pointer_width = "32",
424        not(any(
425            target_arch = "arm",
426            target_arch = "mips",
427            target_arch = "mips32r6",
428            target_arch = "powerpc"
429        )),
430    ))]
431    unsafe {
432        ret(syscall_readonly!(
433            __NR_fadvise64_64,
434            fd,
435            hi(pos),
436            lo(pos),
437            hi(len),
438            lo(len),
439            advice
440        ))
441    }
442
443    // On 64-bit architectures, use `fadvise64` which is sufficient.
444    #[cfg(target_pointer_width = "64")]
445    unsafe {
446        ret(syscall_readonly!(
447            __NR_fadvise64,
448            fd,
449            loff_t_from_u64(pos),
450            loff_t_from_u64(len),
451            advice
452        ))
453    }
454}
455
456#[inline]
457pub(crate) fn fsync(fd: BorrowedFd<'_>) -> io::Result<()> {
458    unsafe { ret(syscall_readonly!(__NR_fsync, fd)) }
459}
460
461#[inline]
462pub(crate) fn fdatasync(fd: BorrowedFd<'_>) -> io::Result<()> {
463    unsafe { ret(syscall_readonly!(__NR_fdatasync, fd)) }
464}
465
466#[inline]
467pub(crate) fn flock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> {
468    unsafe {
469        ret(syscall_readonly!(
470            __NR_flock,
471            fd,
472            c_uint(operation as c::c_uint)
473        ))
474    }
475}
476
477#[inline]
478pub(crate) fn syncfs(fd: BorrowedFd<'_>) -> io::Result<()> {
479    unsafe { ret(syscall_readonly!(__NR_syncfs, fd)) }
480}
481
482#[inline]
483pub(crate) fn sync() {
484    unsafe { ret_infallible(syscall_readonly!(__NR_sync)) }
485}
486
487#[inline]
488pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result<Stat> {
489    // 32-bit and mips64 Linux: `struct stat64` is not y2038 compatible; use
490    // `statx`.
491    //
492    // And, some old platforms don't support `statx`, and some fail with a
493    // confusing error code, so we call `crate::fs::statx` to handle that. If
494    // `statx` isn't available, fall back to the buggy system call.
495    #[cfg(any(
496        target_pointer_width = "32",
497        target_arch = "mips64",
498        target_arch = "mips64r6"
499    ))]
500    {
501        match crate::fs::statx(fd, cstr!(""), AtFlags::EMPTY_PATH, StatxFlags::BASIC_STATS) {
502            Ok(x) => statx_to_stat(x),
503            Err(io::Errno::NOSYS) => fstat_old(fd),
504            Err(err) => Err(err),
505        }
506    }
507
508    #[cfg(all(
509        target_pointer_width = "64",
510        not(target_arch = "mips64"),
511        not(target_arch = "mips64r6")
512    ))]
513    unsafe {
514        let mut result = MaybeUninit::<Stat>::uninit();
515        ret(syscall!(__NR_fstat, fd, &mut result))?;
516
517        #[cfg(sanitize_memory)]
518        crate::msan::unpoison_maybe_uninit(&result);
519
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,
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,
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/[email protected]/
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, 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// Some linux_raw_sys structs have unsigned types for values which are
1669// interpreted as signed. This defines a utility or casting to the
1670// same-sized signed type.
1671#[cfg(any(
1672    target_pointer_width = "32",
1673    target_arch = "mips64",
1674    target_arch = "mips64r6"
1675))]
1676mod to_signed {
1677    pub(super) trait ToSigned {
1678        type Signed;
1679        fn to_signed(self) -> Self::Signed;
1680    }
1681    impl ToSigned for u32 {
1682        type Signed = i32;
1683
1684        fn to_signed(self) -> Self::Signed {
1685            self as _
1686        }
1687    }
1688    impl ToSigned for i32 {
1689        type Signed = i32;
1690
1691        fn to_signed(self) -> Self::Signed {
1692            self
1693        }
1694    }
1695    impl ToSigned for u64 {
1696        type Signed = i64;
1697
1698        fn to_signed(self) -> Self::Signed {
1699            self as _
1700        }
1701    }
1702    impl ToSigned for i64 {
1703        type Signed = i64;
1704
1705        fn to_signed(self) -> Self::Signed {
1706            self
1707        }
1708    }
1709}
1710#[cfg(any(
1711    target_pointer_width = "32",
1712    target_arch = "mips64",
1713    target_arch = "mips64r6"
1714))]
1715use to_signed::*;
1716
1717#[cfg(test)]
1718mod tests {
1719    use super::*;
1720
1721    #[test]
1722    fn test_sizes() {
1723        assert_eq_size!(linux_raw_sys::general::__kernel_loff_t, u64);
1724        assert_eq_align!(linux_raw_sys::general::__kernel_loff_t, u64);
1725
1726        // Assert that `Timestamps` has the expected layout.
1727        assert_eq_size!([linux_raw_sys::general::__kernel_timespec; 2], Timestamps);
1728        assert_eq_align!([linux_raw_sys::general::__kernel_timespec; 2], Timestamps);
1729    }
1730}