Auto merge of #31069 - sfackler:file-try-clone, r=alexcrichton
I have it set as stable right now under the rationale that it's extending an existing, stable API to another type in the "obvious" way. r? @alexcrichton cc @reem
This commit is contained in:
commit
7b9d6d3bc8
5 changed files with 87 additions and 39 deletions
|
@ -309,6 +309,18 @@ impl File {
|
||||||
pub fn metadata(&self) -> io::Result<Metadata> {
|
pub fn metadata(&self) -> io::Result<Metadata> {
|
||||||
self.inner.file_attr().map(Metadata)
|
self.inner.file_attr().map(Metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new independently owned handle to the underlying file.
|
||||||
|
///
|
||||||
|
/// The returned `File` is a reference to the same state that this object
|
||||||
|
/// references. Both handles will read and write with the same cursor
|
||||||
|
/// position.
|
||||||
|
#[unstable(feature = "file_try_clone", reason = "newly added", issue = "31405")]
|
||||||
|
pub fn try_clone(&self) -> io::Result<File> {
|
||||||
|
Ok(File {
|
||||||
|
inner: try!(self.inner.duplicate())
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsInner<fs_imp::File> for File {
|
impl AsInner<fs_imp::File> for File {
|
||||||
|
@ -2283,6 +2295,28 @@ mod tests {
|
||||||
assert!(v == &bytes[..]);
|
assert!(v == &bytes[..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn file_try_clone() {
|
||||||
|
let tmpdir = tmpdir();
|
||||||
|
|
||||||
|
let mut f1 = check!(OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&tmpdir.join("test")));
|
||||||
|
let mut f2 = check!(f1.try_clone());
|
||||||
|
|
||||||
|
check!(f1.write_all(b"hello world"));
|
||||||
|
check!(f1.seek(SeekFrom::Start(2)));
|
||||||
|
|
||||||
|
let mut buf = vec![];
|
||||||
|
check!(f2.read_to_end(&mut buf));
|
||||||
|
assert_eq!(buf, b"llo world");
|
||||||
|
drop(f2);
|
||||||
|
|
||||||
|
check!(f1.write_all(b"!"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
fn unlink_readonly() {
|
fn unlink_readonly() {
|
||||||
|
|
|
@ -13,6 +13,7 @@ use libc::{self, c_int, size_t, c_void};
|
||||||
use mem;
|
use mem;
|
||||||
use sys::cvt;
|
use sys::cvt;
|
||||||
use sys_common::AsInner;
|
use sys_common::AsInner;
|
||||||
|
use sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
pub struct FileDesc {
|
pub struct FileDesc {
|
||||||
fd: c_int,
|
fd: c_int,
|
||||||
|
@ -65,6 +66,47 @@ impl FileDesc {
|
||||||
debug_assert_eq!(ret, 0);
|
debug_assert_eq!(ret, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn duplicate(&self) -> io::Result<FileDesc> {
|
||||||
|
// We want to atomically duplicate this file descriptor and set the
|
||||||
|
// CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
|
||||||
|
// flag, however, isn't supported on older Linux kernels (earlier than
|
||||||
|
// 2.6.24).
|
||||||
|
//
|
||||||
|
// To detect this and ensure that CLOEXEC is still set, we
|
||||||
|
// follow a strategy similar to musl [1] where if passing
|
||||||
|
// F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not
|
||||||
|
// supported (the third parameter, 0, is always valid), so we stop
|
||||||
|
// trying that. We also *still* call the `set_cloexec` method as
|
||||||
|
// apparently some kernel at some point stopped setting CLOEXEC even
|
||||||
|
// though it reported doing so on F_DUPFD_CLOEXEC.
|
||||||
|
//
|
||||||
|
// Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to
|
||||||
|
// resolve so we at least compile this.
|
||||||
|
//
|
||||||
|
// [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
use libc::F_DUPFD as F_DUPFD_CLOEXEC;
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
|
use libc::F_DUPFD_CLOEXEC;
|
||||||
|
|
||||||
|
let make_filedesc = |fd| {
|
||||||
|
let fd = FileDesc::new(fd);
|
||||||
|
fd.set_cloexec();
|
||||||
|
fd
|
||||||
|
};
|
||||||
|
static TRY_CLOEXEC: AtomicBool = AtomicBool::new(true);
|
||||||
|
let fd = self.raw();
|
||||||
|
if !cfg!(target_os = "android") && TRY_CLOEXEC.load(Ordering::Relaxed) {
|
||||||
|
match cvt(unsafe { libc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }) {
|
||||||
|
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {
|
||||||
|
TRY_CLOEXEC.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
res => return res.map(make_filedesc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).map(make_filedesc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsInner<c_int> for FileDesc {
|
impl AsInner<c_int> for FileDesc {
|
||||||
|
|
|
@ -414,6 +414,10 @@ impl File {
|
||||||
Ok(n as u64)
|
Ok(n as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn duplicate(&self) -> io::Result<File> {
|
||||||
|
self.0.duplicate().map(File)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn fd(&self) -> &FileDesc { &self.0 }
|
pub fn fd(&self) -> &FileDesc { &self.0 }
|
||||||
|
|
||||||
pub fn into_fd(self) -> FileDesc { self.0 }
|
pub fn into_fd(self) -> FileDesc { self.0 }
|
||||||
|
|
|
@ -15,7 +15,6 @@ use io;
|
||||||
use libc::{self, c_int, size_t};
|
use libc::{self, c_int, size_t};
|
||||||
use net::{SocketAddr, Shutdown};
|
use net::{SocketAddr, Shutdown};
|
||||||
use str;
|
use str;
|
||||||
use sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use sys::fd::FileDesc;
|
use sys::fd::FileDesc;
|
||||||
use sys_common::{AsInner, FromInner, IntoInner};
|
use sys_common::{AsInner, FromInner, IntoInner};
|
||||||
use sys_common::net::{getsockopt, setsockopt};
|
use sys_common::net::{getsockopt, setsockopt};
|
||||||
|
@ -67,44 +66,7 @@ impl Socket {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn duplicate(&self) -> io::Result<Socket> {
|
pub fn duplicate(&self) -> io::Result<Socket> {
|
||||||
// We want to atomically duplicate this file descriptor and set the
|
self.0.duplicate().map(Socket)
|
||||||
// CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
|
|
||||||
// flag, however, isn't supported on older Linux kernels (earlier than
|
|
||||||
// 2.6.24).
|
|
||||||
//
|
|
||||||
// To detect this and ensure that CLOEXEC is still set, we
|
|
||||||
// follow a strategy similar to musl [1] where if passing
|
|
||||||
// F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not
|
|
||||||
// supported (the third parameter, 0, is always valid), so we stop
|
|
||||||
// trying that. We also *still* call the `set_cloexec` method as
|
|
||||||
// apparently some kernel at some point stopped setting CLOEXEC even
|
|
||||||
// though it reported doing so on F_DUPFD_CLOEXEC.
|
|
||||||
//
|
|
||||||
// Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to
|
|
||||||
// resolve so we at least compile this.
|
|
||||||
//
|
|
||||||
// [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963
|
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
use libc::F_DUPFD as F_DUPFD_CLOEXEC;
|
|
||||||
#[cfg(not(target_os = "android"))]
|
|
||||||
use libc::F_DUPFD_CLOEXEC;
|
|
||||||
|
|
||||||
let make_socket = |fd| {
|
|
||||||
let fd = FileDesc::new(fd);
|
|
||||||
fd.set_cloexec();
|
|
||||||
Socket(fd)
|
|
||||||
};
|
|
||||||
static TRY_CLOEXEC: AtomicBool = AtomicBool::new(true);
|
|
||||||
let fd = self.0.raw();
|
|
||||||
if !cfg!(target_os = "android") && TRY_CLOEXEC.load(Ordering::Relaxed) {
|
|
||||||
match cvt(unsafe { libc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }) {
|
|
||||||
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {
|
|
||||||
TRY_CLOEXEC.store(false, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
res => return res.map(make_socket),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).map(make_socket)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
|
|
@ -338,6 +338,12 @@ impl File {
|
||||||
Ok(newpos as u64)
|
Ok(newpos as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn duplicate(&self) -> io::Result<File> {
|
||||||
|
Ok(File {
|
||||||
|
handle: try!(self.handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle(&self) -> &Handle { &self.handle }
|
pub fn handle(&self) -> &Handle { &self.handle }
|
||||||
|
|
||||||
pub fn into_handle(self) -> Handle { self.handle }
|
pub fn into_handle(self) -> Handle { self.handle }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue