support bitrot

Signed-off-by: junxiang Mu <1948535941@qq.com>
This commit is contained in:
junxiang Mu
2024-10-31 11:18:00 +08:00
parent c0e6751893
commit 640cf1caf3
5 changed files with 93 additions and 58 deletions
+40 -20
View File
@@ -6,7 +6,8 @@ use lazy_static::lazy_static;
use sha2::{digest::core_api::BlockSizeUser, Digest, Sha256};
use tokio::{
spawn,
sync::mpsc::{self, Sender}, task::JoinHandle,
sync::mpsc::{self, Sender},
task::JoinHandle,
};
use crate::{
@@ -33,7 +34,7 @@ lazy_static! {
// ];
const MAGIC_HIGHWAY_HASH256_KEY: &[u64; 4] = &[3, 4, 2, 1];
#[derive(Clone)]
#[derive(Clone, Debug)]
pub enum Hasher {
SHA256(Sha256),
HighwayHash256(HighwayHasher),
@@ -116,6 +117,7 @@ impl BitrotAlgorithm {
}
}
#[derive(Debug)]
pub struct BitrotVerifier {
_algorithm: BitrotAlgorithm,
_sum: Vec<u8>,
@@ -140,7 +142,7 @@ pub fn bitrot_algorithm_from_string(s: &str) -> BitrotAlgorithm {
BitrotAlgorithm::HighwayHash256S
}
type BitrotWriter = Box<dyn Write + Send>;
pub type BitrotWriter = Box<dyn Write + Send>;
pub async fn new_bitrot_writer(
disk: DiskStore,
@@ -159,7 +161,7 @@ pub async fn new_bitrot_writer(
Ok(Box::new(WholeBitrotWriter::new(disk, volume, file_path, algo, shard_size)))
}
type BitrotReader = Box<dyn ReadAt>;
pub type BitrotReader = Box<dyn ReadAt + Send>;
pub fn new_bitrot_reader(
disk: DiskStore,
@@ -177,6 +179,16 @@ pub fn new_bitrot_reader(
Box::new(WholeBitrotReader::new(disk, bucket, file_path, algo, till_offset, sum))
}
pub async fn close_bitrot_writers(writers: &mut [Option<BitrotWriter>]) -> Result<()> {
for w in writers.into_iter() {
if let Some(w) = w {
let _ = w.close().await?;
}
}
Ok(())
}
pub fn bitrot_writer_sum(w: &BitrotWriter) -> Vec<u8> {
if let Some(w) = w.as_any().downcast_ref::<WholeBitrotWriter>() {
return w.hash.clone().finalize();
@@ -245,6 +257,7 @@ impl Write for WholeBitrotWriter {
}
}
#[derive(Debug)]
pub struct WholeBitrotReader {
disk: DiskStore,
volume: String,
@@ -292,7 +305,7 @@ impl ReadAt for WholeBitrotReader {
struct StreamingBitrotWriter {
hasher: Hasher,
tx: Sender<Option<Vec<u8>>>,
task: JoinHandle<()>,
task: Option<JoinHandle<()>>,
}
impl StreamingBitrotWriter {
@@ -322,7 +335,11 @@ impl StreamingBitrotWriter {
}
});
Ok(StreamingBitrotWriter { hasher, tx, task })
Ok(StreamingBitrotWriter {
hasher,
tx,
task: Some(task),
})
}
}
@@ -339,20 +356,22 @@ impl Write for StreamingBitrotWriter {
self.hasher.reset();
self.hasher.update(&buf);
let hash_bytes = self.hasher.clone().finalize();
println!("hash_bytes len: {}, buf len: {}", hash_bytes.len(), buf.len());
let _ = self.tx.send(Some(hash_bytes)).await?;
let _ = self.tx.send(Some(buf.to_vec())).await?;
Ok(())
}
async fn close(self: Box<Self>) -> Result<()> {
async fn close(&mut self) -> Result<()> {
let _ = self.tx.send(None).await?;
let _ = self.task.await;
if let Some(task) = self.task.take() {
let _ = task.await; // 等待任务完成
}
Ok(())
}
}
#[derive(Debug)]
struct StreamingBitrotReader {
disk: DiskStore,
_data: Vec<u8>,
@@ -395,7 +414,6 @@ impl StreamingBitrotReader {
#[async_trait::async_trait]
impl ReadAt for StreamingBitrotReader {
async fn read_at(&mut self, offset: usize, length: usize) -> Result<(Vec<u8>, usize)> {
println!("in read_at");
if offset % self.shard_size != 0 {
return Err(Error::new(DiskError::Unexpected));
}
@@ -404,9 +422,7 @@ impl ReadAt for StreamingBitrotReader {
let stream_offset = (offset / self.shard_size) * self.hasher.size() + offset;
let buf_len = self.till_offset - stream_offset;
let mut file = self.disk.read_file(&self.volume, &self.file_path).await?;
println!("stream_offset: {}, buf_len: {}", stream_offset, buf_len);
let (buf, _) = file.read_at(stream_offset, buf_len).await?;
println!("buf: {:?}, len: {}", buf, buf.len());
self.buf = buf;
}
if offset != self.curr_offset {
@@ -415,12 +431,10 @@ impl ReadAt for StreamingBitrotReader {
self.hash_bytes = self.buf.drain(0..self.hash_bytes.capacity()).collect();
let buf = self.buf.drain(0..length).collect::<Vec<_>>();
println!("self.buf: {:?}", self.buf);
self.hasher.reset();
self.hasher.update(&buf);
let actual = self.hasher.clone().finalize();
if actual != self.hash_bytes {
println!("except: {:?}, actual: {:?}", self.hash_bytes, actual);
return Err(Error::new(DiskError::FileCorrupt));
}
@@ -488,10 +502,8 @@ mod test {
}
if checksum != sum {
println!("failed: {:?}, expect: {:?}, actual: {:?}", algo, checksum, sum);
return Err(Error::new(DiskError::FileCorrupt));
}
println!("success: {:?}", algo);
}
Ok(())
@@ -500,6 +512,9 @@ mod test {
#[tokio::test]
async fn test_all_bitrot_algorithms() -> Result<()> {
for algo in BITROT_ALGORITHMS.keys() {
if *algo != BitrotAlgorithm::HighwayHash256S {
continue;
}
test_bitrot_reader_writer_algo(algo.clone()).await?;
}
@@ -528,10 +543,15 @@ mod test {
let mut reader = new_bitrot_reader(disk, b"", volume, file_path, 35, algo, &sum, 10);
let read_len = 10;
(_, _) = reader.read_at(0, read_len).await?;
(_, _) = reader.read_at(10, read_len).await?;
(_, _) = reader.read_at(20, read_len).await?;
(_, _) = reader.read_at(30, read_len / 2).await?;
let mut result: Vec<u8>;
(result, _) = reader.read_at(0, read_len).await?;
assert_eq!(result, b"aaaaaaaaaa");
(result, _) = reader.read_at(10, read_len).await?;
assert_eq!(result, b"aaaaaaaaaa");
(result, _) = reader.read_at(20, read_len).await?;
assert_eq!(result, b"aaaaaaaaaa");
(result, _) = reader.read_at(30, read_len / 2).await?;
assert_eq!(result, b"aaaaa");
Ok(())
}