/
fs_hasher.go
94 lines (89 loc) · 2.54 KB
/
fs_hasher.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
// Package fh
// implements basic hashing functionality
package filestat
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"fmt"
"hash"
"io"
"log"
"os"
"strings"
)
const (
Idle = ""
MD5 = "md5"
SHA1 = "sha1"
SHA256 = "sha256"
SHA512 = "sha512"
)
const EMPTY_CHECKSUM = "-"
// HashFileFunc - type specifies signature of function that calculates hash / checksum for file [path]
// initial settings (algo, size, etc.) are taken from closure - see GetHashFileFunc
type HashFileFunc func(fs FileStat, prefix string) (result string, written int64, err error)
// GetHashFileFunc customizes hasher func
func GetHashFileFunc(algo string, ndMaxSize int64, inBlocks bool) (HashFileFunc, error) {
var fileHasher func() hash.Hash
switch strings.ToLower(algo) {
case Idle:
fileHasher = func() hash.Hash { return &idleHasher{} }
case MD5:
fileHasher = md5.New
case SHA1:
fileHasher = sha1.New
case SHA256:
fileHasher = sha256.New
case SHA512:
fileHasher = sha512.New
default:
return nil, fmt.Errorf("invalid value for algo hashing: [%s] - not supported", algo)
// fileHasher = func() hash.Hash { return &idleHasher{} }
// fileHasher = md5.New
}
return func(fs FileStat, prefix string) (result string, written int64, err error) {
var (
h = fileHasher()
size = fs.Size()
dMaxSize = ndMaxSize
)
if _, ok := h.(*idleHasher); !ok {
if inBlocks { // dSize is size in blocks
dMaxSize = dMaxSize * fs.Blksize() // files can have different block sizes
}
file, err := os.Open(fs.Path())
if err != nil {
return result, written, fmt.Errorf("hasing file [%s] failed: %w", fs.Path(), err)
}
defer func() {
if e := file.Close(); e != nil && err == nil {
log.Printf("error while closing file [%s]: %v", fs.Path(), e)
}
}()
switch {
case dMaxSize > 0:
if size > dMaxSize {
size = dMaxSize
}
case dMaxSize < 0:
size = -dMaxSize
if size > fs.Size() {
size = fs.Size()
}
if ret, err := file.Seek(-size, io.SeekEnd); err != nil {
return result, written, fmt.Errorf("seek file %s (%d) at offset %d is failed with ret = %d: %w", fs.Path(), fs.Size(), -size, ret, err)
}
case dMaxSize == 0:
// size = fs.Size()
}
if written, err = io.CopyN(h, file, size); err != nil {
return result, written, fmt.Errorf("hashing file [%s] is failed - written %d: %w", fs.Path(), written, err)
}
}
checksum := hex.EncodeToString(h.Sum(nil))
return fmt.Sprintf("%s:%d:%s:%s", prefix, size, algo, checksum), written, nil
}, nil
}