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)] 196 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"))] 207 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"))] 220 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 ))] 257 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 278 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 284 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 309 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 362 pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> { 363 inner_readlink(None, path) 364 } 365 366 #[cfg(not(target_os = "redox"))] 367 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 457 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"))] 507 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"))] 568 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"))] 597 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"))] 617 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"))] 623 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")] 683 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] 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")] 757 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")] 800 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"] 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 ))] 874 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