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