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