mirror of
https://github.com/syncthing/syncthing.git
synced 2025-12-23 22:18:14 -05:00
fix(db): clean files for dropped folders at startup (#10280)
This adds a cleanup stage to remove database files for folders that no longer exist on startup. Folder database files were already removed when dropping a folder, assuming that the folder database had been opened at that point. This won't be the case though when a folder is removed from the config when Syncthing isn't running, or when a folder is dropped and re-migrated in a restarted migration.
This commit is contained in:
@@ -82,6 +82,10 @@ func Open(path string, opts ...Option) (*DB, error) {
|
||||
opt(db)
|
||||
}
|
||||
|
||||
if err := db.cleanDroppedFolders(); err != nil {
|
||||
slog.Warn("Failed to clean dropped folders", slogutil.Error(err))
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
@@ -120,10 +124,9 @@ func OpenForMigration(path string) (*DB, error) {
|
||||
folderDBOpener: openFolderDBForMigration,
|
||||
}
|
||||
|
||||
// // Touch device IDs that should always exist and have a low index
|
||||
// // numbers, and will never change
|
||||
// db.localDeviceIdx, _ = db.deviceIdxLocked(protocol.LocalDeviceID)
|
||||
// db.tplInput["LocalDeviceIdx"] = db.localDeviceIdx
|
||||
if err := db.cleanDroppedFolders(); err != nil {
|
||||
slog.Warn("Failed to clean dropped folders", slogutil.Error(err))
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
@@ -8,9 +8,14 @@ package sqlite
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/syncthing/syncthing/internal/slogutil"
|
||||
)
|
||||
|
||||
func (s *DB) DropFolder(folder string) error {
|
||||
@@ -41,6 +46,37 @@ func (s *DB) ListFolders() ([]string, error) {
|
||||
return res, wrap(err)
|
||||
}
|
||||
|
||||
// cleanDroppedFolders removes old database files for folders that no longer
|
||||
// exist in the main database.
|
||||
func (s *DB) cleanDroppedFolders() error {
|
||||
// All expected folder databeses.
|
||||
var names []string
|
||||
err := s.stmt(`SELECT database_name FROM folders`).Select(&names)
|
||||
if err != nil {
|
||||
return wrap(err)
|
||||
}
|
||||
|
||||
// All folder database files on disk.
|
||||
files, err := filepath.Glob(filepath.Join(s.pathBase, "folder.*"))
|
||||
if err != nil {
|
||||
return wrap(err)
|
||||
}
|
||||
|
||||
// Any files that don't match a name in the database are removed.
|
||||
for _, file := range files {
|
||||
base := filepath.Base(file)
|
||||
inDB := slices.ContainsFunc(names, func(name string) bool { return strings.HasPrefix(base, name) })
|
||||
if !inDB {
|
||||
if err := os.Remove(file); err != nil {
|
||||
slog.Warn("Failed to remove database file for old, dropped folder", slogutil.FilePath(base))
|
||||
} else {
|
||||
slog.Info("Cleaned out database file for old, dropped folder", slogutil.FilePath(base))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// wrap returns the error wrapped with the calling function name and
|
||||
// optional extra context strings as prefix. A nil error wraps to nil.
|
||||
func wrap(err error, context ...string) error {
|
||||
|
||||
Reference in New Issue
Block a user