Add a check option to garbage collect DB files
All checks were successful
Alpine 3.20 Success
Debian Bookworm Success

This commit is contained in:
Přemysl Eric Janouch 2024-12-21 12:18:54 +01:00
parent 615af97043
commit e895beadb7
Signed by: p
GPG Key ID: A0420B94F92B9493

47
main.go
View File

@ -2148,36 +2148,54 @@ func collectFileListing(root string) (paths []string, err error) {
return return
} }
func checkFiles(root, suffix string, hashes []string) (bool, []string, error) { func checkFiles(gc bool,
root, suffix string, hashes []string) (bool, []string, error) {
db := hashesToFileListing(root, suffix, hashes) db := hashesToFileListing(root, suffix, hashes)
fs, err := collectFileListing(root) fs, err := collectFileListing(root)
if err != nil { if err != nil {
return false, nil, err return false, nil, err
} }
iDB, iFS, ok, intersection := 0, 0, true, []string{} // There are two legitimate cases of FS-only database files:
// 1. There is no code to unlink images at all
// (although sync should create orphan records for everything).
// 2. thumbnail: failures may result in an unreferenced garbage image.
ok := true
onlyDB := func(path string) {
ok = false
fmt.Printf("only in DB: %s\n", path)
}
onlyFS := func(path string) {
if !gc {
ok = false
fmt.Printf("only in FS: %s\n", path)
} else if err := os.Remove(path); err != nil {
ok = false
fmt.Printf("only in FS (removing failed): %s: %s\n", path, err)
} else {
fmt.Printf("only in FS (removing): %s\n", path)
}
}
iDB, iFS, intersection := 0, 0, []string{}
for iDB < len(db) && iFS < len(fs) { for iDB < len(db) && iFS < len(fs) {
if db[iDB] == fs[iFS] { if db[iDB] == fs[iFS] {
intersection = append(intersection, db[iDB]) intersection = append(intersection, db[iDB])
iDB++ iDB++
iFS++ iFS++
} else if db[iDB] < fs[iFS] { } else if db[iDB] < fs[iFS] {
ok = false onlyDB(db[iDB])
fmt.Printf("only in DB: %s\n", db[iDB])
iDB++ iDB++
} else { } else {
ok = false onlyFS(fs[iFS])
fmt.Printf("only in FS: %s\n", fs[iFS])
iFS++ iFS++
} }
} }
for _, path := range db[iDB:] { for _, path := range db[iDB:] {
ok = false onlyDB(path)
fmt.Printf("only in DB: %s\n", path)
} }
for _, path := range fs[iFS:] { for _, path := range fs[iFS:] {
ok = false onlyFS(path)
fmt.Printf("only in FS: %s\n", path)
} }
return ok, intersection, nil return ok, intersection, nil
} }
@ -2225,6 +2243,7 @@ func checkHashes(paths []string) (bool, error) {
// cmdCheck carries out various database consistency checks. // cmdCheck carries out various database consistency checks.
func cmdCheck(fs *flag.FlagSet, args []string) error { func cmdCheck(fs *flag.FlagSet, args []string) error {
full := fs.Bool("full", false, "verify image hashes") full := fs.Bool("full", false, "verify image hashes")
gc := fs.Bool("gc", false, "garbage collect database files")
if err := fs.Parse(args); err != nil { if err := fs.Parse(args); err != nil {
return err return err
} }
@ -2261,13 +2280,13 @@ func cmdCheck(fs *flag.FlagSet, args []string) error {
// This somewhat duplicates {image,thumb}Path(). // This somewhat duplicates {image,thumb}Path().
log.Println("checking SQL against filesystem") log.Println("checking SQL against filesystem")
okImages, intersection, err := checkFiles( okImages, intersection, err := checkFiles(*gc,
filepath.Join(galleryDirectory, nameOfImageRoot), "", allSHA1) filepath.Join(galleryDirectory, nameOfImageRoot), "", allSHA1)
if err != nil { if err != nil {
return err return err
} }
okThumbs, _, err := checkFiles( okThumbs, _, err := checkFiles(*gc,
filepath.Join(galleryDirectory, nameOfThumbRoot), ".webp", thumbSHA1) filepath.Join(galleryDirectory, nameOfThumbRoot), ".webp", thumbSHA1)
if err != nil { if err != nil {
return err return err
@ -2276,11 +2295,11 @@ func cmdCheck(fs *flag.FlagSet, args []string) error {
ok = false ok = false
} }
log.Println("checking for dead symlinks") log.Println("checking for dead symlinks (should become orphans on sync)")
for _, path := range intersection { for _, path := range intersection {
if _, err := os.Stat(path); err != nil { if _, err := os.Stat(path); err != nil {
ok = false ok = false
fmt.Printf("%s: %s\n", path, err) fmt.Printf("%s: %s\n", path, err.(*os.PathError).Unwrap())
} }
} }