From 375e468a8e375fe9c33c0f0f2a80c30193beeb83 Mon Sep 17 00:00:00 2001 From: Steven Polley Date: Fri, 30 Jun 2023 19:50:47 -0600 Subject: [PATCH] separate file handling functions from main file --- main.go | 117 +++--------------------------------------------- processFiles.go | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 112 deletions(-) create mode 100644 processFiles.go diff --git a/main.go b/main.go index d655c70..2d770b5 100644 --- a/main.go +++ b/main.go @@ -1,15 +1,12 @@ package main import ( - "crypto/sha256" "encoding/json" "fmt" - "io" "io/fs" "log" "net/http" "os" - "strings" "sync" ) @@ -31,9 +28,9 @@ type HTTPResponseJSON struct { // Caches data about available ROMs in memory so we don't need to reference the filesystem for each request type ROMCache struct { - ROMs []LineageOSROM `json:"roms"` - Cached map[string]bool `json:"-"` // to quickly lookup if a file is already cached - sync.Mutex `json:"-"` // We have multiple goroutines that may be accessing this data simultaneously, so we much lock / unlock it to prevent race conditions + ROMs []LineageOSROM `json:"roms"` + Cached map[string]bool `json:"-"` // to quickly lookup if a file is already cached + sync.RWMutex `json:"-"` // We have multiple goroutines that may be accessing this data simultaneously, so we much lock / unlock it to prevent race conditions } const ( @@ -161,9 +158,9 @@ func updateROMCache() { // save file to disk for next startup so we don't have to rehash all the files again wg.Wait() - romCache.Lock() + romCache.RLock() romCacheJson, err := json.Marshal(romCache) - romCache.Unlock() + romCache.RUnlock() if err != nil { log.Printf("failed to marshal romCache to json: %v", err) return @@ -181,58 +178,6 @@ func updateROMCache() { } } -// returns true if new builds were moved -func moveBuildArtifacts() bool { - - f, err := os.Open(buildOutDirectory) - if err != nil { - log.Printf("failed to open ROM directory: %v", err) - return false - } - defer f.Close() - files, err := f.ReadDir(0) - if err != nil { - log.Printf("failed to read files in directory: %v", err) - return false - } - - var newROMs bool - - for _, v := range files { - if isLineageROM, _ := isLineageROMZip(v); !isLineageROM { // skip files that aren't LineageOS ROMs - continue - } - - newROMs = true - log.Printf("new build found - moving file %s", v.Name()) - romCache.Lock() // lock to prevent multiple concurrent goroutines moving the same file - err := moveBuildFile(fmt.Sprintf("%s/%s", buildOutDirectory, v.Name()), fmt.Sprintf("%s/%s", romDirectory, v.Name())) - romCache.Unlock() - if err != nil { - log.Printf("failed to move file '%s' from out to rom directory: %v", v.Name(), err) - continue - } - } - - return newROMs -} - -// false if a file is not a LineageOS ROM .zip file -// no formal validation is performed - only file naming convention is checked -// also returns a lineage ROM's filename sliced and delimited by -'s -// Example filename: lineage-20.0-20230604-UNOFFICIAL-sunfish.zip -func isLineageROMZip(v fs.DirEntry) (bool, []string) { - - // skip directories, non .zip files and files that don't begin with lineage- - if v.Type().IsDir() || !strings.HasSuffix(v.Name(), ".zip") || !strings.HasPrefix(v.Name(), "lineage-") { - return false, nil - } - - splitName := strings.Split(v.Name(), "-") - // expect 5 dashes - return len(splitName) == 5, splitName -} - // http - GET / // Writes JSON response for the updater app to know what versions are available to download func lineageOSROMListHandler(w http.ResponseWriter, r *http.Request) { @@ -256,55 +201,3 @@ func lineageOSROMListHandler(w http.ResponseWriter, r *http.Request) { w.Write(b) } - -// Returns a sha256 hash of a file located at the path provided -func hashFile(filename string) (string, error) { - f, err := os.Open(filename) - if err != nil { - return "", fmt.Errorf("failed to open file '%s': %v: ", filename, err) - } - defer f.Close() - - h := sha256.New() - if _, err := io.Copy(h, f); err != nil { - return "", fmt.Errorf("failed to copy data from file to hash function: %v", err) - } - - return fmt.Sprintf("%x", h.Sum(nil)), nil -} - -// A custom "move file" function because in docker container the mounted folders are different overlay filesystems -// Instead of os.Rename, we must copy and delete -func moveBuildFile(src, dst string) error { - sourceFileStat, err := os.Stat(src) - if err != nil { - return err - } - - if !sourceFileStat.Mode().IsRegular() { - return fmt.Errorf("%s is not a regular file", src) - } - - source, err := os.Open(src) - if err != nil { - return err - } - defer source.Close() - - destination, err := os.Create(dst) - if err != nil { - return err - } - defer destination.Close() - _, err = io.Copy(destination, source) - if err != nil { - return fmt.Errorf("failed to copy file: %v", err) - } - - err = os.Remove(src) - if err != nil { - return fmt.Errorf("failed to delete source file after copy: %v", err) - } - - return nil -} diff --git a/processFiles.go b/processFiles.go new file mode 100644 index 0000000..a7b8944 --- /dev/null +++ b/processFiles.go @@ -0,0 +1,117 @@ +package main + +import ( + "crypto/sha256" + "fmt" + "io" + "io/fs" + "log" + "os" + "strings" +) + +// Searches the build toolchain output directory for new LineageOS builds. +// Any new builds are moved into the public http server directory. +// Returns true if new builds were moved +func moveBuildArtifacts() bool { + + f, err := os.Open(buildOutDirectory) + if err != nil { + log.Printf("failed to open ROM directory: %v", err) + return false + } + defer f.Close() + files, err := f.ReadDir(0) + if err != nil { + log.Printf("failed to read files in directory: %v", err) + return false + } + + var newROMs bool + + for _, v := range files { + if isLineageROM, _ := isLineageROMZip(v); !isLineageROM { // skip files that aren't LineageOS ROMs + continue + } + + newROMs = true + log.Printf("new build found - moving file %s", v.Name()) + romCache.Lock() // lock to prevent multiple concurrent goroutines moving the same file + err := moveBuildFile(fmt.Sprintf("%s/%s", buildOutDirectory, v.Name()), fmt.Sprintf("%s/%s", romDirectory, v.Name())) + romCache.Unlock() + if err != nil { + log.Printf("failed to move file '%s' from out to rom directory: %v", v.Name(), err) + continue + } + } + + return newROMs +} + +// Returns a sha256 hash of a file located at the path provided +func hashFile(filename string) (string, error) { + f, err := os.Open(filename) + if err != nil { + return "", fmt.Errorf("failed to open file '%s': %v: ", filename, err) + } + defer f.Close() + + h := sha256.New() + if _, err := io.Copy(h, f); err != nil { + return "", fmt.Errorf("failed to copy data from file to hash function: %v", err) + } + + return fmt.Sprintf("%x", h.Sum(nil)), nil +} + +// false if a file is not a LineageOS ROM .zip file +// no formal validation is performed - only file naming convention is checked +// also returns a lineage ROM's filename sliced and delimited by -'s +// Example filename: lineage-20.0-20230604-UNOFFICIAL-sunfish.zip +func isLineageROMZip(v fs.DirEntry) (bool, []string) { + + // skip directories, non .zip files and files that don't begin with lineage- + if v.Type().IsDir() || !strings.HasSuffix(v.Name(), ".zip") || !strings.HasPrefix(v.Name(), "lineage-") { + return false, nil + } + + splitName := strings.Split(v.Name(), "-") + // expect 5 dashes + return len(splitName) == 5, splitName +} + +// A custom "move file" function because in docker container the mounted folders are different overlay filesystems +// Instead of os.Rename, we must copy and delete +func moveBuildFile(src, dst string) error { + sourceFileStat, err := os.Stat(src) + if err != nil { + return err + } + + if !sourceFileStat.Mode().IsRegular() { + return fmt.Errorf("%s is not a regular file", src) + } + + source, err := os.Open(src) + if err != nil { + return err + } + defer source.Close() + + destination, err := os.Create(dst) + if err != nil { + return err + } + defer destination.Close() + _, err = io.Copy(destination, source) + if err != nil { + return fmt.Errorf("failed to copy file: %v", err) + } + + err = os.Remove(src) + if err != nil { + return fmt.Errorf("failed to delete source file after copy: %v", err) + } + + return nil +}