13da5c369Sopenharmony_ciuse crate::errno::Errno;
23da5c369Sopenharmony_ciuse libc::{self, c_char, c_int, c_uint, size_t, ssize_t};
33da5c369Sopenharmony_ciuse std::ffi::OsString;
43da5c369Sopenharmony_ci#[cfg(not(target_os = "redox"))]
53da5c369Sopenharmony_ciuse std::os::raw;
63da5c369Sopenharmony_ciuse std::os::unix::ffi::OsStringExt;
73da5c369Sopenharmony_ciuse std::os::unix::io::RawFd;
83da5c369Sopenharmony_ci
93da5c369Sopenharmony_ci#[cfg(feature = "fs")]
103da5c369Sopenharmony_ciuse crate::{sys::stat::Mode, NixPath, Result};
113da5c369Sopenharmony_ci#[cfg(any(target_os = "android", target_os = "linux"))]
123da5c369Sopenharmony_ciuse std::ptr; // For splice and copy_file_range
133da5c369Sopenharmony_ci
143da5c369Sopenharmony_ci#[cfg(any(
153da5c369Sopenharmony_ci    target_os = "linux",
163da5c369Sopenharmony_ci    target_os = "android",
173da5c369Sopenharmony_ci    target_os = "emscripten",
183da5c369Sopenharmony_ci    target_os = "fuchsia",
193da5c369Sopenharmony_ci    target_os = "wasi",
203da5c369Sopenharmony_ci    target_env = "uclibc",
213da5c369Sopenharmony_ci    target_os = "freebsd"
223da5c369Sopenharmony_ci))]
233da5c369Sopenharmony_ci#[cfg(feature = "fs")]
243da5c369Sopenharmony_cipub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice};
253da5c369Sopenharmony_ci
263da5c369Sopenharmony_ci#[cfg(not(target_os = "redox"))]
273da5c369Sopenharmony_ci#[cfg(any(feature = "fs", feature = "process"))]
283da5c369Sopenharmony_cilibc_bitflags! {
293da5c369Sopenharmony_ci    #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "process"))))]
303da5c369Sopenharmony_ci    pub struct AtFlags: c_int {
313da5c369Sopenharmony_ci        AT_REMOVEDIR;
323da5c369Sopenharmony_ci        AT_SYMLINK_FOLLOW;
333da5c369Sopenharmony_ci        AT_SYMLINK_NOFOLLOW;
343da5c369Sopenharmony_ci        #[cfg(any(target_os = "android", target_os = "linux"))]
353da5c369Sopenharmony_ci        AT_NO_AUTOMOUNT;
363da5c369Sopenharmony_ci        #[cfg(any(target_os = "android", target_os = "linux"))]
373da5c369Sopenharmony_ci        AT_EMPTY_PATH;
383da5c369Sopenharmony_ci        #[cfg(any(target_os = "illumos", target_os = "solaris"))]
393da5c369Sopenharmony_ci        AT_EACCESS;
403da5c369Sopenharmony_ci    }
413da5c369Sopenharmony_ci}
423da5c369Sopenharmony_ci
433da5c369Sopenharmony_ci#[cfg(any(feature = "fs", feature = "term"))]
443da5c369Sopenharmony_cilibc_bitflags!(
453da5c369Sopenharmony_ci    /// Configuration options for opened files.
463da5c369Sopenharmony_ci    #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "term"))))]
473da5c369Sopenharmony_ci    pub struct OFlag: c_int {
483da5c369Sopenharmony_ci        /// Mask for the access mode of the file.
493da5c369Sopenharmony_ci        O_ACCMODE;
503da5c369Sopenharmony_ci        /// Use alternate I/O semantics.
513da5c369Sopenharmony_ci        #[cfg(target_os = "netbsd")]
523da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
533da5c369Sopenharmony_ci        O_ALT_IO;
543da5c369Sopenharmony_ci        /// Open the file in append-only mode.
553da5c369Sopenharmony_ci        O_APPEND;
563da5c369Sopenharmony_ci        /// Generate a signal when input or output becomes possible.
573da5c369Sopenharmony_ci        #[cfg(not(any(target_os = "illumos", target_os = "solaris", target_os = "haiku")))]
583da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
593da5c369Sopenharmony_ci        O_ASYNC;
603da5c369Sopenharmony_ci        /// Closes the file descriptor once an `execve` call is made.
613da5c369Sopenharmony_ci        ///
623da5c369Sopenharmony_ci        /// Also sets the file offset to the beginning of the file.
633da5c369Sopenharmony_ci        O_CLOEXEC;
643da5c369Sopenharmony_ci        /// Create the file if it does not exist.
653da5c369Sopenharmony_ci        O_CREAT;
663da5c369Sopenharmony_ci        /// Try to minimize cache effects of the I/O for this file.
673da5c369Sopenharmony_ci        #[cfg(any(target_os = "android",
683da5c369Sopenharmony_ci                  target_os = "dragonfly",
693da5c369Sopenharmony_ci                  target_os = "freebsd",
703da5c369Sopenharmony_ci                  target_os = "linux",
713da5c369Sopenharmony_ci                  target_os = "netbsd"))]
723da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
733da5c369Sopenharmony_ci        O_DIRECT;
743da5c369Sopenharmony_ci        /// If the specified path isn't a directory, fail.
753da5c369Sopenharmony_ci        #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
763da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
773da5c369Sopenharmony_ci        O_DIRECTORY;
783da5c369Sopenharmony_ci        /// Implicitly follow each `write()` with an `fdatasync()`.
793da5c369Sopenharmony_ci        #[cfg(any(target_os = "android",
803da5c369Sopenharmony_ci                  target_os = "ios",
813da5c369Sopenharmony_ci                  target_os = "linux",
823da5c369Sopenharmony_ci                  target_os = "macos",
833da5c369Sopenharmony_ci                  target_os = "netbsd",
843da5c369Sopenharmony_ci                  target_os = "openbsd"))]
853da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
863da5c369Sopenharmony_ci        O_DSYNC;
873da5c369Sopenharmony_ci        /// Error out if a file was not created.
883da5c369Sopenharmony_ci        O_EXCL;
893da5c369Sopenharmony_ci        /// Open for execute only.
903da5c369Sopenharmony_ci        #[cfg(target_os = "freebsd")]
913da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
923da5c369Sopenharmony_ci        O_EXEC;
933da5c369Sopenharmony_ci        /// Open with an exclusive file lock.
943da5c369Sopenharmony_ci        #[cfg(any(target_os = "dragonfly",
953da5c369Sopenharmony_ci                  target_os = "freebsd",
963da5c369Sopenharmony_ci                  target_os = "ios",
973da5c369Sopenharmony_ci                  target_os = "macos",
983da5c369Sopenharmony_ci                  target_os = "netbsd",
993da5c369Sopenharmony_ci                  target_os = "openbsd",
1003da5c369Sopenharmony_ci                  target_os = "redox"))]
1013da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
1023da5c369Sopenharmony_ci        O_EXLOCK;
1033da5c369Sopenharmony_ci        /// Same as `O_SYNC`.
1043da5c369Sopenharmony_ci        #[cfg(any(target_os = "dragonfly",
1053da5c369Sopenharmony_ci                  target_os = "freebsd",
1063da5c369Sopenharmony_ci                  target_os = "ios",
1073da5c369Sopenharmony_ci                  all(target_os = "linux", not(any(target_env = "musl", target_env = "ohos"))),
1083da5c369Sopenharmony_ci                  target_os = "macos",
1093da5c369Sopenharmony_ci                  target_os = "netbsd",
1103da5c369Sopenharmony_ci                  target_os = "openbsd",
1113da5c369Sopenharmony_ci                  target_os = "redox"))]
1123da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
1133da5c369Sopenharmony_ci        O_FSYNC;
1143da5c369Sopenharmony_ci        /// Allow files whose sizes can't be represented in an `off_t` to be opened.
1153da5c369Sopenharmony_ci        #[cfg(any(target_os = "android", target_os = "linux"))]
1163da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
1173da5c369Sopenharmony_ci        O_LARGEFILE;
1183da5c369Sopenharmony_ci        /// Do not update the file last access time during `read(2)`s.
1193da5c369Sopenharmony_ci        #[cfg(any(target_os = "android", target_os = "linux"))]
1203da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
1213da5c369Sopenharmony_ci        O_NOATIME;
1223da5c369Sopenharmony_ci        /// Don't attach the device as the process' controlling terminal.
1233da5c369Sopenharmony_ci        #[cfg(not(target_os = "redox"))]
1243da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
1253da5c369Sopenharmony_ci        O_NOCTTY;
1263da5c369Sopenharmony_ci        /// Same as `O_NONBLOCK`.
1273da5c369Sopenharmony_ci        #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
1283da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
1293da5c369Sopenharmony_ci        O_NDELAY;
1303da5c369Sopenharmony_ci        /// `open()` will fail if the given path is a symbolic link.
1313da5c369Sopenharmony_ci        O_NOFOLLOW;
1323da5c369Sopenharmony_ci        /// When possible, open the file in nonblocking mode.
1333da5c369Sopenharmony_ci        O_NONBLOCK;
1343da5c369Sopenharmony_ci        /// Don't deliver `SIGPIPE`.
1353da5c369Sopenharmony_ci        #[cfg(target_os = "netbsd")]
1363da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
1373da5c369Sopenharmony_ci        O_NOSIGPIPE;
1383da5c369Sopenharmony_ci        /// Obtain a file descriptor for low-level access.
1393da5c369Sopenharmony_ci        ///
1403da5c369Sopenharmony_ci        /// The file itself is not opened and other file operations will fail.
1413da5c369Sopenharmony_ci        #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
1423da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
1433da5c369Sopenharmony_ci        O_PATH;
1443da5c369Sopenharmony_ci        /// Only allow reading.
1453da5c369Sopenharmony_ci        ///
1463da5c369Sopenharmony_ci        /// This should not be combined with `O_WRONLY` or `O_RDWR`.
1473da5c369Sopenharmony_ci        O_RDONLY;
1483da5c369Sopenharmony_ci        /// Allow both reading and writing.
1493da5c369Sopenharmony_ci        ///
1503da5c369Sopenharmony_ci        /// This should not be combined with `O_WRONLY` or `O_RDONLY`.
1513da5c369Sopenharmony_ci        O_RDWR;
1523da5c369Sopenharmony_ci        /// Similar to `O_DSYNC` but applies to `read`s instead.
1533da5c369Sopenharmony_ci        #[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))]
1543da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
1553da5c369Sopenharmony_ci        O_RSYNC;
1563da5c369Sopenharmony_ci        /// Skip search permission checks.
1573da5c369Sopenharmony_ci        #[cfg(target_os = "netbsd")]
1583da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
1593da5c369Sopenharmony_ci        O_SEARCH;
1603da5c369Sopenharmony_ci        /// Open with a shared file lock.
1613da5c369Sopenharmony_ci        #[cfg(any(target_os = "dragonfly",
1623da5c369Sopenharmony_ci                  target_os = "freebsd",
1633da5c369Sopenharmony_ci                  target_os = "ios",
1643da5c369Sopenharmony_ci                  target_os = "macos",
1653da5c369Sopenharmony_ci                  target_os = "netbsd",
1663da5c369Sopenharmony_ci                  target_os = "openbsd",
1673da5c369Sopenharmony_ci                  target_os = "redox"))]
1683da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
1693da5c369Sopenharmony_ci        O_SHLOCK;
1703da5c369Sopenharmony_ci        /// Implicitly follow each `write()` with an `fsync()`.
1713da5c369Sopenharmony_ci        #[cfg(not(target_os = "redox"))]
1723da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
1733da5c369Sopenharmony_ci        O_SYNC;
1743da5c369Sopenharmony_ci        /// Create an unnamed temporary file.
1753da5c369Sopenharmony_ci        #[cfg(any(target_os = "android", target_os = "linux"))]
1763da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
1773da5c369Sopenharmony_ci        O_TMPFILE;
1783da5c369Sopenharmony_ci        /// Truncate an existing regular file to 0 length if it allows writing.
1793da5c369Sopenharmony_ci        O_TRUNC;
1803da5c369Sopenharmony_ci        /// Restore default TTY attributes.
1813da5c369Sopenharmony_ci        #[cfg(target_os = "freebsd")]
1823da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
1833da5c369Sopenharmony_ci        O_TTY_INIT;
1843da5c369Sopenharmony_ci        /// Only allow writing.
1853da5c369Sopenharmony_ci        ///
1863da5c369Sopenharmony_ci        /// This should not be combined with `O_RDONLY` or `O_RDWR`.
1873da5c369Sopenharmony_ci        O_WRONLY;
1883da5c369Sopenharmony_ci    }
1893da5c369Sopenharmony_ci);
1903da5c369Sopenharmony_ci
1913da5c369Sopenharmony_cifeature! {
1923da5c369Sopenharmony_ci#![feature = "fs"]
1933da5c369Sopenharmony_ci
1943da5c369Sopenharmony_ci// The conversion is not identical on all operating systems.
1953da5c369Sopenharmony_ci#[allow(clippy::useless_conversion)]
1963da5c369Sopenharmony_cipub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
1973da5c369Sopenharmony_ci    let fd = path.with_nix_path(|cstr| {
1983da5c369Sopenharmony_ci        unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
1993da5c369Sopenharmony_ci    })?;
2003da5c369Sopenharmony_ci
2013da5c369Sopenharmony_ci    Errno::result(fd)
2023da5c369Sopenharmony_ci}
2033da5c369Sopenharmony_ci
2043da5c369Sopenharmony_ci// The conversion is not identical on all operating systems.
2053da5c369Sopenharmony_ci#[allow(clippy::useless_conversion)]
2063da5c369Sopenharmony_ci#[cfg(not(target_os = "redox"))]
2073da5c369Sopenharmony_cipub fn openat<P: ?Sized + NixPath>(
2083da5c369Sopenharmony_ci    dirfd: RawFd,
2093da5c369Sopenharmony_ci    path: &P,
2103da5c369Sopenharmony_ci    oflag: OFlag,
2113da5c369Sopenharmony_ci    mode: Mode,
2123da5c369Sopenharmony_ci) -> Result<RawFd> {
2133da5c369Sopenharmony_ci    let fd = path.with_nix_path(|cstr| {
2143da5c369Sopenharmony_ci        unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
2153da5c369Sopenharmony_ci    })?;
2163da5c369Sopenharmony_ci    Errno::result(fd)
2173da5c369Sopenharmony_ci}
2183da5c369Sopenharmony_ci
2193da5c369Sopenharmony_ci#[cfg(not(target_os = "redox"))]
2203da5c369Sopenharmony_cipub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
2213da5c369Sopenharmony_ci    old_dirfd: Option<RawFd>,
2223da5c369Sopenharmony_ci    old_path: &P1,
2233da5c369Sopenharmony_ci    new_dirfd: Option<RawFd>,
2243da5c369Sopenharmony_ci    new_path: &P2,
2253da5c369Sopenharmony_ci) -> Result<()> {
2263da5c369Sopenharmony_ci    let res = old_path.with_nix_path(|old_cstr| {
2273da5c369Sopenharmony_ci        new_path.with_nix_path(|new_cstr| unsafe {
2283da5c369Sopenharmony_ci            libc::renameat(
2293da5c369Sopenharmony_ci                at_rawfd(old_dirfd),
2303da5c369Sopenharmony_ci                old_cstr.as_ptr(),
2313da5c369Sopenharmony_ci                at_rawfd(new_dirfd),
2323da5c369Sopenharmony_ci                new_cstr.as_ptr(),
2333da5c369Sopenharmony_ci            )
2343da5c369Sopenharmony_ci        })
2353da5c369Sopenharmony_ci    })??;
2363da5c369Sopenharmony_ci    Errno::result(res).map(drop)
2373da5c369Sopenharmony_ci}
2383da5c369Sopenharmony_ci}
2393da5c369Sopenharmony_ci
2403da5c369Sopenharmony_ci#[cfg(all(target_os = "linux", target_env = "gnu",))]
2413da5c369Sopenharmony_ci#[cfg(feature = "fs")]
2423da5c369Sopenharmony_cilibc_bitflags! {
2433da5c369Sopenharmony_ci    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
2443da5c369Sopenharmony_ci    pub struct RenameFlags: u32 {
2453da5c369Sopenharmony_ci        RENAME_EXCHANGE;
2463da5c369Sopenharmony_ci        RENAME_NOREPLACE;
2473da5c369Sopenharmony_ci        RENAME_WHITEOUT;
2483da5c369Sopenharmony_ci    }
2493da5c369Sopenharmony_ci}
2503da5c369Sopenharmony_ci
2513da5c369Sopenharmony_cifeature! {
2523da5c369Sopenharmony_ci#![feature = "fs"]
2533da5c369Sopenharmony_ci#[cfg(all(
2543da5c369Sopenharmony_ci    target_os = "linux",
2553da5c369Sopenharmony_ci    target_env = "gnu",
2563da5c369Sopenharmony_ci))]
2573da5c369Sopenharmony_cipub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
2583da5c369Sopenharmony_ci    old_dirfd: Option<RawFd>,
2593da5c369Sopenharmony_ci    old_path: &P1,
2603da5c369Sopenharmony_ci    new_dirfd: Option<RawFd>,
2613da5c369Sopenharmony_ci    new_path: &P2,
2623da5c369Sopenharmony_ci    flags: RenameFlags,
2633da5c369Sopenharmony_ci) -> Result<()> {
2643da5c369Sopenharmony_ci    let res = old_path.with_nix_path(|old_cstr| {
2653da5c369Sopenharmony_ci        new_path.with_nix_path(|new_cstr| unsafe {
2663da5c369Sopenharmony_ci            libc::renameat2(
2673da5c369Sopenharmony_ci                at_rawfd(old_dirfd),
2683da5c369Sopenharmony_ci                old_cstr.as_ptr(),
2693da5c369Sopenharmony_ci                at_rawfd(new_dirfd),
2703da5c369Sopenharmony_ci                new_cstr.as_ptr(),
2713da5c369Sopenharmony_ci                flags.bits(),
2723da5c369Sopenharmony_ci            )
2733da5c369Sopenharmony_ci        })
2743da5c369Sopenharmony_ci    })??;
2753da5c369Sopenharmony_ci    Errno::result(res).map(drop)
2763da5c369Sopenharmony_ci}
2773da5c369Sopenharmony_ci
2783da5c369Sopenharmony_cifn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> {
2793da5c369Sopenharmony_ci    unsafe { v.set_len(len as usize) }
2803da5c369Sopenharmony_ci    v.shrink_to_fit();
2813da5c369Sopenharmony_ci    Ok(OsString::from_vec(v.to_vec()))
2823da5c369Sopenharmony_ci}
2833da5c369Sopenharmony_ci
2843da5c369Sopenharmony_cifn readlink_maybe_at<P: ?Sized + NixPath>(
2853da5c369Sopenharmony_ci    dirfd: Option<RawFd>,
2863da5c369Sopenharmony_ci    path: &P,
2873da5c369Sopenharmony_ci    v: &mut Vec<u8>,
2883da5c369Sopenharmony_ci) -> Result<libc::ssize_t> {
2893da5c369Sopenharmony_ci    path.with_nix_path(|cstr| unsafe {
2903da5c369Sopenharmony_ci        match dirfd {
2913da5c369Sopenharmony_ci            #[cfg(target_os = "redox")]
2923da5c369Sopenharmony_ci            Some(_) => unreachable!(),
2933da5c369Sopenharmony_ci            #[cfg(not(target_os = "redox"))]
2943da5c369Sopenharmony_ci            Some(dirfd) => libc::readlinkat(
2953da5c369Sopenharmony_ci                dirfd,
2963da5c369Sopenharmony_ci                cstr.as_ptr(),
2973da5c369Sopenharmony_ci                v.as_mut_ptr() as *mut c_char,
2983da5c369Sopenharmony_ci                v.capacity() as size_t,
2993da5c369Sopenharmony_ci            ),
3003da5c369Sopenharmony_ci            None => libc::readlink(
3013da5c369Sopenharmony_ci                cstr.as_ptr(),
3023da5c369Sopenharmony_ci                v.as_mut_ptr() as *mut c_char,
3033da5c369Sopenharmony_ci                v.capacity() as size_t,
3043da5c369Sopenharmony_ci            ),
3053da5c369Sopenharmony_ci        }
3063da5c369Sopenharmony_ci    })
3073da5c369Sopenharmony_ci}
3083da5c369Sopenharmony_ci
3093da5c369Sopenharmony_cifn inner_readlink<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P) -> Result<OsString> {
3103da5c369Sopenharmony_ci    let mut v = Vec::with_capacity(libc::PATH_MAX as usize);
3113da5c369Sopenharmony_ci    // simple case: result is strictly less than `PATH_MAX`
3123da5c369Sopenharmony_ci    let res = readlink_maybe_at(dirfd, path, &mut v)?;
3133da5c369Sopenharmony_ci    let len = Errno::result(res)?;
3143da5c369Sopenharmony_ci    debug_assert!(len >= 0);
3153da5c369Sopenharmony_ci    if (len as usize) < v.capacity() {
3163da5c369Sopenharmony_ci        return wrap_readlink_result(v, res);
3173da5c369Sopenharmony_ci    }
3183da5c369Sopenharmony_ci    // Uh oh, the result is too long...
3193da5c369Sopenharmony_ci    // Let's try to ask lstat how many bytes to allocate.
3203da5c369Sopenharmony_ci    let reported_size = match dirfd {
3213da5c369Sopenharmony_ci        #[cfg(target_os = "redox")]
3223da5c369Sopenharmony_ci        Some(_) => unreachable!(),
3233da5c369Sopenharmony_ci        #[cfg(any(target_os = "android", target_os = "linux"))]
3243da5c369Sopenharmony_ci        Some(dirfd) => {
3253da5c369Sopenharmony_ci            let flags = if path.is_empty() { AtFlags::AT_EMPTY_PATH } else { AtFlags::empty() };
3263da5c369Sopenharmony_ci            super::sys::stat::fstatat(dirfd, path, flags | AtFlags::AT_SYMLINK_NOFOLLOW)
3273da5c369Sopenharmony_ci        },
3283da5c369Sopenharmony_ci        #[cfg(not(any(target_os = "android", target_os = "linux", target_os = "redox")))]
3293da5c369Sopenharmony_ci        Some(dirfd) => super::sys::stat::fstatat(dirfd, path, AtFlags::AT_SYMLINK_NOFOLLOW),
3303da5c369Sopenharmony_ci        None => super::sys::stat::lstat(path)
3313da5c369Sopenharmony_ci    }
3323da5c369Sopenharmony_ci        .map(|x| x.st_size)
3333da5c369Sopenharmony_ci        .unwrap_or(0);
3343da5c369Sopenharmony_ci    let mut try_size = if reported_size > 0 {
3353da5c369Sopenharmony_ci        // Note: even if `lstat`'s apparently valid answer turns out to be
3363da5c369Sopenharmony_ci        // wrong, we will still read the full symlink no matter what.
3373da5c369Sopenharmony_ci        reported_size as usize + 1
3383da5c369Sopenharmony_ci    } else {
3393da5c369Sopenharmony_ci        // If lstat doesn't cooperate, or reports an error, be a little less
3403da5c369Sopenharmony_ci        // precise.
3413da5c369Sopenharmony_ci        (libc::PATH_MAX as usize).max(128) << 1
3423da5c369Sopenharmony_ci    };
3433da5c369Sopenharmony_ci    loop {
3443da5c369Sopenharmony_ci        v.reserve_exact(try_size);
3453da5c369Sopenharmony_ci        let res = readlink_maybe_at(dirfd, path, &mut v)?;
3463da5c369Sopenharmony_ci        let len = Errno::result(res)?;
3473da5c369Sopenharmony_ci        debug_assert!(len >= 0);
3483da5c369Sopenharmony_ci        if (len as usize) < v.capacity() {
3493da5c369Sopenharmony_ci            break wrap_readlink_result(v, res);
3503da5c369Sopenharmony_ci        } else {
3513da5c369Sopenharmony_ci            // Ugh! Still not big enough!
3523da5c369Sopenharmony_ci            match try_size.checked_shl(1) {
3533da5c369Sopenharmony_ci                Some(next_size) => try_size = next_size,
3543da5c369Sopenharmony_ci                // It's absurd that this would happen, but handle it sanely
3553da5c369Sopenharmony_ci                // anyway.
3563da5c369Sopenharmony_ci                None => break Err(Errno::ENAMETOOLONG),
3573da5c369Sopenharmony_ci            }
3583da5c369Sopenharmony_ci        }
3593da5c369Sopenharmony_ci    }
3603da5c369Sopenharmony_ci}
3613da5c369Sopenharmony_ci
3623da5c369Sopenharmony_cipub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
3633da5c369Sopenharmony_ci    inner_readlink(None, path)
3643da5c369Sopenharmony_ci}
3653da5c369Sopenharmony_ci
3663da5c369Sopenharmony_ci#[cfg(not(target_os = "redox"))]
3673da5c369Sopenharmony_cipub fn readlinkat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P) -> Result<OsString> {
3683da5c369Sopenharmony_ci    inner_readlink(Some(dirfd), path)
3693da5c369Sopenharmony_ci}
3703da5c369Sopenharmony_ci
3713da5c369Sopenharmony_ci/// Computes the raw fd consumed by a function of the form `*at`.
3723da5c369Sopenharmony_ci#[cfg(not(target_os = "redox"))]
3733da5c369Sopenharmony_cipub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int {
3743da5c369Sopenharmony_ci    match fd {
3753da5c369Sopenharmony_ci        None => libc::AT_FDCWD,
3763da5c369Sopenharmony_ci        Some(fd) => fd,
3773da5c369Sopenharmony_ci    }
3783da5c369Sopenharmony_ci}
3793da5c369Sopenharmony_ci}
3803da5c369Sopenharmony_ci
3813da5c369Sopenharmony_ci#[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
3823da5c369Sopenharmony_ci#[cfg(feature = "fs")]
3833da5c369Sopenharmony_cilibc_bitflags!(
3843da5c369Sopenharmony_ci    /// Additional flags for file sealing, which allows for limiting operations on a file.
3853da5c369Sopenharmony_ci    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
3863da5c369Sopenharmony_ci    pub struct SealFlag: c_int {
3873da5c369Sopenharmony_ci        /// Prevents further calls to `fcntl()` with `F_ADD_SEALS`.
3883da5c369Sopenharmony_ci        F_SEAL_SEAL;
3893da5c369Sopenharmony_ci        /// The file cannot be reduced in size.
3903da5c369Sopenharmony_ci        F_SEAL_SHRINK;
3913da5c369Sopenharmony_ci        /// The size of the file cannot be increased.
3923da5c369Sopenharmony_ci        F_SEAL_GROW;
3933da5c369Sopenharmony_ci        /// The file contents cannot be modified.
3943da5c369Sopenharmony_ci        F_SEAL_WRITE;
3953da5c369Sopenharmony_ci    }
3963da5c369Sopenharmony_ci);
3973da5c369Sopenharmony_ci
3983da5c369Sopenharmony_ci#[cfg(feature = "fs")]
3993da5c369Sopenharmony_cilibc_bitflags!(
4003da5c369Sopenharmony_ci    /// Additional configuration flags for `fcntl`'s `F_SETFD`.
4013da5c369Sopenharmony_ci    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
4023da5c369Sopenharmony_ci    pub struct FdFlag: c_int {
4033da5c369Sopenharmony_ci        /// The file descriptor will automatically be closed during a successful `execve(2)`.
4043da5c369Sopenharmony_ci        FD_CLOEXEC;
4053da5c369Sopenharmony_ci    }
4063da5c369Sopenharmony_ci);
4073da5c369Sopenharmony_ci
4083da5c369Sopenharmony_cifeature! {
4093da5c369Sopenharmony_ci#![feature = "fs"]
4103da5c369Sopenharmony_ci
4113da5c369Sopenharmony_ci#[cfg(not(target_os = "redox"))]
4123da5c369Sopenharmony_ci#[derive(Debug, Eq, Hash, PartialEq)]
4133da5c369Sopenharmony_ci#[non_exhaustive]
4143da5c369Sopenharmony_cipub enum FcntlArg<'a> {
4153da5c369Sopenharmony_ci    F_DUPFD(RawFd),
4163da5c369Sopenharmony_ci    F_DUPFD_CLOEXEC(RawFd),
4173da5c369Sopenharmony_ci    F_GETFD,
4183da5c369Sopenharmony_ci    F_SETFD(FdFlag), // FD_FLAGS
4193da5c369Sopenharmony_ci    F_GETFL,
4203da5c369Sopenharmony_ci    F_SETFL(OFlag), // O_NONBLOCK
4213da5c369Sopenharmony_ci    F_SETLK(&'a libc::flock),
4223da5c369Sopenharmony_ci    F_SETLKW(&'a libc::flock),
4233da5c369Sopenharmony_ci    F_GETLK(&'a mut libc::flock),
4243da5c369Sopenharmony_ci    #[cfg(any(target_os = "linux", target_os = "android"))]
4253da5c369Sopenharmony_ci    F_OFD_SETLK(&'a libc::flock),
4263da5c369Sopenharmony_ci    #[cfg(any(target_os = "linux", target_os = "android"))]
4273da5c369Sopenharmony_ci    F_OFD_SETLKW(&'a libc::flock),
4283da5c369Sopenharmony_ci    #[cfg(any(target_os = "linux", target_os = "android"))]
4293da5c369Sopenharmony_ci    F_OFD_GETLK(&'a mut libc::flock),
4303da5c369Sopenharmony_ci    #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
4313da5c369Sopenharmony_ci    F_ADD_SEALS(SealFlag),
4323da5c369Sopenharmony_ci    #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
4333da5c369Sopenharmony_ci    F_GET_SEALS,
4343da5c369Sopenharmony_ci    #[cfg(any(target_os = "macos", target_os = "ios"))]
4353da5c369Sopenharmony_ci    F_FULLFSYNC,
4363da5c369Sopenharmony_ci    #[cfg(any(target_os = "linux", target_os = "android"))]
4373da5c369Sopenharmony_ci    F_GETPIPE_SZ,
4383da5c369Sopenharmony_ci    #[cfg(any(target_os = "linux", target_os = "android"))]
4393da5c369Sopenharmony_ci    F_SETPIPE_SZ(c_int),
4403da5c369Sopenharmony_ci    // TODO: Rest of flags
4413da5c369Sopenharmony_ci}
4423da5c369Sopenharmony_ci
4433da5c369Sopenharmony_ci#[cfg(target_os = "redox")]
4443da5c369Sopenharmony_ci#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
4453da5c369Sopenharmony_ci#[non_exhaustive]
4463da5c369Sopenharmony_cipub enum FcntlArg {
4473da5c369Sopenharmony_ci    F_DUPFD(RawFd),
4483da5c369Sopenharmony_ci    F_DUPFD_CLOEXEC(RawFd),
4493da5c369Sopenharmony_ci    F_GETFD,
4503da5c369Sopenharmony_ci    F_SETFD(FdFlag), // FD_FLAGS
4513da5c369Sopenharmony_ci    F_GETFL,
4523da5c369Sopenharmony_ci    F_SETFL(OFlag), // O_NONBLOCK
4533da5c369Sopenharmony_ci}
4543da5c369Sopenharmony_cipub use self::FcntlArg::*;
4553da5c369Sopenharmony_ci
4563da5c369Sopenharmony_ci// TODO: Figure out how to handle value fcntl returns
4573da5c369Sopenharmony_cipub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
4583da5c369Sopenharmony_ci    let res = unsafe {
4593da5c369Sopenharmony_ci        match arg {
4603da5c369Sopenharmony_ci            F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
4613da5c369Sopenharmony_ci            F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd),
4623da5c369Sopenharmony_ci            F_GETFD => libc::fcntl(fd, libc::F_GETFD),
4633da5c369Sopenharmony_ci            F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()),
4643da5c369Sopenharmony_ci            F_GETFL => libc::fcntl(fd, libc::F_GETFL),
4653da5c369Sopenharmony_ci            F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()),
4663da5c369Sopenharmony_ci            #[cfg(not(target_os = "redox"))]
4673da5c369Sopenharmony_ci            F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock),
4683da5c369Sopenharmony_ci            #[cfg(not(target_os = "redox"))]
4693da5c369Sopenharmony_ci            F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
4703da5c369Sopenharmony_ci            #[cfg(not(target_os = "redox"))]
4713da5c369Sopenharmony_ci            F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
4723da5c369Sopenharmony_ci            #[cfg(any(target_os = "android", target_os = "linux"))]
4733da5c369Sopenharmony_ci            F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock),
4743da5c369Sopenharmony_ci            #[cfg(any(target_os = "android", target_os = "linux"))]
4753da5c369Sopenharmony_ci            F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock),
4763da5c369Sopenharmony_ci            #[cfg(any(target_os = "android", target_os = "linux"))]
4773da5c369Sopenharmony_ci            F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock),
4783da5c369Sopenharmony_ci            #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
4793da5c369Sopenharmony_ci            F_ADD_SEALS(flag) => libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()),
4803da5c369Sopenharmony_ci            #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
4813da5c369Sopenharmony_ci            F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS),
4823da5c369Sopenharmony_ci            #[cfg(any(target_os = "macos", target_os = "ios"))]
4833da5c369Sopenharmony_ci            F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC),
4843da5c369Sopenharmony_ci            #[cfg(any(target_os = "linux", target_os = "android"))]
4853da5c369Sopenharmony_ci            F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ),
4863da5c369Sopenharmony_ci            #[cfg(any(target_os = "linux", target_os = "android"))]
4873da5c369Sopenharmony_ci            F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size),
4883da5c369Sopenharmony_ci        }
4893da5c369Sopenharmony_ci    };
4903da5c369Sopenharmony_ci
4913da5c369Sopenharmony_ci    Errno::result(res)
4923da5c369Sopenharmony_ci}
4933da5c369Sopenharmony_ci
4943da5c369Sopenharmony_ci// TODO: convert to libc_enum
4953da5c369Sopenharmony_ci#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
4963da5c369Sopenharmony_ci#[non_exhaustive]
4973da5c369Sopenharmony_cipub enum FlockArg {
4983da5c369Sopenharmony_ci    LockShared,
4993da5c369Sopenharmony_ci    LockExclusive,
5003da5c369Sopenharmony_ci    Unlock,
5013da5c369Sopenharmony_ci    LockSharedNonblock,
5023da5c369Sopenharmony_ci    LockExclusiveNonblock,
5033da5c369Sopenharmony_ci    UnlockNonblock,
5043da5c369Sopenharmony_ci}
5053da5c369Sopenharmony_ci
5063da5c369Sopenharmony_ci#[cfg(not(target_os = "redox"))]
5073da5c369Sopenharmony_cipub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
5083da5c369Sopenharmony_ci    use self::FlockArg::*;
5093da5c369Sopenharmony_ci
5103da5c369Sopenharmony_ci    let res = unsafe {
5113da5c369Sopenharmony_ci        match arg {
5123da5c369Sopenharmony_ci            LockShared => libc::flock(fd, libc::LOCK_SH),
5133da5c369Sopenharmony_ci            LockExclusive => libc::flock(fd, libc::LOCK_EX),
5143da5c369Sopenharmony_ci            Unlock => libc::flock(fd, libc::LOCK_UN),
5153da5c369Sopenharmony_ci            LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB),
5163da5c369Sopenharmony_ci            LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB),
5173da5c369Sopenharmony_ci            UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB),
5183da5c369Sopenharmony_ci        }
5193da5c369Sopenharmony_ci    };
5203da5c369Sopenharmony_ci
5213da5c369Sopenharmony_ci    Errno::result(res).map(drop)
5223da5c369Sopenharmony_ci}
5233da5c369Sopenharmony_ci}
5243da5c369Sopenharmony_ci
5253da5c369Sopenharmony_ci#[cfg(any(target_os = "android", target_os = "linux"))]
5263da5c369Sopenharmony_ci#[cfg(feature = "zerocopy")]
5273da5c369Sopenharmony_cilibc_bitflags! {
5283da5c369Sopenharmony_ci    /// Additional flags to `splice` and friends.
5293da5c369Sopenharmony_ci    #[cfg_attr(docsrs, doc(cfg(feature = "zerocopy")))]
5303da5c369Sopenharmony_ci    pub struct SpliceFFlags: c_uint {
5313da5c369Sopenharmony_ci        /// Request that pages be moved instead of copied.
5323da5c369Sopenharmony_ci        ///
5333da5c369Sopenharmony_ci        /// Not applicable to `vmsplice`.
5343da5c369Sopenharmony_ci        SPLICE_F_MOVE;
5353da5c369Sopenharmony_ci        /// Do not block on I/O.
5363da5c369Sopenharmony_ci        SPLICE_F_NONBLOCK;
5373da5c369Sopenharmony_ci        /// Hint that more data will be coming in a subsequent splice.
5383da5c369Sopenharmony_ci        ///
5393da5c369Sopenharmony_ci        /// Not applicable to `vmsplice`.
5403da5c369Sopenharmony_ci        SPLICE_F_MORE;
5413da5c369Sopenharmony_ci        /// Gift the user pages to the kernel.
5423da5c369Sopenharmony_ci        ///
5433da5c369Sopenharmony_ci        /// Not applicable to `splice`.
5443da5c369Sopenharmony_ci        SPLICE_F_GIFT;
5453da5c369Sopenharmony_ci    }
5463da5c369Sopenharmony_ci}
5473da5c369Sopenharmony_ci
5483da5c369Sopenharmony_cifeature! {
5493da5c369Sopenharmony_ci#![feature = "zerocopy"]
5503da5c369Sopenharmony_ci
5513da5c369Sopenharmony_ci/// Copy a range of data from one file to another
5523da5c369Sopenharmony_ci///
5533da5c369Sopenharmony_ci/// The `copy_file_range` system call performs an in-kernel copy between
5543da5c369Sopenharmony_ci/// file descriptors `fd_in` and `fd_out` without the additional cost of
5553da5c369Sopenharmony_ci/// transferring data from the kernel to user space and then back into the
5563da5c369Sopenharmony_ci/// kernel. It copies up to `len` bytes of data from file descriptor `fd_in` to
5573da5c369Sopenharmony_ci/// file descriptor `fd_out`, overwriting any data that exists within the
5583da5c369Sopenharmony_ci/// requested range of the target file.
5593da5c369Sopenharmony_ci///
5603da5c369Sopenharmony_ci/// If the `off_in` and/or `off_out` arguments are used, the values
5613da5c369Sopenharmony_ci/// will be mutated to reflect the new position within the file after
5623da5c369Sopenharmony_ci/// copying. If they are not used, the relevant filedescriptors will be seeked
5633da5c369Sopenharmony_ci/// to the new position.
5643da5c369Sopenharmony_ci///
5653da5c369Sopenharmony_ci/// On successful completion the number of bytes actually copied will be
5663da5c369Sopenharmony_ci/// returned.
5673da5c369Sopenharmony_ci#[cfg(any(target_os = "android", target_os = "linux"))]
5683da5c369Sopenharmony_cipub fn copy_file_range(
5693da5c369Sopenharmony_ci    fd_in: RawFd,
5703da5c369Sopenharmony_ci    off_in: Option<&mut libc::loff_t>,
5713da5c369Sopenharmony_ci    fd_out: RawFd,
5723da5c369Sopenharmony_ci    off_out: Option<&mut libc::loff_t>,
5733da5c369Sopenharmony_ci    len: usize,
5743da5c369Sopenharmony_ci) -> Result<usize> {
5753da5c369Sopenharmony_ci    let off_in = off_in
5763da5c369Sopenharmony_ci        .map(|offset| offset as *mut libc::loff_t)
5773da5c369Sopenharmony_ci        .unwrap_or(ptr::null_mut());
5783da5c369Sopenharmony_ci    let off_out = off_out
5793da5c369Sopenharmony_ci        .map(|offset| offset as *mut libc::loff_t)
5803da5c369Sopenharmony_ci        .unwrap_or(ptr::null_mut());
5813da5c369Sopenharmony_ci
5823da5c369Sopenharmony_ci    let ret = unsafe {
5833da5c369Sopenharmony_ci        libc::syscall(
5843da5c369Sopenharmony_ci            libc::SYS_copy_file_range,
5853da5c369Sopenharmony_ci            fd_in,
5863da5c369Sopenharmony_ci            off_in,
5873da5c369Sopenharmony_ci            fd_out,
5883da5c369Sopenharmony_ci            off_out,
5893da5c369Sopenharmony_ci            len,
5903da5c369Sopenharmony_ci            0,
5913da5c369Sopenharmony_ci        )
5923da5c369Sopenharmony_ci    };
5933da5c369Sopenharmony_ci    Errno::result(ret).map(|r| r as usize)
5943da5c369Sopenharmony_ci}
5953da5c369Sopenharmony_ci
5963da5c369Sopenharmony_ci#[cfg(any(target_os = "linux", target_os = "android"))]
5973da5c369Sopenharmony_cipub fn splice(
5983da5c369Sopenharmony_ci    fd_in: RawFd,
5993da5c369Sopenharmony_ci    off_in: Option<&mut libc::loff_t>,
6003da5c369Sopenharmony_ci    fd_out: RawFd,
6013da5c369Sopenharmony_ci    off_out: Option<&mut libc::loff_t>,
6023da5c369Sopenharmony_ci    len: usize,
6033da5c369Sopenharmony_ci    flags: SpliceFFlags,
6043da5c369Sopenharmony_ci) -> Result<usize> {
6053da5c369Sopenharmony_ci    let off_in = off_in
6063da5c369Sopenharmony_ci        .map(|offset| offset as *mut libc::loff_t)
6073da5c369Sopenharmony_ci        .unwrap_or(ptr::null_mut());
6083da5c369Sopenharmony_ci    let off_out = off_out
6093da5c369Sopenharmony_ci        .map(|offset| offset as *mut libc::loff_t)
6103da5c369Sopenharmony_ci        .unwrap_or(ptr::null_mut());
6113da5c369Sopenharmony_ci
6123da5c369Sopenharmony_ci    let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) };
6133da5c369Sopenharmony_ci    Errno::result(ret).map(|r| r as usize)
6143da5c369Sopenharmony_ci}
6153da5c369Sopenharmony_ci
6163da5c369Sopenharmony_ci#[cfg(any(target_os = "linux", target_os = "android"))]
6173da5c369Sopenharmony_cipub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> {
6183da5c369Sopenharmony_ci    let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) };
6193da5c369Sopenharmony_ci    Errno::result(ret).map(|r| r as usize)
6203da5c369Sopenharmony_ci}
6213da5c369Sopenharmony_ci
6223da5c369Sopenharmony_ci#[cfg(any(target_os = "linux", target_os = "android"))]
6233da5c369Sopenharmony_cipub fn vmsplice(
6243da5c369Sopenharmony_ci    fd: RawFd,
6253da5c369Sopenharmony_ci    iov: &[std::io::IoSlice<'_>],
6263da5c369Sopenharmony_ci    flags: SpliceFFlags
6273da5c369Sopenharmony_ci    ) -> Result<usize>
6283da5c369Sopenharmony_ci{
6293da5c369Sopenharmony_ci    let ret = unsafe {
6303da5c369Sopenharmony_ci        libc::vmsplice(
6313da5c369Sopenharmony_ci            fd,
6323da5c369Sopenharmony_ci            iov.as_ptr() as *const libc::iovec,
6333da5c369Sopenharmony_ci            iov.len(),
6343da5c369Sopenharmony_ci            flags.bits(),
6353da5c369Sopenharmony_ci        )
6363da5c369Sopenharmony_ci    };
6373da5c369Sopenharmony_ci    Errno::result(ret).map(|r| r as usize)
6383da5c369Sopenharmony_ci}
6393da5c369Sopenharmony_ci}
6403da5c369Sopenharmony_ci
6413da5c369Sopenharmony_ci#[cfg(any(target_os = "linux"))]
6423da5c369Sopenharmony_ci#[cfg(feature = "fs")]
6433da5c369Sopenharmony_cilibc_bitflags!(
6443da5c369Sopenharmony_ci    /// Mode argument flags for fallocate determining operation performed on a given range.
6453da5c369Sopenharmony_ci    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
6463da5c369Sopenharmony_ci    pub struct FallocateFlags: c_int {
6473da5c369Sopenharmony_ci        /// File size is not changed.
6483da5c369Sopenharmony_ci        ///
6493da5c369Sopenharmony_ci        /// offset + len can be greater than file size.
6503da5c369Sopenharmony_ci        FALLOC_FL_KEEP_SIZE;
6513da5c369Sopenharmony_ci        /// Deallocates space by creating a hole.
6523da5c369Sopenharmony_ci        ///
6533da5c369Sopenharmony_ci        /// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes.
6543da5c369Sopenharmony_ci        FALLOC_FL_PUNCH_HOLE;
6553da5c369Sopenharmony_ci        /// Removes byte range from a file without leaving a hole.
6563da5c369Sopenharmony_ci        ///
6573da5c369Sopenharmony_ci        /// Byte range to collapse starts at offset and continues for len bytes.
6583da5c369Sopenharmony_ci        FALLOC_FL_COLLAPSE_RANGE;
6593da5c369Sopenharmony_ci        /// Zeroes space in specified byte range.
6603da5c369Sopenharmony_ci        ///
6613da5c369Sopenharmony_ci        /// Byte range starts at offset and continues for len bytes.
6623da5c369Sopenharmony_ci        FALLOC_FL_ZERO_RANGE;
6633da5c369Sopenharmony_ci        /// Increases file space by inserting a hole within the file size.
6643da5c369Sopenharmony_ci        ///
6653da5c369Sopenharmony_ci        /// Does not overwrite existing data. Hole starts at offset and continues for len bytes.
6663da5c369Sopenharmony_ci        FALLOC_FL_INSERT_RANGE;
6673da5c369Sopenharmony_ci        /// Shared file data extants are made private to the file.
6683da5c369Sopenharmony_ci        ///
6693da5c369Sopenharmony_ci        /// Gaurantees that a subsequent write will not fail due to lack of space.
6703da5c369Sopenharmony_ci        FALLOC_FL_UNSHARE_RANGE;
6713da5c369Sopenharmony_ci    }
6723da5c369Sopenharmony_ci);
6733da5c369Sopenharmony_ci
6743da5c369Sopenharmony_cifeature! {
6753da5c369Sopenharmony_ci#![feature = "fs"]
6763da5c369Sopenharmony_ci
6773da5c369Sopenharmony_ci/// Manipulates file space.
6783da5c369Sopenharmony_ci///
6793da5c369Sopenharmony_ci/// Allows the caller to directly manipulate the allocated disk space for the
6803da5c369Sopenharmony_ci/// file referred to by fd.
6813da5c369Sopenharmony_ci#[cfg(any(target_os = "linux"))]
6823da5c369Sopenharmony_ci#[cfg(feature = "fs")]
6833da5c369Sopenharmony_cipub fn fallocate(
6843da5c369Sopenharmony_ci    fd: RawFd,
6853da5c369Sopenharmony_ci    mode: FallocateFlags,
6863da5c369Sopenharmony_ci    offset: libc::off_t,
6873da5c369Sopenharmony_ci    len: libc::off_t,
6883da5c369Sopenharmony_ci) -> Result<()> {
6893da5c369Sopenharmony_ci    let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) };
6903da5c369Sopenharmony_ci    Errno::result(res).map(drop)
6913da5c369Sopenharmony_ci}
6923da5c369Sopenharmony_ci
6933da5c369Sopenharmony_ci/// Argument to [`fspacectl`] describing the range to zero.  The first member is
6943da5c369Sopenharmony_ci/// the file offset, and the second is the length of the region.
6953da5c369Sopenharmony_ci#[cfg(any(target_os = "freebsd"))]
6963da5c369Sopenharmony_ci#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6973da5c369Sopenharmony_cipub struct SpacectlRange(pub libc::off_t, pub libc::off_t);
6983da5c369Sopenharmony_ci
6993da5c369Sopenharmony_ci#[cfg(any(target_os = "freebsd"))]
7003da5c369Sopenharmony_ciimpl SpacectlRange {
7013da5c369Sopenharmony_ci    #[inline]
7023da5c369Sopenharmony_ci    pub fn is_empty(&self) -> bool {
7033da5c369Sopenharmony_ci        self.1 == 0
7043da5c369Sopenharmony_ci    }
7053da5c369Sopenharmony_ci
7063da5c369Sopenharmony_ci    #[inline]
7073da5c369Sopenharmony_ci    pub fn len(&self) -> libc::off_t {
7083da5c369Sopenharmony_ci        self.1
7093da5c369Sopenharmony_ci    }
7103da5c369Sopenharmony_ci
7113da5c369Sopenharmony_ci    #[inline]
7123da5c369Sopenharmony_ci    pub fn offset(&self) -> libc::off_t {
7133da5c369Sopenharmony_ci        self.0
7143da5c369Sopenharmony_ci    }
7153da5c369Sopenharmony_ci}
7163da5c369Sopenharmony_ci
7173da5c369Sopenharmony_ci/// Punch holes in a file.
7183da5c369Sopenharmony_ci///
7193da5c369Sopenharmony_ci/// `fspacectl` instructs the file system to deallocate a portion of a file.
7203da5c369Sopenharmony_ci/// After a successful operation, this region of the file will return all zeroes
7213da5c369Sopenharmony_ci/// if read.  If the file system supports deallocation, then it may free the
7223da5c369Sopenharmony_ci/// underlying storage, too.
7233da5c369Sopenharmony_ci///
7243da5c369Sopenharmony_ci/// # Arguments
7253da5c369Sopenharmony_ci///
7263da5c369Sopenharmony_ci/// - `fd`      -   File to operate on
7273da5c369Sopenharmony_ci/// - `range.0` -   File offset at which to begin deallocation
7283da5c369Sopenharmony_ci/// - `range.1` -   Length of the region to deallocate
7293da5c369Sopenharmony_ci///
7303da5c369Sopenharmony_ci/// # Returns
7313da5c369Sopenharmony_ci///
7323da5c369Sopenharmony_ci/// The operation may deallocate less than the entire requested region.  On
7333da5c369Sopenharmony_ci/// success, it returns the region that still remains to be deallocated.  The
7343da5c369Sopenharmony_ci/// caller should loop until the returned region is empty.
7353da5c369Sopenharmony_ci///
7363da5c369Sopenharmony_ci/// # Example
7373da5c369Sopenharmony_ci///
7383da5c369Sopenharmony_ci#[cfg_attr(fbsd14, doc = " ```")]
7393da5c369Sopenharmony_ci#[cfg_attr(not(fbsd14), doc = " ```no_run")]
7403da5c369Sopenharmony_ci/// # use std::io::Write;
7413da5c369Sopenharmony_ci/// # use std::os::unix::fs::FileExt;
7423da5c369Sopenharmony_ci/// # use std::os::unix::io::AsRawFd;
7433da5c369Sopenharmony_ci/// # use nix::fcntl::*;
7443da5c369Sopenharmony_ci/// # use tempfile::tempfile;
7453da5c369Sopenharmony_ci/// const INITIAL: &[u8] = b"0123456789abcdef";
7463da5c369Sopenharmony_ci/// let mut f = tempfile().unwrap();
7473da5c369Sopenharmony_ci/// f.write_all(INITIAL).unwrap();
7483da5c369Sopenharmony_ci/// let mut range = SpacectlRange(3, 6);
7493da5c369Sopenharmony_ci/// while (!range.is_empty()) {
7503da5c369Sopenharmony_ci///     range = fspacectl(f.as_raw_fd(), range).unwrap();
7513da5c369Sopenharmony_ci/// }
7523da5c369Sopenharmony_ci/// let mut buf = vec![0; INITIAL.len()];
7533da5c369Sopenharmony_ci/// f.read_exact_at(&mut buf, 0).unwrap();
7543da5c369Sopenharmony_ci/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
7553da5c369Sopenharmony_ci/// ```
7563da5c369Sopenharmony_ci#[cfg(target_os = "freebsd")]
7573da5c369Sopenharmony_cipub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> {
7583da5c369Sopenharmony_ci    let mut rqsr = libc::spacectl_range{r_offset: range.0, r_len: range.1};
7593da5c369Sopenharmony_ci    let res = unsafe { libc::fspacectl(
7603da5c369Sopenharmony_ci            fd,
7613da5c369Sopenharmony_ci            libc::SPACECTL_DEALLOC, // Only one command is supported ATM
7623da5c369Sopenharmony_ci            &rqsr,
7633da5c369Sopenharmony_ci            0,                      // No flags are currently supported
7643da5c369Sopenharmony_ci            &mut rqsr
7653da5c369Sopenharmony_ci    )};
7663da5c369Sopenharmony_ci    Errno::result(res).map(|_| SpacectlRange(rqsr.r_offset, rqsr.r_len))
7673da5c369Sopenharmony_ci}
7683da5c369Sopenharmony_ci
7693da5c369Sopenharmony_ci/// Like [`fspacectl`], but will never return incomplete.
7703da5c369Sopenharmony_ci///
7713da5c369Sopenharmony_ci/// # Arguments
7723da5c369Sopenharmony_ci///
7733da5c369Sopenharmony_ci/// - `fd`      -   File to operate on
7743da5c369Sopenharmony_ci/// - `offset`  -   File offset at which to begin deallocation
7753da5c369Sopenharmony_ci/// - `len`     -   Length of the region to deallocate
7763da5c369Sopenharmony_ci///
7773da5c369Sopenharmony_ci/// # Returns
7783da5c369Sopenharmony_ci///
7793da5c369Sopenharmony_ci/// Returns `()` on success.  On failure, the region may or may not be partially
7803da5c369Sopenharmony_ci/// deallocated.
7813da5c369Sopenharmony_ci///
7823da5c369Sopenharmony_ci/// # Example
7833da5c369Sopenharmony_ci///
7843da5c369Sopenharmony_ci#[cfg_attr(fbsd14, doc = " ```")]
7853da5c369Sopenharmony_ci#[cfg_attr(not(fbsd14), doc = " ```no_run")]
7863da5c369Sopenharmony_ci/// # use std::io::Write;
7873da5c369Sopenharmony_ci/// # use std::os::unix::fs::FileExt;
7883da5c369Sopenharmony_ci/// # use std::os::unix::io::AsRawFd;
7893da5c369Sopenharmony_ci/// # use nix::fcntl::*;
7903da5c369Sopenharmony_ci/// # use tempfile::tempfile;
7913da5c369Sopenharmony_ci/// const INITIAL: &[u8] = b"0123456789abcdef";
7923da5c369Sopenharmony_ci/// let mut f = tempfile().unwrap();
7933da5c369Sopenharmony_ci/// f.write_all(INITIAL).unwrap();
7943da5c369Sopenharmony_ci/// fspacectl_all(f.as_raw_fd(), 3, 6).unwrap();
7953da5c369Sopenharmony_ci/// let mut buf = vec![0; INITIAL.len()];
7963da5c369Sopenharmony_ci/// f.read_exact_at(&mut buf, 0).unwrap();
7973da5c369Sopenharmony_ci/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
7983da5c369Sopenharmony_ci/// ```
7993da5c369Sopenharmony_ci#[cfg(target_os = "freebsd")]
8003da5c369Sopenharmony_cipub fn fspacectl_all(fd: RawFd, offset: libc::off_t, len: libc::off_t)
8013da5c369Sopenharmony_ci    -> Result<()>
8023da5c369Sopenharmony_ci{
8033da5c369Sopenharmony_ci    let mut rqsr = libc::spacectl_range{r_offset: offset, r_len: len};
8043da5c369Sopenharmony_ci    while rqsr.r_len > 0 {
8053da5c369Sopenharmony_ci        let res = unsafe { libc::fspacectl(
8063da5c369Sopenharmony_ci                fd,
8073da5c369Sopenharmony_ci                libc::SPACECTL_DEALLOC, // Only one command is supported ATM
8083da5c369Sopenharmony_ci                &rqsr,
8093da5c369Sopenharmony_ci                0,                      // No flags are currently supported
8103da5c369Sopenharmony_ci                &mut rqsr
8113da5c369Sopenharmony_ci        )};
8123da5c369Sopenharmony_ci        Errno::result(res)?;
8133da5c369Sopenharmony_ci    }
8143da5c369Sopenharmony_ci    Ok(())
8153da5c369Sopenharmony_ci}
8163da5c369Sopenharmony_ci
8173da5c369Sopenharmony_ci#[cfg(any(
8183da5c369Sopenharmony_ci    target_os = "linux",
8193da5c369Sopenharmony_ci    target_os = "android",
8203da5c369Sopenharmony_ci    target_os = "emscripten",
8213da5c369Sopenharmony_ci    target_os = "fuchsia",
8223da5c369Sopenharmony_ci    target_os = "wasi",
8233da5c369Sopenharmony_ci    target_env = "uclibc",
8243da5c369Sopenharmony_ci    target_os = "freebsd"
8253da5c369Sopenharmony_ci))]
8263da5c369Sopenharmony_cimod posix_fadvise {
8273da5c369Sopenharmony_ci    use crate::errno::Errno;
8283da5c369Sopenharmony_ci    use std::os::unix::io::RawFd;
8293da5c369Sopenharmony_ci    use crate::Result;
8303da5c369Sopenharmony_ci
8313da5c369Sopenharmony_ci    #[cfg(feature = "fs")]
8323da5c369Sopenharmony_ci    libc_enum! {
8333da5c369Sopenharmony_ci        #[repr(i32)]
8343da5c369Sopenharmony_ci        #[non_exhaustive]
8353da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
8363da5c369Sopenharmony_ci        pub enum PosixFadviseAdvice {
8373da5c369Sopenharmony_ci            POSIX_FADV_NORMAL,
8383da5c369Sopenharmony_ci            POSIX_FADV_SEQUENTIAL,
8393da5c369Sopenharmony_ci            POSIX_FADV_RANDOM,
8403da5c369Sopenharmony_ci            POSIX_FADV_NOREUSE,
8413da5c369Sopenharmony_ci            POSIX_FADV_WILLNEED,
8423da5c369Sopenharmony_ci            POSIX_FADV_DONTNEED,
8433da5c369Sopenharmony_ci        }
8443da5c369Sopenharmony_ci    }
8453da5c369Sopenharmony_ci
8463da5c369Sopenharmony_ci    feature! {
8473da5c369Sopenharmony_ci    #![feature = "fs"]
8483da5c369Sopenharmony_ci    pub fn posix_fadvise(
8493da5c369Sopenharmony_ci        fd: RawFd,
8503da5c369Sopenharmony_ci        offset: libc::off_t,
8513da5c369Sopenharmony_ci        len: libc::off_t,
8523da5c369Sopenharmony_ci        advice: PosixFadviseAdvice,
8533da5c369Sopenharmony_ci    ) -> Result<()> {
8543da5c369Sopenharmony_ci        let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) };
8553da5c369Sopenharmony_ci
8563da5c369Sopenharmony_ci        if res == 0 {
8573da5c369Sopenharmony_ci            Ok(())
8583da5c369Sopenharmony_ci        } else {
8593da5c369Sopenharmony_ci            Err(Errno::from_i32(res))
8603da5c369Sopenharmony_ci        }
8613da5c369Sopenharmony_ci    }
8623da5c369Sopenharmony_ci    }
8633da5c369Sopenharmony_ci}
8643da5c369Sopenharmony_ci
8653da5c369Sopenharmony_ci#[cfg(any(
8663da5c369Sopenharmony_ci    target_os = "linux",
8673da5c369Sopenharmony_ci    target_os = "android",
8683da5c369Sopenharmony_ci    target_os = "dragonfly",
8693da5c369Sopenharmony_ci    target_os = "emscripten",
8703da5c369Sopenharmony_ci    target_os = "fuchsia",
8713da5c369Sopenharmony_ci    target_os = "wasi",
8723da5c369Sopenharmony_ci    target_os = "freebsd"
8733da5c369Sopenharmony_ci))]
8743da5c369Sopenharmony_cipub fn posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Result<()> {
8753da5c369Sopenharmony_ci    let res = unsafe { libc::posix_fallocate(fd, offset, len) };
8763da5c369Sopenharmony_ci    match Errno::result(res) {
8773da5c369Sopenharmony_ci        Err(err) => Err(err),
8783da5c369Sopenharmony_ci        Ok(0) => Ok(()),
8793da5c369Sopenharmony_ci        Ok(errno) => Err(Errno::from_i32(errno)),
8803da5c369Sopenharmony_ci    }
8813da5c369Sopenharmony_ci}
8823da5c369Sopenharmony_ci}
883