nodedb_lite/storage/pagedb_storage/open/
native.rs1use std::path::Path;
6use std::sync::Arc;
7
8use pagedb::vfs::DefaultVfs;
9use pagedb::{Db, RealmId};
10
11use crate::error::LiteError;
12use crate::storage::corruption::CorruptionPolicy;
13use crate::storage::encryption::Encryption;
14use crate::storage::pagedb_storage::errors::is_corruption;
15use crate::storage::pagedb_storage::types::{PagedbStorage, lite_open_options};
16
17impl PagedbStorage<DefaultVfs> {
18 pub async fn open(path: impl AsRef<Path>, encryption: Encryption) -> Result<Self, LiteError> {
36 Self::open_with_policy(path, encryption, CorruptionPolicy::FailClosed).await
37 }
38
39 pub async fn open_with_policy(
48 path: impl AsRef<Path>,
49 encryption: Encryption,
50 policy: CorruptionPolicy,
51 ) -> Result<Self, LiteError> {
52 let path = path.as_ref();
53 let kek = crate::storage::encryption::resolve_kek_native(&encryption, path)?;
54 let snapshot_descriptor = Some(
55 super::super::snapshot::metadata::SnapshotDescriptor::from_native(&encryption, path)?,
56 );
57 let realm = RealmId::new([0u8; 16]);
58
59 let vfs = pagedb::vfs::open_default(path).map_err(LiteError::from)?;
60
61 match Db::open(vfs, kek, 4096, realm, lite_open_options()).await {
62 Ok(db) => Ok(Self {
63 db: Arc::new(db),
64 snapshot_descriptor,
65 }),
66 Err(e) if is_corruption(&e) && path.exists() => {
67 if !policy.may_discard() {
68 tracing::error!(
69 path = %path.display(),
70 error = %e,
71 "pagedb open detected corruption — refusing to open. The store has \
72 been left exactly as it was found."
73 );
74 return Err(LiteError::from(e));
75 }
76 tracing::error!(
77 path = %path.display(),
78 error = %e,
79 "pagedb open detected corruption — the caller opted into discarding the \
80 store (rename aside, recreate fresh). The new database is empty."
81 );
82 Self::discard_and_recreate(path, &encryption).await
83 }
84 Err(e) => Err(LiteError::from(e)),
85 }
86 }
87
88 pub(crate) async fn discard_and_recreate(
99 path: &Path,
100 encryption: &Encryption,
101 ) -> Result<Self, LiteError> {
102 let kek = crate::storage::encryption::resolve_kek_native(encryption, path)?;
103 let snapshot_descriptor = Some(
104 super::super::snapshot::metadata::SnapshotDescriptor::from_native(encryption, path)?,
105 );
106 let realm = RealmId::new([0u8; 16]);
107
108 let timestamp = crate::runtime::now_secs();
109 let corrupt_path = path.with_extension(format!("corrupt.{timestamp}"));
110
111 tracing::error!(
112 path = %path.display(),
113 corrupt_backup = %corrupt_path.display(),
114 "renaming corrupted pagedb store to backup and creating a fresh database"
115 );
116
117 if let Err(rename_err) = std::fs::rename(path, &corrupt_path) {
118 tracing::error!(error = %rename_err, "failed to rename corrupted pagedb directory");
119 return Err(LiteError::Storage {
120 detail: format!("pagedb corrupted and rename failed: rename={rename_err}"),
121 });
122 }
123
124 let vfs = pagedb::vfs::open_default(path).map_err(LiteError::from)?;
125 let db = Db::open(vfs, kek, 4096, realm, lite_open_options())
126 .await
127 .map_err(|e2| LiteError::Storage {
128 detail: format!(
129 "pagedb corrupted, backup saved to {}, fresh create failed: {e2}",
130 corrupt_path.display()
131 ),
132 })?;
133 Ok(Self {
134 db: Arc::new(db),
135 snapshot_descriptor,
136 })
137 }
138}