| Andrew Walbran | 12f6140 | 2020-10-14 11:10:53 +0100 | [diff] [blame^] | 1 | use crate::errno::Errno; |
| 2 | use libc::{self, c_char, c_int, c_uint, size_t, ssize_t}; |
| 3 | use std::ffi::OsString; |
| 4 | #[cfg(not(target_os = "redox"))] |
| 5 | use std::os::raw; |
| 6 | use std::os::unix::ffi::OsStringExt; |
| 7 | use std::os::unix::io::RawFd; |
| 8 | use crate::sys::stat::Mode; |
| 9 | use crate::{NixPath, Result}; |
| 10 | |
| 11 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 12 | use std::ptr; // For splice and copy_file_range |
| 13 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 14 | use crate::sys::uio::IoVec; // For vmsplice |
| 15 | |
| 16 | #[cfg(any( |
| 17 | target_os = "linux", |
| 18 | target_os = "android", |
| 19 | target_os = "emscripten", |
| 20 | target_os = "fuchsia", |
| 21 | any(target_os = "wasi", target_env = "wasi"), |
| 22 | target_env = "uclibc", |
| 23 | target_env = "freebsd" |
| 24 | ))] |
| 25 | pub use self::posix_fadvise::*; |
| 26 | |
| 27 | #[cfg(not(target_os = "redox"))] |
| 28 | libc_bitflags! { |
| 29 | pub struct AtFlags: c_int { |
| 30 | AT_REMOVEDIR; |
| 31 | AT_SYMLINK_FOLLOW; |
| 32 | AT_SYMLINK_NOFOLLOW; |
| 33 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 34 | AT_NO_AUTOMOUNT; |
| 35 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 36 | AT_EMPTY_PATH; |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | libc_bitflags!( |
| 41 | /// Configuration options for opened files. |
| 42 | pub struct OFlag: c_int { |
| 43 | /// Mask for the access mode of the file. |
| 44 | O_ACCMODE; |
| 45 | /// Use alternate I/O semantics. |
| 46 | #[cfg(target_os = "netbsd")] |
| 47 | O_ALT_IO; |
| 48 | /// Open the file in append-only mode. |
| 49 | O_APPEND; |
| 50 | /// Generate a signal when input or output becomes possible. |
| 51 | O_ASYNC; |
| 52 | /// Closes the file descriptor once an `execve` call is made. |
| 53 | /// |
| 54 | /// Also sets the file offset to the beginning of the file. |
| 55 | O_CLOEXEC; |
| 56 | /// Create the file if it does not exist. |
| 57 | O_CREAT; |
| 58 | /// Try to minimize cache effects of the I/O for this file. |
| 59 | #[cfg(any(target_os = "android", |
| 60 | target_os = "dragonfly", |
| 61 | target_os = "freebsd", |
| 62 | target_os = "linux", |
| 63 | target_os = "netbsd"))] |
| 64 | O_DIRECT; |
| 65 | /// If the specified path isn't a directory, fail. |
| 66 | O_DIRECTORY; |
| 67 | /// Implicitly follow each `write()` with an `fdatasync()`. |
| 68 | #[cfg(any(target_os = "android", |
| 69 | target_os = "ios", |
| 70 | target_os = "linux", |
| 71 | target_os = "macos", |
| 72 | target_os = "netbsd", |
| 73 | target_os = "openbsd"))] |
| 74 | O_DSYNC; |
| 75 | /// Error out if a file was not created. |
| 76 | O_EXCL; |
| 77 | /// Open for execute only. |
| 78 | #[cfg(target_os = "freebsd")] |
| 79 | O_EXEC; |
| 80 | /// Open with an exclusive file lock. |
| 81 | #[cfg(any(target_os = "dragonfly", |
| 82 | target_os = "freebsd", |
| 83 | target_os = "ios", |
| 84 | target_os = "macos", |
| 85 | target_os = "netbsd", |
| 86 | target_os = "openbsd", |
| 87 | target_os = "redox"))] |
| 88 | O_EXLOCK; |
| 89 | /// Same as `O_SYNC`. |
| 90 | #[cfg(any(target_os = "dragonfly", |
| 91 | target_os = "freebsd", |
| 92 | target_os = "ios", |
| 93 | all(target_os = "linux", not(target_env = "musl")), |
| 94 | target_os = "macos", |
| 95 | target_os = "netbsd", |
| 96 | target_os = "openbsd", |
| 97 | target_os = "redox"))] |
| 98 | O_FSYNC; |
| 99 | /// Allow files whose sizes can't be represented in an `off_t` to be opened. |
| 100 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 101 | O_LARGEFILE; |
| 102 | /// Do not update the file last access time during `read(2)`s. |
| 103 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 104 | O_NOATIME; |
| 105 | /// Don't attach the device as the process' controlling terminal. |
| 106 | #[cfg(not(target_os = "redox"))] |
| 107 | O_NOCTTY; |
| 108 | /// Same as `O_NONBLOCK`. |
| 109 | #[cfg(not(target_os = "redox"))] |
| 110 | O_NDELAY; |
| 111 | /// `open()` will fail if the given path is a symbolic link. |
| 112 | O_NOFOLLOW; |
| 113 | /// When possible, open the file in nonblocking mode. |
| 114 | O_NONBLOCK; |
| 115 | /// Don't deliver `SIGPIPE`. |
| 116 | #[cfg(target_os = "netbsd")] |
| 117 | O_NOSIGPIPE; |
| 118 | /// Obtain a file descriptor for low-level access. |
| 119 | /// |
| 120 | /// The file itself is not opened and other file operations will fail. |
| 121 | #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))] |
| 122 | O_PATH; |
| 123 | /// Only allow reading. |
| 124 | /// |
| 125 | /// This should not be combined with `O_WRONLY` or `O_RDWR`. |
| 126 | O_RDONLY; |
| 127 | /// Allow both reading and writing. |
| 128 | /// |
| 129 | /// This should not be combined with `O_WRONLY` or `O_RDONLY`. |
| 130 | O_RDWR; |
| 131 | /// Similar to `O_DSYNC` but applies to `read`s instead. |
| 132 | #[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))] |
| 133 | O_RSYNC; |
| 134 | /// Skip search permission checks. |
| 135 | #[cfg(target_os = "netbsd")] |
| 136 | O_SEARCH; |
| 137 | /// Open with a shared file lock. |
| 138 | #[cfg(any(target_os = "dragonfly", |
| 139 | target_os = "freebsd", |
| 140 | target_os = "ios", |
| 141 | target_os = "macos", |
| 142 | target_os = "netbsd", |
| 143 | target_os = "openbsd", |
| 144 | target_os = "redox"))] |
| 145 | O_SHLOCK; |
| 146 | /// Implicitly follow each `write()` with an `fsync()`. |
| 147 | #[cfg(not(target_os = "redox"))] |
| 148 | O_SYNC; |
| 149 | /// Create an unnamed temporary file. |
| 150 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 151 | O_TMPFILE; |
| 152 | /// Truncate an existing regular file to 0 length if it allows writing. |
| 153 | O_TRUNC; |
| 154 | /// Restore default TTY attributes. |
| 155 | #[cfg(target_os = "freebsd")] |
| 156 | O_TTY_INIT; |
| 157 | /// Only allow writing. |
| 158 | /// |
| 159 | /// This should not be combined with `O_RDONLY` or `O_RDWR`. |
| 160 | O_WRONLY; |
| 161 | } |
| 162 | ); |
| 163 | |
| 164 | // The conversion is not identical on all operating systems. |
| 165 | #[allow(clippy::identity_conversion)] |
| 166 | pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> { |
| 167 | let fd = path.with_nix_path(|cstr| { |
| 168 | unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) } |
| 169 | })?; |
| 170 | |
| 171 | Errno::result(fd) |
| 172 | } |
| 173 | |
| 174 | // The conversion is not identical on all operating systems. |
| 175 | #[allow(clippy::identity_conversion)] |
| 176 | #[cfg(not(target_os = "redox"))] |
| 177 | pub fn openat<P: ?Sized + NixPath>( |
| 178 | dirfd: RawFd, |
| 179 | path: &P, |
| 180 | oflag: OFlag, |
| 181 | mode: Mode, |
| 182 | ) -> Result<RawFd> { |
| 183 | let fd = path.with_nix_path(|cstr| { |
| 184 | unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) } |
| 185 | })?; |
| 186 | Errno::result(fd) |
| 187 | } |
| 188 | |
| 189 | #[cfg(not(target_os = "redox"))] |
| 190 | pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>( |
| 191 | old_dirfd: Option<RawFd>, |
| 192 | old_path: &P1, |
| 193 | new_dirfd: Option<RawFd>, |
| 194 | new_path: &P2, |
| 195 | ) -> Result<()> { |
| 196 | let res = old_path.with_nix_path(|old_cstr| { |
| 197 | new_path.with_nix_path(|new_cstr| unsafe { |
| 198 | libc::renameat( |
| 199 | at_rawfd(old_dirfd), |
| 200 | old_cstr.as_ptr(), |
| 201 | at_rawfd(new_dirfd), |
| 202 | new_cstr.as_ptr(), |
| 203 | ) |
| 204 | }) |
| 205 | })??; |
| 206 | Errno::result(res).map(drop) |
| 207 | } |
| 208 | |
| 209 | fn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> { |
| 210 | unsafe { v.set_len(len as usize) } |
| 211 | v.shrink_to_fit(); |
| 212 | Ok(OsString::from_vec(v.to_vec())) |
| 213 | } |
| 214 | |
| 215 | fn readlink_maybe_at<P: ?Sized + NixPath>( |
| 216 | dirfd: Option<RawFd>, |
| 217 | path: &P, |
| 218 | v: &mut Vec<u8>, |
| 219 | ) -> Result<libc::ssize_t> { |
| 220 | path.with_nix_path(|cstr| unsafe { |
| 221 | match dirfd { |
| 222 | #[cfg(target_os = "redox")] |
| 223 | Some(_) => unreachable!(), |
| 224 | #[cfg(not(target_os = "redox"))] |
| 225 | Some(dirfd) => libc::readlinkat( |
| 226 | dirfd, |
| 227 | cstr.as_ptr(), |
| 228 | v.as_mut_ptr() as *mut c_char, |
| 229 | v.capacity() as size_t, |
| 230 | ), |
| 231 | None => libc::readlink( |
| 232 | cstr.as_ptr(), |
| 233 | v.as_mut_ptr() as *mut c_char, |
| 234 | v.capacity() as size_t, |
| 235 | ), |
| 236 | } |
| 237 | }) |
| 238 | } |
| 239 | |
| 240 | fn inner_readlink<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P) -> Result<OsString> { |
| 241 | let mut v = Vec::with_capacity(libc::PATH_MAX as usize); |
| 242 | // simple case: result is strictly less than `PATH_MAX` |
| 243 | let res = readlink_maybe_at(dirfd, path, &mut v)?; |
| 244 | let len = Errno::result(res)?; |
| 245 | debug_assert!(len >= 0); |
| 246 | if (len as usize) < v.capacity() { |
| 247 | return wrap_readlink_result(v, res); |
| 248 | } |
| 249 | // Uh oh, the result is too long... |
| 250 | // Let's try to ask lstat how many bytes to allocate. |
| 251 | let reported_size = super::sys::stat::lstat(path) |
| 252 | .and_then(|x| Ok(x.st_size)) |
| 253 | .unwrap_or(0); |
| 254 | let mut try_size = if reported_size > 0 { |
| 255 | // Note: even if `lstat`'s apparently valid answer turns out to be |
| 256 | // wrong, we will still read the full symlink no matter what. |
| 257 | reported_size as usize + 1 |
| 258 | } else { |
| 259 | // If lstat doesn't cooperate, or reports an error, be a little less |
| 260 | // precise. |
| 261 | (libc::PATH_MAX as usize).max(128) << 1 |
| 262 | }; |
| 263 | loop { |
| 264 | v.reserve_exact(try_size); |
| 265 | let res = readlink_maybe_at(dirfd, path, &mut v)?; |
| 266 | let len = Errno::result(res)?; |
| 267 | debug_assert!(len >= 0); |
| 268 | if (len as usize) < v.capacity() { |
| 269 | break wrap_readlink_result(v, res); |
| 270 | } else { |
| 271 | // Ugh! Still not big enough! |
| 272 | match try_size.checked_shl(1) { |
| 273 | Some(next_size) => try_size = next_size, |
| 274 | // It's absurd that this would happen, but handle it sanely |
| 275 | // anyway. |
| 276 | None => break Err(super::Error::Sys(Errno::ENAMETOOLONG)), |
| 277 | } |
| 278 | } |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> { |
| 283 | inner_readlink(None, path) |
| 284 | } |
| 285 | |
| 286 | #[cfg(not(target_os = "redox"))] |
| 287 | pub fn readlinkat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P) -> Result<OsString> { |
| 288 | inner_readlink(Some(dirfd), path) |
| 289 | } |
| 290 | |
| 291 | /// Computes the raw fd consumed by a function of the form `*at`. |
| 292 | #[cfg(not(target_os = "redox"))] |
| 293 | pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int { |
| 294 | match fd { |
| 295 | None => libc::AT_FDCWD, |
| 296 | Some(fd) => fd, |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 301 | libc_bitflags!( |
| 302 | /// Additional flags for file sealing, which allows for limiting operations on a file. |
| 303 | pub struct SealFlag: c_int { |
| 304 | /// Prevents further calls to `fcntl()` with `F_ADD_SEALS`. |
| 305 | F_SEAL_SEAL; |
| 306 | /// The file cannot be reduced in size. |
| 307 | F_SEAL_SHRINK; |
| 308 | /// The size of the file cannot be increased. |
| 309 | F_SEAL_GROW; |
| 310 | /// The file contents cannot be modified. |
| 311 | F_SEAL_WRITE; |
| 312 | } |
| 313 | ); |
| 314 | |
| 315 | libc_bitflags!( |
| 316 | /// Additional configuration flags for `fcntl`'s `F_SETFD`. |
| 317 | pub struct FdFlag: c_int { |
| 318 | /// The file descriptor will automatically be closed during a successful `execve(2)`. |
| 319 | FD_CLOEXEC; |
| 320 | } |
| 321 | ); |
| 322 | |
| 323 | #[cfg(not(target_os = "redox"))] |
| 324 | #[derive(Debug, Eq, Hash, PartialEq)] |
| 325 | pub enum FcntlArg<'a> { |
| 326 | F_DUPFD(RawFd), |
| 327 | F_DUPFD_CLOEXEC(RawFd), |
| 328 | F_GETFD, |
| 329 | F_SETFD(FdFlag), // FD_FLAGS |
| 330 | F_GETFL, |
| 331 | F_SETFL(OFlag), // O_NONBLOCK |
| 332 | F_SETLK(&'a libc::flock), |
| 333 | F_SETLKW(&'a libc::flock), |
| 334 | F_GETLK(&'a mut libc::flock), |
| 335 | #[cfg(any(target_os = "linux", target_os = "android"))] |
| 336 | F_OFD_SETLK(&'a libc::flock), |
| 337 | #[cfg(any(target_os = "linux", target_os = "android"))] |
| 338 | F_OFD_SETLKW(&'a libc::flock), |
| 339 | #[cfg(any(target_os = "linux", target_os = "android"))] |
| 340 | F_OFD_GETLK(&'a mut libc::flock), |
| 341 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 342 | F_ADD_SEALS(SealFlag), |
| 343 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 344 | F_GET_SEALS, |
| 345 | #[cfg(any(target_os = "macos", target_os = "ios"))] |
| 346 | F_FULLFSYNC, |
| 347 | #[cfg(any(target_os = "linux", target_os = "android"))] |
| 348 | F_GETPIPE_SZ, |
| 349 | #[cfg(any(target_os = "linux", target_os = "android"))] |
| 350 | F_SETPIPE_SZ(c_int), |
| 351 | // TODO: Rest of flags |
| 352 | } |
| 353 | |
| 354 | #[cfg(target_os = "redox")] |
| 355 | #[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)] |
| 356 | pub enum FcntlArg { |
| 357 | F_DUPFD(RawFd), |
| 358 | F_DUPFD_CLOEXEC(RawFd), |
| 359 | F_GETFD, |
| 360 | F_SETFD(FdFlag), // FD_FLAGS |
| 361 | F_GETFL, |
| 362 | F_SETFL(OFlag), // O_NONBLOCK |
| 363 | } |
| 364 | pub use self::FcntlArg::*; |
| 365 | |
| 366 | // TODO: Figure out how to handle value fcntl returns |
| 367 | pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> { |
| 368 | let res = unsafe { |
| 369 | match arg { |
| 370 | F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd), |
| 371 | F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd), |
| 372 | F_GETFD => libc::fcntl(fd, libc::F_GETFD), |
| 373 | F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()), |
| 374 | F_GETFL => libc::fcntl(fd, libc::F_GETFL), |
| 375 | F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()), |
| 376 | #[cfg(not(target_os = "redox"))] |
| 377 | F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock), |
| 378 | #[cfg(not(target_os = "redox"))] |
| 379 | F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock), |
| 380 | #[cfg(not(target_os = "redox"))] |
| 381 | F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock), |
| 382 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 383 | F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock), |
| 384 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 385 | F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock), |
| 386 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 387 | F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock), |
| 388 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 389 | F_ADD_SEALS(flag) => libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()), |
| 390 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 391 | F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS), |
| 392 | #[cfg(any(target_os = "macos", target_os = "ios"))] |
| 393 | F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC), |
| 394 | #[cfg(any(target_os = "linux", target_os = "android"))] |
| 395 | F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ), |
| 396 | #[cfg(any(target_os = "linux", target_os = "android"))] |
| 397 | F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size), |
| 398 | } |
| 399 | }; |
| 400 | |
| 401 | Errno::result(res) |
| 402 | } |
| 403 | |
| 404 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
| 405 | pub enum FlockArg { |
| 406 | LockShared, |
| 407 | LockExclusive, |
| 408 | Unlock, |
| 409 | LockSharedNonblock, |
| 410 | LockExclusiveNonblock, |
| 411 | UnlockNonblock, |
| 412 | } |
| 413 | |
| 414 | #[cfg(not(target_os = "redox"))] |
| 415 | pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> { |
| 416 | use self::FlockArg::*; |
| 417 | |
| 418 | let res = unsafe { |
| 419 | match arg { |
| 420 | LockShared => libc::flock(fd, libc::LOCK_SH), |
| 421 | LockExclusive => libc::flock(fd, libc::LOCK_EX), |
| 422 | Unlock => libc::flock(fd, libc::LOCK_UN), |
| 423 | LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB), |
| 424 | LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB), |
| 425 | UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB), |
| 426 | } |
| 427 | }; |
| 428 | |
| 429 | Errno::result(res).map(drop) |
| 430 | } |
| 431 | |
| 432 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 433 | libc_bitflags! { |
| 434 | /// Additional flags to `splice` and friends. |
| 435 | pub struct SpliceFFlags: c_uint { |
| 436 | /// Request that pages be moved instead of copied. |
| 437 | /// |
| 438 | /// Not applicable to `vmsplice`. |
| 439 | SPLICE_F_MOVE; |
| 440 | /// Do not block on I/O. |
| 441 | SPLICE_F_NONBLOCK; |
| 442 | /// Hint that more data will be coming in a subsequent splice. |
| 443 | /// |
| 444 | /// Not applicable to `vmsplice`. |
| 445 | SPLICE_F_MORE; |
| 446 | /// Gift the user pages to the kernel. |
| 447 | /// |
| 448 | /// Not applicable to `splice`. |
| 449 | SPLICE_F_GIFT; |
| 450 | } |
| 451 | } |
| 452 | |
| 453 | /// Copy a range of data from one file to another |
| 454 | /// |
| 455 | /// The `copy_file_range` system call performs an in-kernel copy between |
| 456 | /// file descriptors `fd_in` and `fd_out` without the additional cost of |
| 457 | /// transferring data from the kernel to user space and then back into the |
| 458 | /// kernel. It copies up to `len` bytes of data from file descriptor `fd_in` to |
| 459 | /// file descriptor `fd_out`, overwriting any data that exists within the |
| 460 | /// requested range of the target file. |
| 461 | /// |
| 462 | /// If the `off_in` and/or `off_out` arguments are used, the values |
| 463 | /// will be mutated to reflect the new position within the file after |
| 464 | /// copying. If they are not used, the relevant filedescriptors will be seeked |
| 465 | /// to the new position. |
| 466 | /// |
| 467 | /// On successful completion the number of bytes actually copied will be |
| 468 | /// returned. |
| 469 | #[cfg(any(target_os = "android", target_os = "linux"))] |
| 470 | pub fn copy_file_range( |
| 471 | fd_in: RawFd, |
| 472 | off_in: Option<&mut libc::loff_t>, |
| 473 | fd_out: RawFd, |
| 474 | off_out: Option<&mut libc::loff_t>, |
| 475 | len: usize, |
| 476 | ) -> Result<usize> { |
| 477 | let off_in = off_in |
| 478 | .map(|offset| offset as *mut libc::loff_t) |
| 479 | .unwrap_or(ptr::null_mut()); |
| 480 | let off_out = off_out |
| 481 | .map(|offset| offset as *mut libc::loff_t) |
| 482 | .unwrap_or(ptr::null_mut()); |
| 483 | |
| 484 | let ret = unsafe { |
| 485 | libc::syscall( |
| 486 | libc::SYS_copy_file_range, |
| 487 | fd_in, |
| 488 | off_in, |
| 489 | fd_out, |
| 490 | off_out, |
| 491 | len, |
| 492 | 0, |
| 493 | ) |
| 494 | }; |
| 495 | Errno::result(ret).map(|r| r as usize) |
| 496 | } |
| 497 | |
| 498 | #[cfg(any(target_os = "linux", target_os = "android"))] |
| 499 | pub fn splice( |
| 500 | fd_in: RawFd, |
| 501 | off_in: Option<&mut libc::loff_t>, |
| 502 | fd_out: RawFd, |
| 503 | off_out: Option<&mut libc::loff_t>, |
| 504 | len: usize, |
| 505 | flags: SpliceFFlags, |
| 506 | ) -> Result<usize> { |
| 507 | let off_in = off_in |
| 508 | .map(|offset| offset as *mut libc::loff_t) |
| 509 | .unwrap_or(ptr::null_mut()); |
| 510 | let off_out = off_out |
| 511 | .map(|offset| offset as *mut libc::loff_t) |
| 512 | .unwrap_or(ptr::null_mut()); |
| 513 | |
| 514 | let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) }; |
| 515 | Errno::result(ret).map(|r| r as usize) |
| 516 | } |
| 517 | |
| 518 | #[cfg(any(target_os = "linux", target_os = "android"))] |
| 519 | pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> { |
| 520 | let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) }; |
| 521 | Errno::result(ret).map(|r| r as usize) |
| 522 | } |
| 523 | |
| 524 | #[cfg(any(target_os = "linux", target_os = "android"))] |
| 525 | pub fn vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result<usize> { |
| 526 | let ret = unsafe { |
| 527 | libc::vmsplice( |
| 528 | fd, |
| 529 | iov.as_ptr() as *const libc::iovec, |
| 530 | iov.len(), |
| 531 | flags.bits(), |
| 532 | ) |
| 533 | }; |
| 534 | Errno::result(ret).map(|r| r as usize) |
| 535 | } |
| 536 | |
| 537 | #[cfg(any(target_os = "linux"))] |
| 538 | libc_bitflags!( |
| 539 | /// Mode argument flags for fallocate determining operation performed on a given range. |
| 540 | pub struct FallocateFlags: c_int { |
| 541 | /// File size is not changed. |
| 542 | /// |
| 543 | /// offset + len can be greater than file size. |
| 544 | FALLOC_FL_KEEP_SIZE; |
| 545 | /// Deallocates space by creating a hole. |
| 546 | /// |
| 547 | /// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes. |
| 548 | FALLOC_FL_PUNCH_HOLE; |
| 549 | /// Removes byte range from a file without leaving a hole. |
| 550 | /// |
| 551 | /// Byte range to collapse starts at offset and continues for len bytes. |
| 552 | FALLOC_FL_COLLAPSE_RANGE; |
| 553 | /// Zeroes space in specified byte range. |
| 554 | /// |
| 555 | /// Byte range starts at offset and continues for len bytes. |
| 556 | FALLOC_FL_ZERO_RANGE; |
| 557 | /// Increases file space by inserting a hole within the file size. |
| 558 | /// |
| 559 | /// Does not overwrite existing data. Hole starts at offset and continues for len bytes. |
| 560 | FALLOC_FL_INSERT_RANGE; |
| 561 | /// Shared file data extants are made private to the file. |
| 562 | /// |
| 563 | /// Gaurantees that a subsequent write will not fail due to lack of space. |
| 564 | FALLOC_FL_UNSHARE_RANGE; |
| 565 | } |
| 566 | ); |
| 567 | |
| 568 | /// Manipulates file space. |
| 569 | /// |
| 570 | /// Allows the caller to directly manipulate the allocated disk space for the |
| 571 | /// file referred to by fd. |
| 572 | #[cfg(any(target_os = "linux"))] |
| 573 | pub fn fallocate( |
| 574 | fd: RawFd, |
| 575 | mode: FallocateFlags, |
| 576 | offset: libc::off_t, |
| 577 | len: libc::off_t, |
| 578 | ) -> Result<()> { |
| 579 | let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) }; |
| 580 | Errno::result(res).map(drop) |
| 581 | } |
| 582 | |
| 583 | #[cfg(any( |
| 584 | target_os = "linux", |
| 585 | target_os = "android", |
| 586 | target_os = "emscripten", |
| 587 | target_os = "fuchsia", |
| 588 | any(target_os = "wasi", target_env = "wasi"), |
| 589 | target_env = "uclibc", |
| 590 | target_env = "freebsd" |
| 591 | ))] |
| 592 | mod posix_fadvise { |
| 593 | use crate::errno::Errno; |
| 594 | use libc; |
| 595 | use std::os::unix::io::RawFd; |
| 596 | use crate::Result; |
| 597 | |
| 598 | libc_enum! { |
| 599 | #[repr(i32)] |
| 600 | pub enum PosixFadviseAdvice { |
| 601 | POSIX_FADV_NORMAL, |
| 602 | POSIX_FADV_SEQUENTIAL, |
| 603 | POSIX_FADV_RANDOM, |
| 604 | POSIX_FADV_NOREUSE, |
| 605 | POSIX_FADV_WILLNEED, |
| 606 | POSIX_FADV_DONTNEED, |
| 607 | } |
| 608 | } |
| 609 | |
| 610 | pub fn posix_fadvise( |
| 611 | fd: RawFd, |
| 612 | offset: libc::off_t, |
| 613 | len: libc::off_t, |
| 614 | advice: PosixFadviseAdvice, |
| 615 | ) -> Result<libc::c_int> { |
| 616 | let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) }; |
| 617 | Errno::result(res) |
| 618 | } |
| 619 | } |
| 620 | |
| 621 | #[cfg(any( |
| 622 | target_os = "linux", |
| 623 | target_os = "android", |
| 624 | target_os = "emscripten", |
| 625 | target_os = "fuchsia", |
| 626 | any(target_os = "wasi", target_env = "wasi"), |
| 627 | target_os = "freebsd" |
| 628 | ))] |
| 629 | pub fn posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Result<()> { |
| 630 | let res = unsafe { libc::posix_fallocate(fd, offset, len) }; |
| 631 | match Errno::result(res) { |
| 632 | Err(err) => Err(err), |
| 633 | Ok(0) => Ok(()), |
| 634 | Ok(errno) => Err(crate::Error::Sys(Errno::from_i32(errno))), |
| 635 | } |
| 636 | } |