From 8da67b3aa2614326e1981ffbb215691583e058f2 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Fri, 9 Feb 2024 14:35:53 +0100 Subject: [PATCH] [block-info-short-682] `garage block info`: find blocks by prefix (fix #682) --- src/garage/admin/block.rs | 47 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/garage/admin/block.rs b/src/garage/admin/block.rs index c4a45738..edeb88c0 100644 --- a/src/garage/admin/block.rs +++ b/src/garage/admin/block.rs @@ -25,8 +25,7 @@ impl AdminRpcHandler { } async fn handle_block_info(&self, hash: &String) -> Result { - let hash = hex::decode(hash).ok_or_bad_request("invalid hash")?; - let hash = Hash::try_from(&hash).ok_or_bad_request("invalid hash")?; + let hash = self.find_block_hash_by_prefix(hash)?; let refcount = self.garage.block_manager.get_block_rc(&hash)?; let block_refs = self .garage @@ -189,4 +188,48 @@ impl AdminRpcHandler { Ok(()) } + + // ---- helper function ---- + fn find_block_hash_by_prefix(&self, prefix: &str) -> Result { + if prefix.len() < 4 { + return Err(Error::BadRequest( + "Please specify at least 4 characters of the block hash".into(), + )); + } + + let prefix_bin = + hex::decode(&prefix[..prefix.len() & !1]).ok_or_bad_request("invalid hash")?; + + let iter = self + .garage + .block_ref_table + .data + .store + .range(&prefix_bin[..]..) + .map_err(GarageError::from)?; + let mut found = None; + for item in iter { + let (k, _v) = item.map_err(GarageError::from)?; + let hash = Hash::try_from(&k[..32]).unwrap(); + if &hash.as_slice()[..prefix_bin.len()] != prefix_bin { + break; + } + if hex::encode(hash.as_slice()).starts_with(prefix) { + match &found { + Some(x) if *x == hash => (), + Some(_) => { + return Err(Error::BadRequest(format!( + "Several blocks match prefix `{}`", + prefix + ))); + } + None => { + found = Some(hash); + } + } + } + } + + found.ok_or_else(|| Error::BadRequest("No matching block found".into())) + } }