gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/filesystem/siadir/siadir.go (about) 1 package siadir 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "sync" 8 "time" 9 10 "gitlab.com/NebulousLabs/errors" 11 "go.sia.tech/siad/modules" 12 13 "gitlab.com/SkynetLabs/skyd/skymodules" 14 ) 15 16 type ( 17 // SiaDir contains the metadata information about a renter directory 18 SiaDir struct { 19 metadata Metadata 20 21 // path is the path of the SiaDir folder. 22 path string 23 24 // Utility fields 25 deleted bool 26 deps modules.Dependencies 27 mu sync.Mutex 28 } 29 30 // Metadata is the metadata that is saved to disk as a .siadir file 31 Metadata struct { 32 // For each field in the metadata there is an aggregate value and a 33 // siadir specific value. If a field has the aggregate prefix it means 34 // that the value takes into account all the siafiles and siadirs in the 35 // sub tree. The definition of aggregate and siadir specific values is 36 // otherwise the same. 37 // 38 // Health is the health of the most in need siafile that is not stuck 39 // 40 // LastHealthCheckTime is the oldest LastHealthCheckTime of any of the 41 // siafiles in the siadir and is the last time the health was calculated 42 // by the health loop 43 // 44 // MinRedundancy is the minimum redundancy of any of the siafiles in the 45 // siadir 46 // 47 // ModTime is the last time any of the siafiles in the siadir was 48 // updated 49 // 50 // NumFiles is the total number of siafiles in a siadir 51 // 52 // NumLostFiles is the total number of siafiles in a siadir that are not on 53 // disk and have a health of <= 0 54 // 55 // NumStuckChunks is the sum of all the Stuck Chunks of any of the 56 // siafiles in the siadir 57 // 58 // NumSubDirs is the number of sub-siadirs in a siadir 59 // 60 // NumUnfinishedFiles is the total number of unfinished siafiles in a 61 // siadir 62 // 63 // Size is the total amount of data stored in the siafiles of the siadir 64 // 65 // StuckHealth is the health of the most in need siafile in the siadir, 66 // stuck or not stuck 67 68 // The following fields are aggregate values of the siadir. These values are 69 // the totals of the siadir and any sub siadirs, or are calculated based on 70 // all the values in the subtree 71 AggregateHealth float64 `json:"aggregatehealth"` 72 AggregateLastHealthCheckTime time.Time `json:"aggregatelasthealthchecktime"` 73 AggregateMinRedundancy float64 `json:"aggregateminredundancy"` 74 AggregateModTime time.Time `json:"aggregatemodtime"` 75 AggregateNumFiles uint64 `json:"aggregatenumfiles"` 76 AggregateNumLostFiles uint64 `json:"aggregatenumlostfiles"` 77 AggregateNumStuckChunks uint64 `json:"aggregatenumstuckchunks"` 78 AggregateNumSubDirs uint64 `json:"aggregatenumsubdirs"` 79 AggregateNumUnfinishedFiles uint64 `json:"aggregatenumunfinishedfiles"` 80 AggregateRemoteHealth float64 `json:"aggregateremotehealth"` 81 AggregateRepairSize uint64 `json:"aggregaterepairsize"` 82 AggregateSize uint64 `json:"aggregatesize"` 83 AggregateStuckHealth float64 `json:"aggregatestuckhealth"` 84 AggregateStuckSize uint64 `json:"aggregatestucksize"` 85 86 // Aggregate Skynet Specific Stats 87 AggregateSkynetFiles uint64 `json:"aggregateskynetfiles"` 88 AggregateSkynetSize uint64 `json:"aggregateskynetsize"` 89 90 // The following fields are information specific to the siadir that is not 91 // an aggregate of the entire sub directory tree 92 Health float64 `json:"health"` 93 LastHealthCheckTime time.Time `json:"lasthealthchecktime"` 94 MinRedundancy float64 `json:"minredundancy"` 95 Mode os.FileMode `json:"mode"` 96 ModTime time.Time `json:"modtime"` 97 NumFiles uint64 `json:"numfiles"` 98 NumLostFiles uint64 `json:"numlostfiles"` 99 NumStuckChunks uint64 `json:"numstuckchunks"` 100 NumSubDirs uint64 `json:"numsubdirs"` 101 NumUnfinishedFiles uint64 `json:"numunfinishedfiles"` 102 RemoteHealth float64 `json:"remotehealth"` 103 RepairSize uint64 `json:"repairsize"` 104 Size uint64 `json:"size"` 105 StuckHealth float64 `json:"stuckhealth"` 106 StuckSize uint64 `json:"stucksize"` 107 108 // Skynet Specific Stats 109 SkynetFiles uint64 `json:"skynetfiles"` 110 SkynetSize uint64 `json:"skynetsize"` 111 112 // Version is the used version of the header file. 113 Version string `json:"version"` 114 } 115 ) 116 117 // EqualMetadatas compares two Metadatas. If using this function to check 118 // persistence the time fields should be checked separately due to how time is 119 // persisted 120 func EqualMetadatas(md1, md2 Metadata) (err error) { 121 // Check Aggregate Fields 122 if md1.AggregateHealth != md2.AggregateHealth { 123 err = errors.Compose(err, fmt.Errorf("AggregateHealth not equal, %v and %v", md1.AggregateHealth, md2.AggregateHealth)) 124 } 125 if md1.AggregateLastHealthCheckTime != md2.AggregateLastHealthCheckTime { 126 err = errors.Compose(err, fmt.Errorf("AggregateLastHealthCheckTimes not equal, %v and %v", md1.AggregateLastHealthCheckTime, md2.AggregateLastHealthCheckTime)) 127 } 128 if md1.AggregateMinRedundancy != md2.AggregateMinRedundancy { 129 err = errors.Compose(err, fmt.Errorf("AggregateMinRedundancy not equal, %v and %v", md1.AggregateMinRedundancy, md2.AggregateMinRedundancy)) 130 } 131 if md1.AggregateModTime != md2.AggregateModTime { 132 err = errors.Compose(err, fmt.Errorf("AggregateModTimes not equal, %v and %v", md1.AggregateModTime, md2.AggregateModTime)) 133 } 134 if md1.AggregateNumFiles != md2.AggregateNumFiles { 135 err = errors.Compose(err, fmt.Errorf("AggregateNumFiles not equal, %v and %v", md1.AggregateNumFiles, md2.AggregateNumFiles)) 136 } 137 if md1.AggregateNumLostFiles != md2.AggregateNumLostFiles { 138 err = errors.Compose(err, fmt.Errorf("AggregateNumLostFiles not equal, %v and %v", md1.AggregateNumLostFiles, md2.AggregateNumLostFiles)) 139 } 140 if md1.AggregateNumStuckChunks != md2.AggregateNumStuckChunks { 141 err = errors.Compose(err, fmt.Errorf("AggregateNumStuckChunks not equal, %v and %v", md1.AggregateNumStuckChunks, md2.AggregateNumStuckChunks)) 142 } 143 if md1.AggregateNumSubDirs != md2.AggregateNumSubDirs { 144 err = errors.Compose(err, fmt.Errorf("AggregateNumSubDirs not equal, %v and %v", md1.AggregateNumSubDirs, md2.AggregateNumSubDirs)) 145 } 146 if md1.AggregateNumUnfinishedFiles != md2.AggregateNumUnfinishedFiles { 147 err = errors.Compose(err, fmt.Errorf("AggregateNumUnfinishedFiles not equal, %v and %v", md1.AggregateNumUnfinishedFiles, md2.AggregateNumUnfinishedFiles)) 148 } 149 if md1.AggregateRemoteHealth != md2.AggregateRemoteHealth { 150 err = errors.Compose(err, fmt.Errorf("AggregateRemoteHealth not equal, %v and %v", md1.AggregateRemoteHealth, md2.AggregateRemoteHealth)) 151 } 152 if md1.AggregateRepairSize != md2.AggregateRepairSize { 153 err = errors.Compose(err, fmt.Errorf("AggregateRepairSize not equal, %v and %v", md1.AggregateRepairSize, md2.AggregateRepairSize)) 154 } 155 if md1.AggregateSize != md2.AggregateSize { 156 err = errors.Compose(err, fmt.Errorf("AggregateSize not equal, %v and %v", md1.AggregateSize, md2.AggregateSize)) 157 } 158 if md1.AggregateStuckHealth != md2.AggregateStuckHealth { 159 err = errors.Compose(err, fmt.Errorf("AggregateStuckHealth not equal, %v and %v", md1.AggregateStuckHealth, md2.AggregateStuckHealth)) 160 } 161 if md1.AggregateStuckSize != md2.AggregateStuckSize { 162 err = errors.Compose(err, fmt.Errorf("AggregateStuckSize not equal, %v and %v", md1.AggregateStuckSize, md2.AggregateStuckSize)) 163 } 164 165 // Aggregate Skynet Fields 166 if md1.AggregateSkynetFiles != md2.AggregateSkynetFiles { 167 err = errors.Compose(err, fmt.Errorf("AggregateSkynetFiles not equal, %v and %v", md1.AggregateSkynetFiles, md2.AggregateSkynetFiles)) 168 } 169 if md1.AggregateSkynetSize != md2.AggregateSkynetSize { 170 err = errors.Compose(err, fmt.Errorf("AggregateSkynetSize not equal, %v and %v", md1.AggregateSkynetSize, md2.AggregateSkynetSize)) 171 } 172 173 // Check SiaDir Fields 174 if md1.Health != md2.Health { 175 err = errors.Compose(err, fmt.Errorf("Healths not equal, %v and %v", md1.Health, md2.Health)) 176 } 177 if md1.LastHealthCheckTime != md2.LastHealthCheckTime { 178 err = errors.Compose(err, fmt.Errorf("LastHealthCheckTime not equal, %v and %v", md1.LastHealthCheckTime, md2.LastHealthCheckTime)) 179 } 180 if md1.MinRedundancy != md2.MinRedundancy { 181 err = errors.Compose(err, fmt.Errorf("MinRedundancy not equal, %v and %v", md1.MinRedundancy, md2.MinRedundancy)) 182 } 183 if md1.ModTime != md2.ModTime { 184 err = errors.Compose(err, fmt.Errorf("ModTime not equal, %v and %v", md1.ModTime, md2.ModTime)) 185 } 186 if md1.NumFiles != md2.NumFiles { 187 err = errors.Compose(err, fmt.Errorf("NumFiles not equal, %v and %v", md1.NumFiles, md2.NumFiles)) 188 } 189 if md1.NumLostFiles != md2.NumLostFiles { 190 err = errors.Compose(err, fmt.Errorf("NumLostFiles not equal, %v and %v", md1.NumLostFiles, md2.NumLostFiles)) 191 } 192 if md1.NumStuckChunks != md2.NumStuckChunks { 193 err = errors.Compose(err, fmt.Errorf("NumStuckChunks not equal, %v and %v", md1.NumStuckChunks, md2.NumStuckChunks)) 194 } 195 if md1.NumSubDirs != md2.NumSubDirs { 196 err = errors.Compose(err, fmt.Errorf("NumSubDirs not equal, %v and %v", md1.NumSubDirs, md2.NumSubDirs)) 197 } 198 if md1.NumUnfinishedFiles != md2.NumUnfinishedFiles { 199 err = errors.Compose(err, fmt.Errorf("NumUnfinishedFiles not equal, %v and %v", md1.NumUnfinishedFiles, md2.NumUnfinishedFiles)) 200 } 201 if md1.RemoteHealth != md2.RemoteHealth { 202 err = errors.Compose(err, fmt.Errorf("RemoteHealth not equal, %v and %v", md1.RemoteHealth, md2.RemoteHealth)) 203 } 204 if md1.RepairSize != md2.RepairSize { 205 err = errors.Compose(err, fmt.Errorf("RepairSize not equal, %v and %v", md1.RepairSize, md2.RepairSize)) 206 } 207 if md1.Size != md2.Size { 208 err = errors.Compose(err, fmt.Errorf("Sizes not equal, %v and %v", md1.Size, md2.Size)) 209 } 210 if md1.StuckHealth != md2.StuckHealth { 211 err = errors.Compose(err, fmt.Errorf("StuckHealth not equal, %v and %v", md1.StuckHealth, md2.StuckHealth)) 212 } 213 if md1.StuckSize != md2.StuckSize { 214 err = errors.Compose(err, fmt.Errorf("StuckSize not equal, %v and %v", md1.StuckSize, md2.StuckSize)) 215 } 216 217 // Skynet Fields 218 if md1.SkynetFiles != md2.SkynetFiles { 219 err = errors.Compose(err, fmt.Errorf("SkynetFiles not equal, %v and %v", md1.SkynetFiles, md2.SkynetFiles)) 220 } 221 if md1.SkynetSize != md2.SkynetSize { 222 err = errors.Compose(err, fmt.Errorf("SkynetSize not equal, %v and %v", md1.SkynetSize, md2.SkynetSize)) 223 } 224 return 225 } 226 227 // VerifyMetadataInit verifies that metadata was properly initialized. 228 func VerifyMetadataInit(md Metadata) error { 229 // Check that the modTimes are not Zero 230 if md.AggregateModTime.IsZero() { 231 return errors.New("AggregateModTime not initialized") 232 } 233 if md.ModTime.IsZero() { 234 return errors.New("ModTime not initialized") 235 } 236 237 // All the rest of the metadata should be default values 238 initMetadata := Metadata{ 239 AggregateHealth: DefaultDirHealth, 240 AggregateMinRedundancy: DefaultDirRedundancy, 241 AggregateModTime: md.AggregateModTime, 242 AggregateRemoteHealth: DefaultDirHealth, 243 AggregateStuckHealth: DefaultDirHealth, 244 245 Health: DefaultDirHealth, 246 MinRedundancy: DefaultDirRedundancy, 247 ModTime: md.ModTime, 248 RemoteHealth: DefaultDirHealth, 249 StuckHealth: DefaultDirHealth, 250 } 251 252 return EqualMetadatas(md, initMetadata) 253 } 254 255 // mdPath returns the path of the SiaDir's metadata on disk. 256 func (sd *SiaDir) mdPath() string { 257 return filepath.Join(sd.path, skymodules.SiaDirExtension) 258 } 259 260 // Deleted returns the deleted field of the siaDir 261 func (sd *SiaDir) Deleted() bool { 262 sd.mu.Lock() 263 defer sd.mu.Unlock() 264 return sd.deleted 265 } 266 267 // Metadata returns the metadata of the SiaDir 268 func (sd *SiaDir) Metadata() Metadata { 269 sd.mu.Lock() 270 defer sd.mu.Unlock() 271 return sd.metadata 272 } 273 274 // Path returns the path of the SiaDir on disk. 275 func (sd *SiaDir) Path() string { 276 sd.mu.Lock() 277 defer sd.mu.Unlock() 278 return sd.path 279 } 280 281 // MDPath returns the path of the SiaDir's metadata on disk. 282 func (sd *SiaDir) MDPath() string { 283 sd.mu.Lock() 284 defer sd.mu.Unlock() 285 return sd.mdPath() 286 }