1use super::storage_backup_list::list_backup_descriptors;
12use super::storage_file_fs::{
13 create_real_directory_all, open_regular_file, path_exists_no_follow, path_is_real_directory,
14 resolve_under_root, tmp_path_for, write_atomic_file,
15};
16use super::storage_tree::{bounded_tree_entries, StorageTreeEntryKind};
17use super::*;
18use crate::storage::RemoteAuthStorageClient;
19use appcore_security::{TokenClaims, TokenProvider};
20use std::fs;
21use std::io::Read;
22use std::path::{Path, PathBuf};
23
24#[derive(Debug, Clone)]
26pub struct FileStorageProvider {
27 pub(super) storage_path: PathBuf,
28 pub(super) backup_path: PathBuf,
29 opened: bool,
30}
31
32impl FileStorageProvider {
33 pub fn new(storage_path: impl Into<PathBuf>, backup_path: impl Into<PathBuf>) -> Self {
35 Self {
36 storage_path: storage_path.into(),
37 backup_path: backup_path.into(),
38 opened: false,
39 }
40 }
41
42 pub fn create_dirs(&self) -> StorageResult<()> {
44 if let Some(parent) = self.storage_path.parent() {
45 fs::create_dir_all(parent).map_err(|_| StorageError::NotAvailable)?;
46 }
47 create_real_directory_all(&self.backup_path)?;
48 if path_exists_no_follow(&self.storage_path)? {
49 super::storage_file_fs::ensure_real_directory(&self.storage_path)?;
50 }
51 self.recover_snapshot_restore()?;
52 create_real_directory_all(&self.storage_path)?;
53 Ok(())
54 }
55
56 pub fn write_bytes(&self, path: &str, bytes: &[u8]) -> StorageResult<()> {
58 self.write_bytes_atomic(path, bytes)
59 }
60
61 pub fn write_bytes_atomic(&self, path: &str, bytes: &[u8]) -> StorageResult<()> {
63 self.resolve_storage(path)?;
64 self.with_storage_lock(|| {
65 let full = self.resolve_storage(path)?;
66 if let Some(parent) = full.parent() {
67 fs::create_dir_all(parent).map_err(|_| StorageError::NotAvailable)?;
68 }
69 let full = self.resolve_storage(path)?;
70 let tmp = tmp_path_for(&full);
71 write_atomic_file(&tmp, &full, bytes)
72 .map_err(|_| StorageError::TransactionFailed(path.to_string()))
73 })?;
74 Ok(())
75 }
76
77 pub fn write_secure_bytes<P: TokenProvider>(
79 &self,
80 path: &str,
81 bytes: &[u8],
82 provider: &P,
83 claims: &TokenClaims,
84 ) -> StorageResult<()> {
85 let sealed = provider
86 .seal(bytes, claims)
87 .map_err(|_| StorageError::SecurityFailed(path.to_string()))?;
88 self.write_bytes_atomic(path, &sealed)
89 }
90
91 pub fn write_auth_required_bytes<P: TokenProvider>(
93 &self,
94 path: &str,
95 bytes: &[u8],
96 provider: Option<&P>,
97 claims: &TokenClaims,
98 ) -> StorageResult<()> {
99 let provider = require_auth_provider(path, provider)?;
100 self.write_secure_bytes(path, bytes, provider, claims)
101 }
102
103 pub fn write_remote_auth_required_bytes(
105 &self,
106 path: &str,
107 bytes: &[u8],
108 client: Option<&RemoteAuthStorageClient>,
109 ) -> StorageResult<()> {
110 let client = require_remote_auth_client(path, client)?;
111 let sealed = client.seal_resource(path, bytes)?;
112 self.write_bytes_atomic(path, &sealed)
113 }
114
115 pub fn read_bytes(&self, path: &str) -> StorageResult<Vec<u8>> {
117 self.resolve_storage(path)?;
118 self.with_storage_lock(|| {
119 let full = self.resolve_storage(path)?;
120 let mut file = open_regular_file(&full)
121 .map_err(|_| StorageError::RepositoryNotFound(path.to_string()))?;
122 let mut bytes = Vec::new();
123 file.read_to_end(&mut bytes)
124 .map_err(|_| StorageError::RepositoryNotFound(path.to_string()))?;
125 Ok(bytes)
126 })
127 }
128
129 pub(super) fn read_bytes_bounded(&self, path: &str, max_bytes: u64) -> StorageResult<Vec<u8>> {
130 self.resolve_storage(path)?;
131 self.with_storage_lock(|| {
132 let full = self.resolve_storage(path)?;
133 let file = open_regular_file(&full)
134 .map_err(|_| StorageError::RepositoryNotFound(path.to_string()))?;
135 let metadata = file
136 .metadata()
137 .map_err(|_| StorageError::RepositoryNotFound(path.to_string()))?;
138 if metadata.len() > max_bytes {
139 return Err(StorageError::TransactionFailed(path.to_string()));
140 }
141 let capacity = usize::try_from(metadata.len())
142 .map_err(|_| StorageError::TransactionFailed(path.to_string()))?;
143 let read_limit = max_bytes
144 .checked_add(1)
145 .ok_or_else(|| StorageError::TransactionFailed(path.to_string()))?;
146 let mut bytes = Vec::with_capacity(capacity);
147 file.take(read_limit)
148 .read_to_end(&mut bytes)
149 .map_err(|_| StorageError::RepositoryNotFound(path.to_string()))?;
150 if bytes.len() as u64 > max_bytes {
151 return Err(StorageError::TransactionFailed(path.to_string()));
152 }
153 Ok(bytes)
154 })
155 }
156
157 pub fn read_secure_bytes<P: TokenProvider>(
159 &self,
160 path: &str,
161 provider: &P,
162 claims: &TokenClaims,
163 ) -> StorageResult<Vec<u8>> {
164 let sealed = self.read_bytes(path)?;
165 provider
166 .open(&sealed, claims)
167 .map_err(|_| StorageError::SecurityFailed(path.to_string()))
168 }
169
170 pub fn read_auth_required_bytes<P: TokenProvider>(
172 &self,
173 path: &str,
174 provider: Option<&P>,
175 claims: &TokenClaims,
176 ) -> StorageResult<Vec<u8>> {
177 let provider = require_auth_provider(path, provider)?;
178 self.read_secure_bytes(path, provider, claims)
179 }
180
181 pub fn read_remote_auth_required_bytes(
183 &self,
184 path: &str,
185 client: Option<&RemoteAuthStorageClient>,
186 ) -> StorageResult<Vec<u8>> {
187 let client = require_remote_auth_client(path, client)?;
188 let sealed = self.read_bytes(path)?;
189 client.open_resource(path, &sealed)
190 }
191
192 pub fn exists(&self, path: &str) -> StorageResult<bool> {
194 self.resolve_storage(path)?;
195 self.with_storage_lock(|| {
196 let full = self.resolve_storage(path)?;
197 path_exists_no_follow(&full)
198 })
199 }
200
201 pub fn backup_file(&self, source: &str, backup_name: &str) -> StorageResult<()> {
203 self.backup_file_atomic(source, backup_name)
204 }
205
206 pub fn backup_file_atomic(&self, source: &str, backup_name: &str) -> StorageResult<()> {
208 self.resolve_storage(source)?;
209 self.resolve_backup(backup_name)?;
210 self.with_storage_lock(|| {
211 let source_full = self.resolve_storage(source)?;
212 if !path_exists_no_follow(&source_full)? {
213 return Err(StorageError::RepositoryNotFound(source.to_string()));
214 }
215 let backup_full = self.resolve_backup(backup_name)?;
216 if let Some(parent) = backup_full.parent() {
217 fs::create_dir_all(parent)
218 .map_err(|_| StorageError::BackupFailed(backup_name.to_string()))?;
219 }
220 let backup_full = self.resolve_backup(backup_name)?;
221 let mut source_file = open_regular_file(&source_full)
222 .map_err(|_| StorageError::BackupFailed(backup_name.to_string()))?;
223 let mut bytes = Vec::new();
224 source_file
225 .read_to_end(&mut bytes)
226 .map_err(|_| StorageError::BackupFailed(backup_name.to_string()))?;
227 let tmp = tmp_path_for(&backup_full);
228 write_atomic_file(&tmp, &backup_full, &bytes)
229 .map_err(|_| StorageError::BackupFailed(backup_name.to_string()))
230 })?;
231 Ok(())
232 }
233
234 pub fn cleanup_temp_files(&self) -> StorageResult<usize> {
236 self.with_storage_lock(|| {
237 let mut removed = cleanup_tmp_in_dir(&self.storage_path)?;
238 removed += cleanup_tmp_in_dir(&self.backup_path)?;
239 Ok(removed)
240 })
241 }
242
243 fn resolve_storage(&self, relative: &str) -> StorageResult<PathBuf> {
244 resolve_under_root(&self.storage_path, relative)
245 }
246
247 fn resolve_backup(&self, relative: &str) -> StorageResult<PathBuf> {
248 resolve_under_root(&self.backup_path, relative)
249 }
250}
251
252fn require_auth_provider<'a, P: TokenProvider>(
253 path: &str,
254 provider: Option<&'a P>,
255) -> StorageResult<&'a P> {
256 provider.ok_or_else(|| StorageError::AuthUnavailable(path.to_string()))
257}
258
259fn require_remote_auth_client<'a>(
260 path: &str,
261 client: Option<&'a RemoteAuthStorageClient>,
262) -> StorageResult<&'a RemoteAuthStorageClient> {
263 client.ok_or_else(|| StorageError::AuthUnavailable(path.to_string()))
264}
265
266fn cleanup_tmp_in_dir(root: &Path) -> StorageResult<usize> {
267 if !path_exists_no_follow(root)? {
268 return Ok(0);
269 }
270 let mut removed = 0usize;
271 for entry in bounded_tree_entries(root)? {
272 if entry.kind == StorageTreeEntryKind::File
273 && entry
274 .path
275 .file_name()
276 .map(|name| name.to_string_lossy().ends_with(".tmp"))
277 .unwrap_or(false)
278 {
279 fs::remove_file(entry.path).map_err(|_| StorageError::NotAvailable)?;
280 removed += 1;
281 }
282 }
283 Ok(removed)
284}
285
286fn count_tmp_in_dir(root: &Path) -> StorageResult<usize> {
287 if !path_exists_no_follow(root)? {
288 return Ok(0);
289 }
290 let mut count = 0usize;
291 for entry in bounded_tree_entries(root)? {
292 if entry.kind == StorageTreeEntryKind::File
293 && entry
294 .path
295 .file_name()
296 .map(|name| name.to_string_lossy().ends_with(".tmp"))
297 .unwrap_or(false)
298 {
299 count += 1;
300 }
301 }
302 Ok(count)
303}
304
305impl StorageProvider for FileStorageProvider {
306 fn status(&self) -> StorageStatus {
307 if path_is_real_directory(&self.storage_path) && path_is_real_directory(&self.backup_path) {
308 StorageStatus::Online
309 } else {
310 StorageStatus::Offline
311 }
312 }
313
314 fn health(&self) -> StorageHealth {
315 if path_is_real_directory(&self.storage_path) && path_is_real_directory(&self.backup_path) {
316 let orphan_tmp = match (
317 count_tmp_in_dir(&self.storage_path),
318 count_tmp_in_dir(&self.backup_path),
319 ) {
320 (Ok(storage), Ok(backups)) => storage.saturating_add(backups),
321 _ => {
322 return StorageHealth {
323 status: StorageStatus::Degraded,
324 message: Some("temporary file scan failed".to_string()),
325 };
326 }
327 };
328 if orphan_tmp > 0 {
329 return StorageHealth {
330 status: StorageStatus::Degraded,
331 message: Some(format!("found {orphan_tmp} orphan temp files")),
332 };
333 }
334 return StorageHealth {
335 status: StorageStatus::Online,
336 message: None,
337 };
338 }
339 StorageHealth {
340 status: StorageStatus::Degraded,
341 message: Some("storage or backup path is missing".to_string()),
342 }
343 }
344
345 fn open(&mut self) -> StorageResult<()> {
346 self.create_dirs()?;
347 self.opened = true;
348 Ok(())
349 }
350
351 fn close(&mut self) -> StorageResult<()> {
352 self.opened = false;
353 Ok(())
354 }
355
356 fn begin_transaction(&mut self) -> StorageResult<Box<dyn Transaction>> {
357 if !self.opened {
358 return Err(StorageError::NotAvailable);
359 }
360 Err(StorageError::TransactionsUnsupported)
361 }
362
363 fn list_backups(&self) -> Vec<BackupDescriptor> {
364 self.with_storage_lock(|| Ok(list_backup_descriptors(&self.backup_path)))
365 .unwrap_or_default()
366 }
367}