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