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