github.com/btcsuite/btcd@v0.24.0/database/ffldb/blockio.go (about)

     1  // Copyright (c) 2015-2016 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  // This file contains the implementation functions for reading, writing, and
     6  // otherwise working with the flat files that house the actual blocks.
     7  
     8  package ffldb
     9  
    10  import (
    11  	"container/list"
    12  	"encoding/binary"
    13  	"fmt"
    14  	"hash/crc32"
    15  	"io"
    16  	"os"
    17  	"path/filepath"
    18  	"sort"
    19  	"strconv"
    20  	"strings"
    21  	"sync"
    22  
    23  	"github.com/btcsuite/btcd/chaincfg/chainhash"
    24  	"github.com/btcsuite/btcd/database"
    25  	"github.com/btcsuite/btcd/wire"
    26  )
    27  
    28  const (
    29  	// blockFileExtension is the extension that's used to store the block
    30  	// files on the disk.
    31  	blockFileExtension = ".fdb"
    32  
    33  	// The Bitcoin protocol encodes block height as int32, so max number of
    34  	// blocks is 2^31.  Max block size per the protocol is 32MiB per block.
    35  	// So the theoretical max at the time this comment was written is 64PiB
    36  	// (pebibytes).  With files @ 512MiB each, this would require a maximum
    37  	// of 134,217,728 files.  Thus, choose 9 digits of precision for the
    38  	// filenames.  An additional benefit is 9 digits provides 10^9 files @
    39  	// 512MiB each for a total of ~476.84PiB (roughly 7.4 times the current
    40  	// theoretical max), so there is room for the max block size to grow in
    41  	// the future.
    42  	blockFilenameTemplate = "%09d" + blockFileExtension
    43  
    44  	// maxOpenFiles is the max number of open files to maintain in the
    45  	// open blocks cache.  Note that this does not include the current
    46  	// write file, so there will typically be one more than this value open.
    47  	maxOpenFiles = 25
    48  
    49  	// maxBlockFileSize is the maximum size for each file used to store
    50  	// blocks.
    51  	//
    52  	// NOTE: The current code uses uint32 for all offsets, so this value
    53  	// must be less than 2^32 (4 GiB).  This is also why it's a typed
    54  	// constant.
    55  	maxBlockFileSize uint32 = 512 * 1024 * 1024 // 512 MiB
    56  
    57  	// blockLocSize is the number of bytes the serialized block location
    58  	// data that is stored in the block index.
    59  	//
    60  	// The serialized block location format is:
    61  	//
    62  	//  [0:4]  Block file (4 bytes)
    63  	//  [4:8]  File offset (4 bytes)
    64  	//  [8:12] Block length (4 bytes)
    65  	blockLocSize = 12
    66  )
    67  
    68  var (
    69  	// castagnoli houses the Catagnoli polynomial used for CRC-32 checksums.
    70  	castagnoli = crc32.MakeTable(crc32.Castagnoli)
    71  )
    72  
    73  // filer is an interface which acts very similar to a *os.File and is typically
    74  // implemented by it.  It exists so the test code can provide mock files for
    75  // properly testing corruption and file system issues.
    76  type filer interface {
    77  	io.Closer
    78  	io.WriterAt
    79  	io.ReaderAt
    80  	Truncate(size int64) error
    81  	Sync() error
    82  }
    83  
    84  // lockableFile represents a block file on disk that has been opened for either
    85  // read or read/write access.  It also contains a read-write mutex to support
    86  // multiple concurrent readers.
    87  type lockableFile struct {
    88  	sync.RWMutex
    89  	file filer
    90  }
    91  
    92  // writeCursor represents the current file and offset of the block file on disk
    93  // for performing all writes. It also contains a read-write mutex to support
    94  // multiple concurrent readers which can reuse the file handle.
    95  type writeCursor struct {
    96  	sync.RWMutex
    97  
    98  	// curFile is the current block file that will be appended to when
    99  	// writing new blocks.
   100  	curFile *lockableFile
   101  
   102  	// curFileNum is the current block file number and is used to allow
   103  	// readers to use the same open file handle.
   104  	curFileNum uint32
   105  
   106  	// curOffset is the offset in the current write block file where the
   107  	// next new block will be written.
   108  	curOffset uint32
   109  }
   110  
   111  // blockStore houses information used to handle reading and writing blocks (and
   112  // part of blocks) into flat files with support for multiple concurrent readers.
   113  type blockStore struct {
   114  	// network is the specific network to use in the flat files for each
   115  	// block.
   116  	network wire.BitcoinNet
   117  
   118  	// basePath is the base path used for the flat block files and metadata.
   119  	basePath string
   120  
   121  	// maxBlockFileSize is the maximum size for each file used to store
   122  	// blocks.  It is defined on the store so the whitebox tests can
   123  	// override the value.
   124  	maxBlockFileSize uint32
   125  
   126  	// The following fields are related to the flat files which hold the
   127  	// actual blocks.   The number of open files is limited by maxOpenFiles.
   128  	//
   129  	// obfMutex protects concurrent access to the openBlockFiles map.  It is
   130  	// a RWMutex so multiple readers can simultaneously access open files.
   131  	//
   132  	// openBlockFiles houses the open file handles for existing block files
   133  	// which have been opened read-only along with an individual RWMutex.
   134  	// This scheme allows multiple concurrent readers to the same file while
   135  	// preventing the file from being closed out from under them.
   136  	//
   137  	// lruMutex protects concurrent access to the least recently used list
   138  	// and lookup map.
   139  	//
   140  	// openBlocksLRU tracks how the open files are refenced by pushing the
   141  	// most recently used files to the front of the list thereby trickling
   142  	// the least recently used files to end of the list.  When a file needs
   143  	// to be closed due to exceeding the max number of allowed open
   144  	// files, the one at the end of the list is closed.
   145  	//
   146  	// fileNumToLRUElem is a mapping between a specific block file number
   147  	// and the associated list element on the least recently used list.
   148  	//
   149  	// Thus, with the combination of these fields, the database supports
   150  	// concurrent non-blocking reads across multiple and individual files
   151  	// along with intelligently limiting the number of open file handles by
   152  	// closing the least recently used files as needed.
   153  	//
   154  	// NOTE: The locking order used throughout is well-defined and MUST be
   155  	// followed.  Failure to do so could lead to deadlocks.  In particular,
   156  	// the locking order is as follows:
   157  	//   1) obfMutex
   158  	//   2) lruMutex
   159  	//   3) writeCursor mutex
   160  	//   4) specific file mutexes
   161  	//
   162  	// None of the mutexes are required to be locked at the same time, and
   163  	// often aren't.  However, if they are to be locked simultaneously, they
   164  	// MUST be locked in the order previously specified.
   165  	//
   166  	// Due to the high performance and multi-read concurrency requirements,
   167  	// write locks should only be held for the minimum time necessary.
   168  	obfMutex         sync.RWMutex
   169  	lruMutex         sync.Mutex
   170  	openBlocksLRU    *list.List // Contains uint32 block file numbers.
   171  	fileNumToLRUElem map[uint32]*list.Element
   172  	openBlockFiles   map[uint32]*lockableFile
   173  
   174  	// writeCursor houses the state for the current file and location that
   175  	// new blocks are written to.
   176  	writeCursor *writeCursor
   177  
   178  	// These functions are set to openFile, openWriteFile, and deleteFile by
   179  	// default, but are exposed here to allow the whitebox tests to replace
   180  	// them when working with mock files.
   181  	openFileFunc      func(fileNum uint32) (*lockableFile, error)
   182  	openWriteFileFunc func(fileNum uint32) (filer, error)
   183  	deleteFileFunc    func(fileNum uint32) error
   184  }
   185  
   186  // blockLocation identifies a particular block file and location.
   187  type blockLocation struct {
   188  	blockFileNum uint32
   189  	fileOffset   uint32
   190  	blockLen     uint32
   191  }
   192  
   193  // deserializeBlockLoc deserializes the passed serialized block location
   194  // information.  This is data stored into the block index metadata for each
   195  // block.  The serialized data passed to this function MUST be at least
   196  // blockLocSize bytes or it will panic.  The error check is avoided here because
   197  // this information will always be coming from the block index which includes a
   198  // checksum to detect corruption.  Thus it is safe to use this unchecked here.
   199  func deserializeBlockLoc(serializedLoc []byte) blockLocation {
   200  	// The serialized block location format is:
   201  	//
   202  	//  [0:4]  Block file (4 bytes)
   203  	//  [4:8]  File offset (4 bytes)
   204  	//  [8:12] Block length (4 bytes)
   205  	return blockLocation{
   206  		blockFileNum: byteOrder.Uint32(serializedLoc[0:4]),
   207  		fileOffset:   byteOrder.Uint32(serializedLoc[4:8]),
   208  		blockLen:     byteOrder.Uint32(serializedLoc[8:12]),
   209  	}
   210  }
   211  
   212  // serializeBlockLoc returns the serialization of the passed block location.
   213  // This is data to be stored into the block index metadata for each block.
   214  func serializeBlockLoc(loc blockLocation) []byte {
   215  	// The serialized block location format is:
   216  	//
   217  	//  [0:4]  Block file (4 bytes)
   218  	//  [4:8]  File offset (4 bytes)
   219  	//  [8:12] Block length (4 bytes)
   220  	var serializedData [12]byte
   221  	byteOrder.PutUint32(serializedData[0:4], loc.blockFileNum)
   222  	byteOrder.PutUint32(serializedData[4:8], loc.fileOffset)
   223  	byteOrder.PutUint32(serializedData[8:12], loc.blockLen)
   224  	return serializedData[:]
   225  }
   226  
   227  // blockFilePath return the file path for the provided block file number.
   228  func blockFilePath(dbPath string, fileNum uint32) string {
   229  	fileName := fmt.Sprintf(blockFilenameTemplate, fileNum)
   230  	return filepath.Join(dbPath, fileName)
   231  }
   232  
   233  // openWriteFile returns a file handle for the passed flat file number in
   234  // read/write mode.  The file will be created if needed.  It is typically used
   235  // for the current file that will have all new data appended.  Unlike openFile,
   236  // this function does not keep track of the open file and it is not subject to
   237  // the maxOpenFiles limit.
   238  func (s *blockStore) openWriteFile(fileNum uint32) (filer, error) {
   239  	// The current block file needs to be read-write so it is possible to
   240  	// append to it.  Also, it shouldn't be part of the least recently used
   241  	// file.
   242  	filePath := blockFilePath(s.basePath, fileNum)
   243  	file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0666)
   244  	if err != nil {
   245  		str := fmt.Sprintf("failed to open file %q: %v", filePath, err)
   246  		return nil, makeDbErr(database.ErrDriverSpecific, str, err)
   247  	}
   248  
   249  	return file, nil
   250  }
   251  
   252  // openFile returns a read-only file handle for the passed flat file number.
   253  // The function also keeps track of the open files, performs least recently
   254  // used tracking, and limits the number of open files to maxOpenFiles by closing
   255  // the least recently used file as needed.
   256  //
   257  // This function MUST be called with the overall files mutex (s.obfMutex) locked
   258  // for WRITES.
   259  func (s *blockStore) openFile(fileNum uint32) (*lockableFile, error) {
   260  	// Open the appropriate file as read-only.
   261  	filePath := blockFilePath(s.basePath, fileNum)
   262  	file, err := os.Open(filePath)
   263  	if err != nil {
   264  		return nil, makeDbErr(database.ErrDriverSpecific, err.Error(),
   265  			err)
   266  	}
   267  	blockFile := &lockableFile{file: file}
   268  
   269  	// Close the least recently used file if the file exceeds the max
   270  	// allowed open files.  This is not done until after the file open in
   271  	// case the file fails to open, there is no need to close any files.
   272  	//
   273  	// A write lock is required on the LRU list here to protect against
   274  	// modifications happening as already open files are read from and
   275  	// shuffled to the front of the list.
   276  	//
   277  	// Also, add the file that was just opened to the front of the least
   278  	// recently used list to indicate it is the most recently used file and
   279  	// therefore should be closed last.
   280  	s.lruMutex.Lock()
   281  	lruList := s.openBlocksLRU
   282  	if lruList.Len() >= maxOpenFiles {
   283  		lruFileNum := lruList.Remove(lruList.Back()).(uint32)
   284  		oldBlockFile := s.openBlockFiles[lruFileNum]
   285  
   286  		// Close the old file under the write lock for the file in case
   287  		// any readers are currently reading from it so it's not closed
   288  		// out from under them.
   289  		oldBlockFile.Lock()
   290  		_ = oldBlockFile.file.Close()
   291  		oldBlockFile.Unlock()
   292  
   293  		delete(s.openBlockFiles, lruFileNum)
   294  		delete(s.fileNumToLRUElem, lruFileNum)
   295  	}
   296  	s.fileNumToLRUElem[fileNum] = lruList.PushFront(fileNum)
   297  	s.lruMutex.Unlock()
   298  
   299  	// Store a reference to it in the open block files map.
   300  	s.openBlockFiles[fileNum] = blockFile
   301  
   302  	return blockFile, nil
   303  }
   304  
   305  // deleteFile removes the block file for the passed flat file number.  The file
   306  // must already be closed and it is the responsibility of the caller to do any
   307  // other state cleanup necessary.
   308  func (s *blockStore) deleteFile(fileNum uint32) error {
   309  	filePath := blockFilePath(s.basePath, fileNum)
   310  	if err := os.Remove(filePath); err != nil {
   311  		return makeDbErr(database.ErrDriverSpecific, err.Error(), err)
   312  	}
   313  
   314  	return nil
   315  }
   316  
   317  // blockFile attempts to return an existing file handle for the passed flat file
   318  // number if it is already open as well as marking it as most recently used.  It
   319  // will also open the file when it's not already open subject to the rules
   320  // described in openFile.
   321  //
   322  // NOTE: The returned block file will already have the read lock acquired and
   323  // the caller MUST call .RUnlock() to release it once it has finished all read
   324  // operations.  This is necessary because otherwise it would be possible for a
   325  // separate goroutine to close the file after it is returned from here, but
   326  // before the caller has acquired a read lock.
   327  func (s *blockStore) blockFile(fileNum uint32) (*lockableFile, error) {
   328  	// When the requested block file is open for writes, return it.
   329  	wc := s.writeCursor
   330  	wc.RLock()
   331  	if fileNum == wc.curFileNum && wc.curFile.file != nil {
   332  		obf := wc.curFile
   333  		obf.RLock()
   334  		wc.RUnlock()
   335  		return obf, nil
   336  	}
   337  	wc.RUnlock()
   338  
   339  	// Try to return an open file under the overall files read lock.
   340  	s.obfMutex.RLock()
   341  	if obf, ok := s.openBlockFiles[fileNum]; ok {
   342  		s.lruMutex.Lock()
   343  		s.openBlocksLRU.MoveToFront(s.fileNumToLRUElem[fileNum])
   344  		s.lruMutex.Unlock()
   345  
   346  		obf.RLock()
   347  		s.obfMutex.RUnlock()
   348  		return obf, nil
   349  	}
   350  	s.obfMutex.RUnlock()
   351  
   352  	// Since the file isn't open already, need to check the open block files
   353  	// map again under write lock in case multiple readers got here and a
   354  	// separate one is already opening the file.
   355  	s.obfMutex.Lock()
   356  	if obf, ok := s.openBlockFiles[fileNum]; ok {
   357  		obf.RLock()
   358  		s.obfMutex.Unlock()
   359  		return obf, nil
   360  	}
   361  
   362  	// The file isn't open, so open it while potentially closing the least
   363  	// recently used one as needed.
   364  	obf, err := s.openFileFunc(fileNum)
   365  	if err != nil {
   366  		s.obfMutex.Unlock()
   367  		return nil, err
   368  	}
   369  	obf.RLock()
   370  	s.obfMutex.Unlock()
   371  	return obf, nil
   372  }
   373  
   374  // writeData is a helper function for writeBlock which writes the provided data
   375  // at the current write offset and updates the write cursor accordingly.  The
   376  // field name parameter is only used when there is an error to provide a nicer
   377  // error message.
   378  //
   379  // The write cursor will be advanced the number of bytes actually written in the
   380  // event of failure.
   381  //
   382  // NOTE: This function MUST be called with the write cursor current file lock
   383  // held and must only be called during a write transaction so it is effectively
   384  // locked for writes.  Also, the write cursor current file must NOT be nil.
   385  func (s *blockStore) writeData(data []byte, fieldName string) error {
   386  	wc := s.writeCursor
   387  	n, err := wc.curFile.file.WriteAt(data, int64(wc.curOffset))
   388  	wc.curOffset += uint32(n)
   389  	if err != nil {
   390  		str := fmt.Sprintf("failed to write %s to file %d at "+
   391  			"offset %d: %v", fieldName, wc.curFileNum,
   392  			wc.curOffset-uint32(n), err)
   393  		return makeDbErr(database.ErrDriverSpecific, str, err)
   394  	}
   395  
   396  	return nil
   397  }
   398  
   399  // writeBlock appends the specified raw block bytes to the store's write cursor
   400  // location and increments it accordingly.  When the block would exceed the max
   401  // file size for the current flat file, this function will close the current
   402  // file, create the next file, update the write cursor, and write the block to
   403  // the new file.
   404  //
   405  // The write cursor will also be advanced the number of bytes actually written
   406  // in the event of failure.
   407  //
   408  // Format: <network><block length><serialized block><checksum>
   409  func (s *blockStore) writeBlock(rawBlock []byte) (blockLocation, error) {
   410  	// Compute how many bytes will be written.
   411  	// 4 bytes each for block network + 4 bytes for block length +
   412  	// length of raw block + 4 bytes for checksum.
   413  	blockLen := uint32(len(rawBlock))
   414  	fullLen := blockLen + 12
   415  
   416  	// Move to the next block file if adding the new block would exceed the
   417  	// max allowed size for the current block file.  Also detect overflow
   418  	// to be paranoid, even though it isn't possible currently, numbers
   419  	// might change in the future to make it possible.
   420  	//
   421  	// NOTE: The writeCursor.offset field isn't protected by the mutex
   422  	// since it's only read/changed during this function which can only be
   423  	// called during a write transaction, of which there can be only one at
   424  	// a time.
   425  	wc := s.writeCursor
   426  	finalOffset := wc.curOffset + fullLen
   427  	if finalOffset < wc.curOffset || finalOffset > s.maxBlockFileSize {
   428  		// This is done under the write cursor lock since the curFileNum
   429  		// field is accessed elsewhere by readers.
   430  		//
   431  		// Close the current write file to force a read-only reopen
   432  		// with LRU tracking.  The close is done under the write lock
   433  		// for the file to prevent it from being closed out from under
   434  		// any readers currently reading from it.
   435  		wc.Lock()
   436  		wc.curFile.Lock()
   437  		if wc.curFile.file != nil {
   438  			_ = wc.curFile.file.Close()
   439  			wc.curFile.file = nil
   440  		}
   441  		wc.curFile.Unlock()
   442  
   443  		// Start writes into next file.
   444  		wc.curFileNum++
   445  		wc.curOffset = 0
   446  		wc.Unlock()
   447  	}
   448  
   449  	// All writes are done under the write lock for the file to ensure any
   450  	// readers are finished and blocked first.
   451  	wc.curFile.Lock()
   452  	defer wc.curFile.Unlock()
   453  
   454  	// Open the current file if needed.  This will typically only be the
   455  	// case when moving to the next file to write to or on initial database
   456  	// load.  However, it might also be the case if rollbacks happened after
   457  	// file writes started during a transaction commit.
   458  	if wc.curFile.file == nil {
   459  		file, err := s.openWriteFileFunc(wc.curFileNum)
   460  		if err != nil {
   461  			return blockLocation{}, err
   462  		}
   463  		wc.curFile.file = file
   464  	}
   465  
   466  	// Bitcoin network.
   467  	origOffset := wc.curOffset
   468  	hasher := crc32.New(castagnoli)
   469  	var scratch [4]byte
   470  	byteOrder.PutUint32(scratch[:], uint32(s.network))
   471  	if err := s.writeData(scratch[:], "network"); err != nil {
   472  		return blockLocation{}, err
   473  	}
   474  	_, _ = hasher.Write(scratch[:])
   475  
   476  	// Block length.
   477  	byteOrder.PutUint32(scratch[:], blockLen)
   478  	if err := s.writeData(scratch[:], "block length"); err != nil {
   479  		return blockLocation{}, err
   480  	}
   481  	_, _ = hasher.Write(scratch[:])
   482  
   483  	// Serialized block.
   484  	if err := s.writeData(rawBlock, "block"); err != nil {
   485  		return blockLocation{}, err
   486  	}
   487  	_, _ = hasher.Write(rawBlock)
   488  
   489  	// Castagnoli CRC-32 as a checksum of all the previous.
   490  	if err := s.writeData(hasher.Sum(nil), "checksum"); err != nil {
   491  		return blockLocation{}, err
   492  	}
   493  
   494  	loc := blockLocation{
   495  		blockFileNum: wc.curFileNum,
   496  		fileOffset:   origOffset,
   497  		blockLen:     fullLen,
   498  	}
   499  	return loc, nil
   500  }
   501  
   502  // readBlock reads the specified block record and returns the serialized block.
   503  // It ensures the integrity of the block data by checking that the serialized
   504  // network matches the current network associated with the block store and
   505  // comparing the calculated checksum against the one stored in the flat file.
   506  // This function also automatically handles all file management such as opening
   507  // and closing files as necessary to stay within the maximum allowed open files
   508  // limit.
   509  //
   510  // Returns ErrDriverSpecific if the data fails to read for any reason and
   511  // ErrCorruption if the checksum of the read data doesn't match the checksum
   512  // read from the file.
   513  //
   514  // Format: <network><block length><serialized block><checksum>
   515  func (s *blockStore) readBlock(hash *chainhash.Hash, loc blockLocation) ([]byte, error) {
   516  	// Get the referenced block file handle opening the file as needed.  The
   517  	// function also handles closing files as needed to avoid going over the
   518  	// max allowed open files.
   519  	blockFile, err := s.blockFile(loc.blockFileNum)
   520  	if err != nil {
   521  		return nil, err
   522  	}
   523  
   524  	serializedData := make([]byte, loc.blockLen)
   525  	n, err := blockFile.file.ReadAt(serializedData, int64(loc.fileOffset))
   526  	blockFile.RUnlock()
   527  	if err != nil {
   528  		str := fmt.Sprintf("failed to read block %s from file %d, "+
   529  			"offset %d: %v", hash, loc.blockFileNum, loc.fileOffset,
   530  			err)
   531  		return nil, makeDbErr(database.ErrDriverSpecific, str, err)
   532  	}
   533  
   534  	// Calculate the checksum of the read data and ensure it matches the
   535  	// serialized checksum.  This will detect any data corruption in the
   536  	// flat file without having to do much more expensive merkle root
   537  	// calculations on the loaded block.
   538  	serializedChecksum := binary.BigEndian.Uint32(serializedData[n-4:])
   539  	calculatedChecksum := crc32.Checksum(serializedData[:n-4], castagnoli)
   540  	if serializedChecksum != calculatedChecksum {
   541  		str := fmt.Sprintf("block data for block %s checksum "+
   542  			"does not match - got %x, want %x", hash,
   543  			calculatedChecksum, serializedChecksum)
   544  		return nil, makeDbErr(database.ErrCorruption, str, nil)
   545  	}
   546  
   547  	// The network associated with the block must match the current active
   548  	// network, otherwise somebody probably put the block files for the
   549  	// wrong network in the directory.
   550  	serializedNet := byteOrder.Uint32(serializedData[:4])
   551  	if serializedNet != uint32(s.network) {
   552  		str := fmt.Sprintf("block data for block %s is for the "+
   553  			"wrong network - got %d, want %d", hash, serializedNet,
   554  			uint32(s.network))
   555  		return nil, makeDbErr(database.ErrDriverSpecific, str, nil)
   556  	}
   557  
   558  	// The raw block excludes the network, length of the block, and
   559  	// checksum.
   560  	return serializedData[8 : n-4], nil
   561  }
   562  
   563  // readBlockRegion reads the specified amount of data at the provided offset for
   564  // a given block location.  The offset is relative to the start of the
   565  // serialized block (as opposed to the beginning of the block record).  This
   566  // function automatically handles all file management such as opening and
   567  // closing files as necessary to stay within the maximum allowed open files
   568  // limit.
   569  //
   570  // Returns ErrDriverSpecific if the data fails to read for any reason.
   571  func (s *blockStore) readBlockRegion(loc blockLocation, offset, numBytes uint32) ([]byte, error) {
   572  	// Get the referenced block file handle opening the file as needed.  The
   573  	// function also handles closing files as needed to avoid going over the
   574  	// max allowed open files.
   575  	blockFile, err := s.blockFile(loc.blockFileNum)
   576  	if err != nil {
   577  		return nil, err
   578  	}
   579  
   580  	// Regions are offsets into the actual block, however the serialized
   581  	// data for a block includes an initial 4 bytes for network + 4 bytes
   582  	// for block length.  Thus, add 8 bytes to adjust.
   583  	readOffset := loc.fileOffset + 8 + offset
   584  	serializedData := make([]byte, numBytes)
   585  	_, err = blockFile.file.ReadAt(serializedData, int64(readOffset))
   586  	blockFile.RUnlock()
   587  	if err != nil {
   588  		str := fmt.Sprintf("failed to read region from block file %d, "+
   589  			"offset %d, len %d: %v", loc.blockFileNum, readOffset,
   590  			numBytes, err)
   591  		return nil, makeDbErr(database.ErrDriverSpecific, str, err)
   592  	}
   593  
   594  	return serializedData, nil
   595  }
   596  
   597  // syncBlocks performs a file system sync on the flat file associated with the
   598  // store's current write cursor.  It is safe to call even when there is not a
   599  // current write file in which case it will have no effect.
   600  //
   601  // This is used when flushing cached metadata updates to disk to ensure all the
   602  // block data is fully written before updating the metadata.  This ensures the
   603  // metadata and block data can be properly reconciled in failure scenarios.
   604  func (s *blockStore) syncBlocks() error {
   605  	wc := s.writeCursor
   606  	wc.RLock()
   607  	defer wc.RUnlock()
   608  
   609  	// Nothing to do if there is no current file associated with the write
   610  	// cursor.
   611  	wc.curFile.RLock()
   612  	defer wc.curFile.RUnlock()
   613  	if wc.curFile.file == nil {
   614  		return nil
   615  	}
   616  
   617  	// Sync the file to disk.
   618  	if err := wc.curFile.file.Sync(); err != nil {
   619  		str := fmt.Sprintf("failed to sync file %d: %v", wc.curFileNum,
   620  			err)
   621  		return makeDbErr(database.ErrDriverSpecific, str, err)
   622  	}
   623  
   624  	return nil
   625  }
   626  
   627  // handleRollback rolls the block files on disk back to the provided file number
   628  // and offset.  This involves potentially deleting and truncating the files that
   629  // were partially written.
   630  //
   631  // There are effectively two scenarios to consider here:
   632  //  1. Transient write failures from which recovery is possible
   633  //  2. More permanent failures such as hard disk death and/or removal
   634  //
   635  // In either case, the write cursor will be repositioned to the old block file
   636  // offset regardless of any other errors that occur while attempting to undo
   637  // writes.
   638  //
   639  // For the first scenario, this will lead to any data which failed to be undone
   640  // being overwritten and thus behaves as desired as the system continues to run.
   641  //
   642  // For the second scenario, the metadata which stores the current write cursor
   643  // position within the block files will not have been updated yet and thus if
   644  // the system eventually recovers (perhaps the hard drive is reconnected), it
   645  // will also lead to any data which failed to be undone being overwritten and
   646  // thus behaves as desired.
   647  //
   648  // Therefore, any errors are simply logged at a warning level rather than being
   649  // returned since there is nothing more that could be done about it anyways.
   650  func (s *blockStore) handleRollback(oldBlockFileNum, oldBlockOffset uint32) {
   651  	// Grab the write cursor mutex since it is modified throughout this
   652  	// function.
   653  	wc := s.writeCursor
   654  	wc.Lock()
   655  	defer wc.Unlock()
   656  
   657  	// Nothing to do if the rollback point is the same as the current write
   658  	// cursor.
   659  	if wc.curFileNum == oldBlockFileNum && wc.curOffset == oldBlockOffset {
   660  		return
   661  	}
   662  
   663  	// Regardless of any failures that happen below, reposition the write
   664  	// cursor to the old block file and offset.
   665  	defer func() {
   666  		wc.curFileNum = oldBlockFileNum
   667  		wc.curOffset = oldBlockOffset
   668  	}()
   669  
   670  	log.Debugf("ROLLBACK: Rolling back to file %d, offset %d",
   671  		oldBlockFileNum, oldBlockOffset)
   672  
   673  	// Close the current write file if it needs to be deleted.  Then delete
   674  	// all files that are newer than the provided rollback file while
   675  	// also moving the write cursor file backwards accordingly.
   676  	if wc.curFileNum > oldBlockFileNum {
   677  		wc.curFile.Lock()
   678  		if wc.curFile.file != nil {
   679  			_ = wc.curFile.file.Close()
   680  			wc.curFile.file = nil
   681  		}
   682  		wc.curFile.Unlock()
   683  	}
   684  	for ; wc.curFileNum > oldBlockFileNum; wc.curFileNum-- {
   685  		if err := s.deleteFileFunc(wc.curFileNum); err != nil {
   686  			log.Warnf("ROLLBACK: Failed to delete block file "+
   687  				"number %d: %v", wc.curFileNum, err)
   688  			return
   689  		}
   690  	}
   691  
   692  	// Open the file for the current write cursor if needed.
   693  	wc.curFile.Lock()
   694  	if wc.curFile.file == nil {
   695  		obf, err := s.openWriteFileFunc(wc.curFileNum)
   696  		if err != nil {
   697  			wc.curFile.Unlock()
   698  			log.Warnf("ROLLBACK: %v", err)
   699  			return
   700  		}
   701  		wc.curFile.file = obf
   702  	}
   703  
   704  	// Truncate the to the provided rollback offset.
   705  	if err := wc.curFile.file.Truncate(int64(oldBlockOffset)); err != nil {
   706  		wc.curFile.Unlock()
   707  		log.Warnf("ROLLBACK: Failed to truncate file %d: %v",
   708  			wc.curFileNum, err)
   709  		return
   710  	}
   711  
   712  	// Sync the file to disk.
   713  	err := wc.curFile.file.Sync()
   714  	wc.curFile.Unlock()
   715  	if err != nil {
   716  		log.Warnf("ROLLBACK: Failed to sync file %d: %v",
   717  			wc.curFileNum, err)
   718  		return
   719  	}
   720  }
   721  
   722  // scanBlockFiles searches the database directory for all flat block files to
   723  // find the first file, last file, and the end of the most recent file.  The
   724  // position at the last file is considered the current write cursor which is
   725  // also stored in the metadata.  Thus, it is used to detect unexpected shutdowns
   726  // in the middle of writes so the block files can be reconciled.
   727  func scanBlockFiles(dbPath string) (int, int, uint32, error) {
   728  	firstFile, lastFile, lastFileLen, err := int(-1), int(-1), uint32(0), error(nil)
   729  
   730  	files, err := filepath.Glob(filepath.Join(dbPath, "*"+blockFileExtension))
   731  	if err != nil {
   732  		return 0, 0, 0, err
   733  	}
   734  	sort.Strings(files)
   735  
   736  	// Return early if there's no block files.
   737  	if len(files) == 0 {
   738  		return firstFile, lastFile, lastFileLen, nil
   739  	}
   740  
   741  	// Grab the first and last file's number.
   742  	firstFile, err = strconv.Atoi(strings.TrimSuffix(filepath.Base(files[0]), blockFileExtension))
   743  	if err != nil {
   744  		return 0, 0, 0, fmt.Errorf("scanBlockFiles error: %v", err)
   745  	}
   746  	lastFile, err = strconv.Atoi(strings.TrimSuffix(filepath.Base(files[len(files)-1]), blockFileExtension))
   747  	if err != nil {
   748  		return 0, 0, 0, fmt.Errorf("scanBlockFiles error: %v", err)
   749  	}
   750  
   751  	// Get the last file's length.
   752  	filePath := blockFilePath(dbPath, uint32(lastFile))
   753  	st, err := os.Stat(filePath)
   754  	if err != nil {
   755  		return 0, 0, 0, err
   756  	}
   757  	lastFileLen = uint32(st.Size())
   758  
   759  	log.Tracef("Scan found latest block file #%d with length %d", lastFile, lastFileLen)
   760  
   761  	return firstFile, lastFile, lastFileLen, err
   762  }
   763  
   764  // newBlockStore returns a new block store with the current block file number
   765  // and offset set and all fields initialized.
   766  func newBlockStore(basePath string, network wire.BitcoinNet) (*blockStore, error) {
   767  	// Look for the end of the latest block to file to determine what the
   768  	// write cursor position is from the viewpoing of the block files on
   769  	// disk.
   770  	_, fileNum, fileOff, err := scanBlockFiles(basePath)
   771  	if err != nil {
   772  		return nil, err
   773  	}
   774  	if fileNum == -1 {
   775  		fileNum = 0
   776  		fileOff = 0
   777  	}
   778  
   779  	store := &blockStore{
   780  		network:          network,
   781  		basePath:         basePath,
   782  		maxBlockFileSize: maxBlockFileSize,
   783  		openBlockFiles:   make(map[uint32]*lockableFile),
   784  		openBlocksLRU:    list.New(),
   785  		fileNumToLRUElem: make(map[uint32]*list.Element),
   786  
   787  		writeCursor: &writeCursor{
   788  			curFile:    &lockableFile{},
   789  			curFileNum: uint32(fileNum),
   790  			curOffset:  fileOff,
   791  		},
   792  	}
   793  	store.openFileFunc = store.openFile
   794  	store.openWriteFileFunc = store.openWriteFile
   795  	store.deleteFileFunc = store.deleteFile
   796  	return store, nil
   797  }