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  }