func populateFSAddNodes(p *populate) (err error) {
	for _, nodeToAdd := range p.nodesToAdd {
		if nodeToAdd.FsEntry.attributes.Nlink == dirLinkCount {
			err = p.fs.insertDirEntry(
				p.txns,
				nodeToAdd.parentINode,
				nodeToAdd.FsEntry,
			)
		} else {
			err = p.fs.insertFsEntry(
				p.txns,
				nodeToAdd.parentINode,
				nodeToAdd.FsEntry,
			)
		}
		if err != nil {
			return
		}
	}
	return
}
populateFSAddNodes adds the resolved nodes to the file system,
discriminating between leaf nodes and directory nodes
package fuse
import (
	"context"
	"os"
	"path"
	"time"
	"github.com/oneconcern/datamon/pkg/cafs"
	"github.com/oneconcern/datamon/pkg/core"
	"github.com/oneconcern/datamon/pkg/core/status"
	"github.com/oneconcern/datamon/pkg/dlogger"
	"github.com/oneconcern/datamon/pkg/errors"
	iradix "github.com/hashicorp/go-immutable-radix"
	"go.uber.org/zap"
	"github.com/oneconcern/datamon/pkg/model"
	jfuse "github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/fuseutil"
)
var _ fuseutil.FileSystem = &readOnlyFsInternal{}
type readOnlyFsInternal struct {
	fsCommon
	fsDirStore *iradix.Tree
	fsEntryStore *iradix.Tree
	readDirMap map[fuseops.InodeID][]fuseutil.Dirent
	isReadOnly bool
	cafs     cafs.Fs
	streamed bool
	withVerifyHash bool
	lruSize        int
	prefetch       int
}
func defaultReadOnlyFS(bundle *core.Bundle) *readOnlyFsInternal {
	return &readOnlyFsInternal{
		fsCommon: fsCommon{
			bundle:     bundle,
			lookupTree: iradix.New(),
			l:          dlogger.MustGetLogger("info"),
		},
		readDirMap:   make(map[fuseops.InodeID][]fuseutil.Dirent),
		fsEntryStore: iradix.New(),
		fsDirStore:   iradix.New(),
	}
}
func asFsEntry(p interface{}) *FsEntry {
	fe := p.(FsEntry)
	return &fe
}
func (fs *readOnlyFsInternal) LookUpInode(ctx context.Context, op *fuseops.LookUpInodeOp) (err error) {
	t0 := fs.opStart(op)
	defer fs.opEnd(t0, op, err)
	lookupKey := formLookupKey(op.Parent, op.Name)
	val, found := fs.lookupTree.Get(lookupKey)
	if found {
		childEntry := asFsEntry(val)
		op.Entry.Attributes = childEntry.attributes
		if fs.isReadOnly {
			op.Entry.AttributesExpiration = time.Now().Add(cacheYearLong)
			op.Entry.EntryExpiration = op.Entry.AttributesExpiration
		}
		op.Entry.Child = childEntry.iNode
		op.Entry.Generation = 1
	} else {
		err = jfuse.ENOENT
		return
	}
	return nil
}
func (fs *readOnlyFsInternal) GetInodeAttributes(
	ctx context.Context,
	op *fuseops.GetInodeAttributesOp) (err error) {
	t0 := fs.opStart(op)
	defer fs.opEnd(t0, op, err)
	key := formKey(op.Inode)
	e, found := fs.fsEntryStore.Get(key)
	if !found {
		err = jfuse.ENOENT
		return
	}
	fe := asFsEntry(e)
	op.AttributesExpiration = time.Now().Add(cacheYearLong)
	op.Attributes = fe.attributes
	return nil
}
func (fs *readOnlyFsInternal) ForgetInode(
	ctx context.Context,
	op *fuseops.ForgetInodeOp) (err error) {
	t0 := fs.opStart(op)
	defer fs.opEnd(t0, op, err)
	return
}
func (fs *readOnlyFsInternal) OpenDir(ctx context.Context, op *fuseops.OpenDirOp) (err error) {
	t0 := fs.opStart(op)
	fs.opEnd(t0, op, err)
	p, found := fs.fsEntryStore.Get(formKey(op.Inode))
	if !found {
		err = jfuse.ENOENT
		return
	}
	fe := asFsEntry(p)
	if !fe.isDir() {
		err = jfuse.ENOENT
		return
	}
	return nil
}
func (fs *readOnlyFsInternal) ReadDir(ctx context.Context, op *fuseops.ReadDirOp) (err error) {
	t0 := fs.opStart(op)
	defer fs.opEnd(t0, op, err)
	offset := int(op.Offset)
	iNode := op.Inode
	children, found := fs.readDirMap[iNode]
	if !found {
		err = jfuse.ENOENT
		return
	}
	if offset > len(children) {
		err = jfuse.ENOENT
		return
	}
	for i := offset; i < len(children); i++ {
		n := fuseutil.WriteDirent(op.Dst[op.BytesRead:], children[i])
		if n == 0 {
			break
		}
		op.BytesRead += n
	}
	return nil
}
func (fs *readOnlyFsInternal) ReleaseDirHandle(
	ctx context.Context,
	op *fuseops.ReleaseDirHandleOp) (err error) {
	t0 := fs.opStart(op)
	defer fs.opEnd(t0, op, err)
	return
}
func (fs *readOnlyFsInternal) OpenFile(
	ctx context.Context,
	op *fuseops.OpenFileOp) (err error) {
	t0 := fs.opStart(op)
	defer fs.opEnd(t0, op, err)
	return
}
func (fs *readOnlyFsInternal) ReadFile(
	ctx context.Context,
	op *fuseops.ReadFileOp) (err error) {
	var n int
	t0 := fs.opStart(op)
	defer func() {
		fs.opEnd(t0, op, err)
		if fs.MetricsEnabled() {
			fs.m.Volume.Files.Inc("read")
			fs.m.Volume.Files.Size(int64(n), "read")
			fs.m.Volume.IO.IORecord(t0, "read")(int64(n), err)
		}
	}()
	p, found := fs.fsEntryStore.Get(formKey(op.Inode))
	if !found {
		err = jfuse.ENOENT
		return
	}
	fe := asFsEntry(p)
	fs.l.Debug("reading file", zap.String("file", fe.fullPath), zap.Uint64("inode", uint64(fe.iNode)))
	n, err = fs.readAtBundle(fe, op.Dst, op.Offset)
	op.BytesRead = n
	return err
}
func (fs *readOnlyFsInternal) ReleaseFileHandle(
	ctx context.Context,
	op *fuseops.ReleaseFileHandleOp) (err error) {
	t0 := fs.opStart(op)
	defer fs.opEnd(t0, op, err)
	return
}
func (fs *readOnlyFsInternal) FlushFile(
	ctx context.Context,
	op *fuseops.FlushFileOp) (err error) {
	return
}
type fsNodeToAdd struct {
	parentINode fuseops.InodeID
	FsEntry     FsEntry
}
type FsEntry struct {
	hash string  
	iNode      fuseops.InodeID          
	attributes fuseops.InodeAttributes  
	fullPath   string
}
func (f FsEntry) isDir() bool {
	return f.hash == ""
}
func newFsEntry(bundleEntry *model.BundleEntry, t time.Time, id fuseops.InodeID, linkCount uint32) *FsEntry {
	var mode os.FileMode = fileReadOnlyMode
	if bundleEntry.Hash == "" {
		mode = dirReadOnlyMode
	}
	return &FsEntry{
		fullPath: bundleEntry.NameWithPath,
		hash:     bundleEntry.Hash,
		iNode:    id,
		attributes: fuseops.InodeAttributes{
			Size:   bundleEntry.Size,
			Nlink:  linkCount,
			Mode:   mode,
			Atime:  t,
			Mtime:  t,
			Ctime:  t,
			Crtime: t,
			Uid:    1020,  
			Gid:    2000,  
		},
	}
}
func newBundleEntry(nameWithPath string) *model.BundleEntry {
	return &model.BundleEntry{
		Hash:         "",  
		NameWithPath: nameWithPath,
		FileMode:     dirReadOnlyMode,
		Size:         2048,  
	}
}
type populateFSTxns struct {
	dirStore     *iradix.Txn
	lookupTree   *iradix.Txn
	fsEntryStore *iradix.Txn
}
func (txns *populateFSTxns) commitToFS(fs *readOnlyFsInternal) {
	fs.fsEntryStore = txns.fsEntryStore.Commit()
	fs.lookupTree = txns.lookupTree.Commit()
	fs.fsDirStore = txns.dirStore.Commit()
}
func newFSTxns(fs *readOnlyFsInternal) *populateFSTxns {
	return &populateFSTxns{
		dirStore:     fs.fsDirStore.Txn(),
		lookupTree:   fs.lookupTree.Txn(),
		fsEntryStore: fs.fsEntryStore.Txn(),
	}
}
type populate struct {
	fs          *readOnlyFsInternal
	bundle      *core.Bundle
	txns        *populateFSTxns
	nodesToAdd  []fsNodeToAdd
	iNode       *fuseops.InodeID
	bundleEntry model.BundleEntry
}
func (p *populate) WithINode(i *fuseops.InodeID) *populate {
	p.iNode = i
	return p
}
func (p *populate) WithEntry(entry model.BundleEntry) *populate {
	p.bundleEntry = entry
	return p
}
func (p *populate) WithNodesFromEntry() *populate {
	next := func(i *fuseops.InodeID) fuseops.InodeID {
		*i++
		return *i
	}
	be := p.bundleEntry
	FsEntry := newFsEntry(
		&be,
		p.bundle.BundleDescriptor.Timestamp,
		next(p.iNode),
		fileLinkCount,
	)
	nameWithPath := be.NameWithPath
	for {
		parentPath := path.Dir(nameWithPath)
		logger := p.fs.l.With(zap.String("parentPath", parentPath))
		logger.Debug("Processing parent", zap.String("fullPath", be.NameWithPath))
		if parentPath == "" || parentPath == "." || parentPath == "/" {
			p.nodesToAdd = append(p.nodesToAdd, fsNodeToAdd{
				parentINode: fuseops.RootInodeID,
				FsEntry:     *FsEntry,
			})
			if len(p.nodesToAdd) > 1 {
				p.nodesToAdd[len(p.nodesToAdd)-2].parentINode = p.nodesToAdd[len(p.nodesToAdd)-1].FsEntry.iNode
			}
			break
		}
		p.nodesToAdd = append(p.nodesToAdd, fsNodeToAdd{
			parentINode: 0,  
			FsEntry:     *FsEntry,
		})
		if len(p.nodesToAdd) > 1 {
			p.nodesToAdd[len(p.nodesToAdd)-2].parentINode = p.nodesToAdd[len(p.nodesToAdd)-1].FsEntry.iNode
		}
		parent, found := p.txns.dirStore.Get([]byte(parentPath))
		if !found {
			logger.Debug("parentPath not found")
			FsEntry = newFsEntry(
				newBundleEntry(parentPath),
				p.bundle.BundleDescriptor.Timestamp,
				next(p.iNode),
				dirLinkCount,
			)
			nameWithPath = parentPath
			continue
		} else {
			logger.Debug("parentPath found")
			parentDirEntry := asFsEntry(parent)
			if len(p.nodesToAdd) >= 1 {
				p.nodesToAdd[len(p.nodesToAdd)-1].parentINode = parentDirEntry.iNode
			}
		}
		logger.Debug("last node",
			zap.String("path", p.nodesToAdd[len(p.nodesToAdd)-1].FsEntry.fullPath),
			zap.Uint64("childInode", uint64(p.nodesToAdd[len(p.nodesToAdd)-1].FsEntry.iNode)),
			zap.Uint64("parentInode", uint64(p.nodesToAdd[len(p.nodesToAdd)-1].parentINode)))
		break
	}
	p.fs.l.Debug("Nodes added", zap.Int("count", len(p.nodesToAdd)))
	return p
}