-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathverifysfv.go
109 lines (95 loc) · 2.38 KB
/
verifysfv.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package main
import (
"flag"
"fmt"
"hash/crc32"
"log"
"os"
"runtime"
"sync"
"github.com/cwlbraa/verifysfv/sfv"
"github.com/gosuri/uiprogress"
)
// command line option configuration
var poly = flag.String("poly", "crc32c", "crc base polynomial: crc32c (Castagnoli), ieee, or koopman")
var parallelism = flag.Int("j", runtime.NumCPU(), "# of parallel workers to spin up")
var memory = flag.Int("mem", runtime.NumCPU()*4, "kBs of memory to use as file buffers")
func main() {
flag.Usage = func() {
fmt.Printf("verifysfv: a tiny, fast, almost-always-io-bound tool for verifying sfv files\n\n")
fmt.Printf("Usage: verify [options] fileManifest.sfv\n\n")
fmt.Printf("options:\n")
flag.PrintDefaults()
}
// parse and verify args
flag.Parse()
if len(flag.Args()) < 1 {
flag.Usage()
os.Exit(1)
}
sfvFilepath := flag.Args()[0]
polynomial := parsePoly(*poly)
verifysfv.SetBufSize(*memory * 1024 / *parallelism)
// open and parse sfv file
parsed, err := verifysfv.Read(sfvFilepath)
if err != nil {
log.Fatal(err)
}
count := len(parsed.Checksums)
// start up progress bar
bar := uiprogress.AddBar(count).AppendCompleted().PrependElapsed()
uiprogress.Start()
// initialize threadsafe data structures
checksums := make(chan verifysfv.Checksum, count)
errs := make(chan error, count) // nil errors indicate success
var wg sync.WaitGroup
// fire off worker threads
for i := 0; i < *parallelism; i++ {
wg.Add(1)
go func() {
for checksum := range checksums {
success, result, err := checksum.Verify(polynomial)
bar.Incr()
if !success && err == nil {
errs <- fmt.Errorf("corruption: expected %x but computed %x for %s\n",
checksum.CRC32, result, checksum.Filename)
continue
}
errs <- err // nil error indicates success
}
wg.Done()
}()
}
// feed data to worker threads
for _, chk := range parsed.Checksums {
checksums <- chk
}
close(checksums)
// close errs asyncronously so we can print errors as we get them
go func() {
wg.Wait()
close(errs)
}()
// detect & print errors
exitCode := 0
for err := range errs {
if err != nil {
exitCode = 1
fmt.Println(err)
}
}
os.Exit(exitCode)
}
func parsePoly(in string) uint32 {
switch in {
case "crc32c":
return crc32.Castagnoli
case "ieee":
return crc32.IEEE
case "koop":
return crc32.Koopman
default:
log.Fatalf("unsupported polynomial %s", in)
}
return 0
}