aboutsummaryrefslogtreecommitdiffstats
path: root/master/persistent.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2015-10-12 10:16:57 +0200
committerDmitry Vyukov <dvyukov@google.com>2015-10-12 10:16:57 +0200
commit874c5754bb22dbf77d6b600ff91f0f4f1fc5073a (patch)
tree0075fbd088046ad5c86e6e972235701d68b3ce7c /master/persistent.go
initial commit
Diffstat (limited to 'master/persistent.go')
-rw-r--r--master/persistent.go129
1 files changed, 129 insertions, 0 deletions
diff --git a/master/persistent.go b/master/persistent.go
new file mode 100644
index 000000000..12f4bbdc8
--- /dev/null
+++ b/master/persistent.go
@@ -0,0 +1,129 @@
+// Copyright 2015 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+package main
+
+import (
+ "crypto/sha1"
+ "encoding/hex"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+)
+
+type Sig [sha1.Size]byte
+
+// PersistentSet is a set of binary blobs with a persistent mirror on disk.
+type PersistentSet struct {
+ dir string
+ m map[Sig][]byte
+ a [][]byte
+}
+
+func hash(data []byte) Sig {
+ return Sig(sha1.Sum(data))
+}
+
+func newPersistentSet(dir string, verify func(data []byte) bool) *PersistentSet {
+ ps := &PersistentSet{
+ dir: dir,
+ m: make(map[Sig][]byte),
+ }
+ os.MkdirAll(dir, 0770)
+ filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ log.Fatalf("error during dir walk: %v\n", err)
+ }
+ if info.IsDir() {
+ return nil
+ }
+ data, err := ioutil.ReadFile(path)
+ if err != nil {
+ log.Fatalf("error during file read: %v\n", err)
+ return nil
+ }
+ sig := hash(data)
+ if _, ok := ps.m[sig]; ok {
+ return nil
+ }
+ name := info.Name()
+ if len(data) == 0 {
+ // This can happen is master runs on machine-under-test,
+ // and it has crashed midway.
+ log.Printf("removing empty file %v", name)
+ os.Remove(path)
+ return nil
+ }
+ const hexLen = 2 * sha1.Size
+ if len(name) > hexLen+1 && isHexString(name[:hexLen]) && name[hexLen] == '.' {
+ return nil // description file
+ }
+ if len(name) != hexLen || !isHexString(name) {
+ log.Fatalf("unknown file in persistent dir %v: %v", dir, name)
+ }
+ if verify != nil && !verify(data) {
+ os.Remove(path)
+ return nil
+ }
+ if name != hex.EncodeToString(sig[:]) {
+ log.Printf("bad hash in persistent dir %v for file %v, expect %v", dir, name, hex.EncodeToString(sig[:]))
+ if err := ioutil.WriteFile(filepath.Join(ps.dir, hex.EncodeToString(sig[:])), data, 0660); err != nil {
+ log.Fatalf("failed to write file: %v", err)
+ }
+ os.Remove(path)
+ }
+ ps.m[sig] = data
+ ps.a = append(ps.a, data)
+ return nil
+ })
+ return ps
+}
+
+func isHexString(s string) bool {
+ for _, v := range []byte(s) {
+ if v >= '0' && v <= '9' || v >= 'a' && v <= 'f' {
+ continue
+ }
+ return false
+ }
+ return true
+}
+
+func (ps *PersistentSet) add(data []byte) bool {
+ sig := hash(data)
+ if _, ok := ps.m[sig]; ok {
+ return false
+ }
+ data = append([]byte{}, data...)
+ ps.m[sig] = data
+ ps.a = append(ps.a, data)
+ fname := filepath.Join(ps.dir, hex.EncodeToString(sig[:]))
+ if err := ioutil.WriteFile(fname, data, 0660); err != nil {
+ log.Fatalf("failed to write file: %v", err)
+ }
+ return true
+}
+
+// addDescription creates a complementary to data file on disk.
+func (ps *PersistentSet) addDescription(data []byte, desc []byte, typ string) {
+ sig := hash(data)
+ fname := filepath.Join(ps.dir, fmt.Sprintf("%v.%v", hex.EncodeToString(sig[:]), typ))
+ if err := ioutil.WriteFile(fname, desc, 0660); err != nil {
+ log.Fatalf("failed to write file: %v", err)
+ }
+}
+
+func (ps *PersistentSet) minimize(set map[string]bool) {
+ ps.a = nil
+ for sig, data := range ps.m {
+ s := hex.EncodeToString(sig[:])
+ if set[s] {
+ ps.a = append(ps.a, data)
+ } else {
+ delete(ps.m, sig)
+ os.Remove(filepath.Join(ps.dir, s))
+ }
+ }
+}