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