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

Issue:
DB corrupted after a partial write in LogChange::flush_to_file
Test from fuzzer:

```rust
#[test]
fn test_from_fuzzer() {
	crate::set_number_of_allowed_io_operations(18);
	let tmp = tempdir().unwrap();
	let mut options = Options::with_columns(tmp.path(), 1);
	options.columns[0].btree_index = true;
	let db = Db::open_or_create(&options).unwrap();
	db.commit(vec![(0, vec![249], None)]).unwrap();
	drop(db); // The intrumented failure happens here
	crate::set_number_of_allowed_io_operations(usize::MAX);
	Db::open_or_create(&options).unwrap(); // Fails with Io(Error { kind: UnexpectedEof, message: "failed to fill whole buffer" })
}
```

If I/O operations fail inside of `LogChange::flush_to_file` it seems it might lead to a corrupted log and, hence, a corrupted database.


Discussion / hints:

Fix patch:
diff --git a/src/log.rs b/src/log.rs
index 0407d52a..223e2f28 100644
--- a/src/log.rs
+++ b/src/log.rs
@@ -12,7 +12,7 @@ use parking_lot::{Condvar, MappedRwLockWriteGuard, Mutex, RwLock, RwLockWriteGua
 use std::{
 	collections::{HashMap, VecDeque},
 	convert::TryInto,
-	io::{Read, Seek, Write},
+	io::{ErrorKind, Read, Seek, Write},
 	sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering},
 };
 
@@ -553,13 +553,24 @@ impl Log {
 		if try_io!(file.metadata()).len() == 0 {
 			return Ok((file, None))
 		}
-		// read first record id
+		match Self::read_first_record_id(&mut file) {
+			Err(Error::Io(e)) if e.kind() == ErrorKind::UnexpectedEof => {
+				log::error!(target: "parity-db", "Opened existing log {}. No first record id found", path.display());
+				Ok((file, None))
+			},
+			Err(e) => Err(e),
+			Ok(id) => {
+				try_io!(file.seek(std::io::SeekFrom::Start(0)));
+				log::debug!(target: "parity-db", "Opened existing log {}, first record_id = {}", path.display(), id);
+				Ok((file, Some(id)))
+			},
+		}
+	}
+
+	fn read_first_record_id(file: &mut std::fs::File) -> Result<u64> {
 		let mut buf = [0; 9];
 		try_io!(file.read_exact(&mut buf));
-		try_io!(file.seek(std::io::SeekFrom::Start(0)));
-		let id = u64::from_le_bytes(buf[1..].try_into().unwrap());
-		log::debug!(target: "parity-db", "Opened existing log {}, first record_id = {}", path.display(), id);
-		Ok((file, Some(id)))
+		Ok(u64::from_le_bytes(buf[1..].try_into().unwrap()))
 	}
 
 	fn drop_log(&self, id: u32) -> Result<()> {


Test patch: