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