@@ -2,10 +2,155 @@ package firewalldb
22
33import (
44 "context"
5+ "encoding/binary"
6+ "errors"
7+ "fmt"
8+ "os"
9+ "path/filepath"
10+ "time"
511
612 "go.etcd.io/bbolt"
713)
814
15+ const (
16+ // DBFilename is the default filename of the rules' database.
17+ DBFilename = "rules.db"
18+
19+ // dbFilePermission is the default permission the rules' database file
20+ // is created with.
21+ dbFilePermission = 0600
22+
23+ // DefaultRulesDBTimeout is the default maximum time we wait for the
24+ // db bbolt database to be opened. If the database is already
25+ // opened by another process, the unique lock cannot be obtained. With
26+ // the timeout we error out after the given time instead of just
27+ // blocking for forever.
28+ DefaultRulesDBTimeout = 5 * time .Second
29+ )
30+
31+ var (
32+ // byteOrder is the default byte order we'll use for serialization
33+ // within the database.
34+ byteOrder = binary .BigEndian
35+ )
36+
37+ // BoltDB is a bolt-backed persistent store.
38+ type BoltDB struct {
39+ * bbolt.DB
40+
41+ sessionIDIndex SessionDB
42+ }
43+
44+ // NewBoltDB creates a new bolt database that can be found at the given
45+ // directory.
46+ func NewBoltDB (dir , fileName string , sessionIDIndex SessionDB ) (* BoltDB ,
47+ error ) {
48+
49+ firstInit := false
50+ path := filepath .Join (dir , fileName )
51+
52+ // If the database file does not exist yet, create its directory.
53+ if ! fileExists (path ) {
54+ if err := os .MkdirAll (dir , 0700 ); err != nil {
55+ return nil , err
56+ }
57+ firstInit = true
58+ }
59+
60+ db , err := initDB (path , firstInit )
61+ if err != nil {
62+ return nil , err
63+ }
64+
65+ // Attempt to sync the database's current version with the latest known
66+ // version available.
67+ if err := syncVersions (db ); err != nil {
68+ return nil , err
69+ }
70+
71+ return & BoltDB {
72+ DB : db ,
73+ sessionIDIndex : sessionIDIndex ,
74+ }, nil
75+ }
76+
77+ // fileExists reports whether the named file or directory exists.
78+ func fileExists (path string ) bool {
79+ if _ , err := os .Stat (path ); err != nil {
80+ if os .IsNotExist (err ) {
81+ return false
82+ }
83+ }
84+ return true
85+ }
86+
87+ // initDB initializes all the required top-level buckets for the database.
88+ func initDB (filepath string , firstInit bool ) (* bbolt.DB , error ) {
89+ db , err := bbolt .Open (filepath , dbFilePermission , & bbolt.Options {
90+ Timeout : DefaultRulesDBTimeout ,
91+ })
92+ if err == bbolt .ErrTimeout {
93+ return nil , fmt .Errorf ("error while trying to open %s: timed " +
94+ "out after %v when trying to obtain exclusive lock" ,
95+ filepath , DefaultRulesDBTimeout )
96+ }
97+ if err != nil {
98+ return nil , err
99+ }
100+
101+ err = db .Update (func (tx * bbolt.Tx ) error {
102+ if firstInit {
103+ metadataBucket , err := tx .CreateBucketIfNotExists (
104+ metadataBucketKey ,
105+ )
106+ if err != nil {
107+ return err
108+ }
109+ err = setDBVersion (metadataBucket , latestDBVersion )
110+ if err != nil {
111+ return err
112+ }
113+ }
114+
115+ rulesBucket , err := tx .CreateBucketIfNotExists (rulesBucketKey )
116+ if err != nil {
117+ return err
118+ }
119+
120+ // Delete everything under the "temp" key if such a bucket
121+ // exists.
122+ err = rulesBucket .DeleteBucket (tempBucketKey )
123+ if err != nil && ! errors .Is (err , bbolt .ErrBucketNotFound ) {
124+ return err
125+ }
126+
127+ actionsBucket , err := tx .CreateBucketIfNotExists (
128+ actionsBucketKey ,
129+ )
130+ if err != nil {
131+ return err
132+ }
133+
134+ _ , err = actionsBucket .CreateBucketIfNotExists (actionsKey )
135+ if err != nil {
136+ return err
137+ }
138+
139+ _ , err = actionsBucket .CreateBucketIfNotExists (actionsIndex )
140+ if err != nil {
141+ return err
142+ }
143+
144+ _ , err = tx .CreateBucketIfNotExists (privacyBucketKey )
145+ return err
146+ })
147+ if err != nil {
148+ return nil , err
149+ }
150+
151+ return db , nil
152+ }
153+
9154// kvdbExecutor is a concrete implementation of the DBExecutor interface that
10155// uses a bbolt database as its backing store.
11156type kvdbExecutor [T any ] struct {
0 commit comments