// GoCheese -- Python private package repository and caching proxy // Copyright (C) 2019-2024 Sergey Matveev // // 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, version 3 of the License. // // 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. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . package main import ( "bufio" "bytes" "crypto/sha256" "errors" "fmt" "io" "io/fs" "log" "os" "path/filepath" "runtime" "strings" "sync" ) func checker(jobs chan string, isGood *bool, workers *sync.WaitGroup) { hasherBLAKE2b256 := blake2b256New() hasherSHA256 := sha256.New() digestBuf := make([]byte, 32) defer workers.Done() var fd *os.File var br bufio.Reader var err error var digest []byte for fn := range jobs { fd, err = os.Open(fn) if err != nil { fmt.Println("ERR", fn, err) *isGood = false continue } digest, err = os.ReadFile(fn + "." + HashAlgoBLAKE2b256) if err != nil { if !errors.Is(err, fs.ErrNotExist) { fmt.Println("ERR", fn+"."+HashAlgoBLAKE2b256, err) *isGood = false } goto NoBLAKE2b256 } hasherBLAKE2b256.Reset() br.Reset(fd) _, err = io.Copy(hasherBLAKE2b256, &br) fd.Seek(0, io.SeekStart) if err != nil { fmt.Println("ERR", fn+"."+HashAlgoBLAKE2b256, err) *isGood = false goto NoBLAKE2b256 } if bytes.Equal(hasherBLAKE2b256.Sum(digestBuf[:0]), digest) { fmt.Println("GOOD", fn+"."+HashAlgoBLAKE2b256) } else { fmt.Println("BAD", fn+"."+HashAlgoBLAKE2b256) *isGood = false } NoBLAKE2b256: digest, err = os.ReadFile(fn + "." + HashAlgoSHA256) if err != nil { if !errors.Is(err, fs.ErrNotExist) { fmt.Println("ERR", fn+"."+HashAlgoSHA256, err) *isGood = false } fd.Close() continue } hasherSHA256.Reset() br.Reset(fd) _, err = io.Copy(hasherSHA256, &br) fd.Close() if err != nil { fmt.Println("ERR", fn+"."+HashAlgoBLAKE2b256, err) *isGood = false continue } if bytes.Equal(hasherSHA256.Sum(digestBuf[:0]), digest) { fmt.Println("GOOD", fn+"."+HashAlgoSHA256) } else { fmt.Println("BAD", fn+"."+HashAlgoSHA256) *isGood = false } } } func goodIntegrity() bool { isGood := true jobs := make(chan string, 1<<10) var workers sync.WaitGroup workers.Add(runtime.NumCPU()) for i := 0; i < runtime.NumCPU(); i++ { go checker(jobs, &isGood, &workers) } fd, err := os.Open(Root) if err != nil { log.Fatal(err) } dirs, err := fd.ReadDir(0) fd.Close() if err != nil { log.Fatal(err) } for _, dir := range dirs { fd, err = os.Open(filepath.Join(Root, dir.Name())) if err != nil { log.Fatal(err) } files, err := fd.ReadDir(0) fd.Close() if err != nil { log.Fatal(err) } for _, file := range files { for _, ext := range KnownExts { if strings.HasSuffix(file.Name(), ext) { jobs <- filepath.Join(Root, dir.Name(), file.Name()) } } } } close(jobs) workers.Wait() return isGood }