2019-02-10 19:23:21 +00:00
|
|
|
use crate::os::unix::prelude::*;
|
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
use crate::ffi::{CStr, CString, OsStr, OsString};
|
2019-02-10 19:23:21 +00:00
|
|
|
use crate::fmt;
|
2019-12-22 22:42:04 +00:00
|
|
|
use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom};
|
2019-02-10 19:23:21 +00:00
|
|
|
use crate::mem;
|
|
|
|
use crate::path::{Path, PathBuf};
|
|
|
|
use crate::ptr;
|
|
|
|
use crate::sync::Arc;
|
|
|
|
use crate::sys::fd::FileDesc;
|
|
|
|
use crate::sys::time::SystemTime;
|
|
|
|
use crate::sys::{cvt, cvt_r};
|
|
|
|
use crate::sys_common::{AsInner, FromInner};
|
|
|
|
|
|
|
|
use libc::{c_int, mode_t};
|
2015-02-03 05:39:14 +00:00
|
|
|
|
2018-05-24 21:50:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
|
2018-06-25 18:42:27 +00:00
|
|
|
use libc::dirfd;
|
2019-12-22 22:42:04 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "emscripten"))]
|
|
|
|
use libc::fstatat64;
|
|
|
|
#[cfg(not(any(
|
|
|
|
target_os = "linux",
|
|
|
|
target_os = "emscripten",
|
|
|
|
target_os = "solaris",
|
2020-04-13 23:37:22 +00:00
|
|
|
target_os = "illumos",
|
2019-12-22 22:42:04 +00:00
|
|
|
target_os = "l4re",
|
|
|
|
target_os = "fuchsia",
|
|
|
|
target_os = "redox"
|
|
|
|
)))]
|
|
|
|
use libc::readdir_r as readdir64_r;
|
2016-02-21 09:04:14 +00:00
|
|
|
#[cfg(target_os = "android")]
|
2019-12-22 22:42:04 +00:00
|
|
|
use libc::{
|
|
|
|
dirent as dirent64, fstat as fstat64, fstatat as fstatat64, lseek64, lstat as lstat64,
|
|
|
|
open as open64, stat as stat64,
|
|
|
|
};
|
|
|
|
#[cfg(not(any(
|
|
|
|
target_os = "linux",
|
|
|
|
target_os = "emscripten",
|
|
|
|
target_os = "l4re",
|
|
|
|
target_os = "android"
|
|
|
|
)))]
|
|
|
|
use libc::{
|
|
|
|
dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64,
|
|
|
|
lstat as lstat64, off_t as off64_t, open as open64, stat as stat64,
|
|
|
|
};
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
|
|
|
|
use libc::{
|
|
|
|
dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, readdir64_r, stat64,
|
|
|
|
};
|
std: Deprecate all std::os::*::raw types
This commit is an implementation of [RFC 1415][rfc] which deprecates all types
in the `std::os::*::raw` modules.
[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1415-trim-std-os.md
Many of the types in these modules don't actually have a canonical platform
representation, for example the definition of `stat` on 32-bit Linux will change
depending on whether C code is compiled with LFS support or not. Unfortunately
the current types in `std::os::*::raw` are billed as "compatible with C", which
in light of this means it isn't really possible.
To make matters worse, platforms like Android sometimes define these types as
*smaller* than the way they're actually represented in the `stat` structure
itself. This means that when methods like `DirEntry::ino` are called on Android
the result may be truncated as we're tied to returning a `ino_t` type, not the
underlying type.
The commit here incorporates two backwards-compatible components:
* Deprecate all `raw` types that aren't in `std::os::raw`
* Expand the `std::os::*::fs::MetadataExt` trait on all platforms for method
accessors of all fields. The fields now returned widened types which are the
same across platforms (consistency across platforms is not required, however,
it's just convenient).
and two also backwards-incompatible components:
* Change the definition of all `std::os::*::raw` type aliases to
correspond to the newly widened types that are being returned on each
platform.
* Change the definition of `std::os::*::raw::stat` on Linux to match the LFS
definitions rather than the standard ones.
The breaking changes here will specifically break code that assumes that `libc`
and `std` agree on the definition of `std::os::*::raw` types, or that the `std`
types are faithful representations of the types in C. An [audit] has been
performed of crates.io to determine the fallout which was determined two be
minimal, with the two found cases of breakage having been fixed now.
[audit]: https://github.com/rust-lang/rfcs/pull/1415#issuecomment-180645582
---
Ok, so after all that, we're finally able to support LFS on Linux! This commit
then simultaneously starts using `stat64` and friends on Linux to ensure that we
can open >4GB files on 32-bit Linux. Yay!
Closes #28978
Closes #30050
Closes #31549
2016-02-05 01:16:47 +00:00
|
|
|
|
2019-03-29 22:57:14 +00:00
|
|
|
pub use crate::sys_common::fs::remove_dir_all;
|
|
|
|
|
2015-02-03 05:39:14 +00:00
|
|
|
pub struct File(FileDesc);
|
|
|
|
|
2020-01-08 06:18:42 +00:00
|
|
|
// FIXME: This should be available on Linux with all `target_env`.
|
|
|
|
// But currently only glibc exposes `statx` fn and structs.
|
|
|
|
// We don't want to import unverified raw C structs here directly.
|
|
|
|
// https://github.com/rust-lang/rust/pull/67774
|
2019-10-13 23:17:15 +00:00
|
|
|
macro_rules! cfg_has_statx {
|
|
|
|
({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => {
|
|
|
|
cfg_if::cfg_if! {
|
2020-01-08 06:18:42 +00:00
|
|
|
if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
|
2019-10-13 23:17:15 +00:00
|
|
|
$($then_tt)*
|
|
|
|
} else {
|
|
|
|
$($else_tt)*
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
($($block_inner:tt)*) => {
|
2020-01-08 06:18:42 +00:00
|
|
|
#[cfg(all(target_os = "linux", target_env = "gnu"))]
|
2019-10-13 23:17:15 +00:00
|
|
|
{
|
|
|
|
$($block_inner)*
|
|
|
|
}
|
|
|
|
};
|
2019-10-04 08:17:23 +00:00
|
|
|
}
|
|
|
|
|
2019-10-13 23:17:15 +00:00
|
|
|
cfg_has_statx! {{
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct FileAttr {
|
|
|
|
stat: stat64,
|
|
|
|
statx_extra_fields: Option<StatxExtraFields>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
struct StatxExtraFields {
|
|
|
|
// This is needed to check if btime is supported by the filesystem.
|
|
|
|
stx_mask: u32,
|
|
|
|
stx_btime: libc::statx_timestamp,
|
|
|
|
}
|
|
|
|
|
|
|
|
// We prefer `statx` on Linux if available, which contains file creation time.
|
|
|
|
// Default `stat64` contains no creation time.
|
|
|
|
unsafe fn try_statx(
|
|
|
|
fd: c_int,
|
|
|
|
path: *const libc::c_char,
|
|
|
|
flags: i32,
|
|
|
|
mask: u32,
|
|
|
|
) -> Option<io::Result<FileAttr>> {
|
2019-10-22 04:02:32 +00:00
|
|
|
use crate::sync::atomic::{AtomicU8, Ordering};
|
2019-10-13 23:17:15 +00:00
|
|
|
|
|
|
|
// Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`
|
2019-10-22 04:02:32 +00:00
|
|
|
// We store the availability in global to avoid unnecessary syscalls.
|
|
|
|
// 0: Unknown
|
|
|
|
// 1: Not available
|
|
|
|
// 2: Available
|
|
|
|
static STATX_STATE: AtomicU8 = AtomicU8::new(0);
|
2019-10-13 23:17:15 +00:00
|
|
|
syscall! {
|
|
|
|
fn statx(
|
|
|
|
fd: c_int,
|
|
|
|
pathname: *const libc::c_char,
|
|
|
|
flags: c_int,
|
|
|
|
mask: libc::c_uint,
|
|
|
|
statxbuf: *mut libc::statx
|
|
|
|
) -> c_int
|
|
|
|
}
|
|
|
|
|
2019-10-22 04:02:32 +00:00
|
|
|
match STATX_STATE.load(Ordering::Relaxed) {
|
|
|
|
0 => {
|
2019-10-22 18:43:45 +00:00
|
|
|
// It is a trick to call `statx` with NULL pointers to check if the syscall
|
|
|
|
// is available. According to the manual, it is expected to fail with EFAULT.
|
|
|
|
// We do this mainly for performance, since it is nearly hundreds times
|
2019-11-27 03:19:54 +00:00
|
|
|
// faster than a normal successful call.
|
2019-10-22 18:43:45 +00:00
|
|
|
let err = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut()))
|
2019-10-22 04:02:32 +00:00
|
|
|
.err()
|
|
|
|
.and_then(|e| e.raw_os_error());
|
2019-10-22 18:43:45 +00:00
|
|
|
// We don't check `err == Some(libc::ENOSYS)` because the syscall may be limited
|
|
|
|
// and returns `EPERM`. Listing all possible errors seems not a good idea.
|
2019-10-22 04:02:32 +00:00
|
|
|
// See: https://github.com/rust-lang/rust/issues/65662
|
2019-10-22 18:43:45 +00:00
|
|
|
if err != Some(libc::EFAULT) {
|
2019-10-22 04:02:32 +00:00
|
|
|
STATX_STATE.store(1, Ordering::Relaxed);
|
2019-10-22 18:43:45 +00:00
|
|
|
return None;
|
2019-10-13 23:17:15 +00:00
|
|
|
}
|
2019-10-22 18:43:45 +00:00
|
|
|
STATX_STATE.store(2, Ordering::Relaxed);
|
2019-10-04 08:17:23 +00:00
|
|
|
}
|
2019-10-22 18:43:45 +00:00
|
|
|
1 => return None,
|
|
|
|
_ => {}
|
|
|
|
}
|
2019-10-22 04:02:32 +00:00
|
|
|
|
2019-10-22 18:43:45 +00:00
|
|
|
let mut buf: libc::statx = mem::zeroed();
|
|
|
|
if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) {
|
|
|
|
return Some(Err(err));
|
2019-10-04 08:17:23 +00:00
|
|
|
}
|
2019-10-22 18:43:45 +00:00
|
|
|
|
|
|
|
// We cannot fill `stat64` exhaustively because of private padding fields.
|
|
|
|
let mut stat: stat64 = mem::zeroed();
|
|
|
|
// `c_ulong` on gnu-mips, `dev_t` otherwise
|
|
|
|
stat.st_dev = libc::makedev(buf.stx_dev_major, buf.stx_dev_minor) as _;
|
|
|
|
stat.st_ino = buf.stx_ino as libc::ino64_t;
|
|
|
|
stat.st_nlink = buf.stx_nlink as libc::nlink_t;
|
|
|
|
stat.st_mode = buf.stx_mode as libc::mode_t;
|
|
|
|
stat.st_uid = buf.stx_uid as libc::uid_t;
|
|
|
|
stat.st_gid = buf.stx_gid as libc::gid_t;
|
|
|
|
stat.st_rdev = libc::makedev(buf.stx_rdev_major, buf.stx_rdev_minor) as _;
|
|
|
|
stat.st_size = buf.stx_size as off64_t;
|
|
|
|
stat.st_blksize = buf.stx_blksize as libc::blksize_t;
|
|
|
|
stat.st_blocks = buf.stx_blocks as libc::blkcnt64_t;
|
|
|
|
stat.st_atime = buf.stx_atime.tv_sec as libc::time_t;
|
|
|
|
// `i64` on gnu-x86_64-x32, `c_ulong` otherwise.
|
|
|
|
stat.st_atime_nsec = buf.stx_atime.tv_nsec as _;
|
|
|
|
stat.st_mtime = buf.stx_mtime.tv_sec as libc::time_t;
|
|
|
|
stat.st_mtime_nsec = buf.stx_mtime.tv_nsec as _;
|
|
|
|
stat.st_ctime = buf.stx_ctime.tv_sec as libc::time_t;
|
|
|
|
stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _;
|
|
|
|
|
|
|
|
let extra = StatxExtraFields {
|
|
|
|
stx_mask: buf.stx_mask,
|
|
|
|
stx_btime: buf.stx_btime,
|
|
|
|
};
|
|
|
|
|
|
|
|
Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) }))
|
2019-10-04 08:17:23 +00:00
|
|
|
}
|
2019-10-13 23:17:15 +00:00
|
|
|
|
|
|
|
} else {
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct FileAttr {
|
|
|
|
stat: stat64,
|
|
|
|
}
|
|
|
|
}}
|
2015-02-03 05:39:14 +00:00
|
|
|
|
2018-05-24 21:50:28 +00:00
|
|
|
// all DirEntry's will have a reference to this struct
|
|
|
|
struct InnerReadDir {
|
2015-02-20 17:46:56 +00:00
|
|
|
dirp: Dir,
|
2018-05-24 21:50:28 +00:00
|
|
|
root: PathBuf,
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
2018-06-12 19:03:27 +00:00
|
|
|
pub struct ReadDir {
|
|
|
|
inner: Arc<InnerReadDir>,
|
2020-10-09 16:59:39 +00:00
|
|
|
#[cfg(not(any(
|
|
|
|
target_os = "solaris",
|
|
|
|
target_os = "illumos",
|
|
|
|
target_os = "fuchsia",
|
|
|
|
target_os = "redox",
|
|
|
|
)))]
|
2018-06-12 19:03:27 +00:00
|
|
|
end_of_stream: bool,
|
|
|
|
}
|
2018-05-24 21:50:28 +00:00
|
|
|
|
2015-02-20 17:46:56 +00:00
|
|
|
struct Dir(*mut libc::DIR);
|
|
|
|
|
|
|
|
unsafe impl Send for Dir {}
|
|
|
|
unsafe impl Sync for Dir {}
|
|
|
|
|
2015-02-03 05:39:14 +00:00
|
|
|
pub struct DirEntry {
|
2016-02-15 00:24:02 +00:00
|
|
|
entry: dirent64,
|
2020-10-07 21:35:51 +00:00
|
|
|
dir: Arc<InnerReadDir>,
|
2018-05-24 21:50:28 +00:00
|
|
|
// We need to store an owned copy of the entry name
|
2017-02-21 22:41:28 +00:00
|
|
|
// on Solaris and Fuchsia because a) it uses a zero-length
|
|
|
|
// array to store the name, b) its lifetime between readdir
|
|
|
|
// calls is not guaranteed.
|
2020-04-13 23:37:22 +00:00
|
|
|
#[cfg(any(
|
|
|
|
target_os = "solaris",
|
|
|
|
target_os = "illumos",
|
|
|
|
target_os = "fuchsia",
|
|
|
|
target_os = "redox"
|
|
|
|
))]
|
2019-12-22 22:42:04 +00:00
|
|
|
name: Box<[u8]>,
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
2016-11-25 18:21:49 +00:00
|
|
|
#[derive(Clone, Debug)]
|
2015-02-03 05:39:14 +00:00
|
|
|
pub struct OpenOptions {
|
2016-01-13 17:08:08 +00:00
|
|
|
// generic
|
2015-02-03 05:39:14 +00:00
|
|
|
read: bool,
|
|
|
|
write: bool,
|
2016-01-13 17:08:08 +00:00
|
|
|
append: bool,
|
|
|
|
truncate: bool,
|
|
|
|
create: bool,
|
|
|
|
create_new: bool,
|
|
|
|
// system-specific
|
2016-01-13 20:47:46 +00:00
|
|
|
custom_flags: i32,
|
2015-02-03 05:39:14 +00:00
|
|
|
mode: mode_t,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
2019-12-22 22:42:04 +00:00
|
|
|
pub struct FilePermissions {
|
|
|
|
mode: mode_t,
|
|
|
|
}
|
2015-02-03 05:39:14 +00:00
|
|
|
|
2016-07-10 22:37:41 +00:00
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
2019-12-22 22:42:04 +00:00
|
|
|
pub struct FileType {
|
|
|
|
mode: mode_t,
|
|
|
|
}
|
2015-04-16 06:21:13 +00:00
|
|
|
|
2016-11-25 18:21:49 +00:00
|
|
|
#[derive(Debug)]
|
2019-12-22 22:42:04 +00:00
|
|
|
pub struct DirBuilder {
|
|
|
|
mode: mode_t,
|
|
|
|
}
|
2015-04-28 00:29:35 +00:00
|
|
|
|
2019-10-13 23:17:15 +00:00
|
|
|
cfg_has_statx! {{
|
|
|
|
impl FileAttr {
|
|
|
|
fn from_stat64(stat: stat64) -> Self {
|
|
|
|
Self { stat, statx_extra_fields: None }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
impl FileAttr {
|
|
|
|
fn from_stat64(stat: stat64) -> Self {
|
|
|
|
Self { stat }
|
2019-10-04 08:17:23 +00:00
|
|
|
}
|
|
|
|
}
|
2019-10-13 23:17:15 +00:00
|
|
|
}}
|
2019-10-04 08:17:23 +00:00
|
|
|
|
2019-10-13 23:17:15 +00:00
|
|
|
impl FileAttr {
|
2019-12-22 22:42:04 +00:00
|
|
|
pub fn size(&self) -> u64 {
|
|
|
|
self.stat.st_size as u64
|
|
|
|
}
|
2015-02-03 05:39:14 +00:00
|
|
|
pub fn perm(&self) -> FilePermissions {
|
2017-09-17 21:42:58 +00:00
|
|
|
FilePermissions { mode: (self.stat.st_mode as mode_t) }
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
2015-04-16 06:21:13 +00:00
|
|
|
pub fn file_type(&self) -> FileType {
|
|
|
|
FileType { mode: self.stat.st_mode as mode_t }
|
|
|
|
}
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
2016-02-21 15:31:56 +00:00
|
|
|
#[cfg(target_os = "netbsd")]
|
|
|
|
impl FileAttr {
|
|
|
|
pub fn modified(&self) -> io::Result<SystemTime> {
|
|
|
|
Ok(SystemTime::from(libc::timespec {
|
|
|
|
tv_sec: self.stat.st_mtime as libc::time_t,
|
|
|
|
tv_nsec: self.stat.st_mtimensec as libc::c_long,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn accessed(&self) -> io::Result<SystemTime> {
|
|
|
|
Ok(SystemTime::from(libc::timespec {
|
|
|
|
tv_sec: self.stat.st_atime as libc::time_t,
|
|
|
|
tv_nsec: self.stat.st_atimensec as libc::c_long,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn created(&self) -> io::Result<SystemTime> {
|
|
|
|
Ok(SystemTime::from(libc::timespec {
|
|
|
|
tv_sec: self.stat.st_birthtime as libc::time_t,
|
|
|
|
tv_nsec: self.stat.st_birthtimensec as libc::c_long,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
std: Stabilize APIs for the 1.10 release
This commit applies the FCP decisions made by the libs team for the 1.10 cycle,
including both new stabilizations and deprecations. Specifically, the list of
APIs is:
Stabilized:
* `os::windows::fs::OpenOptionsExt::access_mode`
* `os::windows::fs::OpenOptionsExt::share_mode`
* `os::windows::fs::OpenOptionsExt::custom_flags`
* `os::windows::fs::OpenOptionsExt::attributes`
* `os::windows::fs::OpenOptionsExt::security_qos_flags`
* `os::unix::fs::OpenOptionsExt::custom_flags`
* `sync::Weak::new`
* `Default for sync::Weak`
* `panic::set_hook`
* `panic::take_hook`
* `panic::PanicInfo`
* `panic::PanicInfo::payload`
* `panic::PanicInfo::location`
* `panic::Location`
* `panic::Location::file`
* `panic::Location::line`
* `ffi::CStr::from_bytes_with_nul`
* `ffi::CStr::from_bytes_with_nul_unchecked`
* `ffi::FromBytesWithNulError`
* `fs::Metadata::modified`
* `fs::Metadata::accessed`
* `fs::Metadata::created`
* `sync::atomic::Atomic{Usize,Isize,Bool,Ptr}::compare_exchange`
* `sync::atomic::Atomic{Usize,Isize,Bool,Ptr}::compare_exchange_weak`
* `collections::{btree,hash}_map::{Occupied,Vacant,}Entry::key`
* `os::unix::net::{UnixStream, UnixListener, UnixDatagram, SocketAddr}`
* `SocketAddr::is_unnamed`
* `SocketAddr::as_pathname`
* `UnixStream::connect`
* `UnixStream::pair`
* `UnixStream::try_clone`
* `UnixStream::local_addr`
* `UnixStream::peer_addr`
* `UnixStream::set_read_timeout`
* `UnixStream::set_write_timeout`
* `UnixStream::read_timeout`
* `UnixStream::write_Timeout`
* `UnixStream::set_nonblocking`
* `UnixStream::take_error`
* `UnixStream::shutdown`
* Read/Write/RawFd impls for `UnixStream`
* `UnixListener::bind`
* `UnixListener::accept`
* `UnixListener::try_clone`
* `UnixListener::local_addr`
* `UnixListener::set_nonblocking`
* `UnixListener::take_error`
* `UnixListener::incoming`
* RawFd impls for `UnixListener`
* `UnixDatagram::bind`
* `UnixDatagram::unbound`
* `UnixDatagram::pair`
* `UnixDatagram::connect`
* `UnixDatagram::try_clone`
* `UnixDatagram::local_addr`
* `UnixDatagram::peer_addr`
* `UnixDatagram::recv_from`
* `UnixDatagram::recv`
* `UnixDatagram::send_to`
* `UnixDatagram::send`
* `UnixDatagram::set_read_timeout`
* `UnixDatagram::set_write_timeout`
* `UnixDatagram::read_timeout`
* `UnixDatagram::write_timeout`
* `UnixDatagram::set_nonblocking`
* `UnixDatagram::take_error`
* `UnixDatagram::shutdown`
* RawFd impls for `UnixDatagram`
* `{BTree,Hash}Map::values_mut`
* `<[_]>::binary_search_by_key`
Deprecated:
* `StaticCondvar` - this, and all other static synchronization primitives
below, are usable today through the lazy-static crate on
stable Rust today. Additionally, we'd like the non-static
versions to be directly usable in a static context one day,
so they're unlikely to be the final forms of the APIs in any
case.
* `CONDVAR_INIT`
* `StaticMutex`
* `MUTEX_INIT`
* `StaticRwLock`
* `RWLOCK_INIT`
* `iter::Peekable::is_empty`
Closes #27717
Closes #27720
cc #27784 (but encode methods still exist)
Closes #30014
Closes #30425
Closes #30449
Closes #31190
Closes #31399
Closes #31767
Closes #32111
Closes #32281
Closes #32312
Closes #32551
Closes #33018
2016-05-17 18:57:07 +00:00
|
|
|
#[cfg(not(target_os = "netbsd"))]
|
2016-01-13 01:24:16 +00:00
|
|
|
impl FileAttr {
|
2020-10-07 18:38:25 +00:00
|
|
|
#[cfg(not(target_os = "vxworks"))]
|
2016-01-13 01:24:16 +00:00
|
|
|
pub fn modified(&self) -> io::Result<SystemTime> {
|
|
|
|
Ok(SystemTime::from(libc::timespec {
|
std: Deprecate all std::os::*::raw types
This commit is an implementation of [RFC 1415][rfc] which deprecates all types
in the `std::os::*::raw` modules.
[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1415-trim-std-os.md
Many of the types in these modules don't actually have a canonical platform
representation, for example the definition of `stat` on 32-bit Linux will change
depending on whether C code is compiled with LFS support or not. Unfortunately
the current types in `std::os::*::raw` are billed as "compatible with C", which
in light of this means it isn't really possible.
To make matters worse, platforms like Android sometimes define these types as
*smaller* than the way they're actually represented in the `stat` structure
itself. This means that when methods like `DirEntry::ino` are called on Android
the result may be truncated as we're tied to returning a `ino_t` type, not the
underlying type.
The commit here incorporates two backwards-compatible components:
* Deprecate all `raw` types that aren't in `std::os::raw`
* Expand the `std::os::*::fs::MetadataExt` trait on all platforms for method
accessors of all fields. The fields now returned widened types which are the
same across platforms (consistency across platforms is not required, however,
it's just convenient).
and two also backwards-incompatible components:
* Change the definition of all `std::os::*::raw` type aliases to
correspond to the newly widened types that are being returned on each
platform.
* Change the definition of `std::os::*::raw::stat` on Linux to match the LFS
definitions rather than the standard ones.
The breaking changes here will specifically break code that assumes that `libc`
and `std` agree on the definition of `std::os::*::raw` types, or that the `std`
types are faithful representations of the types in C. An [audit] has been
performed of crates.io to determine the fallout which was determined two be
minimal, with the two found cases of breakage having been fixed now.
[audit]: https://github.com/rust-lang/rfcs/pull/1415#issuecomment-180645582
---
Ok, so after all that, we're finally able to support LFS on Linux! This commit
then simultaneously starts using `stat64` and friends on Linux to ensure that we
can open >4GB files on 32-bit Linux. Yay!
Closes #28978
Closes #30050
Closes #31549
2016-02-05 01:16:47 +00:00
|
|
|
tv_sec: self.stat.st_mtime as libc::time_t,
|
2017-10-19 17:49:59 +00:00
|
|
|
tv_nsec: self.stat.st_mtime_nsec as _,
|
2016-01-13 01:24:16 +00:00
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2020-10-07 18:38:25 +00:00
|
|
|
#[cfg(target_os = "vxworks")]
|
|
|
|
pub fn modified(&self) -> io::Result<SystemTime> {
|
|
|
|
Ok(SystemTime::from(libc::timespec {
|
|
|
|
tv_sec: self.stat.st_mtime as libc::time_t,
|
|
|
|
tv_nsec: 0,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "vxworks"))]
|
2016-01-13 01:24:16 +00:00
|
|
|
pub fn accessed(&self) -> io::Result<SystemTime> {
|
|
|
|
Ok(SystemTime::from(libc::timespec {
|
std: Deprecate all std::os::*::raw types
This commit is an implementation of [RFC 1415][rfc] which deprecates all types
in the `std::os::*::raw` modules.
[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1415-trim-std-os.md
Many of the types in these modules don't actually have a canonical platform
representation, for example the definition of `stat` on 32-bit Linux will change
depending on whether C code is compiled with LFS support or not. Unfortunately
the current types in `std::os::*::raw` are billed as "compatible with C", which
in light of this means it isn't really possible.
To make matters worse, platforms like Android sometimes define these types as
*smaller* than the way they're actually represented in the `stat` structure
itself. This means that when methods like `DirEntry::ino` are called on Android
the result may be truncated as we're tied to returning a `ino_t` type, not the
underlying type.
The commit here incorporates two backwards-compatible components:
* Deprecate all `raw` types that aren't in `std::os::raw`
* Expand the `std::os::*::fs::MetadataExt` trait on all platforms for method
accessors of all fields. The fields now returned widened types which are the
same across platforms (consistency across platforms is not required, however,
it's just convenient).
and two also backwards-incompatible components:
* Change the definition of all `std::os::*::raw` type aliases to
correspond to the newly widened types that are being returned on each
platform.
* Change the definition of `std::os::*::raw::stat` on Linux to match the LFS
definitions rather than the standard ones.
The breaking changes here will specifically break code that assumes that `libc`
and `std` agree on the definition of `std::os::*::raw` types, or that the `std`
types are faithful representations of the types in C. An [audit] has been
performed of crates.io to determine the fallout which was determined two be
minimal, with the two found cases of breakage having been fixed now.
[audit]: https://github.com/rust-lang/rfcs/pull/1415#issuecomment-180645582
---
Ok, so after all that, we're finally able to support LFS on Linux! This commit
then simultaneously starts using `stat64` and friends on Linux to ensure that we
can open >4GB files on 32-bit Linux. Yay!
Closes #28978
Closes #30050
Closes #31549
2016-02-05 01:16:47 +00:00
|
|
|
tv_sec: self.stat.st_atime as libc::time_t,
|
2017-10-19 17:49:59 +00:00
|
|
|
tv_nsec: self.stat.st_atime_nsec as _,
|
2016-01-13 01:24:16 +00:00
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2020-10-07 18:38:25 +00:00
|
|
|
#[cfg(target_os = "vxworks")]
|
|
|
|
pub fn accessed(&self) -> io::Result<SystemTime> {
|
|
|
|
Ok(SystemTime::from(libc::timespec {
|
|
|
|
tv_sec: self.stat.st_atime as libc::time_t,
|
|
|
|
tv_nsec: 0,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
#[cfg(any(
|
|
|
|
target_os = "freebsd",
|
|
|
|
target_os = "openbsd",
|
|
|
|
target_os = "macos",
|
|
|
|
target_os = "ios"
|
|
|
|
))]
|
2016-01-13 01:24:16 +00:00
|
|
|
pub fn created(&self) -> io::Result<SystemTime> {
|
|
|
|
Ok(SystemTime::from(libc::timespec {
|
std: Deprecate all std::os::*::raw types
This commit is an implementation of [RFC 1415][rfc] which deprecates all types
in the `std::os::*::raw` modules.
[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1415-trim-std-os.md
Many of the types in these modules don't actually have a canonical platform
representation, for example the definition of `stat` on 32-bit Linux will change
depending on whether C code is compiled with LFS support or not. Unfortunately
the current types in `std::os::*::raw` are billed as "compatible with C", which
in light of this means it isn't really possible.
To make matters worse, platforms like Android sometimes define these types as
*smaller* than the way they're actually represented in the `stat` structure
itself. This means that when methods like `DirEntry::ino` are called on Android
the result may be truncated as we're tied to returning a `ino_t` type, not the
underlying type.
The commit here incorporates two backwards-compatible components:
* Deprecate all `raw` types that aren't in `std::os::raw`
* Expand the `std::os::*::fs::MetadataExt` trait on all platforms for method
accessors of all fields. The fields now returned widened types which are the
same across platforms (consistency across platforms is not required, however,
it's just convenient).
and two also backwards-incompatible components:
* Change the definition of all `std::os::*::raw` type aliases to
correspond to the newly widened types that are being returned on each
platform.
* Change the definition of `std::os::*::raw::stat` on Linux to match the LFS
definitions rather than the standard ones.
The breaking changes here will specifically break code that assumes that `libc`
and `std` agree on the definition of `std::os::*::raw` types, or that the `std`
types are faithful representations of the types in C. An [audit] has been
performed of crates.io to determine the fallout which was determined two be
minimal, with the two found cases of breakage having been fixed now.
[audit]: https://github.com/rust-lang/rfcs/pull/1415#issuecomment-180645582
---
Ok, so after all that, we're finally able to support LFS on Linux! This commit
then simultaneously starts using `stat64` and friends on Linux to ensure that we
can open >4GB files on 32-bit Linux. Yay!
Closes #28978
Closes #30050
Closes #31549
2016-02-05 01:16:47 +00:00
|
|
|
tv_sec: self.stat.st_birthtime as libc::time_t,
|
2016-01-13 01:24:16 +00:00
|
|
|
tv_nsec: self.stat.st_birthtime_nsec as libc::c_long,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
#[cfg(not(any(
|
|
|
|
target_os = "freebsd",
|
|
|
|
target_os = "openbsd",
|
|
|
|
target_os = "macos",
|
|
|
|
target_os = "ios"
|
|
|
|
)))]
|
2016-01-13 01:24:16 +00:00
|
|
|
pub fn created(&self) -> io::Result<SystemTime> {
|
2019-10-13 23:17:15 +00:00
|
|
|
cfg_has_statx! {
|
2019-10-04 08:17:23 +00:00
|
|
|
if let Some(ext) = &self.statx_extra_fields {
|
|
|
|
return if (ext.stx_mask & libc::STATX_BTIME) != 0 {
|
|
|
|
Ok(SystemTime::from(libc::timespec {
|
|
|
|
tv_sec: ext.stx_btime.tv_sec as libc::time_t,
|
2019-10-18 02:19:50 +00:00
|
|
|
tv_nsec: ext.stx_btime.tv_nsec as _,
|
2019-10-04 08:17:23 +00:00
|
|
|
}))
|
|
|
|
} else {
|
|
|
|
Err(io::Error::new(
|
|
|
|
io::ErrorKind::Other,
|
|
|
|
"creation time is not available for the filesystem",
|
|
|
|
))
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
Err(io::Error::new(
|
|
|
|
io::ErrorKind::Other,
|
|
|
|
"creation time is not available on this platform \
|
|
|
|
currently",
|
|
|
|
))
|
2016-01-13 01:24:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
std: Deprecate all std::os::*::raw types
This commit is an implementation of [RFC 1415][rfc] which deprecates all types
in the `std::os::*::raw` modules.
[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1415-trim-std-os.md
Many of the types in these modules don't actually have a canonical platform
representation, for example the definition of `stat` on 32-bit Linux will change
depending on whether C code is compiled with LFS support or not. Unfortunately
the current types in `std::os::*::raw` are billed as "compatible with C", which
in light of this means it isn't really possible.
To make matters worse, platforms like Android sometimes define these types as
*smaller* than the way they're actually represented in the `stat` structure
itself. This means that when methods like `DirEntry::ino` are called on Android
the result may be truncated as we're tied to returning a `ino_t` type, not the
underlying type.
The commit here incorporates two backwards-compatible components:
* Deprecate all `raw` types that aren't in `std::os::raw`
* Expand the `std::os::*::fs::MetadataExt` trait on all platforms for method
accessors of all fields. The fields now returned widened types which are the
same across platforms (consistency across platforms is not required, however,
it's just convenient).
and two also backwards-incompatible components:
* Change the definition of all `std::os::*::raw` type aliases to
correspond to the newly widened types that are being returned on each
platform.
* Change the definition of `std::os::*::raw::stat` on Linux to match the LFS
definitions rather than the standard ones.
The breaking changes here will specifically break code that assumes that `libc`
and `std` agree on the definition of `std::os::*::raw` types, or that the `std`
types are faithful representations of the types in C. An [audit] has been
performed of crates.io to determine the fallout which was determined two be
minimal, with the two found cases of breakage having been fixed now.
[audit]: https://github.com/rust-lang/rfcs/pull/1415#issuecomment-180645582
---
Ok, so after all that, we're finally able to support LFS on Linux! This commit
then simultaneously starts using `stat64` and friends on Linux to ensure that we
can open >4GB files on 32-bit Linux. Yay!
Closes #28978
Closes #30050
Closes #31549
2016-02-05 01:16:47 +00:00
|
|
|
impl AsInner<stat64> for FileAttr {
|
2019-12-22 22:42:04 +00:00
|
|
|
fn as_inner(&self) -> &stat64 {
|
|
|
|
&self.stat
|
|
|
|
}
|
2015-04-16 06:21:13 +00:00
|
|
|
}
|
|
|
|
|
2015-02-03 05:39:14 +00:00
|
|
|
impl FilePermissions {
|
2017-08-15 13:42:11 +00:00
|
|
|
pub fn readonly(&self) -> bool {
|
|
|
|
// check if any class (owner, group, others) has write permission
|
|
|
|
self.mode & 0o222 == 0
|
|
|
|
}
|
|
|
|
|
2015-02-03 05:39:14 +00:00
|
|
|
pub fn set_readonly(&mut self, readonly: bool) {
|
|
|
|
if readonly {
|
2017-08-15 13:42:11 +00:00
|
|
|
// remove write permission for all classes; equivalent to `chmod a-w <file>`
|
2015-02-03 05:39:14 +00:00
|
|
|
self.mode &= !0o222;
|
|
|
|
} else {
|
2017-08-15 13:42:11 +00:00
|
|
|
// add write permission for all classes; equivalent to `chmod a+w <file>`
|
2015-02-03 05:39:14 +00:00
|
|
|
self.mode |= 0o222;
|
|
|
|
}
|
|
|
|
}
|
2019-12-22 22:42:04 +00:00
|
|
|
pub fn mode(&self) -> u32 {
|
|
|
|
self.mode as u32
|
|
|
|
}
|
2015-04-16 06:21:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl FileType {
|
2019-12-22 22:42:04 +00:00
|
|
|
pub fn is_dir(&self) -> bool {
|
|
|
|
self.is(libc::S_IFDIR)
|
|
|
|
}
|
|
|
|
pub fn is_file(&self) -> bool {
|
|
|
|
self.is(libc::S_IFREG)
|
|
|
|
}
|
|
|
|
pub fn is_symlink(&self) -> bool {
|
|
|
|
self.is(libc::S_IFLNK)
|
|
|
|
}
|
2015-04-16 06:21:13 +00:00
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
pub fn is(&self, mode: mode_t) -> bool {
|
|
|
|
self.mode & libc::S_IFMT == mode
|
|
|
|
}
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
std: Deprecate all std::os::*::raw types
This commit is an implementation of [RFC 1415][rfc] which deprecates all types
in the `std::os::*::raw` modules.
[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1415-trim-std-os.md
Many of the types in these modules don't actually have a canonical platform
representation, for example the definition of `stat` on 32-bit Linux will change
depending on whether C code is compiled with LFS support or not. Unfortunately
the current types in `std::os::*::raw` are billed as "compatible with C", which
in light of this means it isn't really possible.
To make matters worse, platforms like Android sometimes define these types as
*smaller* than the way they're actually represented in the `stat` structure
itself. This means that when methods like `DirEntry::ino` are called on Android
the result may be truncated as we're tied to returning a `ino_t` type, not the
underlying type.
The commit here incorporates two backwards-compatible components:
* Deprecate all `raw` types that aren't in `std::os::raw`
* Expand the `std::os::*::fs::MetadataExt` trait on all platforms for method
accessors of all fields. The fields now returned widened types which are the
same across platforms (consistency across platforms is not required, however,
it's just convenient).
and two also backwards-incompatible components:
* Change the definition of all `std::os::*::raw` type aliases to
correspond to the newly widened types that are being returned on each
platform.
* Change the definition of `std::os::*::raw::stat` on Linux to match the LFS
definitions rather than the standard ones.
The breaking changes here will specifically break code that assumes that `libc`
and `std` agree on the definition of `std::os::*::raw` types, or that the `std`
types are faithful representations of the types in C. An [audit] has been
performed of crates.io to determine the fallout which was determined two be
minimal, with the two found cases of breakage having been fixed now.
[audit]: https://github.com/rust-lang/rfcs/pull/1415#issuecomment-180645582
---
Ok, so after all that, we're finally able to support LFS on Linux! This commit
then simultaneously starts using `stat64` and friends on Linux to ensure that we
can open >4GB files on 32-bit Linux. Yay!
Closes #28978
Closes #30050
Closes #31549
2016-02-05 01:16:47 +00:00
|
|
|
impl FromInner<u32> for FilePermissions {
|
|
|
|
fn from_inner(mode: u32) -> FilePermissions {
|
2015-02-03 05:39:14 +00:00
|
|
|
FilePermissions { mode: mode as mode_t }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-16 18:38:56 +00:00
|
|
|
impl fmt::Debug for ReadDir {
|
2019-03-01 08:34:11 +00:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2016-10-16 18:38:56 +00:00
|
|
|
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
|
|
|
|
// Thus the result will be e g 'ReadDir("/home")'
|
2018-06-12 19:03:27 +00:00
|
|
|
fmt::Debug::fmt(&*self.inner.root, f)
|
2016-10-16 18:38:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-03 05:39:14 +00:00
|
|
|
impl Iterator for ReadDir {
|
|
|
|
type Item = io::Result<DirEntry>;
|
|
|
|
|
2020-04-13 23:37:22 +00:00
|
|
|
#[cfg(any(
|
|
|
|
target_os = "solaris",
|
|
|
|
target_os = "fuchsia",
|
|
|
|
target_os = "redox",
|
|
|
|
target_os = "illumos"
|
|
|
|
))]
|
2016-01-21 16:30:22 +00:00
|
|
|
fn next(&mut self) -> Option<io::Result<DirEntry>> {
|
2019-02-10 19:23:21 +00:00
|
|
|
use crate::slice;
|
|
|
|
|
2016-01-21 16:30:22 +00:00
|
|
|
unsafe {
|
|
|
|
loop {
|
2016-01-27 02:46:28 +00:00
|
|
|
// Although readdir_r(3) would be a correct function to use here because
|
2017-02-21 22:41:28 +00:00
|
|
|
// of the thread safety, on Illumos and Fuchsia the readdir(3C) function
|
|
|
|
// is safe to use in threaded applications and it is generally preferred
|
|
|
|
// over the readdir_r(3C) function.
|
2016-07-12 04:43:53 +00:00
|
|
|
super::os::set_errno(0);
|
2018-06-12 19:03:27 +00:00
|
|
|
let entry_ptr = libc::readdir(self.inner.dirp.0);
|
2016-01-21 16:30:22 +00:00
|
|
|
if entry_ptr.is_null() {
|
2016-07-12 04:43:53 +00:00
|
|
|
// NULL can mean either the end is reached or an error occurred.
|
|
|
|
// So we had to clear errno beforehand to check for an error now.
|
|
|
|
return match super::os::errno() {
|
|
|
|
0 => None,
|
|
|
|
e => Some(Err(Error::from_raw_os_error(e))),
|
2019-12-22 22:42:04 +00:00
|
|
|
};
|
2016-01-21 16:30:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let name = (*entry_ptr).d_name.as_ptr();
|
|
|
|
let namelen = libc::strlen(name) as usize;
|
|
|
|
|
|
|
|
let ret = DirEntry {
|
|
|
|
entry: *entry_ptr,
|
2019-12-22 22:42:04 +00:00
|
|
|
name: slice::from_raw_parts(name as *const u8, namelen as usize)
|
|
|
|
.to_owned()
|
|
|
|
.into_boxed_slice(),
|
2020-10-07 21:35:51 +00:00
|
|
|
dir: Arc::clone(&self.inner),
|
2016-01-21 16:30:22 +00:00
|
|
|
};
|
|
|
|
if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
|
2019-12-22 22:42:04 +00:00
|
|
|
return Some(Ok(ret));
|
2016-01-21 16:30:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-13 23:37:22 +00:00
|
|
|
#[cfg(not(any(
|
|
|
|
target_os = "solaris",
|
|
|
|
target_os = "fuchsia",
|
|
|
|
target_os = "redox",
|
|
|
|
target_os = "illumos"
|
|
|
|
)))]
|
2015-02-03 05:39:14 +00:00
|
|
|
fn next(&mut self) -> Option<io::Result<DirEntry>> {
|
2018-06-12 19:03:27 +00:00
|
|
|
if self.end_of_stream {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2015-12-02 18:31:29 +00:00
|
|
|
unsafe {
|
2020-10-07 21:35:51 +00:00
|
|
|
let mut ret = DirEntry { entry: mem::zeroed(), dir: Arc::clone(&self.inner) };
|
2015-12-02 18:31:29 +00:00
|
|
|
let mut entry_ptr = ptr::null_mut();
|
|
|
|
loop {
|
2018-06-12 19:03:27 +00:00
|
|
|
if readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 {
|
|
|
|
if entry_ptr.is_null() {
|
|
|
|
// We encountered an error (which will be returned in this iteration), but
|
|
|
|
// we also reached the end of the directory stream. The `end_of_stream`
|
|
|
|
// flag is enabled to make sure that we return `None` in the next iteration
|
|
|
|
// (instead of looping forever)
|
|
|
|
self.end_of_stream = true;
|
|
|
|
}
|
2019-12-22 22:42:04 +00:00
|
|
|
return Some(Err(Error::last_os_error()));
|
2015-12-02 18:31:29 +00:00
|
|
|
}
|
|
|
|
if entry_ptr.is_null() {
|
2019-12-22 22:42:04 +00:00
|
|
|
return None;
|
2015-12-02 18:31:29 +00:00
|
|
|
}
|
|
|
|
if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
|
2019-12-22 22:42:04 +00:00
|
|
|
return Some(Ok(ret));
|
2015-12-02 18:31:29 +00:00
|
|
|
}
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-20 17:46:56 +00:00
|
|
|
impl Drop for Dir {
|
2015-02-03 05:39:14 +00:00
|
|
|
fn drop(&mut self) {
|
2015-02-20 17:46:56 +00:00
|
|
|
let r = unsafe { libc::closedir(self.0) };
|
2015-02-03 05:39:14 +00:00
|
|
|
debug_assert_eq!(r, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DirEntry {
|
|
|
|
pub fn path(&self) -> PathBuf {
|
2020-10-07 21:35:51 +00:00
|
|
|
self.dir.root.join(OsStr::from_bytes(self.name_bytes()))
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
2015-04-16 06:21:13 +00:00
|
|
|
pub fn file_name(&self) -> OsString {
|
|
|
|
OsStr::from_bytes(self.name_bytes()).to_os_string()
|
|
|
|
}
|
|
|
|
|
2018-05-24 21:50:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
|
|
|
|
pub fn metadata(&self) -> io::Result<FileAttr> {
|
2020-10-07 21:35:51 +00:00
|
|
|
let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?;
|
2019-10-04 08:17:23 +00:00
|
|
|
let name = self.entry.d_name.as_ptr();
|
|
|
|
|
2019-10-13 23:17:15 +00:00
|
|
|
cfg_has_statx! {
|
2019-10-04 08:17:23 +00:00
|
|
|
if let Some(ret) = unsafe { try_statx(
|
|
|
|
fd,
|
|
|
|
name,
|
|
|
|
libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
|
|
|
|
libc::STATX_ALL,
|
|
|
|
) } {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-24 21:50:28 +00:00
|
|
|
let mut stat: stat64 = unsafe { mem::zeroed() };
|
2019-12-22 22:42:04 +00:00
|
|
|
cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?;
|
2019-10-04 08:17:23 +00:00
|
|
|
Ok(FileAttr::from_stat64(stat))
|
2018-05-24 21:50:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))]
|
2015-04-16 06:21:13 +00:00
|
|
|
pub fn metadata(&self) -> io::Result<FileAttr> {
|
|
|
|
lstat(&self.path())
|
|
|
|
}
|
|
|
|
|
2020-10-07 18:38:25 +00:00
|
|
|
#[cfg(any(
|
|
|
|
target_os = "solaris",
|
|
|
|
target_os = "illumos",
|
|
|
|
target_os = "haiku",
|
|
|
|
target_os = "vxworks"
|
|
|
|
))]
|
2016-09-25 04:38:56 +00:00
|
|
|
pub fn file_type(&self) -> io::Result<FileType> {
|
|
|
|
lstat(&self.path()).map(|m| m.file_type())
|
|
|
|
}
|
|
|
|
|
2020-10-07 18:38:25 +00:00
|
|
|
#[cfg(not(any(
|
|
|
|
target_os = "solaris",
|
|
|
|
target_os = "illumos",
|
|
|
|
target_os = "haiku",
|
|
|
|
target_os = "vxworks"
|
|
|
|
)))]
|
2015-04-16 06:21:13 +00:00
|
|
|
pub fn file_type(&self) -> io::Result<FileType> {
|
2015-12-02 18:31:29 +00:00
|
|
|
match self.entry.d_type {
|
|
|
|
libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }),
|
|
|
|
libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }),
|
|
|
|
libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }),
|
|
|
|
libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }),
|
|
|
|
libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }),
|
|
|
|
libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }),
|
|
|
|
libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }),
|
|
|
|
_ => lstat(&self.path()).map(|m| m.file_type()),
|
2015-04-16 06:21:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
#[cfg(any(
|
|
|
|
target_os = "macos",
|
|
|
|
target_os = "ios",
|
|
|
|
target_os = "linux",
|
|
|
|
target_os = "emscripten",
|
|
|
|
target_os = "android",
|
|
|
|
target_os = "solaris",
|
2020-04-13 23:37:22 +00:00
|
|
|
target_os = "illumos",
|
2019-12-22 22:42:04 +00:00
|
|
|
target_os = "haiku",
|
|
|
|
target_os = "l4re",
|
|
|
|
target_os = "fuchsia",
|
2020-10-07 18:38:25 +00:00
|
|
|
target_os = "redox",
|
|
|
|
target_os = "vxworks"
|
2019-12-22 22:42:04 +00:00
|
|
|
))]
|
std: Deprecate all std::os::*::raw types
This commit is an implementation of [RFC 1415][rfc] which deprecates all types
in the `std::os::*::raw` modules.
[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1415-trim-std-os.md
Many of the types in these modules don't actually have a canonical platform
representation, for example the definition of `stat` on 32-bit Linux will change
depending on whether C code is compiled with LFS support or not. Unfortunately
the current types in `std::os::*::raw` are billed as "compatible with C", which
in light of this means it isn't really possible.
To make matters worse, platforms like Android sometimes define these types as
*smaller* than the way they're actually represented in the `stat` structure
itself. This means that when methods like `DirEntry::ino` are called on Android
the result may be truncated as we're tied to returning a `ino_t` type, not the
underlying type.
The commit here incorporates two backwards-compatible components:
* Deprecate all `raw` types that aren't in `std::os::raw`
* Expand the `std::os::*::fs::MetadataExt` trait on all platforms for method
accessors of all fields. The fields now returned widened types which are the
same across platforms (consistency across platforms is not required, however,
it's just convenient).
and two also backwards-incompatible components:
* Change the definition of all `std::os::*::raw` type aliases to
correspond to the newly widened types that are being returned on each
platform.
* Change the definition of `std::os::*::raw::stat` on Linux to match the LFS
definitions rather than the standard ones.
The breaking changes here will specifically break code that assumes that `libc`
and `std` agree on the definition of `std::os::*::raw` types, or that the `std`
types are faithful representations of the types in C. An [audit] has been
performed of crates.io to determine the fallout which was determined two be
minimal, with the two found cases of breakage having been fixed now.
[audit]: https://github.com/rust-lang/rfcs/pull/1415#issuecomment-180645582
---
Ok, so after all that, we're finally able to support LFS on Linux! This commit
then simultaneously starts using `stat64` and friends on Linux to ensure that we
can open >4GB files on 32-bit Linux. Yay!
Closes #28978
Closes #30050
Closes #31549
2016-02-05 01:16:47 +00:00
|
|
|
pub fn ino(&self) -> u64 {
|
|
|
|
self.entry.d_ino as u64
|
2015-12-02 18:31:29 +00:00
|
|
|
}
|
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
#[cfg(any(
|
|
|
|
target_os = "freebsd",
|
|
|
|
target_os = "openbsd",
|
|
|
|
target_os = "netbsd",
|
|
|
|
target_os = "dragonfly"
|
|
|
|
))]
|
std: Deprecate all std::os::*::raw types
This commit is an implementation of [RFC 1415][rfc] which deprecates all types
in the `std::os::*::raw` modules.
[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1415-trim-std-os.md
Many of the types in these modules don't actually have a canonical platform
representation, for example the definition of `stat` on 32-bit Linux will change
depending on whether C code is compiled with LFS support or not. Unfortunately
the current types in `std::os::*::raw` are billed as "compatible with C", which
in light of this means it isn't really possible.
To make matters worse, platforms like Android sometimes define these types as
*smaller* than the way they're actually represented in the `stat` structure
itself. This means that when methods like `DirEntry::ino` are called on Android
the result may be truncated as we're tied to returning a `ino_t` type, not the
underlying type.
The commit here incorporates two backwards-compatible components:
* Deprecate all `raw` types that aren't in `std::os::raw`
* Expand the `std::os::*::fs::MetadataExt` trait on all platforms for method
accessors of all fields. The fields now returned widened types which are the
same across platforms (consistency across platforms is not required, however,
it's just convenient).
and two also backwards-incompatible components:
* Change the definition of all `std::os::*::raw` type aliases to
correspond to the newly widened types that are being returned on each
platform.
* Change the definition of `std::os::*::raw::stat` on Linux to match the LFS
definitions rather than the standard ones.
The breaking changes here will specifically break code that assumes that `libc`
and `std` agree on the definition of `std::os::*::raw` types, or that the `std`
types are faithful representations of the types in C. An [audit] has been
performed of crates.io to determine the fallout which was determined two be
minimal, with the two found cases of breakage having been fixed now.
[audit]: https://github.com/rust-lang/rfcs/pull/1415#issuecomment-180645582
---
Ok, so after all that, we're finally able to support LFS on Linux! This commit
then simultaneously starts using `stat64` and friends on Linux to ensure that we
can open >4GB files on 32-bit Linux. Yay!
Closes #28978
Closes #30050
Closes #31549
2016-02-05 01:16:47 +00:00
|
|
|
pub fn ino(&self) -> u64 {
|
|
|
|
self.entry.d_fileno as u64
|
2015-04-16 06:21:13 +00:00
|
|
|
}
|
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
#[cfg(any(
|
|
|
|
target_os = "macos",
|
|
|
|
target_os = "ios",
|
|
|
|
target_os = "netbsd",
|
|
|
|
target_os = "openbsd",
|
|
|
|
target_os = "freebsd",
|
|
|
|
target_os = "dragonfly"
|
|
|
|
))]
|
2015-12-02 18:31:29 +00:00
|
|
|
fn name_bytes(&self) -> &[u8] {
|
2019-02-10 19:23:21 +00:00
|
|
|
use crate::slice;
|
2015-02-03 05:39:14 +00:00
|
|
|
unsafe {
|
2019-12-22 22:42:04 +00:00
|
|
|
slice::from_raw_parts(
|
|
|
|
self.entry.d_name.as_ptr() as *const u8,
|
|
|
|
self.entry.d_namlen as usize,
|
|
|
|
)
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-22 22:42:04 +00:00
|
|
|
#[cfg(any(
|
|
|
|
target_os = "android",
|
|
|
|
target_os = "linux",
|
|
|
|
target_os = "emscripten",
|
|
|
|
target_os = "l4re",
|
2020-10-07 18:38:25 +00:00
|
|
|
target_os = "haiku",
|
|
|
|
target_os = "vxworks"
|
2019-12-22 22:42:04 +00:00
|
|
|
))]
|
2015-12-02 18:31:29 +00:00
|
|
|
fn name_bytes(&self) -> &[u8] {
|
2019-12-22 22:42:04 +00:00
|
|
|
unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() }
|
2015-02-20 17:46:56 +00:00
|
|
|
}
|
2020-04-13 23:37:22 +00:00
|
|
|
#[cfg(any(
|
|
|
|
target_os = "solaris",
|
|
|
|
target_os = "illumos",
|
|
|
|
target_os = "fuchsia",
|
|
|
|
target_os = "redox"
|
|
|
|
))]
|
2016-01-21 16:30:22 +00:00
|
|
|
fn name_bytes(&self) -> &[u8] {
|
|
|
|
&*self.name
|
|
|
|
}
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl OpenOptions {
|
|
|
|
pub fn new() -> OpenOptions {
|
|
|
|
OpenOptions {
|
2016-01-13 17:08:08 +00:00
|
|
|
// generic
|
2015-02-03 05:39:14 +00:00
|
|
|
read: false,
|
|
|
|
write: false,
|
2016-01-13 17:08:08 +00:00
|
|
|
append: false,
|
|
|
|
truncate: false,
|
|
|
|
create: false,
|
|
|
|
create_new: false,
|
|
|
|
// system-specific
|
|
|
|
custom_flags: 0,
|
2015-02-11 22:40:09 +00:00
|
|
|
mode: 0o666,
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
pub fn read(&mut self, read: bool) {
|
|
|
|
self.read = read;
|
|
|
|
}
|
|
|
|
pub fn write(&mut self, write: bool) {
|
|
|
|
self.write = write;
|
|
|
|
}
|
|
|
|
pub fn append(&mut self, append: bool) {
|
|
|
|
self.append = append;
|
|
|
|
}
|
|
|
|
pub fn truncate(&mut self, truncate: bool) {
|
|
|
|
self.truncate = truncate;
|
|
|
|
}
|
|
|
|
pub fn create(&mut self, create: bool) {
|
|
|
|
self.create = create;
|
|
|
|
}
|
|
|
|
pub fn create_new(&mut self, create_new: bool) {
|
|
|
|
self.create_new = create_new;
|
|
|
|
}
|
2016-01-13 17:08:08 +00:00
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
pub fn custom_flags(&mut self, flags: i32) {
|
|
|
|
self.custom_flags = flags;
|
|
|
|
}
|
|
|
|
pub fn mode(&mut self, mode: u32) {
|
|
|
|
self.mode = mode as mode_t;
|
|
|
|
}
|
2016-01-13 17:08:08 +00:00
|
|
|
|
|
|
|
fn get_access_mode(&self) -> io::Result<c_int> {
|
|
|
|
match (self.read, self.write, self.append) {
|
2019-12-22 22:42:04 +00:00
|
|
|
(true, false, false) => Ok(libc::O_RDONLY),
|
|
|
|
(false, true, false) => Ok(libc::O_WRONLY),
|
|
|
|
(true, true, false) => Ok(libc::O_RDWR),
|
|
|
|
(false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND),
|
|
|
|
(true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND),
|
2016-01-13 17:08:08 +00:00
|
|
|
(false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)),
|
|
|
|
}
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
2016-01-13 17:08:08 +00:00
|
|
|
fn get_creation_mode(&self) -> io::Result<c_int> {
|
|
|
|
match (self.write, self.append) {
|
2016-01-15 18:04:53 +00:00
|
|
|
(true, false) => {}
|
2019-12-22 22:42:04 +00:00
|
|
|
(false, false) => {
|
2016-01-15 18:04:53 +00:00
|
|
|
if self.truncate || self.create || self.create_new {
|
|
|
|
return Err(Error::from_raw_os_error(libc::EINVAL));
|
2019-12-22 22:42:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
(_, true) => {
|
2016-01-15 18:04:53 +00:00
|
|
|
if self.truncate && !self.create_new {
|
|
|
|
return Err(Error::from_raw_os_error(libc::EINVAL));
|
2019-12-22 22:42:04 +00:00
|
|
|
}
|
|
|
|
}
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
2016-01-13 17:08:08 +00:00
|
|
|
|
|
|
|
Ok(match (self.create, self.truncate, self.create_new) {
|
2019-12-22 22:42:04 +00:00
|
|
|
(false, false, false) => 0,
|
|
|
|
(true, false, false) => libc::O_CREAT,
|
|
|
|
(false, true, false) => libc::O_TRUNC,
|
|
|
|
(true, true, false) => libc::O_CREAT | libc::O_TRUNC,
|
|
|
|
(_, _, true) => libc::O_CREAT | libc::O_EXCL,
|
|
|
|
})
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl File {
|
|
|
|
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
|
2016-03-23 03:01:37 +00:00
|
|
|
let path = cstr(path)?;
|
2015-04-03 22:34:15 +00:00
|
|
|
File::open_c(&path, opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
|
2019-12-22 22:42:04 +00:00
|
|
|
let flags = libc::O_CLOEXEC
|
|
|
|
| opts.get_access_mode()?
|
|
|
|
| opts.get_creation_mode()?
|
|
|
|
| (opts.custom_flags as c_int & !libc::O_ACCMODE);
|
2020-05-05 14:43:57 +00:00
|
|
|
// The third argument of `open64` is documented to have type `mode_t`. On
|
|
|
|
// some platforms (like macOS, where `open64` is actually `open`), `mode_t` is `u16`.
|
|
|
|
// However, since this is a variadic function, C integer promotion rules mean that on
|
|
|
|
// the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms).
|
2019-12-22 22:42:04 +00:00
|
|
|
let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
|
2020-07-21 21:32:36 +00:00
|
|
|
Ok(File(FileDesc::new(fd)))
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn file_attr(&self) -> io::Result<FileAttr> {
|
2019-10-04 08:17:23 +00:00
|
|
|
let fd = self.0.raw();
|
|
|
|
|
2019-10-13 23:17:15 +00:00
|
|
|
cfg_has_statx! {
|
2019-10-04 08:17:23 +00:00
|
|
|
if let Some(ret) = unsafe { try_statx(
|
|
|
|
fd,
|
|
|
|
b"\0" as *const _ as *const libc::c_char,
|
|
|
|
libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT,
|
|
|
|
libc::STATX_ALL,
|
|
|
|
) } {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
std: Deprecate all std::os::*::raw types
This commit is an implementation of [RFC 1415][rfc] which deprecates all types
in the `std::os::*::raw` modules.
[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1415-trim-std-os.md
Many of the types in these modules don't actually have a canonical platform
representation, for example the definition of `stat` on 32-bit Linux will change
depending on whether C code is compiled with LFS support or not. Unfortunately
the current types in `std::os::*::raw` are billed as "compatible with C", which
in light of this means it isn't really possible.
To make matters worse, platforms like Android sometimes define these types as
*smaller* than the way they're actually represented in the `stat` structure
itself. This means that when methods like `DirEntry::ino` are called on Android
the result may be truncated as we're tied to returning a `ino_t` type, not the
underlying type.
The commit here incorporates two backwards-compatible components:
* Deprecate all `raw` types that aren't in `std::os::raw`
* Expand the `std::os::*::fs::MetadataExt` trait on all platforms for method
accessors of all fields. The fields now returned widened types which are the
same across platforms (consistency across platforms is not required, however,
it's just convenient).
and two also backwards-incompatible components:
* Change the definition of all `std::os::*::raw` type aliases to
correspond to the newly widened types that are being returned on each
platform.
* Change the definition of `std::os::*::raw::stat` on Linux to match the LFS
definitions rather than the standard ones.
The breaking changes here will specifically break code that assumes that `libc`
and `std` agree on the definition of `std::os::*::raw` types, or that the `std`
types are faithful representations of the types in C. An [audit] has been
performed of crates.io to determine the fallout which was determined two be
minimal, with the two found cases of breakage having been fixed now.
[audit]: https://github.com/rust-lang/rfcs/pull/1415#issuecomment-180645582
---
Ok, so after all that, we're finally able to support LFS on Linux! This commit
then simultaneously starts using `stat64` and friends on Linux to ensure that we
can open >4GB files on 32-bit Linux. Yay!
Closes #28978
Closes #30050
Closes #31549
2016-02-05 01:16:47 +00:00
|
|
|
let mut stat: stat64 = unsafe { mem::zeroed() };
|
2019-12-22 22:42:04 +00:00
|
|
|
cvt(unsafe { fstat64(fd, &mut stat) })?;
|
2019-10-04 08:17:23 +00:00
|
|
|
Ok(FileAttr::from_stat64(stat))
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn fsync(&self) -> io::Result<()> {
|
2019-04-19 18:42:52 +00:00
|
|
|
cvt_r(|| unsafe { os_fsync(self.0.raw()) })?;
|
|
|
|
return Ok(());
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
|
|
|
unsafe fn os_fsync(fd: c_int) -> c_int {
|
|
|
|
libc::fcntl(fd, libc::F_FULLFSYNC)
|
|
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
|
2019-12-22 22:42:04 +00:00
|
|
|
unsafe fn os_fsync(fd: c_int) -> c_int {
|
|
|
|
libc::fsync(fd)
|
|
|
|
}
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn datasync(&self) -> io::Result<()> {
|
2016-03-23 03:01:37 +00:00
|
|
|
cvt_r(|| unsafe { os_datasync(self.0.raw()) })?;
|
2015-02-03 05:39:14 +00:00
|
|
|
return Ok(());
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
|
|
|
unsafe fn os_datasync(fd: c_int) -> c_int {
|
|
|
|
libc::fcntl(fd, libc::F_FULLFSYNC)
|
|
|
|
}
|
2020-10-13 13:57:31 +00:00
|
|
|
#[cfg(any(
|
|
|
|
target_os = "freebsd",
|
|
|
|
target_os = "linux",
|
|
|
|
target_os = "android",
|
|
|
|
target_os = "netbsd",
|
|
|
|
target_os = "openbsd"
|
|
|
|
))]
|
2019-12-22 22:42:04 +00:00
|
|
|
unsafe fn os_datasync(fd: c_int) -> c_int {
|
|
|
|
libc::fdatasync(fd)
|
|
|
|
}
|
2020-10-13 13:57:31 +00:00
|
|
|
#[cfg(not(any(
|
|
|
|
target_os = "android",
|
|
|
|
target_os = "freebsd",
|
|
|
|
target_os = "ios",
|
|
|
|
target_os = "linux",
|
|
|
|
target_os = "macos",
|
|
|
|
target_os = "netbsd",
|
|
|
|
target_os = "openbsd"
|
|
|
|
)))]
|
2019-12-22 22:42:04 +00:00
|
|
|
unsafe fn os_datasync(fd: c_int) -> c_int {
|
|
|
|
libc::fsync(fd)
|
|
|
|
}
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn truncate(&self, size: u64) -> io::Result<()> {
|
2016-04-25 20:39:05 +00:00
|
|
|
#[cfg(target_os = "android")]
|
2019-02-10 19:23:21 +00:00
|
|
|
return crate::sys::android::ftruncate64(self.0.raw(), size);
|
2016-04-25 20:39:05 +00:00
|
|
|
|
|
|
|
#[cfg(not(target_os = "android"))]
|
2019-08-06 17:34:10 +00:00
|
|
|
{
|
2019-08-08 09:44:59 +00:00
|
|
|
use crate::convert::TryInto;
|
2019-12-22 22:42:04 +00:00
|
|
|
let size: off64_t =
|
|
|
|
size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
|
2020-01-02 08:56:12 +00:00
|
|
|
cvt_r(|| unsafe { ftruncate64(self.0.raw(), size) }).map(drop)
|
2019-08-06 17:34:10 +00:00
|
|
|
}
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
|
|
|
self.0.read(buf)
|
|
|
|
}
|
|
|
|
|
2019-04-27 15:34:08 +00:00
|
|
|
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
std: Add `{read,write}_vectored` for more types
This commit implements the `{read,write}_vectored` methods on more types
in the standard library, namely:
* `std::fs::File`
* `std::process::ChildStd{in,out,err}`
* `std::io::Std{in,out,err}`
* `std::io::Std{in,out,err}Lock`
* `std::io::Std{in,out,err}Raw`
Where supported the OS implementations hook up to native support,
otherwise it falls back to the already-defaulted implementation.
2019-04-10 19:51:25 +00:00
|
|
|
self.0.read_vectored(bufs)
|
|
|
|
}
|
|
|
|
|
2020-01-03 19:26:05 +00:00
|
|
|
#[inline]
|
2020-03-12 01:02:52 +00:00
|
|
|
pub fn is_read_vectored(&self) -> bool {
|
|
|
|
self.0.is_read_vectored()
|
2020-01-03 19:26:05 +00:00
|
|
|
}
|
|
|
|
|
2016-08-15 23:11:33 +00:00
|
|
|
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
|
|
|
|
self.0.read_at(buf, offset)
|
|
|
|
}
|
|
|
|
|
2015-02-03 05:39:14 +00:00
|
|
|
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
|
|
|
self.0.write(buf)
|
|
|
|
}
|
|
|
|
|
2019-04-27 15:34:08 +00:00
|
|
|
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
std: Add `{read,write}_vectored` for more types
This commit implements the `{read,write}_vectored` methods on more types
in the standard library, namely:
* `std::fs::File`
* `std::process::ChildStd{in,out,err}`
* `std::io::Std{in,out,err}`
* `std::io::Std{in,out,err}Lock`
* `std::io::Std{in,out,err}Raw`
Where supported the OS implementations hook up to native support,
otherwise it falls back to the already-defaulted implementation.
2019-04-10 19:51:25 +00:00
|
|
|
self.0.write_vectored(bufs)
|
|
|
|
}
|
|
|
|
|
2020-01-03 19:26:05 +00:00
|
|
|
#[inline]
|
2020-03-12 01:02:52 +00:00
|
|
|
pub fn is_write_vectored(&self) -> bool {
|
|
|
|
self.0.is_write_vectored()
|
2020-01-03 19:26:05 +00:00
|
|
|
}
|
|
|
|
|
2016-08-15 23:11:33 +00:00
|
|
|
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
|
|
|
|
self.0.write_at(buf, offset)
|
|
|
|
}
|
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
pub fn flush(&self) -> io::Result<()> {
|
|
|
|
Ok(())
|
|
|
|
}
|
2015-02-03 05:39:14 +00:00
|
|
|
|
|
|
|
pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
|
|
|
|
let (whence, pos) = match pos {
|
2016-07-18 12:31:26 +00:00
|
|
|
// Casting to `i64` is fine, too large values will end up as
|
|
|
|
// negative which will cause an error in `lseek64`.
|
|
|
|
SeekFrom::Start(off) => (libc::SEEK_SET, off as i64),
|
|
|
|
SeekFrom::End(off) => (libc::SEEK_END, off),
|
|
|
|
SeekFrom::Current(off) => (libc::SEEK_CUR, off),
|
2015-02-03 05:39:14 +00:00
|
|
|
};
|
2016-03-23 03:01:37 +00:00
|
|
|
let n = cvt(unsafe { lseek64(self.0.raw(), pos, whence) })?;
|
2015-02-03 05:39:14 +00:00
|
|
|
Ok(n as u64)
|
|
|
|
}
|
|
|
|
|
2016-01-21 05:24:23 +00:00
|
|
|
pub fn duplicate(&self) -> io::Result<File> {
|
|
|
|
self.0.duplicate().map(File)
|
|
|
|
}
|
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
pub fn fd(&self) -> &FileDesc {
|
|
|
|
&self.0
|
|
|
|
}
|
2015-07-16 06:31:24 +00:00
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
pub fn into_fd(self) -> FileDesc {
|
|
|
|
self.0
|
|
|
|
}
|
2016-11-19 17:53:54 +00:00
|
|
|
|
|
|
|
pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
|
|
|
|
cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?;
|
|
|
|
Ok(())
|
|
|
|
}
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
2015-04-28 00:29:35 +00:00
|
|
|
impl DirBuilder {
|
|
|
|
pub fn new() -> DirBuilder {
|
|
|
|
DirBuilder { mode: 0o777 }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn mkdir(&self, p: &Path) -> io::Result<()> {
|
2016-03-23 03:01:37 +00:00
|
|
|
let p = cstr(p)?;
|
|
|
|
cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?;
|
2015-04-28 00:29:35 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
std: Deprecate all std::os::*::raw types
This commit is an implementation of [RFC 1415][rfc] which deprecates all types
in the `std::os::*::raw` modules.
[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1415-trim-std-os.md
Many of the types in these modules don't actually have a canonical platform
representation, for example the definition of `stat` on 32-bit Linux will change
depending on whether C code is compiled with LFS support or not. Unfortunately
the current types in `std::os::*::raw` are billed as "compatible with C", which
in light of this means it isn't really possible.
To make matters worse, platforms like Android sometimes define these types as
*smaller* than the way they're actually represented in the `stat` structure
itself. This means that when methods like `DirEntry::ino` are called on Android
the result may be truncated as we're tied to returning a `ino_t` type, not the
underlying type.
The commit here incorporates two backwards-compatible components:
* Deprecate all `raw` types that aren't in `std::os::raw`
* Expand the `std::os::*::fs::MetadataExt` trait on all platforms for method
accessors of all fields. The fields now returned widened types which are the
same across platforms (consistency across platforms is not required, however,
it's just convenient).
and two also backwards-incompatible components:
* Change the definition of all `std::os::*::raw` type aliases to
correspond to the newly widened types that are being returned on each
platform.
* Change the definition of `std::os::*::raw::stat` on Linux to match the LFS
definitions rather than the standard ones.
The breaking changes here will specifically break code that assumes that `libc`
and `std` agree on the definition of `std::os::*::raw` types, or that the `std`
types are faithful representations of the types in C. An [audit] has been
performed of crates.io to determine the fallout which was determined two be
minimal, with the two found cases of breakage having been fixed now.
[audit]: https://github.com/rust-lang/rfcs/pull/1415#issuecomment-180645582
---
Ok, so after all that, we're finally able to support LFS on Linux! This commit
then simultaneously starts using `stat64` and friends on Linux to ensure that we
can open >4GB files on 32-bit Linux. Yay!
Closes #28978
Closes #30050
Closes #31549
2016-02-05 01:16:47 +00:00
|
|
|
pub fn set_mode(&mut self, mode: u32) {
|
|
|
|
self.mode = mode as mode_t;
|
2015-04-28 00:29:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-18 06:47:40 +00:00
|
|
|
fn cstr(path: &Path) -> io::Result<CString> {
|
2016-03-23 03:01:37 +00:00
|
|
|
Ok(CString::new(path.as_os_str().as_bytes())?)
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
std: Stabilize parts of std::os::platform::io
This commit stabilizes the platform-specific `io` modules, specifically around
the traits having to do with the raw representation of each object on each
platform.
Specifically, the following material was stabilized:
* `AsRaw{Fd,Socket,Handle}`
* `RawFd` (renamed from `Fd`)
* `RawHandle` (renamed from `Handle`)
* `RawSocket` (renamed from `Socket`)
* `AsRaw{Fd,Socket,Handle}` implementations
* `std::os::{unix, windows}::io`
The following material was added as `#[unstable]`:
* `FromRaw{Fd,Socket,Handle}`
* Implementations for various primitives
There are a number of future improvements that are possible to make to this
module, but this should cover a good bit of functionality desired from these
modules for now. Some specific future additions may include:
* `IntoRawXXX` traits to consume the raw representation and cancel the
auto-destructor.
* `Fd`, `Socket`, and `Handle` abstractions that behave like Rust objects and
have nice methods for various syscalls.
At this time though, these are considered backwards-compatible extensions and
will not be stabilized at this time.
This commit is a breaking change due to the addition of `Raw` in from of the
type aliases in each of the platform-specific modules.
[breaking-change]
2015-03-26 23:18:29 +00:00
|
|
|
impl FromInner<c_int> for File {
|
|
|
|
fn from_inner(fd: c_int) -> File {
|
|
|
|
File(FileDesc::new(fd))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-19 09:27:19 +00:00
|
|
|
impl fmt::Debug for File {
|
2019-03-01 08:34:11 +00:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2015-04-19 09:27:19 +00:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
fn get_path(fd: c_int) -> Option<PathBuf> {
|
|
|
|
let mut p = PathBuf::from("/proc/self/fd");
|
|
|
|
p.push(&fd.to_string());
|
|
|
|
readlink(&p).ok()
|
|
|
|
}
|
|
|
|
|
2015-07-10 14:23:14 +00:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
fn get_path(fd: c_int) -> Option<PathBuf> {
|
Reduce the reliance on `PATH_MAX`
- Rewrite `std::sys::fs::readlink` not to rely on `PATH_MAX`
It currently has the following problems:
1. It uses `_PC_NAME_MAX` to query the maximum length of a file path in
the underlying system. However, the meaning of the constant is the
maximum length of *a path component*, not a full path. The correct
constant should be `_PC_PATH_MAX`.
2. `pathconf` *may* fail if the referred file does not exist. This can
be problematic if the file which the symbolic link points to does not
exist, but the link itself does exist. In this case, the current
implementation resorts to the hard-coded value of `1024`, which is not
ideal.
3. There may exist a platform where there is no limit on file path
lengths in general. That's the reaon why GNU Hurd doesn't define
`PATH_MAX` at all, in addition to having `pathconf` always returning
`-1`. In these platforms, the content of the symbolic link can be
silently truncated if the length exceeds the hard-coded limit mentioned
above.
4. The value obtained by `pathconf` may be outdated at the point of
actually calling `readlink`. This is inherently racy.
This commit introduces a loop that gradually increases the length of the
buffer passed to `readlink`, eliminating the need of `pathconf`.
- Remove the arbitrary memory limit of `std::sys::fs::realpath`
As per POSIX 2013, `realpath` will return a malloc'ed buffer if the
second argument is a null pointer.[1]
[1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html
- Comment on functions that are still using `PATH_MAX`
There are some functions that only work in terms of `PATH_MAX`, such as
`F_GETPATH` in OS X. Comments on them for posterity.
2015-08-19 04:11:40 +00:00
|
|
|
// FIXME: The use of PATH_MAX is generally not encouraged, but it
|
2017-03-12 18:13:35 +00:00
|
|
|
// is inevitable in this case because macOS defines `fcntl` with
|
Reduce the reliance on `PATH_MAX`
- Rewrite `std::sys::fs::readlink` not to rely on `PATH_MAX`
It currently has the following problems:
1. It uses `_PC_NAME_MAX` to query the maximum length of a file path in
the underlying system. However, the meaning of the constant is the
maximum length of *a path component*, not a full path. The correct
constant should be `_PC_PATH_MAX`.
2. `pathconf` *may* fail if the referred file does not exist. This can
be problematic if the file which the symbolic link points to does not
exist, but the link itself does exist. In this case, the current
implementation resorts to the hard-coded value of `1024`, which is not
ideal.
3. There may exist a platform where there is no limit on file path
lengths in general. That's the reaon why GNU Hurd doesn't define
`PATH_MAX` at all, in addition to having `pathconf` always returning
`-1`. In these platforms, the content of the symbolic link can be
silently truncated if the length exceeds the hard-coded limit mentioned
above.
4. The value obtained by `pathconf` may be outdated at the point of
actually calling `readlink`. This is inherently racy.
This commit introduces a loop that gradually increases the length of the
buffer passed to `readlink`, eliminating the need of `pathconf`.
- Remove the arbitrary memory limit of `std::sys::fs::realpath`
As per POSIX 2013, `realpath` will return a malloc'ed buffer if the
second argument is a null pointer.[1]
[1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html
- Comment on functions that are still using `PATH_MAX`
There are some functions that only work in terms of `PATH_MAX`, such as
`F_GETPATH` in OS X. Comments on them for posterity.
2015-08-19 04:11:40 +00:00
|
|
|
// `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
|
|
|
|
// alternatives. If a better method is invented, it should be used
|
|
|
|
// instead.
|
2019-12-22 22:42:04 +00:00
|
|
|
let mut buf = vec![0; libc::PATH_MAX as usize];
|
2015-07-10 14:23:14 +00:00
|
|
|
let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) };
|
|
|
|
if n == -1 {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let l = buf.iter().position(|&c| c == 0).unwrap();
|
|
|
|
buf.truncate(l as usize);
|
Reduce the reliance on `PATH_MAX`
- Rewrite `std::sys::fs::readlink` not to rely on `PATH_MAX`
It currently has the following problems:
1. It uses `_PC_NAME_MAX` to query the maximum length of a file path in
the underlying system. However, the meaning of the constant is the
maximum length of *a path component*, not a full path. The correct
constant should be `_PC_PATH_MAX`.
2. `pathconf` *may* fail if the referred file does not exist. This can
be problematic if the file which the symbolic link points to does not
exist, but the link itself does exist. In this case, the current
implementation resorts to the hard-coded value of `1024`, which is not
ideal.
3. There may exist a platform where there is no limit on file path
lengths in general. That's the reaon why GNU Hurd doesn't define
`PATH_MAX` at all, in addition to having `pathconf` always returning
`-1`. In these platforms, the content of the symbolic link can be
silently truncated if the length exceeds the hard-coded limit mentioned
above.
4. The value obtained by `pathconf` may be outdated at the point of
actually calling `readlink`. This is inherently racy.
This commit introduces a loop that gradually increases the length of the
buffer passed to `readlink`, eliminating the need of `pathconf`.
- Remove the arbitrary memory limit of `std::sys::fs::realpath`
As per POSIX 2013, `realpath` will return a malloc'ed buffer if the
second argument is a null pointer.[1]
[1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html
- Comment on functions that are still using `PATH_MAX`
There are some functions that only work in terms of `PATH_MAX`, such as
`F_GETPATH` in OS X. Comments on them for posterity.
2015-08-19 04:11:40 +00:00
|
|
|
buf.shrink_to_fit();
|
2015-07-10 14:23:14 +00:00
|
|
|
Some(PathBuf::from(OsString::from_vec(buf)))
|
|
|
|
}
|
|
|
|
|
2020-10-07 18:38:25 +00:00
|
|
|
#[cfg(target_os = "vxworks")]
|
|
|
|
fn get_path(fd: c_int) -> Option<PathBuf> {
|
|
|
|
let mut buf = vec![0; libc::PATH_MAX as usize];
|
|
|
|
let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) };
|
|
|
|
if n == -1 {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let l = buf.iter().position(|&c| c == 0).unwrap();
|
|
|
|
buf.truncate(l as usize);
|
|
|
|
Some(PathBuf::from(OsString::from_vec(buf)))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "vxworks")))]
|
2015-04-19 09:27:19 +00:00
|
|
|
fn get_path(_fd: c_int) -> Option<PathBuf> {
|
|
|
|
// FIXME(#24570): implement this for other Unix platforms
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2020-10-07 18:38:25 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "vxworks"))]
|
2015-04-19 09:27:19 +00:00
|
|
|
fn get_mode(fd: c_int) -> Option<(bool, bool)> {
|
|
|
|
let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
|
|
|
|
if mode == -1 {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
match mode & libc::O_ACCMODE {
|
|
|
|
libc::O_RDONLY => Some((true, false)),
|
|
|
|
libc::O_RDWR => Some((true, true)),
|
|
|
|
libc::O_WRONLY => Some((false, true)),
|
2019-12-22 22:42:04 +00:00
|
|
|
_ => None,
|
2015-04-19 09:27:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-07 18:38:25 +00:00
|
|
|
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "vxworks")))]
|
2015-04-19 09:27:19 +00:00
|
|
|
fn get_mode(_fd: c_int) -> Option<(bool, bool)> {
|
|
|
|
// FIXME(#24570): implement this for other Unix platforms
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
let fd = self.0.raw();
|
2015-05-17 20:17:26 +00:00
|
|
|
let mut b = f.debug_struct("File");
|
|
|
|
b.field("fd", &fd);
|
2015-04-19 09:27:19 +00:00
|
|
|
if let Some(path) = get_path(fd) {
|
2015-05-17 20:17:26 +00:00
|
|
|
b.field("path", &path);
|
2015-04-19 09:27:19 +00:00
|
|
|
}
|
|
|
|
if let Some((read, write)) = get_mode(fd) {
|
2015-05-17 20:17:26 +00:00
|
|
|
b.field("read", &read).field("write", &write);
|
2015-04-19 09:27:19 +00:00
|
|
|
}
|
|
|
|
b.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-03 05:39:14 +00:00
|
|
|
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
|
2018-05-24 21:50:28 +00:00
|
|
|
let root = p.to_path_buf();
|
2016-03-23 03:01:37 +00:00
|
|
|
let p = cstr(p)?;
|
2015-02-03 05:39:14 +00:00
|
|
|
unsafe {
|
|
|
|
let ptr = libc::opendir(p.as_ptr());
|
|
|
|
if ptr.is_null() {
|
|
|
|
Err(Error::last_os_error())
|
|
|
|
} else {
|
2018-05-24 21:50:28 +00:00
|
|
|
let inner = InnerReadDir { dirp: Dir(ptr), root };
|
2020-10-09 17:54:50 +00:00
|
|
|
Ok(ReadDir {
|
|
|
|
inner: Arc::new(inner),
|
|
|
|
#[cfg(not(any(
|
2020-10-09 16:59:39 +00:00
|
|
|
target_os = "solaris",
|
|
|
|
target_os = "illumos",
|
|
|
|
target_os = "fuchsia",
|
|
|
|
target_os = "redox",
|
2020-10-09 17:54:50 +00:00
|
|
|
)))]
|
|
|
|
end_of_stream: false,
|
|
|
|
})
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn unlink(p: &Path) -> io::Result<()> {
|
2016-03-23 03:01:37 +00:00
|
|
|
let p = cstr(p)?;
|
|
|
|
cvt(unsafe { libc::unlink(p.as_ptr()) })?;
|
2015-02-03 05:39:14 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
|
2016-03-23 03:01:37 +00:00
|
|
|
let old = cstr(old)?;
|
|
|
|
let new = cstr(new)?;
|
|
|
|
cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?;
|
2015-02-03 05:39:14 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
|
2016-03-23 03:01:37 +00:00
|
|
|
let p = cstr(p)?;
|
|
|
|
cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?;
|
2015-02-03 05:39:14 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn rmdir(p: &Path) -> io::Result<()> {
|
2016-03-23 03:01:37 +00:00
|
|
|
let p = cstr(p)?;
|
|
|
|
cvt(unsafe { libc::rmdir(p.as_ptr()) })?;
|
2015-02-03 05:39:14 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn readlink(p: &Path) -> io::Result<PathBuf> {
|
2016-03-23 03:01:37 +00:00
|
|
|
let c_path = cstr(p)?;
|
2015-02-03 05:39:14 +00:00
|
|
|
let p = c_path.as_ptr();
|
Reduce the reliance on `PATH_MAX`
- Rewrite `std::sys::fs::readlink` not to rely on `PATH_MAX`
It currently has the following problems:
1. It uses `_PC_NAME_MAX` to query the maximum length of a file path in
the underlying system. However, the meaning of the constant is the
maximum length of *a path component*, not a full path. The correct
constant should be `_PC_PATH_MAX`.
2. `pathconf` *may* fail if the referred file does not exist. This can
be problematic if the file which the symbolic link points to does not
exist, but the link itself does exist. In this case, the current
implementation resorts to the hard-coded value of `1024`, which is not
ideal.
3. There may exist a platform where there is no limit on file path
lengths in general. That's the reaon why GNU Hurd doesn't define
`PATH_MAX` at all, in addition to having `pathconf` always returning
`-1`. In these platforms, the content of the symbolic link can be
silently truncated if the length exceeds the hard-coded limit mentioned
above.
4. The value obtained by `pathconf` may be outdated at the point of
actually calling `readlink`. This is inherently racy.
This commit introduces a loop that gradually increases the length of the
buffer passed to `readlink`, eliminating the need of `pathconf`.
- Remove the arbitrary memory limit of `std::sys::fs::realpath`
As per POSIX 2013, `realpath` will return a malloc'ed buffer if the
second argument is a null pointer.[1]
[1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html
- Comment on functions that are still using `PATH_MAX`
There are some functions that only work in terms of `PATH_MAX`, such as
`F_GETPATH` in OS X. Comments on them for posterity.
2015-08-19 04:11:40 +00:00
|
|
|
|
|
|
|
let mut buf = Vec::with_capacity(256);
|
|
|
|
|
|
|
|
loop {
|
2019-12-22 22:42:04 +00:00
|
|
|
let buf_read =
|
|
|
|
cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize;
|
Reduce the reliance on `PATH_MAX`
- Rewrite `std::sys::fs::readlink` not to rely on `PATH_MAX`
It currently has the following problems:
1. It uses `_PC_NAME_MAX` to query the maximum length of a file path in
the underlying system. However, the meaning of the constant is the
maximum length of *a path component*, not a full path. The correct
constant should be `_PC_PATH_MAX`.
2. `pathconf` *may* fail if the referred file does not exist. This can
be problematic if the file which the symbolic link points to does not
exist, but the link itself does exist. In this case, the current
implementation resorts to the hard-coded value of `1024`, which is not
ideal.
3. There may exist a platform where there is no limit on file path
lengths in general. That's the reaon why GNU Hurd doesn't define
`PATH_MAX` at all, in addition to having `pathconf` always returning
`-1`. In these platforms, the content of the symbolic link can be
silently truncated if the length exceeds the hard-coded limit mentioned
above.
4. The value obtained by `pathconf` may be outdated at the point of
actually calling `readlink`. This is inherently racy.
This commit introduces a loop that gradually increases the length of the
buffer passed to `readlink`, eliminating the need of `pathconf`.
- Remove the arbitrary memory limit of `std::sys::fs::realpath`
As per POSIX 2013, `realpath` will return a malloc'ed buffer if the
second argument is a null pointer.[1]
[1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html
- Comment on functions that are still using `PATH_MAX`
There are some functions that only work in terms of `PATH_MAX`, such as
`F_GETPATH` in OS X. Comments on them for posterity.
2015-08-19 04:11:40 +00:00
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
unsafe {
|
|
|
|
buf.set_len(buf_read);
|
|
|
|
}
|
Reduce the reliance on `PATH_MAX`
- Rewrite `std::sys::fs::readlink` not to rely on `PATH_MAX`
It currently has the following problems:
1. It uses `_PC_NAME_MAX` to query the maximum length of a file path in
the underlying system. However, the meaning of the constant is the
maximum length of *a path component*, not a full path. The correct
constant should be `_PC_PATH_MAX`.
2. `pathconf` *may* fail if the referred file does not exist. This can
be problematic if the file which the symbolic link points to does not
exist, but the link itself does exist. In this case, the current
implementation resorts to the hard-coded value of `1024`, which is not
ideal.
3. There may exist a platform where there is no limit on file path
lengths in general. That's the reaon why GNU Hurd doesn't define
`PATH_MAX` at all, in addition to having `pathconf` always returning
`-1`. In these platforms, the content of the symbolic link can be
silently truncated if the length exceeds the hard-coded limit mentioned
above.
4. The value obtained by `pathconf` may be outdated at the point of
actually calling `readlink`. This is inherently racy.
This commit introduces a loop that gradually increases the length of the
buffer passed to `readlink`, eliminating the need of `pathconf`.
- Remove the arbitrary memory limit of `std::sys::fs::realpath`
As per POSIX 2013, `realpath` will return a malloc'ed buffer if the
second argument is a null pointer.[1]
[1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html
- Comment on functions that are still using `PATH_MAX`
There are some functions that only work in terms of `PATH_MAX`, such as
`F_GETPATH` in OS X. Comments on them for posterity.
2015-08-19 04:11:40 +00:00
|
|
|
|
|
|
|
if buf_read != buf.capacity() {
|
|
|
|
buf.shrink_to_fit();
|
|
|
|
|
|
|
|
return Ok(PathBuf::from(OsString::from_vec(buf)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Trigger the internal buffer resizing logic of `Vec` by requiring
|
|
|
|
// more space than the current capacity. The length is guaranteed to be
|
|
|
|
// the same as the capacity due to the if statement above.
|
|
|
|
buf.reserve(1);
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
|
2016-03-23 03:01:37 +00:00
|
|
|
let src = cstr(src)?;
|
|
|
|
let dst = cstr(dst)?;
|
|
|
|
cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })?;
|
2015-02-03 05:39:14 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
|
2016-03-23 03:01:37 +00:00
|
|
|
let src = cstr(src)?;
|
|
|
|
let dst = cstr(dst)?;
|
|
|
|
cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?;
|
2015-02-03 05:39:14 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn stat(p: &Path) -> io::Result<FileAttr> {
|
2016-03-23 03:01:37 +00:00
|
|
|
let p = cstr(p)?;
|
2019-10-04 08:17:23 +00:00
|
|
|
|
2019-10-13 23:17:15 +00:00
|
|
|
cfg_has_statx! {
|
2019-10-04 08:17:23 +00:00
|
|
|
if let Some(ret) = unsafe { try_statx(
|
|
|
|
libc::AT_FDCWD,
|
|
|
|
p.as_ptr(),
|
|
|
|
libc::AT_STATX_SYNC_AS_STAT,
|
|
|
|
libc::STATX_ALL,
|
|
|
|
) } {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
std: Deprecate all std::os::*::raw types
This commit is an implementation of [RFC 1415][rfc] which deprecates all types
in the `std::os::*::raw` modules.
[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1415-trim-std-os.md
Many of the types in these modules don't actually have a canonical platform
representation, for example the definition of `stat` on 32-bit Linux will change
depending on whether C code is compiled with LFS support or not. Unfortunately
the current types in `std::os::*::raw` are billed as "compatible with C", which
in light of this means it isn't really possible.
To make matters worse, platforms like Android sometimes define these types as
*smaller* than the way they're actually represented in the `stat` structure
itself. This means that when methods like `DirEntry::ino` are called on Android
the result may be truncated as we're tied to returning a `ino_t` type, not the
underlying type.
The commit here incorporates two backwards-compatible components:
* Deprecate all `raw` types that aren't in `std::os::raw`
* Expand the `std::os::*::fs::MetadataExt` trait on all platforms for method
accessors of all fields. The fields now returned widened types which are the
same across platforms (consistency across platforms is not required, however,
it's just convenient).
and two also backwards-incompatible components:
* Change the definition of all `std::os::*::raw` type aliases to
correspond to the newly widened types that are being returned on each
platform.
* Change the definition of `std::os::*::raw::stat` on Linux to match the LFS
definitions rather than the standard ones.
The breaking changes here will specifically break code that assumes that `libc`
and `std` agree on the definition of `std::os::*::raw` types, or that the `std`
types are faithful representations of the types in C. An [audit] has been
performed of crates.io to determine the fallout which was determined two be
minimal, with the two found cases of breakage having been fixed now.
[audit]: https://github.com/rust-lang/rfcs/pull/1415#issuecomment-180645582
---
Ok, so after all that, we're finally able to support LFS on Linux! This commit
then simultaneously starts using `stat64` and friends on Linux to ensure that we
can open >4GB files on 32-bit Linux. Yay!
Closes #28978
Closes #30050
Closes #31549
2016-02-05 01:16:47 +00:00
|
|
|
let mut stat: stat64 = unsafe { mem::zeroed() };
|
2019-12-22 22:42:04 +00:00
|
|
|
cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?;
|
2019-10-04 08:17:23 +00:00
|
|
|
Ok(FileAttr::from_stat64(stat))
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn lstat(p: &Path) -> io::Result<FileAttr> {
|
2016-03-23 03:01:37 +00:00
|
|
|
let p = cstr(p)?;
|
2019-10-04 08:17:23 +00:00
|
|
|
|
2019-10-13 23:17:15 +00:00
|
|
|
cfg_has_statx! {
|
2019-10-04 08:17:23 +00:00
|
|
|
if let Some(ret) = unsafe { try_statx(
|
|
|
|
libc::AT_FDCWD,
|
|
|
|
p.as_ptr(),
|
|
|
|
libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
|
|
|
|
libc::STATX_ALL,
|
|
|
|
) } {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
std: Deprecate all std::os::*::raw types
This commit is an implementation of [RFC 1415][rfc] which deprecates all types
in the `std::os::*::raw` modules.
[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1415-trim-std-os.md
Many of the types in these modules don't actually have a canonical platform
representation, for example the definition of `stat` on 32-bit Linux will change
depending on whether C code is compiled with LFS support or not. Unfortunately
the current types in `std::os::*::raw` are billed as "compatible with C", which
in light of this means it isn't really possible.
To make matters worse, platforms like Android sometimes define these types as
*smaller* than the way they're actually represented in the `stat` structure
itself. This means that when methods like `DirEntry::ino` are called on Android
the result may be truncated as we're tied to returning a `ino_t` type, not the
underlying type.
The commit here incorporates two backwards-compatible components:
* Deprecate all `raw` types that aren't in `std::os::raw`
* Expand the `std::os::*::fs::MetadataExt` trait on all platforms for method
accessors of all fields. The fields now returned widened types which are the
same across platforms (consistency across platforms is not required, however,
it's just convenient).
and two also backwards-incompatible components:
* Change the definition of all `std::os::*::raw` type aliases to
correspond to the newly widened types that are being returned on each
platform.
* Change the definition of `std::os::*::raw::stat` on Linux to match the LFS
definitions rather than the standard ones.
The breaking changes here will specifically break code that assumes that `libc`
and `std` agree on the definition of `std::os::*::raw` types, or that the `std`
types are faithful representations of the types in C. An [audit] has been
performed of crates.io to determine the fallout which was determined two be
minimal, with the two found cases of breakage having been fixed now.
[audit]: https://github.com/rust-lang/rfcs/pull/1415#issuecomment-180645582
---
Ok, so after all that, we're finally able to support LFS on Linux! This commit
then simultaneously starts using `stat64` and friends on Linux to ensure that we
can open >4GB files on 32-bit Linux. Yay!
Closes #28978
Closes #30050
Closes #31549
2016-02-05 01:16:47 +00:00
|
|
|
let mut stat: stat64 = unsafe { mem::zeroed() };
|
2019-12-22 22:42:04 +00:00
|
|
|
cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?;
|
2019-10-04 08:17:23 +00:00
|
|
|
Ok(FileAttr::from_stat64(stat))
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
2015-04-16 06:21:13 +00:00
|
|
|
pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
|
2016-03-23 03:01:37 +00:00
|
|
|
let path = CString::new(p.as_os_str().as_bytes())?;
|
Reduce the reliance on `PATH_MAX`
- Rewrite `std::sys::fs::readlink` not to rely on `PATH_MAX`
It currently has the following problems:
1. It uses `_PC_NAME_MAX` to query the maximum length of a file path in
the underlying system. However, the meaning of the constant is the
maximum length of *a path component*, not a full path. The correct
constant should be `_PC_PATH_MAX`.
2. `pathconf` *may* fail if the referred file does not exist. This can
be problematic if the file which the symbolic link points to does not
exist, but the link itself does exist. In this case, the current
implementation resorts to the hard-coded value of `1024`, which is not
ideal.
3. There may exist a platform where there is no limit on file path
lengths in general. That's the reaon why GNU Hurd doesn't define
`PATH_MAX` at all, in addition to having `pathconf` always returning
`-1`. In these platforms, the content of the symbolic link can be
silently truncated if the length exceeds the hard-coded limit mentioned
above.
4. The value obtained by `pathconf` may be outdated at the point of
actually calling `readlink`. This is inherently racy.
This commit introduces a loop that gradually increases the length of the
buffer passed to `readlink`, eliminating the need of `pathconf`.
- Remove the arbitrary memory limit of `std::sys::fs::realpath`
As per POSIX 2013, `realpath` will return a malloc'ed buffer if the
second argument is a null pointer.[1]
[1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html
- Comment on functions that are still using `PATH_MAX`
There are some functions that only work in terms of `PATH_MAX`, such as
`F_GETPATH` in OS X. Comments on them for posterity.
2015-08-19 04:11:40 +00:00
|
|
|
let buf;
|
2015-04-16 06:21:13 +00:00
|
|
|
unsafe {
|
2015-11-03 00:23:22 +00:00
|
|
|
let r = libc::realpath(path.as_ptr(), ptr::null_mut());
|
2015-04-16 06:21:13 +00:00
|
|
|
if r.is_null() {
|
2019-12-22 22:42:04 +00:00
|
|
|
return Err(io::Error::last_os_error());
|
2015-04-16 06:21:13 +00:00
|
|
|
}
|
Reduce the reliance on `PATH_MAX`
- Rewrite `std::sys::fs::readlink` not to rely on `PATH_MAX`
It currently has the following problems:
1. It uses `_PC_NAME_MAX` to query the maximum length of a file path in
the underlying system. However, the meaning of the constant is the
maximum length of *a path component*, not a full path. The correct
constant should be `_PC_PATH_MAX`.
2. `pathconf` *may* fail if the referred file does not exist. This can
be problematic if the file which the symbolic link points to does not
exist, but the link itself does exist. In this case, the current
implementation resorts to the hard-coded value of `1024`, which is not
ideal.
3. There may exist a platform where there is no limit on file path
lengths in general. That's the reaon why GNU Hurd doesn't define
`PATH_MAX` at all, in addition to having `pathconf` always returning
`-1`. In these platforms, the content of the symbolic link can be
silently truncated if the length exceeds the hard-coded limit mentioned
above.
4. The value obtained by `pathconf` may be outdated at the point of
actually calling `readlink`. This is inherently racy.
This commit introduces a loop that gradually increases the length of the
buffer passed to `readlink`, eliminating the need of `pathconf`.
- Remove the arbitrary memory limit of `std::sys::fs::realpath`
As per POSIX 2013, `realpath` will return a malloc'ed buffer if the
second argument is a null pointer.[1]
[1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html
- Comment on functions that are still using `PATH_MAX`
There are some functions that only work in terms of `PATH_MAX`, such as
`F_GETPATH` in OS X. Comments on them for posterity.
2015-08-19 04:11:40 +00:00
|
|
|
buf = CStr::from_ptr(r).to_bytes().to_vec();
|
|
|
|
libc::free(r as *mut _);
|
2015-04-16 06:21:13 +00:00
|
|
|
}
|
|
|
|
Ok(PathBuf::from(OsString::from_vec(buf)))
|
|
|
|
}
|
2015-07-10 08:54:00 +00:00
|
|
|
|
2019-04-11 15:02:13 +00:00
|
|
|
fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
|
|
|
|
use crate::fs::File;
|
|
|
|
|
|
|
|
let reader = File::open(from)?;
|
|
|
|
let metadata = reader.metadata()?;
|
|
|
|
if !metadata.is_file() {
|
|
|
|
return Err(Error::new(
|
|
|
|
ErrorKind::InvalidInput,
|
|
|
|
"the source path is not an existing regular file",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
Ok((reader, metadata))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn open_to_and_set_permissions(
|
2019-02-28 09:25:54 +00:00
|
|
|
to: &Path,
|
2019-04-11 15:02:13 +00:00
|
|
|
reader_metadata: crate::fs::Metadata,
|
|
|
|
) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
|
|
|
|
use crate::fs::OpenOptions;
|
2019-02-28 09:25:54 +00:00
|
|
|
use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt};
|
|
|
|
|
2019-04-11 15:02:13 +00:00
|
|
|
let perm = reader_metadata.permissions();
|
2019-02-28 09:25:54 +00:00
|
|
|
let writer = OpenOptions::new()
|
|
|
|
// create the file with the correct mode right away
|
|
|
|
.mode(perm.mode())
|
|
|
|
.write(true)
|
|
|
|
.create(true)
|
|
|
|
.truncate(true)
|
|
|
|
.open(to)?;
|
|
|
|
let writer_metadata = writer.metadata()?;
|
|
|
|
if writer_metadata.is_file() {
|
|
|
|
// Set the correct file permissions, in case the file already existed.
|
|
|
|
// Don't set the permissions on already existing non-files like
|
|
|
|
// pipes/FIFOs or device nodes.
|
|
|
|
writer.set_permissions(perm)?;
|
|
|
|
}
|
2019-04-11 15:02:13 +00:00
|
|
|
Ok((writer, writer_metadata))
|
2019-02-28 09:25:54 +00:00
|
|
|
}
|
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
#[cfg(not(any(
|
|
|
|
target_os = "linux",
|
|
|
|
target_os = "android",
|
|
|
|
target_os = "macos",
|
|
|
|
target_os = "ios"
|
|
|
|
)))]
|
2015-07-10 08:54:00 +00:00
|
|
|
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
2019-04-11 15:02:13 +00:00
|
|
|
let (mut reader, reader_metadata) = open_from(from)?;
|
|
|
|
let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
|
2015-07-10 08:54:00 +00:00
|
|
|
|
2019-02-28 09:25:54 +00:00
|
|
|
io::copy(&mut reader, &mut writer)
|
2015-07-10 08:54:00 +00:00
|
|
|
}
|
2018-05-15 13:25:09 +00:00
|
|
|
|
2018-05-16 08:17:06 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2018-05-15 13:25:09 +00:00
|
|
|
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
2020-07-28 22:35:01 +00:00
|
|
|
let (mut reader, reader_metadata) = open_from(from)?;
|
|
|
|
let max_len = u64::MAX;
|
|
|
|
let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
|
|
|
|
|
2020-09-10 20:12:42 +00:00
|
|
|
return match copy_regular_files(reader.as_raw_fd(), writer.as_raw_fd(), max_len) {
|
|
|
|
CopyResult::Ended(result) => result,
|
|
|
|
CopyResult::Fallback(written) => {
|
|
|
|
// fallback is only > 0 on EOVERFLOW, which shouldn't happen
|
|
|
|
// because the copy loop starts at a file offset 0 and countns down from `len`
|
|
|
|
assert_eq!(0, written);
|
|
|
|
io::copy::generic_copy(&mut reader, &mut writer)
|
|
|
|
}
|
|
|
|
};
|
2020-07-28 22:35:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// linux-specific implementation that will attempt to use copy_file_range for copy offloading
|
|
|
|
/// as the name says, it only works on regular files
|
2020-09-10 20:12:42 +00:00
|
|
|
///
|
|
|
|
/// Callers must handle fallback to a generic copy loop.
|
|
|
|
/// `Fallback` may indicate non-zero number of bytes already written
|
|
|
|
/// if one of the files' cursor +`max_len` would exceed u64::MAX (`EOVERFLOW`).
|
|
|
|
/// If the initial file offset was 0 then `Fallback` will only contain `0`.
|
2020-07-28 22:35:01 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2020-09-10 20:12:42 +00:00
|
|
|
pub(crate) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> CopyResult {
|
2019-02-10 19:23:21 +00:00
|
|
|
use crate::cmp;
|
|
|
|
use crate::sync::atomic::{AtomicBool, Ordering};
|
2018-05-17 12:10:14 +00:00
|
|
|
|
|
|
|
// Kernel prior to 4.5 don't have copy_file_range
|
2018-08-19 13:30:23 +00:00
|
|
|
// We store the availability in a global to avoid unnecessary syscalls
|
2018-05-17 12:10:14 +00:00
|
|
|
static HAS_COPY_FILE_RANGE: AtomicBool = AtomicBool::new(true);
|
2018-05-15 13:25:09 +00:00
|
|
|
|
|
|
|
unsafe fn copy_file_range(
|
|
|
|
fd_in: libc::c_int,
|
|
|
|
off_in: *mut libc::loff_t,
|
|
|
|
fd_out: libc::c_int,
|
|
|
|
off_out: *mut libc::loff_t,
|
|
|
|
len: libc::size_t,
|
|
|
|
flags: libc::c_uint,
|
|
|
|
) -> libc::c_long {
|
2019-12-22 22:42:04 +00:00
|
|
|
libc::syscall(libc::SYS_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags)
|
2018-05-15 13:25:09 +00:00
|
|
|
}
|
|
|
|
|
2018-05-24 12:51:59 +00:00
|
|
|
let has_copy_file_range = HAS_COPY_FILE_RANGE.load(Ordering::Relaxed);
|
2018-05-15 13:25:09 +00:00
|
|
|
let mut written = 0u64;
|
2020-08-14 20:39:04 +00:00
|
|
|
while written < max_len {
|
2018-05-24 12:51:59 +00:00
|
|
|
let copy_result = if has_copy_file_range {
|
2020-09-10 20:12:42 +00:00
|
|
|
let bytes_to_copy = cmp::min(max_len - written, usize::MAX as u64);
|
|
|
|
// cap to 2GB chunks in case u64::MAX is passed in as file size and the file has a non-zero offset
|
|
|
|
// this allows us to copy large chunks without hitting the limit,
|
|
|
|
// unless someone sets a file offset close to u64::MAX - 2GB, in which case the fallback would kick in
|
|
|
|
let bytes_to_copy = cmp::min(bytes_to_copy as usize, 0x8000_0000usize);
|
2018-05-17 12:10:14 +00:00
|
|
|
let copy_result = unsafe {
|
|
|
|
// We actually don't have to adjust the offsets,
|
|
|
|
// because copy_file_range adjusts the file offset automatically
|
2019-02-28 09:25:54 +00:00
|
|
|
cvt(copy_file_range(
|
2020-09-10 20:12:42 +00:00
|
|
|
reader,
|
2019-02-28 09:25:54 +00:00
|
|
|
ptr::null_mut(),
|
2020-09-10 20:12:42 +00:00
|
|
|
writer,
|
2019-02-28 09:25:54 +00:00
|
|
|
ptr::null_mut(),
|
|
|
|
bytes_to_copy,
|
|
|
|
0,
|
|
|
|
))
|
2018-05-17 12:10:14 +00:00
|
|
|
};
|
|
|
|
if let Err(ref copy_err) = copy_result {
|
2018-06-01 07:32:20 +00:00
|
|
|
match copy_err.raw_os_error() {
|
2020-08-12 18:09:55 +00:00
|
|
|
Some(libc::ENOSYS | libc::EPERM | libc::EOPNOTSUPP) => {
|
2018-06-01 07:32:20 +00:00
|
|
|
HAS_COPY_FILE_RANGE.store(false, Ordering::Relaxed);
|
|
|
|
}
|
|
|
|
_ => {}
|
2018-05-17 12:10:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
copy_result
|
|
|
|
} else {
|
|
|
|
Err(io::Error::from_raw_os_error(libc::ENOSYS))
|
2018-05-15 13:25:09 +00:00
|
|
|
};
|
|
|
|
match copy_result {
|
2020-08-14 20:39:04 +00:00
|
|
|
Ok(0) if written == 0 => {
|
|
|
|
// fallback to work around several kernel bugs where copy_file_range will fail to
|
|
|
|
// copy any bytes and return 0 instead of an error if
|
|
|
|
// - reading virtual files from the proc filesystem which appear to have 0 size
|
|
|
|
// but are not empty. noted in coreutils to affect kernels at least up to 5.6.19.
|
|
|
|
// - copying from an overlay filesystem in docker. reported to occur on fedora 32.
|
2020-09-10 20:12:42 +00:00
|
|
|
return CopyResult::Fallback(0);
|
2020-08-14 20:39:04 +00:00
|
|
|
}
|
2020-09-10 20:12:42 +00:00
|
|
|
Ok(0) => return CopyResult::Ended(Ok(written)), // reached EOF
|
2018-05-15 13:25:09 +00:00
|
|
|
Ok(ret) => written += ret as u64,
|
|
|
|
Err(err) => {
|
|
|
|
match err.raw_os_error() {
|
2020-09-10 20:12:42 +00:00
|
|
|
// when file offset + max_length > u64::MAX
|
|
|
|
Some(libc::EOVERFLOW) => return CopyResult::Fallback(written),
|
2020-08-12 18:09:55 +00:00
|
|
|
Some(
|
|
|
|
libc::ENOSYS | libc::EXDEV | libc::EINVAL | libc::EPERM | libc::EOPNOTSUPP,
|
|
|
|
) => {
|
2019-12-22 22:42:04 +00:00
|
|
|
// Try fallback io::copy if either:
|
|
|
|
// - Kernel version is < 4.5 (ENOSYS)
|
|
|
|
// - Files are mounted on different fs (EXDEV)
|
2020-08-11 23:15:08 +00:00
|
|
|
// - copy_file_range is broken in various ways on RHEL/CentOS 7 (EOPNOTSUPP)
|
2019-12-22 22:42:04 +00:00
|
|
|
// - copy_file_range is disallowed, for example by seccomp (EPERM)
|
|
|
|
// - copy_file_range cannot be used with pipes or device nodes (EINVAL)
|
|
|
|
assert_eq!(written, 0);
|
2020-09-10 20:12:42 +00:00
|
|
|
return CopyResult::Fallback(0);
|
2020-07-28 22:35:01 +00:00
|
|
|
}
|
2020-09-10 20:12:42 +00:00
|
|
|
_ => return CopyResult::Ended(Err(err)),
|
2020-07-28 22:35:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-10 20:12:42 +00:00
|
|
|
CopyResult::Ended(Ok(written))
|
2020-07-28 22:35:01 +00:00
|
|
|
}
|
|
|
|
|
2020-09-10 20:12:42 +00:00
|
|
|
#[derive(PartialEq)]
|
2020-07-28 22:35:01 +00:00
|
|
|
pub(crate) enum SpliceMode {
|
|
|
|
Sendfile,
|
|
|
|
Splice,
|
|
|
|
}
|
|
|
|
|
2020-09-10 20:12:42 +00:00
|
|
|
pub(crate) enum CopyResult {
|
|
|
|
Ended(io::Result<u64>),
|
|
|
|
Fallback(u64),
|
|
|
|
}
|
|
|
|
|
2020-07-28 22:35:01 +00:00
|
|
|
/// performs splice or sendfile between file descriptors
|
2020-09-10 20:12:42 +00:00
|
|
|
/// Does _not_ fall back to a generic copy loop.
|
2020-07-28 22:35:01 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
|
|
pub(crate) fn sendfile_splice(
|
|
|
|
mode: SpliceMode,
|
2020-09-10 20:12:42 +00:00
|
|
|
reader: RawFd,
|
|
|
|
writer: RawFd,
|
2020-07-28 22:35:01 +00:00
|
|
|
len: u64,
|
2020-09-10 20:12:42 +00:00
|
|
|
) -> CopyResult {
|
2020-07-28 22:35:01 +00:00
|
|
|
let mut written = 0u64;
|
|
|
|
while written < len {
|
|
|
|
let chunk_size = crate::cmp::min(len - written, 0x7ffff000_u64) as usize;
|
|
|
|
|
|
|
|
let result = match mode {
|
2020-09-10 20:12:42 +00:00
|
|
|
SpliceMode::Sendfile => {
|
|
|
|
cvt(unsafe { libc::sendfile(writer, reader, ptr::null_mut(), chunk_size) })
|
|
|
|
}
|
2020-07-28 22:35:01 +00:00
|
|
|
SpliceMode::Splice => cvt(unsafe {
|
|
|
|
libc::splice(
|
2020-09-10 20:12:42 +00:00
|
|
|
reader,
|
2020-07-28 22:35:01 +00:00
|
|
|
ptr::null_mut(),
|
2020-09-10 20:12:42 +00:00
|
|
|
writer,
|
2020-07-28 22:35:01 +00:00
|
|
|
ptr::null_mut(),
|
2020-09-10 20:12:42 +00:00
|
|
|
// default pipe size is 64KiB. try to only fill/drain half of that capacity
|
|
|
|
// so that the next loop iteration won't be put to sleep.
|
|
|
|
// If reader and writer operate at the same pace they will experience fewer blocking waits.
|
|
|
|
// This is only needed for splice since sendfile stays in kernel space when it has to block.
|
|
|
|
//crate::cmp::min(32*1024, chunk_size),
|
2020-07-28 22:35:01 +00:00
|
|
|
chunk_size,
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
}),
|
|
|
|
};
|
|
|
|
|
|
|
|
match result {
|
|
|
|
Ok(0) => break, // EOF
|
|
|
|
Ok(ret) => written += ret as u64,
|
|
|
|
Err(err) => {
|
|
|
|
match err.raw_os_error() {
|
|
|
|
Some(os_err) if os_err == libc::EINVAL => {
|
2020-09-10 20:12:42 +00:00
|
|
|
// splice/sendfile do not support this particular file descritor (EINVAL)
|
2020-07-28 22:35:01 +00:00
|
|
|
assert_eq!(written, 0);
|
2020-09-10 20:12:42 +00:00
|
|
|
return CopyResult::Fallback(0);
|
|
|
|
}
|
|
|
|
Some(os_err) if mode == SpliceMode::Sendfile && os_err == libc::EOVERFLOW => {
|
|
|
|
return CopyResult::Fallback(written);
|
2019-12-22 22:42:04 +00:00
|
|
|
}
|
2020-09-10 20:12:42 +00:00
|
|
|
_ => return CopyResult::Ended(Err(err)),
|
2018-05-15 13:25:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-09-10 20:12:42 +00:00
|
|
|
CopyResult::Ended(Ok(written))
|
2018-05-15 13:25:09 +00:00
|
|
|
}
|
2019-03-03 19:47:17 +00:00
|
|
|
|
|
|
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
|
|
|
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
2019-04-11 15:02:13 +00:00
|
|
|
use crate::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
|
2019-03-03 19:47:17 +00:00
|
|
|
const COPYFILE_ACL: u32 = 1 << 0;
|
|
|
|
const COPYFILE_STAT: u32 = 1 << 1;
|
|
|
|
const COPYFILE_XATTR: u32 = 1 << 2;
|
|
|
|
const COPYFILE_DATA: u32 = 1 << 3;
|
|
|
|
|
|
|
|
const COPYFILE_SECURITY: u32 = COPYFILE_STAT | COPYFILE_ACL;
|
|
|
|
const COPYFILE_METADATA: u32 = COPYFILE_SECURITY | COPYFILE_XATTR;
|
|
|
|
const COPYFILE_ALL: u32 = COPYFILE_METADATA | COPYFILE_DATA;
|
|
|
|
|
|
|
|
const COPYFILE_STATE_COPIED: u32 = 8;
|
|
|
|
|
|
|
|
#[allow(non_camel_case_types)]
|
|
|
|
type copyfile_state_t = *mut libc::c_void;
|
|
|
|
#[allow(non_camel_case_types)]
|
|
|
|
type copyfile_flags_t = u32;
|
|
|
|
|
|
|
|
extern "C" {
|
2019-02-28 09:25:54 +00:00
|
|
|
fn fcopyfile(
|
|
|
|
from: libc::c_int,
|
|
|
|
to: libc::c_int,
|
2019-03-03 19:47:17 +00:00
|
|
|
state: copyfile_state_t,
|
|
|
|
flags: copyfile_flags_t,
|
|
|
|
) -> libc::c_int;
|
|
|
|
fn copyfile_state_alloc() -> copyfile_state_t;
|
|
|
|
fn copyfile_state_free(state: copyfile_state_t) -> libc::c_int;
|
|
|
|
fn copyfile_state_get(
|
|
|
|
state: copyfile_state_t,
|
|
|
|
flag: u32,
|
|
|
|
dst: *mut libc::c_void,
|
|
|
|
) -> libc::c_int;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct FreeOnDrop(copyfile_state_t);
|
|
|
|
impl Drop for FreeOnDrop {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
// The code below ensures that `FreeOnDrop` is never a null pointer
|
|
|
|
unsafe {
|
|
|
|
// `copyfile_state_free` returns -1 if the `to` or `from` files
|
2019-11-27 03:19:54 +00:00
|
|
|
// cannot be closed. However, this is not considered this an
|
2019-03-03 19:47:17 +00:00
|
|
|
// error.
|
|
|
|
copyfile_state_free(self.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-11 15:02:13 +00:00
|
|
|
// MacOS prior to 10.12 don't support `fclonefileat`
|
|
|
|
// We store the availability in a global to avoid unnecessary syscalls
|
|
|
|
static HAS_FCLONEFILEAT: AtomicBool = AtomicBool::new(true);
|
|
|
|
syscall! {
|
|
|
|
fn fclonefileat(
|
|
|
|
srcfd: libc::c_int,
|
|
|
|
dst_dirfd: libc::c_int,
|
|
|
|
dst: *const libc::c_char,
|
|
|
|
flags: libc::c_int
|
|
|
|
) -> libc::c_int
|
|
|
|
}
|
|
|
|
|
|
|
|
let (reader, reader_metadata) = open_from(from)?;
|
|
|
|
|
|
|
|
// Opportunistically attempt to create a copy-on-write clone of `from`
|
|
|
|
// using `fclonefileat`.
|
|
|
|
if HAS_FCLONEFILEAT.load(Ordering::Relaxed) {
|
|
|
|
let to = cstr(to)?;
|
2019-12-22 22:42:04 +00:00
|
|
|
let clonefile_result =
|
|
|
|
cvt(unsafe { fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) });
|
2019-04-11 15:02:13 +00:00
|
|
|
match clonefile_result {
|
|
|
|
Ok(_) => return Ok(reader_metadata.len()),
|
|
|
|
Err(err) => match err.raw_os_error() {
|
|
|
|
// `fclonefileat` will fail on non-APFS volumes, if the
|
|
|
|
// destination already exists, or if the source and destination
|
|
|
|
// are on different devices. In all these cases `fcopyfile`
|
|
|
|
// should succeed.
|
|
|
|
Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (),
|
|
|
|
Some(libc::ENOSYS) => HAS_FCLONEFILEAT.store(false, Ordering::Relaxed),
|
|
|
|
_ => return Err(err),
|
2019-12-22 22:42:04 +00:00
|
|
|
},
|
2019-04-11 15:02:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fall back to using `fcopyfile` if `fclonefileat` does not succeed.
|
|
|
|
let (writer, writer_metadata) = open_to_and_set_permissions(to, reader_metadata)?;
|
2019-03-03 19:47:17 +00:00
|
|
|
|
|
|
|
// We ensure that `FreeOnDrop` never contains a null pointer so it is
|
|
|
|
// always safe to call `copyfile_state_free`
|
|
|
|
let state = unsafe {
|
|
|
|
let state = copyfile_state_alloc();
|
|
|
|
if state.is_null() {
|
|
|
|
return Err(crate::io::Error::last_os_error());
|
|
|
|
}
|
|
|
|
FreeOnDrop(state)
|
|
|
|
};
|
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { COPYFILE_DATA };
|
2019-02-28 09:25:54 +00:00
|
|
|
|
2019-12-22 22:42:04 +00:00
|
|
|
cvt(unsafe { fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?;
|
2019-03-03 19:47:17 +00:00
|
|
|
|
|
|
|
let mut bytes_copied: libc::off_t = 0;
|
|
|
|
cvt(unsafe {
|
|
|
|
copyfile_state_get(
|
|
|
|
state.0,
|
|
|
|
COPYFILE_STATE_COPIED,
|
|
|
|
&mut bytes_copied as *mut libc::off_t as *mut libc::c_void,
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
Ok(bytes_copied as u64)
|
|
|
|
}
|