From 0354b8721b3b45ff80befdc1252441aa21e95100 Mon Sep 17 00:00:00 2001 From: Martin von Zweigbergk Date: Sat, 16 Oct 2021 23:57:59 -0700 Subject: [PATCH] index: transparently reindex if the index is corrupt I'm about to change the index format (to remove predecessor information), which will break the format. Let's prepare for that by having `IndexStore` reindex the repo if it fails to read the index.. --- lib/src/index.rs | 24 +++++++++++++++++++++--- lib/src/index_store.rs | 18 ++++++++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/lib/src/index.rs b/lib/src/index.rs index 358ea8c27..b8a8f7c92 100644 --- a/lib/src/index.rs +++ b/lib/src/index.rs @@ -29,6 +29,7 @@ use blake2::{Blake2b, Digest}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use itertools::Itertools; use tempfile::NamedTempFile; +use thiserror::Error; use crate::backend::{ChangeId, CommitId}; use crate::commit::Commit; @@ -219,6 +220,14 @@ impl CommitLookupEntry<'_> { } } +#[derive(Error, Debug)] +pub enum IndexLoadError { + #[error("Index file '{0}' is corrupt.")] + IndexCorrupt(String), + #[error("I/O error while loading index file: {0}")] + IoError(#[from] io::Error), +} + // File format: // u32: number of entries // u32: number of parent overflow entries @@ -612,7 +621,14 @@ impl MutableIndex { persist_content_addressed_temp_file(temp_file, &index_file_path)?; let mut cursor = Cursor::new(&buf); - ReadonlyIndex::load_from(&mut cursor, dir, index_file_id_hex, hash_length) + ReadonlyIndex::load_from(&mut cursor, dir, index_file_id_hex, hash_length).map_err(|err| { + match err { + IndexLoadError::IndexCorrupt(err) => { + panic!("Just-created index file is corrupt: {}", err) + } + IndexLoadError::IoError(err) => err, + } + }) } pub fn num_commits(&self) -> u32 { @@ -1389,7 +1405,7 @@ impl ReadonlyIndex { dir: PathBuf, name: String, hash_length: usize, - ) -> io::Result> { + ) -> Result, IndexLoadError> { let parent_filename_len = file.read_u32::()?; let num_parent_commits; let maybe_parent_file; @@ -1420,7 +1436,9 @@ impl ReadonlyIndex { let predecessor_overflow_size = (num_predecessor_overflow_entries as usize) * 4; let expected_size = graph_size + lookup_size + parent_overflow_size + predecessor_overflow_size; - assert_eq!(data.len(), expected_size); + if data.len() != expected_size { + return Err(IndexLoadError::IndexCorrupt(name)); + } let overflow_predecessor = data.split_off(graph_size + lookup_size + parent_overflow_size); let overflow_parent = data.split_off(graph_size + lookup_size); let lookup = data.split_off(graph_size); diff --git a/lib/src/index_store.rs b/lib/src/index_store.rs index e4e038f7d..00965696e 100644 --- a/lib/src/index_store.rs +++ b/lib/src/index_store.rs @@ -26,7 +26,7 @@ use crate::backend::CommitId; use crate::commit::Commit; use crate::dag_walk; use crate::file_util::persist_content_addressed_temp_file; -use crate::index::{MutableIndex, ReadonlyIndex}; +use crate::index::{IndexLoadError, MutableIndex, ReadonlyIndex}; use crate::op_store::OperationId; use crate::operation::Operation; use crate::store::Store; @@ -54,8 +54,18 @@ impl IndexStore { let op_id_hex = op.id().hex(); let op_id_file = self.dir.join("operations").join(&op_id_hex); if op_id_file.exists() { - self.load_index_at_operation(store.hash_length(), op.id()) - .unwrap() + match self.load_index_at_operation(store.hash_length(), op.id()) { + Err(IndexLoadError::IndexCorrupt(_)) => { + // If the index was corrupt (maybe it was written in a different format), + // we just reindex. + // TODO: Move this message to a callback or something. + println!("The index was corrupt (maybe the format has changed). Reindexing..."); + std::fs::remove_dir_all(self.dir.join("operations")).unwrap(); + std::fs::create_dir(self.dir.join("operations")).unwrap(); + self.index_at_operation(store, op).unwrap() + } + result => result.unwrap(), + } } else { self.index_at_operation(store, op).unwrap() } @@ -69,7 +79,7 @@ impl IndexStore { &self, hash_length: usize, op_id: &OperationId, - ) -> io::Result> { + ) -> Result, IndexLoadError> { let op_id_file = self.dir.join("operations").join(op_id.hex()); let mut buf = vec![]; File::open(op_id_file)