-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathetag.go
101 lines (86 loc) · 1.97 KB
/
etag.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
package rmsgo
import (
"crypto/md5"
"encoding/hex"
"fmt"
"io"
"log"
"os"
"sort"
. "github.com/cvanloo/rmsgo/mock"
"golang.org/x/exp/maps"
)
func init() {
var err error
hostname, err = os.Hostname()
if err != nil {
log.Fatalf("failed to read hostname: %v", err)
}
}
// ETag is a unique identifier assigned to a specific version of a
// remoteStorage resource.
type ETag []byte
var hostname string
// String creates a string from an Etag e.
// To go the opposite way an obtain an ETag from a string, use ParseETag.
func (e ETag) String() string {
return hex.EncodeToString(e)
}
// ParseETag decodes an ETag previously encoded by (ETag).String()
func ParseETag(s string) (ETag, error) {
// one byte is 2 hex digits
if len(s) != md5.Size*2 {
return nil, fmt.Errorf("not a valid etag")
}
return hex.DecodeString(s)
}
func (e ETag) Equal(other ETag) bool {
assert(len(e) == len(other), "comparison with malformed ETag")
for i := 0; i < len(e); i++ {
if e[i] != other[i] {
return false
}
}
return true
}
func calculateETag(n *node) error {
hash := md5.New()
io.WriteString(hash, hostname)
ns := []*node{n}
for len(ns) > 0 {
cn := ns[0]
ns = ns[1:]
if cn.isFolder {
io.WriteString(hash, cn.name)
children := maps.Values(cn.children)
// Ensure that etag is deterministic by always hashing children in
// the same order.
sort.Slice(children, func(i, j int) bool {
return children[i].rname < children[j].rname
})
ns = append(ns, children...)
} else {
io.WriteString(hash, cn.name)
io.WriteString(hash, cn.mime)
io.WriteString(hash, cn.lastMod.Format(timeFormat))
fd, err := FS.Open(cn.sname)
if err != nil {
return err
}
n, err := io.Copy(hash, fd)
if err != nil {
return err
}
if cn.length != n {
return fmt.Errorf("etag: expected to read %d bytes, got: %d", cn.length, n)
}
err = fd.Close()
if err != nil {
return err
}
}
}
n.etag = hash.Sum(nil)
n.etagValid = true
return nil
}