github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/inode/dir.go (about)

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