1use crate::errno::Errno;
2use libc::{self, c_char, c_int, c_uint, size_t, ssize_t};
3use std::ffi::OsString;
4#[cfg(not(target_os = "redox"))]
5use std::os::raw;
6use std::os::unix::ffi::OsStringExt;
7use std::os::unix::io::RawFd;
8use crate::sys::stat::Mode;
9use crate::{NixPath, Result};
10
11#[cfg(any(target_os = "android", target_os = "linux"))]
12use std::ptr; #[cfg(any(target_os = "android", target_os = "linux"))]
14use crate::sys::uio::IoVec; #[cfg(any(
17 target_os = "linux",
18 target_os = "android",
19 target_os = "emscripten",
20 target_os = "fuchsia",
21 any(target_os = "wasi", target_env = "wasi"),
22 target_env = "uclibc",
23 target_os = "freebsd"
24))]
25pub use self::posix_fadvise::*;
26
27#[cfg(not(target_os = "redox"))]
28libc_bitflags! {
29 pub struct AtFlags: c_int {
30 AT_REMOVEDIR;
31 AT_SYMLINK_FOLLOW;
32 AT_SYMLINK_NOFOLLOW;
33 #[cfg(any(target_os = "android", target_os = "linux"))]
34 AT_NO_AUTOMOUNT;
35 #[cfg(any(target_os = "android", target_os = "linux"))]
36 AT_EMPTY_PATH;
37 #[cfg(any(target_os = "illumos", target_os = "solaris"))]
38 AT_EACCESS;
39 }
40}
41
42libc_bitflags!(
43 pub struct OFlag: c_int {
45 O_ACCMODE;
47 #[cfg(target_os = "netbsd")]
49 O_ALT_IO;
50 O_APPEND;
52 #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
54 O_ASYNC;
55 O_CLOEXEC;
59 O_CREAT;
61 #[cfg(any(target_os = "android",
63 target_os = "dragonfly",
64 target_os = "freebsd",
65 target_os = "linux",
66 target_os = "netbsd"))]
67 O_DIRECT;
68 #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
70 O_DIRECTORY;
71 #[cfg(any(target_os = "android",
73 target_os = "ios",
74 target_os = "linux",
75 target_os = "macos",
76 target_os = "netbsd",
77 target_os = "openbsd"))]
78 O_DSYNC;
79 O_EXCL;
81 #[cfg(target_os = "freebsd")]
83 O_EXEC;
84 #[cfg(any(target_os = "dragonfly",
86 target_os = "freebsd",
87 target_os = "ios",
88 target_os = "macos",
89 target_os = "netbsd",
90 target_os = "openbsd",
91 target_os = "redox"))]
92 O_EXLOCK;
93 #[cfg(any(target_os = "dragonfly",
95 target_os = "freebsd",
96 target_os = "ios",
97 all(target_os = "linux", not(target_env = "musl")),
98 target_os = "macos",
99 target_os = "netbsd",
100 target_os = "openbsd",
101 target_os = "redox"))]
102 O_FSYNC;
103 #[cfg(any(target_os = "android", target_os = "linux"))]
105 O_LARGEFILE;
106 #[cfg(any(target_os = "android", target_os = "linux"))]
108 O_NOATIME;
109 #[cfg(not(target_os = "redox"))]
111 O_NOCTTY;
112 #[cfg(not(target_os = "redox"))]
114 O_NDELAY;
115 O_NOFOLLOW;
117 O_NONBLOCK;
119 #[cfg(target_os = "netbsd")]
121 O_NOSIGPIPE;
122 #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
126 O_PATH;
127 O_RDONLY;
131 O_RDWR;
135 #[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))]
137 O_RSYNC;
138 #[cfg(target_os = "netbsd")]
140 O_SEARCH;
141 #[cfg(any(target_os = "dragonfly",
143 target_os = "freebsd",
144 target_os = "ios",
145 target_os = "macos",
146 target_os = "netbsd",
147 target_os = "openbsd",
148 target_os = "redox"))]
149 O_SHLOCK;
150 #[cfg(not(target_os = "redox"))]
152 O_SYNC;
153 #[cfg(any(target_os = "android", target_os = "linux"))]
155 O_TMPFILE;
156 O_TRUNC;
158 #[cfg(target_os = "freebsd")]
160 O_TTY_INIT;
161 O_WRONLY;
165 }
166);
167
168#[allow(clippy::useless_conversion)]
170pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
171 let fd = path.with_nix_path(|cstr| {
172 unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
173 })?;
174
175 Errno::result(fd)
176}
177
178#[allow(clippy::useless_conversion)]
180#[cfg(not(target_os = "redox"))]
181pub fn openat<P: ?Sized + NixPath>(
182 dirfd: RawFd,
183 path: &P,
184 oflag: OFlag,
185 mode: Mode,
186) -> Result<RawFd> {
187 let fd = path.with_nix_path(|cstr| {
188 unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
189 })?;
190 Errno::result(fd)
191}
192
193#[cfg(not(target_os = "redox"))]
194pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
195 old_dirfd: Option<RawFd>,
196 old_path: &P1,
197 new_dirfd: Option<RawFd>,
198 new_path: &P2,
199) -> Result<()> {
200 let res = old_path.with_nix_path(|old_cstr| {
201 new_path.with_nix_path(|new_cstr| unsafe {
202 libc::renameat(
203 at_rawfd(old_dirfd),
204 old_cstr.as_ptr(),
205 at_rawfd(new_dirfd),
206 new_cstr.as_ptr(),
207 )
208 })
209 })??;
210 Errno::result(res).map(drop)
211}
212
213#[cfg(all(
214 target_os = "linux",
215 target_env = "gnu",
216))]
217libc_bitflags! {
218 pub struct RenameFlags: u32 {
219 RENAME_EXCHANGE;
220 RENAME_NOREPLACE;
221 RENAME_WHITEOUT;
222 }
223}
224
225#[cfg(all(
226 target_os = "linux",
227 target_env = "gnu",
228))]
229pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
230 old_dirfd: Option<RawFd>,
231 old_path: &P1,
232 new_dirfd: Option<RawFd>,
233 new_path: &P2,
234 flags: RenameFlags,
235) -> Result<()> {
236 let res = old_path.with_nix_path(|old_cstr| {
237 new_path.with_nix_path(|new_cstr| unsafe {
238 libc::renameat2(
239 at_rawfd(old_dirfd),
240 old_cstr.as_ptr(),
241 at_rawfd(new_dirfd),
242 new_cstr.as_ptr(),
243 flags.bits(),
244 )
245 })
246 })??;
247 Errno::result(res).map(drop)
248}
249
250fn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> {
251 unsafe { v.set_len(len as usize) }
252 v.shrink_to_fit();
253 Ok(OsString::from_vec(v.to_vec()))
254}
255
256fn readlink_maybe_at<P: ?Sized + NixPath>(
257 dirfd: Option<RawFd>,
258 path: &P,
259 v: &mut Vec<u8>,
260) -> Result<libc::ssize_t> {
261 path.with_nix_path(|cstr| unsafe {
262 match dirfd {
263 #[cfg(target_os = "redox")]
264 Some(_) => unreachable!(),
265 #[cfg(not(target_os = "redox"))]
266 Some(dirfd) => libc::readlinkat(
267 dirfd,
268 cstr.as_ptr(),
269 v.as_mut_ptr() as *mut c_char,
270 v.capacity() as size_t,
271 ),
272 None => libc::readlink(
273 cstr.as_ptr(),
274 v.as_mut_ptr() as *mut c_char,
275 v.capacity() as size_t,
276 ),
277 }
278 })
279}
280
281fn inner_readlink<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P) -> Result<OsString> {
282 let mut v = Vec::with_capacity(libc::PATH_MAX as usize);
283 let res = readlink_maybe_at(dirfd, path, &mut v)?;
285 let len = Errno::result(res)?;
286 debug_assert!(len >= 0);
287 if (len as usize) < v.capacity() {
288 return wrap_readlink_result(v, res);
289 }
290 let reported_size = match dirfd {
293 #[cfg(target_os = "redox")]
294 Some(_) => unreachable!(),
295 #[cfg(any(target_os = "android", target_os = "linux"))]
296 Some(dirfd) => {
297 let flags = if path.is_empty() { AtFlags::AT_EMPTY_PATH } else { AtFlags::empty() };
298 super::sys::stat::fstatat(dirfd, path, flags | AtFlags::AT_SYMLINK_NOFOLLOW)
299 },
300 #[cfg(not(any(target_os = "android", target_os = "linux", target_os = "redox")))]
301 Some(dirfd) => super::sys::stat::fstatat(dirfd, path, AtFlags::AT_SYMLINK_NOFOLLOW),
302 None => super::sys::stat::lstat(path)
303 }
304 .map(|x| x.st_size)
305 .unwrap_or(0);
306 let mut try_size = if reported_size > 0 {
307 reported_size as usize + 1
310 } else {
311 (libc::PATH_MAX as usize).max(128) << 1
314 };
315 loop {
316 v.reserve_exact(try_size);
317 let res = readlink_maybe_at(dirfd, path, &mut v)?;
318 let len = Errno::result(res)?;
319 debug_assert!(len >= 0);
320 if (len as usize) < v.capacity() {
321 break wrap_readlink_result(v, res);
322 } else {
323 match try_size.checked_shl(1) {
325 Some(next_size) => try_size = next_size,
326 None => break Err(Errno::ENAMETOOLONG),
329 }
330 }
331 }
332}
333
334pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
335 inner_readlink(None, path)
336}
337
338#[cfg(not(target_os = "redox"))]
339pub fn readlinkat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P) -> Result<OsString> {
340 inner_readlink(Some(dirfd), path)
341}
342
343#[cfg(not(target_os = "redox"))]
345pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int {
346 match fd {
347 None => libc::AT_FDCWD,
348 Some(fd) => fd,
349 }
350}
351
352#[cfg(any(target_os = "android", target_os = "linux"))]
353libc_bitflags!(
354 pub struct SealFlag: c_int {
356 F_SEAL_SEAL;
358 F_SEAL_SHRINK;
360 F_SEAL_GROW;
362 F_SEAL_WRITE;
364 }
365);
366
367libc_bitflags!(
368 pub struct FdFlag: c_int {
370 FD_CLOEXEC;
372 }
373);
374
375#[cfg(not(target_os = "redox"))]
376#[derive(Debug, Eq, Hash, PartialEq)]
377#[non_exhaustive]
378pub enum FcntlArg<'a> {
379 F_DUPFD(RawFd),
380 F_DUPFD_CLOEXEC(RawFd),
381 F_GETFD,
382 F_SETFD(FdFlag), F_GETFL,
384 F_SETFL(OFlag), F_SETLK(&'a libc::flock),
386 F_SETLKW(&'a libc::flock),
387 F_GETLK(&'a mut libc::flock),
388 #[cfg(any(target_os = "linux", target_os = "android"))]
389 F_OFD_SETLK(&'a libc::flock),
390 #[cfg(any(target_os = "linux", target_os = "android"))]
391 F_OFD_SETLKW(&'a libc::flock),
392 #[cfg(any(target_os = "linux", target_os = "android"))]
393 F_OFD_GETLK(&'a mut libc::flock),
394 #[cfg(any(target_os = "android", target_os = "linux"))]
395 F_ADD_SEALS(SealFlag),
396 #[cfg(any(target_os = "android", target_os = "linux"))]
397 F_GET_SEALS,
398 #[cfg(any(target_os = "macos", target_os = "ios"))]
399 F_FULLFSYNC,
400 #[cfg(any(target_os = "linux", target_os = "android"))]
401 F_GETPIPE_SZ,
402 #[cfg(any(target_os = "linux", target_os = "android"))]
403 F_SETPIPE_SZ(c_int),
404 }
406
407#[cfg(target_os = "redox")]
408#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
409#[non_exhaustive]
410pub enum FcntlArg {
411 F_DUPFD(RawFd),
412 F_DUPFD_CLOEXEC(RawFd),
413 F_GETFD,
414 F_SETFD(FdFlag), F_GETFL,
416 F_SETFL(OFlag), }
418pub use self::FcntlArg::*;
419
420pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
422 let res = unsafe {
423 match arg {
424 F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
425 F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd),
426 F_GETFD => libc::fcntl(fd, libc::F_GETFD),
427 F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()),
428 F_GETFL => libc::fcntl(fd, libc::F_GETFL),
429 F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()),
430 #[cfg(not(target_os = "redox"))]
431 F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock),
432 #[cfg(not(target_os = "redox"))]
433 F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
434 #[cfg(not(target_os = "redox"))]
435 F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
436 #[cfg(any(target_os = "android", target_os = "linux"))]
437 F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock),
438 #[cfg(any(target_os = "android", target_os = "linux"))]
439 F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock),
440 #[cfg(any(target_os = "android", target_os = "linux"))]
441 F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock),
442 #[cfg(any(target_os = "android", target_os = "linux"))]
443 F_ADD_SEALS(flag) => libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()),
444 #[cfg(any(target_os = "android", target_os = "linux"))]
445 F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS),
446 #[cfg(any(target_os = "macos", target_os = "ios"))]
447 F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC),
448 #[cfg(any(target_os = "linux", target_os = "android"))]
449 F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ),
450 #[cfg(any(target_os = "linux", target_os = "android"))]
451 F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size),
452 }
453 };
454
455 Errno::result(res)
456}
457
458#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
459#[non_exhaustive]
460pub enum FlockArg {
461 LockShared,
462 LockExclusive,
463 Unlock,
464 LockSharedNonblock,
465 LockExclusiveNonblock,
466 UnlockNonblock,
467}
468
469#[cfg(not(target_os = "redox"))]
470pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
471 use self::FlockArg::*;
472
473 let res = unsafe {
474 match arg {
475 LockShared => libc::flock(fd, libc::LOCK_SH),
476 LockExclusive => libc::flock(fd, libc::LOCK_EX),
477 Unlock => libc::flock(fd, libc::LOCK_UN),
478 LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB),
479 LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB),
480 UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB),
481 }
482 };
483
484 Errno::result(res).map(drop)
485}
486
487#[cfg(any(target_os = "android", target_os = "linux"))]
488libc_bitflags! {
489 pub struct SpliceFFlags: c_uint {
491 SPLICE_F_MOVE;
495 SPLICE_F_NONBLOCK;
497 SPLICE_F_MORE;
501 SPLICE_F_GIFT;
505 }
506}
507
508#[cfg(any(target_os = "android", target_os = "linux"))]
525pub fn copy_file_range(
526 fd_in: RawFd,
527 off_in: Option<&mut libc::loff_t>,
528 fd_out: RawFd,
529 off_out: Option<&mut libc::loff_t>,
530 len: usize,
531) -> Result<usize> {
532 let off_in = off_in
533 .map(|offset| offset as *mut libc::loff_t)
534 .unwrap_or(ptr::null_mut());
535 let off_out = off_out
536 .map(|offset| offset as *mut libc::loff_t)
537 .unwrap_or(ptr::null_mut());
538
539 let ret = unsafe {
540 libc::syscall(
541 libc::SYS_copy_file_range,
542 fd_in,
543 off_in,
544 fd_out,
545 off_out,
546 len,
547 0,
548 )
549 };
550 Errno::result(ret).map(|r| r as usize)
551}
552
553#[cfg(any(target_os = "linux", target_os = "android"))]
554pub fn splice(
555 fd_in: RawFd,
556 off_in: Option<&mut libc::loff_t>,
557 fd_out: RawFd,
558 off_out: Option<&mut libc::loff_t>,
559 len: usize,
560 flags: SpliceFFlags,
561) -> Result<usize> {
562 let off_in = off_in
563 .map(|offset| offset as *mut libc::loff_t)
564 .unwrap_or(ptr::null_mut());
565 let off_out = off_out
566 .map(|offset| offset as *mut libc::loff_t)
567 .unwrap_or(ptr::null_mut());
568
569 let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) };
570 Errno::result(ret).map(|r| r as usize)
571}
572
573#[cfg(any(target_os = "linux", target_os = "android"))]
574pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> {
575 let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) };
576 Errno::result(ret).map(|r| r as usize)
577}
578
579#[cfg(any(target_os = "linux", target_os = "android"))]
580pub fn vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result<usize> {
581 let ret = unsafe {
582 libc::vmsplice(
583 fd,
584 iov.as_ptr() as *const libc::iovec,
585 iov.len(),
586 flags.bits(),
587 )
588 };
589 Errno::result(ret).map(|r| r as usize)
590}
591
592#[cfg(any(target_os = "linux"))]
593libc_bitflags!(
594 pub struct FallocateFlags: c_int {
596 FALLOC_FL_KEEP_SIZE;
600 FALLOC_FL_PUNCH_HOLE;
604 FALLOC_FL_COLLAPSE_RANGE;
608 FALLOC_FL_ZERO_RANGE;
612 FALLOC_FL_INSERT_RANGE;
616 FALLOC_FL_UNSHARE_RANGE;
620 }
621);
622
623#[cfg(any(target_os = "linux"))]
628pub fn fallocate(
629 fd: RawFd,
630 mode: FallocateFlags,
631 offset: libc::off_t,
632 len: libc::off_t,
633) -> Result<()> {
634 let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) };
635 Errno::result(res).map(drop)
636}
637
638#[cfg(any(
639 target_os = "linux",
640 target_os = "android",
641 target_os = "emscripten",
642 target_os = "fuchsia",
643 any(target_os = "wasi", target_env = "wasi"),
644 target_env = "uclibc",
645 target_os = "freebsd"
646))]
647mod posix_fadvise {
648 use crate::errno::Errno;
649 use std::os::unix::io::RawFd;
650 use crate::Result;
651
652 libc_enum! {
653 #[repr(i32)]
654 #[non_exhaustive]
655 pub enum PosixFadviseAdvice {
656 POSIX_FADV_NORMAL,
657 POSIX_FADV_SEQUENTIAL,
658 POSIX_FADV_RANDOM,
659 POSIX_FADV_NOREUSE,
660 POSIX_FADV_WILLNEED,
661 POSIX_FADV_DONTNEED,
662 }
663 }
664
665 pub fn posix_fadvise(
666 fd: RawFd,
667 offset: libc::off_t,
668 len: libc::off_t,
669 advice: PosixFadviseAdvice,
670 ) -> Result<()> {
671 let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) };
672
673 if res == 0 {
674 Ok(())
675 } else {
676 Err(Errno::from_i32(res))
677 }
678 }
679}
680
681#[cfg(any(
682 target_os = "linux",
683 target_os = "android",
684 target_os = "emscripten",
685 target_os = "fuchsia",
686 any(target_os = "wasi", target_env = "wasi"),
687 target_os = "freebsd"
688))]
689pub fn posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Result<()> {
690 let res = unsafe { libc::posix_fallocate(fd, offset, len) };
691 match Errno::result(res) {
692 Err(err) => Err(err),
693 Ok(0) => Ok(()),
694 Ok(errno) => Err(Errno::from_i32(errno)),
695 }
696}