• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! file control options
2 use crate::errno::Errno;
3 #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))]
4 use core::slice;
5 use 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 ))]
12 use std::ffi::CStr;
13 use std::ffi::OsString;
14 #[cfg(not(any(target_os = "redox", target_os = "solaris")))]
15 use std::ops::{Deref, DerefMut};
16 #[cfg(not(target_os = "redox"))]
17 use std::os::raw;
18 use std::os::unix::ffi::OsStringExt;
19 use std::os::unix::io::RawFd;
20 #[cfg(not(any(target_os = "redox", target_os = "solaris")))]
21 use 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 ))]
28 use std::path::PathBuf;
29 #[cfg(any(linux_android, target_os = "freebsd"))]
30 use std::{os::unix::io::AsFd, ptr};
31 
32 #[cfg(feature = "fs")]
33 use 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")]
44 pub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice};
45 
46 #[cfg(not(target_os = "redox"))]
47 #[cfg(any(feature = "fs", feature = "process", feature = "user"))]
48 libc_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 ))]
82 libc_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 ))]
at_rawfd(fd: Option<RawFd>) -> raw::c_int207 pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int {
208     fd.unwrap_or(libc::AT_FDCWD)
209 }
210 
211 feature! {
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)]
220 pub 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"))]
243 pub 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 
255 cfg_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"))]
377 pub 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")]
399 libc_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 
416 feature! {
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"))]
425 pub 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 
446 fn 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 
452 fn 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 
477 fn 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)
568 pub 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"))]
580 pub 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")]
591 libc_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")]
611 libc_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 
620 feature! {
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]
627 pub 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]
702 pub 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 }
716 pub 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
723 pub 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]
810 pub 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.")]
829 pub 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")))]
856 pub 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)]
863 pub struct Flock<T: Flockable>(T);
864 
865 #[cfg(not(any(target_os = "redox", target_os = "solaris")))]
866 impl<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")))]
876 impl<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")))]
884 impl<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")))]
891 impl<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")))]
986 unsafe 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")))]
990 unsafe impl Flockable for OwnedFd {}
991 }
992 
993 #[cfg(linux_android)]
994 #[cfg(feature = "zerocopy")]
995 libc_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 
1016 feature! {
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"))]
1040 pub 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)]
1090 pub 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)]
1116 pub 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)]
1131 pub 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")]
1150 libc_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 
1181 feature! {
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")]
1190 pub 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)]
1204 pub struct SpacectlRange(pub libc::off_t, pub libc::off_t);
1205 
1206 #[cfg(any(target_os = "freebsd"))]
1207 impl 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
1271 pub 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
1320 pub 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 ))]
1352 mod 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 ))]
1415 pub 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 }
1428