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