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(×).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(×).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