1 use crate::errno::Errno;
2 use libc::{self, c_char, c_int, c_uint, size_t, ssize_t};
3 use std::ffi::OsString;
4 #[cfg(not(target_os = "redox"))]
5 use std::os::raw;
6 use std::os::unix::ffi::OsStringExt;
7 use std::os::unix::io::RawFd;
8 use crate::sys::stat::Mode;
9 use crate::{NixPath, Result};
10 
11 #[cfg(any(target_os = "android", target_os = "linux"))]
12 use std::ptr; // For splice and copy_file_range
13 #[cfg(any(target_os = "android", target_os = "linux"))]
14 use 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 ))]
25 pub use self::posix_fadvise::*;
26 
27 #[cfg(not(target_os = "redox"))]
28 libc_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 
42 libc_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)]
open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd>170 pub 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"))]
openat<P: ?Sized + NixPath>( dirfd: RawFd, path: &P, oflag: OFlag, mode: Mode, ) -> Result<RawFd>181 pub 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"))]
renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>( old_dirfd: Option<RawFd>, old_path: &P1, new_dirfd: Option<RawFd>, new_path: &P2, ) -> Result<()>194 pub 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 ))]
217 libc_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 ))]
renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>( old_dirfd: Option<RawFd>, old_path: &P1, new_dirfd: Option<RawFd>, new_path: &P2, flags: RenameFlags, ) -> Result<()>229 pub 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 
wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString>250 fn 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 
readlink_maybe_at<P: ?Sized + NixPath>( dirfd: Option<RawFd>, path: &P, v: &mut Vec<u8>, ) -> Result<libc::ssize_t>256 fn 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 
inner_readlink<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P) -> Result<OsString>281 fn 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 
readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString>334 pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
335     inner_readlink(None, path)
336 }
337 
338 #[cfg(not(target_os = "redox"))]
readlinkat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P) -> Result<OsString>339 pub 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"))]
at_rawfd(fd: Option<RawFd>) -> raw::c_int345 pub(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"))]
353 libc_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 
367 libc_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]
378 pub 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]
410 pub 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 }
418 pub use self::FcntlArg::*;
419 
420 // TODO: Figure out how to handle value fcntl returns
fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int>421 pub 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]
460 pub enum FlockArg {
461     LockShared,
462     LockExclusive,
463     Unlock,
464     LockSharedNonblock,
465     LockExclusiveNonblock,
466     UnlockNonblock,
467 }
468 
469 #[cfg(not(target_os = "redox"))]
flock(fd: RawFd, arg: FlockArg) -> Result<()>470 pub 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"))]
488 libc_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"))]
copy_file_range( fd_in: RawFd, off_in: Option<&mut libc::loff_t>, fd_out: RawFd, off_out: Option<&mut libc::loff_t>, len: usize, ) -> Result<usize>525 pub 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"))]
splice( fd_in: RawFd, off_in: Option<&mut libc::loff_t>, fd_out: RawFd, off_out: Option<&mut libc::loff_t>, len: usize, flags: SpliceFFlags, ) -> Result<usize>554 pub 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"))]
tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize>574 pub 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"))]
vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result<usize>580 pub 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"))]
593 libc_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"))]
fallocate( fd: RawFd, mode: FallocateFlags, offset: libc::off_t, len: libc::off_t, ) -> Result<()>628 pub 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 ))]
647 mod 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 
posix_fadvise( fd: RawFd, offset: libc::off_t, len: libc::off_t, advice: PosixFadviseAdvice, ) -> Result<()>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 ))]
posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Result<()>689 pub 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 }
697