1
2
3 package main
4
5 import (
6 "crypto/md5"
7 "errors"
8 "fmt"
9 "io/ioutil"
10 "os"
11 "path/filepath"
12 "sort"
13 "sync"
14 )
15
16
17 type result struct {
18 path string
19 sum [md5.Size]byte
20 err error
21 }
22
23
24
25
26
27 func sumFiles(done <-chan struct{}, root string) (<-chan result, <-chan error) {
28
29
30 c := make(chan result)
31 errc := make(chan error, 1)
32 go func() {
33 var wg sync.WaitGroup
34 err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
35 if err != nil {
36 return err
37 }
38 if !info.Mode().IsRegular() {
39 return nil
40 }
41 wg.Add(1)
42 go func() {
43 data, err := ioutil.ReadFile(path)
44 select {
45 case c <- result{path, md5.Sum(data), err}:
46 case <-done:
47 }
48 wg.Done()
49 }()
50
51 select {
52 case <-done:
53 return errors.New("walk canceled")
54 default:
55 return nil
56 }
57 })
58
59
60 go func() {
61 wg.Wait()
62 close(c)
63 }()
64
65 errc <- err
66 }()
67 return c, errc
68 }
69
70
71
72
73
74 func MD5All(root string) (map[string][md5.Size]byte, error) {
75
76
77 done := make(chan struct{})
78 defer close(done)
79
80 c, errc := sumFiles(done, root)
81
82 m := make(map[string][md5.Size]byte)
83 for r := range c {
84 if r.err != nil {
85 return nil, r.err
86 }
87 m[r.path] = r.sum
88 }
89 if err := <-errc; err != nil {
90 return nil, err
91 }
92 return m, nil
93 }
94
95 func main() {
96
97
98 m, err := MD5All(os.Args[1])
99 if err != nil {
100 fmt.Println(err)
101 return
102 }
103 var paths []string
104 for path := range m {
105 paths = append(paths, path)
106 }
107 sort.Strings(paths)
108 for _, path := range paths {
109 fmt.Printf("%x %s\n", m[path], path)
110 }
111 }
112
View as plain text