github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/persist/boltdb.go (about) 1 package persist 2 3 import ( 4 "time" 5 6 "github.com/NebulousLabs/bolt" 7 ) 8 9 // BoltDatabase is a persist-level wrapper for the bolt database, providing 10 // extra information such as a version number. 11 type BoltDatabase struct { 12 Metadata 13 *bolt.DB 14 } 15 16 // checkMetadata confirms that the metadata in the database is 17 // correct. If there is no metadata, correct metadata is inserted 18 func (db *BoltDatabase) checkMetadata(md Metadata) error { 19 err := db.Update(func(tx *bolt.Tx) error { 20 // Check if the database has metadata. If not, create metadata for the 21 // database. 22 bucket := tx.Bucket([]byte("Metadata")) 23 if bucket == nil { 24 err := db.updateMetadata(tx) 25 if err != nil { 26 return err 27 } 28 return nil 29 } 30 31 // Verify that the metadata matches the expected metadata. 32 header := bucket.Get([]byte("Header")) 33 if string(header) != md.Header { 34 return ErrBadHeader 35 } 36 version := bucket.Get([]byte("Version")) 37 if string(version) != md.Version { 38 return ErrBadVersion 39 } 40 return nil 41 }) 42 return err 43 } 44 45 // updateMetadata will set the contents of the metadata bucket to the values 46 // in db.Metadata. 47 func (db *BoltDatabase) updateMetadata(tx *bolt.Tx) error { 48 bucket, err := tx.CreateBucketIfNotExists([]byte("Metadata")) 49 if err != nil { 50 return err 51 } 52 err = bucket.Put([]byte("Header"), []byte(db.Header)) 53 if err != nil { 54 return err 55 } 56 err = bucket.Put([]byte("Version"), []byte(db.Version)) 57 if err != nil { 58 return err 59 } 60 return nil 61 } 62 63 // Close closes the database. 64 func (db *BoltDatabase) Close() error { 65 return db.DB.Close() 66 } 67 68 // OpenDatabase opens a database and validates its metadata. 69 func OpenDatabase(md Metadata, filename string) (*BoltDatabase, error) { 70 // Open the database using a 1 second timeout (without the timeout, 71 // database will potentially hang indefinitely. 72 db, err := bolt.Open(filename, 0600, &bolt.Options{Timeout: 3 * time.Second}) 73 if err != nil { 74 return nil, err 75 } 76 77 // Check the metadata. 78 boltDB := &BoltDatabase{ 79 Metadata: md, 80 DB: db, 81 } 82 err = boltDB.checkMetadata(md) 83 if err != nil { 84 db.Close() 85 return nil, err 86 } 87 88 return boltDB, nil 89 }