harmos_storage/
frozen_file.rs1use std::{
20 fs::File,
21 io::Write,
22 sync::{Arc, Mutex},
23};
24
25use sha2::{Digest, Sha256};
27
28use crate::local::io_error;
30use crate::{ByteRange, Result, StorageManager};
31
32#[derive(Clone)]
42pub struct FrozenFile {
43 backing: Arc<Backing>,
44 length: u64,
45 sha256: [u8; 32],
46 pub(crate) session: StorageManager<crate::ErasedCatalog>,
47}
48
49struct Backing {
50 file: Mutex<File>,
51}
52
53impl FrozenFile {
60 pub fn length(&self) -> u64 {
61 self.length
62 }
63
64 pub fn sha256(&self) -> &[u8; 32] {
65 &self.sha256
66 }
67
68 pub async fn read(&self, range: ByteRange) -> Result<crate::FileReader> {
71 let file = self.opened_file()?;
72 self.session.read_opened_file(file, range).await
73 }
74
75 pub(crate) fn opened_file(&self) -> Result<File> {
76 self.backing.file.lock().unwrap().try_clone().map_err(io_error)
77 }
78}
79
80impl<C> StorageManager<C> {
81 pub async fn freeze_opened_file(&self, source: File, range: ByteRange) -> Result<FrozenFile> {
85 let backing = tokio::task::spawn_blocking(move || {
86 let file = tempfile::NamedTempFile::new().map_err(io_error)?.into_file();
87 Ok::<_, crate::Error>(Arc::new(Backing { file: Mutex::new(file) }))
88 })
89 .await
90 .map_err(task_error)??;
91 let mut reader = self.read_opened_file(source, range).await?;
92 let mut digest = Sha256::new();
93 while let Some(batch) = reader.next().await? {
94 digest.update(batch.bytes());
95 let target = Arc::clone(&backing);
96 tokio::task::spawn_blocking(move || target.file.lock().unwrap().write_all(batch.bytes()).map_err(io_error))
97 .await
98 .map_err(task_error)??;
99 }
100 let completion = reader.completion()?;
101 if range.limit.is_some_and(|length| length != completion.bytes) {
102 return Err(crate::Error::InvalidInput {
103 field: "frozen file",
104 reason: "source ended before the selected prefix",
105 });
106 }
107 reader.check_unchanged().await?;
108 let session = self.erased();
109 Ok(FrozenFile {
110 backing,
111 length: completion.bytes,
112 sha256: digest.finalize().into(),
113 session,
114 })
115 }
116}
117
118fn task_error(error: tokio::task::JoinError) -> crate::Error {
123 crate::Error::backend("freeze opened file", crate::BackendFailure::Permanent, error)
124}
125
126