• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(dead_code)]
8 #![allow(clippy::undocumented_unsafe_blocks)]
9 
10 use super::super::c;
11 use super::super::conv::{
12     by_ref, c_int, c_uint, dev_t, oflags_for_open_how, opt_mut, pass_usize, raw_fd, ret, ret_c_int,
13     ret_c_uint, ret_owned_fd, ret_usize, size_of, slice_mut, zero,
14 };
15 #[cfg(target_pointer_width = "64")]
16 use super::super::conv::{loff_t, loff_t_from_u64, ret_u64};
17 #[cfg(any(
18     target_arch = "aarch64",
19     target_arch = "riscv64",
20     target_arch = "mips64",
21     target_pointer_width = "32",
22 ))]
23 use crate::fd::AsFd;
24 use crate::fd::{BorrowedFd, OwnedFd};
25 use crate::ffi::CStr;
26 use crate::fs::{
27     Access, Advice, AtFlags, FallocateFlags, FileType, FlockOperation, MemfdFlags, Mode, OFlags,
28     RenameFlags, ResolveFlags, SealFlags, Stat, StatFs, StatVfs, StatVfsMountFlags, StatxFlags,
29     Timestamps,
30 };
31 use crate::io::{self, SeekFrom};
32 use crate::process::{Gid, Uid};
33 use core::convert::TryInto;
34 use core::mem::MaybeUninit;
35 #[cfg(target_arch = "mips64")]
36 use linux_raw_sys::general::stat as linux_stat64;
37 use linux_raw_sys::general::{
38     __kernel_fsid_t, __kernel_timespec, open_how, statx, AT_EACCESS, AT_FDCWD, AT_REMOVEDIR,
39     AT_SYMLINK_NOFOLLOW, F_ADD_SEALS, F_GETFL, F_GETLEASE, F_GETOWN, F_GETPIPE_SZ, F_GETSIG,
40     F_GET_SEALS, F_SETFL, F_SETPIPE_SZ, SEEK_CUR, SEEK_END, SEEK_SET, STATX__RESERVED,
41 };
42 #[cfg(target_pointer_width = "32")]
43 use {
44     super::super::conv::{hi, lo, slice_just_addr},
45     linux_raw_sys::general::stat64 as linux_stat64,
46     linux_raw_sys::general::timespec as __kernel_old_timespec,
47 };
48 
49 #[inline]
open(filename: &CStr, flags: OFlags, mode: Mode) -> io::Result<OwnedFd>50 pub(crate) fn open(filename: &CStr, flags: OFlags, mode: Mode) -> io::Result<OwnedFd> {
51     #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
52     {
53         openat(crate::fs::cwd().as_fd(), filename, flags, mode)
54     }
55     #[cfg(all(
56         target_pointer_width = "32",
57         not(any(target_arch = "aarch64", target_arch = "riscv64")),
58     ))]
59     unsafe {
60         ret_owned_fd(syscall_readonly!(__NR_open, filename, flags, mode))
61     }
62     #[cfg(all(
63         target_pointer_width = "64",
64         not(any(target_arch = "aarch64", target_arch = "riscv64")),
65     ))]
66     unsafe {
67         ret_owned_fd(syscall_readonly!(__NR_open, filename, flags, mode))
68     }
69 }
70 
71 #[inline]
openat( dirfd: BorrowedFd<'_>, filename: &CStr, flags: OFlags, mode: Mode, ) -> io::Result<OwnedFd>72 pub(crate) fn openat(
73     dirfd: BorrowedFd<'_>,
74     filename: &CStr,
75     flags: OFlags,
76     mode: Mode,
77 ) -> io::Result<OwnedFd> {
78     #[cfg(target_pointer_width = "32")]
79     unsafe {
80         ret_owned_fd(syscall_readonly!(__NR_openat, dirfd, filename, flags, mode))
81     }
82     #[cfg(target_pointer_width = "64")]
83     unsafe {
84         ret_owned_fd(syscall_readonly!(__NR_openat, dirfd, filename, flags, mode))
85     }
86 }
87 
88 #[inline]
openat2( dirfd: BorrowedFd<'_>, pathname: &CStr, flags: OFlags, mode: Mode, resolve: ResolveFlags, ) -> io::Result<OwnedFd>89 pub(crate) fn openat2(
90     dirfd: BorrowedFd<'_>,
91     pathname: &CStr,
92     flags: OFlags,
93     mode: Mode,
94     resolve: ResolveFlags,
95 ) -> io::Result<OwnedFd> {
96     #[cfg(target_pointer_width = "32")]
97     unsafe {
98         ret_owned_fd(syscall_readonly!(
99             __NR_openat2,
100             dirfd,
101             pathname,
102             by_ref(&open_how {
103                 flags: oflags_for_open_how(flags),
104                 mode: u64::from(mode.bits()),
105                 resolve: resolve.bits(),
106             }),
107             size_of::<open_how, _>()
108         ))
109     }
110     #[cfg(target_pointer_width = "64")]
111     unsafe {
112         ret_owned_fd(syscall_readonly!(
113             __NR_openat2,
114             dirfd,
115             pathname,
116             by_ref(&open_how {
117                 flags: oflags_for_open_how(flags),
118                 mode: u64::from(mode.bits()),
119                 resolve: resolve.bits(),
120             }),
121             size_of::<open_how, _>()
122         ))
123     }
124 }
125 
126 #[inline]
chmod(filename: &CStr, mode: Mode) -> io::Result<()>127 pub(crate) fn chmod(filename: &CStr, mode: Mode) -> io::Result<()> {
128     unsafe {
129         ret(syscall_readonly!(
130             __NR_fchmodat,
131             raw_fd(AT_FDCWD),
132             filename,
133             mode
134         ))
135     }
136 }
137 
138 #[inline]
chmodat(dirfd: BorrowedFd<'_>, filename: &CStr, mode: Mode) -> io::Result<()>139 pub(crate) fn chmodat(dirfd: BorrowedFd<'_>, filename: &CStr, mode: Mode) -> io::Result<()> {
140     unsafe { ret(syscall_readonly!(__NR_fchmodat, dirfd, filename, 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<'_>, filename: &CStr, owner: Option<Uid>, group: Option<Gid>, flags: AtFlags, ) -> io::Result<()>149 pub(crate) fn chownat(
150     dirfd: BorrowedFd<'_>,
151     filename: &CStr,
152     owner: Option<Uid>,
153     group: Option<Gid>,
154     flags: AtFlags,
155 ) -> io::Result<()> {
156     unsafe {
157         let (ow, gr) = crate::process::translate_fchown_args(owner, group);
158         ret(syscall_readonly!(
159             __NR_fchownat,
160             dirfd,
161             filename,
162             c_uint(ow),
163             c_uint(gr),
164             flags
165         ))
166     }
167 }
168 
169 #[inline]
fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()>170 pub(crate) fn fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> {
171     unsafe {
172         let (ow, gr) = crate::process::translate_fchown_args(owner, group);
173         ret(syscall_readonly!(__NR_fchown, fd, c_uint(ow), c_uint(gr)))
174     }
175 }
176 
177 #[inline]
mknodat( dirfd: BorrowedFd<'_>, filename: &CStr, file_type: FileType, mode: Mode, dev: u64, ) -> io::Result<()>178 pub(crate) fn mknodat(
179     dirfd: BorrowedFd<'_>,
180     filename: &CStr,
181     file_type: FileType,
182     mode: Mode,
183     dev: u64,
184 ) -> io::Result<()> {
185     #[cfg(target_pointer_width = "32")]
186     unsafe {
187         ret(syscall_readonly!(
188             __NR_mknodat,
189             dirfd,
190             filename,
191             (mode, file_type),
192             dev_t(dev)?
193         ))
194     }
195     #[cfg(target_pointer_width = "64")]
196     unsafe {
197         ret(syscall_readonly!(
198             __NR_mknodat,
199             dirfd,
200             filename,
201             (mode, file_type),
202             dev_t(dev)
203         ))
204     }
205 }
206 
207 #[inline]
seek(fd: BorrowedFd<'_>, pos: SeekFrom) -> io::Result<u64>208 pub(crate) fn seek(fd: BorrowedFd<'_>, pos: SeekFrom) -> io::Result<u64> {
209     let (whence, offset) = match pos {
210         SeekFrom::Start(pos) => {
211             let pos: u64 = pos;
212             // Silently cast; we'll get `EINVAL` if the value is negative.
213             (SEEK_SET, pos as i64)
214         }
215         SeekFrom::End(offset) => (SEEK_END, offset),
216         SeekFrom::Current(offset) => (SEEK_CUR, offset),
217     };
218     _seek(fd, offset, whence)
219 }
220 
221 #[inline]
_seek(fd: BorrowedFd<'_>, offset: i64, whence: c::c_uint) -> io::Result<u64>222 pub(crate) fn _seek(fd: BorrowedFd<'_>, offset: i64, whence: c::c_uint) -> io::Result<u64> {
223     #[cfg(target_pointer_width = "32")]
224     unsafe {
225         let mut result = MaybeUninit::<u64>::uninit();
226         ret(syscall!(
227             __NR__llseek,
228             fd,
229             // Don't use the hi/lo functions here because Linux's llseek
230             // takes its 64-bit argument differently from everything else.
231             pass_usize((offset >> 32) as usize),
232             pass_usize(offset as usize),
233             &mut result,
234             c_uint(whence)
235         ))?;
236         Ok(result.assume_init())
237     }
238     #[cfg(target_pointer_width = "64")]
239     unsafe {
240         ret_u64(syscall_readonly!(
241             __NR_lseek,
242             fd,
243             loff_t(offset),
244             c_uint(whence)
245         ))
246     }
247 }
248 
249 #[inline]
tell(fd: BorrowedFd<'_>) -> io::Result<u64>250 pub(crate) fn tell(fd: BorrowedFd<'_>) -> io::Result<u64> {
251     _seek(fd, 0, SEEK_CUR).map(|x| x as u64)
252 }
253 
254 #[inline]
ftruncate(fd: BorrowedFd<'_>, length: u64) -> io::Result<()>255 pub(crate) fn ftruncate(fd: BorrowedFd<'_>, length: u64) -> io::Result<()> {
256     // <https://github.com/torvalds/linux/blob/fcadab740480e0e0e9fa9bd272acd409884d431a/arch/arm64/kernel/sys32.c#L81-L83>
257     #[cfg(all(
258         target_pointer_width = "32",
259         any(target_arch = "arm", target_arch = "mips", target_arch = "powerpc"),
260     ))]
261     unsafe {
262         ret(syscall_readonly!(
263             __NR_ftruncate64,
264             fd,
265             zero(),
266             hi(length),
267             lo(length)
268         ))
269     }
270     #[cfg(all(
271         target_pointer_width = "32",
272         not(any(target_arch = "arm", target_arch = "mips", target_arch = "powerpc")),
273     ))]
274     unsafe {
275         ret(syscall_readonly!(
276             __NR_ftruncate64,
277             fd,
278             hi(length),
279             lo(length)
280         ))
281     }
282     #[cfg(target_pointer_width = "64")]
283     unsafe {
284         ret(syscall_readonly!(
285             __NR_ftruncate,
286             fd,
287             loff_t_from_u64(length)
288         ))
289     }
290 }
291 
292 #[inline]
fallocate( fd: BorrowedFd<'_>, mode: FallocateFlags, offset: u64, len: u64, ) -> io::Result<()>293 pub(crate) fn fallocate(
294     fd: BorrowedFd<'_>,
295     mode: FallocateFlags,
296     offset: u64,
297     len: u64,
298 ) -> io::Result<()> {
299     #[cfg(target_pointer_width = "32")]
300     unsafe {
301         ret(syscall_readonly!(
302             __NR_fallocate,
303             fd,
304             mode,
305             hi(offset),
306             lo(offset),
307             hi(len),
308             lo(len)
309         ))
310     }
311     #[cfg(target_pointer_width = "64")]
312     unsafe {
313         ret(syscall_readonly!(
314             __NR_fallocate,
315             fd,
316             mode,
317             loff_t_from_u64(offset),
318             loff_t_from_u64(len)
319         ))
320     }
321 }
322 
323 #[inline]
fadvise(fd: BorrowedFd<'_>, pos: u64, len: u64, advice: Advice) -> io::Result<()>324 pub(crate) fn fadvise(fd: BorrowedFd<'_>, pos: u64, len: u64, advice: Advice) -> io::Result<()> {
325     // On ARM, the arguments are reordered so that the len and pos argument
326     // pairs are aligned. And ARM has a custom syscall code for this.
327     #[cfg(target_arch = "arm")]
328     unsafe {
329         ret(syscall_readonly!(
330             __NR_arm_fadvise64_64,
331             fd,
332             advice,
333             hi(pos),
334             lo(pos),
335             hi(len),
336             lo(len)
337         ))
338     }
339 
340     // On powerpc, the arguments are reordered as on ARM.
341     #[cfg(target_arch = "powerpc")]
342     unsafe {
343         ret(syscall_readonly!(
344             __NR_fadvise64_64,
345             fd,
346             advice,
347             hi(pos),
348             lo(pos),
349             hi(len),
350             lo(len)
351         ))
352     }
353     // On mips, the arguments are not reordered, and padding is inserted
354     // instead to ensure alignment.
355     #[cfg(target_arch = "mips")]
356     unsafe {
357         ret(syscall_readonly!(
358             __NR_fadvise64,
359             fd,
360             zero(),
361             hi(pos),
362             lo(pos),
363             hi(len),
364             lo(len),
365             advice
366         ))
367     }
368     #[cfg(all(
369         target_pointer_width = "32",
370         not(any(target_arch = "arm", target_arch = "mips", target_arch = "powerpc")),
371     ))]
372     unsafe {
373         ret(syscall_readonly!(
374             __NR_fadvise64_64,
375             fd,
376             hi(pos),
377             lo(pos),
378             hi(len),
379             lo(len),
380             advice
381         ))
382     }
383     #[cfg(target_pointer_width = "64")]
384     unsafe {
385         ret(syscall_readonly!(
386             __NR_fadvise64,
387             fd,
388             loff_t_from_u64(pos),
389             loff_t_from_u64(len),
390             advice
391         ))
392     }
393 }
394 
395 #[inline]
fsync(fd: BorrowedFd<'_>) -> io::Result<()>396 pub(crate) fn fsync(fd: BorrowedFd<'_>) -> io::Result<()> {
397     unsafe { ret(syscall_readonly!(__NR_fsync, fd)) }
398 }
399 
400 #[inline]
fdatasync(fd: BorrowedFd<'_>) -> io::Result<()>401 pub(crate) fn fdatasync(fd: BorrowedFd<'_>) -> io::Result<()> {
402     unsafe { ret(syscall_readonly!(__NR_fdatasync, fd)) }
403 }
404 
405 #[inline]
flock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()>406 pub(crate) fn flock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> {
407     unsafe { ret(syscall!(__NR_flock, fd, c_uint(operation as c::c_uint))) }
408 }
409 
410 #[inline]
fstat(fd: BorrowedFd<'_>) -> io::Result<Stat>411 pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result<Stat> {
412     #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
413     {
414         match statx(fd, cstr!(""), AtFlags::EMPTY_PATH, StatxFlags::BASIC_STATS) {
415             Ok(x) => statx_to_stat(x),
416             Err(io::Errno::NOSYS) => fstat_old(fd),
417             Err(err) => Err(err),
418         }
419     }
420 
421     #[cfg(all(target_pointer_width = "64", not(target_arch = "mips64")))]
422     unsafe {
423         let mut result = MaybeUninit::<Stat>::uninit();
424         ret(syscall!(__NR_fstat, fd, &mut result))?;
425         Ok(result.assume_init())
426     }
427 }
428 
429 #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
fstat_old(fd: BorrowedFd<'_>) -> io::Result<Stat>430 fn fstat_old(fd: BorrowedFd<'_>) -> io::Result<Stat> {
431     let mut result = MaybeUninit::<linux_stat64>::uninit();
432 
433     #[cfg(target_arch = "mips64")]
434     unsafe {
435         ret(syscall!(__NR_fstat, fd, &mut result))?;
436         stat_to_stat(result.assume_init())
437     }
438 
439     #[cfg(target_pointer_width = "32")]
440     unsafe {
441         ret(syscall!(__NR_fstat64, fd, &mut result))?;
442         stat_to_stat(result.assume_init())
443     }
444 }
445 
446 #[inline]
stat(filename: &CStr) -> io::Result<Stat>447 pub(crate) fn stat(filename: &CStr) -> io::Result<Stat> {
448     #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
449     {
450         match statx(
451             crate::fs::cwd().as_fd(),
452             filename,
453             AtFlags::empty(),
454             StatxFlags::BASIC_STATS,
455         ) {
456             Ok(x) => statx_to_stat(x),
457             Err(io::Errno::NOSYS) => stat_old(filename),
458             Err(err) => Err(err),
459         }
460     }
461 
462     #[cfg(all(target_pointer_width = "64", not(target_arch = "mips64")))]
463     unsafe {
464         let mut result = MaybeUninit::<Stat>::uninit();
465         ret(syscall!(
466             __NR_newfstatat,
467             raw_fd(AT_FDCWD),
468             filename,
469             &mut result,
470             c_uint(0)
471         ))?;
472         Ok(result.assume_init())
473     }
474 }
475 
476 #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
stat_old(filename: &CStr) -> io::Result<Stat>477 fn stat_old(filename: &CStr) -> io::Result<Stat> {
478     let mut result = MaybeUninit::<linux_stat64>::uninit();
479 
480     #[cfg(target_arch = "mips64")]
481     unsafe {
482         ret(syscall!(
483             __NR_newfstatat,
484             raw_fd(AT_FDCWD),
485             filename,
486             &mut result,
487             c_uint(0)
488         ))?;
489         stat_to_stat(result.assume_init())
490     }
491 
492     #[cfg(target_pointer_width = "32")]
493     unsafe {
494         ret(syscall!(
495             __NR_fstatat64,
496             raw_fd(AT_FDCWD),
497             filename,
498             &mut result,
499             c_uint(0)
500         ))?;
501         stat_to_stat(result.assume_init())
502     }
503 }
504 
505 #[inline]
statat(dirfd: BorrowedFd<'_>, filename: &CStr, flags: AtFlags) -> io::Result<Stat>506 pub(crate) fn statat(dirfd: BorrowedFd<'_>, filename: &CStr, flags: AtFlags) -> io::Result<Stat> {
507     #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
508     {
509         match statx(dirfd, filename, flags, StatxFlags::BASIC_STATS) {
510             Ok(x) => statx_to_stat(x),
511             Err(io::Errno::NOSYS) => statat_old(dirfd, filename, flags),
512             Err(err) => Err(err),
513         }
514     }
515 
516     #[cfg(all(target_pointer_width = "64", not(target_arch = "mips64")))]
517     unsafe {
518         let mut result = MaybeUninit::<Stat>::uninit();
519         ret(syscall!(
520             __NR_newfstatat,
521             dirfd,
522             filename,
523             &mut result,
524             flags
525         ))?;
526         Ok(result.assume_init())
527     }
528 }
529 
530 #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
statat_old(dirfd: BorrowedFd<'_>, filename: &CStr, flags: AtFlags) -> io::Result<Stat>531 fn statat_old(dirfd: BorrowedFd<'_>, filename: &CStr, flags: AtFlags) -> io::Result<Stat> {
532     let mut result = MaybeUninit::<linux_stat64>::uninit();
533 
534     #[cfg(target_arch = "mips64")]
535     unsafe {
536         ret(syscall!(
537             __NR_newfstatat,
538             dirfd,
539             filename,
540             &mut result,
541             flags
542         ))?;
543         stat_to_stat(result.assume_init())
544     }
545 
546     #[cfg(target_pointer_width = "32")]
547     unsafe {
548         ret(syscall!(
549             __NR_fstatat64,
550             dirfd,
551             filename,
552             &mut result,
553             flags
554         ))?;
555         stat_to_stat(result.assume_init())
556     }
557 }
558 
559 #[inline]
lstat(filename: &CStr) -> io::Result<Stat>560 pub(crate) fn lstat(filename: &CStr) -> io::Result<Stat> {
561     #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
562     {
563         match statx(
564             crate::fs::cwd().as_fd(),
565             filename,
566             AtFlags::SYMLINK_NOFOLLOW,
567             StatxFlags::BASIC_STATS,
568         ) {
569             Ok(x) => statx_to_stat(x),
570             Err(io::Errno::NOSYS) => lstat_old(filename),
571             Err(err) => Err(err),
572         }
573     }
574 
575     #[cfg(all(target_pointer_width = "64", not(target_arch = "mips64")))]
576     unsafe {
577         let mut result = MaybeUninit::<Stat>::uninit();
578         ret(syscall!(
579             __NR_newfstatat,
580             raw_fd(AT_FDCWD),
581             filename,
582             &mut result,
583             c_uint(AT_SYMLINK_NOFOLLOW)
584         ))?;
585         Ok(result.assume_init())
586     }
587 }
588 
589 #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
lstat_old(filename: &CStr) -> io::Result<Stat>590 fn lstat_old(filename: &CStr) -> io::Result<Stat> {
591     let mut result = MaybeUninit::<linux_stat64>::uninit();
592 
593     #[cfg(target_arch = "mips64")]
594     unsafe {
595         ret(syscall!(
596             __NR_newfstatat,
597             raw_fd(AT_FDCWD),
598             filename,
599             &mut result,
600             c_uint(AT_SYMLINK_NOFOLLOW)
601         ))?;
602         stat_to_stat(result.assume_init())
603     }
604 
605     #[cfg(target_pointer_width = "32")]
606     unsafe {
607         ret(syscall!(
608             __NR_fstatat64,
609             raw_fd(AT_FDCWD),
610             filename,
611             &mut result,
612             c_uint(AT_SYMLINK_NOFOLLOW)
613         ))?;
614         stat_to_stat(result.assume_init())
615     }
616 }
617 
618 /// Convert from a Linux `statx` value to rustix's `Stat`.
619 #[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat>620 fn statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat> {
621     Ok(Stat {
622         st_dev: crate::fs::makedev(x.stx_dev_major, x.stx_dev_minor),
623         st_mode: x.stx_mode.into(),
624         st_nlink: x.stx_nlink.into(),
625         st_uid: x.stx_uid.into(),
626         st_gid: x.stx_gid.into(),
627         st_rdev: crate::fs::makedev(x.stx_rdev_major, x.stx_rdev_minor),
628         st_size: x.stx_size.try_into().map_err(|_| io::Errno::OVERFLOW)?,
629         st_blksize: x.stx_blksize.into(),
630         st_blocks: x.stx_blocks.into(),
631         st_atime: x
632             .stx_atime
633             .tv_sec
634             .try_into()
635             .map_err(|_| io::Errno::OVERFLOW)?,
636         st_atime_nsec: x.stx_atime.tv_nsec.into(),
637         st_mtime: x
638             .stx_mtime
639             .tv_sec
640             .try_into()
641             .map_err(|_| io::Errno::OVERFLOW)?,
642         st_mtime_nsec: x.stx_mtime.tv_nsec.into(),
643         st_ctime: x
644             .stx_ctime
645             .tv_sec
646             .try_into()
647             .map_err(|_| io::Errno::OVERFLOW)?,
648         st_ctime_nsec: x.stx_ctime.tv_nsec.into(),
649         st_ino: x.stx_ino.into(),
650     })
651 }
652 
653 /// Convert from a Linux `stat64` value to rustix's `Stat`.
654 #[cfg(target_pointer_width = "32")]
stat_to_stat(s64: linux_raw_sys::general::stat64) -> io::Result<Stat>655 fn stat_to_stat(s64: linux_raw_sys::general::stat64) -> io::Result<Stat> {
656     Ok(Stat {
657         st_dev: s64.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?,
658         st_mode: s64.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?,
659         st_nlink: s64.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?,
660         st_uid: s64.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?,
661         st_gid: s64.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?,
662         st_rdev: s64.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?,
663         st_size: s64.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?,
664         st_blksize: s64.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?,
665         st_blocks: s64.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?,
666         st_atime: s64.st_atime.try_into().map_err(|_| io::Errno::OVERFLOW)?,
667         st_atime_nsec: s64
668             .st_atime_nsec
669             .try_into()
670             .map_err(|_| io::Errno::OVERFLOW)?,
671         st_mtime: s64.st_mtime.try_into().map_err(|_| io::Errno::OVERFLOW)?,
672         st_mtime_nsec: s64
673             .st_mtime_nsec
674             .try_into()
675             .map_err(|_| io::Errno::OVERFLOW)?,
676         st_ctime: s64.st_ctime.try_into().map_err(|_| io::Errno::OVERFLOW)?,
677         st_ctime_nsec: s64
678             .st_ctime_nsec
679             .try_into()
680             .map_err(|_| io::Errno::OVERFLOW)?,
681         st_ino: s64.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?,
682     })
683 }
684 
685 /// Convert from a Linux `stat` value to rustix's `Stat`.
686 #[cfg(target_arch = "mips64")]
stat_to_stat(s: linux_raw_sys::general::stat) -> io::Result<Stat>687 fn stat_to_stat(s: linux_raw_sys::general::stat) -> io::Result<Stat> {
688     Ok(Stat {
689         st_dev: s.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?,
690         st_mode: s.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?,
691         st_nlink: s.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?,
692         st_uid: s.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?,
693         st_gid: s.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?,
694         st_rdev: s.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?,
695         st_size: s.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?,
696         st_blksize: s.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?,
697         st_blocks: s.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?,
698         st_atime: s.st_atime.try_into().map_err(|_| io::Errno::OVERFLOW)?,
699         st_atime_nsec: s
700             .st_atime_nsec
701             .try_into()
702             .map_err(|_| io::Errno::OVERFLOW)?,
703         st_mtime: s.st_mtime.try_into().map_err(|_| io::Errno::OVERFLOW)?,
704         st_mtime_nsec: s
705             .st_mtime_nsec
706             .try_into()
707             .map_err(|_| io::Errno::OVERFLOW)?,
708         st_ctime: s.st_ctime.try_into().map_err(|_| io::Errno::OVERFLOW)?,
709         st_ctime_nsec: s
710             .st_ctime_nsec
711             .try_into()
712             .map_err(|_| io::Errno::OVERFLOW)?,
713         st_ino: s.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?,
714     })
715 }
716 
717 #[inline]
statx( dirfd: BorrowedFd<'_>, pathname: &CStr, flags: AtFlags, mask: StatxFlags, ) -> io::Result<statx>718 pub(crate) fn statx(
719     dirfd: BorrowedFd<'_>,
720     pathname: &CStr,
721     flags: AtFlags,
722     mask: StatxFlags,
723 ) -> io::Result<statx> {
724     // If a future Linux kernel adds more fields to `struct statx` and users
725     // passing flags unknown to rustix in `StatxFlags`, we could end up
726     // writing outside of the buffer. To prevent this possibility, we mask off
727     // any flags that we don't know about.
728     //
729     // This includes `STATX__RESERVED`, which has a value that we know, but
730     // which could take on arbitrary new meaning in the future. Linux currently
731     // rejects this flag with `EINVAL`, so we do the same.
732     //
733     // This doesn't rely on `STATX_ALL` because [it's deprecated] and already
734     // doesn't represent all the known flags.
735     //
736     // [it's deprecated]: https://patchwork.kernel.org/project/linux-fsdevel/patch/20200505095915.11275-7-mszeredi@redhat.com/
737     if (mask.bits() & STATX__RESERVED) == STATX__RESERVED {
738         return Err(io::Errno::INVAL);
739     }
740     let mask = mask & StatxFlags::all();
741 
742     unsafe {
743         let mut statx_buf = MaybeUninit::<statx>::uninit();
744         ret(syscall!(
745             __NR_statx,
746             dirfd,
747             pathname,
748             flags,
749             mask,
750             &mut statx_buf
751         ))?;
752         Ok(statx_buf.assume_init())
753     }
754 }
755 
756 #[inline]
is_statx_available() -> bool757 pub(crate) fn is_statx_available() -> bool {
758     unsafe {
759         // Call `statx` with null pointers so that if it fails for any reason
760         // other than `EFAULT`, we know it's not supported.
761         matches!(
762             ret(syscall!(
763                 __NR_statx,
764                 raw_fd(AT_FDCWD),
765                 zero(),
766                 zero(),
767                 zero(),
768                 zero()
769             )),
770             Err(io::Errno::FAULT)
771         )
772     }
773 }
774 
775 #[inline]
fstatfs(fd: BorrowedFd<'_>) -> io::Result<StatFs>776 pub(crate) fn fstatfs(fd: BorrowedFd<'_>) -> io::Result<StatFs> {
777     #[cfg(target_pointer_width = "32")]
778     unsafe {
779         let mut result = MaybeUninit::<StatFs>::uninit();
780         ret(syscall!(
781             __NR_fstatfs64,
782             fd,
783             size_of::<StatFs, _>(),
784             &mut result
785         ))?;
786         Ok(result.assume_init())
787     }
788 
789     #[cfg(target_pointer_width = "64")]
790     unsafe {
791         let mut result = MaybeUninit::<StatFs>::uninit();
792         ret(syscall!(__NR_fstatfs, fd, &mut result))?;
793         Ok(result.assume_init())
794     }
795 }
796 
797 #[inline]
fstatvfs(fd: BorrowedFd<'_>) -> io::Result<StatVfs>798 pub(crate) fn fstatvfs(fd: BorrowedFd<'_>) -> io::Result<StatVfs> {
799     // Linux doesn't have an `fstatvfs` syscall; we have to do `fstatfs` and
800     // translate the fields as best we can.
801     let statfs = fstatfs(fd)?;
802 
803     Ok(statfs_to_statvfs(statfs))
804 }
805 
806 #[inline]
statfs(filename: &CStr) -> io::Result<StatFs>807 pub(crate) fn statfs(filename: &CStr) -> io::Result<StatFs> {
808     #[cfg(target_pointer_width = "32")]
809     unsafe {
810         let mut result = MaybeUninit::<StatFs>::uninit();
811         ret(syscall!(
812             __NR_statfs64,
813             filename,
814             size_of::<StatFs, _>(),
815             &mut result
816         ))?;
817         Ok(result.assume_init())
818     }
819     #[cfg(target_pointer_width = "64")]
820     unsafe {
821         let mut result = MaybeUninit::<StatFs>::uninit();
822         ret(syscall!(__NR_statfs, filename, &mut result))?;
823         Ok(result.assume_init())
824     }
825 }
826 
827 #[inline]
statvfs(filename: &CStr) -> io::Result<StatVfs>828 pub(crate) fn statvfs(filename: &CStr) -> io::Result<StatVfs> {
829     // Linux doesn't have a `statvfs` syscall; we have to do `statfs` and
830     // translate the fields as best we can.
831     let statfs = statfs(filename)?;
832 
833     Ok(statfs_to_statvfs(statfs))
834 }
835 
statfs_to_statvfs(statfs: StatFs) -> StatVfs836 fn statfs_to_statvfs(statfs: StatFs) -> StatVfs {
837     let __kernel_fsid_t { val } = statfs.f_fsid;
838     let [f_fsid_val0, f_fsid_val1]: [i32; 2] = val;
839 
840     StatVfs {
841         f_bsize: statfs.f_bsize as u64,
842         f_frsize: if statfs.f_frsize != 0 {
843             statfs.f_frsize
844         } else {
845             statfs.f_bsize
846         } as u64,
847         f_blocks: statfs.f_blocks as u64,
848         f_bfree: statfs.f_bfree as u64,
849         f_bavail: statfs.f_bavail as u64,
850         f_files: statfs.f_files as u64,
851         f_ffree: statfs.f_ffree as u64,
852         f_favail: statfs.f_ffree as u64,
853         f_fsid: f_fsid_val0 as u32 as u64 | ((f_fsid_val1 as u32 as u64) << 32),
854         f_flag: unsafe { StatVfsMountFlags::from_bits_unchecked(statfs.f_flags as u64) },
855         f_namemax: statfs.f_namelen as u64,
856     }
857 }
858 
859 #[inline]
readlink(path: &CStr, buf: &mut [u8]) -> io::Result<usize>860 pub(crate) fn readlink(path: &CStr, buf: &mut [u8]) -> io::Result<usize> {
861     let (buf_addr_mut, buf_len) = slice_mut(buf);
862     unsafe {
863         ret_usize(syscall!(
864             __NR_readlinkat,
865             raw_fd(AT_FDCWD),
866             path,
867             buf_addr_mut,
868             buf_len
869         ))
870     }
871 }
872 
873 #[inline]
readlinkat(dirfd: BorrowedFd<'_>, path: &CStr, buf: &mut [u8]) -> io::Result<usize>874 pub(crate) fn readlinkat(dirfd: BorrowedFd<'_>, path: &CStr, buf: &mut [u8]) -> io::Result<usize> {
875     let (buf_addr_mut, buf_len) = slice_mut(buf);
876     unsafe {
877         ret_usize(syscall!(
878             __NR_readlinkat,
879             dirfd,
880             path,
881             buf_addr_mut,
882             buf_len
883         ))
884     }
885 }
886 
887 #[inline]
fcntl_getfl(fd: BorrowedFd<'_>) -> io::Result<OFlags>888 pub(crate) fn fcntl_getfl(fd: BorrowedFd<'_>) -> io::Result<OFlags> {
889     #[cfg(target_pointer_width = "32")]
890     unsafe {
891         ret_c_uint(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETFL)))
892             .map(OFlags::from_bits_truncate)
893     }
894     #[cfg(target_pointer_width = "64")]
895     unsafe {
896         ret_c_uint(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETFL)))
897             .map(OFlags::from_bits_truncate)
898     }
899 }
900 
901 #[inline]
fcntl_setfl(fd: BorrowedFd<'_>, flags: OFlags) -> io::Result<()>902 pub(crate) fn fcntl_setfl(fd: BorrowedFd<'_>, flags: OFlags) -> io::Result<()> {
903     #[cfg(target_pointer_width = "32")]
904     unsafe {
905         ret(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_SETFL), flags))
906     }
907     #[cfg(target_pointer_width = "64")]
908     unsafe {
909         ret(syscall_readonly!(__NR_fcntl, fd, c_uint(F_SETFL), flags))
910     }
911 }
912 
913 #[inline]
fcntl_getlease(fd: BorrowedFd<'_>) -> io::Result<c::c_int>914 pub(crate) fn fcntl_getlease(fd: BorrowedFd<'_>) -> io::Result<c::c_int> {
915     #[cfg(target_pointer_width = "32")]
916     unsafe {
917         ret_c_int(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETLEASE)))
918     }
919     #[cfg(target_pointer_width = "64")]
920     unsafe {
921         ret_c_int(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETLEASE)))
922     }
923 }
924 
925 #[inline]
fcntl_getown(fd: BorrowedFd<'_>) -> io::Result<c::c_int>926 pub(crate) fn fcntl_getown(fd: BorrowedFd<'_>) -> io::Result<c::c_int> {
927     #[cfg(target_pointer_width = "32")]
928     unsafe {
929         ret_c_int(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETOWN)))
930     }
931     #[cfg(target_pointer_width = "64")]
932     unsafe {
933         ret_c_int(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETOWN)))
934     }
935 }
936 
937 #[inline]
fcntl_getsig(fd: BorrowedFd<'_>) -> io::Result<c::c_int>938 pub(crate) fn fcntl_getsig(fd: BorrowedFd<'_>) -> io::Result<c::c_int> {
939     #[cfg(target_pointer_width = "32")]
940     unsafe {
941         ret_c_int(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETSIG)))
942     }
943     #[cfg(target_pointer_width = "64")]
944     unsafe {
945         ret_c_int(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETSIG)))
946     }
947 }
948 
949 #[inline]
fcntl_getpipe_sz(fd: BorrowedFd<'_>) -> io::Result<usize>950 pub(crate) fn fcntl_getpipe_sz(fd: BorrowedFd<'_>) -> io::Result<usize> {
951     #[cfg(target_pointer_width = "32")]
952     unsafe {
953         ret_usize(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETPIPE_SZ)))
954     }
955     #[cfg(target_pointer_width = "64")]
956     unsafe {
957         ret_usize(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETPIPE_SZ)))
958     }
959 }
960 
961 #[inline]
fcntl_setpipe_sz(fd: BorrowedFd<'_>, size: c::c_int) -> io::Result<usize>962 pub(crate) fn fcntl_setpipe_sz(fd: BorrowedFd<'_>, size: c::c_int) -> io::Result<usize> {
963     #[cfg(target_pointer_width = "32")]
964     unsafe {
965         ret_usize(syscall_readonly!(
966             __NR_fcntl64,
967             fd,
968             c_uint(F_SETPIPE_SZ),
969             c_int(size)
970         ))
971     }
972     #[cfg(target_pointer_width = "64")]
973     unsafe {
974         ret_usize(syscall_readonly!(
975             __NR_fcntl,
976             fd,
977             c_uint(F_SETPIPE_SZ),
978             c_int(size)
979         ))
980     }
981 }
982 
983 #[inline]
fcntl_get_seals(fd: BorrowedFd<'_>) -> io::Result<SealFlags>984 pub(crate) fn fcntl_get_seals(fd: BorrowedFd<'_>) -> io::Result<SealFlags> {
985     #[cfg(target_pointer_width = "32")]
986     unsafe {
987         ret_c_int(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GET_SEALS)))
988             .map(|seals| SealFlags::from_bits_unchecked(seals as u32))
989     }
990     #[cfg(target_pointer_width = "64")]
991     unsafe {
992         ret_c_int(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GET_SEALS)))
993             .map(|seals| SealFlags::from_bits_unchecked(seals as u32))
994     }
995 }
996 
997 #[inline]
fcntl_add_seals(fd: BorrowedFd<'_>, seals: SealFlags) -> io::Result<()>998 pub(crate) fn fcntl_add_seals(fd: BorrowedFd<'_>, seals: SealFlags) -> io::Result<()> {
999     #[cfg(target_pointer_width = "32")]
1000     unsafe {
1001         ret(syscall_readonly!(
1002             __NR_fcntl64,
1003             fd,
1004             c_uint(F_ADD_SEALS),
1005             seals
1006         ))
1007     }
1008     #[cfg(target_pointer_width = "64")]
1009     unsafe {
1010         ret(syscall_readonly!(
1011             __NR_fcntl,
1012             fd,
1013             c_uint(F_ADD_SEALS),
1014             seals
1015         ))
1016     }
1017 }
1018 
1019 #[inline]
rename(oldname: &CStr, newname: &CStr) -> io::Result<()>1020 pub(crate) fn rename(oldname: &CStr, newname: &CStr) -> io::Result<()> {
1021     #[cfg(target_arch = "riscv64")]
1022     unsafe {
1023         ret(syscall_readonly!(
1024             __NR_renameat2,
1025             raw_fd(AT_FDCWD),
1026             oldname,
1027             raw_fd(AT_FDCWD),
1028             newname,
1029             c_uint(0)
1030         ))
1031     }
1032     #[cfg(not(target_arch = "riscv64"))]
1033     unsafe {
1034         ret(syscall_readonly!(
1035             __NR_renameat,
1036             raw_fd(AT_FDCWD),
1037             oldname,
1038             raw_fd(AT_FDCWD),
1039             newname
1040         ))
1041     }
1042 }
1043 
1044 #[inline]
renameat( old_dirfd: BorrowedFd<'_>, oldname: &CStr, new_dirfd: BorrowedFd<'_>, newname: &CStr, ) -> io::Result<()>1045 pub(crate) fn renameat(
1046     old_dirfd: BorrowedFd<'_>,
1047     oldname: &CStr,
1048     new_dirfd: BorrowedFd<'_>,
1049     newname: &CStr,
1050 ) -> io::Result<()> {
1051     #[cfg(target_arch = "riscv64")]
1052     unsafe {
1053         ret(syscall_readonly!(
1054             __NR_renameat2,
1055             old_dirfd,
1056             oldname,
1057             new_dirfd,
1058             newname,
1059             c_uint(0)
1060         ))
1061     }
1062     #[cfg(not(target_arch = "riscv64"))]
1063     unsafe {
1064         ret(syscall_readonly!(
1065             __NR_renameat,
1066             old_dirfd,
1067             oldname,
1068             new_dirfd,
1069             newname
1070         ))
1071     }
1072 }
1073 
1074 #[inline]
renameat2( old_dirfd: BorrowedFd<'_>, oldname: &CStr, new_dirfd: BorrowedFd<'_>, newname: &CStr, flags: RenameFlags, ) -> io::Result<()>1075 pub(crate) fn renameat2(
1076     old_dirfd: BorrowedFd<'_>,
1077     oldname: &CStr,
1078     new_dirfd: BorrowedFd<'_>,
1079     newname: &CStr,
1080     flags: RenameFlags,
1081 ) -> io::Result<()> {
1082     unsafe {
1083         ret(syscall_readonly!(
1084             __NR_renameat2,
1085             old_dirfd,
1086             oldname,
1087             new_dirfd,
1088             newname,
1089             flags
1090         ))
1091     }
1092 }
1093 
1094 #[inline]
unlink(pathname: &CStr) -> io::Result<()>1095 pub(crate) fn unlink(pathname: &CStr) -> io::Result<()> {
1096     unsafe {
1097         ret(syscall_readonly!(
1098             __NR_unlinkat,
1099             raw_fd(AT_FDCWD),
1100             pathname,
1101             c_uint(0)
1102         ))
1103     }
1104 }
1105 
1106 #[inline]
unlinkat(dirfd: BorrowedFd<'_>, pathname: &CStr, flags: AtFlags) -> io::Result<()>1107 pub(crate) fn unlinkat(dirfd: BorrowedFd<'_>, pathname: &CStr, flags: AtFlags) -> io::Result<()> {
1108     unsafe { ret(syscall_readonly!(__NR_unlinkat, dirfd, pathname, flags)) }
1109 }
1110 
1111 #[inline]
rmdir(pathname: &CStr) -> io::Result<()>1112 pub(crate) fn rmdir(pathname: &CStr) -> io::Result<()> {
1113     unsafe {
1114         ret(syscall_readonly!(
1115             __NR_unlinkat,
1116             raw_fd(AT_FDCWD),
1117             pathname,
1118             c_uint(AT_REMOVEDIR)
1119         ))
1120     }
1121 }
1122 
1123 #[inline]
link(oldname: &CStr, newname: &CStr) -> io::Result<()>1124 pub(crate) fn link(oldname: &CStr, newname: &CStr) -> io::Result<()> {
1125     unsafe {
1126         ret(syscall_readonly!(
1127             __NR_linkat,
1128             raw_fd(AT_FDCWD),
1129             oldname,
1130             raw_fd(AT_FDCWD),
1131             newname,
1132             c_uint(0)
1133         ))
1134     }
1135 }
1136 
1137 #[inline]
linkat( old_dirfd: BorrowedFd<'_>, oldname: &CStr, new_dirfd: BorrowedFd<'_>, newname: &CStr, flags: AtFlags, ) -> io::Result<()>1138 pub(crate) fn linkat(
1139     old_dirfd: BorrowedFd<'_>,
1140     oldname: &CStr,
1141     new_dirfd: BorrowedFd<'_>,
1142     newname: &CStr,
1143     flags: AtFlags,
1144 ) -> io::Result<()> {
1145     unsafe {
1146         ret(syscall_readonly!(
1147             __NR_linkat,
1148             old_dirfd,
1149             oldname,
1150             new_dirfd,
1151             newname,
1152             flags
1153         ))
1154     }
1155 }
1156 
1157 #[inline]
symlink(oldname: &CStr, newname: &CStr) -> io::Result<()>1158 pub(crate) fn symlink(oldname: &CStr, newname: &CStr) -> io::Result<()> {
1159     unsafe {
1160         ret(syscall_readonly!(
1161             __NR_symlinkat,
1162             oldname,
1163             raw_fd(AT_FDCWD),
1164             newname
1165         ))
1166     }
1167 }
1168 
1169 #[inline]
symlinkat(oldname: &CStr, dirfd: BorrowedFd<'_>, newname: &CStr) -> io::Result<()>1170 pub(crate) fn symlinkat(oldname: &CStr, dirfd: BorrowedFd<'_>, newname: &CStr) -> io::Result<()> {
1171     unsafe { ret(syscall_readonly!(__NR_symlinkat, oldname, dirfd, newname)) }
1172 }
1173 
1174 #[inline]
mkdir(pathname: &CStr, mode: Mode) -> io::Result<()>1175 pub(crate) fn mkdir(pathname: &CStr, mode: Mode) -> io::Result<()> {
1176     unsafe {
1177         ret(syscall_readonly!(
1178             __NR_mkdirat,
1179             raw_fd(AT_FDCWD),
1180             pathname,
1181             mode
1182         ))
1183     }
1184 }
1185 
1186 #[inline]
mkdirat(dirfd: BorrowedFd<'_>, pathname: &CStr, mode: Mode) -> io::Result<()>1187 pub(crate) fn mkdirat(dirfd: BorrowedFd<'_>, pathname: &CStr, mode: Mode) -> io::Result<()> {
1188     unsafe { ret(syscall_readonly!(__NR_mkdirat, dirfd, pathname, mode)) }
1189 }
1190 
1191 #[inline]
getdents(fd: BorrowedFd<'_>, dirent: &mut [u8]) -> io::Result<usize>1192 pub(crate) fn getdents(fd: BorrowedFd<'_>, dirent: &mut [u8]) -> io::Result<usize> {
1193     let (dirent_addr_mut, dirent_len) = slice_mut(dirent);
1194 
1195     unsafe { ret_usize(syscall!(__NR_getdents64, fd, dirent_addr_mut, dirent_len)) }
1196 }
1197 
1198 #[inline]
getdents_uninit( fd: BorrowedFd<'_>, dirent: &mut [MaybeUninit<u8>], ) -> io::Result<usize>1199 pub(crate) fn getdents_uninit(
1200     fd: BorrowedFd<'_>,
1201     dirent: &mut [MaybeUninit<u8>],
1202 ) -> io::Result<usize> {
1203     let (dirent_addr_mut, dirent_len) = slice_mut(dirent);
1204 
1205     unsafe { ret_usize(syscall!(__NR_getdents64, fd, dirent_addr_mut, dirent_len)) }
1206 }
1207 
1208 #[inline]
utimensat( dirfd: BorrowedFd<'_>, pathname: &CStr, times: &Timestamps, flags: AtFlags, ) -> io::Result<()>1209 pub(crate) fn utimensat(
1210     dirfd: BorrowedFd<'_>,
1211     pathname: &CStr,
1212     times: &Timestamps,
1213     flags: AtFlags,
1214 ) -> io::Result<()> {
1215     _utimensat(dirfd, Some(pathname), times, flags)
1216 }
1217 
1218 #[inline]
_utimensat( dirfd: BorrowedFd<'_>, pathname: Option<&CStr>, times: &Timestamps, flags: AtFlags, ) -> io::Result<()>1219 fn _utimensat(
1220     dirfd: BorrowedFd<'_>,
1221     pathname: Option<&CStr>,
1222     times: &Timestamps,
1223     flags: AtFlags,
1224 ) -> io::Result<()> {
1225     // Assert that `Timestamps` has the expected layout.
1226     let _ = unsafe { core::mem::transmute::<Timestamps, [__kernel_timespec; 2]>(times.clone()) };
1227 
1228     #[cfg(target_pointer_width = "32")]
1229     unsafe {
1230         match ret(syscall_readonly!(
1231             __NR_utimensat_time64,
1232             dirfd,
1233             pathname,
1234             by_ref(times),
1235             flags
1236         )) {
1237             Err(io::Errno::NOSYS) => _utimensat_old(dirfd, pathname, times, flags),
1238             otherwise => otherwise,
1239         }
1240     }
1241     #[cfg(target_pointer_width = "64")]
1242     unsafe {
1243         ret(syscall_readonly!(
1244             __NR_utimensat,
1245             dirfd,
1246             pathname,
1247             by_ref(times),
1248             flags
1249         ))
1250     }
1251 }
1252 
1253 #[cfg(target_pointer_width = "32")]
_utimensat_old( dirfd: BorrowedFd<'_>, pathname: Option<&CStr>, times: &Timestamps, flags: AtFlags, ) -> io::Result<()>1254 unsafe fn _utimensat_old(
1255     dirfd: BorrowedFd<'_>,
1256     pathname: Option<&CStr>,
1257     times: &Timestamps,
1258     flags: AtFlags,
1259 ) -> io::Result<()> {
1260     // See the comments in `rustix_clock_gettime_via_syscall` about
1261     // emulation.
1262     let old_times = [
1263         __kernel_old_timespec {
1264             tv_sec: times
1265                 .last_access
1266                 .tv_sec
1267                 .try_into()
1268                 .map_err(|_| io::Errno::OVERFLOW)?,
1269             tv_nsec: times
1270                 .last_access
1271                 .tv_nsec
1272                 .try_into()
1273                 .map_err(|_| io::Errno::INVAL)?,
1274         },
1275         __kernel_old_timespec {
1276             tv_sec: times
1277                 .last_modification
1278                 .tv_sec
1279                 .try_into()
1280                 .map_err(|_| io::Errno::OVERFLOW)?,
1281             tv_nsec: times
1282                 .last_modification
1283                 .tv_nsec
1284                 .try_into()
1285                 .map_err(|_| io::Errno::INVAL)?,
1286         },
1287     ];
1288     // The length of the array is fixed and not passed into the syscall.
1289     let old_times_addr = slice_just_addr(&old_times);
1290     ret(syscall_readonly!(
1291         __NR_utimensat,
1292         dirfd,
1293         pathname,
1294         old_times_addr,
1295         flags
1296     ))
1297 }
1298 
1299 #[inline]
futimens(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()>1300 pub(crate) fn futimens(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()> {
1301     _utimensat(fd, None, times, AtFlags::empty())
1302 }
1303 
accessat( dirfd: BorrowedFd<'_>, path: &CStr, access: Access, flags: AtFlags, ) -> io::Result<()>1304 pub(crate) fn accessat(
1305     dirfd: BorrowedFd<'_>,
1306     path: &CStr,
1307     access: Access,
1308     flags: AtFlags,
1309 ) -> io::Result<()> {
1310     // Linux's `faccessat` doesn't have a flags parameter. If we have
1311     // `AT_EACCESS` and we're not setuid or setgid, we can emulate it.
1312     if flags.is_empty()
1313         || (flags.bits() == AT_EACCESS
1314             && crate::process::getuid() == crate::process::geteuid()
1315             && crate::process::getgid() == crate::process::getegid())
1316     {
1317         return unsafe { ret(syscall_readonly!(__NR_faccessat, dirfd, path, access)) };
1318     }
1319 
1320     if flags.bits() != AT_EACCESS {
1321         return Err(io::Errno::INVAL);
1322     }
1323 
1324     // TODO: Use faccessat2 in newer Linux versions.
1325     Err(io::Errno::NOSYS)
1326 }
1327 
1328 #[inline]
copy_file_range( fd_in: BorrowedFd<'_>, off_in: Option<&mut u64>, fd_out: BorrowedFd<'_>, off_out: Option<&mut u64>, len: u64, ) -> io::Result<u64>1329 pub(crate) fn copy_file_range(
1330     fd_in: BorrowedFd<'_>,
1331     off_in: Option<&mut u64>,
1332     fd_out: BorrowedFd<'_>,
1333     off_out: Option<&mut u64>,
1334     len: u64,
1335 ) -> io::Result<u64> {
1336     let len: usize = len.try_into().unwrap_or(usize::MAX);
1337     _copy_file_range(fd_in, off_in, fd_out, off_out, len, 0).map(|result| result as u64)
1338 }
1339 
1340 #[inline]
_copy_file_range( fd_in: BorrowedFd<'_>, off_in: Option<&mut u64>, fd_out: BorrowedFd<'_>, off_out: Option<&mut u64>, len: usize, flags: c::c_uint, ) -> io::Result<usize>1341 fn _copy_file_range(
1342     fd_in: BorrowedFd<'_>,
1343     off_in: Option<&mut u64>,
1344     fd_out: BorrowedFd<'_>,
1345     off_out: Option<&mut u64>,
1346     len: usize,
1347     flags: c::c_uint,
1348 ) -> io::Result<usize> {
1349     unsafe {
1350         ret_usize(syscall!(
1351             __NR_copy_file_range,
1352             fd_in,
1353             opt_mut(off_in),
1354             fd_out,
1355             opt_mut(off_out),
1356             pass_usize(len),
1357             c_uint(flags)
1358         ))
1359     }
1360 }
1361 
1362 #[inline]
memfd_create(name: &CStr, flags: MemfdFlags) -> io::Result<OwnedFd>1363 pub(crate) fn memfd_create(name: &CStr, flags: MemfdFlags) -> io::Result<OwnedFd> {
1364     unsafe { ret_owned_fd(syscall_readonly!(__NR_memfd_create, name, flags)) }
1365 }
1366 
1367 #[inline]
sendfile( out_fd: BorrowedFd<'_>, in_fd: BorrowedFd<'_>, offset: Option<&mut u64>, count: usize, ) -> io::Result<usize>1368 pub(crate) fn sendfile(
1369     out_fd: BorrowedFd<'_>,
1370     in_fd: BorrowedFd<'_>,
1371     offset: Option<&mut u64>,
1372     count: usize,
1373 ) -> io::Result<usize> {
1374     #[cfg(target_pointer_width = "32")]
1375     unsafe {
1376         ret_usize(syscall!(
1377             __NR_sendfile64,
1378             out_fd,
1379             in_fd,
1380             opt_mut(offset),
1381             pass_usize(count)
1382         ))
1383     }
1384     #[cfg(target_pointer_width = "64")]
1385     unsafe {
1386         ret_usize(syscall!(
1387             __NR_sendfile,
1388             out_fd,
1389             in_fd,
1390             opt_mut(offset),
1391             pass_usize(count)
1392         ))
1393     }
1394 }
1395 
1396 #[inline]
1397 #[cfg(any(target_os = "android", target_os = "linux"))]
mount( source: Option<&CStr>, target: &CStr, file_system_type: Option<&CStr>, flags: super::types::MountFlagsArg, data: Option<&CStr>, ) -> io::Result<()>1398 pub(crate) fn mount(
1399     source: Option<&CStr>,
1400     target: &CStr,
1401     file_system_type: Option<&CStr>,
1402     flags: super::types::MountFlagsArg,
1403     data: Option<&CStr>,
1404 ) -> io::Result<()> {
1405     unsafe {
1406         ret(syscall_readonly!(
1407             __NR_mount,
1408             source,
1409             target,
1410             file_system_type,
1411             flags,
1412             data
1413         ))
1414     }
1415 }
1416