Skip to main content

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