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