|
@@ -134,13 +134,13 @@ struct EnvHandle {
|
|
|
|
|
|
|
|
} // namespace
|
|
} // namespace
|
|
|
|
|
|
|
|
-AuditReport audit(const std::string& env_path, const std::string& project_in) {
|
|
|
|
|
- AuditReport rep;
|
|
|
|
|
- rep.project = project_in.empty() ? infer_project(env_path) : project_in;
|
|
|
|
|
|
|
+namespace {
|
|
|
|
|
|
|
|
- EnvHandle eh(env_path, /*readonly=*/true);
|
|
|
|
|
- MDB_txn* txn = nullptr;
|
|
|
|
|
- ck(mdb_txn_begin(eh.env, nullptr, MDB_RDONLY, &txn), "txn_begin (audit)");
|
|
|
|
|
|
|
+// Shared body. Runs inside a read txn the caller owns; touches no descriptor
|
|
|
|
|
+// and no env lifetime, so it is safe both in-process and standalone.
|
|
|
|
|
+AuditReport audit_in_txn(MDB_txn* txn, const std::string& project) {
|
|
|
|
|
+ AuditReport rep;
|
|
|
|
|
+ rep.project = project;
|
|
|
|
|
|
|
|
const auto all = list_subdbs(txn);
|
|
const auto all = list_subdbs(txn);
|
|
|
|
|
|
|
@@ -206,10 +206,44 @@ AuditReport audit(const std::string& env_path, const std::string& project_in) {
|
|
|
m.home_occupied = (it != ids_by_subdb.end()) && it->second.count(m.id) > 0;
|
|
m.home_occupied = (it != ids_by_subdb.end()) && it->second.count(m.id) > 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- mdb_txn_abort(txn);
|
|
|
|
|
return rep;
|
|
return rep;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+} // namespace
|
|
|
|
|
+
|
|
|
|
|
+AuditReport audit_env(MDB_env* env, const std::string& project) {
|
|
|
|
|
+ if (!env) return {};
|
|
|
|
|
+ MDB_txn* txn = nullptr;
|
|
|
|
|
+ ck(mdb_txn_begin(env, nullptr, MDB_RDONLY, &txn), "txn_begin (audit_env)");
|
|
|
|
|
+ try {
|
|
|
|
|
+ AuditReport rep = audit_in_txn(txn, project);
|
|
|
|
|
+ mdb_txn_abort(txn);
|
|
|
|
|
+ return rep;
|
|
|
|
|
+ } catch (...) {
|
|
|
|
|
+ mdb_txn_abort(txn);
|
|
|
|
|
+ throw;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+AuditReport audit(const std::string& env_path, const std::string& project_in) {
|
|
|
|
|
+ const std::string project =
|
|
|
|
|
+ project_in.empty() ? infer_project(env_path) : project_in;
|
|
|
|
|
+
|
|
|
|
|
+ // Opens its own env — CLI only. See the header for why the server must
|
|
|
|
|
+ // never take this path.
|
|
|
|
|
+ EnvHandle eh(env_path, /*readonly=*/true);
|
|
|
|
|
+ MDB_txn* txn = nullptr;
|
|
|
|
|
+ ck(mdb_txn_begin(eh.env, nullptr, MDB_RDONLY, &txn), "txn_begin (audit)");
|
|
|
|
|
+ try {
|
|
|
|
|
+ AuditReport rep = audit_in_txn(txn, project);
|
|
|
|
|
+ mdb_txn_abort(txn);
|
|
|
|
|
+ return rep;
|
|
|
|
|
+ } catch (...) {
|
|
|
|
|
+ mdb_txn_abort(txn);
|
|
|
|
|
+ throw;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
RepairResult repair(const std::string& env_path,
|
|
RepairResult repair(const std::string& env_path,
|
|
|
const AuditReport& report,
|
|
const AuditReport& report,
|
|
|
bool stamp_identity) {
|
|
bool stamp_identity) {
|