Prechádzať zdrojové kódy

fix(import): prevent sqlite disk I/O error by validating temp DB then swapping

mhsanaei 1 týždeň pred
rodič
commit
d8523bbdac
2 zmenil súbory, kde vykonal 46 pridanie a 6 odobranie
  1. 27 0
      database/db.go
  2. 19 6
      web/service/server.go

+ 27 - 0
database/db.go

@@ -4,6 +4,7 @@ package database
 
 import (
 	"bytes"
+	"errors"
 	"io"
 	"io/fs"
 	"log"
@@ -199,3 +200,29 @@ func Checkpoint() error {
 	}
 	return nil
 }
+
+// ValidateSQLiteDB opens the provided sqlite DB path with a throw-away connection
+// and runs a PRAGMA integrity_check to ensure the file is structurally sound.
+// It does not mutate global state or run migrations.
+func ValidateSQLiteDB(dbPath string) error {
+	if _, err := os.Stat(dbPath); err != nil { // file must exist
+		return err
+	}
+	gdb, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{Logger: logger.Discard})
+	if err != nil {
+		return err
+	}
+	sqlDB, err := gdb.DB()
+	if err != nil {
+		return err
+	}
+	defer sqlDB.Close()
+	var res string
+	if err := gdb.Raw("PRAGMA integrity_check;").Scan(&res).Error; err != nil {
+		return err
+	}
+	if res != "ok" {
+		return errors.New("sqlite integrity check failed: " + res)
+	}
+	return nil
+}

+ 19 - 6
web/service/server.go

@@ -942,13 +942,26 @@ func (s *ServerService) ImportDB(file multipart.File) error {
 		return common.NewErrorf("Error saving db: %v", err)
 	}
 
-	// Check if we can init the db or not
-	if err = database.InitDB(tempPath); err != nil {
-		return common.NewErrorf("Error checking db: %v", err)
+	// Close temp file before opening via sqlite
+	if err = tempFile.Close(); err != nil {
+		return common.NewErrorf("Error closing temporary db file: %v", err)
 	}
+	tempFile = nil
 
-	// Stop Xray
-	s.StopXrayService()
+	// Validate integrity (no migrations / side effects)
+	if err = database.ValidateSQLiteDB(tempPath); err != nil {
+		return common.NewErrorf("Invalid or corrupt db file: %v", err)
+	}
+
+	// Stop Xray (ignore error but log)
+	if errStop := s.StopXrayService(); errStop != nil {
+		logger.Warningf("Failed to stop Xray before DB import: %v", errStop)
+	}
+
+	// Close existing DB to release file locks (especially on Windows)
+	if errClose := database.CloseDB(); errClose != nil {
+		logger.Warningf("Failed to close existing DB before replacement: %v", errClose)
+	}
 
 	// Backup the current database for fallback
 	fallbackPath := fmt.Sprintf("%s.backup", config.GetDBPath())
@@ -983,7 +996,7 @@ func (s *ServerService) ImportDB(file multipart.File) error {
 		return common.NewErrorf("Error moving db file: %v", err)
 	}
 
-	// Migrate DB
+	// Open & migrate new DB
 	if err = database.InitDB(config.GetDBPath()); err != nil {
 		if errRename := os.Rename(fallbackPath, config.GetDBPath()); errRename != nil {
 			return common.NewErrorf("Error migrating db and restoring fallback: %v", errRename)