提交 b3f2644b 编写于 作者: T Tobias Bucher

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).
上级 b98cc352
......@@ -1903,6 +1903,130 @@ fn file_test_io_seek_shakedown() {
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 @@ fn copy_file_preserves_perm_bits() {
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()));
......
......@@ -20,6 +20,51 @@
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 {
......
......@@ -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")]
......
......@@ -18,6 +18,11 @@
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 @@ pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
(&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 @@ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
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",
......
......@@ -483,10 +483,18 @@ pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
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> {
......
......@@ -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 {
......
......@@ -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;
}
......@@ -311,6 +311,10 @@ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
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 @@ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
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> {
......
......@@ -104,6 +104,23 @@ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
}
}
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 @@ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
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;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册