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