diff options
| author | Tobias Bucher <tobiasbucher5991@gmail.com> | 2016-08-16 01:11:33 +0200 |
|---|---|---|
| committer | Tobias Bucher <tobiasbucher5991@gmail.com> | 2016-10-09 10:48:07 +0200 |
| commit | b3f2644b66647d47338051d783cae8e661312472 (patch) | |
| tree | 681ee7fee5cea595cfadbcaf44e7219b61c31338 /src/libstd/sys | |
| parent | b98cc352cbb428f3c9a0e608bc57a83109a26a57 (diff) | |
| download | rust-b3f2644b66647d47338051d783cae8e661312472.tar.gz rust-b3f2644b66647d47338051d783cae8e661312472.zip | |
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).
Diffstat (limited to 'src/libstd/sys')
| -rw-r--r-- | src/libstd/sys/unix/ext/fs.rs | 45 | ||||
| -rw-r--r-- | src/libstd/sys/unix/ext/mod.rs | 2 | ||||
| -rw-r--r-- | src/libstd/sys/unix/fd.rs | 25 | ||||
| -rw-r--r-- | src/libstd/sys/unix/fs.rs | 8 | ||||
| -rw-r--r-- | src/libstd/sys/windows/ext/fs.rs | 51 | ||||
| -rw-r--r-- | src/libstd/sys/windows/ext/mod.rs | 2 | ||||
| -rw-r--r-- | src/libstd/sys/windows/fs.rs | 8 | ||||
| -rw-r--r-- | src/libstd/sys/windows/handle.rs | 30 |
8 files changed, 170 insertions, 1 deletions
diff --git a/src/libstd/sys/unix/ext/fs.rs b/src/libstd/sys/unix/ext/fs.rs index 77587918ac9..fcfab051588 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<usize>; + + /// 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<usize>; +} + +#[unstable(feature = "file_offset", issue = "35918")] +impl FileExt for fs::File { + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { + self.as_inner().read_at(buf, offset) + } + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + 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 1be3d75d866..b2483f4e209 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 eadf98867a6..4f92c3274d2 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<usize> { + 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<usize> { 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<usize> { + 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 606e2c2264a..fe8cbc84215 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<usize> { + self.0.read_at(buf, offset) + } + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) } + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + self.0.write_at(buf, offset) + } + pub fn flush(&self) -> io::Result<()> { Ok(()) } pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> { diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs index 4388a0bdff2..1e2b8bf38fa 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<usize>; + + /// 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<usize>; +} + +#[unstable(feature = "file_offset", issue = "35918")] +impl FileExt for fs::File { + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { + self.as_inner().read_at(buf, offset) + } + + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + 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 c3578fdfdb1..932bb5e9564 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 90a16853d56..a927eae020d 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<usize> { + self.handle.read_at(buf, offset) + } + pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { 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<usize> { + self.handle.write_at(buf, offset) + } + pub fn flush(&self) -> io::Result<()> { Ok(()) } pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> { diff --git a/src/libstd/sys/windows/handle.rs b/src/libstd/sys/windows/handle.rs index 97e746ee345..10b86ba44bc 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<usize> { + let mut read = 0; + let len = cmp::min(buf.len(), <c::DWORD>::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<usize> { + let mut written = 0; + let len = cmp::min(buf.len(), <c::DWORD>::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<Handle> { let mut ret = 0 as c::HANDLE; |
