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;
8
9#[cfg(feature = "fs")]
10use crate::{sys::stat::Mode, NixPath, Result};
11#[cfg(any(target_os = "android", target_os = "linux"))]
12use 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")]
24pub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice};
25
26#[cfg(not(target_os = "redox"))]
27#[cfg(any(feature = "fs", feature = "process"))]
28libc_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"))]
44libc_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(target_env = "musl")),
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
191feature! {
192#![feature = "fs"]
193
194// The conversion is not identical on all operating systems.
195#[allow(clippy::useless_conversion)]
196pub 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"))]
207pub 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"))]
220pub 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")]
242libc_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
251feature! {
252#![feature = "fs"]
253#[cfg(all(
254 target_os = "linux",
255 target_env = "gnu",
256))]
257pub 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
278fn 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
284fn 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
309fn 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
362pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
363 inner_readlink(None, path)
364}
365
366#[cfg(not(target_os = "redox"))]
367pub 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"))]
373pub(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")]
383libc_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")]
399libc_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
408feature! {
409#![feature = "fs"]
410
411#[cfg(not(target_os = "redox"))]
412#[derive(Debug, Eq, Hash, PartialEq)]
413#[non_exhaustive]
414pub 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]
446pub 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}
454pub use self::FcntlArg::*;
455
456// TODO: Figure out how to handle value fcntl returns
457pub 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]
497pub enum FlockArg {
498 LockShared,
499 LockExclusive,
500 Unlock,
501 LockSharedNonblock,
502 LockExclusiveNonblock,
503 UnlockNonblock,
504}
505
506#[cfg(not(target_os = "redox"))]
507pub 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")]
527libc_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
548feature! {
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"))]
568pub 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"))]
597pub 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"))]
617pub 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"))]
623pub 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(target_os = "linux")]
642#[cfg(feature = "fs")]
643libc_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
674feature! {
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(target_os = "linux")]
682#[cfg(feature = "fs")]
683pub 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)]
697pub struct SpacectlRange(pub libc::off_t, pub libc::off_t);
698
699#[cfg(any(target_os = "freebsd"))]
700impl SpacectlRange {
701 #[inline]
702 pub fn is_empty(&self) -> bool {
703 self.1 == 0
704 }
705
706 #[inline]
707 pub fn len(&self) -> libc::off_t {
708 self.1
709 }
710
711 #[inline]
712 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")]
757pub 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")]
800pub 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))]
826mod 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"]
848 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))]
874pub 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