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