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