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