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