1 //! libc syscalls supporting `rustix::fs`.
2 
3 use crate::backend::c;
4 #[cfg(any(
5     not(target_os = "redox"),
6     feature = "alloc",
7     all(linux_kernel, feature = "procfs")
8 ))]
9 use crate::backend::conv::ret_usize;
10 use crate::backend::conv::{borrowed_fd, c_str, ret, ret_c_int, ret_off_t, ret_owned_fd};
11 use crate::fd::{BorrowedFd, OwnedFd};
12 use crate::ffi::CStr;
13 #[cfg(all(apple, feature = "alloc"))]
14 use crate::ffi::CString;
15 #[cfg(not(any(target_os = "espidf", target_os = "vita")))]
16 use crate::fs::Access;
17 #[cfg(not(any(
18     apple,
19     netbsdlike,
20     solarish,
21     target_os = "dragonfly",
22     target_os = "espidf",
23     target_os = "haiku",
24     target_os = "redox",
25     target_os = "vita",
26 )))]
27 use crate::fs::Advice;
28 #[cfg(not(any(target_os = "espidf", target_os = "redox")))]
29 use crate::fs::AtFlags;
30 #[cfg(not(any(
31     netbsdlike,
32     solarish,
33     target_os = "aix",
34     target_os = "dragonfly",
35     target_os = "espidf",
36     target_os = "nto",
37     target_os = "redox",
38     target_os = "vita",
39 )))]
40 use crate::fs::FallocateFlags;
41 #[cfg(not(any(target_os = "espidf", target_os = "vita", target_os = "wasi")))]
42 use crate::fs::FlockOperation;
43 #[cfg(any(linux_kernel, target_os = "freebsd"))]
44 use crate::fs::MemfdFlags;
45 #[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))]
46 use crate::fs::SealFlags;
47 #[cfg(not(any(
48     solarish,
49     target_os = "espidf",
50     target_os = "haiku",
51     target_os = "netbsd",
52     target_os = "nto",
53     target_os = "redox",
54     target_os = "vita",
55     target_os = "wasi",
56 )))]
57 use crate::fs::StatFs;
58 #[cfg(not(any(target_os = "espidf", target_os = "vita")))]
59 use crate::fs::Timestamps;
60 #[cfg(not(any(
61     apple,
62     target_os = "espidf",
63     target_os = "redox",
64     target_os = "vita",
65     target_os = "wasi"
66 )))]
67 use crate::fs::{Dev, FileType};
68 use crate::fs::{Mode, OFlags, SeekFrom, Stat};
69 #[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))]
70 use crate::fs::{StatVfs, StatVfsMountFlags};
71 use crate::io;
72 #[cfg(all(target_env = "gnu", fix_y2038))]
73 use crate::timespec::LibcTimespec;
74 #[cfg(not(target_os = "wasi"))]
75 use crate::ugid::{Gid, Uid};
76 #[cfg(all(apple, feature = "alloc"))]
77 use alloc::vec;
78 use core::mem::MaybeUninit;
79 #[cfg(apple)]
80 use {
81     crate::backend::conv::nonnegative_ret,
82     crate::fs::{copyfile_state_t, CloneFlags, CopyfileFlags},
83 };
84 #[cfg(any(apple, linux_kernel))]
85 use {crate::fs::XattrFlags, core::mem::size_of, core::ptr::null_mut};
86 #[cfg(linux_kernel)]
87 use {
88     crate::fs::{RenameFlags, ResolveFlags, Statx, StatxFlags, CWD},
89     core::ptr::null,
90 };
91 
92 #[cfg(all(target_env = "gnu", fix_y2038))]
93 weak!(fn __utimensat64(c::c_int, *const c::c_char, *const LibcTimespec, c::c_int) -> c::c_int);
94 #[cfg(all(target_env = "gnu", fix_y2038))]
95 weak!(fn __futimens64(c::c_int, *const LibcTimespec) -> c::c_int);
96 
97 /// Use a direct syscall (via libc) for `open`.
98 ///
99 /// This is only currently necessary as a workaround for old glibc; see below.
100 #[cfg(all(unix, target_env = "gnu"))]
open_via_syscall(path: &CStr, oflags: OFlags, mode: Mode) -> io::Result<OwnedFd>101 fn open_via_syscall(path: &CStr, oflags: OFlags, mode: Mode) -> io::Result<OwnedFd> {
102     // Linux on aarch64, loongarch64 and riscv64 has no `open` syscall so use
103     // `openat`.
104     #[cfg(any(
105         target_arch = "aarch64",
106         target_arch = "riscv32",
107         target_arch = "riscv64",
108         target_arch = "csky",
109         target_arch = "loongarch64"
110     ))]
111     {
112         openat_via_syscall(CWD, path, oflags, mode)
113     }
114 
115     // Use the `open` syscall.
116     #[cfg(not(any(
117         target_arch = "aarch64",
118         target_arch = "riscv32",
119         target_arch = "riscv64",
120         target_arch = "csky",
121         target_arch = "loongarch64"
122     )))]
123     unsafe {
124         syscall! {
125             fn open(
126                 pathname: *const c::c_char,
127                 oflags: c::c_int,
128                 mode: c::mode_t
129             ) via SYS_open -> c::c_int
130         }
131 
132         ret_owned_fd(open(
133             c_str(path),
134             bitflags_bits!(oflags),
135             bitflags_bits!(mode),
136         ))
137     }
138 }
139 
open(path: &CStr, oflags: OFlags, mode: Mode) -> io::Result<OwnedFd>140 pub(crate) fn open(path: &CStr, oflags: OFlags, mode: Mode) -> io::Result<OwnedFd> {
141     // Work around <https://sourceware.org/bugzilla/show_bug.cgi?id=17523>.
142     // glibc versions before 2.25 don't handle `O_TMPFILE` correctly.
143     #[cfg(all(unix, target_env = "gnu", not(target_os = "hurd")))]
144     if oflags.contains(OFlags::TMPFILE) && crate::backend::if_glibc_is_less_than_2_25() {
145         return open_via_syscall(path, oflags, mode);
146     }
147 
148     // On these platforms, `mode_t` is `u16` and can't be passed directly to a
149     // variadic function.
150     #[cfg(any(
151         apple,
152         freebsdlike,
153         all(target_os = "android", target_pointer_width = "32")
154     ))]
155     let mode: c::c_uint = mode.bits().into();
156 
157     // Otherwise, cast to `mode_t` as that's what `open` is documented to take.
158     #[cfg(not(any(
159         apple,
160         freebsdlike,
161         all(target_os = "android", target_pointer_width = "32")
162     )))]
163     let mode: c::mode_t = mode.bits() as _;
164 
165     unsafe { ret_owned_fd(c::open(c_str(path), bitflags_bits!(oflags), mode)) }
166 }
167 
168 /// Use a direct syscall (via libc) for `openat`.
169 ///
170 /// This is only currently necessary as a workaround for old glibc; see below.
171 #[cfg(all(unix, target_env = "gnu", not(target_os = "hurd")))]
openat_via_syscall( dirfd: BorrowedFd<'_>, path: &CStr, oflags: OFlags, mode: Mode, ) -> io::Result<OwnedFd>172 fn openat_via_syscall(
173     dirfd: BorrowedFd<'_>,
174     path: &CStr,
175     oflags: OFlags,
176     mode: Mode,
177 ) -> io::Result<OwnedFd> {
178     syscall! {
179         fn openat(
180             base_dirfd: c::c_int,
181             pathname: *const c::c_char,
182             oflags: c::c_int,
183             mode: c::mode_t
184         ) via SYS_openat -> c::c_int
185     }
186 
187     unsafe {
188         ret_owned_fd(openat(
189             borrowed_fd(dirfd),
190             c_str(path),
191             bitflags_bits!(oflags),
192             bitflags_bits!(mode),
193         ))
194     }
195 }
196 
197 #[cfg(not(target_os = "redox"))]
openat( dirfd: BorrowedFd<'_>, path: &CStr, oflags: OFlags, mode: Mode, ) -> io::Result<OwnedFd>198 pub(crate) fn openat(
199     dirfd: BorrowedFd<'_>,
200     path: &CStr,
201     oflags: OFlags,
202     mode: Mode,
203 ) -> io::Result<OwnedFd> {
204     // Work around <https://sourceware.org/bugzilla/show_bug.cgi?id=17523>.
205     // glibc versions before 2.25 don't handle `O_TMPFILE` correctly.
206     #[cfg(all(unix, target_env = "gnu", not(target_os = "hurd")))]
207     if oflags.contains(OFlags::TMPFILE) && crate::backend::if_glibc_is_less_than_2_25() {
208         return openat_via_syscall(dirfd, path, oflags, mode);
209     }
210 
211     // On these platforms, `mode_t` is `u16` and can't be passed directly to a
212     // variadic function.
213     #[cfg(any(
214         apple,
215         freebsdlike,
216         all(target_os = "android", target_pointer_width = "32")
217     ))]
218     let mode: c::c_uint = mode.bits().into();
219 
220     // Otherwise, cast to `mode_t` as that's what `open` is documented to take.
221     #[cfg(not(any(
222         apple,
223         freebsdlike,
224         all(target_os = "android", target_pointer_width = "32")
225     )))]
226     let mode: c::mode_t = mode.bits() as _;
227 
228     unsafe {
229         ret_owned_fd(c::openat(
230             borrowed_fd(dirfd),
231             c_str(path),
232             bitflags_bits!(oflags),
233             mode,
234         ))
235     }
236 }
237 
238 #[cfg(not(any(
239     solarish,
240     target_os = "espidf",
241     target_os = "haiku",
242     target_os = "netbsd",
243     target_os = "nto",
244     target_os = "redox",
245     target_os = "vita",
246     target_os = "wasi",
247 )))]
248 #[inline]
statfs(filename: &CStr) -> io::Result<StatFs>249 pub(crate) fn statfs(filename: &CStr) -> io::Result<StatFs> {
250     unsafe {
251         let mut result = MaybeUninit::<StatFs>::uninit();
252         ret(c::statfs(c_str(filename), result.as_mut_ptr()))?;
253         Ok(result.assume_init())
254     }
255 }
256 
257 #[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))]
258 #[inline]
statvfs(filename: &CStr) -> io::Result<StatVfs>259 pub(crate) fn statvfs(filename: &CStr) -> io::Result<StatVfs> {
260     unsafe {
261         let mut result = MaybeUninit::<c::statvfs>::uninit();
262         ret(c::statvfs(c_str(filename), result.as_mut_ptr()))?;
263         Ok(libc_statvfs_to_statvfs(result.assume_init()))
264     }
265 }
266 
267 #[cfg(feature = "alloc")]
268 #[inline]
readlink(path: &CStr, buf: &mut [u8]) -> io::Result<usize>269 pub(crate) fn readlink(path: &CStr, buf: &mut [u8]) -> io::Result<usize> {
270     unsafe {
271         ret_usize(
272             c::readlink(c_str(path), buf.as_mut_ptr().cast::<c::c_char>(), buf.len()) as isize,
273         )
274     }
275 }
276 
277 #[cfg(not(target_os = "redox"))]
278 #[inline]
readlinkat( dirfd: BorrowedFd<'_>, path: &CStr, buf: &mut [MaybeUninit<u8>], ) -> io::Result<usize>279 pub(crate) fn readlinkat(
280     dirfd: BorrowedFd<'_>,
281     path: &CStr,
282     buf: &mut [MaybeUninit<u8>],
283 ) -> io::Result<usize> {
284     unsafe {
285         ret_usize(c::readlinkat(
286             borrowed_fd(dirfd),
287             c_str(path),
288             buf.as_mut_ptr().cast::<c::c_char>(),
289             buf.len(),
290         ) as isize)
291     }
292 }
293 
mkdir(path: &CStr, mode: Mode) -> io::Result<()>294 pub(crate) fn mkdir(path: &CStr, mode: Mode) -> io::Result<()> {
295     unsafe { ret(c::mkdir(c_str(path), mode.bits() as c::mode_t)) }
296 }
297 
298 #[cfg(not(target_os = "redox"))]
mkdirat(dirfd: BorrowedFd<'_>, path: &CStr, mode: Mode) -> io::Result<()>299 pub(crate) fn mkdirat(dirfd: BorrowedFd<'_>, path: &CStr, mode: Mode) -> io::Result<()> {
300     unsafe {
301         ret(c::mkdirat(
302             borrowed_fd(dirfd),
303             c_str(path),
304             mode.bits() as c::mode_t,
305         ))
306     }
307 }
308 
309 #[cfg(linux_kernel)]
getdents_uninit( fd: BorrowedFd<'_>, buf: &mut [MaybeUninit<u8>], ) -> io::Result<usize>310 pub(crate) fn getdents_uninit(
311     fd: BorrowedFd<'_>,
312     buf: &mut [MaybeUninit<u8>],
313 ) -> io::Result<usize> {
314     syscall! {
315         fn getdents64(
316             fd: c::c_int,
317             dirp: *mut c::c_void,
318             count: usize
319         ) via SYS_getdents64 -> c::ssize_t
320     }
321     unsafe {
322         ret_usize(getdents64(
323             borrowed_fd(fd),
324             buf.as_mut_ptr().cast::<c::c_void>(),
325             buf.len(),
326         ))
327     }
328 }
329 
link(old_path: &CStr, new_path: &CStr) -> io::Result<()>330 pub(crate) fn link(old_path: &CStr, new_path: &CStr) -> io::Result<()> {
331     unsafe { ret(c::link(c_str(old_path), c_str(new_path))) }
332 }
333 
334 #[cfg(not(any(target_os = "espidf", target_os = "redox")))]
linkat( old_dirfd: BorrowedFd<'_>, old_path: &CStr, new_dirfd: BorrowedFd<'_>, new_path: &CStr, flags: AtFlags, ) -> io::Result<()>335 pub(crate) fn linkat(
336     old_dirfd: BorrowedFd<'_>,
337     old_path: &CStr,
338     new_dirfd: BorrowedFd<'_>,
339     new_path: &CStr,
340     flags: AtFlags,
341 ) -> io::Result<()> {
342     // macOS <= 10.9 lacks `linkat`.
343     #[cfg(target_os = "macos")]
344     unsafe {
345         weak! {
346             fn linkat(
347                 c::c_int,
348                 *const c::c_char,
349                 c::c_int,
350                 *const c::c_char,
351                 c::c_int
352             ) -> c::c_int
353         }
354         // If we have `linkat`, use it.
355         if let Some(libc_linkat) = linkat.get() {
356             return ret(libc_linkat(
357                 borrowed_fd(old_dirfd),
358                 c_str(old_path),
359                 borrowed_fd(new_dirfd),
360                 c_str(new_path),
361                 bitflags_bits!(flags),
362             ));
363         }
364         // Otherwise, see if we can emulate the `AT_FDCWD` case.
365         if borrowed_fd(old_dirfd) != c::AT_FDCWD || borrowed_fd(new_dirfd) != c::AT_FDCWD {
366             return Err(io::Errno::NOSYS);
367         }
368         if flags.intersects(!AtFlags::SYMLINK_FOLLOW) {
369             return Err(io::Errno::INVAL);
370         }
371         if !flags.is_empty() {
372             return Err(io::Errno::OPNOTSUPP);
373         }
374         ret(c::link(c_str(old_path), c_str(new_path)))
375     }
376 
377     #[cfg(not(target_os = "macos"))]
378     unsafe {
379         ret(c::linkat(
380             borrowed_fd(old_dirfd),
381             c_str(old_path),
382             borrowed_fd(new_dirfd),
383             c_str(new_path),
384             bitflags_bits!(flags),
385         ))
386     }
387 }
388 
rmdir(path: &CStr) -> io::Result<()>389 pub(crate) fn rmdir(path: &CStr) -> io::Result<()> {
390     unsafe { ret(c::rmdir(c_str(path))) }
391 }
392 
unlink(path: &CStr) -> io::Result<()>393 pub(crate) fn unlink(path: &CStr) -> io::Result<()> {
394     unsafe { ret(c::unlink(c_str(path))) }
395 }
396 
397 #[cfg(not(any(target_os = "espidf", target_os = "redox")))]
unlinkat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<()>398 pub(crate) fn unlinkat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<()> {
399     // macOS <= 10.9 lacks `unlinkat`.
400     #[cfg(target_os = "macos")]
401     unsafe {
402         weak! {
403             fn unlinkat(
404                 c::c_int,
405                 *const c::c_char,
406                 c::c_int
407             ) -> c::c_int
408         }
409         // If we have `unlinkat`, use it.
410         if let Some(libc_unlinkat) = unlinkat.get() {
411             return ret(libc_unlinkat(
412                 borrowed_fd(dirfd),
413                 c_str(path),
414                 bitflags_bits!(flags),
415             ));
416         }
417         // Otherwise, see if we can emulate the `AT_FDCWD` case.
418         if borrowed_fd(dirfd) != c::AT_FDCWD {
419             return Err(io::Errno::NOSYS);
420         }
421         if flags.intersects(!AtFlags::REMOVEDIR) {
422             return Err(io::Errno::INVAL);
423         }
424         if flags.contains(AtFlags::REMOVEDIR) {
425             ret(c::rmdir(c_str(path)))
426         } else {
427             ret(c::unlink(c_str(path)))
428         }
429     }
430 
431     #[cfg(not(target_os = "macos"))]
432     unsafe {
433         ret(c::unlinkat(
434             borrowed_fd(dirfd),
435             c_str(path),
436             bitflags_bits!(flags),
437         ))
438     }
439 }
440 
rename(old_path: &CStr, new_path: &CStr) -> io::Result<()>441 pub(crate) fn rename(old_path: &CStr, new_path: &CStr) -> io::Result<()> {
442     unsafe { ret(c::rename(c_str(old_path), c_str(new_path))) }
443 }
444 
445 #[cfg(not(target_os = "redox"))]
renameat( old_dirfd: BorrowedFd<'_>, old_path: &CStr, new_dirfd: BorrowedFd<'_>, new_path: &CStr, ) -> io::Result<()>446 pub(crate) fn renameat(
447     old_dirfd: BorrowedFd<'_>,
448     old_path: &CStr,
449     new_dirfd: BorrowedFd<'_>,
450     new_path: &CStr,
451 ) -> io::Result<()> {
452     // macOS <= 10.9 lacks `renameat`.
453     #[cfg(target_os = "macos")]
454     unsafe {
455         weak! {
456             fn renameat(
457                 c::c_int,
458                 *const c::c_char,
459                 c::c_int,
460                 *const c::c_char
461             ) -> c::c_int
462         }
463         // If we have `renameat`, use it.
464         if let Some(libc_renameat) = renameat.get() {
465             return ret(libc_renameat(
466                 borrowed_fd(old_dirfd),
467                 c_str(old_path),
468                 borrowed_fd(new_dirfd),
469                 c_str(new_path),
470             ));
471         }
472         // Otherwise, see if we can emulate the `AT_FDCWD` case.
473         if borrowed_fd(old_dirfd) != c::AT_FDCWD || borrowed_fd(new_dirfd) != c::AT_FDCWD {
474             return Err(io::Errno::NOSYS);
475         }
476         ret(c::rename(c_str(old_path), c_str(new_path)))
477     }
478 
479     #[cfg(not(target_os = "macos"))]
480     unsafe {
481         ret(c::renameat(
482             borrowed_fd(old_dirfd),
483             c_str(old_path),
484             borrowed_fd(new_dirfd),
485             c_str(new_path),
486         ))
487     }
488 }
489 
490 #[cfg(all(target_os = "linux", target_env = "gnu"))]
renameat2( old_dirfd: BorrowedFd<'_>, old_path: &CStr, new_dirfd: BorrowedFd<'_>, new_path: &CStr, flags: RenameFlags, ) -> io::Result<()>491 pub(crate) fn renameat2(
492     old_dirfd: BorrowedFd<'_>,
493     old_path: &CStr,
494     new_dirfd: BorrowedFd<'_>,
495     new_path: &CStr,
496     flags: RenameFlags,
497 ) -> io::Result<()> {
498     // `renameat2` wasn't supported in glibc until 2.28.
499     weak_or_syscall! {
500         fn renameat2(
501             olddirfd: c::c_int,
502             oldpath: *const c::c_char,
503             newdirfd: c::c_int,
504             newpath: *const c::c_char,
505             flags: c::c_uint
506         ) via SYS_renameat2 -> c::c_int
507     }
508 
509     unsafe {
510         ret(renameat2(
511             borrowed_fd(old_dirfd),
512             c_str(old_path),
513             borrowed_fd(new_dirfd),
514             c_str(new_path),
515             flags.bits(),
516         ))
517     }
518 }
519 
520 #[cfg(any(
521     target_os = "android",
522     all(target_os = "linux", not(target_env = "gnu")),
523 ))]
524 #[inline]
renameat2( old_dirfd: BorrowedFd<'_>, old_path: &CStr, new_dirfd: BorrowedFd<'_>, new_path: &CStr, flags: RenameFlags, ) -> io::Result<()>525 pub(crate) fn renameat2(
526     old_dirfd: BorrowedFd<'_>,
527     old_path: &CStr,
528     new_dirfd: BorrowedFd<'_>,
529     new_path: &CStr,
530     flags: RenameFlags,
531 ) -> io::Result<()> {
532     // At present, `libc` only has `renameat2` defined for glibc. If we have
533     // no flags, we can use plain `renameat`, but otherwise we use `syscall!`.
534     // to call `renameat2` ourselves.
535     if flags.is_empty() {
536         renameat(old_dirfd, old_path, new_dirfd, new_path)
537     } else {
538         syscall! {
539             fn renameat2(
540                 olddirfd: c::c_int,
541                 oldpath: *const c::c_char,
542                 newdirfd: c::c_int,
543                 newpath: *const c::c_char,
544                 flags: c::c_uint
545             ) via SYS_renameat2 -> c::c_int
546         }
547 
548         unsafe {
549             ret(renameat2(
550                 borrowed_fd(old_dirfd),
551                 c_str(old_path),
552                 borrowed_fd(new_dirfd),
553                 c_str(new_path),
554                 flags.bits(),
555             ))
556         }
557     }
558 }
559 
symlink(old_path: &CStr, new_path: &CStr) -> io::Result<()>560 pub(crate) fn symlink(old_path: &CStr, new_path: &CStr) -> io::Result<()> {
561     unsafe { ret(c::symlink(c_str(old_path), c_str(new_path))) }
562 }
563 
564 #[cfg(not(target_os = "redox"))]
symlinkat( old_path: &CStr, new_dirfd: BorrowedFd<'_>, new_path: &CStr, ) -> io::Result<()>565 pub(crate) fn symlinkat(
566     old_path: &CStr,
567     new_dirfd: BorrowedFd<'_>,
568     new_path: &CStr,
569 ) -> io::Result<()> {
570     unsafe {
571         ret(c::symlinkat(
572             c_str(old_path),
573             borrowed_fd(new_dirfd),
574             c_str(new_path),
575         ))
576     }
577 }
578 
stat(path: &CStr) -> io::Result<Stat>579 pub(crate) fn stat(path: &CStr) -> io::Result<Stat> {
580     // See the comments in `fstat` about using `crate::fs::statx` here.
581     #[cfg(all(
582         linux_kernel,
583         any(
584             target_pointer_width = "32",
585             target_arch = "mips64",
586             target_arch = "mips64r6"
587         )
588     ))]
589     {
590         match crate::fs::statx(
591             crate::fs::CWD,
592             path,
593             AtFlags::empty(),
594             StatxFlags::BASIC_STATS,
595         ) {
596             Ok(x) => statx_to_stat(x),
597             Err(io::Errno::NOSYS) => statat_old(crate::fs::CWD, path, AtFlags::empty()),
598             Err(err) => Err(err),
599         }
600     }
601 
602     // Main version: libc is y2038 safe. Or, the platform is not y2038 safe and
603     // there's nothing practical we can do.
604     #[cfg(not(all(
605         linux_kernel,
606         any(
607             target_pointer_width = "32",
608             target_arch = "mips64",
609             target_arch = "mips64r6"
610         )
611     )))]
612     unsafe {
613         let mut stat = MaybeUninit::<Stat>::uninit();
614         ret(c::stat(c_str(path), stat.as_mut_ptr()))?;
615         Ok(stat.assume_init())
616     }
617 }
618 
lstat(path: &CStr) -> io::Result<Stat>619 pub(crate) fn lstat(path: &CStr) -> io::Result<Stat> {
620     // See the comments in `fstat` about using `crate::fs::statx` here.
621     #[cfg(all(
622         linux_kernel,
623         any(
624             target_pointer_width = "32",
625             target_arch = "mips64",
626             target_arch = "mips64r6"
627         )
628     ))]
629     {
630         match crate::fs::statx(
631             crate::fs::CWD,
632             path,
633             AtFlags::SYMLINK_NOFOLLOW,
634             StatxFlags::BASIC_STATS,
635         ) {
636             Ok(x) => statx_to_stat(x),
637             Err(io::Errno::NOSYS) => statat_old(crate::fs::CWD, path, AtFlags::SYMLINK_NOFOLLOW),
638             Err(err) => Err(err),
639         }
640     }
641 
642     // Main version: libc is y2038 safe. Or, the platform is not y2038 safe and
643     // there's nothing practical we can do.
644     #[cfg(not(all(
645         linux_kernel,
646         any(
647             target_pointer_width = "32",
648             target_arch = "mips64",
649             target_arch = "mips64r6"
650         )
651     )))]
652     unsafe {
653         let mut stat = MaybeUninit::<Stat>::uninit();
654         ret(c::lstat(c_str(path), stat.as_mut_ptr()))?;
655         Ok(stat.assume_init())
656     }
657 }
658 
659 #[cfg(not(any(target_os = "espidf", target_os = "redox")))]
statat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat>660 pub(crate) fn statat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat> {
661     // See the comments in `fstat` about using `crate::fs::statx` here.
662     #[cfg(all(
663         linux_kernel,
664         any(
665             target_pointer_width = "32",
666             target_arch = "mips64",
667             target_arch = "mips64r6"
668         )
669     ))]
670     {
671         match crate::fs::statx(dirfd, path, flags, StatxFlags::BASIC_STATS) {
672             Ok(x) => statx_to_stat(x),
673             Err(io::Errno::NOSYS) => statat_old(dirfd, path, flags),
674             Err(err) => Err(err),
675         }
676     }
677 
678     // Main version: libc is y2038 safe. Or, the platform is not y2038 safe and
679     // there's nothing practical we can do.
680     #[cfg(not(all(
681         linux_kernel,
682         any(
683             target_pointer_width = "32",
684             target_arch = "mips64",
685             target_arch = "mips64r6"
686         )
687     )))]
688     unsafe {
689         let mut stat = MaybeUninit::<Stat>::uninit();
690         ret(c::fstatat(
691             borrowed_fd(dirfd),
692             c_str(path),
693             stat.as_mut_ptr(),
694             bitflags_bits!(flags),
695         ))?;
696         Ok(stat.assume_init())
697     }
698 }
699 
700 #[cfg(all(
701     linux_kernel,
702     any(
703         target_pointer_width = "32",
704         target_arch = "mips64",
705         target_arch = "mips64r6"
706     )
707 ))]
statat_old(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat>708 fn statat_old(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat> {
709     unsafe {
710         let mut result = MaybeUninit::<c::stat64>::uninit();
711         ret(c::fstatat(
712             borrowed_fd(dirfd),
713             c_str(path),
714             result.as_mut_ptr(),
715             bitflags_bits!(flags),
716         ))?;
717         stat64_to_stat(result.assume_init())
718     }
719 }
720 
721 #[cfg(not(any(target_os = "espidf", target_os = "emscripten", target_os = "vita")))]
access(path: &CStr, access: Access) -> io::Result<()>722 pub(crate) fn access(path: &CStr, access: Access) -> io::Result<()> {
723     unsafe { ret(c::access(c_str(path), access.bits())) }
724 }
725 
726 #[cfg(not(any(
727     target_os = "emscripten",
728     target_os = "espidf",
729     target_os = "redox",
730     target_os = "vita"
731 )))]
accessat( dirfd: BorrowedFd<'_>, path: &CStr, access: Access, flags: AtFlags, ) -> io::Result<()>732 pub(crate) fn accessat(
733     dirfd: BorrowedFd<'_>,
734     path: &CStr,
735     access: Access,
736     flags: AtFlags,
737 ) -> io::Result<()> {
738     // macOS <= 10.9 lacks `faccessat`.
739     #[cfg(target_os = "macos")]
740     unsafe {
741         weak! {
742             fn faccessat(
743                 c::c_int,
744                 *const c::c_char,
745                 c::c_int,
746                 c::c_int
747             ) -> c::c_int
748         }
749         // If we have `faccessat`, use it.
750         if let Some(libc_faccessat) = faccessat.get() {
751             return ret(libc_faccessat(
752                 borrowed_fd(dirfd),
753                 c_str(path),
754                 bitflags_bits!(access),
755                 bitflags_bits!(flags),
756             ));
757         }
758         // Otherwise, see if we can emulate the `AT_FDCWD` case.
759         if borrowed_fd(dirfd) != c::AT_FDCWD {
760             return Err(io::Errno::NOSYS);
761         }
762         if flags.intersects(!(AtFlags::EACCESS | AtFlags::SYMLINK_NOFOLLOW)) {
763             return Err(io::Errno::INVAL);
764         }
765         if !flags.is_empty() {
766             return Err(io::Errno::OPNOTSUPP);
767         }
768         ret(c::access(c_str(path), bitflags_bits!(access)))
769     }
770 
771     #[cfg(not(target_os = "macos"))]
772     unsafe {
773         ret(c::faccessat(
774             borrowed_fd(dirfd),
775             c_str(path),
776             bitflags_bits!(access),
777             bitflags_bits!(flags),
778         ))
779     }
780 }
781 
782 #[cfg(target_os = "emscripten")]
access(_path: &CStr, _access: Access) -> io::Result<()>783 pub(crate) fn access(_path: &CStr, _access: Access) -> io::Result<()> {
784     Ok(())
785 }
786 
787 #[cfg(target_os = "emscripten")]
accessat( _dirfd: BorrowedFd<'_>, _path: &CStr, _access: Access, _flags: AtFlags, ) -> io::Result<()>788 pub(crate) fn accessat(
789     _dirfd: BorrowedFd<'_>,
790     _path: &CStr,
791     _access: Access,
792     _flags: AtFlags,
793 ) -> io::Result<()> {
794     Ok(())
795 }
796 
797 #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "vita")))]
utimensat( dirfd: BorrowedFd<'_>, path: &CStr, times: &Timestamps, flags: AtFlags, ) -> io::Result<()>798 pub(crate) fn utimensat(
799     dirfd: BorrowedFd<'_>,
800     path: &CStr,
801     times: &Timestamps,
802     flags: AtFlags,
803 ) -> io::Result<()> {
804     // Old 32-bit version: libc has `utimensat` but it is not y2038 safe by
805     // default. But there may be a `__utimensat64` we can use.
806     #[cfg(all(fix_y2038, not(apple)))]
807     {
808         #[cfg(target_env = "gnu")]
809         if let Some(libc_utimensat) = __utimensat64.get() {
810             let libc_times: [LibcTimespec; 2] = [
811                 times.last_access.clone().into(),
812                 times.last_modification.clone().into(),
813             ];
814 
815             unsafe {
816                 return ret(libc_utimensat(
817                     borrowed_fd(dirfd),
818                     c_str(path),
819                     libc_times.as_ptr(),
820                     bitflags_bits!(flags),
821                 ));
822             }
823         }
824 
825         utimensat_old(dirfd, path, times, flags)
826     }
827 
828     // Main version: libc is y2038 safe and has `utimensat`. Or, the platform
829     // is not y2038 safe and there's nothing practical we can do.
830     #[cfg(not(any(apple, fix_y2038)))]
831     unsafe {
832         use crate::utils::as_ptr;
833 
834         ret(c::utimensat(
835             borrowed_fd(dirfd),
836             c_str(path),
837             as_ptr(times).cast(),
838             bitflags_bits!(flags),
839         ))
840     }
841 
842     // Apple version: `utimensat` was introduced in macOS 10.13.
843     #[cfg(apple)]
844     unsafe {
845         use crate::utils::as_ptr;
846 
847         // ABI details
848         weak! {
849             fn utimensat(
850                 c::c_int,
851                 *const c::c_char,
852                 *const c::timespec,
853                 c::c_int
854             ) -> c::c_int
855         }
856         extern "C" {
857             fn setattrlist(
858                 path: *const c::c_char,
859                 attr_list: *const Attrlist,
860                 attr_buf: *const c::c_void,
861                 attr_buf_size: c::size_t,
862                 options: c::c_ulong,
863             ) -> c::c_int;
864         }
865         const FSOPT_NOFOLLOW: c::c_ulong = 0x0000_0001;
866 
867         // If we have `utimensat`, use it.
868         if let Some(have_utimensat) = utimensat.get() {
869             return ret(have_utimensat(
870                 borrowed_fd(dirfd),
871                 c_str(path),
872                 as_ptr(times).cast(),
873                 bitflags_bits!(flags),
874             ));
875         }
876 
877         // Convert `times`. We only need this in the child, but do it before
878         // calling `fork` because it might fail.
879         let (attrbuf_size, times, attrs) = times_to_attrlist(times)?;
880 
881         // `setattrlistat` was introduced in 10.13 along with `utimensat`, so
882         // if we don't have `utimensat`, we don't have `setattrlistat` either.
883         // Emulate it using `fork`, and `fchdir` and [`setattrlist`].
884         //
885         // [`setattrlist`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setattrlist.2.html
886         match c::fork() {
887             -1 => Err(io::Errno::IO),
888             0 => {
889                 if c::fchdir(borrowed_fd(dirfd)) != 0 {
890                     let code = match libc_errno::errno().0 {
891                         c::EACCES => 2,
892                         c::ENOTDIR => 3,
893                         _ => 1,
894                     };
895                     c::_exit(code);
896                 }
897 
898                 let mut flags_arg = 0;
899                 if flags.contains(AtFlags::SYMLINK_NOFOLLOW) {
900                     flags_arg |= FSOPT_NOFOLLOW;
901                 }
902 
903                 if setattrlist(
904                     c_str(path),
905                     &attrs,
906                     as_ptr(&times).cast(),
907                     attrbuf_size,
908                     flags_arg,
909                 ) != 0
910                 {
911                     // Translate expected `errno` codes into ad-hoc integer
912                     // values suitable for exit statuses.
913                     let code = match libc_errno::errno().0 {
914                         c::EACCES => 2,
915                         c::ENOTDIR => 3,
916                         c::EPERM => 4,
917                         c::EROFS => 5,
918                         c::ELOOP => 6,
919                         c::ENOENT => 7,
920                         c::ENAMETOOLONG => 8,
921                         c::EINVAL => 9,
922                         c::ESRCH => 10,
923                         c::ENOTSUP => 11,
924                         _ => 1,
925                     };
926                     c::_exit(code);
927                 }
928 
929                 c::_exit(0);
930             }
931             child_pid => {
932                 let mut wstatus = 0;
933                 let _ = ret_c_int(c::waitpid(child_pid, &mut wstatus, 0))?;
934                 if c::WIFEXITED(wstatus) {
935                     // Translate our ad-hoc exit statuses back to `errno`
936                     // codes.
937                     match c::WEXITSTATUS(wstatus) {
938                         0 => Ok(()),
939                         2 => Err(io::Errno::ACCESS),
940                         3 => Err(io::Errno::NOTDIR),
941                         4 => Err(io::Errno::PERM),
942                         5 => Err(io::Errno::ROFS),
943                         6 => Err(io::Errno::LOOP),
944                         7 => Err(io::Errno::NOENT),
945                         8 => Err(io::Errno::NAMETOOLONG),
946                         9 => Err(io::Errno::INVAL),
947                         10 => Err(io::Errno::SRCH),
948                         11 => Err(io::Errno::NOTSUP),
949                         _ => Err(io::Errno::IO),
950                     }
951                 } else {
952                     Err(io::Errno::IO)
953                 }
954             }
955         }
956     }
957 }
958 
959 #[cfg(all(fix_y2038, not(apple)))]
utimensat_old( dirfd: BorrowedFd<'_>, path: &CStr, times: &Timestamps, flags: AtFlags, ) -> io::Result<()>960 fn utimensat_old(
961     dirfd: BorrowedFd<'_>,
962     path: &CStr,
963     times: &Timestamps,
964     flags: AtFlags,
965 ) -> io::Result<()> {
966     let old_times = [
967         c::timespec {
968             tv_sec: times
969                 .last_access
970                 .tv_sec
971                 .try_into()
972                 .map_err(|_| io::Errno::OVERFLOW)?,
973             tv_nsec: times
974                 .last_access
975                 .tv_nsec
976                 .try_into()
977                 .map_err(|_| io::Errno::OVERFLOW)?,
978         },
979         c::timespec {
980             tv_sec: times
981                 .last_modification
982                 .tv_sec
983                 .try_into()
984                 .map_err(|_| io::Errno::OVERFLOW)?,
985             tv_nsec: times
986                 .last_modification
987                 .tv_nsec
988                 .try_into()
989                 .map_err(|_| io::Errno::OVERFLOW)?,
990         },
991     ];
992     unsafe {
993         ret(c::utimensat(
994             borrowed_fd(dirfd),
995             c_str(path),
996             old_times.as_ptr(),
997             bitflags_bits!(flags),
998         ))
999     }
1000 }
1001 
1002 #[cfg(not(target_os = "wasi"))]
chmod(path: &CStr, mode: Mode) -> io::Result<()>1003 pub(crate) fn chmod(path: &CStr, mode: Mode) -> io::Result<()> {
1004     unsafe { ret(c::chmod(c_str(path), mode.bits() as c::mode_t)) }
1005 }
1006 
1007 #[cfg(not(any(
1008     linux_kernel,
1009     target_os = "espidf",
1010     target_os = "redox",
1011     target_os = "wasi"
1012 )))]
chmodat( dirfd: BorrowedFd<'_>, path: &CStr, mode: Mode, flags: AtFlags, ) -> io::Result<()>1013 pub(crate) fn chmodat(
1014     dirfd: BorrowedFd<'_>,
1015     path: &CStr,
1016     mode: Mode,
1017     flags: AtFlags,
1018 ) -> io::Result<()> {
1019     unsafe {
1020         ret(c::fchmodat(
1021             borrowed_fd(dirfd),
1022             c_str(path),
1023             mode.bits() as c::mode_t,
1024             bitflags_bits!(flags),
1025         ))
1026     }
1027 }
1028 
1029 #[cfg(linux_kernel)]
chmodat( dirfd: BorrowedFd<'_>, path: &CStr, mode: Mode, flags: AtFlags, ) -> io::Result<()>1030 pub(crate) fn chmodat(
1031     dirfd: BorrowedFd<'_>,
1032     path: &CStr,
1033     mode: Mode,
1034     flags: AtFlags,
1035 ) -> io::Result<()> {
1036     // Linux's `fchmodat` does not have a flags argument.
1037     //
1038     // Use `c::syscall` rather than `c::fchmodat` because some libc
1039     // implementations, such as musl, add extra logic to `fchmod` to emulate
1040     // support for `AT_SYMLINK_NOFOLLOW`, which uses `/proc` outside our
1041     // control.
1042     syscall! {
1043         fn fchmodat(
1044             base_dirfd: c::c_int,
1045             pathname: *const c::c_char,
1046             mode: c::mode_t
1047         ) via SYS_fchmodat -> c::c_int
1048     }
1049     if flags == AtFlags::SYMLINK_NOFOLLOW {
1050         return Err(io::Errno::OPNOTSUPP);
1051     }
1052     if !flags.is_empty() {
1053         return Err(io::Errno::INVAL);
1054     }
1055     unsafe {
1056         ret(fchmodat(
1057             borrowed_fd(dirfd),
1058             c_str(path),
1059             mode.bits() as c::mode_t,
1060         ))
1061     }
1062 }
1063 
1064 #[cfg(apple)]
fclonefileat( srcfd: BorrowedFd<'_>, dst_dirfd: BorrowedFd<'_>, dst: &CStr, flags: CloneFlags, ) -> io::Result<()>1065 pub(crate) fn fclonefileat(
1066     srcfd: BorrowedFd<'_>,
1067     dst_dirfd: BorrowedFd<'_>,
1068     dst: &CStr,
1069     flags: CloneFlags,
1070 ) -> io::Result<()> {
1071     syscall! {
1072         fn fclonefileat(
1073             srcfd: BorrowedFd<'_>,
1074             dst_dirfd: BorrowedFd<'_>,
1075             dst: *const c::c_char,
1076             flags: c::c_int
1077         ) via SYS_fclonefileat -> c::c_int
1078     }
1079 
1080     unsafe {
1081         ret(fclonefileat(
1082             srcfd,
1083             dst_dirfd,
1084             c_str(dst),
1085             bitflags_bits!(flags),
1086         ))
1087     }
1088 }
1089 
1090 #[cfg(not(any(target_os = "espidf", target_os = "redox", target_os = "wasi")))]
chownat( dirfd: BorrowedFd<'_>, path: &CStr, owner: Option<Uid>, group: Option<Gid>, flags: AtFlags, ) -> io::Result<()>1091 pub(crate) fn chownat(
1092     dirfd: BorrowedFd<'_>,
1093     path: &CStr,
1094     owner: Option<Uid>,
1095     group: Option<Gid>,
1096     flags: AtFlags,
1097 ) -> io::Result<()> {
1098     unsafe {
1099         let (ow, gr) = crate::ugid::translate_fchown_args(owner, group);
1100         ret(c::fchownat(
1101             borrowed_fd(dirfd),
1102             c_str(path),
1103             ow,
1104             gr,
1105             bitflags_bits!(flags),
1106         ))
1107     }
1108 }
1109 
1110 #[cfg(not(any(
1111     apple,
1112     target_os = "espidf",
1113     target_os = "redox",
1114     target_os = "vita",
1115     target_os = "wasi"
1116 )))]
mknodat( dirfd: BorrowedFd<'_>, path: &CStr, file_type: FileType, mode: Mode, dev: Dev, ) -> io::Result<()>1117 pub(crate) fn mknodat(
1118     dirfd: BorrowedFd<'_>,
1119     path: &CStr,
1120     file_type: FileType,
1121     mode: Mode,
1122     dev: Dev,
1123 ) -> io::Result<()> {
1124     unsafe {
1125         ret(c::mknodat(
1126             borrowed_fd(dirfd),
1127             c_str(path),
1128             (mode.bits() | file_type.as_raw_mode()) as c::mode_t,
1129             dev.try_into().map_err(|_e| io::Errno::PERM)?,
1130         ))
1131     }
1132 }
1133 
1134 #[cfg(linux_kernel)]
copy_file_range( fd_in: BorrowedFd<'_>, off_in: Option<&mut u64>, fd_out: BorrowedFd<'_>, off_out: Option<&mut u64>, len: usize, ) -> io::Result<usize>1135 pub(crate) fn copy_file_range(
1136     fd_in: BorrowedFd<'_>,
1137     off_in: Option<&mut u64>,
1138     fd_out: BorrowedFd<'_>,
1139     off_out: Option<&mut u64>,
1140     len: usize,
1141 ) -> io::Result<usize> {
1142     syscall! {
1143         fn copy_file_range(
1144             fd_in: c::c_int,
1145             off_in: *mut c::loff_t,
1146             fd_out: c::c_int,
1147             off_out: *mut c::loff_t,
1148             len: usize,
1149             flags: c::c_uint
1150         ) via SYS_copy_file_range -> c::ssize_t
1151     }
1152 
1153     let mut off_in_val: c::loff_t = 0;
1154     let mut off_out_val: c::loff_t = 0;
1155     // Silently cast; we'll get `EINVAL` if the value is negative.
1156     let off_in_ptr = if let Some(off_in) = &off_in {
1157         off_in_val = **off_in as i64;
1158         &mut off_in_val
1159     } else {
1160         null_mut()
1161     };
1162     let off_out_ptr = if let Some(off_out) = &off_out {
1163         off_out_val = **off_out as i64;
1164         &mut off_out_val
1165     } else {
1166         null_mut()
1167     };
1168     let copied = unsafe {
1169         ret_usize(copy_file_range(
1170             borrowed_fd(fd_in),
1171             off_in_ptr,
1172             borrowed_fd(fd_out),
1173             off_out_ptr,
1174             len,
1175             0, // no flags are defined yet
1176         ))?
1177     };
1178     if let Some(off_in) = off_in {
1179         *off_in = off_in_val as u64;
1180     }
1181     if let Some(off_out) = off_out {
1182         *off_out = off_out_val as u64;
1183     }
1184     Ok(copied)
1185 }
1186 
1187 #[cfg(not(any(
1188     apple,
1189     netbsdlike,
1190     solarish,
1191     target_os = "dragonfly",
1192     target_os = "espidf",
1193     target_os = "haiku",
1194     target_os = "redox",
1195     target_os = "vita",
1196 )))]
fadvise(fd: BorrowedFd<'_>, offset: u64, len: u64, advice: Advice) -> io::Result<()>1197 pub(crate) fn fadvise(fd: BorrowedFd<'_>, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
1198     let offset = offset as i64;
1199     let len = len as i64;
1200 
1201     // FreeBSD returns `EINVAL` on invalid offsets; emulate the POSIX behavior.
1202     #[cfg(target_os = "freebsd")]
1203     let offset = if (offset as i64) < 0 {
1204         i64::MAX
1205     } else {
1206         offset
1207     };
1208 
1209     // FreeBSD returns `EINVAL` on overflow; emulate the POSIX behavior.
1210     #[cfg(target_os = "freebsd")]
1211     let len = if len > 0 && offset.checked_add(len).is_none() {
1212         i64::MAX - offset
1213     } else {
1214         len
1215     };
1216 
1217     let err = unsafe { c::posix_fadvise(borrowed_fd(fd), offset, len, advice as c::c_int) };
1218 
1219     // `posix_fadvise` returns its error status rather than using `errno`.
1220     if err == 0 {
1221         Ok(())
1222     } else {
1223         Err(io::Errno(err))
1224     }
1225 }
1226 
fcntl_getfl(fd: BorrowedFd<'_>) -> io::Result<OFlags>1227 pub(crate) fn fcntl_getfl(fd: BorrowedFd<'_>) -> io::Result<OFlags> {
1228     let flags = unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GETFL))? };
1229     Ok(OFlags::from_bits_retain(bitcast!(flags)))
1230 }
1231 
fcntl_setfl(fd: BorrowedFd<'_>, flags: OFlags) -> io::Result<()>1232 pub(crate) fn fcntl_setfl(fd: BorrowedFd<'_>, flags: OFlags) -> io::Result<()> {
1233     unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_SETFL, flags.bits())) }
1234 }
1235 
1236 #[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))]
fcntl_get_seals(fd: BorrowedFd<'_>) -> io::Result<SealFlags>1237 pub(crate) fn fcntl_get_seals(fd: BorrowedFd<'_>) -> io::Result<SealFlags> {
1238     let flags = unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GET_SEALS))? };
1239     Ok(SealFlags::from_bits_retain(bitcast!(flags)))
1240 }
1241 
1242 #[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))]
fcntl_add_seals(fd: BorrowedFd<'_>, seals: SealFlags) -> io::Result<()>1243 pub(crate) fn fcntl_add_seals(fd: BorrowedFd<'_>, seals: SealFlags) -> io::Result<()> {
1244     unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_ADD_SEALS, seals.bits())) }
1245 }
1246 
1247 #[cfg(not(any(
1248     target_os = "emscripten",
1249     target_os = "espidf",
1250     target_os = "fuchsia",
1251     target_os = "redox",
1252     target_os = "vita",
1253     target_os = "wasi"
1254 )))]
1255 #[inline]
fcntl_lock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()>1256 pub(crate) fn fcntl_lock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> {
1257     use c::{flock, F_RDLCK, F_SETLK, F_SETLKW, F_UNLCK, F_WRLCK, SEEK_SET};
1258 
1259     let (cmd, l_type) = match operation {
1260         FlockOperation::LockShared => (F_SETLKW, F_RDLCK),
1261         FlockOperation::LockExclusive => (F_SETLKW, F_WRLCK),
1262         FlockOperation::Unlock => (F_SETLKW, F_UNLCK),
1263         FlockOperation::NonBlockingLockShared => (F_SETLK, F_RDLCK),
1264         FlockOperation::NonBlockingLockExclusive => (F_SETLK, F_WRLCK),
1265         FlockOperation::NonBlockingUnlock => (F_SETLK, F_UNLCK),
1266     };
1267 
1268     unsafe {
1269         let mut lock: flock = core::mem::zeroed();
1270         lock.l_type = l_type as _;
1271 
1272         // When `l_len` is zero, this locks all the bytes from
1273         // `l_whence`/`l_start` to the end of the file, even as the
1274         // file grows dynamically.
1275         lock.l_whence = SEEK_SET as _;
1276         lock.l_start = 0;
1277         lock.l_len = 0;
1278 
1279         ret(c::fcntl(borrowed_fd(fd), cmd, &lock))
1280     }
1281 }
1282 
seek(fd: BorrowedFd<'_>, pos: SeekFrom) -> io::Result<u64>1283 pub(crate) fn seek(fd: BorrowedFd<'_>, pos: SeekFrom) -> io::Result<u64> {
1284     let (whence, offset) = match pos {
1285         SeekFrom::Start(pos) => {
1286             let pos: u64 = pos;
1287             // Silently cast; we'll get `EINVAL` if the value is negative.
1288             (c::SEEK_SET, pos as i64)
1289         }
1290         SeekFrom::End(offset) => (c::SEEK_END, offset),
1291         SeekFrom::Current(offset) => (c::SEEK_CUR, offset),
1292         #[cfg(any(apple, freebsdlike, linux_kernel, solarish))]
1293         SeekFrom::Data(offset) => (c::SEEK_DATA, offset),
1294         #[cfg(any(apple, freebsdlike, linux_kernel, solarish))]
1295         SeekFrom::Hole(offset) => (c::SEEK_HOLE, offset),
1296     };
1297 
1298     // ESP-IDF and Vita don't support 64-bit offsets.
1299     #[cfg(any(target_os = "espidf", target_os = "vita"))]
1300     let offset: i32 = offset.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1301 
1302     let offset = unsafe { ret_off_t(c::lseek(borrowed_fd(fd), offset, whence))? };
1303     Ok(offset as u64)
1304 }
1305 
tell(fd: BorrowedFd<'_>) -> io::Result<u64>1306 pub(crate) fn tell(fd: BorrowedFd<'_>) -> io::Result<u64> {
1307     let offset = unsafe { ret_off_t(c::lseek(borrowed_fd(fd), 0, c::SEEK_CUR))? };
1308     Ok(offset as u64)
1309 }
1310 
1311 #[cfg(not(any(linux_kernel, target_os = "wasi")))]
fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()>1312 pub(crate) fn fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()> {
1313     unsafe { ret(c::fchmod(borrowed_fd(fd), bitflags_bits!(mode))) }
1314 }
1315 
1316 #[cfg(linux_kernel)]
fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()>1317 pub(crate) fn fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()> {
1318     // Use `c::syscall` rather than `c::fchmod` because some libc
1319     // implementations, such as musl, add extra logic to `fchmod` to emulate
1320     // support for `O_PATH`, which uses `/proc` outside our control and
1321     // interferes with our own use of `O_PATH`.
1322     syscall! {
1323         fn fchmod(
1324             fd: c::c_int,
1325             mode: c::mode_t
1326         ) via SYS_fchmod -> c::c_int
1327     }
1328     unsafe { ret(fchmod(borrowed_fd(fd), mode.bits() as c::mode_t)) }
1329 }
1330 
1331 #[cfg(not(target_os = "wasi"))]
chown(path: &CStr, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()>1332 pub(crate) fn chown(path: &CStr, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> {
1333     unsafe {
1334         let (ow, gr) = crate::ugid::translate_fchown_args(owner, group);
1335         ret(c::chown(c_str(path), ow, gr))
1336     }
1337 }
1338 
1339 #[cfg(linux_kernel)]
fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()>1340 pub(crate) fn fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> {
1341     // Use `c::syscall` rather than `c::fchown` because some libc
1342     // implementations, such as musl, add extra logic to `fchown` to emulate
1343     // support for `O_PATH`, which uses `/proc` outside our control and
1344     // interferes with our own use of `O_PATH`.
1345     syscall! {
1346         fn fchown(
1347             fd: c::c_int,
1348             owner: c::uid_t,
1349             group: c::gid_t
1350         ) via SYS_fchown -> c::c_int
1351     }
1352     unsafe {
1353         let (ow, gr) = crate::ugid::translate_fchown_args(owner, group);
1354         ret(fchown(borrowed_fd(fd), ow, gr))
1355     }
1356 }
1357 
1358 #[cfg(not(any(linux_kernel, target_os = "wasi")))]
fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()>1359 pub(crate) fn fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> {
1360     unsafe {
1361         let (ow, gr) = crate::ugid::translate_fchown_args(owner, group);
1362         ret(c::fchown(borrowed_fd(fd), ow, gr))
1363     }
1364 }
1365 
1366 #[cfg(not(any(
1367     target_os = "espidf",
1368     target_os = "solaris",
1369     target_os = "vita",
1370     target_os = "wasi"
1371 )))]
flock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()>1372 pub(crate) fn flock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> {
1373     unsafe { ret(c::flock(borrowed_fd(fd), operation as c::c_int)) }
1374 }
1375 
1376 #[cfg(linux_kernel)]
syncfs(fd: BorrowedFd<'_>) -> io::Result<()>1377 pub(crate) fn syncfs(fd: BorrowedFd<'_>) -> io::Result<()> {
1378     // Some versions of Android libc lack a `syncfs` function.
1379     #[cfg(target_os = "android")]
1380     syscall! {
1381         fn syncfs(fd: c::c_int) via SYS_syncfs -> c::c_int
1382     }
1383 
1384     // `syncfs` was added to glibc in 2.20.
1385     #[cfg(not(target_os = "android"))]
1386     weak_or_syscall! {
1387         fn syncfs(fd: c::c_int) via SYS_syncfs -> c::c_int
1388     }
1389 
1390     unsafe { ret(syncfs(borrowed_fd(fd))) }
1391 }
1392 
1393 #[cfg(not(any(
1394     target_os = "espidf",
1395     target_os = "redox",
1396     target_os = "vita",
1397     target_os = "wasi"
1398 )))]
sync()1399 pub(crate) fn sync() {
1400     unsafe { c::sync() }
1401 }
1402 
fstat(fd: BorrowedFd<'_>) -> io::Result<Stat>1403 pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result<Stat> {
1404     // 32-bit and mips64 Linux: `struct stat64` is not y2038 compatible; use
1405     // `statx`.
1406     //
1407     // And, some old platforms don't support `statx`, and some fail with a
1408     // confusing error code, so we call `crate::fs::statx` to handle that. If
1409     // `statx` isn't available, fall back to the buggy system call.
1410     #[cfg(all(
1411         linux_kernel,
1412         any(
1413             target_pointer_width = "32",
1414             target_arch = "mips64",
1415             target_arch = "mips64r6"
1416         )
1417     ))]
1418     {
1419         match crate::fs::statx(fd, cstr!(""), AtFlags::EMPTY_PATH, StatxFlags::BASIC_STATS) {
1420             Ok(x) => statx_to_stat(x),
1421             Err(io::Errno::NOSYS) => fstat_old(fd),
1422             Err(err) => Err(err),
1423         }
1424     }
1425 
1426     // Main version: libc is y2038 safe. Or, the platform is not y2038 safe and
1427     // there's nothing practical we can do.
1428     #[cfg(not(all(
1429         linux_kernel,
1430         any(
1431             target_pointer_width = "32",
1432             target_arch = "mips64",
1433             target_arch = "mips64r6"
1434         )
1435     )))]
1436     unsafe {
1437         let mut stat = MaybeUninit::<Stat>::uninit();
1438         ret(c::fstat(borrowed_fd(fd), stat.as_mut_ptr()))?;
1439         Ok(stat.assume_init())
1440     }
1441 }
1442 
1443 #[cfg(all(
1444     linux_kernel,
1445     any(
1446         target_pointer_width = "32",
1447         target_arch = "mips64",
1448         target_arch = "mips64r6"
1449     )
1450 ))]
fstat_old(fd: BorrowedFd<'_>) -> io::Result<Stat>1451 fn fstat_old(fd: BorrowedFd<'_>) -> io::Result<Stat> {
1452     unsafe {
1453         let mut result = MaybeUninit::<c::stat64>::uninit();
1454         ret(c::fstat(borrowed_fd(fd), result.as_mut_ptr()))?;
1455         stat64_to_stat(result.assume_init())
1456     }
1457 }
1458 
1459 #[cfg(not(any(
1460     solarish,
1461     target_os = "espidf",
1462     target_os = "haiku",
1463     target_os = "netbsd",
1464     target_os = "nto",
1465     target_os = "redox",
1466     target_os = "vita",
1467     target_os = "wasi",
1468 )))]
fstatfs(fd: BorrowedFd<'_>) -> io::Result<StatFs>1469 pub(crate) fn fstatfs(fd: BorrowedFd<'_>) -> io::Result<StatFs> {
1470     let mut statfs = MaybeUninit::<StatFs>::uninit();
1471     unsafe {
1472         ret(c::fstatfs(borrowed_fd(fd), statfs.as_mut_ptr()))?;
1473         Ok(statfs.assume_init())
1474     }
1475 }
1476 
1477 #[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))]
fstatvfs(fd: BorrowedFd<'_>) -> io::Result<StatVfs>1478 pub(crate) fn fstatvfs(fd: BorrowedFd<'_>) -> io::Result<StatVfs> {
1479     let mut statvfs = MaybeUninit::<c::statvfs>::uninit();
1480     unsafe {
1481         ret(c::fstatvfs(borrowed_fd(fd), statvfs.as_mut_ptr()))?;
1482         Ok(libc_statvfs_to_statvfs(statvfs.assume_init()))
1483     }
1484 }
1485 
1486 #[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))]
libc_statvfs_to_statvfs(from: c::statvfs) -> StatVfs1487 fn libc_statvfs_to_statvfs(from: c::statvfs) -> StatVfs {
1488     StatVfs {
1489         f_bsize: from.f_bsize as u64,
1490         f_frsize: from.f_frsize as u64,
1491         f_blocks: from.f_blocks as u64,
1492         f_bfree: from.f_bfree as u64,
1493         f_bavail: from.f_bavail as u64,
1494         f_files: from.f_files as u64,
1495         f_ffree: from.f_ffree as u64,
1496         f_favail: from.f_ffree as u64,
1497         #[cfg(not(target_os = "aix"))]
1498         f_fsid: from.f_fsid as u64,
1499         #[cfg(target_os = "aix")]
1500         f_fsid: ((from.f_fsid.val[0] as u64) << 32) | from.f_fsid.val[1],
1501         f_flag: StatVfsMountFlags::from_bits_retain(from.f_flag as u64),
1502         f_namemax: from.f_namemax as u64,
1503     }
1504 }
1505 
1506 #[cfg(not(any(target_os = "espidf", target_os = "vita")))]
futimens(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()>1507 pub(crate) fn futimens(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()> {
1508     // Old 32-bit version: libc has `futimens` but it is not y2038 safe by
1509     // default. But there may be a `__futimens64` we can use.
1510     #[cfg(all(fix_y2038, not(apple)))]
1511     {
1512         #[cfg(target_env = "gnu")]
1513         if let Some(libc_futimens) = __futimens64.get() {
1514             let libc_times: [LibcTimespec; 2] = [
1515                 times.last_access.clone().into(),
1516                 times.last_modification.clone().into(),
1517             ];
1518 
1519             unsafe {
1520                 return ret(libc_futimens(borrowed_fd(fd), libc_times.as_ptr()));
1521             }
1522         }
1523 
1524         futimens_old(fd, times)
1525     }
1526 
1527     // Main version: libc is y2038 safe and has `futimens`. Or, the platform
1528     // is not y2038 safe and there's nothing practical we can do.
1529     #[cfg(not(any(apple, fix_y2038)))]
1530     unsafe {
1531         use crate::utils::as_ptr;
1532 
1533         ret(c::futimens(borrowed_fd(fd), as_ptr(times).cast()))
1534     }
1535 
1536     // Apple version: `futimens` was introduced in macOS 10.13.
1537     #[cfg(apple)]
1538     unsafe {
1539         use crate::utils::as_ptr;
1540 
1541         // ABI details.
1542         weak! {
1543             fn futimens(c::c_int, *const c::timespec) -> c::c_int
1544         }
1545         extern "C" {
1546             fn fsetattrlist(
1547                 fd: c::c_int,
1548                 attr_list: *const Attrlist,
1549                 attr_buf: *const c::c_void,
1550                 attr_buf_size: c::size_t,
1551                 options: c::c_ulong,
1552             ) -> c::c_int;
1553         }
1554 
1555         // If we have `futimens`, use it.
1556         if let Some(have_futimens) = futimens.get() {
1557             return ret(have_futimens(borrowed_fd(fd), as_ptr(times).cast()));
1558         }
1559 
1560         // Otherwise use `fsetattrlist`.
1561         let (attrbuf_size, times, attrs) = times_to_attrlist(times)?;
1562 
1563         ret(fsetattrlist(
1564             borrowed_fd(fd),
1565             &attrs,
1566             as_ptr(&times).cast(),
1567             attrbuf_size,
1568             0,
1569         ))
1570     }
1571 }
1572 
1573 #[cfg(all(fix_y2038, not(apple)))]
futimens_old(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()>1574 fn futimens_old(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()> {
1575     let old_times = [
1576         c::timespec {
1577             tv_sec: times
1578                 .last_access
1579                 .tv_sec
1580                 .try_into()
1581                 .map_err(|_| io::Errno::OVERFLOW)?,
1582             tv_nsec: times
1583                 .last_access
1584                 .tv_nsec
1585                 .try_into()
1586                 .map_err(|_| io::Errno::OVERFLOW)?,
1587         },
1588         c::timespec {
1589             tv_sec: times
1590                 .last_modification
1591                 .tv_sec
1592                 .try_into()
1593                 .map_err(|_| io::Errno::OVERFLOW)?,
1594             tv_nsec: times
1595                 .last_modification
1596                 .tv_nsec
1597                 .try_into()
1598                 .map_err(|_| io::Errno::OVERFLOW)?,
1599         },
1600     ];
1601 
1602     unsafe { ret(c::futimens(borrowed_fd(fd), old_times.as_ptr())) }
1603 }
1604 
1605 #[cfg(not(any(
1606     apple,
1607     netbsdlike,
1608     solarish,
1609     target_os = "aix",
1610     target_os = "dragonfly",
1611     target_os = "espidf",
1612     target_os = "nto",
1613     target_os = "redox",
1614     target_os = "vita",
1615 )))]
fallocate( fd: BorrowedFd<'_>, mode: FallocateFlags, offset: u64, len: u64, ) -> io::Result<()>1616 pub(crate) fn fallocate(
1617     fd: BorrowedFd<'_>,
1618     mode: FallocateFlags,
1619     offset: u64,
1620     len: u64,
1621 ) -> io::Result<()> {
1622     // Silently cast; we'll get `EINVAL` if the value is negative.
1623     let offset = offset as i64;
1624     let len = len as i64;
1625 
1626     #[cfg(any(linux_kernel, target_os = "fuchsia"))]
1627     unsafe {
1628         ret(c::fallocate(
1629             borrowed_fd(fd),
1630             bitflags_bits!(mode),
1631             offset,
1632             len,
1633         ))
1634     }
1635 
1636     #[cfg(not(any(linux_kernel, target_os = "fuchsia")))]
1637     {
1638         assert!(mode.is_empty());
1639         let err = unsafe { c::posix_fallocate(borrowed_fd(fd), offset, len) };
1640 
1641         // `posix_fallocate` returns its error status rather than using
1642         // `errno`.
1643         if err == 0 {
1644             Ok(())
1645         } else {
1646             Err(io::Errno(err))
1647         }
1648     }
1649 }
1650 
1651 #[cfg(apple)]
fallocate( fd: BorrowedFd<'_>, mode: FallocateFlags, offset: u64, len: u64, ) -> io::Result<()>1652 pub(crate) fn fallocate(
1653     fd: BorrowedFd<'_>,
1654     mode: FallocateFlags,
1655     offset: u64,
1656     len: u64,
1657 ) -> io::Result<()> {
1658     let offset: i64 = offset.try_into().map_err(|_e| io::Errno::INVAL)?;
1659     let len = len as i64;
1660 
1661     assert!(mode.is_empty());
1662 
1663     let new_len = offset.checked_add(len).ok_or(io::Errno::FBIG)?;
1664     let mut store = c::fstore_t {
1665         fst_flags: c::F_ALLOCATECONTIG,
1666         fst_posmode: c::F_PEOFPOSMODE,
1667         fst_offset: 0,
1668         fst_length: new_len,
1669         fst_bytesalloc: 0,
1670     };
1671     unsafe {
1672         if c::fcntl(borrowed_fd(fd), c::F_PREALLOCATE, &store) == -1 {
1673             // Unable to allocate contiguous disk space; attempt to allocate
1674             // non-contiguously.
1675             store.fst_flags = c::F_ALLOCATEALL;
1676             let _ = ret_c_int(c::fcntl(borrowed_fd(fd), c::F_PREALLOCATE, &store))?;
1677         }
1678         ret(c::ftruncate(borrowed_fd(fd), new_len))
1679     }
1680 }
1681 
fsync(fd: BorrowedFd<'_>) -> io::Result<()>1682 pub(crate) fn fsync(fd: BorrowedFd<'_>) -> io::Result<()> {
1683     unsafe { ret(c::fsync(borrowed_fd(fd))) }
1684 }
1685 
1686 #[cfg(not(any(
1687     apple,
1688     target_os = "dragonfly",
1689     target_os = "espidf",
1690     target_os = "haiku",
1691     target_os = "redox",
1692     target_os = "vita",
1693 )))]
fdatasync(fd: BorrowedFd<'_>) -> io::Result<()>1694 pub(crate) fn fdatasync(fd: BorrowedFd<'_>) -> io::Result<()> {
1695     unsafe { ret(c::fdatasync(borrowed_fd(fd))) }
1696 }
1697 
ftruncate(fd: BorrowedFd<'_>, length: u64) -> io::Result<()>1698 pub(crate) fn ftruncate(fd: BorrowedFd<'_>, length: u64) -> io::Result<()> {
1699     let length = length.try_into().map_err(|_overflow_err| io::Errno::FBIG)?;
1700     unsafe { ret(c::ftruncate(borrowed_fd(fd), length)) }
1701 }
1702 
1703 #[cfg(any(linux_kernel, target_os = "freebsd"))]
memfd_create(name: &CStr, flags: MemfdFlags) -> io::Result<OwnedFd>1704 pub(crate) fn memfd_create(name: &CStr, flags: MemfdFlags) -> io::Result<OwnedFd> {
1705     #[cfg(target_os = "freebsd")]
1706     weakcall! {
1707         fn memfd_create(
1708             name: *const c::c_char,
1709             flags: c::c_uint
1710         ) -> c::c_int
1711     }
1712 
1713     #[cfg(linux_kernel)]
1714     weak_or_syscall! {
1715         fn memfd_create(
1716             name: *const c::c_char,
1717             flags: c::c_uint
1718         ) via SYS_memfd_create -> c::c_int
1719     }
1720 
1721     unsafe { ret_owned_fd(memfd_create(c_str(name), bitflags_bits!(flags))) }
1722 }
1723 
1724 #[cfg(feature = "linux-raw-sys")]
openat2( dirfd: BorrowedFd<'_>, path: &CStr, oflags: OFlags, mode: Mode, resolve: ResolveFlags, ) -> io::Result<OwnedFd>1725 pub(crate) fn openat2(
1726     dirfd: BorrowedFd<'_>,
1727     path: &CStr,
1728     oflags: OFlags,
1729     mode: Mode,
1730     resolve: ResolveFlags,
1731 ) -> io::Result<OwnedFd> {
1732     use linux_raw_sys::general::open_how;
1733 
1734     syscall! {
1735         fn openat2(
1736             base_dirfd: c::c_int,
1737             pathname: *const c::c_char,
1738             how: *mut open_how,
1739             size: usize
1740         ) via SYS_OPENAT2 -> c::c_int
1741     }
1742 
1743     let oflags = oflags.bits();
1744     let mut open_how = open_how {
1745         flags: u64::from(oflags),
1746         mode: u64::from(mode.bits()),
1747         resolve: resolve.bits(),
1748     };
1749 
1750     unsafe {
1751         ret_owned_fd(openat2(
1752             borrowed_fd(dirfd),
1753             c_str(path),
1754             &mut open_how,
1755             size_of::<open_how>(),
1756         ))
1757     }
1758 }
1759 #[cfg(all(linux_kernel, target_pointer_width = "32"))]
1760 const SYS_OPENAT2: i32 = 437;
1761 #[cfg(all(linux_kernel, target_pointer_width = "64"))]
1762 const SYS_OPENAT2: i64 = 437;
1763 
1764 #[cfg(target_os = "linux")]
sendfile( out_fd: BorrowedFd<'_>, in_fd: BorrowedFd<'_>, offset: Option<&mut u64>, count: usize, ) -> io::Result<usize>1765 pub(crate) fn sendfile(
1766     out_fd: BorrowedFd<'_>,
1767     in_fd: BorrowedFd<'_>,
1768     offset: Option<&mut u64>,
1769     count: usize,
1770 ) -> io::Result<usize> {
1771     unsafe {
1772         ret_usize(c::sendfile64(
1773             borrowed_fd(out_fd),
1774             borrowed_fd(in_fd),
1775             offset.map_or(null_mut(), crate::utils::as_mut_ptr).cast(),
1776             count,
1777         ))
1778     }
1779 }
1780 
1781 /// Convert from a Linux `statx` value to rustix's `Stat`.
1782 #[cfg(all(linux_kernel, target_pointer_width = "32"))]
1783 #[allow(deprecated)] // for `st_[amc]time` u64->i64 transition
statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat>1784 fn statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat> {
1785     Ok(Stat {
1786         st_dev: crate::fs::makedev(x.stx_dev_major, x.stx_dev_minor).into(),
1787         st_mode: x.stx_mode.into(),
1788         st_nlink: x.stx_nlink.into(),
1789         st_uid: x.stx_uid.into(),
1790         st_gid: x.stx_gid.into(),
1791         st_rdev: crate::fs::makedev(x.stx_rdev_major, x.stx_rdev_minor).into(),
1792         st_size: x.stx_size.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1793         st_blksize: x.stx_blksize.into(),
1794         st_blocks: x.stx_blocks.into(),
1795         st_atime: bitcast!(i64::from(x.stx_atime.tv_sec)),
1796         st_atime_nsec: x.stx_atime.tv_nsec as _,
1797         st_mtime: bitcast!(i64::from(x.stx_mtime.tv_sec)),
1798         st_mtime_nsec: x.stx_mtime.tv_nsec as _,
1799         st_ctime: bitcast!(i64::from(x.stx_ctime.tv_sec)),
1800         st_ctime_nsec: x.stx_ctime.tv_nsec as _,
1801         st_ino: x.stx_ino.into(),
1802     })
1803 }
1804 
1805 /// Convert from a Linux `statx` value to rustix's `Stat`.
1806 ///
1807 /// mips64' `struct stat64` in libc has private fields, and `stx_blocks`
1808 #[cfg(all(linux_kernel, any(target_arch = "mips64", target_arch = "mips64r6")))]
statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat>1809 fn statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat> {
1810     let mut result: Stat = unsafe { core::mem::zeroed() };
1811 
1812     result.st_dev = crate::fs::makedev(x.stx_dev_major, x.stx_dev_minor);
1813     result.st_mode = x.stx_mode.into();
1814     result.st_nlink = x.stx_nlink.into();
1815     result.st_uid = x.stx_uid.into();
1816     result.st_gid = x.stx_gid.into();
1817     result.st_rdev = crate::fs::makedev(x.stx_rdev_major, x.stx_rdev_minor);
1818     result.st_size = x.stx_size.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1819     result.st_blksize = x.stx_blksize.into();
1820     result.st_blocks = x.stx_blocks.try_into().map_err(|_e| io::Errno::OVERFLOW)?;
1821     result.st_atime = bitcast!(i64::from(x.stx_atime.tv_sec));
1822     result.st_atime_nsec = x.stx_atime.tv_nsec as _;
1823     result.st_mtime = bitcast!(i64::from(x.stx_mtime.tv_sec));
1824     result.st_mtime_nsec = x.stx_mtime.tv_nsec as _;
1825     result.st_ctime = bitcast!(i64::from(x.stx_ctime.tv_sec));
1826     result.st_ctime_nsec = x.stx_ctime.tv_nsec as _;
1827     result.st_ino = x.stx_ino.into();
1828 
1829     Ok(result)
1830 }
1831 
1832 /// Convert from a Linux `stat64` value to rustix's `Stat`.
1833 #[cfg(all(linux_kernel, target_pointer_width = "32"))]
1834 #[allow(deprecated)] // for `st_[amc]time` u64->i64 transition
stat64_to_stat(s64: c::stat64) -> io::Result<Stat>1835 fn stat64_to_stat(s64: c::stat64) -> io::Result<Stat> {
1836     Ok(Stat {
1837         st_dev: s64.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1838         st_mode: s64.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1839         st_nlink: s64.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1840         st_uid: s64.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1841         st_gid: s64.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1842         st_rdev: s64.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1843         st_size: s64.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1844         st_blksize: s64.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1845         st_blocks: s64.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1846         st_atime: bitcast!(i64::from(s64.st_atime)),
1847         st_atime_nsec: s64
1848             .st_atime_nsec
1849             .try_into()
1850             .map_err(|_| io::Errno::OVERFLOW)?,
1851         st_mtime: bitcast!(i64::from(s64.st_mtime)),
1852         st_mtime_nsec: s64
1853             .st_mtime_nsec
1854             .try_into()
1855             .map_err(|_| io::Errno::OVERFLOW)?,
1856         st_ctime: bitcast!(i64::from(s64.st_ctime)),
1857         st_ctime_nsec: s64
1858             .st_ctime_nsec
1859             .try_into()
1860             .map_err(|_| io::Errno::OVERFLOW)?,
1861         st_ino: s64.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1862     })
1863 }
1864 
1865 /// Convert from a Linux `stat64` value to rustix's `Stat`.
1866 ///
1867 /// mips64' `struct stat64` in libc has private fields, and `st_blocks` has
1868 /// type `i64`.
1869 #[cfg(all(linux_kernel, any(target_arch = "mips64", target_arch = "mips64r6")))]
stat64_to_stat(s64: c::stat64) -> io::Result<Stat>1870 fn stat64_to_stat(s64: c::stat64) -> io::Result<Stat> {
1871     let mut result: Stat = unsafe { core::mem::zeroed() };
1872 
1873     result.st_dev = s64.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1874     result.st_mode = s64.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1875     result.st_nlink = s64.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1876     result.st_uid = s64.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1877     result.st_gid = s64.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1878     result.st_rdev = s64.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1879     result.st_size = s64.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1880     result.st_blksize = s64.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1881     result.st_blocks = s64.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1882     result.st_atime = i64::from(s64.st_atime) as _;
1883     result.st_atime_nsec = s64
1884         .st_atime_nsec
1885         .try_into()
1886         .map_err(|_| io::Errno::OVERFLOW)?;
1887     result.st_mtime = i64::from(s64.st_mtime) as _;
1888     result.st_mtime_nsec = s64
1889         .st_mtime_nsec
1890         .try_into()
1891         .map_err(|_| io::Errno::OVERFLOW)?;
1892     result.st_ctime = i64::from(s64.st_ctime) as _;
1893     result.st_ctime_nsec = s64
1894         .st_ctime_nsec
1895         .try_into()
1896         .map_err(|_| io::Errno::OVERFLOW)?;
1897     result.st_ino = s64.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1898 
1899     Ok(result)
1900 }
1901 
1902 #[cfg(linux_kernel)]
1903 #[allow(non_upper_case_globals)]
1904 mod sys {
1905     use super::{c, BorrowedFd, Statx};
1906 
1907     weak_or_syscall! {
1908         pub(super) fn statx(
1909             dirfd_: BorrowedFd<'_>,
1910             path: *const c::c_char,
1911             flags: c::c_int,
1912             mask: c::c_uint,
1913             buf: *mut Statx
1914         ) via SYS_statx -> c::c_int
1915     }
1916 }
1917 
1918 #[cfg(linux_kernel)]
1919 #[allow(non_upper_case_globals)]
statx( dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags, mask: StatxFlags, ) -> io::Result<Statx>1920 pub(crate) fn statx(
1921     dirfd: BorrowedFd<'_>,
1922     path: &CStr,
1923     flags: AtFlags,
1924     mask: StatxFlags,
1925 ) -> io::Result<Statx> {
1926     // If a future Linux kernel adds more fields to `struct statx` and users
1927     // passing flags unknown to rustix in `StatxFlags`, we could end up
1928     // writing outside of the buffer. To prevent this possibility, we mask off
1929     // any flags that we don't know about.
1930     //
1931     // This includes `STATX__RESERVED`, which has a value that we know, but
1932     // which could take on arbitrary new meaning in the future. Linux currently
1933     // rejects this flag with `EINVAL`, so we do the same.
1934     //
1935     // This doesn't rely on `STATX_ALL` because [it's deprecated] and already
1936     // doesn't represent all the known flags.
1937     //
1938     // [it's deprecated]: https://patchwork.kernel.org/project/linux-fsdevel/patch/[email protected]/
1939     #[cfg(not(any(target_os = "android", target_env = "musl")))]
1940     const STATX__RESERVED: u32 = c::STATX__RESERVED as u32;
1941     #[cfg(all(
1942         any(target_os = "android", target_env = "musl"),
1943         feature = "linux-raw-sys",
1944     ))]
1945     const STATX__RESERVED: u32 = linux_raw_sys::general::STATX__RESERVED;
1946     #[cfg(any(
1947         not(any(target_os = "android", target_env = "musl")),
1948         feature = "linux-raw-sys",
1949     ))]
1950     if (mask.bits() & STATX__RESERVED) == STATX__RESERVED {
1951         return Err(io::Errno::INVAL);
1952     }
1953     let mask = mask & StatxFlags::all();
1954 
1955     let mut statx_buf = MaybeUninit::<Statx>::uninit();
1956     unsafe {
1957         ret(sys::statx(
1958             dirfd,
1959             c_str(path),
1960             bitflags_bits!(flags),
1961             mask.bits(),
1962             statx_buf.as_mut_ptr(),
1963         ))?;
1964         Ok(statx_buf.assume_init())
1965     }
1966 }
1967 
1968 #[cfg(linux_kernel)]
1969 #[inline]
is_statx_available() -> bool1970 pub(crate) fn is_statx_available() -> bool {
1971     unsafe {
1972         // Call `statx` with null pointers so that if it fails for any reason
1973         // other than `EFAULT`, we know it's not supported.
1974         matches!(
1975             ret(sys::statx(CWD, null(), 0, 0, null_mut())),
1976             Err(io::Errno::FAULT)
1977         )
1978     }
1979 }
1980 
1981 #[cfg(apple)]
fcopyfile( from: BorrowedFd<'_>, to: BorrowedFd<'_>, state: copyfile_state_t, flags: CopyfileFlags, ) -> io::Result<()>1982 pub(crate) unsafe fn fcopyfile(
1983     from: BorrowedFd<'_>,
1984     to: BorrowedFd<'_>,
1985     state: copyfile_state_t,
1986     flags: CopyfileFlags,
1987 ) -> io::Result<()> {
1988     extern "C" {
1989         fn fcopyfile(
1990             from: c::c_int,
1991             to: c::c_int,
1992             state: copyfile_state_t,
1993             flags: c::c_uint,
1994         ) -> c::c_int;
1995     }
1996 
1997     nonnegative_ret(fcopyfile(
1998         borrowed_fd(from),
1999         borrowed_fd(to),
2000         state,
2001         bitflags_bits!(flags),
2002     ))
2003 }
2004 
2005 #[cfg(apple)]
copyfile_state_alloc() -> io::Result<copyfile_state_t>2006 pub(crate) fn copyfile_state_alloc() -> io::Result<copyfile_state_t> {
2007     extern "C" {
2008         fn copyfile_state_alloc() -> copyfile_state_t;
2009     }
2010 
2011     let result = unsafe { copyfile_state_alloc() };
2012     if result.0.is_null() {
2013         Err(io::Errno::last_os_error())
2014     } else {
2015         Ok(result)
2016     }
2017 }
2018 
2019 #[cfg(apple)]
copyfile_state_free(state: copyfile_state_t) -> io::Result<()>2020 pub(crate) unsafe fn copyfile_state_free(state: copyfile_state_t) -> io::Result<()> {
2021     extern "C" {
2022         fn copyfile_state_free(state: copyfile_state_t) -> c::c_int;
2023     }
2024 
2025     nonnegative_ret(copyfile_state_free(state))
2026 }
2027 
2028 #[cfg(apple)]
2029 const COPYFILE_STATE_COPIED: u32 = 8;
2030 
2031 #[cfg(apple)]
copyfile_state_get_copied(state: copyfile_state_t) -> io::Result<u64>2032 pub(crate) unsafe fn copyfile_state_get_copied(state: copyfile_state_t) -> io::Result<u64> {
2033     let mut copied = MaybeUninit::<u64>::uninit();
2034     copyfile_state_get(state, COPYFILE_STATE_COPIED, copied.as_mut_ptr().cast())?;
2035     Ok(copied.assume_init())
2036 }
2037 
2038 #[cfg(apple)]
copyfile_state_get( state: copyfile_state_t, flag: u32, dst: *mut c::c_void, ) -> io::Result<()>2039 pub(crate) unsafe fn copyfile_state_get(
2040     state: copyfile_state_t,
2041     flag: u32,
2042     dst: *mut c::c_void,
2043 ) -> io::Result<()> {
2044     extern "C" {
2045         fn copyfile_state_get(state: copyfile_state_t, flag: u32, dst: *mut c::c_void) -> c::c_int;
2046     }
2047 
2048     nonnegative_ret(copyfile_state_get(state, flag, dst))
2049 }
2050 
2051 #[cfg(all(apple, feature = "alloc"))]
getpath(fd: BorrowedFd<'_>) -> io::Result<CString>2052 pub(crate) fn getpath(fd: BorrowedFd<'_>) -> io::Result<CString> {
2053     // The use of `PATH_MAX` is generally not encouraged, but it
2054     // is inevitable in this case because macOS defines `fcntl` with
2055     // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
2056     // alternatives. If a better method is invented, it should be used
2057     // instead.
2058     let mut buf = vec![0; c::PATH_MAX as usize];
2059 
2060     // From the [macOS `fcntl` manual page]:
2061     // `F_GETPATH` - Get the path of the file descriptor `Fildes`. The argument
2062     //               must be a buffer of size `MAXPATHLEN` or greater.
2063     //
2064     // [macOS `fcntl` manual page]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html
2065     unsafe {
2066         ret(c::fcntl(borrowed_fd(fd), c::F_GETPATH, buf.as_mut_ptr()))?;
2067     }
2068 
2069     let l = buf.iter().position(|&c| c == 0).unwrap();
2070     buf.truncate(l);
2071     buf.shrink_to_fit();
2072 
2073     Ok(CString::new(buf).unwrap())
2074 }
2075 
2076 #[cfg(apple)]
fcntl_rdadvise(fd: BorrowedFd<'_>, offset: u64, len: u64) -> io::Result<()>2077 pub(crate) fn fcntl_rdadvise(fd: BorrowedFd<'_>, offset: u64, len: u64) -> io::Result<()> {
2078     // From the [macOS `fcntl` manual page]:
2079     // `F_RDADVISE` - Issue an advisory read async with no copy to user.
2080     //
2081     // The `F_RDADVISE` command operates on the following structure which holds
2082     // information passed from the user to the system:
2083     //
2084     // ```c
2085     // struct radvisory {
2086     //      off_t   ra_offset;  /* offset into the file */
2087     //      int     ra_count;   /* size of the read     */
2088     // };
2089     // ```
2090     //
2091     // [macOS `fcntl` manual page]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html
2092     let ra_offset = match offset.try_into() {
2093         Ok(len) => len,
2094         // If this conversion fails, the user is providing an offset outside
2095         // any possible file extent, so just ignore it.
2096         Err(_) => return Ok(()),
2097     };
2098     let ra_count = match len.try_into() {
2099         Ok(len) => len,
2100         // If this conversion fails, the user is providing a dubiously large
2101         // hint which is unlikely to improve performance.
2102         Err(_) => return Ok(()),
2103     };
2104     unsafe {
2105         let radvisory = c::radvisory {
2106             ra_offset,
2107             ra_count,
2108         };
2109         ret(c::fcntl(borrowed_fd(fd), c::F_RDADVISE, &radvisory))
2110     }
2111 }
2112 
2113 #[cfg(apple)]
fcntl_fullfsync(fd: BorrowedFd<'_>) -> io::Result<()>2114 pub(crate) fn fcntl_fullfsync(fd: BorrowedFd<'_>) -> io::Result<()> {
2115     unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_FULLFSYNC)) }
2116 }
2117 
2118 #[cfg(apple)]
fcntl_nocache(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>2119 pub(crate) fn fcntl_nocache(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
2120     unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_NOCACHE, value as c::c_int)) }
2121 }
2122 
2123 #[cfg(apple)]
fcntl_global_nocache(fd: BorrowedFd<'_>, value: bool) -> io::Result<()>2124 pub(crate) fn fcntl_global_nocache(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> {
2125     unsafe {
2126         ret(c::fcntl(
2127             borrowed_fd(fd),
2128             c::F_GLOBAL_NOCACHE,
2129             value as c::c_int,
2130         ))
2131     }
2132 }
2133 
2134 /// Convert `times` from a `futimens`/`utimensat` argument into `setattrlist`
2135 /// arguments.
2136 #[cfg(apple)]
times_to_attrlist(times: &Timestamps) -> io::Result<(c::size_t, [c::timespec; 2], Attrlist)>2137 fn times_to_attrlist(times: &Timestamps) -> io::Result<(c::size_t, [c::timespec; 2], Attrlist)> {
2138     // ABI details.
2139     const ATTR_CMN_MODTIME: u32 = 0x0000_0400;
2140     const ATTR_CMN_ACCTIME: u32 = 0x0000_1000;
2141     const ATTR_BIT_MAP_COUNT: u16 = 5;
2142 
2143     let mut times = times.clone();
2144 
2145     // If we have any `UTIME_NOW` elements, replace them with the current time.
2146     if times.last_access.tv_nsec == c::UTIME_NOW.into()
2147         || times.last_modification.tv_nsec == c::UTIME_NOW.into()
2148     {
2149         let now = {
2150             let mut tv = c::timeval {
2151                 tv_sec: 0,
2152                 tv_usec: 0,
2153             };
2154             unsafe {
2155                 let r = c::gettimeofday(&mut tv, null_mut());
2156                 assert_eq!(r, 0);
2157             }
2158             c::timespec {
2159                 tv_sec: tv.tv_sec,
2160                 tv_nsec: (tv.tv_usec * 1000) as _,
2161             }
2162         };
2163         if times.last_access.tv_nsec == c::UTIME_NOW.into() {
2164             times.last_access = crate::timespec::Timespec {
2165                 tv_sec: now.tv_sec.into(),
2166                 tv_nsec: now.tv_nsec as _,
2167             };
2168         }
2169         if times.last_modification.tv_nsec == c::UTIME_NOW.into() {
2170             times.last_modification = crate::timespec::Timespec {
2171                 tv_sec: now.tv_sec.into(),
2172                 tv_nsec: now.tv_nsec as _,
2173             };
2174         }
2175     }
2176 
2177     // Pack the return values following the rules for [`getattrlist`].
2178     //
2179     // [`getattrlist`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getattrlist.2.html
2180     let mut times_size = 0;
2181     let mut attrs = Attrlist {
2182         bitmapcount: ATTR_BIT_MAP_COUNT,
2183         reserved: 0,
2184         commonattr: 0,
2185         volattr: 0,
2186         dirattr: 0,
2187         fileattr: 0,
2188         forkattr: 0,
2189     };
2190     let mut return_times = [c::timespec {
2191         tv_sec: 0,
2192         tv_nsec: 0,
2193     }; 2];
2194     let mut times_index = 0;
2195     if times.last_modification.tv_nsec != c::UTIME_OMIT.into() {
2196         attrs.commonattr |= ATTR_CMN_MODTIME;
2197         return_times[times_index] = c::timespec {
2198             tv_sec: times
2199                 .last_modification
2200                 .tv_sec
2201                 .try_into()
2202                 .map_err(|_| io::Errno::OVERFLOW)?,
2203             tv_nsec: times.last_modification.tv_nsec as _,
2204         };
2205         times_index += 1;
2206         times_size += size_of::<c::timespec>();
2207     }
2208     if times.last_access.tv_nsec != c::UTIME_OMIT.into() {
2209         attrs.commonattr |= ATTR_CMN_ACCTIME;
2210         return_times[times_index] = c::timespec {
2211             tv_sec: times
2212                 .last_access
2213                 .tv_sec
2214                 .try_into()
2215                 .map_err(|_| io::Errno::OVERFLOW)?,
2216             tv_nsec: times.last_access.tv_nsec as _,
2217         };
2218         times_size += size_of::<c::timespec>();
2219     }
2220 
2221     Ok((times_size, return_times, attrs))
2222 }
2223 
2224 /// Support type for `Attrlist`.
2225 #[cfg(apple)]
2226 type Attrgroup = u32;
2227 
2228 /// Attribute list for use with `setattrlist`.
2229 #[cfg(apple)]
2230 #[repr(C)]
2231 struct Attrlist {
2232     bitmapcount: u16,
2233     reserved: u16,
2234     commonattr: Attrgroup,
2235     volattr: Attrgroup,
2236     dirattr: Attrgroup,
2237     fileattr: Attrgroup,
2238     forkattr: Attrgroup,
2239 }
2240 
2241 #[cfg(any(apple, linux_kernel))]
getxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result<usize>2242 pub(crate) fn getxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result<usize> {
2243     let value_ptr = value.as_mut_ptr();
2244 
2245     #[cfg(not(apple))]
2246     unsafe {
2247         ret_usize(c::getxattr(
2248             path.as_ptr(),
2249             name.as_ptr(),
2250             value_ptr.cast::<c::c_void>(),
2251             value.len(),
2252         ))
2253     }
2254 
2255     #[cfg(apple)]
2256     unsafe {
2257         ret_usize(c::getxattr(
2258             path.as_ptr(),
2259             name.as_ptr(),
2260             value_ptr.cast::<c::c_void>(),
2261             value.len(),
2262             0,
2263             0,
2264         ))
2265     }
2266 }
2267 
2268 #[cfg(any(apple, linux_kernel))]
lgetxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result<usize>2269 pub(crate) fn lgetxattr(path: &CStr, name: &CStr, value: &mut [u8]) -> io::Result<usize> {
2270     let value_ptr = value.as_mut_ptr();
2271 
2272     #[cfg(not(apple))]
2273     unsafe {
2274         ret_usize(c::lgetxattr(
2275             path.as_ptr(),
2276             name.as_ptr(),
2277             value_ptr.cast::<c::c_void>(),
2278             value.len(),
2279         ))
2280     }
2281 
2282     #[cfg(apple)]
2283     {
2284         // Passing an empty to slice to getxattr leads to ERANGE on macOS. Pass null instead.
2285         let ptr = if value.is_empty() {
2286             core::ptr::null_mut()
2287         } else {
2288             value_ptr.cast::<c::c_void>()
2289         };
2290 
2291         unsafe {
2292             ret_usize(c::getxattr(
2293                 path.as_ptr(),
2294                 name.as_ptr(),
2295                 ptr,
2296                 value.len(),
2297                 0,
2298                 c::XATTR_NOFOLLOW,
2299             ))
2300         }
2301     }
2302 }
2303 
2304 #[cfg(any(apple, linux_kernel))]
fgetxattr(fd: BorrowedFd<'_>, name: &CStr, value: &mut [u8]) -> io::Result<usize>2305 pub(crate) fn fgetxattr(fd: BorrowedFd<'_>, name: &CStr, value: &mut [u8]) -> io::Result<usize> {
2306     let value_ptr = value.as_mut_ptr();
2307 
2308     #[cfg(not(apple))]
2309     unsafe {
2310         ret_usize(c::fgetxattr(
2311             borrowed_fd(fd),
2312             name.as_ptr(),
2313             value_ptr.cast::<c::c_void>(),
2314             value.len(),
2315         ))
2316     }
2317 
2318     #[cfg(apple)]
2319     unsafe {
2320         ret_usize(c::fgetxattr(
2321             borrowed_fd(fd),
2322             name.as_ptr(),
2323             value_ptr.cast::<c::c_void>(),
2324             value.len(),
2325             0,
2326             0,
2327         ))
2328     }
2329 }
2330 
2331 #[cfg(any(apple, linux_kernel))]
setxattr( path: &CStr, name: &CStr, value: &[u8], flags: XattrFlags, ) -> io::Result<()>2332 pub(crate) fn setxattr(
2333     path: &CStr,
2334     name: &CStr,
2335     value: &[u8],
2336     flags: XattrFlags,
2337 ) -> io::Result<()> {
2338     #[cfg(not(apple))]
2339     unsafe {
2340         ret(c::setxattr(
2341             path.as_ptr(),
2342             name.as_ptr(),
2343             value.as_ptr().cast::<c::c_void>(),
2344             value.len(),
2345             flags.bits() as i32,
2346         ))
2347     }
2348 
2349     #[cfg(apple)]
2350     unsafe {
2351         ret(c::setxattr(
2352             path.as_ptr(),
2353             name.as_ptr(),
2354             value.as_ptr().cast::<c::c_void>(),
2355             value.len(),
2356             0,
2357             flags.bits() as i32,
2358         ))
2359     }
2360 }
2361 
2362 #[cfg(any(apple, linux_kernel))]
lsetxattr( path: &CStr, name: &CStr, value: &[u8], flags: XattrFlags, ) -> io::Result<()>2363 pub(crate) fn lsetxattr(
2364     path: &CStr,
2365     name: &CStr,
2366     value: &[u8],
2367     flags: XattrFlags,
2368 ) -> io::Result<()> {
2369     #[cfg(not(apple))]
2370     unsafe {
2371         ret(c::lsetxattr(
2372             path.as_ptr(),
2373             name.as_ptr(),
2374             value.as_ptr().cast::<c::c_void>(),
2375             value.len(),
2376             flags.bits() as i32,
2377         ))
2378     }
2379 
2380     #[cfg(apple)]
2381     unsafe {
2382         ret(c::setxattr(
2383             path.as_ptr(),
2384             name.as_ptr(),
2385             value.as_ptr().cast::<c::c_void>(),
2386             value.len(),
2387             0,
2388             flags.bits() as i32 | c::XATTR_NOFOLLOW,
2389         ))
2390     }
2391 }
2392 
2393 #[cfg(any(apple, linux_kernel))]
fsetxattr( fd: BorrowedFd<'_>, name: &CStr, value: &[u8], flags: XattrFlags, ) -> io::Result<()>2394 pub(crate) fn fsetxattr(
2395     fd: BorrowedFd<'_>,
2396     name: &CStr,
2397     value: &[u8],
2398     flags: XattrFlags,
2399 ) -> io::Result<()> {
2400     #[cfg(not(apple))]
2401     unsafe {
2402         ret(c::fsetxattr(
2403             borrowed_fd(fd),
2404             name.as_ptr(),
2405             value.as_ptr().cast::<c::c_void>(),
2406             value.len(),
2407             flags.bits() as i32,
2408         ))
2409     }
2410 
2411     #[cfg(apple)]
2412     unsafe {
2413         ret(c::fsetxattr(
2414             borrowed_fd(fd),
2415             name.as_ptr(),
2416             value.as_ptr().cast::<c::c_void>(),
2417             value.len(),
2418             0,
2419             flags.bits() as i32,
2420         ))
2421     }
2422 }
2423 
2424 #[cfg(any(apple, linux_kernel))]
listxattr(path: &CStr, list: &mut [c::c_char]) -> io::Result<usize>2425 pub(crate) fn listxattr(path: &CStr, list: &mut [c::c_char]) -> io::Result<usize> {
2426     #[cfg(not(apple))]
2427     unsafe {
2428         ret_usize(c::listxattr(path.as_ptr(), list.as_mut_ptr(), list.len()))
2429     }
2430 
2431     #[cfg(apple)]
2432     unsafe {
2433         ret_usize(c::listxattr(
2434             path.as_ptr(),
2435             list.as_mut_ptr(),
2436             list.len(),
2437             0,
2438         ))
2439     }
2440 }
2441 
2442 #[cfg(any(apple, linux_kernel))]
llistxattr(path: &CStr, list: &mut [c::c_char]) -> io::Result<usize>2443 pub(crate) fn llistxattr(path: &CStr, list: &mut [c::c_char]) -> io::Result<usize> {
2444     #[cfg(not(apple))]
2445     unsafe {
2446         ret_usize(c::llistxattr(path.as_ptr(), list.as_mut_ptr(), list.len()))
2447     }
2448 
2449     #[cfg(apple)]
2450     unsafe {
2451         ret_usize(c::listxattr(
2452             path.as_ptr(),
2453             list.as_mut_ptr(),
2454             list.len(),
2455             c::XATTR_NOFOLLOW,
2456         ))
2457     }
2458 }
2459 
2460 #[cfg(any(apple, linux_kernel))]
flistxattr(fd: BorrowedFd<'_>, list: &mut [c::c_char]) -> io::Result<usize>2461 pub(crate) fn flistxattr(fd: BorrowedFd<'_>, list: &mut [c::c_char]) -> io::Result<usize> {
2462     let fd = borrowed_fd(fd);
2463 
2464     #[cfg(not(apple))]
2465     unsafe {
2466         ret_usize(c::flistxattr(fd, list.as_mut_ptr(), list.len()))
2467     }
2468 
2469     #[cfg(apple)]
2470     unsafe {
2471         ret_usize(c::flistxattr(fd, list.as_mut_ptr(), list.len(), 0))
2472     }
2473 }
2474 
2475 #[cfg(any(apple, linux_kernel))]
removexattr(path: &CStr, name: &CStr) -> io::Result<()>2476 pub(crate) fn removexattr(path: &CStr, name: &CStr) -> io::Result<()> {
2477     #[cfg(not(apple))]
2478     unsafe {
2479         ret(c::removexattr(path.as_ptr(), name.as_ptr()))
2480     }
2481 
2482     #[cfg(apple)]
2483     unsafe {
2484         ret(c::removexattr(path.as_ptr(), name.as_ptr(), 0))
2485     }
2486 }
2487 
2488 #[cfg(any(apple, linux_kernel))]
lremovexattr(path: &CStr, name: &CStr) -> io::Result<()>2489 pub(crate) fn lremovexattr(path: &CStr, name: &CStr) -> io::Result<()> {
2490     #[cfg(not(apple))]
2491     unsafe {
2492         ret(c::lremovexattr(path.as_ptr(), name.as_ptr()))
2493     }
2494 
2495     #[cfg(apple)]
2496     unsafe {
2497         ret(c::removexattr(
2498             path.as_ptr(),
2499             name.as_ptr(),
2500             c::XATTR_NOFOLLOW,
2501         ))
2502     }
2503 }
2504 
2505 #[cfg(any(apple, linux_kernel))]
fremovexattr(fd: BorrowedFd<'_>, name: &CStr) -> io::Result<()>2506 pub(crate) fn fremovexattr(fd: BorrowedFd<'_>, name: &CStr) -> io::Result<()> {
2507     let fd = borrowed_fd(fd);
2508 
2509     #[cfg(not(apple))]
2510     unsafe {
2511         ret(c::fremovexattr(fd, name.as_ptr()))
2512     }
2513 
2514     #[cfg(apple)]
2515     unsafe {
2516         ret(c::fremovexattr(fd, name.as_ptr(), 0))
2517     }
2518 }
2519 
2520 #[test]
test_sizes()2521 fn test_sizes() {
2522     #[cfg(linux_kernel)]
2523     assert_eq_size!(c::loff_t, u64);
2524 
2525     // Assert that `Timestamps` has the expected layout. If we're not fixing
2526     // y2038, libc's type should match ours. If we are, it's smaller.
2527     #[cfg(not(fix_y2038))]
2528     assert_eq_size!([c::timespec; 2], Timestamps);
2529     #[cfg(fix_y2038)]
2530     assert!(core::mem::size_of::<[c::timespec; 2]>() < core::mem::size_of::<Timestamps>());
2531 }
2532