github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/dirhash/main.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Dirhash computes a hash of a file system directory tree. 6 // 7 // Usage: 8 // 9 // dirhash [-d] [dir ...] 10 // 11 // For each directory named on the command line, dirhash prints 12 // the hash of the file system tree rooted at that directory. 13 // 14 // The hash is computed by considering all files in the tree, 15 // in the lexical order used by Go's filepath.Walk, computing 16 // the sha256 hash of each, and then computing a sha256 of 17 // the list of hashes and file names. If the -d flag is given, 18 // dirhash prints to standard error a shell script computing 19 // the overall sha256. 20 // 21 // Except for occasional differences in sort order, "dirhash mydir" 22 // is equivalent to 23 // 24 // (cd mydir; sha256sum $(find . -type f | sort) | sha256sum) 25 // 26 package main 27 28 import ( 29 "crypto/sha256" 30 "flag" 31 "fmt" 32 "io" 33 "log" 34 "os" 35 "path/filepath" 36 ) 37 38 func usage() { 39 fmt.Fprintf(os.Stderr, "usage: dirhash [-d] [dir...]\n") 40 os.Exit(2) 41 } 42 43 var debug = flag.Bool("d", false, "print input for overall sha256sum") 44 45 func main() { 46 log.SetFlags(0) 47 log.SetPrefix("dirhash: ") 48 flag.Usage = usage 49 flag.Parse() 50 51 args := flag.Args() 52 if len(args) == 0 { 53 args = []string{"."} 54 } 55 56 for _, dir := range args { 57 dirhash(dir) 58 } 59 } 60 61 func dirhash(dir string) { 62 dir = filepath.Clean(dir) 63 h := sha256.New() 64 info, err := os.Lstat(dir) 65 if err == nil && info.Mode()&os.ModeSymlink != 0 { 66 log.Printf("%s is a symlink\n", dir) 67 return 68 } 69 if *debug { 70 fmt.Fprintf(os.Stderr, "sha256sum << 'EOF'\n") 71 } 72 filepath.Walk(dir, func(file string, info os.FileInfo, err error) error { 73 if info.Mode()&os.ModeSymlink != 0 { 74 i, err := os.Stat(file) 75 if err != nil { 76 return err 77 } 78 info = i 79 } 80 if info.IsDir() { 81 return nil 82 } 83 rel := file 84 if dir != "." { 85 rel = file[len(dir)+1:] 86 } 87 rel = filepath.ToSlash(rel) 88 fh := filehash(file) 89 if *debug { 90 fmt.Fprintf(os.Stderr, "%s ./%s\n", fh, rel) 91 } 92 fmt.Fprintf(h, "%s ./%s\n", fh, rel) 93 return nil 94 }) 95 if *debug { 96 fmt.Fprintf(os.Stderr, "EOF\n") 97 } 98 fmt.Printf("%x %s\n", h.Sum(nil), dir) 99 } 100 101 func filehash(file string) string { 102 h := sha256.New() 103 f, err := os.Open(file) 104 if err != nil { 105 log.Print(err) 106 } 107 _, err = io.Copy(h, f) 108 if err != nil { 109 log.Print(err) 110 } 111 f.Close() 112 return fmt.Sprintf("%x", h.Sum(nil)) 113 }