nix/
fcntl.rs

1//! file control options
2use crate::errno::Errno;
3#[cfg(all(target_os = "freebsd", target_arch = "x86_64"))]
4use core::slice;
5use libc::{c_int, c_uint, size_t, ssize_t};
6#[cfg(any(
7    target_os = "netbsd",
8    apple_targets,
9    target_os = "dragonfly",
10    all(target_os = "freebsd", target_arch = "x86_64"),
11))]
12use std::ffi::CStr;
13use std::ffi::OsString;
14#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
15use std::ops::{Deref, DerefMut};
16#[cfg(not(target_os = "redox"))]
17use std::os::raw;
18use std::os::unix::ffi::OsStringExt;
19use std::os::unix::io::RawFd;
20#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
21use std::os::unix::io::{AsRawFd, OwnedFd};
22#[cfg(any(
23    target_os = "netbsd",
24    apple_targets,
25    target_os = "dragonfly",
26    all(target_os = "freebsd", target_arch = "x86_64"),
27))]
28use std::path::PathBuf;
29#[cfg(any(linux_android, target_os = "freebsd"))]
30use std::{os::unix::io::AsFd, ptr};
31
32#[cfg(feature = "fs")]
33use crate::{sys::stat::Mode, NixPath, Result};
34
35#[cfg(any(
36    linux_android,
37    target_os = "emscripten",
38    target_os = "fuchsia",
39    target_os = "wasi",
40    target_env = "uclibc",
41    target_os = "freebsd"
42))]
43#[cfg(feature = "fs")]
44pub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice};
45
46#[cfg(not(target_os = "redox"))]
47#[cfg(any(feature = "fs", feature = "process", feature = "user"))]
48libc_bitflags! {
49    /// Flags that control how the various *at syscalls behave.
50    #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "process"))))]
51    pub struct AtFlags: c_int {
52        #[allow(missing_docs)]
53        #[doc(hidden)]
54        // Should not be used by the public API, but only internally.
55        AT_REMOVEDIR;
56        /// Used with [`linkat`](crate::unistd::linkat`) to create a link to a symbolic link's
57        /// target, instead of to the symbolic link itself.
58        AT_SYMLINK_FOLLOW;
59        /// Used with functions like [`fstatat`](crate::sys::stat::fstatat`) to operate on a link
60        /// itself, instead of the symbolic link's target.
61        AT_SYMLINK_NOFOLLOW;
62        /// Don't automount the terminal ("basename") component of pathname if it is a directory
63        /// that is an automount point.
64        #[cfg(linux_android)]
65        AT_NO_AUTOMOUNT;
66        /// If the provided path is an empty string, operate on the provided directory file
67        /// descriptor instead.
68        #[cfg(any(linux_android, target_os = "freebsd", target_os = "hurd"))]
69        AT_EMPTY_PATH;
70        /// Used with [`faccessat`](crate::unistd::faccessat), the checks for accessibility are
71        /// performed using the effective user and group IDs instead of the real user and group ID
72        #[cfg(not(target_os = "android"))]
73        AT_EACCESS;
74    }
75}
76
77#[cfg(any(
78    feature = "fs",
79    feature = "term",
80    all(feature = "fanotify", target_os = "linux")
81))]
82libc_bitflags!(
83    /// Configuration options for opened files.
84    #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "term", all(feature = "fanotify", target_os = "linux")))))]
85    pub struct OFlag: c_int {
86        /// Mask for the access mode of the file.
87        O_ACCMODE;
88        /// Use alternate I/O semantics.
89        #[cfg(target_os = "netbsd")]
90        O_ALT_IO;
91        /// Open the file in append-only mode.
92        O_APPEND;
93        /// Generate a signal when input or output becomes possible.
94        #[cfg(not(any(
95            solarish,
96            target_os = "aix",
97            target_os = "haiku"
98        )))]
99        O_ASYNC;
100        /// Closes the file descriptor once an `execve` call is made.
101        ///
102        /// Also sets the file offset to the beginning of the file.
103        O_CLOEXEC;
104        /// Create the file if it does not exist.
105        O_CREAT;
106        /// Try to minimize cache effects of the I/O for this file.
107        #[cfg(any(
108            freebsdlike,
109            linux_android,
110            solarish,
111            target_os = "netbsd"
112        ))]
113        O_DIRECT;
114        /// If the specified path isn't a directory, fail.
115        O_DIRECTORY;
116        /// Implicitly follow each `write()` with an `fdatasync()`.
117        #[cfg(any(linux_android, apple_targets, netbsdlike))]
118        O_DSYNC;
119        /// Error out if a file was not created.
120        O_EXCL;
121        /// Open for execute only.
122        #[cfg(target_os = "freebsd")]
123        O_EXEC;
124        /// Open with an exclusive file lock.
125        #[cfg(any(bsd, target_os = "redox"))]
126        O_EXLOCK;
127        /// Same as `O_SYNC`.
128        #[cfg(any(bsd,
129                  all(target_os = "linux", not(target_env = "musl"), not(target_env = "ohos")),
130                  target_os = "redox"))]
131        O_FSYNC;
132        /// Allow files whose sizes can't be represented in an `off_t` to be opened.
133        #[cfg(linux_android)]
134        O_LARGEFILE;
135        /// Do not update the file last access time during `read(2)`s.
136        #[cfg(linux_android)]
137        O_NOATIME;
138        /// Don't attach the device as the process' controlling terminal.
139        #[cfg(not(target_os = "redox"))]
140        O_NOCTTY;
141        /// Same as `O_NONBLOCK`.
142        #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
143        O_NDELAY;
144        /// `open()` will fail if the given path is a symbolic link.
145        O_NOFOLLOW;
146        /// When possible, open the file in nonblocking mode.
147        O_NONBLOCK;
148        /// Don't deliver `SIGPIPE`.
149        #[cfg(target_os = "netbsd")]
150        O_NOSIGPIPE;
151        /// Obtain a file descriptor for low-level access.
152        ///
153        /// The file itself is not opened and other file operations will fail.
154        #[cfg(any(linux_android, target_os = "redox"))]
155        O_PATH;
156        /// Only allow reading.
157        ///
158        /// This should not be combined with `O_WRONLY` or `O_RDWR`.
159        O_RDONLY;
160        /// Allow both reading and writing.
161        ///
162        /// This should not be combined with `O_WRONLY` or `O_RDONLY`.
163        O_RDWR;
164        /// Similar to `O_DSYNC` but applies to `read`s instead.
165        #[cfg(any(target_os = "linux", netbsdlike))]
166        O_RSYNC;
167        /// Skip search permission checks.
168        #[cfg(target_os = "netbsd")]
169        O_SEARCH;
170        /// Open with a shared file lock.
171        #[cfg(any(bsd, target_os = "redox"))]
172        O_SHLOCK;
173        /// Implicitly follow each `write()` with an `fsync()`.
174        #[cfg(not(target_os = "redox"))]
175        O_SYNC;
176        /// Create an unnamed temporary file.
177        #[cfg(linux_android)]
178        O_TMPFILE;
179        /// Truncate an existing regular file to 0 length if it allows writing.
180        O_TRUNC;
181        /// Restore default TTY attributes.
182        #[cfg(target_os = "freebsd")]
183        O_TTY_INIT;
184        /// Only allow writing.
185        ///
186        /// This should not be combined with `O_RDONLY` or `O_RDWR`.
187        O_WRONLY;
188    }
189);
190
191/// Computes the raw fd consumed by a function of the form `*at`.
192#[cfg(any(
193    all(feature = "fs", not(target_os = "redox")),
194    all(feature = "process", linux_android),
195    all(feature = "fanotify", target_os = "linux")
196))]
197pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int {
198    fd.unwrap_or(libc::AT_FDCWD)
199}
200
201feature! {
202#![feature = "fs"]
203
204/// open or create a file for reading, writing or executing
205///
206/// # See Also
207/// [`open`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html)
208// The conversion is not identical on all operating systems.
209#[allow(clippy::useless_conversion)]
210pub fn open<P: ?Sized + NixPath>(
211    path: &P,
212    oflag: OFlag,
213    mode: Mode,
214) -> Result<RawFd> {
215    let fd = path.with_nix_path(|cstr| unsafe {
216        libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint)
217    })?;
218
219    Errno::result(fd)
220}
221
222/// open or create a file for reading, writing or executing
223///
224/// The `openat` function is equivalent to the [`open`] function except in the case where the path
225/// specifies a relative path.  In that case, the file to be opened is determined relative to the
226/// directory associated with the file descriptor `fd`.
227///
228/// # See Also
229/// [`openat`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/openat.html)
230// The conversion is not identical on all operating systems.
231#[allow(clippy::useless_conversion)]
232#[cfg(not(target_os = "redox"))]
233pub fn openat<P: ?Sized + NixPath>(
234    dirfd: Option<RawFd>,
235    path: &P,
236    oflag: OFlag,
237    mode: Mode,
238) -> Result<RawFd> {
239    let fd = path.with_nix_path(|cstr| unsafe {
240        libc::openat(at_rawfd(dirfd), cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint)
241    })?;
242    Errno::result(fd)
243}
244
245/// Change the name of a file.
246///
247/// The `renameat` function is equivalent to `rename` except in the case where either `old_path`
248/// or `new_path` specifies a relative path.  In such cases, the file to be renamed (or the its new
249/// name, respectively) is located relative to `old_dirfd` or `new_dirfd`, respectively
250///
251/// # See Also
252/// [`renameat`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html)
253#[cfg(not(target_os = "redox"))]
254pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
255    old_dirfd: Option<RawFd>,
256    old_path: &P1,
257    new_dirfd: Option<RawFd>,
258    new_path: &P2,
259) -> Result<()> {
260    let res = old_path.with_nix_path(|old_cstr| {
261        new_path.with_nix_path(|new_cstr| unsafe {
262            libc::renameat(
263                at_rawfd(old_dirfd),
264                old_cstr.as_ptr(),
265                at_rawfd(new_dirfd),
266                new_cstr.as_ptr(),
267            )
268        })
269    })??;
270    Errno::result(res).map(drop)
271}
272}
273
274#[cfg(all(target_os = "linux", target_env = "gnu"))]
275#[cfg(feature = "fs")]
276libc_bitflags! {
277    /// Flags for use with [`renameat2`].
278    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
279    pub struct RenameFlags: u32 {
280        /// Atomically exchange `old_path` and `new_path`.
281        RENAME_EXCHANGE;
282        /// Don't overwrite `new_path` of the rename.  Return an error if `new_path` already
283        /// exists.
284        RENAME_NOREPLACE;
285        /// creates a "whiteout" object at the source of the rename at the same time as performing
286        /// the rename.
287        ///
288        /// This operation makes sense only for overlay/union filesystem implementations.
289        RENAME_WHITEOUT;
290    }
291}
292
293feature! {
294#![feature = "fs"]
295/// Like [`renameat`], but with an additional `flags` argument.
296///
297/// A `renameat2` call with an empty flags argument is equivalent to `renameat`.
298///
299/// # See Also
300/// * [`rename`](https://man7.org/linux/man-pages/man2/rename.2.html)
301#[cfg(all(target_os = "linux", target_env = "gnu"))]
302pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
303    old_dirfd: Option<RawFd>,
304    old_path: &P1,
305    new_dirfd: Option<RawFd>,
306    new_path: &P2,
307    flags: RenameFlags,
308) -> Result<()> {
309    let res = old_path.with_nix_path(|old_cstr| {
310        new_path.with_nix_path(|new_cstr| unsafe {
311            libc::renameat2(
312                at_rawfd(old_dirfd),
313                old_cstr.as_ptr(),
314                at_rawfd(new_dirfd),
315                new_cstr.as_ptr(),
316                flags.bits(),
317            )
318        })
319    })??;
320    Errno::result(res).map(drop)
321}
322
323fn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> {
324    unsafe { v.set_len(len as usize) }
325    v.shrink_to_fit();
326    Ok(OsString::from_vec(v.to_vec()))
327}
328
329fn readlink_maybe_at<P: ?Sized + NixPath>(
330    dirfd: Option<RawFd>,
331    path: &P,
332    v: &mut Vec<u8>,
333) -> Result<libc::ssize_t> {
334    path.with_nix_path(|cstr| unsafe {
335        match dirfd {
336            #[cfg(target_os = "redox")]
337            Some(_) => unreachable!(),
338            #[cfg(not(target_os = "redox"))]
339            Some(dirfd) => libc::readlinkat(
340                dirfd,
341                cstr.as_ptr(),
342                v.as_mut_ptr().cast(),
343                v.capacity() as size_t,
344            ),
345            None => libc::readlink(
346                cstr.as_ptr(),
347                v.as_mut_ptr().cast(),
348                v.capacity() as size_t,
349            ),
350        }
351    })
352}
353
354fn inner_readlink<P: ?Sized + NixPath>(
355    dirfd: Option<RawFd>,
356    path: &P,
357) -> Result<OsString> {
358    #[cfg(not(target_os = "hurd"))]
359    const PATH_MAX: usize = libc::PATH_MAX as usize;
360    #[cfg(target_os = "hurd")]
361    const PATH_MAX: usize = 1024; // Hurd does not define a hard limit, so try a guess first
362    let mut v = Vec::with_capacity(PATH_MAX);
363
364    {
365        // simple case: result is strictly less than `PATH_MAX`
366        let res = readlink_maybe_at(dirfd, path, &mut v)?;
367        let len = Errno::result(res)?;
368        debug_assert!(len >= 0);
369        if (len as usize) < v.capacity() {
370            return wrap_readlink_result(v, res);
371        }
372    }
373
374    // Uh oh, the result is too long...
375    // Let's try to ask lstat how many bytes to allocate.
376    let mut try_size = {
377        let reported_size = match dirfd {
378            #[cfg(target_os = "redox")]
379            Some(_) => unreachable!(),
380            #[cfg(any(linux_android, target_os = "freebsd", target_os = "hurd"))]
381            Some(dirfd) => {
382                let flags = if path.is_empty() {
383                    AtFlags::AT_EMPTY_PATH
384                } else {
385                    AtFlags::empty()
386                };
387                super::sys::stat::fstatat(
388                    Some(dirfd),
389                    path,
390                    flags | AtFlags::AT_SYMLINK_NOFOLLOW,
391                )
392            }
393            #[cfg(not(any(
394                linux_android,
395                target_os = "redox",
396                target_os = "freebsd",
397                target_os = "hurd"
398            )))]
399            Some(dirfd) => super::sys::stat::fstatat(
400                Some(dirfd),
401                path,
402                AtFlags::AT_SYMLINK_NOFOLLOW,
403            ),
404            None => super::sys::stat::lstat(path),
405        }
406        .map(|x| x.st_size)
407        .unwrap_or(0);
408
409        if reported_size > 0 {
410            // Note: even if `lstat`'s apparently valid answer turns out to be
411            // wrong, we will still read the full symlink no matter what.
412            reported_size as usize + 1
413        } else {
414            // If lstat doesn't cooperate, or reports an error, be a little less
415            // precise.
416            PATH_MAX.max(128) << 1
417        }
418    };
419
420    loop {
421        {
422            v.reserve_exact(try_size);
423            let res = readlink_maybe_at(dirfd, path, &mut v)?;
424            let len = Errno::result(res)?;
425            debug_assert!(len >= 0);
426            if (len as usize) < v.capacity() {
427                return wrap_readlink_result(v, res);
428            }
429        }
430
431        // Ugh! Still not big enough!
432        match try_size.checked_shl(1) {
433            Some(next_size) => try_size = next_size,
434            // It's absurd that this would happen, but handle it sanely
435            // anyway.
436            None => break Err(Errno::ENAMETOOLONG),
437        }
438    }
439}
440
441/// Read value of a symbolic link
442///
443/// # See Also
444/// * [`readlink`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html)
445pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
446    inner_readlink(None, path)
447}
448
449/// Read value of a symbolic link.
450///
451/// Equivalent to [`readlink` ] except where `path` specifies a relative path.  In that case,
452/// interpret `path` relative to open file specified by `dirfd`.
453///
454/// # See Also
455/// * [`readlink`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html)
456#[cfg(not(target_os = "redox"))]
457pub fn readlinkat<P: ?Sized + NixPath>(
458    dirfd: Option<RawFd>,
459    path: &P,
460) -> Result<OsString> {
461    let dirfd = at_rawfd(dirfd);
462    inner_readlink(Some(dirfd), path)
463}
464}
465
466#[cfg(any(linux_android, target_os = "freebsd"))]
467#[cfg(feature = "fs")]
468libc_bitflags!(
469    /// Additional flags for file sealing, which allows for limiting operations on a file.
470    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
471    pub struct SealFlag: c_int {
472        /// Prevents further calls to `fcntl()` with `F_ADD_SEALS`.
473        F_SEAL_SEAL;
474        /// The file cannot be reduced in size.
475        F_SEAL_SHRINK;
476        /// The size of the file cannot be increased.
477        F_SEAL_GROW;
478        /// The file contents cannot be modified.
479        F_SEAL_WRITE;
480        /// The file contents cannot be modified, except via shared writable mappings that were
481        /// created prior to the seal being set. Since Linux 5.1.
482        #[cfg(linux_android)]
483        F_SEAL_FUTURE_WRITE;
484    }
485);
486
487#[cfg(feature = "fs")]
488libc_bitflags!(
489    /// Additional configuration flags for `fcntl`'s `F_SETFD`.
490    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
491    pub struct FdFlag: c_int {
492        /// The file descriptor will automatically be closed during a successful `execve(2)`.
493        FD_CLOEXEC;
494    }
495);
496
497feature! {
498#![feature = "fs"]
499
500/// Commands for use with [`fcntl`].
501#[cfg(not(target_os = "redox"))]
502#[derive(Debug, Eq, Hash, PartialEq)]
503#[non_exhaustive]
504pub enum FcntlArg<'a> {
505    /// Duplicate the provided file descriptor
506    F_DUPFD(RawFd),
507    /// Duplicate the provided file descriptor and set the `FD_CLOEXEC` flag on it.
508    F_DUPFD_CLOEXEC(RawFd),
509    /// Get the close-on-exec flag associated with the file descriptor
510    F_GETFD,
511    /// Set the close-on-exec flag associated with the file descriptor
512    F_SETFD(FdFlag), // FD_FLAGS
513    /// Get descriptor status flags
514    F_GETFL,
515    /// Set descriptor status flags
516    F_SETFL(OFlag), // O_NONBLOCK
517    /// Set or clear a file segment lock
518    F_SETLK(&'a libc::flock),
519    /// Like [`F_SETLK`](FcntlArg::F_SETLK) except that if a shared or exclusive lock is blocked by
520    /// other locks, the process waits until the request can be satisfied.
521    F_SETLKW(&'a libc::flock),
522    /// Get the first lock that blocks the lock description
523    F_GETLK(&'a mut libc::flock),
524    /// Acquire or release an open file description lock
525    #[cfg(linux_android)]
526    F_OFD_SETLK(&'a libc::flock),
527    /// Like [`F_OFD_SETLK`](FcntlArg::F_OFD_SETLK) except that if a conflicting lock is held on
528    /// the file, then wait for that lock to be released.
529    #[cfg(linux_android)]
530    F_OFD_SETLKW(&'a libc::flock),
531    /// Determine whether it would be possible to create the given lock.  If not, return details
532    /// about one existing lock that would prevent it.
533    #[cfg(linux_android)]
534    F_OFD_GETLK(&'a mut libc::flock),
535    /// Add seals to the file
536    #[cfg(any(
537        linux_android,
538        target_os = "freebsd"
539    ))]
540    F_ADD_SEALS(SealFlag),
541    /// Get seals associated with the file
542    #[cfg(any(
543        linux_android,
544        target_os = "freebsd"
545    ))]
546    F_GET_SEALS,
547    /// Asks the drive to flush all buffered data to permanent storage.
548    #[cfg(apple_targets)]
549    F_FULLFSYNC,
550    /// fsync + issue barrier to drive
551    #[cfg(apple_targets)]
552    F_BARRIERFSYNC,
553    /// Return the capacity of a pipe
554    #[cfg(linux_android)]
555    F_GETPIPE_SZ,
556    /// Change the capacity of a pipe
557    #[cfg(linux_android)]
558    F_SETPIPE_SZ(c_int),
559    /// Look up the path of an open file descriptor, if possible.
560    #[cfg(any(
561        target_os = "netbsd",
562        target_os = "dragonfly",
563        apple_targets,
564    ))]
565    F_GETPATH(&'a mut PathBuf),
566    /// Look up the path of an open file descriptor, if possible.
567    #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))]
568    F_KINFO(&'a mut PathBuf),
569    /// Return the full path without firmlinks of the fd.
570    #[cfg(apple_targets)]
571    F_GETPATH_NOFIRMLINK(&'a mut PathBuf),
572    // TODO: Rest of flags
573}
574
575/// Commands for use with [`fcntl`].
576#[cfg(target_os = "redox")]
577#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
578#[non_exhaustive]
579pub enum FcntlArg {
580    /// Duplicate the provided file descriptor
581    F_DUPFD(RawFd),
582    /// Duplicate the provided file descriptor and set the `FD_CLOEXEC` flag on it.
583    F_DUPFD_CLOEXEC(RawFd),
584    /// Get the close-on-exec flag associated with the file descriptor
585    F_GETFD,
586    /// Set the close-on-exec flag associated with the file descriptor
587    F_SETFD(FdFlag), // FD_FLAGS
588    /// Get descriptor status flags
589    F_GETFL,
590    /// Set descriptor status flags
591    F_SETFL(OFlag), // O_NONBLOCK
592}
593pub use self::FcntlArg::*;
594
595/// Perform various operations on open file descriptors.
596///
597/// # See Also
598/// * [`fcntl`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html)
599// TODO: Figure out how to handle value fcntl returns
600pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
601    let res = unsafe {
602        match arg {
603            F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
604            F_DUPFD_CLOEXEC(rawfd) => {
605                libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd)
606            }
607            F_GETFD => libc::fcntl(fd, libc::F_GETFD),
608            F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()),
609            F_GETFL => libc::fcntl(fd, libc::F_GETFL),
610            F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()),
611            #[cfg(not(target_os = "redox"))]
612            F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock),
613            #[cfg(not(target_os = "redox"))]
614            F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
615            #[cfg(not(target_os = "redox"))]
616            F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
617            #[cfg(linux_android)]
618            F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock),
619            #[cfg(linux_android)]
620            F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock),
621            #[cfg(linux_android)]
622            F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock),
623            #[cfg(any(
624                linux_android,
625                target_os = "freebsd"
626            ))]
627            F_ADD_SEALS(flag) => {
628                libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits())
629            }
630            #[cfg(any(
631                linux_android,
632                target_os = "freebsd"
633            ))]
634            F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS),
635            #[cfg(apple_targets)]
636            F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC),
637            #[cfg(apple_targets)]
638            F_BARRIERFSYNC => libc::fcntl(fd, libc::F_BARRIERFSYNC),
639            #[cfg(linux_android)]
640            F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ),
641            #[cfg(linux_android)]
642            F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size),
643            #[cfg(any(
644                target_os = "dragonfly",
645                target_os = "netbsd",
646                apple_targets,
647            ))]
648            F_GETPATH(path) => {
649                let mut buffer = vec![0; libc::PATH_MAX as usize];
650                let res = libc::fcntl(fd, libc::F_GETPATH, buffer.as_mut_ptr());
651                let ok_res = Errno::result(res)?;
652                let optr = CStr::from_bytes_until_nul(&buffer).unwrap();
653                *path = PathBuf::from(OsString::from(optr.to_str().unwrap()));
654                return Ok(ok_res)
655            },
656            #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))]
657            F_KINFO(path) => {
658                let mut info: libc::kinfo_file = std::mem::zeroed();
659                info.kf_structsize = std::mem::size_of::<libc::kinfo_file>() as i32;
660                let res = libc::fcntl(fd, libc::F_KINFO, &mut info);
661                let ok_res = Errno::result(res)?;
662                let p = info.kf_path;
663                let u8_slice = slice::from_raw_parts(p.as_ptr().cast(), p.len());
664                let optr = CStr::from_bytes_until_nul(u8_slice).unwrap();
665                *path = PathBuf::from(OsString::from(optr.to_str().unwrap()));
666                return Ok(ok_res)
667            },
668            #[cfg(apple_targets)]
669            F_GETPATH_NOFIRMLINK(path) => {
670                let mut buffer = vec![0; libc::PATH_MAX as usize];
671                let res = libc::fcntl(fd, libc::F_GETPATH_NOFIRMLINK, buffer.as_mut_ptr());
672                let ok_res = Errno::result(res)?;
673                let optr = CStr::from_bytes_until_nul(&buffer).unwrap();
674                *path = PathBuf::from(OsString::from(optr.to_str().unwrap()));
675                return Ok(ok_res)
676            },
677        }
678    };
679
680    Errno::result(res)
681}
682
683/// Operations for use with [`Flock::lock`].
684#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
685#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
686#[non_exhaustive]
687pub enum FlockArg {
688    /// shared file lock
689    LockShared,
690    /// exclusive file lock
691    LockExclusive,
692    /// Unlock file
693    Unlock,
694    /// Shared lock.  Do not block when locking.
695    LockSharedNonblock,
696    /// Exclusive lock.  Do not block when locking.
697    LockExclusiveNonblock,
698    #[allow(missing_docs)]
699    #[deprecated(since = "0.28.0", note = "Use FlockArg::Unlock instead")]
700    UnlockNonblock,
701}
702
703#[allow(missing_docs)]
704#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
705#[deprecated(since = "0.28.0", note = "`fcntl::Flock` should be used instead.")]
706pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
707    use self::FlockArg::*;
708
709    let res = unsafe {
710        match arg {
711            LockShared => libc::flock(fd, libc::LOCK_SH),
712            LockExclusive => libc::flock(fd, libc::LOCK_EX),
713            Unlock => libc::flock(fd, libc::LOCK_UN),
714            LockSharedNonblock => {
715                libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB)
716            }
717            LockExclusiveNonblock => {
718                libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB)
719            }
720            #[allow(deprecated)]
721            UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB),
722        }
723    };
724
725    Errno::result(res).map(drop)
726}
727
728/// Represents valid types for flock.
729///
730/// # Safety
731/// Types implementing this must not be `Clone`.
732#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
733pub unsafe trait Flockable: AsRawFd {}
734
735/// Represents an owned flock, which unlocks on drop.
736///
737/// See [flock(2)](https://linux.die.net/man/2/flock) for details on locking semantics.
738#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
739#[derive(Debug)]
740pub struct Flock<T: Flockable>(T);
741
742#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
743impl<T: Flockable> Drop for Flock<T> {
744    fn drop(&mut self) {
745        let res = Errno::result(unsafe { libc::flock(self.0.as_raw_fd(), libc::LOCK_UN) });
746        if res.is_err() && !std::thread::panicking() {
747            panic!("Failed to remove flock: {}", res.unwrap_err());
748        }
749    }
750}
751
752#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
753impl<T: Flockable> Deref for Flock<T> {
754    type Target = T;
755
756    fn deref(&self) -> &Self::Target {
757        &self.0
758    }
759}
760#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
761impl<T: Flockable> DerefMut for Flock<T> {
762    fn deref_mut(&mut self) -> &mut Self::Target {
763        &mut self.0
764    }
765}
766
767#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
768impl<T: Flockable> Flock<T> {
769    /// Obtain a/an flock.
770    ///
771    /// # Example
772    /// ```
773    /// # use std::io::Write;
774    /// # use std::fs::File;
775    /// # use nix::fcntl::{Flock, FlockArg};
776    /// # fn do_stuff(file: File) {
777    ///   let mut file = match Flock::lock(file, FlockArg::LockExclusive) {
778    ///       Ok(l) => l,
779    ///       Err(_) => return,
780    ///   };
781    ///
782    ///   // Do stuff
783    ///   let data = "Foo bar";
784    ///   _ = file.write(data.as_bytes());
785    ///   _ = file.sync_data();
786    /// # }
787    pub fn lock(t: T, args: FlockArg) -> std::result::Result<Self, (T, Errno)> {
788        let flags = match args {
789            FlockArg::LockShared => libc::LOCK_SH,
790            FlockArg::LockExclusive => libc::LOCK_EX,
791            FlockArg::LockSharedNonblock => libc::LOCK_SH | libc::LOCK_NB,
792            FlockArg::LockExclusiveNonblock => libc::LOCK_EX | libc::LOCK_NB,
793            #[allow(deprecated)]
794            FlockArg::Unlock | FlockArg::UnlockNonblock => return Err((t, Errno::EINVAL)),
795        };
796        match Errno::result(unsafe { libc::flock(t.as_raw_fd(), flags) }) {
797            Ok(_) => Ok(Self(t)),
798            Err(errno) => Err((t, errno)),
799        }
800    }
801
802    /// Remove the lock and return the object wrapped within.
803    ///
804    /// # Example
805    /// ```
806    /// # use std::fs::File;
807    /// # use nix::fcntl::{Flock, FlockArg};
808    /// fn do_stuff(file: File) -> nix::Result<()> {
809    ///     let mut lock = match Flock::lock(file, FlockArg::LockExclusive) {
810    ///         Ok(l) => l,
811    ///         Err((_,e)) => return Err(e),
812    ///     };
813    ///
814    ///     // Do critical section
815    ///
816    ///     // Unlock
817    ///     let file = match lock.unlock() {
818    ///         Ok(f) => f,
819    ///         Err((_, e)) => return Err(e),
820    ///     };
821    ///
822    ///     // Do anything else
823    ///
824    ///     Ok(())
825    /// }
826    pub fn unlock(self) -> std::result::Result<T, (Self, Errno)> {
827        let inner = unsafe { match Errno::result(libc::flock(self.0.as_raw_fd(), libc::LOCK_UN)) {
828            Ok(_) => std::ptr::read(&self.0),
829            Err(errno) => return Err((self, errno)),
830        }};
831
832        std::mem::forget(self);
833        Ok(inner)
834    }
835}
836
837// Safety: `File` is not [std::clone::Clone].
838#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
839unsafe impl Flockable for std::fs::File {}
840
841// Safety: `OwnedFd` is not [std::clone::Clone].
842#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
843unsafe impl Flockable for OwnedFd {}
844}
845
846#[cfg(linux_android)]
847#[cfg(feature = "zerocopy")]
848libc_bitflags! {
849    /// Additional flags to `splice` and friends.
850    #[cfg_attr(docsrs, doc(cfg(feature = "zerocopy")))]
851    pub struct SpliceFFlags: c_uint {
852        /// Request that pages be moved instead of copied.
853        ///
854        /// Not applicable to `vmsplice`.
855        SPLICE_F_MOVE;
856        /// Do not block on I/O.
857        SPLICE_F_NONBLOCK;
858        /// Hint that more data will be coming in a subsequent splice.
859        ///
860        /// Not applicable to `vmsplice`.
861        SPLICE_F_MORE;
862        /// Gift the user pages to the kernel.
863        ///
864        /// Not applicable to `splice`.
865        SPLICE_F_GIFT;
866    }
867}
868
869feature! {
870#![feature = "zerocopy"]
871
872/// Copy a range of data from one file to another
873///
874/// The `copy_file_range` system call performs an in-kernel copy between
875/// file descriptors `fd_in` and `fd_out` without the additional cost of
876/// transferring data from the kernel to user space and back again. There may be
877/// additional optimizations for specific file systems.  It copies up to `len`
878/// bytes of data from file descriptor `fd_in` to file descriptor `fd_out`,
879/// overwriting any data that exists within the requested range of the target
880/// file.
881///
882/// If the `off_in` and/or `off_out` arguments are used, the values
883/// will be mutated to reflect the new position within the file after
884/// copying. If they are not used, the relevant file descriptors will be seeked
885/// to the new position.
886///
887/// On successful completion the number of bytes actually copied will be
888/// returned.
889// Note: FreeBSD defines the offset argument as "off_t".  Linux and Android
890// define it as "loff_t".  But on both OSes, on all supported platforms, those
891// are 64 bits.  So Nix uses i64 to make the docs simple and consistent.
892#[cfg(any(linux_android, target_os = "freebsd"))]
893pub fn copy_file_range<Fd1: AsFd, Fd2: AsFd>(
894    fd_in: Fd1,
895    off_in: Option<&mut i64>,
896    fd_out: Fd2,
897    off_out: Option<&mut i64>,
898    len: usize,
899) -> Result<usize> {
900    let off_in = off_in
901        .map(|offset| offset as *mut i64)
902        .unwrap_or(ptr::null_mut());
903    let off_out = off_out
904        .map(|offset| offset as *mut i64)
905        .unwrap_or(ptr::null_mut());
906
907    cfg_if::cfg_if! {
908        if #[cfg(target_os = "freebsd")] {
909            let ret = unsafe {
910                libc::copy_file_range(
911                    fd_in.as_fd().as_raw_fd(),
912                    off_in,
913                    fd_out.as_fd().as_raw_fd(),
914                    off_out,
915                    len,
916                    0,
917                )
918            };
919        } else {
920            // May Linux distros still don't include copy_file_range in their
921            // libc implementations, so we need to make a direct syscall.
922            let ret = unsafe {
923                libc::syscall(
924                    libc::SYS_copy_file_range,
925                    fd_in.as_fd().as_raw_fd(),
926                    off_in,
927                    fd_out.as_fd().as_raw_fd(),
928                    off_out,
929                    len,
930                    0,
931                )
932            };
933        }
934    }
935    Errno::result(ret).map(|r| r as usize)
936}
937
938/// Splice data to/from a pipe
939///
940/// # See Also
941/// *[`splice`](https://man7.org/linux/man-pages/man2/splice.2.html)
942#[cfg(linux_android)]
943pub fn splice(
944    fd_in: RawFd,
945    off_in: Option<&mut libc::loff_t>,
946    fd_out: RawFd,
947    off_out: Option<&mut libc::loff_t>,
948    len: usize,
949    flags: SpliceFFlags,
950) -> Result<usize> {
951    let off_in = off_in
952        .map(|offset| offset as *mut libc::loff_t)
953        .unwrap_or(ptr::null_mut());
954    let off_out = off_out
955        .map(|offset| offset as *mut libc::loff_t)
956        .unwrap_or(ptr::null_mut());
957
958    let ret = unsafe {
959        libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits())
960    };
961    Errno::result(ret).map(|r| r as usize)
962}
963
964/// Duplicate pipe content
965///
966/// # See Also
967/// *[`tee`](https://man7.org/linux/man-pages/man2/tee.2.html)
968#[cfg(linux_android)]
969pub fn tee(
970    fd_in: RawFd,
971    fd_out: RawFd,
972    len: usize,
973    flags: SpliceFFlags,
974) -> Result<usize> {
975    let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) };
976    Errno::result(ret).map(|r| r as usize)
977}
978
979/// Splice user pages to/from a pipe
980///
981/// # See Also
982/// *[`vmsplice`](https://man7.org/linux/man-pages/man2/vmsplice.2.html)
983#[cfg(linux_android)]
984pub fn vmsplice(
985    fd: RawFd,
986    iov: &[std::io::IoSlice<'_>],
987    flags: SpliceFFlags,
988) -> Result<usize> {
989    let ret = unsafe {
990        libc::vmsplice(
991            fd,
992            iov.as_ptr().cast(),
993            iov.len(),
994            flags.bits(),
995        )
996    };
997    Errno::result(ret).map(|r| r as usize)
998}
999}
1000
1001#[cfg(target_os = "linux")]
1002#[cfg(feature = "fs")]
1003libc_bitflags!(
1004    /// Mode argument flags for fallocate determining operation performed on a given range.
1005    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
1006    pub struct FallocateFlags: c_int {
1007        /// File size is not changed.
1008        ///
1009        /// offset + len can be greater than file size.
1010        FALLOC_FL_KEEP_SIZE;
1011        /// Deallocates space by creating a hole.
1012        ///
1013        /// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes.
1014        FALLOC_FL_PUNCH_HOLE;
1015        /// Removes byte range from a file without leaving a hole.
1016        ///
1017        /// Byte range to collapse starts at offset and continues for len bytes.
1018        FALLOC_FL_COLLAPSE_RANGE;
1019        /// Zeroes space in specified byte range.
1020        ///
1021        /// Byte range starts at offset and continues for len bytes.
1022        FALLOC_FL_ZERO_RANGE;
1023        /// Increases file space by inserting a hole within the file size.
1024        ///
1025        /// Does not overwrite existing data. Hole starts at offset and continues for len bytes.
1026        FALLOC_FL_INSERT_RANGE;
1027        /// Shared file data extants are made private to the file.
1028        ///
1029        /// Gaurantees that a subsequent write will not fail due to lack of space.
1030        FALLOC_FL_UNSHARE_RANGE;
1031    }
1032);
1033
1034feature! {
1035#![feature = "fs"]
1036
1037/// Manipulates file space.
1038///
1039/// Allows the caller to directly manipulate the allocated disk space for the
1040/// file referred to by fd.
1041#[cfg(target_os = "linux")]
1042#[cfg(feature = "fs")]
1043pub fn fallocate(
1044    fd: RawFd,
1045    mode: FallocateFlags,
1046    offset: libc::off_t,
1047    len: libc::off_t,
1048) -> Result<()> {
1049    let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) };
1050    Errno::result(res).map(drop)
1051}
1052
1053/// Argument to [`fspacectl`] describing the range to zero.  The first member is
1054/// the file offset, and the second is the length of the region.
1055#[cfg(any(target_os = "freebsd"))]
1056#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1057pub struct SpacectlRange(pub libc::off_t, pub libc::off_t);
1058
1059#[cfg(any(target_os = "freebsd"))]
1060impl SpacectlRange {
1061    /// Is the range empty?
1062    ///
1063    /// After a successful call to [`fspacectl`], A value of `true` for `SpacectlRange::is_empty`
1064    /// indicates that the operation is complete.
1065    #[inline]
1066    pub fn is_empty(&self) -> bool {
1067        self.1 == 0
1068    }
1069
1070    /// Remaining length of the range
1071    #[inline]
1072    pub fn len(&self) -> libc::off_t {
1073        self.1
1074    }
1075
1076    /// Next file offset to operate on
1077    #[inline]
1078    pub fn offset(&self) -> libc::off_t {
1079        self.0
1080    }
1081}
1082
1083/// Punch holes in a file.
1084///
1085/// `fspacectl` instructs the file system to deallocate a portion of a file.
1086/// After a successful operation, this region of the file will return all zeroes
1087/// if read.  If the file system supports deallocation, then it may free the
1088/// underlying storage, too.
1089///
1090/// # Arguments
1091///
1092/// - `fd`      -   File to operate on
1093/// - `range.0` -   File offset at which to begin deallocation
1094/// - `range.1` -   Length of the region to deallocate
1095///
1096/// # Returns
1097///
1098/// The operation may deallocate less than the entire requested region.  On
1099/// success, it returns the region that still remains to be deallocated.  The
1100/// caller should loop until the returned region is empty.
1101///
1102/// # Example
1103///
1104#[cfg_attr(fbsd14, doc = " ```")]
1105#[cfg_attr(not(fbsd14), doc = " ```no_run")]
1106/// # use std::io::Write;
1107/// # use std::os::unix::fs::FileExt;
1108/// # use std::os::unix::io::AsRawFd;
1109/// # use nix::fcntl::*;
1110/// # use tempfile::tempfile;
1111/// const INITIAL: &[u8] = b"0123456789abcdef";
1112/// let mut f = tempfile().unwrap();
1113/// f.write_all(INITIAL).unwrap();
1114/// let mut range = SpacectlRange(3, 6);
1115/// while (!range.is_empty()) {
1116///     range = fspacectl(f.as_raw_fd(), range).unwrap();
1117/// }
1118/// let mut buf = vec![0; INITIAL.len()];
1119/// f.read_exact_at(&mut buf, 0).unwrap();
1120/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
1121/// ```
1122#[cfg(target_os = "freebsd")]
1123#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined
1124pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> {
1125    let mut rqsr = libc::spacectl_range {
1126        r_offset: range.0,
1127        r_len: range.1,
1128    };
1129    let res = unsafe {
1130        libc::fspacectl(
1131            fd,
1132            libc::SPACECTL_DEALLOC, // Only one command is supported ATM
1133            &rqsr,
1134            0, // No flags are currently supported
1135            &mut rqsr,
1136        )
1137    };
1138    Errno::result(res).map(|_| SpacectlRange(rqsr.r_offset, rqsr.r_len))
1139}
1140
1141/// Like [`fspacectl`], but will never return incomplete.
1142///
1143/// # Arguments
1144///
1145/// - `fd`      -   File to operate on
1146/// - `offset`  -   File offset at which to begin deallocation
1147/// - `len`     -   Length of the region to deallocate
1148///
1149/// # Returns
1150///
1151/// Returns `()` on success.  On failure, the region may or may not be partially
1152/// deallocated.
1153///
1154/// # Example
1155///
1156#[cfg_attr(fbsd14, doc = " ```")]
1157#[cfg_attr(not(fbsd14), doc = " ```no_run")]
1158/// # use std::io::Write;
1159/// # use std::os::unix::fs::FileExt;
1160/// # use std::os::unix::io::AsRawFd;
1161/// # use nix::fcntl::*;
1162/// # use tempfile::tempfile;
1163/// const INITIAL: &[u8] = b"0123456789abcdef";
1164/// let mut f = tempfile().unwrap();
1165/// f.write_all(INITIAL).unwrap();
1166/// fspacectl_all(f.as_raw_fd(), 3, 6).unwrap();
1167/// let mut buf = vec![0; INITIAL.len()];
1168/// f.read_exact_at(&mut buf, 0).unwrap();
1169/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
1170/// ```
1171#[cfg(target_os = "freebsd")]
1172#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined
1173pub fn fspacectl_all(
1174    fd: RawFd,
1175    offset: libc::off_t,
1176    len: libc::off_t,
1177) -> Result<()> {
1178    let mut rqsr = libc::spacectl_range {
1179        r_offset: offset,
1180        r_len: len,
1181    };
1182    while rqsr.r_len > 0 {
1183        let res = unsafe {
1184            libc::fspacectl(
1185                fd,
1186                libc::SPACECTL_DEALLOC, // Only one command is supported ATM
1187                &rqsr,
1188                0, // No flags are currently supported
1189                &mut rqsr,
1190            )
1191        };
1192        Errno::result(res)?;
1193    }
1194    Ok(())
1195}
1196
1197#[cfg(any(
1198    linux_android,
1199    target_os = "emscripten",
1200    target_os = "fuchsia",
1201    target_os = "wasi",
1202    target_env = "uclibc",
1203    target_os = "freebsd"
1204))]
1205mod posix_fadvise {
1206    use crate::errno::Errno;
1207    use crate::Result;
1208    use std::os::unix::io::RawFd;
1209
1210    #[cfg(feature = "fs")]
1211    libc_enum! {
1212        /// The specific advice provided to [`posix_fadvise`].
1213        #[repr(i32)]
1214        #[non_exhaustive]
1215        #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
1216        pub enum PosixFadviseAdvice {
1217            /// Revert to the default data access behavior.
1218            POSIX_FADV_NORMAL,
1219            /// The file data will be accessed sequentially.
1220            POSIX_FADV_SEQUENTIAL,
1221            /// A hint that file data will be accessed randomly, and prefetching is likely not
1222            /// advantageous.
1223            POSIX_FADV_RANDOM,
1224            /// The specified data will only be accessed once and then not reused.
1225            POSIX_FADV_NOREUSE,
1226            /// The specified data will be accessed in the near future.
1227            POSIX_FADV_WILLNEED,
1228            /// The specified data will not be accessed in the near future.
1229            POSIX_FADV_DONTNEED,
1230        }
1231    }
1232
1233    feature! {
1234    #![feature = "fs"]
1235    /// Allows a process to describe to the system its data access behavior for an open file
1236    /// descriptor.
1237    ///
1238    /// # See Also
1239    /// * [`posix_fadvise`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fadvise.html)
1240    pub fn posix_fadvise(
1241        fd: RawFd,
1242        offset: libc::off_t,
1243        len: libc::off_t,
1244        advice: PosixFadviseAdvice,
1245    ) -> Result<()> {
1246        let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) };
1247
1248        if res == 0 {
1249            Ok(())
1250        } else {
1251            Err(Errno::from_raw(res))
1252        }
1253    }
1254    }
1255}
1256
1257/// Pre-allocate storage for a range in a file
1258///
1259/// # See Also
1260/// * [`posix_fallocate`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fallocate.html)
1261#[cfg(any(
1262    linux_android,
1263    freebsdlike,
1264    target_os = "emscripten",
1265    target_os = "fuchsia",
1266    target_os = "wasi",
1267))]
1268pub fn posix_fallocate(
1269    fd: RawFd,
1270    offset: libc::off_t,
1271    len: libc::off_t,
1272) -> Result<()> {
1273    let res = unsafe { libc::posix_fallocate(fd, offset, len) };
1274    match Errno::result(res) {
1275        Err(err) => Err(err),
1276        Ok(0) => Ok(()),
1277        Ok(errno) => Err(Errno::from_raw(errno)),
1278    }
1279}
1280}