nix/
fcntl.rs

1use crate::errno::Errno;
2use libc::{self, c_char, c_int, c_uint, size_t, ssize_t};
3use std::ffi::OsString;
4#[cfg(not(target_os = "redox"))]
5use std::os::raw;
6use std::os::unix::ffi::OsStringExt;
7use std::os::unix::io::RawFd;
8use crate::sys::stat::Mode;
9use crate::{NixPath, Result};
10
11#[cfg(any(target_os = "android", target_os = "linux"))]
12use std::ptr; // For splice and copy_file_range
13#[cfg(any(target_os = "android", target_os = "linux"))]
14use crate::sys::uio::IoVec; // For vmsplice
15
16#[cfg(any(
17    target_os = "linux",
18    target_os = "android",
19    target_os = "emscripten",
20    target_os = "fuchsia",
21    any(target_os = "wasi", target_env = "wasi"),
22    target_env = "uclibc",
23    target_os = "freebsd"
24))]
25pub use self::posix_fadvise::*;
26
27#[cfg(not(target_os = "redox"))]
28libc_bitflags! {
29    pub struct AtFlags: c_int {
30        AT_REMOVEDIR;
31        AT_SYMLINK_FOLLOW;
32        AT_SYMLINK_NOFOLLOW;
33        #[cfg(any(target_os = "android", target_os = "linux"))]
34        AT_NO_AUTOMOUNT;
35        #[cfg(any(target_os = "android", target_os = "linux"))]
36        AT_EMPTY_PATH;
37        #[cfg(any(target_os = "illumos", target_os = "solaris"))]
38        AT_EACCESS;
39    }
40}
41
42libc_bitflags!(
43    /// Configuration options for opened files.
44    pub struct OFlag: c_int {
45        /// Mask for the access mode of the file.
46        O_ACCMODE;
47        /// Use alternate I/O semantics.
48        #[cfg(target_os = "netbsd")]
49        O_ALT_IO;
50        /// Open the file in append-only mode.
51        O_APPEND;
52        /// Generate a signal when input or output becomes possible.
53        #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
54        O_ASYNC;
55        /// Closes the file descriptor once an `execve` call is made.
56        ///
57        /// Also sets the file offset to the beginning of the file.
58        O_CLOEXEC;
59        /// Create the file if it does not exist.
60        O_CREAT;
61        /// Try to minimize cache effects of the I/O for this file.
62        #[cfg(any(target_os = "android",
63                  target_os = "dragonfly",
64                  target_os = "freebsd",
65                  target_os = "linux",
66                  target_os = "netbsd"))]
67        O_DIRECT;
68        /// If the specified path isn't a directory, fail.
69        #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
70        O_DIRECTORY;
71        /// Implicitly follow each `write()` with an `fdatasync()`.
72        #[cfg(any(target_os = "android",
73                  target_os = "ios",
74                  target_os = "linux",
75                  target_os = "macos",
76                  target_os = "netbsd",
77                  target_os = "openbsd"))]
78        O_DSYNC;
79        /// Error out if a file was not created.
80        O_EXCL;
81        /// Open for execute only.
82        #[cfg(target_os = "freebsd")]
83        O_EXEC;
84        /// Open with an exclusive file lock.
85        #[cfg(any(target_os = "dragonfly",
86                  target_os = "freebsd",
87                  target_os = "ios",
88                  target_os = "macos",
89                  target_os = "netbsd",
90                  target_os = "openbsd",
91                  target_os = "redox"))]
92        O_EXLOCK;
93        /// Same as `O_SYNC`.
94        #[cfg(any(target_os = "dragonfly",
95                  target_os = "freebsd",
96                  target_os = "ios",
97                  all(target_os = "linux", not(target_env = "musl")),
98                  target_os = "macos",
99                  target_os = "netbsd",
100                  target_os = "openbsd",
101                  target_os = "redox"))]
102        O_FSYNC;
103        /// Allow files whose sizes can't be represented in an `off_t` to be opened.
104        #[cfg(any(target_os = "android", target_os = "linux"))]
105        O_LARGEFILE;
106        /// Do not update the file last access time during `read(2)`s.
107        #[cfg(any(target_os = "android", target_os = "linux"))]
108        O_NOATIME;
109        /// Don't attach the device as the process' controlling terminal.
110        #[cfg(not(target_os = "redox"))]
111        O_NOCTTY;
112        /// Same as `O_NONBLOCK`.
113        #[cfg(not(target_os = "redox"))]
114        O_NDELAY;
115        /// `open()` will fail if the given path is a symbolic link.
116        O_NOFOLLOW;
117        /// When possible, open the file in nonblocking mode.
118        O_NONBLOCK;
119        /// Don't deliver `SIGPIPE`.
120        #[cfg(target_os = "netbsd")]
121        O_NOSIGPIPE;
122        /// Obtain a file descriptor for low-level access.
123        ///
124        /// The file itself is not opened and other file operations will fail.
125        #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
126        O_PATH;
127        /// Only allow reading.
128        ///
129        /// This should not be combined with `O_WRONLY` or `O_RDWR`.
130        O_RDONLY;
131        /// Allow both reading and writing.
132        ///
133        /// This should not be combined with `O_WRONLY` or `O_RDONLY`.
134        O_RDWR;
135        /// Similar to `O_DSYNC` but applies to `read`s instead.
136        #[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))]
137        O_RSYNC;
138        /// Skip search permission checks.
139        #[cfg(target_os = "netbsd")]
140        O_SEARCH;
141        /// Open with a shared file lock.
142        #[cfg(any(target_os = "dragonfly",
143                  target_os = "freebsd",
144                  target_os = "ios",
145                  target_os = "macos",
146                  target_os = "netbsd",
147                  target_os = "openbsd",
148                  target_os = "redox"))]
149        O_SHLOCK;
150        /// Implicitly follow each `write()` with an `fsync()`.
151        #[cfg(not(target_os = "redox"))]
152        O_SYNC;
153        /// Create an unnamed temporary file.
154        #[cfg(any(target_os = "android", target_os = "linux"))]
155        O_TMPFILE;
156        /// Truncate an existing regular file to 0 length if it allows writing.
157        O_TRUNC;
158        /// Restore default TTY attributes.
159        #[cfg(target_os = "freebsd")]
160        O_TTY_INIT;
161        /// Only allow writing.
162        ///
163        /// This should not be combined with `O_RDONLY` or `O_RDWR`.
164        O_WRONLY;
165    }
166);
167
168// The conversion is not identical on all operating systems.
169#[allow(clippy::useless_conversion)]
170pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
171    let fd = path.with_nix_path(|cstr| {
172        unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
173    })?;
174
175    Errno::result(fd)
176}
177
178// The conversion is not identical on all operating systems.
179#[allow(clippy::useless_conversion)]
180#[cfg(not(target_os = "redox"))]
181pub fn openat<P: ?Sized + NixPath>(
182    dirfd: RawFd,
183    path: &P,
184    oflag: OFlag,
185    mode: Mode,
186) -> Result<RawFd> {
187    let fd = path.with_nix_path(|cstr| {
188        unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
189    })?;
190    Errno::result(fd)
191}
192
193#[cfg(not(target_os = "redox"))]
194pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
195    old_dirfd: Option<RawFd>,
196    old_path: &P1,
197    new_dirfd: Option<RawFd>,
198    new_path: &P2,
199) -> Result<()> {
200    let res = old_path.with_nix_path(|old_cstr| {
201        new_path.with_nix_path(|new_cstr| unsafe {
202            libc::renameat(
203                at_rawfd(old_dirfd),
204                old_cstr.as_ptr(),
205                at_rawfd(new_dirfd),
206                new_cstr.as_ptr(),
207            )
208        })
209    })??;
210    Errno::result(res).map(drop)
211}
212
213#[cfg(all(
214    target_os = "linux",
215    target_env = "gnu",
216))]
217libc_bitflags! {
218    pub struct RenameFlags: u32 {
219        RENAME_EXCHANGE;
220        RENAME_NOREPLACE;
221        RENAME_WHITEOUT;
222    }
223}
224
225#[cfg(all(
226    target_os = "linux",
227    target_env = "gnu",
228))]
229pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
230    old_dirfd: Option<RawFd>,
231    old_path: &P1,
232    new_dirfd: Option<RawFd>,
233    new_path: &P2,
234    flags: RenameFlags,
235) -> Result<()> {
236    let res = old_path.with_nix_path(|old_cstr| {
237        new_path.with_nix_path(|new_cstr| unsafe {
238            libc::renameat2(
239                at_rawfd(old_dirfd),
240                old_cstr.as_ptr(),
241                at_rawfd(new_dirfd),
242                new_cstr.as_ptr(),
243                flags.bits(),
244            )
245        })
246    })??;
247    Errno::result(res).map(drop)
248}
249
250fn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> {
251    unsafe { v.set_len(len as usize) }
252    v.shrink_to_fit();
253    Ok(OsString::from_vec(v.to_vec()))
254}
255
256fn readlink_maybe_at<P: ?Sized + NixPath>(
257    dirfd: Option<RawFd>,
258    path: &P,
259    v: &mut Vec<u8>,
260) -> Result<libc::ssize_t> {
261    path.with_nix_path(|cstr| unsafe {
262        match dirfd {
263            #[cfg(target_os = "redox")]
264            Some(_) => unreachable!(),
265            #[cfg(not(target_os = "redox"))]
266            Some(dirfd) => libc::readlinkat(
267                dirfd,
268                cstr.as_ptr(),
269                v.as_mut_ptr() as *mut c_char,
270                v.capacity() as size_t,
271            ),
272            None => libc::readlink(
273                cstr.as_ptr(),
274                v.as_mut_ptr() as *mut c_char,
275                v.capacity() as size_t,
276            ),
277        }
278    })
279}
280
281fn inner_readlink<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P) -> Result<OsString> {
282    let mut v = Vec::with_capacity(libc::PATH_MAX as usize);
283    // simple case: result is strictly less than `PATH_MAX`
284    let res = readlink_maybe_at(dirfd, path, &mut v)?;
285    let len = Errno::result(res)?;
286    debug_assert!(len >= 0);
287    if (len as usize) < v.capacity() {
288        return wrap_readlink_result(v, res);
289    }
290    // Uh oh, the result is too long...
291    // Let's try to ask lstat how many bytes to allocate.
292    let reported_size = match dirfd {
293        #[cfg(target_os = "redox")]
294        Some(_) => unreachable!(),
295        #[cfg(any(target_os = "android", target_os = "linux"))]
296        Some(dirfd) => {
297            let flags = if path.is_empty() { AtFlags::AT_EMPTY_PATH } else { AtFlags::empty() };
298            super::sys::stat::fstatat(dirfd, path, flags | AtFlags::AT_SYMLINK_NOFOLLOW)
299        },
300        #[cfg(not(any(target_os = "android", target_os = "linux", target_os = "redox")))]
301        Some(dirfd) => super::sys::stat::fstatat(dirfd, path, AtFlags::AT_SYMLINK_NOFOLLOW),
302        None => super::sys::stat::lstat(path)
303    }
304        .map(|x| x.st_size)
305        .unwrap_or(0);
306    let mut try_size = if reported_size > 0 {
307        // Note: even if `lstat`'s apparently valid answer turns out to be
308        // wrong, we will still read the full symlink no matter what.
309        reported_size as usize + 1
310    } else {
311        // If lstat doesn't cooperate, or reports an error, be a little less
312        // precise.
313        (libc::PATH_MAX as usize).max(128) << 1
314    };
315    loop {
316        v.reserve_exact(try_size);
317        let res = readlink_maybe_at(dirfd, path, &mut v)?;
318        let len = Errno::result(res)?;
319        debug_assert!(len >= 0);
320        if (len as usize) < v.capacity() {
321            break wrap_readlink_result(v, res);
322        } else {
323            // Ugh! Still not big enough!
324            match try_size.checked_shl(1) {
325                Some(next_size) => try_size = next_size,
326                // It's absurd that this would happen, but handle it sanely
327                // anyway.
328                None => break Err(Errno::ENAMETOOLONG),
329            }
330        }
331    }
332}
333
334pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
335    inner_readlink(None, path)
336}
337
338#[cfg(not(target_os = "redox"))]
339pub fn readlinkat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P) -> Result<OsString> {
340    inner_readlink(Some(dirfd), path)
341}
342
343/// Computes the raw fd consumed by a function of the form `*at`.
344#[cfg(not(target_os = "redox"))]
345pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int {
346    match fd {
347        None => libc::AT_FDCWD,
348        Some(fd) => fd,
349    }
350}
351
352#[cfg(any(target_os = "android", target_os = "linux"))]
353libc_bitflags!(
354    /// Additional flags for file sealing, which allows for limiting operations on a file.
355    pub struct SealFlag: c_int {
356        /// Prevents further calls to `fcntl()` with `F_ADD_SEALS`.
357        F_SEAL_SEAL;
358        /// The file cannot be reduced in size.
359        F_SEAL_SHRINK;
360        /// The size of the file cannot be increased.
361        F_SEAL_GROW;
362        /// The file contents cannot be modified.
363        F_SEAL_WRITE;
364    }
365);
366
367libc_bitflags!(
368    /// Additional configuration flags for `fcntl`'s `F_SETFD`.
369    pub struct FdFlag: c_int {
370        /// The file descriptor will automatically be closed during a successful `execve(2)`.
371        FD_CLOEXEC;
372    }
373);
374
375#[cfg(not(target_os = "redox"))]
376#[derive(Debug, Eq, Hash, PartialEq)]
377#[non_exhaustive]
378pub enum FcntlArg<'a> {
379    F_DUPFD(RawFd),
380    F_DUPFD_CLOEXEC(RawFd),
381    F_GETFD,
382    F_SETFD(FdFlag), // FD_FLAGS
383    F_GETFL,
384    F_SETFL(OFlag), // O_NONBLOCK
385    F_SETLK(&'a libc::flock),
386    F_SETLKW(&'a libc::flock),
387    F_GETLK(&'a mut libc::flock),
388    #[cfg(any(target_os = "linux", target_os = "android"))]
389    F_OFD_SETLK(&'a libc::flock),
390    #[cfg(any(target_os = "linux", target_os = "android"))]
391    F_OFD_SETLKW(&'a libc::flock),
392    #[cfg(any(target_os = "linux", target_os = "android"))]
393    F_OFD_GETLK(&'a mut libc::flock),
394    #[cfg(any(target_os = "android", target_os = "linux"))]
395    F_ADD_SEALS(SealFlag),
396    #[cfg(any(target_os = "android", target_os = "linux"))]
397    F_GET_SEALS,
398    #[cfg(any(target_os = "macos", target_os = "ios"))]
399    F_FULLFSYNC,
400    #[cfg(any(target_os = "linux", target_os = "android"))]
401    F_GETPIPE_SZ,
402    #[cfg(any(target_os = "linux", target_os = "android"))]
403    F_SETPIPE_SZ(c_int),
404    // TODO: Rest of flags
405}
406
407#[cfg(target_os = "redox")]
408#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
409#[non_exhaustive]
410pub enum FcntlArg {
411    F_DUPFD(RawFd),
412    F_DUPFD_CLOEXEC(RawFd),
413    F_GETFD,
414    F_SETFD(FdFlag), // FD_FLAGS
415    F_GETFL,
416    F_SETFL(OFlag), // O_NONBLOCK
417}
418pub use self::FcntlArg::*;
419
420// TODO: Figure out how to handle value fcntl returns
421pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
422    let res = unsafe {
423        match arg {
424            F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
425            F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd),
426            F_GETFD => libc::fcntl(fd, libc::F_GETFD),
427            F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()),
428            F_GETFL => libc::fcntl(fd, libc::F_GETFL),
429            F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()),
430            #[cfg(not(target_os = "redox"))]
431            F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock),
432            #[cfg(not(target_os = "redox"))]
433            F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
434            #[cfg(not(target_os = "redox"))]
435            F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
436            #[cfg(any(target_os = "android", target_os = "linux"))]
437            F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock),
438            #[cfg(any(target_os = "android", target_os = "linux"))]
439            F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock),
440            #[cfg(any(target_os = "android", target_os = "linux"))]
441            F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock),
442            #[cfg(any(target_os = "android", target_os = "linux"))]
443            F_ADD_SEALS(flag) => libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()),
444            #[cfg(any(target_os = "android", target_os = "linux"))]
445            F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS),
446            #[cfg(any(target_os = "macos", target_os = "ios"))]
447            F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC),
448            #[cfg(any(target_os = "linux", target_os = "android"))]
449            F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ),
450            #[cfg(any(target_os = "linux", target_os = "android"))]
451            F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size),
452        }
453    };
454
455    Errno::result(res)
456}
457
458#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
459#[non_exhaustive]
460pub enum FlockArg {
461    LockShared,
462    LockExclusive,
463    Unlock,
464    LockSharedNonblock,
465    LockExclusiveNonblock,
466    UnlockNonblock,
467}
468
469#[cfg(not(target_os = "redox"))]
470pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
471    use self::FlockArg::*;
472
473    let res = unsafe {
474        match arg {
475            LockShared => libc::flock(fd, libc::LOCK_SH),
476            LockExclusive => libc::flock(fd, libc::LOCK_EX),
477            Unlock => libc::flock(fd, libc::LOCK_UN),
478            LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB),
479            LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB),
480            UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB),
481        }
482    };
483
484    Errno::result(res).map(drop)
485}
486
487#[cfg(any(target_os = "android", target_os = "linux"))]
488libc_bitflags! {
489    /// Additional flags to `splice` and friends.
490    pub struct SpliceFFlags: c_uint {
491        /// Request that pages be moved instead of copied.
492        ///
493        /// Not applicable to `vmsplice`.
494        SPLICE_F_MOVE;
495        /// Do not block on I/O.
496        SPLICE_F_NONBLOCK;
497        /// Hint that more data will be coming in a subsequent splice.
498        ///
499        /// Not applicable to `vmsplice`.
500        SPLICE_F_MORE;
501        /// Gift the user pages to the kernel.
502        ///
503        /// Not applicable to `splice`.
504        SPLICE_F_GIFT;
505    }
506}
507
508/// Copy a range of data from one file to another
509///
510/// The `copy_file_range` system call performs an in-kernel copy between
511/// file descriptors `fd_in` and `fd_out` without the additional cost of
512/// transferring data from the kernel to user space and then back into the
513/// kernel. It copies up to `len` bytes of data from file descriptor `fd_in` to
514/// file descriptor `fd_out`, overwriting any data that exists within the
515/// requested range of the target file.
516///
517/// If the `off_in` and/or `off_out` arguments are used, the values
518/// will be mutated to reflect the new position within the file after
519/// copying. If they are not used, the relevant filedescriptors will be seeked
520/// to the new position.
521///
522/// On successful completion the number of bytes actually copied will be
523/// returned.
524#[cfg(any(target_os = "android", target_os = "linux"))]
525pub fn copy_file_range(
526    fd_in: RawFd,
527    off_in: Option<&mut libc::loff_t>,
528    fd_out: RawFd,
529    off_out: Option<&mut libc::loff_t>,
530    len: usize,
531) -> Result<usize> {
532    let off_in = off_in
533        .map(|offset| offset as *mut libc::loff_t)
534        .unwrap_or(ptr::null_mut());
535    let off_out = off_out
536        .map(|offset| offset as *mut libc::loff_t)
537        .unwrap_or(ptr::null_mut());
538
539    let ret = unsafe {
540        libc::syscall(
541            libc::SYS_copy_file_range,
542            fd_in,
543            off_in,
544            fd_out,
545            off_out,
546            len,
547            0,
548        )
549    };
550    Errno::result(ret).map(|r| r as usize)
551}
552
553#[cfg(any(target_os = "linux", target_os = "android"))]
554pub fn splice(
555    fd_in: RawFd,
556    off_in: Option<&mut libc::loff_t>,
557    fd_out: RawFd,
558    off_out: Option<&mut libc::loff_t>,
559    len: usize,
560    flags: SpliceFFlags,
561) -> Result<usize> {
562    let off_in = off_in
563        .map(|offset| offset as *mut libc::loff_t)
564        .unwrap_or(ptr::null_mut());
565    let off_out = off_out
566        .map(|offset| offset as *mut libc::loff_t)
567        .unwrap_or(ptr::null_mut());
568
569    let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) };
570    Errno::result(ret).map(|r| r as usize)
571}
572
573#[cfg(any(target_os = "linux", target_os = "android"))]
574pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> {
575    let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) };
576    Errno::result(ret).map(|r| r as usize)
577}
578
579#[cfg(any(target_os = "linux", target_os = "android"))]
580pub fn vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result<usize> {
581    let ret = unsafe {
582        libc::vmsplice(
583            fd,
584            iov.as_ptr() as *const libc::iovec,
585            iov.len(),
586            flags.bits(),
587        )
588    };
589    Errno::result(ret).map(|r| r as usize)
590}
591
592#[cfg(any(target_os = "linux"))]
593libc_bitflags!(
594    /// Mode argument flags for fallocate determining operation performed on a given range.
595    pub struct FallocateFlags: c_int {
596        /// File size is not changed.
597        ///
598        /// offset + len can be greater than file size.
599        FALLOC_FL_KEEP_SIZE;
600        /// Deallocates space by creating a hole.
601        ///
602        /// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes.
603        FALLOC_FL_PUNCH_HOLE;
604        /// Removes byte range from a file without leaving a hole.
605        ///
606        /// Byte range to collapse starts at offset and continues for len bytes.
607        FALLOC_FL_COLLAPSE_RANGE;
608        /// Zeroes space in specified byte range.
609        ///
610        /// Byte range starts at offset and continues for len bytes.
611        FALLOC_FL_ZERO_RANGE;
612        /// Increases file space by inserting a hole within the file size.
613        ///
614        /// Does not overwrite existing data. Hole starts at offset and continues for len bytes.
615        FALLOC_FL_INSERT_RANGE;
616        /// Shared file data extants are made private to the file.
617        ///
618        /// Gaurantees that a subsequent write will not fail due to lack of space.
619        FALLOC_FL_UNSHARE_RANGE;
620    }
621);
622
623/// Manipulates file space.
624///
625/// Allows the caller to directly manipulate the allocated disk space for the
626/// file referred to by fd.
627#[cfg(any(target_os = "linux"))]
628pub fn fallocate(
629    fd: RawFd,
630    mode: FallocateFlags,
631    offset: libc::off_t,
632    len: libc::off_t,
633) -> Result<()> {
634    let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) };
635    Errno::result(res).map(drop)
636}
637
638#[cfg(any(
639    target_os = "linux",
640    target_os = "android",
641    target_os = "emscripten",
642    target_os = "fuchsia",
643    any(target_os = "wasi", target_env = "wasi"),
644    target_env = "uclibc",
645    target_os = "freebsd"
646))]
647mod posix_fadvise {
648    use crate::errno::Errno;
649    use std::os::unix::io::RawFd;
650    use crate::Result;
651
652    libc_enum! {
653        #[repr(i32)]
654        #[non_exhaustive]
655        pub enum PosixFadviseAdvice {
656            POSIX_FADV_NORMAL,
657            POSIX_FADV_SEQUENTIAL,
658            POSIX_FADV_RANDOM,
659            POSIX_FADV_NOREUSE,
660            POSIX_FADV_WILLNEED,
661            POSIX_FADV_DONTNEED,
662        }
663    }
664
665    pub fn posix_fadvise(
666        fd: RawFd,
667        offset: libc::off_t,
668        len: libc::off_t,
669        advice: PosixFadviseAdvice,
670    ) -> Result<()> {
671        let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) };
672
673        if res == 0 {
674            Ok(())
675        } else {
676            Err(Errno::from_i32(res))
677        }
678    }
679}
680
681#[cfg(any(
682    target_os = "linux",
683    target_os = "android",
684    target_os = "emscripten",
685    target_os = "fuchsia",
686    any(target_os = "wasi", target_env = "wasi"),
687    target_os = "freebsd"
688))]
689pub fn posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Result<()> {
690    let res = unsafe { libc::posix_fallocate(fd, offset, len) };
691    match Errno::result(res) {
692        Err(err) => Err(err),
693        Ok(0) => Ok(()),
694        Ok(errno) => Err(Errno::from_i32(errno)),
695    }
696}