Repository: paritytech/parity-db (language: rust)

Issue:
seek_read/seek_write are not guaranteed to read/write exact amount of data on Windows
I was reading through this:
https://github.com/paritytech/parity-db/blob/0a0491b3d2060228d7bc99fb80e9f3ea8b032d77/src/file.rs#L137-L150

And noticed that you're not checking how much data was actually read/written, potentially causing serious issues on Windows and permanently corrupting database.


Discussion / hints:

Fix patch:
diff --git a/src/file.rs b/src/file.rs
index 875614d9..4fc6837e 100644
--- a/src/file.rs
+++ b/src/file.rs
@@ -135,17 +135,73 @@ impl TableFile {
 	}
 
 	#[cfg(windows)]
-	pub fn read_at(&self, buf: &mut [u8], offset: u64) -> Result<()> {
+	pub fn read_at(&self, mut buf: &mut [u8], mut offset: u64) -> Result<()> {
+		use crate::error::Error;
+		use std::io;
 		use std::os::windows::fs::FileExt;
-		self.file.read().as_ref().unwrap().seek_read(buf, offset)?;
-		Ok(())
+
+		let file = self.file.read();
+		let file = file.as_ref().unwrap();
+
+		while !buf.is_empty() {
+			match file.seek_read(buf, offset) {
+				Ok(0) => {
+					break;
+				},
+				Ok(n) => {
+					buf = &mut buf[n..];
+					offset += n as u64;
+				},
+				Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {
+					// Try again
+				},
+				Err(e) => {
+					return Err(Error::Io(e));
+				},
+			}
+		}
+
+		if !buf.is_empty() {
+			Err(Error::Io(io::Error::new(
+				io::ErrorKind::UnexpectedEof,
+				"failed to fill whole buffer",
+			)))
+		} else {
+			Ok(())
+		}
 	}
 
 	#[cfg(windows)]
-	pub fn write_at(&self, buf: &[u8], offset: u64) -> Result<()> {
+	pub fn write_at(&self, mut buf: &[u8], mut offset: u64) -> Result<()> {
+		use crate::error::Error;
+		use std::io;
 		use std::os::windows::fs::FileExt;
+
 		self.dirty.store(true, Ordering::Relaxed);
-		self.file.read().as_ref().unwrap().seek_write(buf, offset)?;
+		let file = self.file.read();
+		let file = file.as_ref().unwrap();
+
+		while !buf.is_empty() {
+			match file.seek_write(buf, offset) {
+				Ok(0) => {
+					return Err(Error::Io(io::Error::new(
+						io::ErrorKind::WriteZero,
+						"failed to write whole buffer",
+					)));
+				},
+				Ok(n) => {
+					buf = &buf[n..];
+					offset += n as u64;
+				},
+				Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {
+					// Try again
+				},
+				Err(e) => {
+					return Err(Error::Io(e));
+				},
+			}
+		}
+
 		Ok(())
 	}
 


Test patch: