~emersion/drmdb

ref: 8f5adfec99640fae00c75d8fe66bb91b1495166d drmdb/database/fs.go -rw-r--r-- 2.5 KiB
8f5adfecSimon Ser Introduce database.DB a month ago
                                                                                
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package database

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"git.sr.ht/~emersion/drmdb/drmtree"
)

const Dir = "db"

var ErrStop = fmt.Errorf("drmdb: stop walking")

func generateKey(n *drmtree.Node) (string, error) {
	if n.Driver == nil || n.Device == nil {
		return "", fmt.Errorf("node is missing driver/device")
	}

	ver := &n.Driver.Version

	var b bytes.Buffer
	b.WriteString(n.Driver.Name)
	b.WriteByte(0)
	fmt.Fprintf(&b, "%v.%v.%v-%v", ver.Major, ver.Minor, ver.Patch, ver.Date)
	b.WriteByte(0)
	b.WriteString(n.Driver.Kernel.SysName)
	b.WriteByte(0)
	b.WriteString(n.Driver.Kernel.Release)
	b.WriteByte(0)
	switch dev := n.Device.DeviceData.(type) {
	case *drmtree.DevicePCI:
		b.WriteString("pci")
		b.WriteByte(0)
		fmt.Fprintf(&b, "%04X:%04X", dev.Vendor, dev.Device)
	case *drmtree.DevicePlatform:
		b.WriteString("platform")
		if len(dev.Compatible) == 0 {
			return "", fmt.Errorf("platform device is missing compatibility info")
		}
		for _, compat := range dev.Compatible {
			b.WriteByte(0)
			b.WriteString(compat)
		}
	default:
		return "", fmt.Errorf("device bus type %v not supported", n.Device.BusType)
	}

	sum := sha256.Sum256(b.Bytes())
	return hex.EncodeToString(sum[:])[:12], nil
}

func initDB() error {
	return os.MkdirAll(Dir, 0755)
}

func store(n *drmtree.Node) (string, error) {
	k, err := generateKey(n)
	if err != nil {
		return "", err
	}

	p := filepath.Join(Dir, k+".json")
	f, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
	if err != nil {
		if os.IsExist(err) {
			return "", fmt.Errorf("data has already been submitted")
		}
		return "", err
	}
	defer f.Close()

	if err := json.NewEncoder(f).Encode(n); err != nil {
		return "", err
	}

	return k, f.Close()
}

func load(k string) (*drmtree.Node, error) {
	p := filepath.Join(Dir, k+".json")
	f, err := os.Open(p)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var n drmtree.Node
	if err := json.NewDecoder(f).Decode(&n); err != nil {
		return nil, err
	}

	return &n, f.Close()
}

func walk(fn func(k string, n *drmtree.Node) error) error {
	files, err := ioutil.ReadDir(Dir)
	if err != nil {
		return err
	}

	for _, fi := range files {
		if !strings.HasSuffix(fi.Name(), ".json") {
			continue
		}
		k := strings.TrimSuffix(fi.Name(), ".json")

		n, err := load(k)
		if err != nil {
			return err
		}

		if err := fn(k, n); err == ErrStop {
			return nil
		} else if err != nil {
			return err
		}
	}

	return nil
}