Files
Kematian-Standalone/native/recovery/db/db.go
T
2026-08-27 11:21:43 -06:00

123 lines
2.8 KiB
Go

package db
import (
"context"
"database/sql"
"fmt"
"os"
"path/filepath"
"strings"
"recovery/recovery/platform"
sqlite3 "github.com/mattn/go-sqlite3"
)
func OpenDatabase(dbPath string, pids []uint32) (*sql.DB, error) {
cleanPath := dbPath
if i := strings.IndexByte(dbPath, '?'); i >= 0 {
cleanPath = dbPath[:i]
}
hasWAL := false
if wal, err := os.Stat(cleanPath + "-wal"); err == nil && wal.Size() > 0 {
hasWAL = true
}
if !hasWAL {
uri := fmt.Sprintf("file:%s?mode=ro&nolock=1&immutable=1", dbPath)
if db, err := sql.Open("sqlite3", uri); err == nil {
if err := db.Ping(); err == nil {
logf("opened %s via immutable snapshot", dbPath)
return db, nil
}
db.Close()
}
}
if snapshot, cloneErr := cloneSnapshot(cleanPath, pids); cloneErr == nil {
logf("opened %s via cloned snapshot (%d bytes)", dbPath, len(snapshot))
return OpenDatabaseFromBytes(snapshot)
} else {
logf("clone failed for %s: %v; falling back to direct read", dbPath, cloneErr)
}
data, err := platform.ReadLockedFile(cleanPath, pids)
if err != nil {
return nil, fmt.Errorf("open %s: %w", dbPath, err)
}
logf("opened %s via injected ReadLockedFile (%d bytes)", dbPath, len(data))
return OpenDatabaseFromBytes(data)
}
func cloneSnapshot(dbPath string, pids []uint32) ([]byte, error) {
tmp, err := os.MkdirTemp("", "kematian_db_*")
if err != nil {
return nil, err
}
defer os.RemoveAll(tmp)
clonePath := filepath.Join(tmp, filepath.Base(dbPath))
mainData, err := platform.ReadLockedFile(dbPath, pids)
if err != nil {
return nil, err
}
if err := os.WriteFile(clonePath, mainData, 0600); err != nil {
return nil, err
}
for _, suffix := range []string{"-wal", "-journal"} {
src := dbPath + suffix
if info, err := os.Stat(src); err == nil && info.Size() > 0 {
if data, err := platform.ReadLockedFile(src, pids); err == nil {
if err := os.WriteFile(clonePath+suffix, data, 0600); err != nil {
return nil, err
}
}
}
}
d, err := sql.Open("sqlite3", clonePath)
if err != nil {
return nil, err
}
d.SetMaxOpenConns(1)
if _, err := d.Exec("PRAGMA journal_mode=DELETE"); err != nil {
d.Close()
return nil, err
}
d.Close()
return os.ReadFile(clonePath)
}
func OpenDatabaseFromBytes(data []byte) (*sql.DB, error) {
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
return nil, err
}
db.SetMaxOpenConns(1)
conn, err := db.Conn(context.Background())
if err != nil {
db.Close()
return nil, err
}
err = conn.Raw(func(driverConn interface{}) error {
sqliteConn, ok := driverConn.(*sqlite3.SQLiteConn)
if !ok {
return fmt.Errorf("not a sqlite3 connection")
}
return sqliteConn.Deserialize(data, "main")
})
conn.Close()
if err != nil {
db.Close()
return nil, fmt.Errorf("deserialize: %w", err)
}
return db, nil
}