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) 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")
if err := fn(k); err == ErrStop {
return nil
} else if err != nil {
return err
}
}
return nil
}