]> Cypherpunks.ru repositories - gocheese.git/blob - integrity.go
More convenient trusted-host
[gocheese.git] / integrity.go
1 // GoCheese -- Python private package repository and caching proxy
2 // Copyright (C) 2019-2024 Sergey Matveev <stargrave@stargrave.org>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, version 3 of the License.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16 package main
17
18 import (
19         "bufio"
20         "bytes"
21         "crypto/sha256"
22         "errors"
23         "fmt"
24         "io"
25         "io/fs"
26         "log"
27         "os"
28         "path/filepath"
29         "runtime"
30         "strings"
31         "sync"
32 )
33
34 func checker(jobs chan string, isGood *bool, workers *sync.WaitGroup) {
35         hasherBLAKE2b256 := blake2b256New()
36         hasherSHA256 := sha256.New()
37         digestBuf := make([]byte, 32)
38         defer workers.Done()
39         var fd *os.File
40         var br bufio.Reader
41         var err error
42         var digest []byte
43         for fn := range jobs {
44                 fd, err = os.Open(fn)
45                 if err != nil {
46                         fmt.Println("ERR", fn, err)
47                         *isGood = false
48                         continue
49                 }
50
51                 digest, err = os.ReadFile(fn + "." + HashAlgoBLAKE2b256)
52                 if err != nil {
53                         if !errors.Is(err, fs.ErrNotExist) {
54                                 fmt.Println("ERR", fn+"."+HashAlgoBLAKE2b256, err)
55                                 *isGood = false
56                         }
57                         goto NoBLAKE2b256
58                 }
59                 hasherBLAKE2b256.Reset()
60                 br.Reset(fd)
61                 _, err = io.Copy(hasherBLAKE2b256, &br)
62                 fd.Seek(0, io.SeekStart)
63                 if err != nil {
64                         fmt.Println("ERR", fn+"."+HashAlgoBLAKE2b256, err)
65                         *isGood = false
66                         goto NoBLAKE2b256
67                 }
68                 if bytes.Equal(hasherBLAKE2b256.Sum(digestBuf[:0]), digest) {
69                         fmt.Println("GOOD", fn+"."+HashAlgoBLAKE2b256)
70                 } else {
71                         fmt.Println("BAD", fn+"."+HashAlgoBLAKE2b256)
72                         *isGood = false
73                 }
74
75         NoBLAKE2b256:
76                 digest, err = os.ReadFile(fn + "." + HashAlgoSHA256)
77                 if err != nil {
78                         if !errors.Is(err, fs.ErrNotExist) {
79                                 fmt.Println("ERR", fn+"."+HashAlgoSHA256, err)
80                                 *isGood = false
81                         }
82                         fd.Close()
83                         continue
84                 }
85                 hasherSHA256.Reset()
86                 br.Reset(fd)
87                 _, err = io.Copy(hasherSHA256, &br)
88                 fd.Close()
89                 if err != nil {
90                         fmt.Println("ERR", fn+"."+HashAlgoBLAKE2b256, err)
91                         *isGood = false
92                         continue
93                 }
94                 if bytes.Equal(hasherSHA256.Sum(digestBuf[:0]), digest) {
95                         fmt.Println("GOOD", fn+"."+HashAlgoSHA256)
96                 } else {
97                         fmt.Println("BAD", fn+"."+HashAlgoSHA256)
98                         *isGood = false
99                 }
100         }
101 }
102
103 func goodIntegrity() bool {
104         isGood := true
105         jobs := make(chan string, 1<<10)
106         var workers sync.WaitGroup
107         workers.Add(runtime.NumCPU())
108         for i := 0; i < runtime.NumCPU(); i++ {
109                 go checker(jobs, &isGood, &workers)
110         }
111         fd, err := os.Open(Root)
112         if err != nil {
113                 log.Fatal(err)
114         }
115         dirs, err := fd.ReadDir(0)
116         fd.Close()
117         if err != nil {
118                 log.Fatal(err)
119         }
120         for _, dir := range dirs {
121                 fd, err = os.Open(filepath.Join(Root, dir.Name()))
122                 if err != nil {
123                         log.Fatal(err)
124                 }
125                 files, err := fd.ReadDir(0)
126                 fd.Close()
127                 if err != nil {
128                         log.Fatal(err)
129                 }
130                 for _, file := range files {
131                         for _, ext := range KnownExts {
132                                 if strings.HasSuffix(file.Name(), ext) {
133                                         jobs <- filepath.Join(Root, dir.Name(), file.Name())
134                                 }
135                         }
136                 }
137         }
138         close(jobs)
139         workers.Wait()
140         return isGood
141 }