1use crate::manifest::{
2 active_durable_file_names, current_sync_snapshot_metadata, BackupManifest, FileEntry,
3};
4use powdb_storage::catalog::Catalog;
5use std::io;
6use std::path::Path;
7use std::time::{SystemTime, UNIX_EPOCH};
8
9pub fn full_backup(catalog: &mut Catalog, dest: &Path) -> io::Result<BackupManifest> {
17 powdb_sync::checkpoint_preserving_retained_segments_if_enabled(catalog)?;
18 let source_lsn = catalog.max_lsn();
19 let catalog_version = catalog.active_catalog_version();
20 let src = catalog.data_dir().to_path_buf();
21 let sync = current_sync_snapshot_metadata(&src, source_lsn, catalog_version)?;
22 crate::secure::create_dir_secure(dest)?;
23
24 let mut files = Vec::new();
25 for name in active_durable_file_names(catalog) {
26 let source_path = src.join(&name);
27 if !source_path.exists() {
28 if name == powdb_storage::catalog::CATALOG_LSN_FILE {
32 continue;
33 }
34 return Err(io::Error::new(
35 io::ErrorKind::NotFound,
36 format!("catalog references missing durable file {name}"),
37 ));
38 }
39 if !source_path.is_file() {
40 return Err(io::Error::new(
41 io::ErrorKind::InvalidData,
42 format!("catalog durable path is not a file: {name}"),
43 ));
44 }
45 let bytes = std::fs::read(source_path)?;
46 let hash = blake3::hash(&bytes).to_hex().to_string();
47 crate::secure::write_file_secure(&dest.join(&name), &bytes)?;
48 files.push(FileEntry {
49 name,
50 len: bytes.len() as u64,
51 blake3_hex: hash,
52 });
53 }
54 files.sort_by(|a, b| a.name.cmp(&b.name));
55
56 let manifest = BackupManifest {
57 format_version: BackupManifest::FORMAT_VERSION,
58 created_unix_secs: SystemTime::now()
59 .duration_since(UNIX_EPOCH)
60 .map(|d| d.as_secs())
61 .unwrap_or(0),
62 source_lsn,
63 catalog_version,
64 sync,
65 files,
66 };
67 manifest.write(dest)?;
68 Ok(manifest)
69}