github.com/avahowell/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  }