gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/host/contractmanager/persist.go (about)

     1  package contractmanager
     2  
     3  import (
     4  	"encoding/binary"
     5  	"os"
     6  	"path/filepath"
     7  	"sync/atomic"
     8  
     9  	"gitlab.com/NebulousLabs/fastrand"
    10  	"gitlab.com/SiaPrime/SiaPrime/build"
    11  	"gitlab.com/SiaPrime/SiaPrime/crypto"
    12  	"gitlab.com/SiaPrime/SiaPrime/persist"
    13  )
    14  
    15  type (
    16  	// savedStorageFolder contains fields that are saved automatically to disk
    17  	// for each storage folder.
    18  	savedStorageFolder struct {
    19  		Index uint16
    20  		Path  string
    21  		Usage []uint64
    22  	}
    23  
    24  	// savedSettings contains fields that are saved atomically to disk inside
    25  	// of the contract manager directory, alongside the WAL and log.
    26  	savedSettings struct {
    27  		SectorSalt     crypto.Hash
    28  		StorageFolders []savedStorageFolder
    29  	}
    30  )
    31  
    32  // savedStorageFolder returns the persistent version of the storage folder.
    33  func (sf *storageFolder) savedStorageFolder() savedStorageFolder {
    34  	ssf := savedStorageFolder{
    35  		Index: sf.index,
    36  		Path:  sf.path,
    37  		Usage: make([]uint64, len(sf.usage)),
    38  	}
    39  	copy(ssf.Usage, sf.usage)
    40  	return ssf
    41  }
    42  
    43  // initSettings will set the default settings for the contract manager.
    44  // initSettings should only be run for brand new contract maangers.
    45  func (cm *ContractManager) initSettings() error {
    46  	// Initialize the sector salt to a random value.
    47  	fastrand.Read(cm.sectorSalt[:])
    48  
    49  	// Ensure that the initialized defaults have stuck.
    50  	ss := cm.savedSettings()
    51  	err := persist.SaveJSON(settingsMetadata, &ss, filepath.Join(cm.persistDir, settingsFile))
    52  	if err != nil {
    53  		cm.log.Println("ERROR: unable to initialize settings file for contract manager:", err)
    54  		return build.ExtendErr("error saving contract manager after initialization", err)
    55  	}
    56  	return nil
    57  }
    58  
    59  // loadSettings will load the contract manager settings.
    60  func (cm *ContractManager) loadSettings() error {
    61  	var ss savedSettings
    62  	err := cm.dependencies.LoadFile(settingsMetadata, &ss, filepath.Join(cm.persistDir, settingsFile))
    63  	if os.IsNotExist(err) {
    64  		// There is no settings file, this must be the first time that the
    65  		// contract manager has been run. Initialize with default settings.
    66  		return cm.initSettings()
    67  	} else if err != nil {
    68  		cm.log.Println("ERROR: unable to load the contract manager settings file:", err)
    69  		return build.ExtendErr("error loading the contract manager settings file", err)
    70  	}
    71  
    72  	// Copy the saved settings into the contract manager.
    73  	cm.sectorSalt = ss.SectorSalt
    74  	for i := range ss.StorageFolders {
    75  		sf := new(storageFolder)
    76  		sf.index = ss.StorageFolders[i].Index
    77  		sf.path = ss.StorageFolders[i].Path
    78  		sf.usage = ss.StorageFolders[i].Usage
    79  		sf.metadataFile, err = cm.dependencies.OpenFile(filepath.Join(ss.StorageFolders[i].Path, metadataFile), os.O_RDWR, 0700)
    80  		if err != nil {
    81  			// Mark the folder as unavailable and log an error.
    82  			atomic.StoreUint64(&sf.atomicUnavailable, 1)
    83  			cm.log.Printf("ERROR: unable to open the %v sector metadata file: %v\n", sf.path, err)
    84  		}
    85  		sf.sectorFile, err = cm.dependencies.OpenFile(filepath.Join(ss.StorageFolders[i].Path, sectorFile), os.O_RDWR, 0700)
    86  		if err != nil {
    87  			// Mark the folder as unavailable and log an error.
    88  			atomic.StoreUint64(&sf.atomicUnavailable, 1)
    89  			cm.log.Printf("ERROR: unable to open the %v sector file: %v\n", sf.path, err)
    90  			if sf.metadataFile != nil {
    91  				sf.metadataFile.Close()
    92  			}
    93  		}
    94  		sf.availableSectors = make(map[sectorID]uint32)
    95  		cm.storageFolders[sf.index] = sf
    96  	}
    97  	return nil
    98  }
    99  
   100  // loadSectorLocations will read the metadata portion of each storage folder
   101  // file and load the sector location information into memory.
   102  func (cm *ContractManager) loadSectorLocations(sf *storageFolder) {
   103  	// Read the sector lookup table for this storage folder into memory.
   104  	sectorLookupBytes, err := readFullMetadata(sf.metadataFile, len(sf.usage)*storageFolderGranularity)
   105  	if err != nil {
   106  		atomic.AddUint64(&sf.atomicFailedReads, 1)
   107  		atomic.StoreUint64(&sf.atomicUnavailable, 1)
   108  		err = build.ComposeErrors(err, sf.metadataFile.Close())
   109  		err = build.ComposeErrors(err, sf.sectorFile.Close())
   110  		cm.log.Printf("ERROR: unable to read sector metadata for folder %v: %v\n", sf.path, err)
   111  		return
   112  	}
   113  	atomic.AddUint64(&sf.atomicSuccessfulReads, 1)
   114  
   115  	// Iterate through the sectors that are in-use and read their storage
   116  	// locations into memory.
   117  	sf.sectors = 0 // may be non-zero from WAL operations - they will be double counted here if not reset.
   118  	for _, sectorIndex := range usageSectors(sf.usage) {
   119  		readHead := sectorMetadataDiskSize * sectorIndex
   120  		var id sectorID
   121  		copy(id[:], sectorLookupBytes[readHead:readHead+12])
   122  		count := binary.LittleEndian.Uint16(sectorLookupBytes[readHead+12 : readHead+14])
   123  		sl := sectorLocation{
   124  			index:         sectorIndex,
   125  			storageFolder: sf.index,
   126  			count:         count,
   127  		}
   128  
   129  		// Add the sector to the sector location map.
   130  		cm.sectorLocations[id] = sl
   131  		sf.sectors++
   132  	}
   133  	atomic.StoreUint64(&sf.atomicUnavailable, 0)
   134  }
   135  
   136  // savedSettings returns the settings of the contract manager in an
   137  // easily-serializable form.
   138  func (cm *ContractManager) savedSettings() savedSettings {
   139  	ss := savedSettings{
   140  		SectorSalt: cm.sectorSalt,
   141  	}
   142  	for _, sf := range cm.storageFolders {
   143  		// Unset all of the usage bits in the storage folder for the queued sectors.
   144  		for _, sectorIndex := range sf.availableSectors {
   145  			sf.clearUsage(sectorIndex)
   146  		}
   147  
   148  		// Copy over the storage folder.
   149  		ss.StorageFolders = append(ss.StorageFolders, sf.savedStorageFolder())
   150  
   151  		// Re-set all of the usage bits for the queued sectors.
   152  		for _, sectorIndex := range sf.availableSectors {
   153  			sf.setUsage(sectorIndex)
   154  		}
   155  	}
   156  	return ss
   157  }