github.com/rohankumardubey/proxyfs@v0.0.0-20210108201508-653efa9ab00e/inode/dir.go (about)

     1  package inode
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/swiftstack/sortedmap"
     8  
     9  	"github.com/swiftstack/ProxyFS/blunder"
    10  	"github.com/swiftstack/ProxyFS/headhunter"
    11  	"github.com/swiftstack/ProxyFS/logger"
    12  	"github.com/swiftstack/ProxyFS/stats"
    13  	"github.com/swiftstack/ProxyFS/utils"
    14  )
    15  
    16  func (vS *volumeStruct) createRootOrSubDir(filePerm InodeMode, userID InodeUserID, groupID InodeGroupID, isRootDir bool) (dirInodeNumber InodeNumber, err error) {
    17  	// Create file mode out of file permissions plus inode type
    18  	fileMode, err := determineMode(filePerm, DirType)
    19  	if nil != err {
    20  		return
    21  	}
    22  
    23  	var dirInode *inMemoryInodeStruct
    24  
    25  	if isRootDir {
    26  		dirInode = vS.makeInMemoryInodeWithThisInodeNumber(DirType, fileMode, userID, groupID, RootDirInodeNumber, true)
    27  		dirInodeNumber = RootDirInodeNumber
    28  	} else {
    29  		dirInode, err = vS.makeInMemoryInode(DirType, fileMode, userID, groupID)
    30  		if nil != err {
    31  			return
    32  		}
    33  		dirInodeNumber = dirInode.InodeNumber
    34  	}
    35  
    36  	dirInode.dirty = true
    37  
    38  	// sorted map from directory entry name (a string) to InodeNumber
    39  
    40  	dirMapping :=
    41  		sortedmap.NewBPlusTree(
    42  			vS.maxEntriesPerDirNode,
    43  			sortedmap.CompareString,
    44  			&dirInodeCallbacks{treeNodeLoadable{inode: dirInode}},
    45  			globals.dirEntryCache)
    46  
    47  	ok, err := dirMapping.Put(".", dirInode.InodeNumber)
    48  	if (nil != err) || (!ok) {
    49  		panic(err)
    50  	}
    51  
    52  	if isRootDir {
    53  		ok, err = dirMapping.Put("..", dirInode.InodeNumber)
    54  		if (nil != err) || (!ok) {
    55  			panic(err)
    56  		}
    57  
    58  		dirInode.LinkCount = 2
    59  	} else {
    60  		dirInode.LinkCount = 1
    61  	}
    62  
    63  	dirInode.payload = dirMapping
    64  
    65  	if isRootDir {
    66  		// If creating RootDir, since this must be atomic, caller already holds vS.Mutex
    67  		ok, err = vS.inodeCacheInsertWhileLocked(dirInode)
    68  	} else {
    69  		ok, err = vS.inodeCacheInsert(dirInode)
    70  	}
    71  	if nil != err {
    72  		return
    73  	}
    74  	if !ok {
    75  		err = fmt.Errorf("inodeCacheInsert(dirInode) failed")
    76  		return
    77  	}
    78  
    79  	// If creating RootDir, force an immediate flush to ensure it is atomically created as well
    80  	if isRootDir {
    81  		err = vS.flushInode(dirInode)
    82  		if nil != err {
    83  			logger.ErrorfWithError(err, "createRootOrSubDir() call to flushInode() failed")
    84  			return
    85  		}
    86  	}
    87  
    88  	err = nil
    89  	return
    90  }
    91  
    92  func (vS *volumeStruct) CreateDir(filePerm InodeMode, userID InodeUserID, groupID InodeGroupID) (dirInodeNumber InodeNumber, err error) {
    93  	err = enforceRWMode(false)
    94  	if nil != err {
    95  		return
    96  	}
    97  
    98  	stats.IncrementOperations(&stats.DirCreateOps)
    99  
   100  	dirInodeNumber, err = vS.createRootOrSubDir(filePerm, userID, groupID, false)
   101  
   102  	if err == nil {
   103  		stats.IncrementOperations(&stats.DirCreateSuccessOps)
   104  	}
   105  
   106  	return
   107  }
   108  
   109  func linkInMemory(dirInode *inMemoryInodeStruct, targetInode *inMemoryInodeStruct, basename string) error {
   110  	dirInode.dirty = true
   111  	targetInode.dirty = true
   112  
   113  	dirMapping := dirInode.payload.(sortedmap.BPlusTree)
   114  
   115  	ok, err := dirMapping.Put(basename, targetInode.InodeNumber)
   116  	if nil != err {
   117  		panic(err)
   118  	}
   119  	if !ok {
   120  		err = fmt.Errorf("%s: failed to create link '%v' to inode %v in directory inode %v: entry exists",
   121  			utils.GetFnName(), basename, targetInode.InodeNumber, dirInode.InodeNumber)
   122  		return blunder.AddError(err, blunder.FileExistsError)
   123  	}
   124  
   125  	updateTime := time.Now()
   126  
   127  	targetInode.LinkCount++
   128  	targetInode.AttrChangeTime = updateTime
   129  
   130  	if targetInode.InodeType == DirType && targetInode.InodeNumber != RootDirInodeNumber {
   131  		subdirMapping := targetInode.payload.(sortedmap.BPlusTree)
   132  		subdirMapping.Put("..", dirInode.InodeNumber)
   133  		dirInode.LinkCount++
   134  	}
   135  
   136  	dirInode.AttrChangeTime = updateTime
   137  	dirInode.ModificationTime = updateTime
   138  
   139  	return nil
   140  }
   141  
   142  // Insert-only version of linkInMemory()
   143  func linkInMemoryInsertOnly(dirInode *inMemoryInodeStruct, basename string, targetInodeNumber InodeNumber) (err error) {
   144  	dirInode.dirty = true
   145  
   146  	dirMapping := dirInode.payload.(sortedmap.BPlusTree)
   147  
   148  	ok, err := dirMapping.Put(basename, targetInodeNumber)
   149  	if nil != err {
   150  		panic(err)
   151  	}
   152  	if !ok {
   153  		err = fmt.Errorf("%s: failed to create link '%v' to inode %v in directory inode %v: entry exists",
   154  			utils.GetFnName(), basename, targetInodeNumber, dirInode.InodeNumber)
   155  		return blunder.AddError(err, blunder.FileExistsError)
   156  	}
   157  
   158  	updateTime := time.Now()
   159  
   160  	dirInode.AttrChangeTime = updateTime
   161  	dirInode.ModificationTime = updateTime
   162  
   163  	return nil
   164  }
   165  
   166  // This is used by the link(2), create(2), and mkdir(2) operations
   167  // (mountstruct.Link(), mountstruct.Create(), and mountstruct.Mkdir())
   168  func (vS *volumeStruct) Link(dirInodeNumber InodeNumber, basename string, targetInodeNumber InodeNumber, insertOnly bool) (err error) {
   169  	var (
   170  		dirInode       *inMemoryInodeStruct
   171  		flushInodeList []*inMemoryInodeStruct
   172  		ok             bool
   173  		snapShotIDType headhunter.SnapShotIDType
   174  		targetInode    *inMemoryInodeStruct
   175  	)
   176  
   177  	err = enforceRWMode(false)
   178  	if nil != err {
   179  		return
   180  	}
   181  
   182  	if (RootDirInodeNumber == dirInodeNumber) && (SnapShotDirName == basename) {
   183  		err = blunder.NewError(blunder.InvalidArgError, "Link() to /%v not allowed", SnapShotDirName)
   184  		return
   185  	}
   186  	snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirInodeNumber))
   187  	if headhunter.SnapShotIDTypeLive != snapShotIDType {
   188  		err = blunder.NewError(blunder.InvalidArgError, "Link() on non-LiveView dirInodeNumber not allowed")
   189  		return
   190  	}
   191  	snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(targetInodeNumber))
   192  	if headhunter.SnapShotIDTypeLive != snapShotIDType {
   193  		err = blunder.NewError(blunder.InvalidArgError, "Link() on non-LiveView targetInodeNumber not allowed")
   194  		return
   195  	}
   196  
   197  	stats.IncrementOperations(&stats.DirLinkOps)
   198  
   199  	dirInode, err = vS.fetchInodeType(dirInodeNumber, DirType)
   200  	if err != nil {
   201  		logger.ErrorfWithError(err, "dirInode error")
   202  		return err
   203  	}
   204  
   205  	if insertOnly {
   206  		err = linkInMemoryInsertOnly(dirInode, basename, targetInodeNumber)
   207  		if err != nil {
   208  			return err
   209  		}
   210  
   211  		flushInodeList = []*inMemoryInodeStruct{dirInode}
   212  	} else {
   213  		targetInode, ok, err = vS.fetchInode(targetInodeNumber)
   214  		if err != nil {
   215  			// the inode is locked so this should never happen (unless the inode
   216  			// was evicted from the cache and it was corrupt when re-read from disk)
   217  			// (err includes volume name and inode number)
   218  			logger.ErrorfWithError(err, "%s: targetInode fetch error", utils.GetFnName())
   219  			return err
   220  		}
   221  		if !ok {
   222  			// this should never happen (see above)
   223  			err = fmt.Errorf("%s: Link failing request to link to inode %d volume '%s' because it is unallocated",
   224  				utils.GetFnName(), targetInode.InodeNumber, vS.volumeName)
   225  			err = blunder.AddError(err, blunder.NotFoundError)
   226  			logger.ErrorWithError(err)
   227  			return err
   228  		}
   229  
   230  		err = linkInMemory(dirInode, targetInode, basename)
   231  		if err != nil {
   232  			return err
   233  		}
   234  
   235  		flushInodeList = []*inMemoryInodeStruct{dirInode, targetInode}
   236  	}
   237  
   238  	// REVIEW TODO: We think we need to do something more than just return an error here :-)
   239  
   240  	err = vS.flushInodes(flushInodeList)
   241  	if err != nil {
   242  		logger.ErrorWithError(err)
   243  		return err
   244  	}
   245  
   246  	stats.IncrementOperations(&stats.DirLinkSuccessOps)
   247  	return nil
   248  }
   249  
   250  // Manipulate a directory to remove an an entry. Like Unlink(), but without any inode loading or flushing.
   251  func unlinkInMemory(dirInode *inMemoryInodeStruct, untargetInode *inMemoryInodeStruct, basename string) (err error) {
   252  	dirMapping := dirInode.payload.(sortedmap.BPlusTree)
   253  
   254  	dirInode.dirty = true
   255  	untargetInode.dirty = true
   256  
   257  	ok, err := dirMapping.DeleteByKey(basename)
   258  	if nil != err {
   259  		panic(err)
   260  	}
   261  	if !ok {
   262  		err = fmt.Errorf("Unlink(): dirInode DeleteByKey of \"%v\" should have returned ok == true", basename)
   263  		panic(err)
   264  	}
   265  
   266  	untargetInode.LinkCount--
   267  
   268  	if DirType == untargetInode.InodeType {
   269  		untargetDirMapping := untargetInode.payload.(sortedmap.BPlusTree)
   270  
   271  		ok, err = untargetDirMapping.DeleteByKey("..")
   272  		if nil != err {
   273  			panic(err)
   274  		}
   275  		if !ok {
   276  			err = fmt.Errorf("Unlink(): untargetInode DeleteByKey of \"..\" should have returned ok == true")
   277  			panic(err)
   278  		}
   279  
   280  		dirInode.LinkCount--
   281  	}
   282  
   283  	updateTime := time.Now()
   284  
   285  	dirInode.AttrChangeTime = updateTime
   286  	dirInode.ModificationTime = updateTime
   287  
   288  	untargetInode.AttrChangeTime = updateTime
   289  
   290  	return
   291  }
   292  
   293  // Remove-only version of unlinkInMemory()
   294  func unlinkInMemoryRemoveOnly(dirInode *inMemoryInodeStruct, basename string) (err error) {
   295  	dirMapping := dirInode.payload.(sortedmap.BPlusTree)
   296  
   297  	dirInode.dirty = true
   298  
   299  	ok, err := dirMapping.DeleteByKey(basename)
   300  	if nil != err {
   301  		panic(err)
   302  	}
   303  	if !ok {
   304  		err = fmt.Errorf("Unlink(): dirInode DeleteByKey of \"%v\" should have returned ok == true", basename)
   305  		panic(err)
   306  	}
   307  
   308  	updateTime := time.Now()
   309  
   310  	dirInode.AttrChangeTime = updateTime
   311  	dirInode.ModificationTime = updateTime
   312  
   313  	return
   314  }
   315  
   316  func (vS *volumeStruct) Unlink(dirInodeNumber InodeNumber, basename string, removeOnly bool) (err error) {
   317  	var (
   318  		dirInode            *inMemoryInodeStruct
   319  		flushInodeList      []*inMemoryInodeStruct
   320  		ok                  bool
   321  		snapShotIDType      headhunter.SnapShotIDType
   322  		untargetInode       *inMemoryInodeStruct
   323  		untargetInodeNumber InodeNumber
   324  	)
   325  
   326  	err = enforceRWMode(false)
   327  	if nil != err {
   328  		return
   329  	}
   330  
   331  	if (RootDirInodeNumber == dirInodeNumber) && (SnapShotDirName == basename) {
   332  		err = blunder.NewError(blunder.InvalidArgError, "Unlink() of /%v not allowed", SnapShotDirName)
   333  		return
   334  	}
   335  	snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirInodeNumber))
   336  	if headhunter.SnapShotIDTypeLive != snapShotIDType {
   337  		err = blunder.NewError(blunder.InvalidArgError, "Unlink() on non-LiveView dirInodeNumber not allowed")
   338  		return
   339  	}
   340  
   341  	stats.IncrementOperations(&stats.DirUnlinkOps)
   342  
   343  	dirInode, err = vS.fetchInodeType(dirInodeNumber, DirType)
   344  	if nil != err {
   345  		return err
   346  	}
   347  
   348  	untargetInodeNumber, err = vS.lookupByDirInodeNumber(dirInodeNumber, basename)
   349  	if nil != err {
   350  		err = blunder.AddError(err, blunder.NotFoundError)
   351  		return err
   352  	}
   353  
   354  	if removeOnly {
   355  		err = unlinkInMemoryRemoveOnly(dirInode, basename)
   356  		if err != nil {
   357  			return err
   358  		}
   359  
   360  		flushInodeList = []*inMemoryInodeStruct{dirInode}
   361  	} else {
   362  
   363  		untargetInode, ok, err = vS.fetchInode(untargetInodeNumber)
   364  		if nil != err {
   365  			// the inode is locked so this should never happen (unless the inode
   366  			// was evicted from the cache and it was corrupt when re-read from disk)
   367  			// (err includes volume name and inode number)
   368  			logger.ErrorfWithError(err, "%s: fetch of target inode failed", utils.GetFnName())
   369  			return err
   370  		}
   371  		if !ok {
   372  			// this should never happen (see above)
   373  			err = fmt.Errorf("%s: failing request to Unlink inode %d volume '%s' because it is unallocated",
   374  				utils.GetFnName(), untargetInode.InodeNumber, vS.volumeName)
   375  			err = blunder.AddError(err, blunder.NotFoundError)
   376  			logger.ErrorWithError(err)
   377  			return err
   378  		}
   379  
   380  		// Pre-flush untargetInode so that no time-based (implicit) flushes will occur during this transaction
   381  		err = vS.flushInode(untargetInode)
   382  		if err != nil {
   383  			logger.ErrorfWithError(err, "Unlink(): untargetInode flush error")
   384  			panic(err)
   385  		}
   386  
   387  		err = unlinkInMemory(dirInode, untargetInode, basename)
   388  		if err != nil {
   389  			return err
   390  		}
   391  
   392  		flushInodeList = []*inMemoryInodeStruct{dirInode, untargetInode}
   393  	}
   394  
   395  	err = vS.flushInodes(flushInodeList)
   396  	if err != nil {
   397  		logger.ErrorWithError(err)
   398  		return err
   399  	}
   400  
   401  	stats.IncrementOperations(&stats.DirUnlinkSuccessOps)
   402  	return
   403  }
   404  
   405  func (vS *volumeStruct) Move(srcDirInodeNumber InodeNumber, srcBasename string, dstDirInodeNumber InodeNumber, dstBasename string) (err error) {
   406  	err = enforceRWMode(false)
   407  	if nil != err {
   408  		return
   409  	}
   410  
   411  	if (RootDirInodeNumber == srcDirInodeNumber) && (SnapShotDirName == srcBasename) {
   412  		err = blunder.NewError(blunder.InvalidArgError, "Move() from /%v not allowed", SnapShotDirName)
   413  		return
   414  	}
   415  	snapShotIDType, _, _ := vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(srcDirInodeNumber))
   416  	if headhunter.SnapShotIDTypeLive != snapShotIDType {
   417  		err = blunder.NewError(blunder.InvalidArgError, "Move() on non-LiveView srcDirInodeNumber not allowed")
   418  		return
   419  	}
   420  	if (RootDirInodeNumber == dstDirInodeNumber) && (SnapShotDirName == dstBasename) {
   421  		err = blunder.NewError(blunder.InvalidArgError, "Move() into /%v not allowed", SnapShotDirName)
   422  		return
   423  	}
   424  	snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dstDirInodeNumber))
   425  	if headhunter.SnapShotIDTypeLive != snapShotIDType {
   426  		err = blunder.NewError(blunder.InvalidArgError, "Move() on non-LiveView dstDirInodeNumber not allowed")
   427  		return
   428  	}
   429  
   430  	stats.IncrementOperations(&stats.DirRenameOps)
   431  
   432  	srcDirInode, err := vS.fetchInodeType(srcDirInodeNumber, DirType)
   433  	if nil != err {
   434  		logger.ErrorfWithError(err, "Move(): srcDirInode fetch error")
   435  		panic(err)
   436  	}
   437  	srcDirMapping := srcDirInode.payload.(sortedmap.BPlusTree)
   438  
   439  	var dstDirInode *inMemoryInodeStruct
   440  	var dstDirMapping sortedmap.BPlusTree
   441  	if srcDirInodeNumber == dstDirInodeNumber {
   442  		if srcBasename == dstBasename {
   443  			err = fmt.Errorf("%v: Source & Target of Move() cannot be identical: %v/%v", utils.GetFnName(), srcDirInodeNumber, srcBasename)
   444  			logger.ErrorWithError(err)
   445  			err = blunder.AddError(err, blunder.FileExistsError)
   446  			return
   447  		}
   448  		dstDirInode = srcDirInode
   449  		dstDirMapping = srcDirMapping
   450  	} else {
   451  		dstDirInode, err = vS.fetchInodeType(dstDirInodeNumber, DirType)
   452  		if nil != err {
   453  			logger.ErrorfWithError(err, "Move(): dstDirInode fetch error")
   454  			panic(err)
   455  		}
   456  		dstDirMapping = dstDirInode.payload.(sortedmap.BPlusTree)
   457  	}
   458  
   459  	srcInodeNumberAsValue, ok, err := srcDirMapping.GetByKey(srcBasename)
   460  	if nil != err {
   461  		panic(err)
   462  	}
   463  	if !ok {
   464  		err = fmt.Errorf("%v: unable to find basename %v in dirInode %v", utils.GetFnName(), srcBasename, srcDirInodeNumber)
   465  		logger.ErrorWithError(err)
   466  		err = blunder.AddError(err, blunder.NotFoundError)
   467  		return
   468  	}
   469  	srcInodeNumber := srcInodeNumberAsValue.(InodeNumber)
   470  
   471  	srcInode, ok, err := vS.fetchInode(srcInodeNumber)
   472  	if nil != err {
   473  		// the inode is locked so this should never happen (unless the inode
   474  		// was evicted from the cache and it was corrupt when re-read from disk)
   475  		// (err includes volume name and inode number)
   476  		logger.ErrorfWithError(err, "%s: fetch of src inode failed", utils.GetFnName())
   477  		return err
   478  	}
   479  	if !ok {
   480  		// this should never happen (see above)
   481  		err = fmt.Errorf("%s: failing request because src inode %d volume '%s' is unallocated",
   482  			utils.GetFnName(), srcInode.InodeNumber, vS.volumeName)
   483  		err = blunder.AddError(err, blunder.NotDirError)
   484  		logger.ErrorWithError(err)
   485  		return err
   486  	}
   487  
   488  	var dstInodeNumber InodeNumber
   489  	var dstInode *inMemoryInodeStruct
   490  	dstInodeNumberAsValue, ok, err := dstDirMapping.GetByKey(dstBasename)
   491  	if nil != err {
   492  		// this indicates disk corruption or software bug
   493  		logger.ErrorfWithError(err, "%s: dstDirInode GetByKey(%s) error inode %d volume '%s'",
   494  			utils.GetFnName(), dstBasename, dstDirInode.InodeNumber, vS.volumeName)
   495  		panic(err)
   496  	}
   497  	if ok {
   498  		dstInodeNumber = dstInodeNumberAsValue.(InodeNumber)
   499  
   500  		dstInode, ok, err = vS.fetchInode(dstInodeNumber)
   501  		if nil != err {
   502  			// this indicates disk corruption or software bug
   503  			// (err includes volume name and inode number)
   504  			logger.ErrorfWithError(err, "%s: dstInode fetch error", utils.GetFnName())
   505  			panic(err)
   506  		}
   507  		if !ok {
   508  			// disk corruption or software bug
   509  			err = fmt.Errorf("%s: dstInode inode %d volume '%s' is unallocated",
   510  				utils.GetFnName(), dstInode.InodeNumber, vS.volumeName)
   511  			err = blunder.AddError(err, blunder.NotFoundError)
   512  			logger.ErrorWithError(err)
   513  			panic(err)
   514  		}
   515  	} else {
   516  		dstInodeNumber = InodeNumber(0)
   517  		dstInode = nil
   518  	}
   519  
   520  	// I believe this is allowed so long at the dstInode is empty --craig
   521  	if (nil != dstInode) && (DirType == dstInode.InodeType) {
   522  		err = fmt.Errorf("%v: Target of Move() is an existing directory: %v/%v", utils.GetFnName(), dstDirInodeNumber, dstBasename)
   523  		logger.ErrorWithError(err)
   524  		err = blunder.AddError(err, blunder.FileExistsError)
   525  		return
   526  	}
   527  
   528  	// All set to proceed
   529  
   530  	if FileType == srcInode.InodeType {
   531  		// Pre-flush srcInode so that no time-based (implicit) flushes will occur during this transaction
   532  		err = vS.flushInode(srcInode)
   533  		if err != nil {
   534  			logger.ErrorfWithError(err, "Move(): srcInode flush error")
   535  			panic(err)
   536  		}
   537  	}
   538  	if (nil != dstInode) && (FileType == dstInode.InodeType) {
   539  		// Pre-flush dstInode so that no time-based (implicit) flushes will occur during this transaction
   540  		err = vS.flushInode(dstInode)
   541  		if err != nil {
   542  			logger.ErrorfWithError(err, "Move(): dstInode flush error")
   543  			panic(err)
   544  		}
   545  	}
   546  
   547  	updateTime := time.Now()
   548  
   549  	inodes := make([]*inMemoryInodeStruct, 0, 4)
   550  
   551  	srcDirInode.dirty = true
   552  	srcDirInode.AttrChangeTime = updateTime
   553  	srcDirInode.ModificationTime = updateTime
   554  	inodes = append(inodes, srcDirInode)
   555  
   556  	if srcDirInodeNumber != dstDirInodeNumber {
   557  		dstDirInode.dirty = true
   558  		dstDirInode.AttrChangeTime = updateTime
   559  		dstDirInode.ModificationTime = updateTime
   560  		inodes = append(inodes, dstDirInode)
   561  
   562  		if DirType == srcInode.InodeType {
   563  			srcDirInode.LinkCount--
   564  			dstDirInode.LinkCount++
   565  
   566  			srcInodeAsDirMapping := srcInode.payload.(sortedmap.BPlusTree)
   567  			ok, err = srcInodeAsDirMapping.PatchByKey("..", dstDirInodeNumber)
   568  			if nil != err {
   569  				logger.ErrorfWithError(err, "Move(): srcInode PatchByKey error")
   570  				panic(err)
   571  			}
   572  			if !ok {
   573  				err = fmt.Errorf("Should have found \"..\" entry")
   574  				logger.ErrorfWithError(err, "Move(): srcInode PatchByKey error")
   575  				panic(err)
   576  			}
   577  		}
   578  	}
   579  
   580  	srcInode.dirty = true
   581  	srcInode.AttrChangeTime = updateTime
   582  	inodes = append(inodes, srcInode)
   583  
   584  	ok, err = srcDirMapping.DeleteByKey(srcBasename)
   585  	if nil != err {
   586  		logger.ErrorfWithError(err, "Move(): srcDirInode DeleteByKey error")
   587  		panic(err)
   588  	}
   589  	if !ok {
   590  		err = fmt.Errorf("Should have found \"%v\" entry", srcBasename)
   591  		logger.ErrorfWithError(err, "Move(): srcDirInode DeleteByKey error")
   592  		panic(err)
   593  	}
   594  
   595  	if nil == dstInode {
   596  		ok, err = dstDirMapping.Put(dstBasename, srcInodeNumber)
   597  		if nil != err {
   598  			logger.ErrorfWithError(err, "Move(): dstDirInode Put error")
   599  			panic(err)
   600  		}
   601  		if !ok {
   602  			err = fmt.Errorf("Should have been able to PUT \"%v\" entry", dstBasename)
   603  			logger.ErrorfWithError(err, "Move(): dstDirInode Put error")
   604  			panic(err)
   605  		}
   606  	} else {
   607  		dstInode.dirty = true
   608  		dstInode.AttrChangeTime = updateTime
   609  		inodes = append(inodes, dstInode)
   610  
   611  		dstInode.LinkCount--
   612  
   613  		ok, err = dstDirMapping.PatchByKey(dstBasename, srcInodeNumber)
   614  		if nil != err {
   615  			logger.ErrorfWithError(err, "Move(): dstDirInode PatchByKey error")
   616  			panic(err)
   617  		}
   618  		if !ok {
   619  			err = fmt.Errorf("Should have been able to PatchByKey \"%v\" entry", dstBasename)
   620  			logger.ErrorfWithError(err, "Move(): dstDirInode PatchByKey error")
   621  			panic(err)
   622  		}
   623  	}
   624  
   625  	// Flush the multi-inode transaction
   626  
   627  	err = vS.flushInodes(inodes)
   628  	if err != nil {
   629  		logger.ErrorfWithError(err, "flushInodes(%v) error", inodes)
   630  		panic(err)
   631  	}
   632  
   633  	// And, if we decremented dstInode.LinkCount to zero, destroy dstInode as well
   634  
   635  	if (nil != dstInode) && (0 == dstInode.LinkCount) {
   636  		err = vS.Destroy(dstInode.InodeNumber)
   637  	}
   638  
   639  	stats.IncrementOperations(&stats.DirRenameSuccessOps)
   640  	return
   641  }
   642  
   643  func (vS *volumeStruct) lookupByDirInode(dirInode *inMemoryInodeStruct, basename string) (targetInodeNumber InodeNumber, err error) {
   644  	var (
   645  		dirInodeSnapShotID uint64
   646  		dirMapping         sortedmap.BPlusTree
   647  		ok                 bool
   648  		value              sortedmap.Value
   649  	)
   650  
   651  	_, dirInodeSnapShotID, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirInode.InodeNumber))
   652  
   653  	dirMapping = dirInode.payload.(sortedmap.BPlusTree)
   654  	value, ok, err = dirMapping.GetByKey(basename)
   655  	if nil != err {
   656  		panic(err)
   657  	}
   658  	if !ok {
   659  		err = fmt.Errorf("unable to find basename %v in dirInode @ %p", basename, dirInode)
   660  		// There are cases where failing to find an inode is not an error.
   661  		// Not logging any errors here; let the caller decide if this is log-worthy
   662  		err = blunder.AddError(err, blunder.NotFoundError)
   663  		return
   664  	}
   665  	targetInodeNumber, ok = value.(InodeNumber)
   666  	if !ok {
   667  		err = fmt.Errorf("dirMapping for basename %v in dirInode @ %p not an InodeNumber", basename, dirInode)
   668  		panic(err)
   669  	}
   670  	targetInodeNumber = InodeNumber(vS.headhunterVolumeHandle.SnapShotIDAndNonceEncode(dirInodeSnapShotID, uint64(targetInodeNumber)))
   671  
   672  	return
   673  }
   674  
   675  func (vS *volumeStruct) lookupByDirInodeNumber(dirInodeNumber InodeNumber, basename string) (targetInodeNumber InodeNumber, err error) {
   676  	var (
   677  		dirInode               *inMemoryInodeStruct
   678  		dirInodeNonce          uint64
   679  		dirInodeSnapShotID     uint64
   680  		dirInodeSnapShotIDType headhunter.SnapShotIDType
   681  		dirMapping             sortedmap.BPlusTree
   682  		ok                     bool
   683  		value                  sortedmap.Value
   684  		snapShot               headhunter.SnapShotStruct
   685  	)
   686  
   687  	dirInodeSnapShotIDType, dirInodeSnapShotID, dirInodeNonce = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirInodeNumber))
   688  
   689  	switch dirInodeSnapShotIDType {
   690  	case headhunter.SnapShotIDTypeLive:
   691  		if (uint64(RootDirInodeNumber) == dirInodeNonce) &&
   692  			(SnapShotDirName == basename) &&
   693  			(0 < vS.headhunterVolumeHandle.SnapShotCount()) {
   694  			// Lookup should only succeed there are any active SnapShot's
   695  
   696  			targetInodeNumber = InodeNumber(vS.headhunterVolumeHandle.SnapShotTypeDotSnapShotAndNonceEncode(dirInodeNonce))
   697  			err = nil
   698  
   699  			return
   700  		}
   701  		// Let normal processing perform the lookup below (SnapShotID == 0, so InodeNumber "adornment" is a no-op)
   702  	case headhunter.SnapShotIDTypeSnapShot:
   703  		// Let normal processing perform the lookup below but "adorn" resultant InodeNumber's with the dirSnapShotID
   704  	case headhunter.SnapShotIDTypeDotSnapShot:
   705  		// Currently, this is only supported in RootDirInodeNumber
   706  
   707  		if uint64(RootDirInodeNumber) != dirInodeNonce {
   708  			err = blunder.AddError(fmt.Errorf("%v other than in '/' not supported", SnapShotDirName), blunder.NotFoundError)
   709  			return
   710  		}
   711  
   712  		switch basename {
   713  		case ".":
   714  			targetInodeNumber = dirInodeNumber
   715  			err = nil
   716  		case "..":
   717  			targetInodeNumber = RootDirInodeNumber
   718  			err = nil
   719  		default:
   720  			// See if basename is the name for an active SnapShot
   721  
   722  			snapShot, ok = vS.headhunterVolumeHandle.SnapShotLookupByName(basename)
   723  
   724  			if ok {
   725  				targetInodeNumber = InodeNumber(vS.headhunterVolumeHandle.SnapShotIDAndNonceEncode(snapShot.ID, dirInodeNonce))
   726  				err = nil
   727  			} else {
   728  				err = blunder.AddError(fmt.Errorf("/%v/%v SnapShot not found", SnapShotDirName, basename), blunder.NotFoundError)
   729  			}
   730  		}
   731  
   732  		return
   733  	}
   734  
   735  	dirInode, err = vS.fetchInodeType(dirInodeNumber, DirType)
   736  	if nil != err {
   737  		logger.ErrorWithError(err)
   738  		return
   739  	}
   740  
   741  	dirMapping = dirInode.payload.(sortedmap.BPlusTree)
   742  	value, ok, err = dirMapping.GetByKey(basename)
   743  	if nil != err {
   744  		panic(err)
   745  	}
   746  	if !ok {
   747  		err = fmt.Errorf("unable to find basename %v in dirInode %v", basename, dirInodeNumber)
   748  		// There are cases where failing to find an inode is not an error.
   749  		// Not logging any errors here; let the caller decide if this is log-worthy
   750  		err = blunder.AddError(err, blunder.NotFoundError)
   751  		return
   752  	}
   753  	targetInodeNumber, ok = value.(InodeNumber)
   754  	if !ok {
   755  		err = fmt.Errorf("dirMapping for basename %v in dirInode %v not an InodeNumber", basename, dirInodeNumber)
   756  		panic(err)
   757  	}
   758  	targetInodeNumber = InodeNumber(vS.headhunterVolumeHandle.SnapShotIDAndNonceEncode(dirInodeSnapShotID, uint64(targetInodeNumber)))
   759  
   760  	return
   761  }
   762  
   763  func (vS *volumeStruct) Lookup(dirInodeNumber InodeNumber, basename string) (targetInodeNumber InodeNumber, err error) {
   764  	stats.IncrementOperations(&stats.DirLookupOps)
   765  
   766  	targetInodeNumber, err = vS.lookupByDirInodeNumber(dirInodeNumber, basename)
   767  
   768  	return
   769  }
   770  
   771  func (vS *volumeStruct) NumDirEntries(dirInodeNumber InodeNumber) (numEntries uint64, err error) {
   772  	var (
   773  		adjustNumEntriesForSnapShotSubDirInRootDirInode bool
   774  		dirMapping                                      sortedmap.BPlusTree
   775  		dirMappingLen                                   int
   776  		inode                                           *inMemoryInodeStruct
   777  		snapShotCount                                   uint64
   778  		snapShotIDType                                  headhunter.SnapShotIDType
   779  	)
   780  
   781  	if RootDirInodeNumber == dirInodeNumber {
   782  		// Account for .. in /<SnapShotDirName> if any SnapShot's exist
   783  		snapShotCount = vS.headhunterVolumeHandle.SnapShotCount()
   784  		adjustNumEntriesForSnapShotSubDirInRootDirInode = (0 != snapShotCount)
   785  	} else {
   786  		snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirInodeNumber))
   787  		if headhunter.SnapShotIDTypeDotSnapShot == snapShotIDType {
   788  			// numEntries == 1 ('.') + 1 ('..') + # SnapShot's
   789  			snapShotCount = vS.headhunterVolumeHandle.SnapShotCount()
   790  			numEntries = 1 + 1 + snapShotCount
   791  			err = nil
   792  			return
   793  		}
   794  		adjustNumEntriesForSnapShotSubDirInRootDirInode = false
   795  	}
   796  
   797  	inode, err = vS.fetchInodeType(dirInodeNumber, DirType)
   798  	if nil != err {
   799  		return
   800  	}
   801  
   802  	dirMapping = inode.payload.(sortedmap.BPlusTree)
   803  
   804  	dirMappingLen, err = dirMapping.Len()
   805  	if nil != err {
   806  		err = blunder.AddError(err, blunder.IOError)
   807  		return
   808  	}
   809  
   810  	if adjustNumEntriesForSnapShotSubDirInRootDirInode {
   811  		numEntries = uint64(dirMappingLen) + 1
   812  	} else {
   813  		numEntries = uint64(dirMappingLen)
   814  	}
   815  
   816  	return
   817  }
   818  
   819  // A maxEntries or maxBufSize argument of zero is interpreted to mean "no maximum".
   820  func (vS *volumeStruct) ReadDir(dirInodeNumber InodeNumber, maxEntries uint64, maxBufSize uint64, prevReturned ...interface{}) (dirEntries []DirEntry, moreEntries bool, err error) {
   821  	var (
   822  		bufSize                      uint64
   823  		dirEntryBasename             string
   824  		dirEntryBasenameAsKey        sortedmap.Key
   825  		dirEntryInodeNumber          InodeNumber
   826  		dirEntryInodeNumberAsValue   sortedmap.Value
   827  		dirIndex                     int
   828  		dirMapping                   sortedmap.BPlusTree
   829  		dirMappingLen                int         // If snapShotDirToBeInserted, this is 1 + dirMapping.Len()
   830  		dotDotInodeNumberReplacement InodeNumber // If == 0, do not replace ..'s InodeNumber
   831  		foundPrevReturned            bool
   832  		inode                        *inMemoryInodeStruct
   833  		key                          sortedmap.Key
   834  		nextEntry                    DirEntry
   835  		nonce                        uint64
   836  		okGetByIndex                 bool
   837  		okKeyAsString                bool
   838  		okPayloadBPlusTree           bool
   839  		okPutToSnapShotListSorted    bool
   840  		okValueAsInodeNumber         bool
   841  		snapShotDirFound             bool
   842  		snapShotDirIndex             int  // If snapShotDirToBeInserted, this is the index where it goes
   843  		snapShotDirToBeInserted      bool // Only true in /<SnapShotDirName>
   844  		snapShotID                   uint64
   845  		snapShotIDType               headhunter.SnapShotIDType
   846  		snapShotList                 []headhunter.SnapShotStruct
   847  		snapShotListElement          headhunter.SnapShotStruct
   848  		snapShotListSorted           sortedmap.LLRBTree // Will also include '.' & '..'
   849  		snapShotListSortedLen        int
   850  		value                        sortedmap.Value
   851  	)
   852  
   853  	// The following defer'd func() is useful for debugging... so leaving it in here as a comment
   854  	/*
   855  		defer func() {
   856  			logger.Errorf("Executed inode.ReadDir()...")
   857  			logger.Errorf("  dirInodeNumber: 0x%016X", dirInodeNumber)
   858  			logger.Errorf("  maxEntries:     0x%016X", maxEntries)
   859  			logger.Errorf("  maxBufSize:     0x%016X", maxBufSize)
   860  			switch len(prevReturned) {
   861  			case 0:
   862  				// Nothing
   863  			case 1:
   864  				logger.Errorf("  prevReturned:   %v", prevReturned[0])
   865  			default:
   866  				logger.Errorf("  len(prevReturned) [%v] should have been 0 or 1", len(prevReturned))
   867  			}
   868  			if nil == err {
   869  				for dirEntriesIndex, dirEntry := range dirEntries {
   870  					logger.Errorf("  dirEntries[%v]:", dirEntriesIndex)
   871  					logger.Errorf("    InodeNumber:     0x%016X", dirEntry.InodeNumber)
   872  					logger.Errorf("    Basename:        %s", dirEntry.Basename)
   873  					logger.Errorf("    InodeType:       %v", dirEntry.Type)
   874  					logger.Errorf("    NextDirLocation: %v", dirEntry.NextDirLocation)
   875  				}
   876  			}
   877  			logger.Errorf("  moreEntries:    %v", moreEntries)
   878  			logger.Errorf("  err:            %v", err)
   879  		}()
   880  	*/
   881  
   882  	stats.IncrementOperations(&stats.DirReaddirOps)
   883  
   884  	dirEntries = make([]DirEntry, 0, int(maxEntries))
   885  	moreEntries = false
   886  
   887  	snapShotIDType, snapShotID, nonce = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirInodeNumber))
   888  
   889  	if headhunter.SnapShotIDTypeDotSnapShot == snapShotIDType {
   890  		if uint64(RootDirInodeNumber) != nonce {
   891  			err = fmt.Errorf("ReadDir() to %v not in '/' not supported", SnapShotDirName)
   892  			err = blunder.AddError(err, blunder.NotSupportedError)
   893  			return
   894  		}
   895  
   896  		snapShotList = vS.headhunterVolumeHandle.SnapShotListByName(false)
   897  
   898  		snapShotListSorted = sortedmap.NewLLRBTree(sortedmap.CompareString, nil)
   899  
   900  		okPutToSnapShotListSorted, err = snapShotListSorted.Put(".", dirInodeNumber)
   901  		if nil != err {
   902  			err = fmt.Errorf("ReadDir() encountered error populating SnapShotListSorted: %v", err)
   903  			err = blunder.AddError(err, blunder.IOError)
   904  			return
   905  		}
   906  		if !okPutToSnapShotListSorted {
   907  			err = fmt.Errorf("ReadDir() encountered !ok populating SnapShotListSorted")
   908  			err = blunder.AddError(err, blunder.IOError)
   909  			return
   910  		}
   911  
   912  		okPutToSnapShotListSorted, err = snapShotListSorted.Put("..", RootDirInodeNumber)
   913  		if nil != err {
   914  			err = fmt.Errorf("ReadDir() encountered error populating SnapShotListSorted: %v", err)
   915  			err = blunder.AddError(err, blunder.IOError)
   916  			return
   917  		}
   918  		if !okPutToSnapShotListSorted {
   919  			err = fmt.Errorf("ReadDir() encountered !ok populating SnapShotListSorted")
   920  			err = blunder.AddError(err, blunder.IOError)
   921  			return
   922  		}
   923  
   924  		for _, snapShotListElement = range snapShotList {
   925  			okPutToSnapShotListSorted, err = snapShotListSorted.Put(snapShotListElement.Name, InodeNumber(vS.headhunterVolumeHandle.SnapShotIDAndNonceEncode(snapShotListElement.ID, uint64(RootDirInodeNumber))))
   926  			if nil != err {
   927  				err = fmt.Errorf("ReadDir() encountered error populating SnapShotListSorted: %v", err)
   928  				err = blunder.AddError(err, blunder.IOError)
   929  				return
   930  			}
   931  			if !okPutToSnapShotListSorted {
   932  				err = fmt.Errorf("ReadDir() encountered !ok populating SnapShotListSorted")
   933  				err = blunder.AddError(err, blunder.IOError)
   934  				return
   935  			}
   936  		}
   937  
   938  		switch len(prevReturned) {
   939  		case 0:
   940  			dirIndex = int(0)
   941  		case 1:
   942  			var (
   943  				okAsInodeDirLocation           bool
   944  				okAsString                     bool
   945  				prevReturnedAsInodeDirLocation InodeDirLocation
   946  				prevReturnedAsString           string
   947  			)
   948  
   949  			prevReturnedAsInodeDirLocation, okAsInodeDirLocation = prevReturned[0].(InodeDirLocation)
   950  			prevReturnedAsString, okAsString = prevReturned[0].(string)
   951  
   952  			if okAsInodeDirLocation {
   953  				if 0 > prevReturnedAsInodeDirLocation {
   954  					dirIndex = int(0)
   955  				} else {
   956  					dirIndex = int(prevReturnedAsInodeDirLocation + 1)
   957  				}
   958  			} else if okAsString {
   959  				dirIndex, foundPrevReturned, err = snapShotListSorted.BisectRight(prevReturnedAsString)
   960  				if nil != err {
   961  					err = fmt.Errorf("ReadDir() encountered error bisecting SnapShotListSorted: %v", err)
   962  				}
   963  				if foundPrevReturned {
   964  					dirIndex++
   965  				}
   966  			} else {
   967  				err = fmt.Errorf("ReadDir() accepts only zero or one (InodeDirLocation or string) trailing prevReturned argument")
   968  				err = blunder.AddError(err, blunder.NotSupportedError)
   969  				return
   970  			}
   971  		default:
   972  			err = fmt.Errorf("ReadDir() accepts only zero or one (InodeDirLocation or string) trailing prevReturned argument")
   973  			err = blunder.AddError(err, blunder.NotSupportedError)
   974  			return
   975  		}
   976  
   977  		bufSize = 0
   978  
   979  		snapShotListSortedLen, err = snapShotListSorted.Len()
   980  		if nil != err {
   981  			err = fmt.Errorf("ReadDir() encountered error accessing SnapShotListSorted: %v", err)
   982  			err = blunder.AddError(err, blunder.IOError)
   983  			return
   984  		}
   985  
   986  		for {
   987  			if snapShotListSortedLen <= dirIndex {
   988  				break
   989  			}
   990  
   991  			dirEntryBasenameAsKey, dirEntryInodeNumberAsValue, okGetByIndex, err = snapShotListSorted.GetByIndex(dirIndex)
   992  			if nil != err {
   993  				err = fmt.Errorf("ReadDir() encountered error accessing SnapShotListSorted: %v", err)
   994  				err = blunder.AddError(err, blunder.IOError)
   995  				return
   996  			}
   997  			if !okGetByIndex {
   998  				err = fmt.Errorf("ReadDir() encountered !ok accessing SnapShotListSorted: %v", err)
   999  				err = blunder.AddError(err, blunder.IOError)
  1000  				return
  1001  			}
  1002  
  1003  			dirEntryBasename, okKeyAsString = dirEntryBasenameAsKey.(string)
  1004  			if !okKeyAsString {
  1005  				err = fmt.Errorf("ReadDir() encountered !ok accessing SnapShotListSorted: %v", err)
  1006  				err = blunder.AddError(err, blunder.IOError)
  1007  				return
  1008  			}
  1009  
  1010  			dirEntryInodeNumber, okValueAsInodeNumber = dirEntryInodeNumberAsValue.(InodeNumber)
  1011  			if !okValueAsInodeNumber {
  1012  				err = fmt.Errorf("ReadDir() encountered !ok accessing SnapShotListSorted: %v", err)
  1013  				err = blunder.AddError(err, blunder.IOError)
  1014  				return
  1015  			}
  1016  
  1017  			nextEntry = DirEntry{
  1018  				InodeNumber:     dirEntryInodeNumber,
  1019  				Basename:        dirEntryBasename,
  1020  				NextDirLocation: InodeDirLocation(dirIndex) + 1,
  1021  			}
  1022  
  1023  			if (0 != maxEntries) && (uint64(len(dirEntries)+1) > maxEntries) {
  1024  				break
  1025  			}
  1026  			if (0 != maxBufSize) && ((bufSize + uint64(nextEntry.Size())) > maxBufSize) {
  1027  				break
  1028  			}
  1029  
  1030  			dirEntries = append(dirEntries, nextEntry)
  1031  			bufSize += uint64(nextEntry.Size())
  1032  			dirIndex++
  1033  		}
  1034  
  1035  		moreEntries = dirIndex < dirMappingLen
  1036  
  1037  		stats.IncrementOperationsEntriesAndBytes(stats.DirRead, uint64(len(dirEntries)), bufSize)
  1038  
  1039  		err = nil
  1040  		return
  1041  	}
  1042  
  1043  	// If we reach here, snapShotIDType is one of headhunter.SnapShotIDType{Live|SnapShotIDTypeSnapShot}
  1044  
  1045  	inode, err = vS.fetchInodeType(dirInodeNumber, DirType)
  1046  	if nil != err {
  1047  		return
  1048  	}
  1049  
  1050  	dirMapping, okPayloadBPlusTree = inode.payload.(sortedmap.BPlusTree)
  1051  	if !okPayloadBPlusTree {
  1052  		err = fmt.Errorf("ReadDir() found unexpected Inode Payload")
  1053  		err = blunder.AddError(err, blunder.IOError)
  1054  		return
  1055  	}
  1056  
  1057  	defer func() {
  1058  		if nil != err {
  1059  			logger.Errorf("Calling dirMapping.DumpRaw() due to error reading DirInode %016X: %v", dirInodeNumber, err)
  1060  			dumpRawLines := dirMapping.DumpRaw()
  1061  			for _, dumpRawLine := range dumpRawLines {
  1062  				logger.Errorf("%s", dumpRawLine)
  1063  			}
  1064  		}
  1065  	}()
  1066  
  1067  	dirMappingLen, err = dirMapping.Len()
  1068  	if nil != err {
  1069  		err = blunder.AddError(err, blunder.IOError)
  1070  		return
  1071  	}
  1072  
  1073  	snapShotDirToBeInserted = false               // By default, SnapShotDirName not to be inserted
  1074  	dotDotInodeNumberReplacement = InodeNumber(0) // By default, do not replace ..'s InodeNumber
  1075  
  1076  	if headhunter.SnapShotIDTypeLive == snapShotIDType {
  1077  		if RootDirInodeNumber == dirInodeNumber {
  1078  			if uint64(0) < vS.headhunterVolumeHandle.SnapShotCount() {
  1079  				// Need to find a spot to insert SnapShotDirName
  1080  
  1081  				snapShotDirIndex, snapShotDirFound, err = dirMapping.BisectRight(SnapShotDirName)
  1082  				if nil != err {
  1083  					err = blunder.AddError(err, blunder.IOError)
  1084  					return
  1085  				}
  1086  				if snapShotDirFound {
  1087  					err = fmt.Errorf("ReadDir() encountered pre-existing /%v dirEntry", SnapShotDirName)
  1088  					err = blunder.AddError(err, blunder.IOError)
  1089  					return
  1090  				}
  1091  
  1092  				snapShotDirToBeInserted = true
  1093  				dirMappingLen++
  1094  			}
  1095  		}
  1096  	} else {
  1097  		if uint64(RootDirInodeNumber) == nonce {
  1098  			dotDotInodeNumberReplacement = InodeNumber(vS.headhunterVolumeHandle.SnapShotTypeDotSnapShotAndNonceEncode(uint64(RootDirInodeNumber)))
  1099  		}
  1100  	}
  1101  
  1102  	switch len(prevReturned) {
  1103  	case 0:
  1104  		dirIndex = int(0)
  1105  	case 1:
  1106  		var (
  1107  			foundDoingBisectRight          bool
  1108  			okAsInodeDirLocation           bool
  1109  			okAsString                     bool
  1110  			prevReturnedAsInodeDirLocation InodeDirLocation
  1111  			prevReturnedAsString           string
  1112  		)
  1113  
  1114  		prevReturnedAsInodeDirLocation, okAsInodeDirLocation = prevReturned[0].(InodeDirLocation)
  1115  		prevReturnedAsString, okAsString = prevReturned[0].(string)
  1116  
  1117  		if okAsInodeDirLocation {
  1118  			if 0 > prevReturnedAsInodeDirLocation {
  1119  				dirIndex = int(0)
  1120  			} else {
  1121  				dirIndex = int(prevReturnedAsInodeDirLocation + 1)
  1122  			}
  1123  		} else if okAsString {
  1124  			if snapShotDirToBeInserted {
  1125  				if SnapShotDirName == prevReturnedAsString {
  1126  					dirIndex = snapShotDirIndex + 1
  1127  				} else {
  1128  					dirIndex, foundDoingBisectRight, err = dirMapping.BisectRight(prevReturnedAsString)
  1129  					if nil != err {
  1130  						err = blunder.AddError(err, blunder.IOError)
  1131  						return
  1132  					}
  1133  					if dirIndex >= snapShotDirIndex {
  1134  						dirIndex++
  1135  					}
  1136  					if foundDoingBisectRight {
  1137  						dirIndex++
  1138  					}
  1139  				}
  1140  			} else {
  1141  				dirIndex, foundDoingBisectRight, err = dirMapping.BisectRight(prevReturnedAsString)
  1142  				if nil != err {
  1143  					err = blunder.AddError(err, blunder.IOError)
  1144  					return
  1145  				}
  1146  				if foundDoingBisectRight {
  1147  					dirIndex++
  1148  				}
  1149  			}
  1150  		} else {
  1151  			err = fmt.Errorf("ReadDir() accepts only zero or one (InodeDirLocation or string) trailing prevReturned argument")
  1152  			err = blunder.AddError(err, blunder.NotSupportedError)
  1153  			return
  1154  		}
  1155  	default:
  1156  		err = fmt.Errorf("ReadDir() accepts only zero or one (InodeDirLocation or string) trailing prevReturned argument")
  1157  		err = blunder.AddError(err, blunder.NotSupportedError)
  1158  		return
  1159  	}
  1160  
  1161  	bufSize = 0
  1162  
  1163  	for {
  1164  		if snapShotDirToBeInserted && (dirIndex == snapShotDirIndex) {
  1165  			dirEntryBasename = SnapShotDirName
  1166  			dirEntryInodeNumber = InodeNumber(vS.headhunterVolumeHandle.SnapShotTypeDotSnapShotAndNonceEncode(uint64(RootDirInodeNumber)))
  1167  		} else {
  1168  			if snapShotDirToBeInserted {
  1169  				if dirIndex < snapShotDirIndex {
  1170  					key, value, okGetByIndex, err = dirMapping.GetByIndex(dirIndex)
  1171  				} else {
  1172  					key, value, okGetByIndex, err = dirMapping.GetByIndex(dirIndex - 1)
  1173  				}
  1174  			} else {
  1175  				key, value, okGetByIndex, err = dirMapping.GetByIndex(dirIndex)
  1176  			}
  1177  			if nil != err {
  1178  				err = blunder.AddError(err, blunder.IOError)
  1179  				return
  1180  			}
  1181  			if !okGetByIndex {
  1182  				break
  1183  			}
  1184  
  1185  			dirEntryBasename, okKeyAsString = key.(string)
  1186  			if !okKeyAsString {
  1187  				err = fmt.Errorf("ReadDir() encountered dirEntry with non-string Key")
  1188  				err = blunder.AddError(err, blunder.IOError)
  1189  				return
  1190  			}
  1191  
  1192  			if (InodeNumber(0) != dotDotInodeNumberReplacement) && (".." == dirEntryBasename) {
  1193  				dirEntryInodeNumber = dotDotInodeNumberReplacement
  1194  			} else {
  1195  				dirEntryInodeNumber = InodeNumber(vS.headhunterVolumeHandle.SnapShotIDAndNonceEncode(snapShotID, uint64(value.(InodeNumber))))
  1196  			}
  1197  		}
  1198  
  1199  		nextEntry = DirEntry{
  1200  			InodeNumber:     dirEntryInodeNumber,
  1201  			Basename:        dirEntryBasename,
  1202  			NextDirLocation: InodeDirLocation(dirIndex) + 1,
  1203  		}
  1204  
  1205  		if (0 != maxEntries) && (uint64(len(dirEntries)+1) > maxEntries) {
  1206  			break
  1207  		}
  1208  		if (0 != maxBufSize) && ((bufSize + uint64(nextEntry.Size())) > maxBufSize) {
  1209  			break
  1210  		}
  1211  
  1212  		dirEntries = append(dirEntries, nextEntry)
  1213  		bufSize += uint64(nextEntry.Size())
  1214  
  1215  		dirIndex++ // Consumed one dirEntry either manufactured (/<SnapShotDirName>) or from dirMapping
  1216  	}
  1217  
  1218  	moreEntries = dirIndex < dirMappingLen
  1219  
  1220  	stats.IncrementOperationsEntriesAndBytes(stats.DirRead, uint64(len(dirEntries)), bufSize)
  1221  
  1222  	err = nil
  1223  	return
  1224  }
  1225  
  1226  func (vS *volumeStruct) ReplaceDirEntries(parentDirInodeNumber InodeNumber, parentDirEntryBasename string, dirInodeNumber InodeNumber, dirEntryInodeNumbers []InodeNumber) (err error) {
  1227  	var (
  1228  		dirEntryBasename          string
  1229  		dirEntryInodeNumber       InodeNumber
  1230  		dirEntryIndex             int
  1231  		dirEntryInode             *inMemoryInodeStruct
  1232  		dirEntryInodes            []*inMemoryInodeStruct
  1233  		dirInode                  *inMemoryInodeStruct
  1234  		dirLinkCount              uint64
  1235  		dirMapping                sortedmap.BPlusTree
  1236  		ok                        bool
  1237  		parentDirEntryInodeNumber InodeNumber
  1238  		parentDirInode            *inMemoryInodeStruct
  1239  		snapShotIDType            headhunter.SnapShotIDType
  1240  	)
  1241  
  1242  	snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(parentDirInodeNumber))
  1243  	if headhunter.SnapShotIDTypeLive != snapShotIDType {
  1244  		err = fmt.Errorf("ReplaceDirEntries(parentDirInodeNumber==0x%016X,,) not allowed for snapShotIDType == %v", parentDirInodeNumber, snapShotIDType)
  1245  		return
  1246  	}
  1247  
  1248  	parentDirInode, err = vS.fetchInodeType(parentDirInodeNumber, DirType)
  1249  	if nil != err {
  1250  		err = fmt.Errorf("ReplaceDirEntries() cannot find parentDirInodeNumber==0x%016X: %v", parentDirInodeNumber, err)
  1251  		return
  1252  	}
  1253  
  1254  	parentDirEntryInodeNumber, err = vS.lookupByDirInode(parentDirInode, parentDirEntryBasename)
  1255  	if nil != err {
  1256  		err = fmt.Errorf("ReplaceDirEntries() lookup in parentDirInodeNumber==0x%016X at basename=\"%s\" failed: %v", parentDirInodeNumber, parentDirEntryBasename, err)
  1257  		return
  1258  	}
  1259  	if parentDirEntryInodeNumber != dirInodeNumber {
  1260  		err = fmt.Errorf("ReplaceDirEntries() lookup in parentDirInodeNumber==0x%016X at basename=\"%s\" returned unexpected parentDirEntryInodeNumber (0x%016X)... expected dirInodeNumber (0x%016X)", parentDirInodeNumber, parentDirEntryBasename, parentDirEntryInodeNumber, dirInodeNumber)
  1261  		return
  1262  	}
  1263  
  1264  	snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirInodeNumber))
  1265  	if headhunter.SnapShotIDTypeLive != snapShotIDType {
  1266  		err = fmt.Errorf("ReplaceDirEntries(,dirInodeNumber==0x%016X,) not allowed for snapShotIDType == %v", dirInodeNumber, snapShotIDType)
  1267  		return
  1268  	}
  1269  
  1270  	dirInode, err = vS.fetchInodeType(dirInodeNumber, DirType)
  1271  	if nil != err {
  1272  		err = fmt.Errorf("ReplaceDirEntries() cannot find dirInodeNumber==0x%016X: %v", dirInodeNumber, err)
  1273  		return
  1274  	}
  1275  
  1276  	dirEntryInodes = make([]*inMemoryInodeStruct, len(dirEntryInodeNumbers))
  1277  
  1278  	for dirEntryIndex, dirEntryInodeNumber = range dirEntryInodeNumbers {
  1279  		snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(dirEntryInodeNumber))
  1280  		if headhunter.SnapShotIDTypeLive != snapShotIDType {
  1281  			err = fmt.Errorf("ReplaceDirEntries(,,dirEntryInodeNumbers[%v]==0x%016X) not allowed for snapShotIDType == %v", dirEntryIndex, dirEntryInodeNumber, snapShotIDType)
  1282  			return
  1283  		}
  1284  
  1285  		dirEntryInodes[dirEntryIndex], ok, err = vS.fetchInode(dirEntryInodeNumber)
  1286  		if (nil != err) || !ok {
  1287  			err = fmt.Errorf("ReplaceDirEntries() cannot find dirEntryInodeNumbers[%v]==0x%016X: %v", dirEntryIndex, dirInodeNumber, err)
  1288  			return
  1289  		}
  1290  	}
  1291  
  1292  	dirMapping = sortedmap.NewBPlusTree(vS.maxEntriesPerDirNode, sortedmap.CompareString, &dirInodeCallbacks{treeNodeLoadable{inode: dirInode}}, globals.dirEntryCache)
  1293  
  1294  	ok, err = dirMapping.Put(".", dirInodeNumber)
  1295  	if (nil != err) || !ok {
  1296  		panic(err)
  1297  	}
  1298  
  1299  	ok, err = dirMapping.Put("..", parentDirInodeNumber)
  1300  	if (nil != err) || !ok {
  1301  		panic(err)
  1302  	}
  1303  
  1304  	dirLinkCount = 2
  1305  
  1306  	for _, dirEntryInode = range dirEntryInodes {
  1307  		dirEntryBasename = fmt.Sprintf("%016X", dirEntryInode.InodeNumber)
  1308  
  1309  		ok, err = dirMapping.Put(dirEntryBasename, dirEntryInode.InodeNumber)
  1310  		if (nil != err) || !ok {
  1311  			panic(err)
  1312  		}
  1313  
  1314  		if DirType == dirEntryInode.InodeType {
  1315  			dirLinkCount++
  1316  		}
  1317  	}
  1318  
  1319  	dirInode.payload = dirMapping
  1320  	dirInode.onDiskInodeV1Struct.LinkCount = dirLinkCount
  1321  
  1322  	dirInode.dirty = true
  1323  
  1324  	err = vS.flushInode(dirInode)
  1325  
  1326  	return
  1327  }