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