diff options
author | Alex Auvolat <alex@adnab.me> | 2024-02-09 14:35:53 +0100 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2024-02-09 14:35:53 +0100 |
commit | 8da67b3aa2614326e1981ffbb215691583e058f2 (patch) | |
tree | fcdd782509db15fe52156763f1bb912e9795bc6d | |
parent | 0c7ce001c907791518f93fd9d2db01eebc517c51 (diff) | |
download | garage-block-info-short-682.tar.gz garage-block-info-short-682.zip |
[block-info-short-682] `garage block info`: find blocks by prefix (fix #682)block-info-short-682
-rw-r--r-- | src/garage/admin/block.rs | 47 |
1 files 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<AdminRpc, Error> { - 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<Hash, Error> { + 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())) + } } |