@@ -2,8 +2,11 @@ package session
22
33import (
44 "bytes"
5+ "encoding/binary"
56 "errors"
67 "fmt"
8+ "os"
9+ "path/filepath"
710 "time"
811
912 "github.com/btcsuite/btcd/btcec/v2"
@@ -53,8 +56,122 @@ var (
5356 // ErrDBInitErr is returned when a bucket that we expect to have been
5457 // set up during DB initialisation is not found.
5558 ErrDBInitErr = errors .New ("db did not initialise properly" )
59+
60+ // byteOrder is the default byte order we'll use for serialization
61+ // within the database.
62+ byteOrder = binary .BigEndian
63+ )
64+
65+ const (
66+ // DBFilename is the default filename of the session database.
67+ DBFilename = "session.db"
68+
69+ // dbFilePermission is the default permission the session database file
70+ // is created with.
71+ dbFilePermission = 0600
72+
73+ // DefaultSessionDBTimeout is the default maximum time we wait for the
74+ // session bbolt database to be opened. If the database is already
75+ // opened by another process, the unique lock cannot be obtained. With
76+ // the timeout we error out after the given time instead of just
77+ // blocking for forever.
78+ DefaultSessionDBTimeout = 5 * time .Second
5679)
5780
81+ // DB is a bolt-backed persistent store.
82+ type DB struct {
83+ * bbolt.DB
84+ }
85+
86+ // A compile-time check to ensure that DB implements the Store interface.
87+ var _ Store = (* DB )(nil )
88+
89+ // NewDB creates a new bolt database that can be found at the given directory.
90+ func NewDB (dir , fileName string ) (* DB , error ) {
91+ firstInit := false
92+ path := filepath .Join (dir , fileName )
93+
94+ // If the database file does not exist yet, create its directory.
95+ if ! fileExists (path ) {
96+ if err := os .MkdirAll (dir , 0700 ); err != nil {
97+ return nil , err
98+ }
99+ firstInit = true
100+ }
101+
102+ db , err := initDB (path , firstInit )
103+ if err != nil {
104+ return nil , err
105+ }
106+
107+ // Attempt to sync the database's current version with the latest known
108+ // version available.
109+ if err := syncVersions (db ); err != nil {
110+ return nil , err
111+ }
112+
113+ return & DB {DB : db }, nil
114+ }
115+
116+ // fileExists reports whether the named file or directory exists.
117+ func fileExists (path string ) bool {
118+ if _ , err := os .Stat (path ); err != nil {
119+ if os .IsNotExist (err ) {
120+ return false
121+ }
122+ }
123+ return true
124+ }
125+
126+ // initDB initializes all the required top-level buckets for the database.
127+ func initDB (filepath string , firstInit bool ) (* bbolt.DB , error ) {
128+ db , err := bbolt .Open (filepath , dbFilePermission , & bbolt.Options {
129+ Timeout : DefaultSessionDBTimeout ,
130+ })
131+ if err == bbolt .ErrTimeout {
132+ return nil , fmt .Errorf ("error while trying to open %s: timed " +
133+ "out after %v when trying to obtain exclusive lock" ,
134+ filepath , DefaultSessionDBTimeout )
135+ }
136+ if err != nil {
137+ return nil , err
138+ }
139+
140+ err = db .Update (func (tx * bbolt.Tx ) error {
141+ if firstInit {
142+ metadataBucket , err := tx .CreateBucketIfNotExists (
143+ metadataBucketKey ,
144+ )
145+ if err != nil {
146+ return err
147+ }
148+ err = setDBVersion (metadataBucket , latestDBVersion )
149+ if err != nil {
150+ return err
151+ }
152+ }
153+
154+ sessionBkt , err := tx .CreateBucketIfNotExists (sessionBucketKey )
155+ if err != nil {
156+ return err
157+ }
158+
159+ _ , err = sessionBkt .CreateBucketIfNotExists (idIndexKey )
160+ if err != nil {
161+ return err
162+ }
163+
164+ _ , err = sessionBkt .CreateBucketIfNotExists (groupIDIndexKey )
165+
166+ return err
167+ })
168+ if err != nil {
169+ return nil , err
170+ }
171+
172+ return db , nil
173+ }
174+
58175// getSessionKey returns the key for a session.
59176func getSessionKey (session * Session ) []byte {
60177 return session .LocalPublicKey .SerializeCompressed ()
0 commit comments