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 }