github.com/swiftstack/proxyfs@v0.0.0-20201223034610-5434d919416e/pfs-fsck/main.go (about) 1 package main 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "log" 9 "net" 10 "net/http" 11 "os" 12 "strconv" 13 "strings" 14 "time" 15 16 etcd "go.etcd.io/etcd/clientv3" 17 18 "github.com/swiftstack/cstruct" 19 "github.com/swiftstack/sortedmap" 20 21 "github.com/swiftstack/ProxyFS/conf" 22 "github.com/swiftstack/ProxyFS/headhunter" 23 ) 24 25 const ( 26 bPlusTreeCacheEvictHighLimitDefault = uint64(10010) 27 bPlusTreeCacheEvictLowLimitDefault = uint64(10000) 28 ) 29 30 type globalsStruct struct { 31 accountName string 32 alsoDump bool 33 bPlusTreeCache sortedmap.BPlusTreeCache 34 bPlusTreeCacheEvictHighLimit uint64 // [FSCK]BPlusTreeCacheEvictHighLimit 35 bPlusTreeCacheEvictLowLimit uint64 // [FSCK]BPlusTreeCacheEvictLowLimit 36 bPlusTreeObjectBPlusTreeLayout sortedmap.LayoutReport 37 checkpointEtcdKeyName string 38 checkpointContainerName string 39 checkpointObjectTrailerV3 *headhunter.CheckpointObjectTrailerV3Struct 40 checkpointObjectTrailerV3Buf []byte 41 checkpointObjectTrailerV3BufPos uint64 42 createdObjectsBPlusTreeLayout sortedmap.LayoutReport 43 deletedObjectsBPlusTreeLayout sortedmap.LayoutReport 44 elementOfBPlusTreeLayoutStructSize uint64 45 etcdAutoSyncInterval time.Duration 46 etcdClient *etcd.Client 47 etcdDialTimeout time.Duration 48 etcdEnabled bool 49 etcdEndpoints []string 50 etcdKV etcd.KV 51 etcdOpTimeout time.Duration 52 initialCheckpointHeader *headhunter.CheckpointHeaderStruct 53 inodeRecBPlusTreeLayout sortedmap.LayoutReport 54 logSegmentBPlusTreeLayout sortedmap.LayoutReport 55 noAuthIPAddr string 56 noAuthTCPPort uint16 57 noAuthURL string 58 recycleBinRefMap map[string]uint64 // Key: object path ; Value: # references 59 snapShotList []*headhunter.ElementOfSnapShotListStruct 60 uint64Size uint64 61 volumeName string 62 } 63 64 var globals globalsStruct 65 66 func main() { 67 var ( 68 bPlusTree sortedmap.BPlusTree 69 elementOfSnapShotList *headhunter.ElementOfSnapShotListStruct 70 err error 71 recycleBinObjectName string 72 recycleBinObjectReferences uint64 73 ) 74 75 setup() 76 77 globals.initialCheckpointHeader = fetchCheckpointHeader() 78 79 globals.recycleBinRefMap = make(map[string]uint64) 80 81 err = fetchCheckpointObjectTrailerV3() 82 if nil != err { 83 log.Fatal(err) 84 } 85 86 globals.inodeRecBPlusTreeLayout, err = fetchBPlusTreeLayout(globals.checkpointObjectTrailerV3.InodeRecBPlusTreeLayoutNumElements) 87 if nil != err { 88 log.Fatalf("fetching inodeRecBPlusTreeLayout failed: %v", err) 89 } 90 globals.logSegmentBPlusTreeLayout, err = fetchBPlusTreeLayout(globals.checkpointObjectTrailerV3.LogSegmentRecBPlusTreeLayoutNumElements) 91 if nil != err { 92 log.Fatalf("fetching logSegmentBPlusTreeLayout failed: %v", err) 93 } 94 globals.bPlusTreeObjectBPlusTreeLayout, err = fetchBPlusTreeLayout(globals.checkpointObjectTrailerV3.BPlusTreeObjectBPlusTreeLayoutNumElements) 95 if nil != err { 96 log.Fatalf("fetching bPlusTreeObjectBPlusTreeLayout failed: %v", err) 97 } 98 globals.createdObjectsBPlusTreeLayout, err = fetchBPlusTreeLayout(globals.checkpointObjectTrailerV3.CreatedObjectsBPlusTreeLayoutNumElements) 99 if nil != err { 100 log.Fatalf("fetching createdObjectsBPlusTreeLayout failed: %v", err) 101 } 102 globals.deletedObjectsBPlusTreeLayout, err = fetchBPlusTreeLayout(globals.checkpointObjectTrailerV3.DeletedObjectsBPlusTreeLayoutNumElements) 103 if nil != err { 104 log.Fatalf("fetching deletedObjectsBPlusTreeLayout failed: %v", err) 105 } 106 107 err = fetchSnapShotList(globals.checkpointObjectTrailerV3.SnapShotListNumElements) 108 if nil != err { 109 log.Fatalf("fetching snapShotList failed: %v", err) 110 } 111 112 if globals.checkpointObjectTrailerV3BufPos < globals.initialCheckpointHeader.CheckpointObjectTrailerStructObjectLength { 113 log.Fatalf("unmarshalling checkpointObjectTrailerV3Buf not fully consumed") 114 } 115 116 globals.bPlusTreeCache = sortedmap.NewBPlusTreeCache(globals.bPlusTreeCacheEvictLowLimit, globals.bPlusTreeCacheEvictHighLimit) 117 118 if uint64(0) != globals.checkpointObjectTrailerV3.InodeRecBPlusTreeObjectNumber { 119 bPlusTree, err = sortedmap.OldBPlusTree(globals.checkpointObjectTrailerV3.InodeRecBPlusTreeObjectNumber, 120 globals.checkpointObjectTrailerV3.InodeRecBPlusTreeObjectOffset, 121 globals.checkpointObjectTrailerV3.InodeRecBPlusTreeObjectLength, 122 sortedmap.CompareUint64, 123 &globals, 124 globals.bPlusTreeCache) 125 if nil != err { 126 log.Fatalf("loading of InodeRecBPlusTree failed: %v", err) 127 } 128 129 err = bPlusTree.Validate() 130 if nil != err { 131 log.Fatalf("validate of InodeRecBPlusTree failed: %v", err) 132 } 133 134 dumpKeysIfRequested(bPlusTree, "InodeRecBPlusTree") 135 } 136 137 if uint64(0) != globals.checkpointObjectTrailerV3.LogSegmentRecBPlusTreeObjectNumber { 138 bPlusTree, err = sortedmap.OldBPlusTree(globals.checkpointObjectTrailerV3.LogSegmentRecBPlusTreeObjectNumber, 139 globals.checkpointObjectTrailerV3.LogSegmentRecBPlusTreeObjectOffset, 140 globals.checkpointObjectTrailerV3.LogSegmentRecBPlusTreeObjectLength, 141 sortedmap.CompareUint64, 142 &globals, 143 globals.bPlusTreeCache) 144 if nil != err { 145 log.Fatalf("loading of LogSegmentRecBPlusTree failed: %v", err) 146 } 147 148 err = bPlusTree.Validate() 149 if nil != err { 150 log.Fatalf("validate of LogSegmentRecBPlusTree failed: %v", err) 151 } 152 153 dumpKeysIfRequested(bPlusTree, "LogSegmentRecBPlusTree") 154 } 155 156 if uint64(0) != globals.checkpointObjectTrailerV3.BPlusTreeObjectBPlusTreeObjectNumber { 157 bPlusTree, err = sortedmap.OldBPlusTree(globals.checkpointObjectTrailerV3.BPlusTreeObjectBPlusTreeObjectNumber, 158 globals.checkpointObjectTrailerV3.BPlusTreeObjectBPlusTreeObjectOffset, 159 globals.checkpointObjectTrailerV3.BPlusTreeObjectBPlusTreeObjectLength, 160 sortedmap.CompareUint64, 161 &globals, 162 globals.bPlusTreeCache) 163 if nil != err { 164 log.Fatalf("loading of BPlusTreeObjectBPlusTree failed: %v", err) 165 } 166 167 err = bPlusTree.Validate() 168 if nil != err { 169 log.Fatalf("validate of BPlusTreeObjectBPlusTree failed: %v", err) 170 } 171 172 dumpKeysIfRequested(bPlusTree, "BPlusTreeObjectBPlusTree") 173 } 174 175 for _, elementOfSnapShotList = range globals.snapShotList { 176 if uint64(0) != elementOfSnapShotList.InodeRecBPlusTreeObjectNumber { 177 bPlusTree, err = sortedmap.OldBPlusTree(elementOfSnapShotList.InodeRecBPlusTreeObjectNumber, 178 elementOfSnapShotList.InodeRecBPlusTreeObjectOffset, 179 elementOfSnapShotList.InodeRecBPlusTreeObjectLength, 180 sortedmap.CompareUint64, 181 &globals, 182 globals.bPlusTreeCache) 183 if nil != err { 184 log.Fatalf("loading of SnapShot.ID==%d InodeRecBPlusTree failed: %v", elementOfSnapShotList.ID, err) 185 } 186 187 err = bPlusTree.Validate() 188 if nil != err { 189 log.Fatalf("validate of SnapShot.ID==%d InodeRecBPlusTree failed: %v", elementOfSnapShotList.ID, err) 190 } 191 192 dumpKeysIfRequested(bPlusTree, fmt.Sprintf("InodeRecBPlusTree for SnapShotID==%d", elementOfSnapShotList.ID)) 193 } 194 195 if uint64(0) != elementOfSnapShotList.LogSegmentRecBPlusTreeObjectNumber { 196 bPlusTree, err = sortedmap.OldBPlusTree(elementOfSnapShotList.LogSegmentRecBPlusTreeObjectNumber, 197 elementOfSnapShotList.LogSegmentRecBPlusTreeObjectOffset, 198 elementOfSnapShotList.LogSegmentRecBPlusTreeObjectLength, 199 sortedmap.CompareUint64, 200 &globals, 201 globals.bPlusTreeCache) 202 if nil != err { 203 log.Fatalf("loading of SnapShot.ID==%d LogSegmentRecBPlusTree failed: %v", elementOfSnapShotList.ID, err) 204 } 205 206 err = bPlusTree.Validate() 207 if nil != err { 208 log.Fatalf("validate of SnapShot.ID==%d LogSegmentRecBPlusTree failed: %v", elementOfSnapShotList.ID, err) 209 } 210 211 dumpKeysIfRequested(bPlusTree, fmt.Sprintf("LogSegmentRecBPlusTree for SnapShotID==%d", elementOfSnapShotList.ID)) 212 } 213 214 if uint64(0) != elementOfSnapShotList.BPlusTreeObjectBPlusTreeObjectNumber { 215 bPlusTree, err = sortedmap.OldBPlusTree(elementOfSnapShotList.BPlusTreeObjectBPlusTreeObjectNumber, 216 elementOfSnapShotList.BPlusTreeObjectBPlusTreeObjectOffset, 217 elementOfSnapShotList.BPlusTreeObjectBPlusTreeObjectLength, 218 sortedmap.CompareUint64, 219 &globals, 220 globals.bPlusTreeCache) 221 if nil != err { 222 log.Fatalf("loading of SnapShot.ID==%d BPlusTreeObjectBPlusTree failed: %v", elementOfSnapShotList.ID, err) 223 } 224 225 err = bPlusTree.Validate() 226 if nil != err { 227 log.Fatalf("validate of SnapShot.ID==%d BPlusTreeObjectBPlusTree failed: %v", elementOfSnapShotList.ID, err) 228 } 229 230 dumpKeysIfRequested(bPlusTree, fmt.Sprintf("BPlusTreeObjectBPlusTree for SnapShotID==%d", elementOfSnapShotList.ID)) 231 } 232 233 if uint64(0) != elementOfSnapShotList.CreatedObjectsBPlusTreeObjectNumber { 234 bPlusTree, err = sortedmap.OldBPlusTree(elementOfSnapShotList.CreatedObjectsBPlusTreeObjectNumber, 235 elementOfSnapShotList.CreatedObjectsBPlusTreeObjectOffset, 236 elementOfSnapShotList.CreatedObjectsBPlusTreeObjectLength, 237 sortedmap.CompareUint64, 238 &globals, 239 globals.bPlusTreeCache) 240 if nil != err { 241 log.Fatalf("loading of SnapShot.ID==%d CreatedObjectsBPlusTree failed: %v", elementOfSnapShotList.ID, err) 242 } 243 244 err = bPlusTree.Validate() 245 if nil != err { 246 log.Fatalf("validate of SnapShot.ID==%d CreatedObjectsBPlusTree failed: %v", elementOfSnapShotList.ID, err) 247 } 248 249 dumpKeysIfRequested(bPlusTree, fmt.Sprintf("CreatedObjectsBPlusTree for SnapShotID==%d", elementOfSnapShotList.ID)) 250 } 251 252 if uint64(0) != elementOfSnapShotList.DeletedObjectsBPlusTreeObjectNumber { 253 bPlusTree, err = sortedmap.OldBPlusTree(elementOfSnapShotList.DeletedObjectsBPlusTreeObjectNumber, 254 elementOfSnapShotList.DeletedObjectsBPlusTreeObjectOffset, 255 elementOfSnapShotList.DeletedObjectsBPlusTreeObjectLength, 256 sortedmap.CompareUint64, 257 &globals, 258 globals.bPlusTreeCache) 259 if nil != err { 260 log.Fatalf("loading of SnapShot.ID==%d DeletedObjectsBPlusTree failed: %v", elementOfSnapShotList.ID, err) 261 } 262 263 err = bPlusTree.Validate() 264 if nil != err { 265 log.Fatalf("validate of SnapShot.ID==%d DeletedObjectsBPlusTree failed: %v", elementOfSnapShotList.ID, err) 266 } 267 268 dumpKeysIfRequested(bPlusTree, fmt.Sprintf("DeletedObjectsBPlusTree for SnapShotID==%d", elementOfSnapShotList.ID)) 269 } 270 } 271 272 // TODO: Validate InodeRec, LogSegmentRec, BPlusTreeObject, CreatedObjects, & DeletedObjects B+Tree Layouts 273 274 if 0 < len(globals.recycleBinRefMap) { 275 for recycleBinObjectName, recycleBinObjectReferences = range globals.recycleBinRefMap { 276 log.Printf("Recycled Object %s referenced %d times", recycleBinObjectName, recycleBinObjectReferences) 277 } 278 os.Exit(1) 279 } 280 } 281 282 func usage() { 283 fmt.Printf("%v [-D] <volumeName> <confFile> [<confOverride>]*\n", os.Args[0]) 284 } 285 286 func setup() { 287 var ( 288 confFile string 289 confMap conf.ConfMap 290 confOverrides []string 291 dummyElementOfBPlusTreeLayout headhunter.ElementOfBPlusTreeLayoutStruct 292 dummyUint64 uint64 293 err error 294 volumeSectionName string 295 ) 296 297 if 3 > len(os.Args) { 298 usage() 299 os.Exit(1) 300 } 301 302 if "-D" == os.Args[1] { 303 globals.alsoDump = true 304 305 if 4 > len(os.Args) { 306 usage() 307 os.Exit(1) 308 } 309 310 globals.volumeName = os.Args[2] 311 confFile = os.Args[3] 312 confOverrides = os.Args[4:] 313 } else { 314 globals.alsoDump = false 315 316 globals.volumeName = os.Args[1] 317 confFile = os.Args[2] 318 confOverrides = os.Args[3:] 319 } 320 321 volumeSectionName = "Volume:" + globals.volumeName 322 323 confMap, err = conf.MakeConfMapFromFile(confFile) 324 if nil != err { 325 log.Fatalf("confFile (\"%s\") not parseable: %v", confFile, err) 326 } 327 328 err = confMap.UpdateFromStrings(confOverrides) 329 if nil != err { 330 log.Fatalf("confOverrides (%v) not parseable: %v", confOverrides, err) 331 } 332 333 globals.noAuthIPAddr, err = confMap.FetchOptionValueString("SwiftClient", "NoAuthIPAddr") 334 if nil != err { 335 globals.noAuthIPAddr = "127.0.0.1" // TODO: Eventually just return 336 } 337 globals.noAuthTCPPort, err = confMap.FetchOptionValueUint16("SwiftClient", "NoAuthTCPPort") 338 if nil != err { 339 log.Fatal(err) 340 } 341 342 globals.noAuthURL = "http://" + net.JoinHostPort(globals.noAuthIPAddr, fmt.Sprintf("%d", globals.noAuthTCPPort)) + "/" 343 344 globals.accountName, err = confMap.FetchOptionValueString(volumeSectionName, "AccountName") 345 if nil != err { 346 log.Fatal(err) 347 } 348 globals.checkpointContainerName, err = confMap.FetchOptionValueString(volumeSectionName, "CheckpointContainerName") 349 if nil != err { 350 log.Fatal(err) 351 } 352 353 globals.etcdEnabled, err = confMap.FetchOptionValueBool("FSGlobals", "EtcdEnabled") 354 if nil != err { 355 globals.etcdEnabled = false // TODO: Current default... perhaps eventually just log.Fatal(err) 356 } 357 358 if globals.etcdEnabled { 359 globals.etcdAutoSyncInterval, err = confMap.FetchOptionValueDuration("FSGlobals", "EtcdAutoSyncInterval") 360 if nil != err { 361 log.Fatal(err) 362 } 363 globals.etcdDialTimeout, err = confMap.FetchOptionValueDuration("FSGlobals", "EtcdDialTimeout") 364 if nil != err { 365 log.Fatal(err) 366 } 367 globals.etcdEndpoints, err = confMap.FetchOptionValueStringSlice("FSGlobals", "EtcdEndpoints") 368 if nil != err { 369 log.Fatal(err) 370 } 371 globals.etcdOpTimeout, err = confMap.FetchOptionValueDuration("FSGlobals", "EtcdOpTimeout") 372 if nil != err { 373 log.Fatal(err) 374 } 375 376 globals.etcdClient, err = etcd.New(etcd.Config{ 377 Endpoints: globals.etcdEndpoints, 378 AutoSyncInterval: globals.etcdAutoSyncInterval, 379 DialTimeout: globals.etcdDialTimeout, 380 }) 381 if nil != err { 382 log.Fatalf("unable to create etcdClient: %v\n", err) 383 } 384 385 globals.etcdKV = etcd.NewKV(globals.etcdClient) 386 387 globals.checkpointEtcdKeyName, err = confMap.FetchOptionValueString(volumeSectionName, "CheckpointEtcdKeyName") 388 } 389 390 globals.bPlusTreeCacheEvictLowLimit, err = confMap.FetchOptionValueUint64("FSCK", "BPlusTreeCacheEvictLowLimit") 391 if nil != err { 392 globals.bPlusTreeCacheEvictLowLimit = bPlusTreeCacheEvictLowLimitDefault 393 } 394 globals.bPlusTreeCacheEvictHighLimit, err = confMap.FetchOptionValueUint64("FSCK", "BPlusTreeCacheEvictHighLimit") 395 if nil != err { 396 globals.bPlusTreeCacheEvictHighLimit = bPlusTreeCacheEvictHighLimitDefault 397 } 398 399 globals.elementOfBPlusTreeLayoutStructSize, _, err = cstruct.Examine(&dummyElementOfBPlusTreeLayout) 400 if nil != err { 401 log.Fatal(err) 402 } 403 globals.uint64Size, _, err = cstruct.Examine(&dummyUint64) 404 if nil != err { 405 log.Fatal(err) 406 } 407 } 408 409 func fetchCheckpointHeader() (checkpointHeader *headhunter.CheckpointHeaderStruct) { 410 var ( 411 cancel context.CancelFunc 412 checkpointContainerHeaderMap http.Header 413 checkpointContainerHeaderString string 414 checkpointContainerHeaderStringSlice []string 415 checkpointContainerHeaderStringSplit []string 416 ctx context.Context 417 err error 418 etcdGetResponse *etcd.GetResponse 419 ok bool 420 ) 421 422 checkpointHeader = &headhunter.CheckpointHeaderStruct{} 423 424 if globals.etcdEnabled { 425 ctx, cancel = context.WithTimeout(context.Background(), globals.etcdOpTimeout) 426 etcdGetResponse, err = globals.etcdKV.Get(ctx, globals.checkpointEtcdKeyName) 427 cancel() 428 if nil != err { 429 log.Fatalf("error contacting etcd: %v", err) 430 } 431 432 if 1 == etcdGetResponse.Count { 433 err = json.Unmarshal(etcdGetResponse.Kvs[0].Value, checkpointHeader) 434 if nil != err { 435 log.Fatalf("error unmarshalling %s's Value (%s): %v", globals.checkpointEtcdKeyName, string(etcdGetResponse.Kvs[0].Value[:]), err) 436 } 437 438 if headhunter.CheckpointVersion3 != checkpointHeader.CheckpointVersion { 439 log.Fatalf("unsupported CheckpointVersion (%v)...must be == CheckpointVersion3 (%v)", checkpointHeader.CheckpointVersion, headhunter.CheckpointVersion3) 440 } 441 442 return 443 } 444 } 445 446 checkpointContainerHeaderMap, err = doHead(globals.accountName + "/" + globals.checkpointContainerName) 447 if nil != err { 448 log.Fatalf("error fetching checkpointContainerHeaderMap: %v", err) 449 } 450 451 checkpointContainerHeaderStringSlice, ok = checkpointContainerHeaderMap[headhunter.CheckpointHeaderName] 452 if !ok { 453 log.Fatalf("error fetching checkpointContainerHeaderStringSlice") 454 } 455 if 1 != len(checkpointContainerHeaderStringSlice) { 456 log.Fatalf("checkpointContainerHeaderStringSlice must be single-valued") 457 } 458 459 checkpointContainerHeaderString = checkpointContainerHeaderStringSlice[0] 460 461 checkpointContainerHeaderStringSplit = strings.Split(checkpointContainerHeaderString, " ") 462 if 4 != len(checkpointContainerHeaderStringSplit) { 463 log.Fatalf("checkpointContainerHeaderStringSplit must be four-valued") 464 } 465 466 checkpointHeader.CheckpointVersion, err = strconv.ParseUint(checkpointContainerHeaderStringSplit[0], 16, 64) 467 if nil != err { 468 log.Fatalf("error parsing CheckpointVersion: %v", err) 469 } 470 if headhunter.CheckpointVersion3 != checkpointHeader.CheckpointVersion { 471 log.Fatalf("unsupported CheckpointVersion (%v)...must be == CheckpointVersion3 (%v)", checkpointHeader.CheckpointVersion, headhunter.CheckpointVersion3) 472 } 473 474 checkpointHeader.CheckpointObjectTrailerStructObjectNumber, err = strconv.ParseUint(checkpointContainerHeaderStringSplit[1], 16, 64) 475 if nil != err { 476 log.Fatalf("error parsing CheckpointObjectTrailerStructObjectNumber:%v", err) 477 } 478 479 checkpointHeader.CheckpointObjectTrailerStructObjectLength, err = strconv.ParseUint(checkpointContainerHeaderStringSplit[2], 16, 64) 480 if nil != err { 481 log.Fatalf("error parsing CheckpointObjectTrailerStructObjectLength:%v", err) 482 } 483 484 checkpointHeader.ReservedToNonce, err = strconv.ParseUint(checkpointContainerHeaderStringSplit[3], 16, 64) 485 if nil != err { 486 log.Fatalf("error parsing ReservedToNonce:%v", err) 487 } 488 489 return 490 } 491 492 func checkpointHeadersAreEqual(checkpointHeader1 *headhunter.CheckpointHeaderStruct, checkpointHeader2 *headhunter.CheckpointHeaderStruct) (areEqual bool) { 493 areEqual = (checkpointHeader1.CheckpointVersion == checkpointHeader2.CheckpointVersion) && 494 (checkpointHeader1.CheckpointObjectTrailerStructObjectNumber == checkpointHeader2.CheckpointObjectTrailerStructObjectNumber) && 495 (checkpointHeader1.CheckpointObjectTrailerStructObjectLength == checkpointHeader2.CheckpointObjectTrailerStructObjectLength) && 496 (checkpointHeader1.ReservedToNonce == checkpointHeader2.ReservedToNonce) 497 return // Note: Comparing CheckpointObjectTrailerStructObjectNumber's would actually have been sufficient 498 } 499 500 func fetchCheckpointObjectTrailerV3() (err error) { 501 var ( 502 checkpointObjectHeaderMap http.Header 503 checkpointObjectPath string 504 inRecycleBinRefMap bool 505 refs uint64 506 ) 507 508 checkpointObjectPath = globals.accountName + "/" + globals.checkpointContainerName + "/" + fmt.Sprintf("%016X", globals.initialCheckpointHeader.CheckpointObjectTrailerStructObjectNumber) 509 510 checkpointObjectHeaderMap, globals.checkpointObjectTrailerV3Buf, err = doGetTail(checkpointObjectPath, globals.initialCheckpointHeader.CheckpointObjectTrailerStructObjectLength) 511 if nil != err { 512 err = fmt.Errorf("error reading checkpointObjectTrailerV3Buf: %v", err) 513 return 514 } 515 516 if metadataRecycleBinHeaderPresent(checkpointObjectHeaderMap) { 517 refs, inRecycleBinRefMap = globals.recycleBinRefMap[checkpointObjectPath] 518 if inRecycleBinRefMap { 519 refs++ 520 } else { 521 refs = 1 522 } 523 globals.recycleBinRefMap[checkpointObjectPath] = refs 524 } 525 526 globals.checkpointObjectTrailerV3 = &headhunter.CheckpointObjectTrailerV3Struct{} 527 528 globals.checkpointObjectTrailerV3BufPos, err = cstruct.Unpack(globals.checkpointObjectTrailerV3Buf, globals.checkpointObjectTrailerV3, headhunter.LittleEndian) 529 if nil != err { 530 err = fmt.Errorf("unable to Unpack checkpointObjectTrailerV3Buf: %v", err) 531 return 532 } 533 534 return 535 } 536 537 func fetchBPlusTreeLayout(numElements uint64) (treeLayout sortedmap.LayoutReport, err error) { 538 var ( 539 alreadyInTreeLayout bool 540 bytesNeeded uint64 541 elementIndex uint64 542 elementOfBPlusTreeLayout headhunter.ElementOfBPlusTreeLayoutStruct 543 ) 544 545 bytesNeeded = numElements * globals.elementOfBPlusTreeLayoutStructSize 546 if bytesNeeded > (globals.initialCheckpointHeader.CheckpointObjectTrailerStructObjectLength - globals.checkpointObjectTrailerV3BufPos) { 547 err = fmt.Errorf("insufficient bytes left in checkpointObjectTrailerV3Buf") 548 return 549 } 550 551 treeLayout = make(sortedmap.LayoutReport) 552 553 for elementIndex = 0; elementIndex < numElements; elementIndex++ { 554 _, err = cstruct.Unpack(globals.checkpointObjectTrailerV3Buf[globals.checkpointObjectTrailerV3BufPos:], &elementOfBPlusTreeLayout, headhunter.LittleEndian) 555 if nil != err { 556 return 557 } 558 559 globals.checkpointObjectTrailerV3BufPos += globals.elementOfBPlusTreeLayoutStructSize 560 561 _, alreadyInTreeLayout = treeLayout[elementOfBPlusTreeLayout.ObjectNumber] 562 if alreadyInTreeLayout { 563 err = fmt.Errorf("duplicate elementOfBPlusTreeLayout.ObjectNumber (0x%016X) encountered", elementOfBPlusTreeLayout.ObjectNumber) 564 return 565 } 566 567 treeLayout[elementOfBPlusTreeLayout.ObjectNumber] = elementOfBPlusTreeLayout.ObjectNumber 568 } 569 570 return 571 } 572 573 func fetchSnapShotList(numElements uint64) (err error) { 574 var ( 575 alreadySeenThisID bool 576 alreadySeenThisIDSet map[uint64]struct{} 577 alreadySeenThisName bool 578 alreadySeenThisNameSet map[string]struct{} 579 alreadySeenThisNonce bool 580 alreadySeenThisNonceSet map[uint64]struct{} 581 elementIndex uint64 582 elementOfSnapShotList *headhunter.ElementOfSnapShotListStruct 583 ) 584 585 globals.snapShotList = make([]*headhunter.ElementOfSnapShotListStruct, numElements) 586 587 alreadySeenThisNonceSet = make(map[uint64]struct{}) 588 alreadySeenThisIDSet = make(map[uint64]struct{}) 589 alreadySeenThisNameSet = make(map[string]struct{}) 590 591 for elementIndex = 0; elementIndex < numElements; elementIndex++ { 592 elementOfSnapShotList = &headhunter.ElementOfSnapShotListStruct{} 593 594 elementOfSnapShotList.Nonce, err = consumeCheckpointObjectTrailerV3BufUint64() 595 if nil != err { 596 return 597 } 598 elementOfSnapShotList.ID, err = consumeCheckpointObjectTrailerV3BufUint64() 599 if nil != err { 600 return 601 } 602 elementOfSnapShotList.TimeStamp, err = consumeCheckpointObjectTrailerV3BufTimeStamp() 603 if nil != err { 604 return 605 } 606 elementOfSnapShotList.Name, err = consumeCheckpointObjectTrailerV3BufString() 607 if nil != err { 608 return 609 } 610 elementOfSnapShotList.InodeRecBPlusTreeObjectNumber, err = consumeCheckpointObjectTrailerV3BufUint64() 611 if nil != err { 612 return 613 } 614 elementOfSnapShotList.InodeRecBPlusTreeObjectOffset, err = consumeCheckpointObjectTrailerV3BufUint64() 615 if nil != err { 616 return 617 } 618 elementOfSnapShotList.InodeRecBPlusTreeObjectLength, err = consumeCheckpointObjectTrailerV3BufUint64() 619 if nil != err { 620 return 621 } 622 elementOfSnapShotList.LogSegmentRecBPlusTreeObjectNumber, err = consumeCheckpointObjectTrailerV3BufUint64() 623 if nil != err { 624 return 625 } 626 elementOfSnapShotList.LogSegmentRecBPlusTreeObjectOffset, err = consumeCheckpointObjectTrailerV3BufUint64() 627 if nil != err { 628 return 629 } 630 elementOfSnapShotList.LogSegmentRecBPlusTreeObjectLength, err = consumeCheckpointObjectTrailerV3BufUint64() 631 if nil != err { 632 return 633 } 634 elementOfSnapShotList.BPlusTreeObjectBPlusTreeObjectNumber, err = consumeCheckpointObjectTrailerV3BufUint64() 635 if nil != err { 636 return 637 } 638 elementOfSnapShotList.BPlusTreeObjectBPlusTreeObjectOffset, err = consumeCheckpointObjectTrailerV3BufUint64() 639 if nil != err { 640 return 641 } 642 elementOfSnapShotList.BPlusTreeObjectBPlusTreeObjectLength, err = consumeCheckpointObjectTrailerV3BufUint64() 643 if nil != err { 644 return 645 } 646 elementOfSnapShotList.CreatedObjectsBPlusTreeObjectNumber, err = consumeCheckpointObjectTrailerV3BufUint64() 647 if nil != err { 648 return 649 } 650 elementOfSnapShotList.CreatedObjectsBPlusTreeObjectOffset, err = consumeCheckpointObjectTrailerV3BufUint64() 651 if nil != err { 652 return 653 } 654 elementOfSnapShotList.CreatedObjectsBPlusTreeObjectLength, err = consumeCheckpointObjectTrailerV3BufUint64() 655 if nil != err { 656 return 657 } 658 elementOfSnapShotList.DeletedObjectsBPlusTreeObjectNumber, err = consumeCheckpointObjectTrailerV3BufUint64() 659 if nil != err { 660 return 661 } 662 elementOfSnapShotList.DeletedObjectsBPlusTreeObjectOffset, err = consumeCheckpointObjectTrailerV3BufUint64() 663 if nil != err { 664 return 665 } 666 elementOfSnapShotList.DeletedObjectsBPlusTreeObjectLength, err = consumeCheckpointObjectTrailerV3BufUint64() 667 if nil != err { 668 return 669 } 670 671 _, alreadySeenThisNonce = alreadySeenThisNonceSet[elementOfSnapShotList.Nonce] 672 if alreadySeenThisNonce { 673 err = fmt.Errorf("duplicate elementOfSnapShotList.Nonce found: 0x%16X", elementOfSnapShotList.Nonce) 674 return 675 } 676 alreadySeenThisNonceSet[elementOfSnapShotList.Nonce] = struct{}{} 677 678 _, alreadySeenThisID = alreadySeenThisIDSet[elementOfSnapShotList.ID] 679 if alreadySeenThisID { 680 err = fmt.Errorf("duplicate elementOfSnapShotList.ID found: 0x%16X", elementOfSnapShotList.ID) 681 return 682 } 683 alreadySeenThisIDSet[elementOfSnapShotList.ID] = struct{}{} 684 685 _, alreadySeenThisName = alreadySeenThisNameSet[elementOfSnapShotList.Name] 686 if alreadySeenThisName { 687 err = fmt.Errorf("duplicate elementOfSnapShotList.Name found: 0x%16X", elementOfSnapShotList.Name) 688 return 689 } 690 alreadySeenThisNameSet[elementOfSnapShotList.Name] = struct{}{} 691 692 globals.snapShotList[elementIndex] = elementOfSnapShotList 693 } 694 695 return 696 } 697 698 func consumeCheckpointObjectTrailerV3BufUint64() (u64 uint64, err error) { 699 var ( 700 u64Copy uint64 701 ) 702 703 if globals.uint64Size > (globals.initialCheckpointHeader.CheckpointObjectTrailerStructObjectLength - globals.checkpointObjectTrailerV3BufPos) { 704 err = fmt.Errorf("insufficient bytes left in checkpointObjectTrailerV3Buf") 705 return 706 } 707 708 _, err = cstruct.Unpack(globals.checkpointObjectTrailerV3Buf[globals.checkpointObjectTrailerV3BufPos:], &u64Copy, headhunter.LittleEndian) 709 if nil != err { 710 return 711 } 712 713 globals.checkpointObjectTrailerV3BufPos += globals.uint64Size 714 715 u64 = u64Copy 716 717 return 718 } 719 720 func consumeCheckpointObjectTrailerV3BufTimeStamp() (timeStamp time.Time, err error) { 721 var ( 722 timeStampBuf []byte 723 timeStampBufLen uint64 724 ) 725 726 timeStampBufLen, err = consumeCheckpointObjectTrailerV3BufUint64() 727 if nil != err { 728 return 729 } 730 731 if timeStampBufLen > (globals.initialCheckpointHeader.CheckpointObjectTrailerStructObjectLength - globals.checkpointObjectTrailerV3BufPos) { 732 err = fmt.Errorf("insufficient bytes left in checkpointObjectTrailerV3Buf") 733 return 734 } 735 736 timeStampBuf = globals.checkpointObjectTrailerV3Buf[globals.checkpointObjectTrailerV3BufPos : globals.checkpointObjectTrailerV3BufPos+timeStampBufLen] 737 738 err = timeStamp.UnmarshalBinary(timeStampBuf) 739 if nil != err { 740 return 741 } 742 743 globals.checkpointObjectTrailerV3BufPos += timeStampBufLen 744 745 return 746 } 747 748 func consumeCheckpointObjectTrailerV3BufString() (str string, err error) { 749 var ( 750 strLen uint64 751 ) 752 753 strLen, err = consumeCheckpointObjectTrailerV3BufUint64() 754 if nil != err { 755 return 756 } 757 758 if strLen > (globals.initialCheckpointHeader.CheckpointObjectTrailerStructObjectLength - globals.checkpointObjectTrailerV3BufPos) { 759 err = fmt.Errorf("insufficient bytes left in checkpointObjectTrailerV3Buf") 760 return 761 } 762 763 str = string(globals.checkpointObjectTrailerV3Buf[globals.checkpointObjectTrailerV3BufPos : globals.checkpointObjectTrailerV3BufPos+strLen]) 764 765 globals.checkpointObjectTrailerV3BufPos += strLen 766 767 return 768 } 769 770 func dumpKeysIfRequested(bPlusTree sortedmap.BPlusTree, bPlusTreeName string) { 771 var ( 772 err error 773 itemIndex int 774 keyAsKey sortedmap.Key 775 keyAsUint64 uint64 776 numberOfItems int 777 ok bool 778 ) 779 780 if globals.alsoDump { 781 fmt.Printf("%s:\n", bPlusTreeName) 782 783 numberOfItems, err = bPlusTree.Len() 784 if nil != err { 785 log.Fatalf(" bPlusTree.Len() failed: %v", err) 786 } 787 788 if 0 == numberOfItems { 789 fmt.Printf(" <empty>\n") 790 return 791 } 792 793 for itemIndex = 0; itemIndex < numberOfItems; itemIndex++ { 794 keyAsKey, _, ok, err = bPlusTree.GetByIndex(itemIndex) 795 if nil != err { 796 log.Fatalf(" bPlusTree.GetByIndex(%d) failed: %v", itemIndex, err) 797 } 798 if !ok { 799 log.Fatalf(" bPlusTree.GetByIndex(%d) returned !ok", itemIndex) 800 } 801 802 keyAsUint64, ok = keyAsKey.(uint64) 803 if !ok { 804 log.Fatalf(" bPlusTree.GetByIndex(%d) returned non-uint64 Key", itemIndex) 805 } 806 807 fmt.Printf(" 0x%016X\n", keyAsUint64) 808 } 809 } 810 } 811 812 func (dummy *globalsStruct) DumpKey(key sortedmap.Key) (keyAsString string, err error) { 813 err = fmt.Errorf("not implemented") 814 return 815 } 816 817 func (dummy *globalsStruct) DumpValue(value sortedmap.Value) (valueAsString string, err error) { 818 err = fmt.Errorf("not implemented") 819 return 820 } 821 822 func (dummy *globalsStruct) GetNode(objectNumber uint64, objectOffset uint64, objectLength uint64) (nodeByteSlice []byte, err error) { 823 var ( 824 inRecycleBinRefMap bool 825 objectHeaderMap http.Header 826 objectPath string 827 refs uint64 828 ) 829 830 objectPath = globals.accountName + "/" + globals.checkpointContainerName + "/" + fmt.Sprintf("%016X", objectNumber) 831 832 objectHeaderMap, nodeByteSlice, err = doGetRange(objectPath, objectOffset, objectLength) 833 if nil != err { 834 err = fmt.Errorf("error reading %s (offset=0x%016X,length=0x%016X): %v", objectPath, objectOffset, objectLength, err) 835 return 836 } 837 838 if metadataRecycleBinHeaderPresent(objectHeaderMap) { 839 refs, inRecycleBinRefMap = globals.recycleBinRefMap[objectPath] 840 if inRecycleBinRefMap { 841 refs++ 842 } else { 843 refs = 1 844 } 845 globals.recycleBinRefMap[objectPath] = refs 846 } 847 848 return 849 } 850 851 func (dummy *globalsStruct) PutNode(nodeByteSlice []byte) (objectNumber uint64, objectOffset uint64, err error) { 852 err = fmt.Errorf("not implemented") 853 return 854 } 855 856 func (dummy *globalsStruct) DiscardNode(objectNumber uint64, objectOffset uint64, objectLength uint64) (err error) { 857 err = nil 858 return 859 } 860 861 func (dummy *globalsStruct) PackKey(key sortedmap.Key) (packedKey []byte, err error) { 862 err = fmt.Errorf("not implemented") 863 return 864 } 865 866 func (dummy *globalsStruct) UnpackKey(payloadData []byte) (key sortedmap.Key, bytesConsumed uint64, err error) { 867 var ( 868 keyAsUint64 uint64 869 ) 870 871 _, err = cstruct.Unpack(payloadData, &keyAsUint64, headhunter.LittleEndian) 872 if nil != err { 873 return 874 } 875 876 key = keyAsUint64 877 bytesConsumed = globals.uint64Size 878 879 return 880 } 881 882 func (dummy *globalsStruct) PackValue(value sortedmap.Value) (packedValue []byte, err error) { 883 err = fmt.Errorf("not implemented") 884 return 885 } 886 887 func (dummy *globalsStruct) UnpackValue(payloadData []byte) (value sortedmap.Value, bytesConsumed uint64, err error) { 888 var ( 889 valueLen uint64 890 ) 891 892 _, err = cstruct.Unpack(payloadData, &valueLen, headhunter.LittleEndian) 893 if nil != err { 894 return 895 } 896 897 if (globals.uint64Size + valueLen) > uint64(len(payloadData)) { 898 err = fmt.Errorf("payloadData insufficient to UnpackValue()") 899 return 900 } 901 902 value = make([]byte, valueLen) 903 _ = copy(value.([]byte), payloadData) 904 905 bytesConsumed = globals.uint64Size + valueLen 906 907 return 908 } 909 910 func doHead(path string) (headerMap http.Header, err error) { 911 var ( 912 headResponse *http.Response 913 ) 914 915 headResponse, err = http.Head(globals.noAuthURL + "v1/" + path) 916 if nil != err { 917 return 918 } 919 920 _, err = ioutil.ReadAll(headResponse.Body) 921 if nil != err { 922 return 923 } 924 err = headResponse.Body.Close() 925 if nil != err { 926 return 927 } 928 929 if (http.StatusOK != headResponse.StatusCode) && (http.StatusNoContent != headResponse.StatusCode) { 930 err = fmt.Errorf("unexpected getResponse.Status: %s", headResponse.Status) 931 return 932 } 933 934 headerMap = headResponse.Header 935 936 return 937 } 938 939 func doGetRange(path string, offset uint64, length uint64) (headerMap http.Header, buf []byte, err error) { 940 var ( 941 getRequest *http.Request 942 getResponse *http.Response 943 httpClient *http.Client 944 ) 945 946 getRequest, err = http.NewRequest("GET", globals.noAuthURL+"v1/"+path, nil) 947 if nil != err { 948 return 949 } 950 951 getRequest.Header.Set("Range", "bytes="+strconv.FormatUint(offset, 10)+"-"+strconv.FormatUint((offset+length-1), 10)) 952 953 httpClient = &http.Client{} 954 955 getResponse, err = httpClient.Do(getRequest) 956 if nil != err { 957 return 958 } 959 960 buf, err = ioutil.ReadAll(getResponse.Body) 961 if nil != err { 962 return 963 } 964 err = getResponse.Body.Close() 965 if nil != err { 966 return 967 } 968 969 if (http.StatusOK != getResponse.StatusCode) && (http.StatusNoContent != getResponse.StatusCode) && (http.StatusPartialContent != getResponse.StatusCode) { 970 err = fmt.Errorf("unexpected getResponse.Status: %s", getResponse.Status) 971 return 972 } 973 974 if uint64(len(buf)) != length { 975 err = fmt.Errorf("unexpected getResponse.Body length") 976 return 977 } 978 979 headerMap = getResponse.Header 980 981 return 982 } 983 984 func doGetTail(path string, length uint64) (headerMap http.Header, buf []byte, err error) { 985 var ( 986 getRequest *http.Request 987 getResponse *http.Response 988 httpClient *http.Client 989 ) 990 991 getRequest, err = http.NewRequest("GET", globals.noAuthURL+"v1/"+path, nil) 992 if nil != err { 993 return 994 } 995 996 getRequest.Header.Set("Range", "bytes=-"+strconv.FormatUint(length, 10)) 997 998 httpClient = &http.Client{} 999 1000 getResponse, err = httpClient.Do(getRequest) 1001 if nil != err { 1002 return 1003 } 1004 1005 buf, err = ioutil.ReadAll(getResponse.Body) 1006 if nil != err { 1007 return 1008 } 1009 err = getResponse.Body.Close() 1010 if nil != err { 1011 return 1012 } 1013 1014 if (http.StatusOK != getResponse.StatusCode) && (http.StatusNoContent != getResponse.StatusCode) && (http.StatusPartialContent != getResponse.StatusCode) { 1015 err = fmt.Errorf("unexpected getResponse.Status: %s", getResponse.Status) 1016 return 1017 } 1018 1019 if uint64(len(buf)) != length { 1020 err = fmt.Errorf("unexpected getResponse.Body length") 1021 return 1022 } 1023 1024 headerMap = getResponse.Header 1025 1026 return 1027 } 1028 1029 func doGetList(path string) (headerMap http.Header, list []string, err error) { 1030 var ( 1031 buf []byte 1032 getResponse *http.Response 1033 ) 1034 1035 list = make([]string, 0) 1036 1037 for { 1038 if 0 == len(list) { 1039 getResponse, err = http.Get(globals.noAuthURL + "v1/" + path) 1040 } else { 1041 getResponse, err = http.Get(globals.noAuthURL + "v1/" + path + "?marker=" + list[len(list)-1]) 1042 } 1043 if nil != err { 1044 return 1045 } 1046 1047 buf, err = ioutil.ReadAll(getResponse.Body) 1048 if nil != err { 1049 return 1050 } 1051 err = getResponse.Body.Close() 1052 if nil != err { 1053 return 1054 } 1055 1056 if (http.StatusOK != getResponse.StatusCode) && (http.StatusNoContent != getResponse.StatusCode) && (http.StatusPartialContent != getResponse.StatusCode) { 1057 err = fmt.Errorf("unexpected getResponse.Status: %s", getResponse.Status) 1058 return 1059 } 1060 1061 time.Sleep(time.Second) 1062 if 0 == len(buf) { 1063 break 1064 } 1065 1066 list = append(list, strings.Split(string(buf[:]), "\n")...) 1067 list = list[:len(list)-1] 1068 } 1069 1070 headerMap = getResponse.Header 1071 1072 return 1073 } 1074 1075 func metadataRecycleBinHeaderPresent(headerMap http.Header) (present bool) { 1076 _, present = headerMap[headhunter.MetadataRecycleBinHeaderName] 1077 return 1078 }