From b3f2644b66647d47338051d783cae8e661312472 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Tue, 16 Aug 2016 01:11:33 +0200 Subject: [PATCH 1/7] Implement reading and writing atomically at certain offsets These functions allow to read from and write to a file in one atomic action from multiple threads, avoiding the race between the seek and the read. The functions are named `{read,write}_at` on non-Windows (which don't change the file cursor), and `seek_{read,write}` on Windows (which change the file cursor). --- src/libstd/fs.rs | 126 +++++++++++++++++++++++++++++- src/libstd/sys/unix/ext/fs.rs | 45 +++++++++++ src/libstd/sys/unix/ext/mod.rs | 2 + src/libstd/sys/unix/fd.rs | 25 ++++++ src/libstd/sys/unix/fs.rs | 8 ++ src/libstd/sys/windows/ext/fs.rs | 51 +++++++++++- src/libstd/sys/windows/ext/mod.rs | 2 + src/libstd/sys/windows/fs.rs | 8 ++ src/libstd/sys/windows/handle.rs | 30 +++++++ 9 files changed, 295 insertions(+), 2 deletions(-) diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 2f2969b110db1..5bb5183fd6a93 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -1903,6 +1903,130 @@ mod tests { check!(fs::remove_file(filename)); } + #[test] + fn file_test_io_eof() { + let tmpdir = tmpdir(); + let filename = tmpdir.join("file_rt_io_file_test_eof.txt"); + let mut buf = [0; 256]; + { + let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); + let mut rw = check!(oo.open(&filename)); + assert_eq!(check!(rw.read(&mut buf)), 0); + assert_eq!(check!(rw.read(&mut buf)), 0); + } + check!(fs::remove_file(&filename)); + } + + #[test] + #[cfg(unix)] + fn file_test_io_read_write_at() { + use os::unix::fs::FileExt; + + let tmpdir = tmpdir(); + let filename = tmpdir.join("file_rt_io_file_test_read_write_at.txt"); + let mut buf = [0; 256]; + let write1 = "asdf"; + let write2 = "qwer-"; + let write3 = "-zxcv"; + let content = "qwer-asdf-zxcv"; + { + let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); + let mut rw = check!(oo.open(&filename)); + assert_eq!(check!(rw.write_at(write1.as_bytes(), 5)), write1.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); + assert_eq!(check!(rw.read_at(&mut buf, 5)), write1.len()); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); + assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len()); + assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok("\0\0\0\0\0")); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0); + assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); + assert_eq!(check!(rw.read(&mut buf)), write1.len()); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len()); + assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.write_at(write3.as_bytes(), 9)), write3.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + } + { + let mut read = check!(File::open(&filename)); + assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 0); + assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); + assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(read.read(&mut buf)), write3.len()); + assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.read_at(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.read_at(&mut buf, 14)), 0); + assert_eq!(check!(read.read_at(&mut buf, 15)), 0); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + } + check!(fs::remove_file(&filename)); + } + + #[test] + #[cfg(windows)] + fn file_test_io_seek_read_write() { + use os::windows::fs::FileExt; + + let tmpdir = tmpdir(); + let filename = tmpdir.join("file_rt_io_file_test_seek_read_write.txt"); + let mut buf = [0; 256]; + let write1 = "asdf"; + let write2 = "qwer-"; + let write3 = "-zxcv"; + let content = "qwer-asdf-zxcv"; + { + let oo = OpenOptions::new().create_new(true).write(true).read(true).clone(); + let mut rw = check!(oo.open(&filename)); + assert_eq!(check!(rw.seek_write(write1.as_bytes(), 5)), write1.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.seek_read(&mut buf, 5)), write1.len()); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.seek(SeekFrom::Start(0))), 0); + assert_eq!(check!(rw.write(write2.as_bytes())), write2.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); + assert_eq!(check!(rw.read(&mut buf)), write1.len()); + assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9); + assert_eq!(check!(rw.seek_read(&mut buf[..write2.len()], 0)), write2.len()); + assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2)); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5); + assert_eq!(check!(rw.seek_write(write3.as_bytes(), 9)), write3.len()); + assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 14); + } + { + let mut read = check!(File::open(&filename)); + assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); + assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9); + assert_eq!(check!(read.read(&mut buf)), write3.len()); + assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len()); + assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content)); + assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14); + assert_eq!(check!(read.seek_read(&mut buf, 14)), 0); + assert_eq!(check!(read.seek_read(&mut buf, 15)), 0); + } + check!(fs::remove_file(&filename)); + } + #[test] fn file_test_stat_is_correct_on_is_file() { let tmpdir = tmpdir(); @@ -2221,8 +2345,8 @@ mod tests { check!(fs::set_permissions(&out, attr.permissions())); } - #[cfg(windows)] #[test] + #[cfg(windows)] fn copy_file_preserves_streams() { let tmp = tmpdir(); check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes())); diff --git a/src/libstd/sys/unix/ext/fs.rs b/src/libstd/sys/unix/ext/fs.rs index 77587918ac94b..fcfab05158841 100644 --- a/src/libstd/sys/unix/ext/fs.rs +++ b/src/libstd/sys/unix/ext/fs.rs @@ -20,6 +20,51 @@ use sys; use sys_common::{FromInner, AsInner, AsInnerMut}; use sys::platform::fs::MetadataExt as UnixMetadataExt; +/// Unix-specific extensions to `File` +#[unstable(feature = "file_offset", issue = "35918")] +pub trait FileExt { + /// Reads a number of bytes starting from a given offset. + /// + /// Returns the number of bytes read. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// Note that similar to `File::read`, it is not an error to return with a + /// short read. + #[unstable(feature = "file_offset", issue = "35918")] + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result; + + /// Writes a number of bytes starting from a given offset. + /// + /// Returns the number of bytes written. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. + /// + /// The current file cursor is not affected by this function. + /// + /// When writing beyond the end of the file, the file is appropiately + /// extended and the intermediate bytes are initialized with the value 0. + /// + /// Note that similar to `File::write`, it is not an error to return a + /// short write. + #[unstable(feature = "file_offset", issue = "35918")] + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result; +} + +#[unstable(feature = "file_offset", issue = "35918")] +impl FileExt for fs::File { + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + self.as_inner().read_at(buf, offset) + } + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + self.as_inner().write_at(buf, offset) + } +} + /// Unix-specific extensions to `Permissions` #[stable(feature = "fs_ext", since = "1.1.0")] pub trait PermissionsExt { diff --git a/src/libstd/sys/unix/ext/mod.rs b/src/libstd/sys/unix/ext/mod.rs index 1be3d75d866dd..b2483f4e20937 100644 --- a/src/libstd/sys/unix/ext/mod.rs +++ b/src/libstd/sys/unix/ext/mod.rs @@ -50,6 +50,8 @@ pub mod prelude { pub use super::fs::{PermissionsExt, OpenOptionsExt, MetadataExt, FileTypeExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::fs::DirEntryExt; + #[doc(no_inline)] #[unstable(feature = "file_offset", issue = "35918")] + pub use super::fs::FileExt; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::thread::JoinHandleExt; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index eadf98867a6b7..4f92c3274d2e6 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -18,6 +18,11 @@ use sys::cvt; use sys_common::AsInner; use sys_common::io::read_to_end_uninitialized; +#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] +use libc::{pread64, pwrite64, off64_t}; +#[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))] +use libc::{pread as pread64, pwrite as pwrite64, off_t as off64_t}; + pub struct FileDesc { fd: c_int, } @@ -50,6 +55,16 @@ impl FileDesc { (&mut me).read_to_end(buf) } + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + let ret = cvt(unsafe { + pread64(self.fd, + buf.as_mut_ptr() as *mut c_void, + buf.len(), + offset as off64_t) + })?; + Ok(ret as usize) + } + pub fn write(&self, buf: &[u8]) -> io::Result { let ret = cvt(unsafe { libc::write(self.fd, @@ -59,6 +74,16 @@ impl FileDesc { Ok(ret as usize) } + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + let ret = cvt(unsafe { + pwrite64(self.fd, + buf.as_ptr() as *const c_void, + buf.len(), + offset as off64_t) + })?; + Ok(ret as usize) + } + #[cfg(not(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten", diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index 606e2c2264ad0..fe8cbc8421572 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -483,10 +483,18 @@ impl File { self.0.read_to_end(buf) } + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + self.0.read_at(buf, offset) + } + pub fn write(&self, buf: &[u8]) -> io::Result { self.0.write(buf) } + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + self.0.write_at(buf, offset) + } + pub fn flush(&self) -> io::Result<()> { Ok(()) } pub fn seek(&self, pos: SeekFrom) -> io::Result { diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs index 4388a0bdff2cf..1e2b8bf38fa71 100644 --- a/src/libstd/sys/windows/ext/fs.rs +++ b/src/libstd/sys/windows/ext/fs.rs @@ -12,12 +12,61 @@ #![stable(feature = "rust1", since = "1.0.0")] -use fs::{OpenOptions, Metadata}; +use fs::{self, OpenOptions, Metadata}; use io; use path::Path; use sys; use sys_common::{AsInnerMut, AsInner}; +/// Windows-specific extensions to `File` +#[unstable(feature = "file_offset", issue = "35918")] +pub trait FileExt { + /// Seeks to a given position and reads a number of bytes. + /// + /// Returns the number of bytes read. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. The current cursor **is** affected by this + /// function, it is set to the end of the read. + /// + /// Reading beyond the end of the file will always return with a length of + /// 0. + /// + /// Note that similar to `File::read`, it is not an error to return with a + /// short read. When returning from such a short read, the file pointer is + /// still updated. + #[unstable(feature = "file_offset", issue = "35918")] + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result; + + /// Seeks to a given position and writes a number of bytes. + /// + /// Returns the number of bytes written. + /// + /// The offset is relative to the start of the file and thus independent + /// from the current cursor. The current cursor **is** affected by this + /// function, it is set to the end of the write. + /// + /// When writing beyond the end of the file, the file is appropiately + /// extended and the intermediate bytes are left uninitialized. + /// + /// Note that similar to `File::write`, it is not an error to return a + /// short write. When returning from such a short write, the file pointer + /// is still updated. + #[unstable(feature = "file_offset", issue = "35918")] + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result; +} + +#[unstable(feature = "file_offset", issue = "35918")] +impl FileExt for fs::File { + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { + self.as_inner().read_at(buf, offset) + } + + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { + self.as_inner().write_at(buf, offset) + } +} + /// Windows-specific extensions to `OpenOptions` #[stable(feature = "open_options_ext", since = "1.10.0")] pub trait OpenOptionsExt { diff --git a/src/libstd/sys/windows/ext/mod.rs b/src/libstd/sys/windows/ext/mod.rs index c3578fdfdb16b..932bb5e956405 100644 --- a/src/libstd/sys/windows/ext/mod.rs +++ b/src/libstd/sys/windows/ext/mod.rs @@ -36,4 +36,6 @@ pub mod prelude { pub use super::ffi::{OsStrExt, OsStringExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::fs::{OpenOptionsExt, MetadataExt}; + #[doc(no_inline)] #[unstable(feature = "file_offset", issue = "35918")] + pub use super::fs::FileExt; } diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 90a16853d56dd..a927eae020dcf 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -311,6 +311,10 @@ impl File { self.handle.read(buf) } + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + self.handle.read_at(buf, offset) + } + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { self.handle.read_to_end(buf) } @@ -319,6 +323,10 @@ impl File { self.handle.write(buf) } + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + self.handle.write_at(buf, offset) + } + pub fn flush(&self) -> io::Result<()> { Ok(()) } pub fn seek(&self, pos: SeekFrom) -> io::Result { diff --git a/src/libstd/sys/windows/handle.rs b/src/libstd/sys/windows/handle.rs index 97e746ee34576..10b86ba44bc79 100644 --- a/src/libstd/sys/windows/handle.rs +++ b/src/libstd/sys/windows/handle.rs @@ -104,6 +104,23 @@ impl RawHandle { } } + pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + let mut read = 0; + let len = cmp::min(buf.len(), ::max_value() as usize) as c::DWORD; + let res = unsafe { + let mut overlapped: c::OVERLAPPED = mem::zeroed(); + overlapped.Offset = offset as u32; + overlapped.OffsetHigh = (offset >> 32) as u32; + cvt(c::ReadFile(self.0, buf.as_mut_ptr() as c::LPVOID, + len, &mut read, &mut overlapped)) + }; + match res { + Ok(_) => Ok(read as usize), + Err(ref e) if e.raw_os_error() == Some(c::ERROR_HANDLE_EOF as i32) => Ok(0), + Err(e) => Err(e), + } + } + pub unsafe fn read_overlapped(&self, buf: &mut [u8], overlapped: *mut c::OVERLAPPED) @@ -174,6 +191,19 @@ impl RawHandle { Ok(amt as usize) } + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + let mut written = 0; + let len = cmp::min(buf.len(), ::max_value() as usize) as c::DWORD; + unsafe { + let mut overlapped: c::OVERLAPPED = mem::zeroed(); + overlapped.Offset = offset as u32; + overlapped.OffsetHigh = (offset >> 32) as u32; + cvt(c::WriteFile(self.0, buf.as_ptr() as c::LPVOID, + len, &mut written, &mut overlapped))?; + } + Ok(written as usize) + } + pub fn duplicate(&self, access: c::DWORD, inherit: bool, options: c::DWORD) -> io::Result { let mut ret = 0 as c::HANDLE; From f352f0eec010ff2814ca27ac0e5a84cd533f2918 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Sat, 8 Oct 2016 14:32:57 +0200 Subject: [PATCH 2/7] Dynamically detect presence of `p{read,write}64` on Android --- src/libstd/sys/unix/android.rs | 41 ++++++++++++++++++++++++---- src/libstd/sys/unix/fd.rs | 50 ++++++++++++++++++++++------------ 2 files changed, 68 insertions(+), 23 deletions(-) diff --git a/src/libstd/sys/unix/android.rs b/src/libstd/sys/unix/android.rs index abbe3fc1846bd..08e6b45dcd358 100644 --- a/src/libstd/sys/unix/android.rs +++ b/src/libstd/sys/unix/android.rs @@ -28,10 +28,11 @@ #![cfg(target_os = "android")] -use libc::{c_int, sighandler_t}; +use libc::{c_int, c_void, sighandler_t, size_t, ssize_t}; +use libc::{ftruncate, pread, pwrite}; use io; -use sys::cvt_r; +use super::{cvt, cvt_r}; // The `log2` and `log2f` functions apparently appeared in android-18, or at // least you can see they're not present in the android-17 header [1] and they @@ -99,10 +100,6 @@ pub unsafe fn signal(signum: c_int, handler: sighandler_t) -> sighandler_t { pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> { weak!(fn ftruncate64(c_int, i64) -> c_int); - extern { - fn ftruncate(fd: c_int, off: i32) -> c_int; - } - unsafe { match ftruncate64.get() { Some(f) => cvt_r(|| f(fd, size as i64)).map(|_| ()), @@ -117,3 +114,35 @@ pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> { } } } + +pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i64) + -> io::Result +{ + weak!(fn pread64(c_int, *mut c_void, size_t, i64) -> ssize_t); + unsafe { + pread64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| { + if offset as u64 > i32::max_value() as u64 { + Err(io::Error::new(io::Error::InvalidInput, + "cannot pread >2GB")) + } else { + cvt(pread(fd, buf, count, offset as i32)) + } + }) + } +} + +pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: i64) + -> io::Result +{ + weak!(fn pwrite64(c_int, *const c_void, size_t, i64) -> ssize_t); + unsafe { + pwrite64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| { + if offset as u64 > i32::max_value() as u64 { + Err(io::Error::new(io::Error::InvalidInput, + "cannot pwrite >2GB")) + } else { + cvt(pwrite(fd, buf, count, offset as i32)) + } + }) + } +} diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index 4f92c3274d2e6..30573d695aac7 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -18,10 +18,26 @@ use sys::cvt; use sys_common::AsInner; use sys_common::io::read_to_end_uninitialized; -#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] -use libc::{pread64, pwrite64, off64_t}; +#[cfg(target_os = "android")] +use super::android::{cvt_pread64, cvt_pwrite64}; +#[cfg(any(target_os = "linux", target_os = "emscripten"))] +use libc::{pread64, pwrite64, off64_t, ssize_t}; #[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))] -use libc::{pread as pread64, pwrite as pwrite64, off_t as off64_t}; +use libc::{pread as pread64, pwrite as pwrite64, off_t as off64_t, ssize_t}; + +#[cfg(not(target_os = "android"))] +unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: off64_t) + -> io::Result +{ + cvt(pread64(fd, buf, count, offset)) +} + +#[cfg(not(target_os = "android"))] +unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: off64_t) + -> io::Result +{ + cvt(pwrite64(fd, buf, count, offset)) +} pub struct FileDesc { fd: c_int, @@ -56,13 +72,13 @@ impl FileDesc { } pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - let ret = cvt(unsafe { - pread64(self.fd, - buf.as_mut_ptr() as *mut c_void, - buf.len(), - offset as off64_t) - })?; - Ok(ret as usize) + unsafe { + cvt_pread64(self.fd, + buf.as_mut_ptr() as *mut c_void, + buf.len(), + offset as off64_t) + .map(|n| n as usize) + } } pub fn write(&self, buf: &[u8]) -> io::Result { @@ -75,13 +91,13 @@ impl FileDesc { } pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - let ret = cvt(unsafe { - pwrite64(self.fd, - buf.as_ptr() as *const c_void, - buf.len(), - offset as off64_t) - })?; - Ok(ret as usize) + unsafe { + cvt_pwrite64(self.fd, + buf.as_ptr() as *const c_void, + buf.len(), + offset as off64_t) + .map(|n| n as usize) + } } #[cfg(not(any(target_env = "newlib", From 70dcfd634ea6abe06a6f8c40ca3d294fb0802670 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Sun, 9 Oct 2016 01:06:55 +0200 Subject: [PATCH 3/7] Use `try_into` and move some functions --- src/libstd/sys/unix/android.rs | 13 +++++---- src/libstd/sys/unix/fd.rs | 53 +++++++++++++++++++--------------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/libstd/sys/unix/android.rs b/src/libstd/sys/unix/android.rs index 08e6b45dcd358..a4272a9f5223b 100644 --- a/src/libstd/sys/unix/android.rs +++ b/src/libstd/sys/unix/android.rs @@ -31,6 +31,7 @@ use libc::{c_int, c_void, sighandler_t, size_t, ssize_t}; use libc::{ftruncate, pread, pwrite}; +use convert::TryInto; use io; use super::{cvt, cvt_r}; @@ -121,11 +122,11 @@ pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i6 weak!(fn pread64(c_int, *mut c_void, size_t, i64) -> ssize_t); unsafe { pread64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| { - if offset as u64 > i32::max_value() as u64 { + if let Ok(o) = offset.try_into() { + cvt(pread(fd, buf, count, o)) + } else { Err(io::Error::new(io::Error::InvalidInput, "cannot pread >2GB")) - } else { - cvt(pread(fd, buf, count, offset as i32)) } }) } @@ -137,11 +138,11 @@ pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: weak!(fn pwrite64(c_int, *const c_void, size_t, i64) -> ssize_t); unsafe { pwrite64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| { - if offset as u64 > i32::max_value() as u64 { + if let Ok(o) = offset.try_into() { + cvt(pwrite(fd, buf, count, o)) + } else { Err(io::Error::new(io::Error::InvalidInput, "cannot pwrite >2GB")) - } else { - cvt(pwrite(fd, buf, count, offset as i32)) } }) } diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index 30573d695aac7..41bb96fed16cb 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -18,27 +18,6 @@ use sys::cvt; use sys_common::AsInner; use sys_common::io::read_to_end_uninitialized; -#[cfg(target_os = "android")] -use super::android::{cvt_pread64, cvt_pwrite64}; -#[cfg(any(target_os = "linux", target_os = "emscripten"))] -use libc::{pread64, pwrite64, off64_t, ssize_t}; -#[cfg(not(any(target_os = "linux", target_os = "emscripten", target_os = "android")))] -use libc::{pread as pread64, pwrite as pwrite64, off_t as off64_t, ssize_t}; - -#[cfg(not(target_os = "android"))] -unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: off64_t) - -> io::Result -{ - cvt(pread64(fd, buf, count, offset)) -} - -#[cfg(not(target_os = "android"))] -unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: off64_t) - -> io::Result -{ - cvt(pwrite64(fd, buf, count, offset)) -} - pub struct FileDesc { fd: c_int, } @@ -72,11 +51,25 @@ impl FileDesc { } pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + #[cfg(target_os = "android")] + use super::android::cvt_pread64; + + #[cfg(not(target_os = "android"))] + unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: usize, offset: i64) + -> io::Result + { + #[cfg(any(target_os = "linux", target_os = "emscripten"))] + use libc::pread64; + #[cfg(not(any(target_os = "linux", target_os = "emscripten")))] + use libc::pread as pread64; + cvt(pread64(fd, buf, count, offset)) + } + unsafe { cvt_pread64(self.fd, buf.as_mut_ptr() as *mut c_void, buf.len(), - offset as off64_t) + offset as i64) .map(|n| n as usize) } } @@ -91,11 +84,25 @@ impl FileDesc { } pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + #[cfg(target_os = "android")] + use super::android::cvt_pwrite64; + + #[cfg(not(target_os = "android"))] + unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: usize, offset: i64) + -> io::Result + { + #[cfg(any(target_os = "linux", target_os = "emscripten"))] + use libc::pwrite64; + #[cfg(not(any(target_os = "linux", target_os = "emscripten")))] + use libc::pwrite as pwrite64; + cvt(pwrite64(fd, buf, count, offset)) + } + unsafe { cvt_pwrite64(self.fd, buf.as_ptr() as *const c_void, buf.len(), - offset as off64_t) + offset as i64) .map(|n| n as usize) } } From 2eda01ee437876d8e613a33de8105d11b66380dd Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Tue, 11 Oct 2016 12:16:35 +0200 Subject: [PATCH 4/7] Fix Android compilation `io::Error` -> `io::ErrorKind` --- src/libstd/sys/unix/android.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstd/sys/unix/android.rs b/src/libstd/sys/unix/android.rs index a4272a9f5223b..d8aa6e372be1f 100644 --- a/src/libstd/sys/unix/android.rs +++ b/src/libstd/sys/unix/android.rs @@ -125,7 +125,7 @@ pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i6 if let Ok(o) = offset.try_into() { cvt(pread(fd, buf, count, o)) } else { - Err(io::Error::new(io::Error::InvalidInput, + Err(io::Error::new(io::ErrorKind::InvalidInput, "cannot pread >2GB")) } }) @@ -141,7 +141,7 @@ pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: if let Ok(o) = offset.try_into() { cvt(pwrite(fd, buf, count, o)) } else { - Err(io::Error::new(io::Error::InvalidInput, + Err(io::Error::new(io::ErrorKind::InvalidInput, "cannot pwrite >2GB")) } }) From 744aecf7933210d1a94b58e33a3dea022b30d1aa Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Wed, 12 Oct 2016 13:06:55 +0200 Subject: [PATCH 5/7] Remove unnecessary `unsafe` block --- src/libstd/sys/unix/android.rs | 36 +++++++++++++++------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/libstd/sys/unix/android.rs b/src/libstd/sys/unix/android.rs index d8aa6e372be1f..81f1a637ad567 100644 --- a/src/libstd/sys/unix/android.rs +++ b/src/libstd/sys/unix/android.rs @@ -120,30 +120,26 @@ pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i6 -> io::Result { weak!(fn pread64(c_int, *mut c_void, size_t, i64) -> ssize_t); - unsafe { - pread64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| { - if let Ok(o) = offset.try_into() { - cvt(pread(fd, buf, count, o)) - } else { - Err(io::Error::new(io::ErrorKind::InvalidInput, - "cannot pread >2GB")) - } - }) - } + pread64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| { + if let Ok(o) = offset.try_into() { + cvt(pread(fd, buf, count, o)) + } else { + Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot pread >2GB")) + } + }) } pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: i64) -> io::Result { weak!(fn pwrite64(c_int, *const c_void, size_t, i64) -> ssize_t); - unsafe { - pwrite64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| { - if let Ok(o) = offset.try_into() { - cvt(pwrite(fd, buf, count, o)) - } else { - Err(io::Error::new(io::ErrorKind::InvalidInput, - "cannot pwrite >2GB")) - } - }) - } + pwrite64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| { + if let Ok(o) = offset.try_into() { + cvt(pwrite(fd, buf, count, o)) + } else { + Err(io::Error::new(io::ErrorKind::InvalidInput, + "cannot pwrite >2GB")) + } + }) } From 94aa08b66cb624155f2fb84f01fd5d9eab3c0c7a Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Fri, 14 Oct 2016 14:19:41 +0200 Subject: [PATCH 6/7] Only use Android fallback for {ftruncate,pread,pwrite} on 32 bit --- src/libstd/sys/unix/android.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/libstd/sys/unix/android.rs b/src/libstd/sys/unix/android.rs index 81f1a637ad567..8b11d077daff2 100644 --- a/src/libstd/sys/unix/android.rs +++ b/src/libstd/sys/unix/android.rs @@ -98,6 +98,7 @@ pub unsafe fn signal(signum: c_int, handler: sighandler_t) -> sighandler_t { // // If it doesn't we just fall back to `ftruncate`, generating an error for // too-large values. +#[cfg(target_pointer_width = "32")] pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> { weak!(fn ftruncate64(c_int, i64) -> c_int); @@ -116,6 +117,14 @@ pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> { } } +#[cfg(target_pointer_width = "64")] +pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> { + unsafe { + cvt_r(|| ftruncate(fd, size as i64)).map(|_| ()) + } +} + +#[cfg(target_pointer_width = "32")] pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i64) -> io::Result { @@ -130,6 +139,7 @@ pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i6 }) } +#[cfg(target_pointer_width = "32")] pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: i64) -> io::Result { @@ -143,3 +153,17 @@ pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: } }) } + +#[cfg(target_pointer_width = "64")] +pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i64) + -> io::Result +{ + cvt(pread(fd, buf, count, offset)) +} + +#[cfg(target_pointer_width = "64")] +pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: i64) + -> io::Result +{ + cvt(pwrite(fd, buf, count, offset)) +} From 15549935f8dd110a67229531d02018279cbbe34b Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Fri, 14 Oct 2016 23:02:47 +0200 Subject: [PATCH 7/7] Android: Fix unused-imports warning --- src/libstd/sys/unix/android.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libstd/sys/unix/android.rs b/src/libstd/sys/unix/android.rs index 8b11d077daff2..10436723a81d0 100644 --- a/src/libstd/sys/unix/android.rs +++ b/src/libstd/sys/unix/android.rs @@ -31,7 +31,6 @@ use libc::{c_int, c_void, sighandler_t, size_t, ssize_t}; use libc::{ftruncate, pread, pwrite}; -use convert::TryInto; use io; use super::{cvt, cvt_r}; @@ -128,6 +127,7 @@ pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> { pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i64) -> io::Result { + use convert::TryInto; weak!(fn pread64(c_int, *mut c_void, size_t, i64) -> ssize_t); pread64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| { if let Ok(o) = offset.try_into() { @@ -143,6 +143,7 @@ pub unsafe fn cvt_pread64(fd: c_int, buf: *mut c_void, count: size_t, offset: i6 pub unsafe fn cvt_pwrite64(fd: c_int, buf: *const c_void, count: size_t, offset: i64) -> io::Result { + use convert::TryInto; weak!(fn pwrite64(c_int, *const c_void, size_t, i64) -> ssize_t); pwrite64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| { if let Ok(o) = offset.try_into() {