• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! libc syscalls supporting `rustix::fs`.
2 
3 use super::super::c;
4 use super::super::conv::{
5     borrowed_fd, c_str, ret, ret_c_int, ret_off_t, ret_owned_fd, ret_ssize_t,
6 };
7 #[cfg(any(target_os = "android", target_os = "linux"))]
8 use super::super::conv::{syscall_ret, syscall_ret_owned_fd, syscall_ret_ssize_t};
9 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
10 use super::super::offset::libc_fallocate;
11 #[cfg(not(any(
12     target_os = "dragonfly",
13     target_os = "haiku",
14     target_os = "illumos",
15     target_os = "ios",
16     target_os = "macos",
17     target_os = "netbsd",
18     target_os = "openbsd",
19     target_os = "redox",
20     target_os = "solaris",
21 )))]
22 use super::super::offset::libc_posix_fadvise;
23 #[cfg(not(any(
24     target_os = "aix",
25     target_os = "android",
26     target_os = "dragonfly",
27     target_os = "fuchsia",
28     target_os = "illumos",
29     target_os = "ios",
30     target_os = "linux",
31     target_os = "macos",
32     target_os = "netbsd",
33     target_os = "openbsd",
34     target_os = "redox",
35     target_os = "solaris",
36 )))]
37 use super::super::offset::libc_posix_fallocate;
38 use super::super::offset::{libc_fstat, libc_fstatat, libc_ftruncate, libc_lseek, libc_off_t};
39 #[cfg(not(any(
40     target_os = "haiku",
41     target_os = "illumos",
42     target_os = "netbsd",
43     target_os = "redox",
44     target_os = "solaris",
45     target_os = "wasi",
46 )))]
47 use super::super::offset::{libc_fstatfs, libc_statfs};
48 #[cfg(not(any(
49     target_os = "haiku",
50     target_os = "illumos",
51     target_os = "redox",
52     target_os = "solaris",
53     target_os = "wasi",
54 )))]
55 use super::super::offset::{libc_fstatvfs, libc_statvfs};
56 #[cfg(all(
57     any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
58     target_env = "gnu",
59 ))]
60 use super::super::time::types::LibcTimespec;
61 use crate::fd::{BorrowedFd, OwnedFd};
62 use crate::ffi::CStr;
63 #[cfg(any(target_os = "ios", target_os = "macos"))]
64 use crate::ffi::CString;
65 #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
66 use crate::fs::Access;
67 #[cfg(not(any(
68     target_os = "dragonfly",
69     target_os = "haiku",
70     target_os = "illumos",
71     target_os = "ios",
72     target_os = "macos",
73     target_os = "netbsd",
74     target_os = "openbsd",
75     target_os = "redox",
76     target_os = "solaris",
77 )))]
78 use crate::fs::Advice;
79 #[cfg(not(any(
80     target_os = "aix",
81     target_os = "dragonfly",
82     target_os = "illumos",
83     target_os = "netbsd",
84     target_os = "openbsd",
85     target_os = "redox",
86     target_os = "solaris",
87 )))]
88 use crate::fs::FallocateFlags;
89 #[cfg(not(any(target_os = "solaris", target_os = "wasi")))]
90 use crate::fs::FlockOperation;
91 #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
92 use crate::fs::MemfdFlags;
93 #[cfg(any(
94     target_os = "android",
95     target_os = "freebsd",
96     target_os = "fuchsia",
97     target_os = "linux",
98 ))]
99 use crate::fs::SealFlags;
100 #[cfg(not(any(
101     target_os = "haiku",
102     target_os = "illumos",
103     target_os = "netbsd",
104     target_os = "redox",
105     target_os = "solaris",
106     target_os = "wasi",
107 )))]
108 use crate::fs::StatFs;
109 #[cfg(any(target_os = "android", target_os = "linux"))]
110 use crate::fs::{cwd, RenameFlags, ResolveFlags, Statx, StatxFlags};
111 #[cfg(not(any(
112     target_os = "ios",
113     target_os = "macos",
114     target_os = "redox",
115     target_os = "wasi",
116 )))]
117 use crate::fs::{Dev, FileType};
118 use crate::fs::{Mode, OFlags, Stat, Timestamps};
119 #[cfg(not(any(
120     target_os = "haiku",
121     target_os = "illumos",
122     target_os = "redox",
123     target_os = "solaris",
124     target_os = "wasi",
125 )))]
126 use crate::fs::{StatVfs, StatVfsMountFlags};
127 use crate::io::{self, SeekFrom};
128 #[cfg(not(target_os = "wasi"))]
129 use crate::process::{Gid, Uid};
130 #[cfg(not(all(
131     any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
132     target_env = "gnu",
133 )))]
134 use crate::utils::as_ptr;
135 use core::convert::TryInto;
136 #[cfg(any(
137     target_os = "android",
138     target_os = "ios",
139     target_os = "linux",
140     target_os = "macos",
141 ))]
142 use core::mem::size_of;
143 use core::mem::MaybeUninit;
144 #[cfg(any(target_os = "android", target_os = "linux"))]
145 use core::ptr::null;
146 #[cfg(any(
147     target_os = "android",
148     target_os = "ios",
149     target_os = "linux",
150     target_os = "macos",
151 ))]
152 use core::ptr::null_mut;
153 #[cfg(any(target_os = "ios", target_os = "macos"))]
154 use {
155     super::super::conv::nonnegative_ret,
156     crate::fs::{copyfile_state_t, CloneFlags, CopyfileFlags},
157 };
158 #[cfg(not(target_os = "redox"))]
159 use {super::super::offset::libc_openat, crate::fs::AtFlags};
160 
161 #[cfg(all(
162     any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
163     target_env = "gnu",
164 ))]
165 weak!(fn __utimensat64(c::c_int, *const c::c_char, *const LibcTimespec, c::c_int) -> c::c_int);
166 #[cfg(all(
167     any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
168     target_env = "gnu",
169 ))]
170 weak!(fn __futimens64(c::c_int, *const LibcTimespec) -> c::c_int);
171 
172 /// Use a direct syscall (via libc) for `openat`.
173 ///
174 /// This is only currently necessary as a workaround for old glibc; see below.
175 #[cfg(all(unix, target_env = "gnu"))]
openat_via_syscall( dirfd: BorrowedFd<'_>, path: &CStr, oflags: OFlags, mode: Mode, ) -> io::Result<OwnedFd>176 fn openat_via_syscall(
177     dirfd: BorrowedFd<'_>,
178     path: &CStr,
179     oflags: OFlags,
180     mode: Mode,
181 ) -> io::Result<OwnedFd> {
182     unsafe {
183         let dirfd = borrowed_fd(dirfd);
184         let path = c_str(path);
185         let oflags = oflags.bits();
186         let mode = c::c_uint::from(mode.bits());
187         ret_owned_fd(c::syscall(
188             c::SYS_openat,
189             c::c_long::from(dirfd),
190             path,
191             c::c_long::from(oflags),
192             mode as c::c_long,
193         ) as c::c_int)
194     }
195 }
196 
197 #[cfg(not(target_os = "redox"))]
openat( dirfd: BorrowedFd<'_>, path: &CStr, oflags: OFlags, mode: Mode, ) -> io::Result<OwnedFd>198 pub(crate) fn openat(
199     dirfd: BorrowedFd<'_>,
200     path: &CStr,
201     oflags: OFlags,
202     mode: Mode,
203 ) -> io::Result<OwnedFd> {
204     // Work around <https://sourceware.org/bugzilla/show_bug.cgi?id=17523>.
205     // Basically old glibc versions don't handle O_TMPFILE correctly.
206     #[cfg(all(unix, target_env = "gnu"))]
207     if oflags.contains(OFlags::TMPFILE) && crate::backend::if_glibc_is_less_than_2_25() {
208         return openat_via_syscall(dirfd, path, oflags, mode);
209     }
210     unsafe {
211         // Pass `mode` as a `c_uint` even if `mode_t` is narrower, since
212         // `libc_openat` is declared as a variadic function and narrower
213         // arguments are promoted.
214         ret_owned_fd(libc_openat(
215             borrowed_fd(dirfd),
216             c_str(path),
217             oflags.bits(),
218             c::c_uint::from(mode.bits()),
219         ))
220     }
221 }
222 
223 #[cfg(not(any(
224     target_os = "haiku",
225     target_os = "illumos",
226     target_os = "netbsd",
227     target_os = "redox",
228     target_os = "solaris",
229     target_os = "wasi",
230 )))]
231 #[inline]
statfs(filename: &CStr) -> io::Result<StatFs>232 pub(crate) fn statfs(filename: &CStr) -> io::Result<StatFs> {
233     unsafe {
234         let mut result = MaybeUninit::<StatFs>::uninit();
235         ret(libc_statfs(c_str(filename), result.as_mut_ptr()))?;
236         Ok(result.assume_init())
237     }
238 }
239 
240 #[cfg(not(any(
241     target_os = "haiku",
242     target_os = "illumos",
243     target_os = "redox",
244     target_os = "solaris",
245     target_os = "wasi",
246 )))]
247 #[inline]
statvfs(filename: &CStr) -> io::Result<StatVfs>248 pub(crate) fn statvfs(filename: &CStr) -> io::Result<StatVfs> {
249     unsafe {
250         let mut result = MaybeUninit::<libc_statvfs>::uninit();
251         ret(libc_statvfs(c_str(filename), result.as_mut_ptr()))?;
252         Ok(libc_statvfs_to_statvfs(result.assume_init()))
253     }
254 }
255 
256 #[cfg(not(target_os = "redox"))]
257 #[inline]
readlinkat(dirfd: BorrowedFd<'_>, path: &CStr, buf: &mut [u8]) -> io::Result<usize>258 pub(crate) fn readlinkat(dirfd: BorrowedFd<'_>, path: &CStr, buf: &mut [u8]) -> io::Result<usize> {
259     unsafe {
260         ret_ssize_t(c::readlinkat(
261             borrowed_fd(dirfd),
262             c_str(path),
263             buf.as_mut_ptr().cast::<c::c_char>(),
264             buf.len(),
265         ))
266         .map(|nread| nread as usize)
267     }
268 }
269 
270 #[cfg(not(target_os = "redox"))]
mkdirat(dirfd: BorrowedFd<'_>, path: &CStr, mode: Mode) -> io::Result<()>271 pub(crate) fn mkdirat(dirfd: BorrowedFd<'_>, path: &CStr, mode: Mode) -> io::Result<()> {
272     unsafe {
273         ret(c::mkdirat(
274             borrowed_fd(dirfd),
275             c_str(path),
276             mode.bits() as c::mode_t,
277         ))
278     }
279 }
280 
281 #[cfg(any(target_os = "android", target_os = "linux"))]
getdents_uninit( fd: BorrowedFd<'_>, buf: &mut [MaybeUninit<u8>], ) -> io::Result<usize>282 pub(crate) fn getdents_uninit(
283     fd: BorrowedFd<'_>,
284     buf: &mut [MaybeUninit<u8>],
285 ) -> io::Result<usize> {
286     unsafe {
287         syscall_ret_ssize_t(c::syscall(
288             c::SYS_getdents64,
289             fd,
290             buf.as_mut_ptr().cast::<c::c_char>(),
291             buf.len(),
292         ))
293     }
294     .map(|nread| nread as usize)
295 }
296 
297 #[cfg(not(target_os = "redox"))]
linkat( old_dirfd: BorrowedFd<'_>, old_path: &CStr, new_dirfd: BorrowedFd<'_>, new_path: &CStr, flags: AtFlags, ) -> io::Result<()>298 pub(crate) fn linkat(
299     old_dirfd: BorrowedFd<'_>,
300     old_path: &CStr,
301     new_dirfd: BorrowedFd<'_>,
302     new_path: &CStr,
303     flags: AtFlags,
304 ) -> io::Result<()> {
305     unsafe {
306         ret(c::linkat(
307             borrowed_fd(old_dirfd),
308             c_str(old_path),
309             borrowed_fd(new_dirfd),
310             c_str(new_path),
311             flags.bits(),
312         ))
313     }
314 }
315 
316 #[cfg(not(target_os = "redox"))]
unlinkat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<()>317 pub(crate) fn unlinkat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<()> {
318     unsafe { ret(c::unlinkat(borrowed_fd(dirfd), c_str(path), flags.bits())) }
319 }
320 
321 #[cfg(not(target_os = "redox"))]
renameat( old_dirfd: BorrowedFd<'_>, old_path: &CStr, new_dirfd: BorrowedFd<'_>, new_path: &CStr, ) -> io::Result<()>322 pub(crate) fn renameat(
323     old_dirfd: BorrowedFd<'_>,
324     old_path: &CStr,
325     new_dirfd: BorrowedFd<'_>,
326     new_path: &CStr,
327 ) -> io::Result<()> {
328     unsafe {
329         ret(c::renameat(
330             borrowed_fd(old_dirfd),
331             c_str(old_path),
332             borrowed_fd(new_dirfd),
333             c_str(new_path),
334         ))
335     }
336 }
337 
338 #[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<()>339 pub(crate) fn renameat2(
340     old_dirfd: BorrowedFd<'_>,
341     old_path: &CStr,
342     new_dirfd: BorrowedFd<'_>,
343     new_path: &CStr,
344     flags: RenameFlags,
345 ) -> io::Result<()> {
346     // `getrandom` wasn't supported in glibc until 2.28.
347     weak_or_syscall! {
348         fn renameat2(
349             olddirfd: c::c_int,
350             oldpath: *const c::c_char,
351             newdirfd: c::c_int,
352             newpath: *const c::c_char,
353             flags: c::c_uint
354         ) via SYS_renameat2 -> c::c_int
355     }
356 
357     unsafe {
358         ret(renameat2(
359             borrowed_fd(old_dirfd),
360             c_str(old_path),
361             borrowed_fd(new_dirfd),
362             c_str(new_path),
363             flags.bits(),
364         ))
365     }
366 }
367 
368 /// At present, `libc` only has `renameat2` defined for glibc. On other
369 /// ABIs, `RenameFlags` has no flags defined, and we use plain `renameat`.
370 #[cfg(any(
371     target_os = "android",
372     all(target_os = "linux", not(target_env = "gnu")),
373 ))]
374 #[inline]
renameat2( old_dirfd: BorrowedFd<'_>, old_path: &CStr, new_dirfd: BorrowedFd<'_>, new_path: &CStr, flags: RenameFlags, ) -> io::Result<()>375 pub(crate) fn renameat2(
376     old_dirfd: BorrowedFd<'_>,
377     old_path: &CStr,
378     new_dirfd: BorrowedFd<'_>,
379     new_path: &CStr,
380     flags: RenameFlags,
381 ) -> io::Result<()> {
382     assert!(flags.is_empty());
383     renameat(old_dirfd, old_path, new_dirfd, new_path)
384 }
385 
386 #[cfg(not(target_os = "redox"))]
symlinkat( old_path: &CStr, new_dirfd: BorrowedFd<'_>, new_path: &CStr, ) -> io::Result<()>387 pub(crate) fn symlinkat(
388     old_path: &CStr,
389     new_dirfd: BorrowedFd<'_>,
390     new_path: &CStr,
391 ) -> io::Result<()> {
392     unsafe {
393         ret(c::symlinkat(
394             c_str(old_path),
395             borrowed_fd(new_dirfd),
396             c_str(new_path),
397         ))
398     }
399 }
400 
401 #[cfg(not(target_os = "redox"))]
statat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat>402 pub(crate) fn statat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat> {
403     // 32-bit and mips64 Linux: `struct stat64` is not y2038 compatible; use
404     // `statx`.
405     #[cfg(all(
406         any(target_os = "android", target_os = "linux"),
407         any(target_pointer_width = "32", target_arch = "mips64"),
408     ))]
409     {
410         match statx(dirfd, path, flags, StatxFlags::BASIC_STATS) {
411             Ok(x) => statx_to_stat(x),
412             Err(io::Errno::NOSYS) => statat_old(dirfd, path, flags),
413             Err(err) => Err(err),
414         }
415     }
416 
417     // Main version: libc is y2038 safe. Or, the platform is not y2038 safe and
418     // there's nothing practical we can do.
419     #[cfg(not(all(
420         any(target_os = "android", target_os = "linux"),
421         any(target_pointer_width = "32", target_arch = "mips64"),
422     )))]
423     unsafe {
424         let mut stat = MaybeUninit::<Stat>::uninit();
425         ret(libc_fstatat(
426             borrowed_fd(dirfd),
427             c_str(path),
428             stat.as_mut_ptr(),
429             flags.bits(),
430         ))?;
431         Ok(stat.assume_init())
432     }
433 }
434 
435 #[cfg(all(
436     any(target_os = "android", target_os = "linux"),
437     any(target_pointer_width = "32", target_arch = "mips64"),
438 ))]
statat_old(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat>439 fn statat_old(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat> {
440     unsafe {
441         let mut result = MaybeUninit::<c::stat64>::uninit();
442         ret(libc_fstatat(
443             borrowed_fd(dirfd),
444             c_str(path),
445             result.as_mut_ptr(),
446             flags.bits(),
447         ))?;
448         stat64_to_stat(result.assume_init())
449     }
450 }
451 
452 #[cfg(not(any(
453     target_os = "emscripten",
454     target_os = "illumos",
455     target_os = "redox",
456     target_os = "solaris",
457 )))]
accessat( dirfd: BorrowedFd<'_>, path: &CStr, access: Access, flags: AtFlags, ) -> io::Result<()>458 pub(crate) fn accessat(
459     dirfd: BorrowedFd<'_>,
460     path: &CStr,
461     access: Access,
462     flags: AtFlags,
463 ) -> io::Result<()> {
464     unsafe {
465         ret(c::faccessat(
466             borrowed_fd(dirfd),
467             c_str(path),
468             access.bits(),
469             flags.bits(),
470         ))
471     }
472 }
473 
474 #[cfg(target_os = "emscripten")]
accessat( _dirfd: BorrowedFd<'_>, _path: &CStr, _access: Access, _flags: AtFlags, ) -> io::Result<()>475 pub(crate) fn accessat(
476     _dirfd: BorrowedFd<'_>,
477     _path: &CStr,
478     _access: Access,
479     _flags: AtFlags,
480 ) -> io::Result<()> {
481     Ok(())
482 }
483 
484 #[cfg(not(target_os = "redox"))]
utimensat( dirfd: BorrowedFd<'_>, path: &CStr, times: &Timestamps, flags: AtFlags, ) -> io::Result<()>485 pub(crate) fn utimensat(
486     dirfd: BorrowedFd<'_>,
487     path: &CStr,
488     times: &Timestamps,
489     flags: AtFlags,
490 ) -> io::Result<()> {
491     // 32-bit gnu version: libc has `utimensat` but it is not y2038 safe by
492     // default.
493     #[cfg(all(
494         any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
495         target_env = "gnu",
496     ))]
497     unsafe {
498         if let Some(libc_utimensat) = __utimensat64.get() {
499             let libc_times: [LibcTimespec; 2] = [
500                 times.last_access.clone().into(),
501                 times.last_modification.clone().into(),
502             ];
503 
504             ret(libc_utimensat(
505                 borrowed_fd(dirfd),
506                 c_str(path),
507                 libc_times.as_ptr(),
508                 flags.bits(),
509             ))
510         } else {
511             utimensat_old(dirfd, path, times, flags)
512         }
513     }
514 
515     // Main version: libc is y2038 safe and has `utimensat`. Or, the platform
516     // is not y2038 safe and there's nothing practical we can do.
517     #[cfg(not(any(
518         target_os = "ios",
519         target_os = "macos",
520         all(
521             any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
522             target_env = "gnu",
523         )
524     )))]
525     unsafe {
526         // Assert that `Timestamps` has the expected layout.
527         let _ = core::mem::transmute::<Timestamps, [c::timespec; 2]>(times.clone());
528 
529         ret(c::utimensat(
530             borrowed_fd(dirfd),
531             c_str(path),
532             as_ptr(times).cast(),
533             flags.bits(),
534         ))
535     }
536 
537     // `utimensat` was introduced in macOS 10.13.
538     #[cfg(any(target_os = "ios", target_os = "macos"))]
539     unsafe {
540         // ABI details
541         weak! {
542             fn utimensat(
543                 c::c_int,
544                 *const c::c_char,
545                 *const c::timespec,
546                 c::c_int
547             ) -> c::c_int
548         }
549         extern "C" {
550             fn setattrlist(
551                 path: *const c::c_char,
552                 attr_list: *const Attrlist,
553                 attr_buf: *const c::c_void,
554                 attr_buf_size: c::size_t,
555                 options: c::c_ulong,
556             ) -> c::c_int;
557         }
558         const FSOPT_NOFOLLOW: c::c_ulong = 0x0000_0001;
559 
560         // If we have `utimensat`, use it.
561         if let Some(have_utimensat) = utimensat.get() {
562             // Assert that `Timestamps` has the expected layout.
563             let _ = core::mem::transmute::<Timestamps, [c::timespec; 2]>(times.clone());
564 
565             return ret(have_utimensat(
566                 borrowed_fd(dirfd),
567                 c_str(path),
568                 as_ptr(times).cast(),
569                 flags.bits(),
570             ));
571         }
572 
573         // `setattrlistat` was introduced in 10.13 along with `utimensat`, so if
574         // we don't have `utimensat`, we don't have `setattrlistat` either.
575         // Emulate it using `fork`, and `fchdir` and [`setattrlist`].
576         //
577         // [`setattrlist`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/setattrlist.2.html
578         match c::fork() {
579             -1 => Err(io::Errno::IO),
580             0 => {
581                 if c::fchdir(borrowed_fd(dirfd)) != 0 {
582                     let code = match libc_errno::errno().0 {
583                         c::EACCES => 2,
584                         c::ENOTDIR => 3,
585                         _ => 1,
586                     };
587                     c::_exit(code);
588                 }
589 
590                 let mut flags_arg = 0;
591                 if flags.contains(AtFlags::SYMLINK_NOFOLLOW) {
592                     flags_arg |= FSOPT_NOFOLLOW;
593                 }
594 
595                 let (attrbuf_size, times, attrs) = times_to_attrlist(times);
596 
597                 if setattrlist(
598                     c_str(path),
599                     &attrs,
600                     as_ptr(&times).cast(),
601                     attrbuf_size,
602                     flags_arg,
603                 ) != 0
604                 {
605                     // Translate expected errno codes into ad-hoc integer
606                     // values suitable for exit statuses.
607                     let code = match libc_errno::errno().0 {
608                         c::EACCES => 2,
609                         c::ENOTDIR => 3,
610                         c::EPERM => 4,
611                         c::EROFS => 5,
612                         c::ELOOP => 6,
613                         c::ENOENT => 7,
614                         c::ENAMETOOLONG => 8,
615                         c::EINVAL => 9,
616                         c::ESRCH => 10,
617                         c::ENOTSUP => 11,
618                         _ => 1,
619                     };
620                     c::_exit(code);
621                 }
622 
623                 c::_exit(0);
624             }
625             child_pid => {
626                 let mut wstatus = 0;
627                 let _ = ret_c_int(c::waitpid(child_pid, &mut wstatus, 0))?;
628                 if c::WIFEXITED(wstatus) {
629                     // Translate our ad-hoc exit statuses back to errno codes.
630                     match c::WEXITSTATUS(wstatus) {
631                         0 => Ok(()),
632                         2 => Err(io::Errno::ACCESS),
633                         3 => Err(io::Errno::NOTDIR),
634                         4 => Err(io::Errno::PERM),
635                         5 => Err(io::Errno::ROFS),
636                         6 => Err(io::Errno::LOOP),
637                         7 => Err(io::Errno::NOENT),
638                         8 => Err(io::Errno::NAMETOOLONG),
639                         9 => Err(io::Errno::INVAL),
640                         10 => Err(io::Errno::SRCH),
641                         11 => Err(io::Errno::NOTSUP),
642                         _ => Err(io::Errno::IO),
643                     }
644                 } else {
645                     Err(io::Errno::IO)
646                 }
647             }
648         }
649     }
650 }
651 
652 #[cfg(all(
653     any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
654     target_env = "gnu",
655 ))]
utimensat_old( dirfd: BorrowedFd<'_>, path: &CStr, times: &Timestamps, flags: AtFlags, ) -> io::Result<()>656 unsafe fn utimensat_old(
657     dirfd: BorrowedFd<'_>,
658     path: &CStr,
659     times: &Timestamps,
660     flags: AtFlags,
661 ) -> io::Result<()> {
662     let old_times = [
663         c::timespec {
664             tv_sec: times
665                 .last_access
666                 .tv_sec
667                 .try_into()
668                 .map_err(|_| io::Errno::OVERFLOW)?,
669             tv_nsec: times.last_access.tv_nsec,
670         },
671         c::timespec {
672             tv_sec: times
673                 .last_modification
674                 .tv_sec
675                 .try_into()
676                 .map_err(|_| io::Errno::OVERFLOW)?,
677             tv_nsec: times.last_modification.tv_nsec,
678         },
679     ];
680     ret(c::utimensat(
681         borrowed_fd(dirfd),
682         c_str(path),
683         old_times.as_ptr(),
684         flags.bits(),
685     ))
686 }
687 
688 #[cfg(not(any(
689     target_os = "android",
690     target_os = "linux",
691     target_os = "redox",
692     target_os = "wasi",
693 )))]
chmodat(dirfd: BorrowedFd<'_>, path: &CStr, mode: Mode) -> io::Result<()>694 pub(crate) fn chmodat(dirfd: BorrowedFd<'_>, path: &CStr, mode: Mode) -> io::Result<()> {
695     unsafe { ret(c::fchmodat(borrowed_fd(dirfd), c_str(path), mode.bits(), 0)) }
696 }
697 
698 #[cfg(any(target_os = "android", target_os = "linux"))]
chmodat(dirfd: BorrowedFd<'_>, path: &CStr, mode: Mode) -> io::Result<()>699 pub(crate) fn chmodat(dirfd: BorrowedFd<'_>, path: &CStr, mode: Mode) -> io::Result<()> {
700     // Linux's `fchmodat` does not have a flags argument.
701     unsafe {
702         // Pass `mode` as a `c_uint` even if `mode_t` is narrower, since
703         // `libc_openat` is declared as a variadic function and narrower
704         // arguments are promoted.
705         syscall_ret(c::syscall(
706             c::SYS_fchmodat,
707             borrowed_fd(dirfd),
708             c_str(path),
709             c::c_uint::from(mode.bits()),
710         ))
711     }
712 }
713 
714 #[cfg(any(target_os = "ios", target_os = "macos"))]
fclonefileat( srcfd: BorrowedFd<'_>, dst_dirfd: BorrowedFd<'_>, dst: &CStr, flags: CloneFlags, ) -> io::Result<()>715 pub(crate) fn fclonefileat(
716     srcfd: BorrowedFd<'_>,
717     dst_dirfd: BorrowedFd<'_>,
718     dst: &CStr,
719     flags: CloneFlags,
720 ) -> io::Result<()> {
721     syscall! {
722         fn fclonefileat(
723             srcfd: BorrowedFd<'_>,
724             dst_dirfd: BorrowedFd<'_>,
725             dst: *const c::c_char,
726             flags: c::c_int
727         ) via SYS_fclonefileat -> c::c_int
728     }
729 
730     unsafe { ret(fclonefileat(srcfd, dst_dirfd, c_str(dst), flags.bits())) }
731 }
732 
733 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
chownat( dirfd: BorrowedFd<'_>, path: &CStr, owner: Option<Uid>, group: Option<Gid>, flags: AtFlags, ) -> io::Result<()>734 pub(crate) fn chownat(
735     dirfd: BorrowedFd<'_>,
736     path: &CStr,
737     owner: Option<Uid>,
738     group: Option<Gid>,
739     flags: AtFlags,
740 ) -> io::Result<()> {
741     unsafe {
742         let (ow, gr) = crate::process::translate_fchown_args(owner, group);
743         ret(c::fchownat(
744             borrowed_fd(dirfd),
745             c_str(path),
746             ow,
747             gr,
748             flags.bits(),
749         ))
750     }
751 }
752 
753 #[cfg(not(any(
754     target_os = "ios",
755     target_os = "macos",
756     target_os = "redox",
757     target_os = "wasi",
758 )))]
mknodat( dirfd: BorrowedFd<'_>, path: &CStr, file_type: FileType, mode: Mode, dev: Dev, ) -> io::Result<()>759 pub(crate) fn mknodat(
760     dirfd: BorrowedFd<'_>,
761     path: &CStr,
762     file_type: FileType,
763     mode: Mode,
764     dev: Dev,
765 ) -> io::Result<()> {
766     unsafe {
767         ret(c::mknodat(
768             borrowed_fd(dirfd),
769             c_str(path),
770             (mode.bits() | file_type.as_raw_mode()) as c::mode_t,
771             dev.try_into().map_err(|_e| io::Errno::PERM)?,
772         ))
773     }
774 }
775 
776 #[cfg(any(target_os = "android", target_os = "linux"))]
copy_file_range( fd_in: BorrowedFd<'_>, off_in: Option<&mut u64>, fd_out: BorrowedFd<'_>, off_out: Option<&mut u64>, len: u64, ) -> io::Result<u64>777 pub(crate) fn copy_file_range(
778     fd_in: BorrowedFd<'_>,
779     off_in: Option<&mut u64>,
780     fd_out: BorrowedFd<'_>,
781     off_out: Option<&mut u64>,
782     len: u64,
783 ) -> io::Result<u64> {
784     assert_eq!(size_of::<c::loff_t>(), size_of::<u64>());
785 
786     let mut off_in_val: c::loff_t = 0;
787     let mut off_out_val: c::loff_t = 0;
788     // Silently cast; we'll get `EINVAL` if the value is negative.
789     let off_in_ptr = if let Some(off_in) = &off_in {
790         off_in_val = (**off_in) as i64;
791         &mut off_in_val
792     } else {
793         null_mut()
794     };
795     let off_out_ptr = if let Some(off_out) = &off_out {
796         off_out_val = (**off_out) as i64;
797         &mut off_out_val
798     } else {
799         null_mut()
800     };
801     let len: usize = len.try_into().unwrap_or(usize::MAX);
802     let copied = unsafe {
803         syscall_ret_ssize_t(c::syscall(
804             c::SYS_copy_file_range,
805             borrowed_fd(fd_in),
806             off_in_ptr,
807             borrowed_fd(fd_out),
808             off_out_ptr,
809             len,
810             0, // no flags are defined yet
811         ))?
812     };
813     if let Some(off_in) = off_in {
814         *off_in = off_in_val as u64;
815     }
816     if let Some(off_out) = off_out {
817         *off_out = off_out_val as u64;
818     }
819     Ok(copied as u64)
820 }
821 
822 #[cfg(not(any(
823     target_os = "dragonfly",
824     target_os = "haiku",
825     target_os = "illumos",
826     target_os = "ios",
827     target_os = "macos",
828     target_os = "netbsd",
829     target_os = "openbsd",
830     target_os = "redox",
831     target_os = "solaris",
832 )))]
fadvise(fd: BorrowedFd<'_>, offset: u64, len: u64, advice: Advice) -> io::Result<()>833 pub(crate) fn fadvise(fd: BorrowedFd<'_>, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
834     let offset = offset as i64;
835     let len = len as i64;
836 
837     // FreeBSD returns `EINVAL` on invalid offsets; emulate the POSIX behavior.
838     #[cfg(target_os = "freebsd")]
839     let offset = if (offset as i64) < 0 {
840         i64::MAX
841     } else {
842         offset
843     };
844 
845     // FreeBSD returns `EINVAL` on overflow; emulate the POSIX behavior.
846     #[cfg(target_os = "freebsd")]
847     let len = if len > 0 && offset.checked_add(len).is_none() {
848         i64::MAX - offset
849     } else {
850         len
851     };
852 
853     let err = unsafe { libc_posix_fadvise(borrowed_fd(fd), offset, len, advice as c::c_int) };
854 
855     // `posix_fadvise` returns its error status rather than using `errno`.
856     if err == 0 {
857         Ok(())
858     } else {
859         Err(io::Errno(err))
860     }
861 }
862 
fcntl_getfl(fd: BorrowedFd<'_>) -> io::Result<OFlags>863 pub(crate) fn fcntl_getfl(fd: BorrowedFd<'_>) -> io::Result<OFlags> {
864     unsafe { ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GETFL)).map(OFlags::from_bits_truncate) }
865 }
866 
fcntl_setfl(fd: BorrowedFd<'_>, flags: OFlags) -> io::Result<()>867 pub(crate) fn fcntl_setfl(fd: BorrowedFd<'_>, flags: OFlags) -> io::Result<()> {
868     unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_SETFL, flags.bits())) }
869 }
870 
871 #[cfg(any(
872     target_os = "android",
873     target_os = "freebsd",
874     target_os = "fuchsia",
875     target_os = "linux",
876 ))]
fcntl_get_seals(fd: BorrowedFd<'_>) -> io::Result<SealFlags>877 pub(crate) fn fcntl_get_seals(fd: BorrowedFd<'_>) -> io::Result<SealFlags> {
878     unsafe {
879         ret_c_int(c::fcntl(borrowed_fd(fd), c::F_GET_SEALS))
880             .map(|flags| SealFlags::from_bits_unchecked(flags))
881     }
882 }
883 
884 #[cfg(any(
885     target_os = "android",
886     target_os = "freebsd",
887     target_os = "fuchsia",
888     target_os = "linux",
889 ))]
fcntl_add_seals(fd: BorrowedFd<'_>, seals: SealFlags) -> io::Result<()>890 pub(crate) fn fcntl_add_seals(fd: BorrowedFd<'_>, seals: SealFlags) -> io::Result<()> {
891     unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_ADD_SEALS, seals.bits())) }
892 }
893 
seek(fd: BorrowedFd<'_>, pos: SeekFrom) -> io::Result<u64>894 pub(crate) fn seek(fd: BorrowedFd<'_>, pos: SeekFrom) -> io::Result<u64> {
895     let (whence, offset): (c::c_int, libc_off_t) = match pos {
896         SeekFrom::Start(pos) => {
897             let pos: u64 = pos;
898             // Silently cast; we'll get `EINVAL` if the value is negative.
899             (c::SEEK_SET, pos as i64)
900         }
901         SeekFrom::End(offset) => (c::SEEK_END, offset),
902         SeekFrom::Current(offset) => (c::SEEK_CUR, offset),
903     };
904     let offset = unsafe { ret_off_t(libc_lseek(borrowed_fd(fd), offset, whence))? };
905     Ok(offset as u64)
906 }
907 
tell(fd: BorrowedFd<'_>) -> io::Result<u64>908 pub(crate) fn tell(fd: BorrowedFd<'_>) -> io::Result<u64> {
909     let offset = unsafe { ret_off_t(libc_lseek(borrowed_fd(fd), 0, c::SEEK_CUR))? };
910     Ok(offset as u64)
911 }
912 
913 #[cfg(not(any(target_os = "android", target_os = "linux", target_os = "wasi")))]
fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()>914 pub(crate) fn fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()> {
915     unsafe { ret(c::fchmod(borrowed_fd(fd), mode.bits())) }
916 }
917 
918 #[cfg(any(target_os = "android", target_os = "linux"))]
fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()>919 pub(crate) fn fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()> {
920     // Use `c::syscall` rather than `c::fchmod` because some libc
921     // implementations, such as musl, add extra logic to `fchmod` to emulate
922     // support for `O_PATH`, which uses `/proc` outside our control and
923     // interferes with our own use of `O_PATH`.
924     unsafe {
925         syscall_ret(c::syscall(
926             c::SYS_fchmod,
927             borrowed_fd(fd),
928             c::c_uint::from(mode.bits()),
929         ))
930     }
931 }
932 
933 #[cfg(any(target_os = "android", target_os = "linux"))]
fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()>934 pub(crate) fn fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> {
935     // Use `c::syscall` rather than `c::fchown` because some libc
936     // implementations, such as musl, add extra logic to `fchown` to emulate
937     // support for `O_PATH`, which uses `/proc` outside our control and
938     // interferes with our own use of `O_PATH`.
939     unsafe {
940         let (ow, gr) = crate::process::translate_fchown_args(owner, group);
941         syscall_ret(c::syscall(c::SYS_fchown, borrowed_fd(fd), ow, gr))
942     }
943 }
944 
945 #[cfg(not(any(target_os = "android", target_os = "linux", target_os = "wasi")))]
fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()>946 pub(crate) fn fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> {
947     unsafe {
948         let (ow, gr) = crate::process::translate_fchown_args(owner, group);
949         ret(c::fchown(borrowed_fd(fd), ow, gr))
950     }
951 }
952 
953 #[cfg(not(any(target_os = "solaris", target_os = "wasi")))]
flock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()>954 pub(crate) fn flock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> {
955     unsafe { ret(c::flock(borrowed_fd(fd), operation as c::c_int)) }
956 }
957 
fstat(fd: BorrowedFd<'_>) -> io::Result<Stat>958 pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result<Stat> {
959     // 32-bit and mips64 Linux: `struct stat64` is not y2038 compatible; use
960     // `statx`.
961     #[cfg(all(
962         any(target_os = "android", target_os = "linux"),
963         any(target_pointer_width = "32", target_arch = "mips64"),
964     ))]
965     {
966         match statx(fd, cstr!(""), AtFlags::EMPTY_PATH, StatxFlags::BASIC_STATS) {
967             Ok(x) => statx_to_stat(x),
968             Err(io::Errno::NOSYS) => fstat_old(fd),
969             Err(err) => Err(err),
970         }
971     }
972 
973     // Main version: libc is y2038 safe. Or, the platform is not y2038 safe and
974     // there's nothing practical we can do.
975     #[cfg(not(all(
976         any(target_os = "android", target_os = "linux"),
977         any(target_pointer_width = "32", target_arch = "mips64"),
978     )))]
979     unsafe {
980         let mut stat = MaybeUninit::<Stat>::uninit();
981         ret(libc_fstat(borrowed_fd(fd), stat.as_mut_ptr()))?;
982         Ok(stat.assume_init())
983     }
984 }
985 
986 #[cfg(all(
987     any(target_os = "android", target_os = "linux"),
988     any(target_pointer_width = "32", target_arch = "mips64"),
989 ))]
fstat_old(fd: BorrowedFd<'_>) -> io::Result<Stat>990 fn fstat_old(fd: BorrowedFd<'_>) -> io::Result<Stat> {
991     unsafe {
992         let mut result = MaybeUninit::<c::stat64>::uninit();
993         ret(libc_fstat(borrowed_fd(fd), result.as_mut_ptr()))?;
994         stat64_to_stat(result.assume_init())
995     }
996 }
997 
998 #[cfg(not(any(
999     target_os = "haiku",
1000     target_os = "illumos",
1001     target_os = "netbsd",
1002     target_os = "redox",
1003     target_os = "solaris",
1004     target_os = "wasi",
1005 )))]
fstatfs(fd: BorrowedFd<'_>) -> io::Result<StatFs>1006 pub(crate) fn fstatfs(fd: BorrowedFd<'_>) -> io::Result<StatFs> {
1007     let mut statfs = MaybeUninit::<StatFs>::uninit();
1008     unsafe {
1009         ret(libc_fstatfs(borrowed_fd(fd), statfs.as_mut_ptr()))?;
1010         Ok(statfs.assume_init())
1011     }
1012 }
1013 
1014 #[cfg(not(any(
1015     target_os = "haiku",
1016     target_os = "illumos",
1017     target_os = "redox",
1018     target_os = "solaris",
1019     target_os = "wasi",
1020 )))]
fstatvfs(fd: BorrowedFd<'_>) -> io::Result<StatVfs>1021 pub(crate) fn fstatvfs(fd: BorrowedFd<'_>) -> io::Result<StatVfs> {
1022     let mut statvfs = MaybeUninit::<libc_statvfs>::uninit();
1023     unsafe {
1024         ret(libc_fstatvfs(borrowed_fd(fd), statvfs.as_mut_ptr()))?;
1025         Ok(libc_statvfs_to_statvfs(statvfs.assume_init()))
1026     }
1027 }
1028 
1029 #[cfg(not(any(
1030     target_os = "haiku",
1031     target_os = "illumos",
1032     target_os = "redox",
1033     target_os = "solaris",
1034     target_os = "wasi"
1035 )))]
libc_statvfs_to_statvfs(from: libc_statvfs) -> StatVfs1036 fn libc_statvfs_to_statvfs(from: libc_statvfs) -> StatVfs {
1037     StatVfs {
1038         f_bsize: from.f_bsize as u64,
1039         f_frsize: from.f_frsize as u64,
1040         f_blocks: from.f_blocks as u64,
1041         f_bfree: from.f_bfree as u64,
1042         f_bavail: from.f_bavail as u64,
1043         f_files: from.f_files as u64,
1044         f_ffree: from.f_ffree as u64,
1045         f_favail: from.f_ffree as u64,
1046         f_fsid: from.f_fsid as u64,
1047         f_flag: unsafe { StatVfsMountFlags::from_bits_unchecked(from.f_flag as u64) },
1048         f_namemax: from.f_namemax as u64,
1049     }
1050 }
1051 
futimens(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()>1052 pub(crate) fn futimens(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()> {
1053     // 32-bit gnu version: libc has `futimens` but it is not y2038 safe by default.
1054     #[cfg(all(
1055         any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
1056         target_env = "gnu",
1057     ))]
1058     unsafe {
1059         if let Some(libc_futimens) = __futimens64.get() {
1060             let libc_times: [LibcTimespec; 2] = [
1061                 times.last_access.clone().into(),
1062                 times.last_modification.clone().into(),
1063             ];
1064 
1065             ret(libc_futimens(borrowed_fd(fd), libc_times.as_ptr()))
1066         } else {
1067             futimens_old(fd, times)
1068         }
1069     }
1070 
1071     // Main version: libc is y2038 safe and has `futimens`. Or, the platform
1072     // is not y2038 safe and there's nothing practical we can do.
1073     #[cfg(not(any(
1074         target_os = "ios",
1075         target_os = "macos",
1076         all(
1077             any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
1078             target_env = "gnu",
1079         )
1080     )))]
1081     unsafe {
1082         // Assert that `Timestamps` has the expected layout.
1083         let _ = core::mem::transmute::<Timestamps, [c::timespec; 2]>(times.clone());
1084 
1085         ret(c::futimens(borrowed_fd(fd), as_ptr(times).cast()))
1086     }
1087 
1088     // `futimens` was introduced in macOS 10.13.
1089     #[cfg(any(target_os = "ios", target_os = "macos"))]
1090     unsafe {
1091         // ABI details.
1092         weak! {
1093             fn futimens(c::c_int, *const c::timespec) -> c::c_int
1094         }
1095         extern "C" {
1096             fn fsetattrlist(
1097                 fd: c::c_int,
1098                 attr_list: *const Attrlist,
1099                 attr_buf: *const c::c_void,
1100                 attr_buf_size: c::size_t,
1101                 options: c::c_ulong,
1102             ) -> c::c_int;
1103         }
1104 
1105         // If we have `futimens`, use it.
1106         if let Some(have_futimens) = futimens.get() {
1107             // Assert that `Timestamps` has the expected layout.
1108             let _ = core::mem::transmute::<Timestamps, [c::timespec; 2]>(times.clone());
1109 
1110             return ret(have_futimens(borrowed_fd(fd), as_ptr(times).cast()));
1111         }
1112 
1113         // Otherwise use `fsetattrlist`.
1114         let (attrbuf_size, times, attrs) = times_to_attrlist(times);
1115 
1116         ret(fsetattrlist(
1117             borrowed_fd(fd),
1118             &attrs,
1119             as_ptr(&times).cast(),
1120             attrbuf_size,
1121             0,
1122         ))
1123     }
1124 }
1125 
1126 #[cfg(all(
1127     any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
1128     target_env = "gnu",
1129 ))]
futimens_old(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()>1130 unsafe fn futimens_old(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()> {
1131     let old_times = [
1132         c::timespec {
1133             tv_sec: times
1134                 .last_access
1135                 .tv_sec
1136                 .try_into()
1137                 .map_err(|_| io::Errno::OVERFLOW)?,
1138             tv_nsec: times.last_access.tv_nsec,
1139         },
1140         c::timespec {
1141             tv_sec: times
1142                 .last_modification
1143                 .tv_sec
1144                 .try_into()
1145                 .map_err(|_| io::Errno::OVERFLOW)?,
1146             tv_nsec: times.last_modification.tv_nsec,
1147         },
1148     ];
1149 
1150     ret(c::futimens(borrowed_fd(fd), old_times.as_ptr()))
1151 }
1152 
1153 #[cfg(not(any(
1154     target_os = "aix",
1155     target_os = "dragonfly",
1156     target_os = "illumos",
1157     target_os = "ios",
1158     target_os = "macos",
1159     target_os = "netbsd",
1160     target_os = "openbsd",
1161     target_os = "redox",
1162     target_os = "solaris",
1163 )))]
fallocate( fd: BorrowedFd<'_>, mode: FallocateFlags, offset: u64, len: u64, ) -> io::Result<()>1164 pub(crate) fn fallocate(
1165     fd: BorrowedFd<'_>,
1166     mode: FallocateFlags,
1167     offset: u64,
1168     len: u64,
1169 ) -> io::Result<()> {
1170     // Silently cast; we'll get `EINVAL` if the value is negative.
1171     let offset = offset as i64;
1172     let len = len as i64;
1173 
1174     #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
1175     unsafe {
1176         ret(libc_fallocate(borrowed_fd(fd), mode.bits(), offset, len))
1177     }
1178 
1179     #[cfg(not(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))]
1180     {
1181         assert!(mode.is_empty());
1182         let err = unsafe { libc_posix_fallocate(borrowed_fd(fd), offset, len) };
1183 
1184         // `posix_fallocate` returns its error status rather than using `errno`.
1185         if err == 0 {
1186             Ok(())
1187         } else {
1188             Err(io::Errno(err))
1189         }
1190     }
1191 }
1192 
1193 #[cfg(any(target_os = "ios", target_os = "macos"))]
fallocate( fd: BorrowedFd<'_>, mode: FallocateFlags, offset: u64, len: u64, ) -> io::Result<()>1194 pub(crate) fn fallocate(
1195     fd: BorrowedFd<'_>,
1196     mode: FallocateFlags,
1197     offset: u64,
1198     len: u64,
1199 ) -> io::Result<()> {
1200     let offset: i64 = offset.try_into().map_err(|_e| io::Errno::INVAL)?;
1201     let len = len as i64;
1202 
1203     assert!(mode.is_empty());
1204 
1205     let new_len = offset.checked_add(len).ok_or(io::Errno::FBIG)?;
1206     let mut store = c::fstore_t {
1207         fst_flags: c::F_ALLOCATECONTIG,
1208         fst_posmode: c::F_PEOFPOSMODE,
1209         fst_offset: 0,
1210         fst_length: new_len,
1211         fst_bytesalloc: 0,
1212     };
1213     unsafe {
1214         if c::fcntl(borrowed_fd(fd), c::F_PREALLOCATE, &store) == -1 {
1215             store.fst_flags = c::F_ALLOCATEALL;
1216             let _ = ret_c_int(c::fcntl(borrowed_fd(fd), c::F_PREALLOCATE, &store))?;
1217         }
1218         ret(c::ftruncate(borrowed_fd(fd), new_len))
1219     }
1220 }
1221 
fsync(fd: BorrowedFd<'_>) -> io::Result<()>1222 pub(crate) fn fsync(fd: BorrowedFd<'_>) -> io::Result<()> {
1223     unsafe { ret(c::fsync(borrowed_fd(fd))) }
1224 }
1225 
1226 #[cfg(not(any(
1227     target_os = "dragonfly",
1228     target_os = "haiku",
1229     target_os = "ios",
1230     target_os = "macos",
1231     target_os = "redox",
1232 )))]
fdatasync(fd: BorrowedFd<'_>) -> io::Result<()>1233 pub(crate) fn fdatasync(fd: BorrowedFd<'_>) -> io::Result<()> {
1234     unsafe { ret(c::fdatasync(borrowed_fd(fd))) }
1235 }
1236 
ftruncate(fd: BorrowedFd<'_>, length: u64) -> io::Result<()>1237 pub(crate) fn ftruncate(fd: BorrowedFd<'_>, length: u64) -> io::Result<()> {
1238     let length = length.try_into().map_err(|_overflow_err| io::Errno::FBIG)?;
1239     unsafe { ret(libc_ftruncate(borrowed_fd(fd), length)) }
1240 }
1241 
1242 #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
memfd_create(path: &CStr, flags: MemfdFlags) -> io::Result<OwnedFd>1243 pub(crate) fn memfd_create(path: &CStr, flags: MemfdFlags) -> io::Result<OwnedFd> {
1244     #[cfg(target_os = "freebsd")]
1245     weakcall! {
1246         fn memfd_create(
1247             name: *const c::c_char,
1248             flags: c::c_uint
1249         ) -> c::c_int
1250     }
1251 
1252     #[cfg(any(target_os = "android", target_os = "linux"))]
1253     weak_or_syscall! {
1254         fn memfd_create(
1255             name: *const c::c_char,
1256             flags: c::c_uint
1257         ) via SYS_memfd_create -> c::c_int
1258     }
1259 
1260     unsafe { ret_owned_fd(memfd_create(c_str(path), flags.bits())) }
1261 }
1262 
1263 #[cfg(any(target_os = "android", target_os = "linux"))]
openat2( dirfd: BorrowedFd<'_>, path: &CStr, oflags: OFlags, mode: Mode, resolve: ResolveFlags, ) -> io::Result<OwnedFd>1264 pub(crate) fn openat2(
1265     dirfd: BorrowedFd<'_>,
1266     path: &CStr,
1267     oflags: OFlags,
1268     mode: Mode,
1269     resolve: ResolveFlags,
1270 ) -> io::Result<OwnedFd> {
1271     let oflags: i32 = oflags.bits();
1272     let open_how = OpenHow {
1273         oflag: u64::from(oflags as u32),
1274         mode: u64::from(mode.bits()),
1275         resolve: resolve.bits(),
1276     };
1277 
1278     unsafe {
1279         syscall_ret_owned_fd(c::syscall(
1280             SYS_OPENAT2,
1281             borrowed_fd(dirfd),
1282             c_str(path),
1283             &open_how,
1284             SIZEOF_OPEN_HOW,
1285         ))
1286     }
1287 }
1288 #[cfg(all(
1289     target_pointer_width = "32",
1290     any(target_os = "android", target_os = "linux"),
1291 ))]
1292 const SYS_OPENAT2: i32 = 437;
1293 #[cfg(all(
1294     target_pointer_width = "64",
1295     any(target_os = "android", target_os = "linux"),
1296 ))]
1297 const SYS_OPENAT2: i64 = 437;
1298 
1299 #[cfg(any(target_os = "android", target_os = "linux"))]
1300 #[repr(C)]
1301 #[derive(Debug)]
1302 struct OpenHow {
1303     oflag: u64,
1304     mode: u64,
1305     resolve: u64,
1306 }
1307 #[cfg(any(target_os = "android", target_os = "linux"))]
1308 const SIZEOF_OPEN_HOW: usize = size_of::<OpenHow>();
1309 
1310 #[cfg(target_os = "linux")]
sendfile( out_fd: BorrowedFd<'_>, in_fd: BorrowedFd<'_>, offset: Option<&mut u64>, count: usize, ) -> io::Result<usize>1311 pub(crate) fn sendfile(
1312     out_fd: BorrowedFd<'_>,
1313     in_fd: BorrowedFd<'_>,
1314     offset: Option<&mut u64>,
1315     count: usize,
1316 ) -> io::Result<usize> {
1317     unsafe {
1318         let nsent = ret_ssize_t(c::sendfile64(
1319             borrowed_fd(out_fd),
1320             borrowed_fd(in_fd),
1321             offset.map_or(null_mut(), crate::utils::as_mut_ptr).cast(),
1322             count,
1323         ))?;
1324         Ok(nsent as usize)
1325     }
1326 }
1327 
1328 /// Convert from a Linux `statx` value to rustix's `Stat`.
1329 #[cfg(all(
1330     any(target_os = "android", target_os = "linux"),
1331     target_pointer_width = "32",
1332 ))]
statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat>1333 fn statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat> {
1334     Ok(Stat {
1335         st_dev: crate::fs::makedev(x.stx_dev_major, x.stx_dev_minor).into(),
1336         st_mode: x.stx_mode.into(),
1337         st_nlink: x.stx_nlink.into(),
1338         st_uid: x.stx_uid.into(),
1339         st_gid: x.stx_gid.into(),
1340         st_rdev: crate::fs::makedev(x.stx_rdev_major, x.stx_rdev_minor).into(),
1341         st_size: x.stx_size.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1342         st_blksize: x.stx_blksize.into(),
1343         st_blocks: x.stx_blocks.into(),
1344         st_atime: x
1345             .stx_atime
1346             .tv_sec
1347             .try_into()
1348             .map_err(|_| io::Errno::OVERFLOW)?,
1349         st_atime_nsec: x.stx_atime.tv_nsec as _,
1350         st_mtime: x
1351             .stx_mtime
1352             .tv_sec
1353             .try_into()
1354             .map_err(|_| io::Errno::OVERFLOW)?,
1355         st_mtime_nsec: x.stx_mtime.tv_nsec as _,
1356         st_ctime: x
1357             .stx_ctime
1358             .tv_sec
1359             .try_into()
1360             .map_err(|_| io::Errno::OVERFLOW)?,
1361         st_ctime_nsec: x.stx_ctime.tv_nsec as _,
1362         st_ino: x.stx_ino.into(),
1363     })
1364 }
1365 
1366 /// Convert from a Linux `statx` value to rustix's `Stat`.
1367 ///
1368 /// mips64' `struct stat64` in libc has private fields, and `stx_blocks`
1369 #[cfg(all(
1370     any(target_os = "android", target_os = "linux"),
1371     target_arch = "mips64",
1372 ))]
statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat>1373 fn statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat> {
1374     let mut result: Stat = unsafe { core::mem::zeroed() };
1375 
1376     result.st_dev = crate::fs::makedev(x.stx_dev_major, x.stx_dev_minor);
1377     result.st_mode = x.stx_mode.into();
1378     result.st_nlink = x.stx_nlink.into();
1379     result.st_uid = x.stx_uid.into();
1380     result.st_gid = x.stx_gid.into();
1381     result.st_rdev = crate::fs::makedev(x.stx_rdev_major, x.stx_rdev_minor);
1382     result.st_size = x.stx_size.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1383     result.st_blksize = x.stx_blksize.into();
1384     result.st_blocks = x.stx_blocks.try_into().map_err(|_e| io::Errno::OVERFLOW)?;
1385     result.st_atime = x
1386         .stx_atime
1387         .tv_sec
1388         .try_into()
1389         .map_err(|_| io::Errno::OVERFLOW)?;
1390     result.st_atime_nsec = x.stx_atime.tv_nsec as _;
1391     result.st_mtime = x
1392         .stx_mtime
1393         .tv_sec
1394         .try_into()
1395         .map_err(|_| io::Errno::OVERFLOW)?;
1396     result.st_mtime_nsec = x.stx_mtime.tv_nsec as _;
1397     result.st_ctime = x
1398         .stx_ctime
1399         .tv_sec
1400         .try_into()
1401         .map_err(|_| io::Errno::OVERFLOW)?;
1402     result.st_ctime_nsec = x.stx_ctime.tv_nsec as _;
1403     result.st_ino = x.stx_ino.into();
1404 
1405     Ok(result)
1406 }
1407 
1408 /// Convert from a Linux `stat64` value to rustix's `Stat`.
1409 #[cfg(all(
1410     any(target_os = "android", target_os = "linux"),
1411     target_pointer_width = "32",
1412 ))]
stat64_to_stat(s64: c::stat64) -> io::Result<Stat>1413 fn stat64_to_stat(s64: c::stat64) -> io::Result<Stat> {
1414     Ok(Stat {
1415         st_dev: s64.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1416         st_mode: s64.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1417         st_nlink: s64.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1418         st_uid: s64.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1419         st_gid: s64.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1420         st_rdev: s64.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1421         st_size: s64.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1422         st_blksize: s64.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1423         st_blocks: s64.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1424         st_atime: s64.st_atime.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1425         st_atime_nsec: s64
1426             .st_atime_nsec
1427             .try_into()
1428             .map_err(|_| io::Errno::OVERFLOW)?,
1429         st_mtime: s64.st_mtime.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1430         st_mtime_nsec: s64
1431             .st_mtime_nsec
1432             .try_into()
1433             .map_err(|_| io::Errno::OVERFLOW)?,
1434         st_ctime: s64.st_ctime.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1435         st_ctime_nsec: s64
1436             .st_ctime_nsec
1437             .try_into()
1438             .map_err(|_| io::Errno::OVERFLOW)?,
1439         st_ino: s64.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1440     })
1441 }
1442 
1443 /// Convert from a Linux `stat64` value to rustix's `Stat`.
1444 ///
1445 /// mips64' `struct stat64` in libc has private fields, and `st_blocks` has
1446 /// type `i64`.
1447 #[cfg(all(
1448     any(target_os = "android", target_os = "linux"),
1449     target_arch = "mips64",
1450 ))]
stat64_to_stat(s64: c::stat64) -> io::Result<Stat>1451 fn stat64_to_stat(s64: c::stat64) -> io::Result<Stat> {
1452     let mut result: Stat = unsafe { core::mem::zeroed() };
1453 
1454     result.st_dev = s64.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1455     result.st_mode = s64.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1456     result.st_nlink = s64.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1457     result.st_uid = s64.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1458     result.st_gid = s64.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1459     result.st_rdev = s64.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1460     result.st_size = s64.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1461     result.st_blksize = s64.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1462     result.st_blocks = s64.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1463     result.st_atime = s64.st_atime.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1464     result.st_atime_nsec = s64
1465         .st_atime_nsec
1466         .try_into()
1467         .map_err(|_| io::Errno::OVERFLOW)?;
1468     result.st_mtime = s64.st_mtime.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1469     result.st_mtime_nsec = s64
1470         .st_mtime_nsec
1471         .try_into()
1472         .map_err(|_| io::Errno::OVERFLOW)?;
1473     result.st_ctime = s64.st_ctime.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1474     result.st_ctime_nsec = s64
1475         .st_ctime_nsec
1476         .try_into()
1477         .map_err(|_| io::Errno::OVERFLOW)?;
1478     result.st_ino = s64.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?;
1479 
1480     Ok(result)
1481 }
1482 
1483 #[cfg(any(target_os = "android", target_os = "linux"))]
1484 #[allow(non_upper_case_globals)]
1485 mod sys {
1486     use super::{c, BorrowedFd, Statx};
1487 
1488     #[cfg(all(target_os = "android", target_arch = "arm"))]
1489     const SYS_statx: c::c_long = 397;
1490     #[cfg(all(target_os = "android", target_arch = "x86"))]
1491     const SYS_statx: c::c_long = 383;
1492     #[cfg(all(target_os = "android", target_arch = "aarch64"))]
1493     const SYS_statx: c::c_long = 291;
1494     #[cfg(all(target_os = "android", target_arch = "x86_64"))]
1495     const SYS_statx: c::c_long = 332;
1496 
1497     weak_or_syscall! {
1498         pub(super) fn statx(
1499             pirfd: BorrowedFd<'_>,
1500             path: *const c::c_char,
1501             flags: c::c_int,
1502             mask: c::c_uint,
1503             buf: *mut Statx
1504         ) via SYS_statx -> c::c_int
1505     }
1506 }
1507 
1508 #[cfg(any(target_os = "android", target_os = "linux"))]
1509 #[allow(non_upper_case_globals)]
statx( dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags, mask: StatxFlags, ) -> io::Result<Statx>1510 pub(crate) fn statx(
1511     dirfd: BorrowedFd<'_>,
1512     path: &CStr,
1513     flags: AtFlags,
1514     mask: StatxFlags,
1515 ) -> io::Result<Statx> {
1516     // If a future Linux kernel adds more fields to `struct statx` and users
1517     // passing flags unknown to rustix in `StatxFlags`, we could end up
1518     // writing outside of the buffer. To prevent this possibility, we mask off
1519     // any flags that we don't know about.
1520     //
1521     // This includes `STATX__RESERVED`, which has a value that we know, but
1522     // which could take on arbitrary new meaning in the future. Linux currently
1523     // rejects this flag with `EINVAL`, so we do the same.
1524     //
1525     // This doesn't rely on `STATX_ALL` because [it's deprecated] and already
1526     // doesn't represent all the known flags.
1527     //
1528     // [it's deprecated]: https://patchwork.kernel.org/project/linux-fsdevel/patch/20200505095915.11275-7-mszeredi@redhat.com/
1529     #[cfg(not(any(target_os = "android", target_env = "musl")))]
1530     const STATX__RESERVED: u32 = libc::STATX__RESERVED as u32;
1531     #[cfg(any(target_os = "android", target_env = "musl"))]
1532     const STATX__RESERVED: u32 = linux_raw_sys::general::STATX__RESERVED;
1533     if (mask.bits() & STATX__RESERVED) == STATX__RESERVED {
1534         return Err(io::Errno::INVAL);
1535     }
1536     let mask = mask & StatxFlags::all();
1537 
1538     let mut statx_buf = MaybeUninit::<Statx>::uninit();
1539     unsafe {
1540         ret(sys::statx(
1541             dirfd,
1542             c_str(path),
1543             flags.bits(),
1544             mask.bits(),
1545             statx_buf.as_mut_ptr(),
1546         ))?;
1547         Ok(statx_buf.assume_init())
1548     }
1549 }
1550 
1551 #[cfg(any(target_os = "android", target_os = "linux"))]
1552 #[inline]
is_statx_available() -> bool1553 pub(crate) fn is_statx_available() -> bool {
1554     unsafe {
1555         // Call `statx` with null pointers so that if it fails for any reason
1556         // other than `EFAULT`, we know it's not supported.
1557         matches!(
1558             ret(sys::statx(cwd(), null(), 0, 0, null_mut())),
1559             Err(io::Errno::FAULT)
1560         )
1561     }
1562 }
1563 
1564 #[cfg(any(target_os = "ios", target_os = "macos"))]
fcopyfile( from: BorrowedFd<'_>, to: BorrowedFd<'_>, state: copyfile_state_t, flags: CopyfileFlags, ) -> io::Result<()>1565 pub(crate) unsafe fn fcopyfile(
1566     from: BorrowedFd<'_>,
1567     to: BorrowedFd<'_>,
1568     state: copyfile_state_t,
1569     flags: CopyfileFlags,
1570 ) -> io::Result<()> {
1571     extern "C" {
1572         fn fcopyfile(
1573             from: c::c_int,
1574             to: c::c_int,
1575             state: copyfile_state_t,
1576             flags: c::c_uint,
1577         ) -> c::c_int;
1578     }
1579 
1580     nonnegative_ret(fcopyfile(
1581         borrowed_fd(from),
1582         borrowed_fd(to),
1583         state,
1584         flags.bits(),
1585     ))
1586 }
1587 
1588 #[cfg(any(target_os = "ios", target_os = "macos"))]
copyfile_state_alloc() -> io::Result<copyfile_state_t>1589 pub(crate) fn copyfile_state_alloc() -> io::Result<copyfile_state_t> {
1590     extern "C" {
1591         fn copyfile_state_alloc() -> copyfile_state_t;
1592     }
1593 
1594     let result = unsafe { copyfile_state_alloc() };
1595     if result.0.is_null() {
1596         Err(io::Errno::last_os_error())
1597     } else {
1598         Ok(result)
1599     }
1600 }
1601 
1602 #[cfg(any(target_os = "ios", target_os = "macos"))]
copyfile_state_free(state: copyfile_state_t) -> io::Result<()>1603 pub(crate) unsafe fn copyfile_state_free(state: copyfile_state_t) -> io::Result<()> {
1604     extern "C" {
1605         fn copyfile_state_free(state: copyfile_state_t) -> c::c_int;
1606     }
1607 
1608     nonnegative_ret(copyfile_state_free(state))
1609 }
1610 
1611 #[cfg(any(target_os = "ios", target_os = "macos"))]
1612 const COPYFILE_STATE_COPIED: u32 = 8;
1613 
1614 #[cfg(any(target_os = "ios", target_os = "macos"))]
copyfile_state_get_copied(state: copyfile_state_t) -> io::Result<u64>1615 pub(crate) unsafe fn copyfile_state_get_copied(state: copyfile_state_t) -> io::Result<u64> {
1616     let mut copied = MaybeUninit::<u64>::uninit();
1617     copyfile_state_get(state, COPYFILE_STATE_COPIED, copied.as_mut_ptr().cast())?;
1618     Ok(copied.assume_init())
1619 }
1620 
1621 #[cfg(any(target_os = "ios", target_os = "macos"))]
copyfile_state_get( state: copyfile_state_t, flag: u32, dst: *mut c::c_void, ) -> io::Result<()>1622 pub(crate) unsafe fn copyfile_state_get(
1623     state: copyfile_state_t,
1624     flag: u32,
1625     dst: *mut c::c_void,
1626 ) -> io::Result<()> {
1627     extern "C" {
1628         fn copyfile_state_get(state: copyfile_state_t, flag: u32, dst: *mut c::c_void) -> c::c_int;
1629     }
1630 
1631     nonnegative_ret(copyfile_state_get(state, flag, dst))
1632 }
1633 
1634 #[cfg(any(target_os = "ios", target_os = "macos"))]
getpath(fd: BorrowedFd<'_>) -> io::Result<CString>1635 pub(crate) fn getpath(fd: BorrowedFd<'_>) -> io::Result<CString> {
1636     // The use of PATH_MAX is generally not encouraged, but it
1637     // is inevitable in this case because macOS defines `fcntl` with
1638     // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
1639     // alternatives. If a better method is invented, it should be used
1640     // instead.
1641     let mut buf = alloc::vec![0; c::PATH_MAX as usize];
1642 
1643     // From the [macOS `fcntl` man page]:
1644     // `F_GETPATH` - Get the path of the file descriptor `Fildes`. The argument
1645     //               must be a buffer of size `MAXPATHLEN` or greater.
1646     //
1647     // [macOS `fcntl` man page]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html
1648     unsafe {
1649         ret(c::fcntl(borrowed_fd(fd), c::F_GETPATH, buf.as_mut_ptr()))?;
1650     }
1651 
1652     let l = buf.iter().position(|&c| c == 0).unwrap();
1653     buf.truncate(l);
1654 
1655     // TODO: On Rust 1.56, we can use `shrink_to` here.
1656     //buf.shrink_to(l + 1);
1657     buf.shrink_to_fit();
1658 
1659     Ok(CString::new(buf).unwrap())
1660 }
1661 
1662 #[cfg(any(target_os = "ios", target_os = "macos"))]
fcntl_rdadvise(fd: BorrowedFd<'_>, offset: u64, len: u64) -> io::Result<()>1663 pub(crate) fn fcntl_rdadvise(fd: BorrowedFd<'_>, offset: u64, len: u64) -> io::Result<()> {
1664     // From the [macOS `fcntl` man page]:
1665     // `F_RDADVISE` - Issue an advisory read async with no copy to user.
1666     //
1667     // The `F_RDADVISE` command operates on the following structure which holds
1668     // information passed from the user to the system:
1669     //
1670     // ```c
1671     // struct radvisory {
1672     //      off_t   ra_offset;  /* offset into the file */
1673     //      int     ra_count;   /* size of the read     */
1674     // };
1675     // ```
1676     //
1677     // [macOS `fcntl` man page]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html
1678     let ra_offset = match offset.try_into() {
1679         Ok(len) => len,
1680         // If this conversion fails, the user is providing an offset outside
1681         // any possible file extent, so just ignore it.
1682         Err(_) => return Ok(()),
1683     };
1684     let ra_count = match len.try_into() {
1685         Ok(len) => len,
1686         // If this conversion fails, the user is providing a dubiously large
1687         // hint which is unlikely to improve performance.
1688         Err(_) => return Ok(()),
1689     };
1690     unsafe {
1691         let radvisory = c::radvisory {
1692             ra_offset,
1693             ra_count,
1694         };
1695         ret(c::fcntl(borrowed_fd(fd), c::F_RDADVISE, &radvisory))
1696     }
1697 }
1698 
1699 #[cfg(any(target_os = "ios", target_os = "macos"))]
fcntl_fullfsync(fd: BorrowedFd<'_>) -> io::Result<()>1700 pub(crate) fn fcntl_fullfsync(fd: BorrowedFd<'_>) -> io::Result<()> {
1701     unsafe { ret(c::fcntl(borrowed_fd(fd), c::F_FULLFSYNC)) }
1702 }
1703 
1704 /// Convert `times` from a `futimens`/`utimensat` argument into `setattrlist`
1705 /// arguments.
1706 #[cfg(any(target_os = "ios", target_os = "macos"))]
times_to_attrlist(times: &Timestamps) -> (c::size_t, [c::timespec; 2], Attrlist)1707 fn times_to_attrlist(times: &Timestamps) -> (c::size_t, [c::timespec; 2], Attrlist) {
1708     // ABI details.
1709     const ATTR_CMN_MODTIME: u32 = 0x0000_0400;
1710     const ATTR_CMN_ACCTIME: u32 = 0x0000_1000;
1711     const ATTR_BIT_MAP_COUNT: u16 = 5;
1712 
1713     let mut times = times.clone();
1714 
1715     // If we have any `UTIME_NOW` elements, replace them with the current time.
1716     if times.last_access.tv_nsec == c::UTIME_NOW || times.last_modification.tv_nsec == c::UTIME_NOW
1717     {
1718         let now = {
1719             let mut tv = c::timeval {
1720                 tv_sec: 0,
1721                 tv_usec: 0,
1722             };
1723             unsafe {
1724                 let r = c::gettimeofday(&mut tv, null_mut());
1725                 assert_eq!(r, 0);
1726             }
1727             c::timespec {
1728                 tv_sec: tv.tv_sec,
1729                 tv_nsec: (tv.tv_usec * 1000) as _,
1730             }
1731         };
1732         if times.last_access.tv_nsec == c::UTIME_NOW {
1733             times.last_access = now;
1734         }
1735         if times.last_modification.tv_nsec == c::UTIME_NOW {
1736             times.last_modification = now;
1737         }
1738     }
1739 
1740     // Pack the return values following the rules for [`getattrlist`].
1741     //
1742     // [`getattrlist`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getattrlist.2.html
1743     let mut times_size = 0;
1744     let mut attrs = Attrlist {
1745         bitmapcount: ATTR_BIT_MAP_COUNT,
1746         reserved: 0,
1747         commonattr: 0,
1748         volattr: 0,
1749         dirattr: 0,
1750         fileattr: 0,
1751         forkattr: 0,
1752     };
1753     let mut return_times = [c::timespec {
1754         tv_sec: 0,
1755         tv_nsec: 0,
1756     }; 2];
1757     let mut times_index = 0;
1758     if times.last_modification.tv_nsec != c::UTIME_OMIT {
1759         attrs.commonattr |= ATTR_CMN_MODTIME;
1760         return_times[times_index] = times.last_modification;
1761         times_index += 1;
1762         times_size += size_of::<c::timespec>();
1763     }
1764     if times.last_access.tv_nsec != c::UTIME_OMIT {
1765         attrs.commonattr |= ATTR_CMN_ACCTIME;
1766         return_times[times_index] = times.last_access;
1767         times_size += size_of::<c::timespec>();
1768     }
1769 
1770     (times_size, return_times, attrs)
1771 }
1772 
1773 /// Support type for `Attrlist`.
1774 #[cfg(any(target_os = "ios", target_os = "macos"))]
1775 type Attrgroup = u32;
1776 
1777 /// Attribute list for use with `setattrlist`.
1778 #[cfg(any(target_os = "ios", target_os = "macos"))]
1779 #[repr(C)]
1780 struct Attrlist {
1781     bitmapcount: u16,
1782     reserved: u16,
1783     commonattr: Attrgroup,
1784     volattr: Attrgroup,
1785     dirattr: Attrgroup,
1786     fileattr: Attrgroup,
1787     forkattr: Attrgroup,
1788 }
1789 
1790 #[cfg(any(target_os = "android", target_os = "linux"))]
mount( source: Option<&CStr>, target: &CStr, file_system_type: Option<&CStr>, flags: super::types::MountFlagsArg, data: Option<&CStr>, ) -> io::Result<()>1791 pub(crate) fn mount(
1792     source: Option<&CStr>,
1793     target: &CStr,
1794     file_system_type: Option<&CStr>,
1795     flags: super::types::MountFlagsArg,
1796     data: Option<&CStr>,
1797 ) -> io::Result<()> {
1798     unsafe {
1799         ret(c::mount(
1800             source.map_or_else(null, CStr::as_ptr),
1801             target.as_ptr(),
1802             file_system_type.map_or_else(null, CStr::as_ptr),
1803             flags.0,
1804             data.map_or_else(null, CStr::as_ptr).cast(),
1805         ))
1806     }
1807 }
1808