ref: afa09d62c88148514522c9254c46ed41cdc1eb69 drmdb/database/db.go -rw-r--r-- 2.5 KiB View raw
                                                                                
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 Init() 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
}