// Copyright (C) 2024 Simon Quigley // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 3 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. package main import ( "fmt" "os" "path/filepath" "strings" "github.com/snapcore/snapd/store" ) // cleanUpFiles removes partial, old, and orphaned snap and assertion files from the download and assertions directories. func cleanUpFiles(snapsDir string, assertionsDir string) { verboseLog("Starting cleanup process...") // Load the seed.yaml data seedData := loadSeedData() // Create a map of valid snap and assertion files based on seed.yaml validSnaps := make(map[string]bool) validAssertions := make(map[string]bool) // Populate valid snaps and assertions from the seed.yaml data for _, snap := range seedData.Snaps { // Ensure correct extraction of revision revision := extractRevisionFromFile(snap.File) if revision == "" { verboseLog("Failed to extract revision from file name: %s", snap.File) continue } snapFileName := fmt.Sprintf("%s_%s.snap", snap.Name, revision) assertionFileName := fmt.Sprintf("%s_%s.assert", snap.Name, revision) validSnaps[snapFileName] = true validAssertions[assertionFileName] = true } // Log valid snaps and assertions verboseLog("Valid Snaps: %v", validSnaps) verboseLog("Valid Assertions: %v", validAssertions) // Remove outdated or partial snap files files, err := os.ReadDir(snapsDir) if err != nil { verboseLog("Error reading snaps directory for cleanup: %v", err) } else { for _, file := range files { filePath := filepath.Join(snapsDir, file.Name()) if strings.HasSuffix(file.Name(), ".partial") || strings.HasSuffix(file.Name(), ".delta") { verboseLog("Removing partial/delta file: %s\n", filePath) if err := os.Remove(filePath); err != nil { verboseLog("Failed to remove file %s: %v", filePath, err) } else if verbose { verboseLog("Removed partial/delta file: %s", filePath) } } else if strings.HasSuffix(file.Name(), ".snap") { if !validSnaps[file.Name()] { verboseLog("Removing outdated or orphaned snap file: %s\n", filePath) if err := os.Remove(filePath); err != nil { verboseLog("Failed to remove snap file %s: %v", filePath, err) } else if verbose { verboseLog("Removed snap file: %s", filePath) } } else { verboseLog("Snap file %s is valid and retained.\n", file.Name()) } } } } // Remove orphaned assertion files files, err = os.ReadDir(assertionsDir) if err != nil { verboseLog("Error reading assertions directory for cleanup: %v", err) } else { for _, file := range files { filePath := filepath.Join(assertionsDir, file.Name()) if strings.HasSuffix(file.Name(), ".assert") { if !validAssertions[file.Name()] { verboseLog("Removing orphaned assertion file: %s\n", filePath) if err := os.Remove(filePath); err != nil { verboseLog("Failed to remove assertion file %s: %v", filePath, err) } else if verbose { verboseLog("Removed assertion file: %s", filePath) } } else { verboseLog("Assertion file %s is valid and retained.\n", file.Name()) } } } } verboseLog("Cleanup process completed.") } // removeOrphanedFiles deletes the assertion and snap file corresponding to the removed snap. func removeOrphanedFiles(snapName string, revision int, assertionsDir string, snapsDir string) { assertionFilePath := filepath.Join(assertionsDir, fmt.Sprintf("%s_%d.assert", snapName, revision)) snapFilePath := filepath.Join(snapsDir, fmt.Sprintf("%s_%d.snap", snapName, revision)) if fileExists(assertionFilePath) { err := os.Remove(assertionFilePath) if err != nil { verboseLog("Failed to remove assertion file %s: %v", assertionFilePath, err) } else { verboseLog("Removed assertion file: %s", assertionFilePath) } } else { verboseLog("Assertion file %s does not exist. No action taken.", assertionFilePath) } if fileExists(snapFilePath) { err := os.Remove(snapFilePath) if err != nil { verboseLog("Failed to remove snap file %s: %v", snapFilePath, err) } else { verboseLog("Removed snap file: %s", snapFilePath) } } else { verboseLog("Snap file %s does not exist. No action taken.", snapFilePath) } } // cleanUpCurrentSnaps removes snaps from currentSnaps that are not marked as required. func cleanUpCurrentSnaps(assertionsDir string, snapsDir string) { var filteredSnaps []*store.CurrentSnap for _, snap := range currentSnaps { if requiredSnaps[snap.InstanceName] { filteredSnaps = append(filteredSnaps, snap) } else { verboseLog("Removing unnecessary snap: %s\n", snap.InstanceName) removeOrphanedFiles(snap.InstanceName, snap.Revision.N, assertionsDir, snapsDir) } } currentSnaps = filteredSnaps // Log the updated currentSnaps verboseLog("Filtered currentSnaps after cleanup:") for _, snap := range currentSnaps { verboseLog("- %s_%d.snap", snap.InstanceName, snap.Revision.N) } } // removeStateJson removes the state.json file if it exists func removeStateJson(stateJsonPath string) { if _, err := os.Stat(stateJsonPath); err == nil { if err := os.Remove(stateJsonPath); err != nil { verboseLog("Failed to remove state.json: %v", err) } else if verbose { verboseLog("Removed state.json at %s", stateJsonPath) } } }