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 
9 #[cfg(feature = "fs")]
10 use crate::{sys::stat::Mode, NixPath, Result};
11 #[cfg(any(target_os = "android", target_os = "linux"))]
12 use std::ptr; // For splice and copy_file_range
13 
14 #[cfg(any(
15     target_os = "linux",
16     target_os = "android",
17     target_os = "emscripten",
18     target_os = "fuchsia",
19     target_os = "wasi",
20     target_env = "uclibc",
21     target_os = "freebsd"
22 ))]
23 #[cfg(feature = "fs")]
24 pub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice};
25 
26 #[cfg(not(target_os = "redox"))]
27 #[cfg(any(feature = "fs", feature = "process"))]
28 libc_bitflags! {
29     #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "process"))))]
30     pub struct AtFlags: c_int {
31         AT_REMOVEDIR;
32         AT_SYMLINK_FOLLOW;
33         AT_SYMLINK_NOFOLLOW;
34         #[cfg(any(target_os = "android", target_os = "linux"))]
35         AT_NO_AUTOMOUNT;
36         #[cfg(any(target_os = "android", target_os = "linux"))]
37         AT_EMPTY_PATH;
38         #[cfg(any(target_os = "illumos", target_os = "solaris"))]
39         AT_EACCESS;
40     }
41 }
42 
43 #[cfg(any(feature = "fs", feature = "term"))]
44 libc_bitflags!(
45     /// Configuration options for opened files.
46     #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "term"))))]
47     pub struct OFlag: c_int {
48         /// Mask for the access mode of the file.
49         O_ACCMODE;
50         /// Use alternate I/O semantics.
51         #[cfg(target_os = "netbsd")]
52         #[cfg_attr(docsrs, doc(cfg(all())))]
53         O_ALT_IO;
54         /// Open the file in append-only mode.
55         O_APPEND;
56         /// Generate a signal when input or output becomes possible.
57         #[cfg(not(any(target_os = "illumos", target_os = "solaris", target_os = "haiku")))]
58         #[cfg_attr(docsrs, doc(cfg(all())))]
59         O_ASYNC;
60         /// Closes the file descriptor once an `execve` call is made.
61         ///
62         /// Also sets the file offset to the beginning of the file.
63         O_CLOEXEC;
64         /// Create the file if it does not exist.
65         O_CREAT;
66         /// Try to minimize cache effects of the I/O for this file.
67         #[cfg(any(target_os = "android",
68                   target_os = "dragonfly",
69                   target_os = "freebsd",
70                   target_os = "linux",
71                   target_os = "netbsd"))]
72         #[cfg_attr(docsrs, doc(cfg(all())))]
73         O_DIRECT;
74         /// If the specified path isn't a directory, fail.
75         #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
76         #[cfg_attr(docsrs, doc(cfg(all())))]
77         O_DIRECTORY;
78         /// Implicitly follow each `write()` with an `fdatasync()`.
79         #[cfg(any(target_os = "android",
80                   target_os = "ios",
81                   target_os = "linux",
82                   target_os = "macos",
83                   target_os = "netbsd",
84                   target_os = "openbsd"))]
85         #[cfg_attr(docsrs, doc(cfg(all())))]
86         O_DSYNC;
87         /// Error out if a file was not created.
88         O_EXCL;
89         /// Open for execute only.
90         #[cfg(target_os = "freebsd")]
91         #[cfg_attr(docsrs, doc(cfg(all())))]
92         O_EXEC;
93         /// Open with an exclusive file lock.
94         #[cfg(any(target_os = "dragonfly",
95                   target_os = "freebsd",
96                   target_os = "ios",
97                   target_os = "macos",
98                   target_os = "netbsd",
99                   target_os = "openbsd",
100                   target_os = "redox"))]
101         #[cfg_attr(docsrs, doc(cfg(all())))]
102         O_EXLOCK;
103         /// Same as `O_SYNC`.
104         #[cfg(any(target_os = "dragonfly",
105                   target_os = "freebsd",
106                   target_os = "ios",
107                   all(target_os = "linux", not(any(target_env = "musl", target_env = "ohos"))),
108                   target_os = "macos",
109                   target_os = "netbsd",
110                   target_os = "openbsd",
111                   target_os = "redox"))]
112         #[cfg_attr(docsrs, doc(cfg(all())))]
113         O_FSYNC;
114         /// Allow files whose sizes can't be represented in an `off_t` to be opened.
115         #[cfg(any(target_os = "android", target_os = "linux"))]
116         #[cfg_attr(docsrs, doc(cfg(all())))]
117         O_LARGEFILE;
118         /// Do not update the file last access time during `read(2)`s.
119         #[cfg(any(target_os = "android", target_os = "linux"))]
120         #[cfg_attr(docsrs, doc(cfg(all())))]
121         O_NOATIME;
122         /// Don't attach the device as the process' controlling terminal.
123         #[cfg(not(target_os = "redox"))]
124         #[cfg_attr(docsrs, doc(cfg(all())))]
125         O_NOCTTY;
126         /// Same as `O_NONBLOCK`.
127         #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
128         #[cfg_attr(docsrs, doc(cfg(all())))]
129         O_NDELAY;
130         /// `open()` will fail if the given path is a symbolic link.
131         O_NOFOLLOW;
132         /// When possible, open the file in nonblocking mode.
133         O_NONBLOCK;
134         /// Don't deliver `SIGPIPE`.
135         #[cfg(target_os = "netbsd")]
136         #[cfg_attr(docsrs, doc(cfg(all())))]
137         O_NOSIGPIPE;
138         /// Obtain a file descriptor for low-level access.
139         ///
140         /// The file itself is not opened and other file operations will fail.
141         #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
142         #[cfg_attr(docsrs, doc(cfg(all())))]
143         O_PATH;
144         /// Only allow reading.
145         ///
146         /// This should not be combined with `O_WRONLY` or `O_RDWR`.
147         O_RDONLY;
148         /// Allow both reading and writing.
149         ///
150         /// This should not be combined with `O_WRONLY` or `O_RDONLY`.
151         O_RDWR;
152         /// Similar to `O_DSYNC` but applies to `read`s instead.
153         #[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))]
154         #[cfg_attr(docsrs, doc(cfg(all())))]
155         O_RSYNC;
156         /// Skip search permission checks.
157         #[cfg(target_os = "netbsd")]
158         #[cfg_attr(docsrs, doc(cfg(all())))]
159         O_SEARCH;
160         /// Open with a shared file lock.
161         #[cfg(any(target_os = "dragonfly",
162                   target_os = "freebsd",
163                   target_os = "ios",
164                   target_os = "macos",
165                   target_os = "netbsd",
166                   target_os = "openbsd",
167                   target_os = "redox"))]
168         #[cfg_attr(docsrs, doc(cfg(all())))]
169         O_SHLOCK;
170         /// Implicitly follow each `write()` with an `fsync()`.
171         #[cfg(not(target_os = "redox"))]
172         #[cfg_attr(docsrs, doc(cfg(all())))]
173         O_SYNC;
174         /// Create an unnamed temporary file.
175         #[cfg(any(target_os = "android", target_os = "linux"))]
176         #[cfg_attr(docsrs, doc(cfg(all())))]
177         O_TMPFILE;
178         /// Truncate an existing regular file to 0 length if it allows writing.
179         O_TRUNC;
180         /// Restore default TTY attributes.
181         #[cfg(target_os = "freebsd")]
182         #[cfg_attr(docsrs, doc(cfg(all())))]
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 feature! {
192 #![feature = "fs"]
193 
194 // The conversion is not identical on all operating systems.
195 #[allow(clippy::useless_conversion)]
opennull196 pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
197     let fd = path.with_nix_path(|cstr| {
198         unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
199     })?;
200 
201     Errno::result(fd)
202 }
203 
204 // The conversion is not identical on all operating systems.
205 #[allow(clippy::useless_conversion)]
206 #[cfg(not(target_os = "redox"))]
openatnull207 pub fn openat<P: ?Sized + NixPath>(
208     dirfd: RawFd,
209     path: &P,
210     oflag: OFlag,
211     mode: Mode,
212 ) -> Result<RawFd> {
213     let fd = path.with_nix_path(|cstr| {
214         unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
215     })?;
216     Errno::result(fd)
217 }
218 
219 #[cfg(not(target_os = "redox"))]
renameatnull220 pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
221     old_dirfd: Option<RawFd>,
222     old_path: &P1,
223     new_dirfd: Option<RawFd>,
224     new_path: &P2,
225 ) -> Result<()> {
226     let res = old_path.with_nix_path(|old_cstr| {
227         new_path.with_nix_path(|new_cstr| unsafe {
228             libc::renameat(
229                 at_rawfd(old_dirfd),
230                 old_cstr.as_ptr(),
231                 at_rawfd(new_dirfd),
232                 new_cstr.as_ptr(),
233             )
234         })
235     })??;
236     Errno::result(res).map(drop)
237 }
238 }
239 
240 #[cfg(all(target_os = "linux", target_env = "gnu",))]
241 #[cfg(feature = "fs")]
242 libc_bitflags! {
243     #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
244     pub struct RenameFlags: u32 {
245         RENAME_EXCHANGE;
246         RENAME_NOREPLACE;
247         RENAME_WHITEOUT;
248     }
249 }
250 
251 feature! {
252 #![feature = "fs"]
253 #[cfg(all(
254     target_os = "linux",
255     target_env = "gnu",
256 ))]
renameat2null257 pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
258     old_dirfd: Option<RawFd>,
259     old_path: &P1,
260     new_dirfd: Option<RawFd>,
261     new_path: &P2,
262     flags: RenameFlags,
263 ) -> Result<()> {
264     let res = old_path.with_nix_path(|old_cstr| {
265         new_path.with_nix_path(|new_cstr| unsafe {
266             libc::renameat2(
267                 at_rawfd(old_dirfd),
268                 old_cstr.as_ptr(),
269                 at_rawfd(new_dirfd),
270                 new_cstr.as_ptr(),
271                 flags.bits(),
272             )
273         })
274     })??;
275     Errno::result(res).map(drop)
276 }
277 
wrap_readlink_resultnull278 fn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> {
279     unsafe { v.set_len(len as usize) }
280     v.shrink_to_fit();
281     Ok(OsString::from_vec(v.to_vec()))
282 }
283 
readlink_maybe_atnull284 fn readlink_maybe_at<P: ?Sized + NixPath>(
285     dirfd: Option<RawFd>,
286     path: &P,
287     v: &mut Vec<u8>,
288 ) -> Result<libc::ssize_t> {
289     path.with_nix_path(|cstr| unsafe {
290         match dirfd {
291             #[cfg(target_os = "redox")]
292             Some(_) => unreachable!(),
293             #[cfg(not(target_os = "redox"))]
294             Some(dirfd) => libc::readlinkat(
295                 dirfd,
296                 cstr.as_ptr(),
297                 v.as_mut_ptr() as *mut c_char,
298                 v.capacity() as size_t,
299             ),
300             None => libc::readlink(
301                 cstr.as_ptr(),
302                 v.as_mut_ptr() as *mut c_char,
303                 v.capacity() as size_t,
304             ),
305         }
306     })
307 }
308 
inner_readlinknull309 fn inner_readlink<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P) -> Result<OsString> {
310     let mut v = Vec::with_capacity(libc::PATH_MAX as usize);
311     // simple case: result is strictly less than `PATH_MAX`
312     let res = readlink_maybe_at(dirfd, path, &mut v)?;
313     let len = Errno::result(res)?;
314     debug_assert!(len >= 0);
315     if (len as usize) < v.capacity() {
316         return wrap_readlink_result(v, res);
317     }
318     // Uh oh, the result is too long...
319     // Let's try to ask lstat how many bytes to allocate.
320     let reported_size = match dirfd {
321         #[cfg(target_os = "redox")]
322         Some(_) => unreachable!(),
323         #[cfg(any(target_os = "android", target_os = "linux"))]
324         Some(dirfd) => {
325             let flags = if path.is_empty() { AtFlags::AT_EMPTY_PATH } else { AtFlags::empty() };
326             super::sys::stat::fstatat(dirfd, path, flags | AtFlags::AT_SYMLINK_NOFOLLOW)
327         },
328         #[cfg(not(any(target_os = "android", target_os = "linux", target_os = "redox")))]
329         Some(dirfd) => super::sys::stat::fstatat(dirfd, path, AtFlags::AT_SYMLINK_NOFOLLOW),
330         None => super::sys::stat::lstat(path)
331     }
332         .map(|x| x.st_size)
333         .unwrap_or(0);
334     let mut try_size = if reported_size > 0 {
335         // Note: even if `lstat`'s apparently valid answer turns out to be
336         // wrong, we will still read the full symlink no matter what.
337         reported_size as usize + 1
338     } else {
339         // If lstat doesn't cooperate, or reports an error, be a little less
340         // precise.
341         (libc::PATH_MAX as usize).max(128) << 1
342     };
343     loop {
344         v.reserve_exact(try_size);
345         let res = readlink_maybe_at(dirfd, path, &mut v)?;
346         let len = Errno::result(res)?;
347         debug_assert!(len >= 0);
348         if (len as usize) < v.capacity() {
349             break wrap_readlink_result(v, res);
350         } else {
351             // Ugh! Still not big enough!
352             match try_size.checked_shl(1) {
353                 Some(next_size) => try_size = next_size,
354                 // It's absurd that this would happen, but handle it sanely
355                 // anyway.
356                 None => break Err(Errno::ENAMETOOLONG),
357             }
358         }
359     }
360 }
361 
readlinknull362 pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
363     inner_readlink(None, path)
364 }
365 
366 #[cfg(not(target_os = "redox"))]
readlinkatnull367 pub fn readlinkat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P) -> Result<OsString> {
368     inner_readlink(Some(dirfd), path)
369 }
370 
371 /// Computes the raw fd consumed by a function of the form `*at`.
372 #[cfg(not(target_os = "redox"))]
373 pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int {
374     match fd {
375         None => libc::AT_FDCWD,
376         Some(fd) => fd,
377     }
378 }
379 }
380 
381 #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
382 #[cfg(feature = "fs")]
383 libc_bitflags!(
384     /// Additional flags for file sealing, which allows for limiting operations on a file.
385     #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
386     pub struct SealFlag: c_int {
387         /// Prevents further calls to `fcntl()` with `F_ADD_SEALS`.
388         F_SEAL_SEAL;
389         /// The file cannot be reduced in size.
390         F_SEAL_SHRINK;
391         /// The size of the file cannot be increased.
392         F_SEAL_GROW;
393         /// The file contents cannot be modified.
394         F_SEAL_WRITE;
395     }
396 );
397 
398 #[cfg(feature = "fs")]
399 libc_bitflags!(
400     /// Additional configuration flags for `fcntl`'s `F_SETFD`.
401     #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
402     pub struct FdFlag: c_int {
403         /// The file descriptor will automatically be closed during a successful `execve(2)`.
404         FD_CLOEXEC;
405     }
406 );
407 
408 feature! {
409 #![feature = "fs"]
410 
411 #[cfg(not(target_os = "redox"))]
412 #[derive(Debug, Eq, Hash, PartialEq)]
413 #[non_exhaustive]
414 pub enum FcntlArg<'a> {
415     F_DUPFD(RawFd),
416     F_DUPFD_CLOEXEC(RawFd),
417     F_GETFD,
418     F_SETFD(FdFlag), // FD_FLAGS
419     F_GETFL,
420     F_SETFL(OFlag), // O_NONBLOCK
421     F_SETLK(&'a libc::flock),
422     F_SETLKW(&'a libc::flock),
423     F_GETLK(&'a mut libc::flock),
424     #[cfg(any(target_os = "linux", target_os = "android"))]
425     F_OFD_SETLK(&'a libc::flock),
426     #[cfg(any(target_os = "linux", target_os = "android"))]
427     F_OFD_SETLKW(&'a libc::flock),
428     #[cfg(any(target_os = "linux", target_os = "android"))]
429     F_OFD_GETLK(&'a mut libc::flock),
430     #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
431     F_ADD_SEALS(SealFlag),
432     #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
433     F_GET_SEALS,
434     #[cfg(any(target_os = "macos", target_os = "ios"))]
435     F_FULLFSYNC,
436     #[cfg(any(target_os = "linux", target_os = "android"))]
437     F_GETPIPE_SZ,
438     #[cfg(any(target_os = "linux", target_os = "android"))]
439     F_SETPIPE_SZ(c_int),
440     // TODO: Rest of flags
441 }
442 
443 #[cfg(target_os = "redox")]
444 #[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
445 #[non_exhaustive]
446 pub enum FcntlArg {
447     F_DUPFD(RawFd),
448     F_DUPFD_CLOEXEC(RawFd),
449     F_GETFD,
450     F_SETFD(FdFlag), // FD_FLAGS
451     F_GETFL,
452     F_SETFL(OFlag), // O_NONBLOCK
453 }
454 pub use self::FcntlArg::*;
455 
456 // TODO: Figure out how to handle value fcntl returns
fcntlnull457 pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
458     let res = unsafe {
459         match arg {
460             F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
461             F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd),
462             F_GETFD => libc::fcntl(fd, libc::F_GETFD),
463             F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()),
464             F_GETFL => libc::fcntl(fd, libc::F_GETFL),
465             F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()),
466             #[cfg(not(target_os = "redox"))]
467             F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock),
468             #[cfg(not(target_os = "redox"))]
469             F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
470             #[cfg(not(target_os = "redox"))]
471             F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
472             #[cfg(any(target_os = "android", target_os = "linux"))]
473             F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock),
474             #[cfg(any(target_os = "android", target_os = "linux"))]
475             F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock),
476             #[cfg(any(target_os = "android", target_os = "linux"))]
477             F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock),
478             #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
479             F_ADD_SEALS(flag) => libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()),
480             #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
481             F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS),
482             #[cfg(any(target_os = "macos", target_os = "ios"))]
483             F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC),
484             #[cfg(any(target_os = "linux", target_os = "android"))]
485             F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ),
486             #[cfg(any(target_os = "linux", target_os = "android"))]
487             F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size),
488         }
489     };
490 
491     Errno::result(res)
492 }
493 
494 // TODO: convert to libc_enum
495 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
496 #[non_exhaustive]
497 pub enum FlockArg {
498     LockShared,
499     LockExclusive,
500     Unlock,
501     LockSharedNonblock,
502     LockExclusiveNonblock,
503     UnlockNonblock,
504 }
505 
506 #[cfg(not(target_os = "redox"))]
flocknull507 pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
508     use self::FlockArg::*;
509 
510     let res = unsafe {
511         match arg {
512             LockShared => libc::flock(fd, libc::LOCK_SH),
513             LockExclusive => libc::flock(fd, libc::LOCK_EX),
514             Unlock => libc::flock(fd, libc::LOCK_UN),
515             LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB),
516             LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB),
517             UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB),
518         }
519     };
520 
521     Errno::result(res).map(drop)
522 }
523 }
524 
525 #[cfg(any(target_os = "android", target_os = "linux"))]
526 #[cfg(feature = "zerocopy")]
527 libc_bitflags! {
528     /// Additional flags to `splice` and friends.
529     #[cfg_attr(docsrs, doc(cfg(feature = "zerocopy")))]
530     pub struct SpliceFFlags: c_uint {
531         /// Request that pages be moved instead of copied.
532         ///
533         /// Not applicable to `vmsplice`.
534         SPLICE_F_MOVE;
535         /// Do not block on I/O.
536         SPLICE_F_NONBLOCK;
537         /// Hint that more data will be coming in a subsequent splice.
538         ///
539         /// Not applicable to `vmsplice`.
540         SPLICE_F_MORE;
541         /// Gift the user pages to the kernel.
542         ///
543         /// Not applicable to `splice`.
544         SPLICE_F_GIFT;
545     }
546 }
547 
548 feature! {
549 #![feature = "zerocopy"]
550 
551 /// Copy a range of data from one file to another
552 ///
553 /// The `copy_file_range` system call performs an in-kernel copy between
554 /// file descriptors `fd_in` and `fd_out` without the additional cost of
555 /// transferring data from the kernel to user space and then back into the
556 /// kernel. It copies up to `len` bytes of data from file descriptor `fd_in` to
557 /// file descriptor `fd_out`, overwriting any data that exists within the
558 /// requested range of the target file.
559 ///
560 /// If the `off_in` and/or `off_out` arguments are used, the values
561 /// will be mutated to reflect the new position within the file after
562 /// copying. If they are not used, the relevant filedescriptors will be seeked
563 /// to the new position.
564 ///
565 /// On successful completion the number of bytes actually copied will be
566 /// returned.
567 #[cfg(any(target_os = "android", target_os = "linux"))]
copy_file_rangenull568 pub fn copy_file_range(
569     fd_in: RawFd,
570     off_in: Option<&mut libc::loff_t>,
571     fd_out: RawFd,
572     off_out: Option<&mut libc::loff_t>,
573     len: usize,
574 ) -> Result<usize> {
575     let off_in = off_in
576         .map(|offset| offset as *mut libc::loff_t)
577         .unwrap_or(ptr::null_mut());
578     let off_out = off_out
579         .map(|offset| offset as *mut libc::loff_t)
580         .unwrap_or(ptr::null_mut());
581 
582     let ret = unsafe {
583         libc::syscall(
584             libc::SYS_copy_file_range,
585             fd_in,
586             off_in,
587             fd_out,
588             off_out,
589             len,
590             0,
591         )
592     };
593     Errno::result(ret).map(|r| r as usize)
594 }
595 
596 #[cfg(any(target_os = "linux", target_os = "android"))]
splicenull597 pub fn splice(
598     fd_in: RawFd,
599     off_in: Option<&mut libc::loff_t>,
600     fd_out: RawFd,
601     off_out: Option<&mut libc::loff_t>,
602     len: usize,
603     flags: SpliceFFlags,
604 ) -> Result<usize> {
605     let off_in = off_in
606         .map(|offset| offset as *mut libc::loff_t)
607         .unwrap_or(ptr::null_mut());
608     let off_out = off_out
609         .map(|offset| offset as *mut libc::loff_t)
610         .unwrap_or(ptr::null_mut());
611 
612     let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) };
613     Errno::result(ret).map(|r| r as usize)
614 }
615 
616 #[cfg(any(target_os = "linux", target_os = "android"))]
teenull617 pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> {
618     let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) };
619     Errno::result(ret).map(|r| r as usize)
620 }
621 
622 #[cfg(any(target_os = "linux", target_os = "android"))]
vmsplicenull623 pub fn vmsplice(
624     fd: RawFd,
625     iov: &[std::io::IoSlice<'_>],
626     flags: SpliceFFlags
627     ) -> Result<usize>
628 {
629     let ret = unsafe {
630         libc::vmsplice(
631             fd,
632             iov.as_ptr() as *const libc::iovec,
633             iov.len(),
634             flags.bits(),
635         )
636     };
637     Errno::result(ret).map(|r| r as usize)
638 }
639 }
640 
641 #[cfg(any(target_os = "linux"))]
642 #[cfg(feature = "fs")]
643 libc_bitflags!(
644     /// Mode argument flags for fallocate determining operation performed on a given range.
645     #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
646     pub struct FallocateFlags: c_int {
647         /// File size is not changed.
648         ///
649         /// offset + len can be greater than file size.
650         FALLOC_FL_KEEP_SIZE;
651         /// Deallocates space by creating a hole.
652         ///
653         /// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes.
654         FALLOC_FL_PUNCH_HOLE;
655         /// Removes byte range from a file without leaving a hole.
656         ///
657         /// Byte range to collapse starts at offset and continues for len bytes.
658         FALLOC_FL_COLLAPSE_RANGE;
659         /// Zeroes space in specified byte range.
660         ///
661         /// Byte range starts at offset and continues for len bytes.
662         FALLOC_FL_ZERO_RANGE;
663         /// Increases file space by inserting a hole within the file size.
664         ///
665         /// Does not overwrite existing data. Hole starts at offset and continues for len bytes.
666         FALLOC_FL_INSERT_RANGE;
667         /// Shared file data extants are made private to the file.
668         ///
669         /// Gaurantees that a subsequent write will not fail due to lack of space.
670         FALLOC_FL_UNSHARE_RANGE;
671     }
672 );
673 
674 feature! {
675 #![feature = "fs"]
676 
677 /// Manipulates file space.
678 ///
679 /// Allows the caller to directly manipulate the allocated disk space for the
680 /// file referred to by fd.
681 #[cfg(any(target_os = "linux"))]
682 #[cfg(feature = "fs")]
fallocatenull683 pub fn fallocate(
684     fd: RawFd,
685     mode: FallocateFlags,
686     offset: libc::off_t,
687     len: libc::off_t,
688 ) -> Result<()> {
689     let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) };
690     Errno::result(res).map(drop)
691 }
692 
693 /// Argument to [`fspacectl`] describing the range to zero.  The first member is
694 /// the file offset, and the second is the length of the region.
695 #[cfg(any(target_os = "freebsd"))]
696 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
697 pub struct SpacectlRange(pub libc::off_t, pub libc::off_t);
698 
699 #[cfg(any(target_os = "freebsd"))]
700 impl SpacectlRange {
701     #[inline]
is_emptynull702     pub fn is_empty(&self) -> bool {
703         self.1 == 0
704     }
705 
706     #[inline]
lennull707     pub fn len(&self) -> libc::off_t {
708         self.1
709     }
710 
711     #[inline]
offsetnull712     pub fn offset(&self) -> libc::off_t {
713         self.0
714     }
715 }
716 
717 /// Punch holes in a file.
718 ///
719 /// `fspacectl` instructs the file system to deallocate a portion of a file.
720 /// After a successful operation, this region of the file will return all zeroes
721 /// if read.  If the file system supports deallocation, then it may free the
722 /// underlying storage, too.
723 ///
724 /// # Arguments
725 ///
726 /// - `fd`      -   File to operate on
727 /// - `range.0` -   File offset at which to begin deallocation
728 /// - `range.1` -   Length of the region to deallocate
729 ///
730 /// # Returns
731 ///
732 /// The operation may deallocate less than the entire requested region.  On
733 /// success, it returns the region that still remains to be deallocated.  The
734 /// caller should loop until the returned region is empty.
735 ///
736 /// # Example
737 ///
738 #[cfg_attr(fbsd14, doc = " ```")]
739 #[cfg_attr(not(fbsd14), doc = " ```no_run")]
740 /// # use std::io::Write;
741 /// # use std::os::unix::fs::FileExt;
742 /// # use std::os::unix::io::AsRawFd;
743 /// # use nix::fcntl::*;
744 /// # use tempfile::tempfile;
745 /// const INITIAL: &[u8] = b"0123456789abcdef";
746 /// let mut f = tempfile().unwrap();
747 /// f.write_all(INITIAL).unwrap();
748 /// let mut range = SpacectlRange(3, 6);
749 /// while (!range.is_empty()) {
750 ///     range = fspacectl(f.as_raw_fd(), range).unwrap();
751 /// }
752 /// let mut buf = vec![0; INITIAL.len()];
753 /// f.read_exact_at(&mut buf, 0).unwrap();
754 /// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
755 /// ```
756 #[cfg(target_os = "freebsd")]
fspacectlnull757 pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> {
758     let mut rqsr = libc::spacectl_range{r_offset: range.0, r_len: range.1};
759     let res = unsafe { libc::fspacectl(
760             fd,
761             libc::SPACECTL_DEALLOC, // Only one command is supported ATM
762             &rqsr,
763             0,                      // No flags are currently supported
764             &mut rqsr
765     )};
766     Errno::result(res).map(|_| SpacectlRange(rqsr.r_offset, rqsr.r_len))
767 }
768 
769 /// Like [`fspacectl`], but will never return incomplete.
770 ///
771 /// # Arguments
772 ///
773 /// - `fd`      -   File to operate on
774 /// - `offset`  -   File offset at which to begin deallocation
775 /// - `len`     -   Length of the region to deallocate
776 ///
777 /// # Returns
778 ///
779 /// Returns `()` on success.  On failure, the region may or may not be partially
780 /// deallocated.
781 ///
782 /// # Example
783 ///
784 #[cfg_attr(fbsd14, doc = " ```")]
785 #[cfg_attr(not(fbsd14), doc = " ```no_run")]
786 /// # use std::io::Write;
787 /// # use std::os::unix::fs::FileExt;
788 /// # use std::os::unix::io::AsRawFd;
789 /// # use nix::fcntl::*;
790 /// # use tempfile::tempfile;
791 /// const INITIAL: &[u8] = b"0123456789abcdef";
792 /// let mut f = tempfile().unwrap();
793 /// f.write_all(INITIAL).unwrap();
794 /// fspacectl_all(f.as_raw_fd(), 3, 6).unwrap();
795 /// let mut buf = vec![0; INITIAL.len()];
796 /// f.read_exact_at(&mut buf, 0).unwrap();
797 /// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
798 /// ```
799 #[cfg(target_os = "freebsd")]
fspacectl_allnull800 pub fn fspacectl_all(fd: RawFd, offset: libc::off_t, len: libc::off_t)
801     -> Result<()>
802 {
803     let mut rqsr = libc::spacectl_range{r_offset: offset, r_len: len};
804     while rqsr.r_len > 0 {
805         let res = unsafe { libc::fspacectl(
806                 fd,
807                 libc::SPACECTL_DEALLOC, // Only one command is supported ATM
808                 &rqsr,
809                 0,                      // No flags are currently supported
810                 &mut rqsr
811         )};
812         Errno::result(res)?;
813     }
814     Ok(())
815 }
816 
817 #[cfg(any(
818     target_os = "linux",
819     target_os = "android",
820     target_os = "emscripten",
821     target_os = "fuchsia",
822     target_os = "wasi",
823     target_env = "uclibc",
824     target_os = "freebsd"
825 ))]
826 mod posix_fadvise {
827     use crate::errno::Errno;
828     use std::os::unix::io::RawFd;
829     use crate::Result;
830 
831     #[cfg(feature = "fs")]
832     libc_enum! {
833         #[repr(i32)]
834         #[non_exhaustive]
835         #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
836         pub enum PosixFadviseAdvice {
837             POSIX_FADV_NORMAL,
838             POSIX_FADV_SEQUENTIAL,
839             POSIX_FADV_RANDOM,
840             POSIX_FADV_NOREUSE,
841             POSIX_FADV_WILLNEED,
842             POSIX_FADV_DONTNEED,
843         }
844     }
845 
846     feature! {
847     #![feature = "fs"]
posix_fadvisenull848     pub fn posix_fadvise(
849         fd: RawFd,
850         offset: libc::off_t,
851         len: libc::off_t,
852         advice: PosixFadviseAdvice,
853     ) -> Result<()> {
854         let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) };
855 
856         if res == 0 {
857             Ok(())
858         } else {
859             Err(Errno::from_i32(res))
860         }
861     }
862     }
863 }
864 
865 #[cfg(any(
866     target_os = "linux",
867     target_os = "android",
868     target_os = "dragonfly",
869     target_os = "emscripten",
870     target_os = "fuchsia",
871     target_os = "wasi",
872     target_os = "freebsd"
873 ))]
posix_fallocatenull874 pub fn posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Result<()> {
875     let res = unsafe { libc::posix_fallocate(fd, offset, len) };
876     match Errno::result(res) {
877         Err(err) => Err(err),
878         Ok(0) => Ok(()),
879         Ok(errno) => Err(Errno::from_i32(errno)),
880     }
881 }
882 }
883