func (d *RemoteOutputServiceDirectory) FUSEReadDir() ([]fuse.DirEntry, fuse.Status) {
	d.lock.Lock()
	defer d.lock.Unlock()
	entries := make([]fuse.DirEntry, 0, len(d.outputBaseIDs))
	for name, outputPathState := range d.outputBaseIDs {
		entries = append(entries, fuse.DirEntry{
			Mode: fuse.S_IFDIR,
			Ino:  outputPathState.rootDirectoryInodeNumber,
			Name: name.String(),
		})
	}
	return entries, fuse.OK
}
FUSEReadDir returns a list of all the output paths managed by this
Remote Output Service.
package fuse
import (
	"context"
	"sync"
	"syscall"
	"github.com/buildbarn/bb-clientd/pkg/cas"
	re_fuse "github.com/buildbarn/bb-remote-execution/pkg/filesystem/fuse"
	"github.com/buildbarn/bb-remote-execution/pkg/proto/remoteoutputservice"
	"github.com/buildbarn/bb-storage/pkg/blobstore"
	"github.com/buildbarn/bb-storage/pkg/digest"
	"github.com/buildbarn/bb-storage/pkg/filesystem/path"
	"github.com/buildbarn/bb-storage/pkg/random"
	"github.com/buildbarn/bb-storage/pkg/util"
	"github.com/hanwen/go-fuse/v2/fuse"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
)
type buildState struct {
	id                 string
	digestFunction     digest.Function
	scopeWalkerFactory *path.VirtualRootScopeWalkerFactory
}
type outputPathState struct {
	buildState               *buildState
	rootDirectory            OutputPath
	rootDirectoryInodeNumber uint64
	casFileFactory           re_fuse.CASFileFactory
}
type RemoteOutputServiceDirectory struct {
	readOnlyDirectory
	inodeNumber                       uint64
	inodeNumberGenerator              random.SingleThreadedGenerator
	entryNotifier                     re_fuse.EntryNotifier
	outputPathFactory                 OutputPathFactory
	bareContentAddressableStorage     blobstore.BlobAccess
	retryingContentAddressableStorage blobstore.BlobAccess
	indexedTreeFetcher                cas.IndexedTreeFetcher
	lock          sync.Mutex
	outputBaseIDs map[path.Component]*outputPathState
	buildIDs      map[string]*outputPathState
}
var (
	_ re_fuse.Directory                             = &RemoteOutputServiceDirectory{}
	_ remoteoutputservice.RemoteOutputServiceServer = &RemoteOutputServiceDirectory{}
)
func NewRemoteOutputServiceDirectory(inodeNumber uint64, inodeNumberGenerator random.SingleThreadedGenerator, entryNotifier re_fuse.EntryNotifier, outputPathFactory OutputPathFactory, bareContentAddressableStorage, retryingContentAddressableStorage blobstore.BlobAccess, indexedTreeFetcher cas.IndexedTreeFetcher) *RemoteOutputServiceDirectory {
	return &RemoteOutputServiceDirectory{
		inodeNumber:                       inodeNumber,
		inodeNumberGenerator:              inodeNumberGenerator,
		entryNotifier:                     entryNotifier,
		outputPathFactory:                 outputPathFactory,
		bareContentAddressableStorage:     bareContentAddressableStorage,
		retryingContentAddressableStorage: retryingContentAddressableStorage,
		indexedTreeFetcher:                indexedTreeFetcher,
		outputBaseIDs: map[path.Component]*outputPathState{},
		buildIDs:      map[string]*outputPathState{},
	}
}
func (d *RemoteOutputServiceDirectory) Clean(ctx context.Context, request *remoteoutputservice.CleanRequest) (*emptypb.Empty, error) {
	outputBaseID, ok := path.NewComponent(request.OutputBaseId)
	if !ok {
		return nil, status.Error(codes.InvalidArgument, "Output base ID is not a valid filename")
	}
	d.lock.Lock()
	outputPathState, ok := d.outputBaseIDs[outputBaseID]
	d.lock.Unlock()
	if ok {
		if err := outputPathState.rootDirectory.RemoveAllChildren(true); err != nil {
			return nil, err
		}
		d.lock.Lock()
		if outputPathState == d.outputBaseIDs[outputBaseID] {
			delete(d.outputBaseIDs, outputBaseID)
			if buildState := outputPathState.buildState; buildState != nil {
				delete(d.buildIDs, buildState.id)
				outputPathState.buildState = nil
			}
		}
		d.lock.Unlock()
		d.entryNotifier(d.inodeNumber, outputBaseID)
	} else if err := d.outputPathFactory.Clean(outputBaseID); err != nil {
		return nil, err
	}
	return &emptypb.Empty{}, nil
}
func (d *RemoteOutputServiceDirectory) findMissingAndRemove(ctx context.Context, queue map[digest.Digest][]func() error) error {
	set := digest.NewSetBuilder()
	for digest := range queue {
		set.Add(digest)
	}
	missing, err := d.bareContentAddressableStorage.FindMissing(ctx, set.Build())
	if err != nil {
		return util.StatusWrap(err, "Failed to find missing blobs")
	}
	for _, digest := range missing.Items() {
		for _, removeFunc := range queue[digest] {
			if err := removeFunc(); err != nil {
				return util.StatusWrapf(err, "Failed to remove file with digest %#v", digest.String())
			}
		}
	}
	return nil
}
func (d *RemoteOutputServiceDirectory) filterMissingChildren(ctx context.Context, rootDirectory re_fuse.PrepopulatedDirectory, digestFunction digest.Function) error {
	queue := map[digest.Digest][]func() error{}
	var savedErr error
	if err := rootDirectory.FilterChildren(func(node re_fuse.InitialNode, removeFunc re_fuse.ChildRemover) bool {
		var digests digest.Set
		if node.Leaf != nil {
			digests = node.Leaf.GetContainingDigests()
		} else if digests, savedErr = node.Directory.GetContainingDigests(ctx); savedErr != nil {
			if status.Code(savedErr) == codes.NotFound {
				savedErr = nil
				if err := removeFunc(); err != nil {
					savedErr = util.StatusWrap(err, "Failed to remove non-existent directory")
					return false
				}
				return true
			}
			return false
		}
		for _, blobDigest := range digests.Items() {
			if !blobDigest.UsesDigestFunction(digestFunction) {
				if err := removeFunc(); err != nil {
					savedErr = util.StatusWrapf(err, "Failed to remove file with different instance name or digest function with digest %#v", blobDigest.String())
					return false
				}
				return true
			}
		}
		for _, blobDigest := range digests.Items() {
			if len(queue) >= blobstore.RecommendedFindMissingDigestsCount {
				savedErr = d.findMissingAndRemove(ctx, queue)
				if savedErr != nil {
					return false
				}
				queue = map[digest.Digest][]func() error{}
			}
			queue[blobDigest] = append(queue[blobDigest], removeFunc)
		}
		return true
	}); err != nil {
		return err
	}
	if savedErr != nil {
		return savedErr
	}
	if len(queue) > 0 {
		return d.findMissingAndRemove(ctx, queue)
	}
	return nil
}
func (d *RemoteOutputServiceDirectory) StartBuild(ctx context.Context, request *remoteoutputservice.StartBuildRequest) (*remoteoutputservice.StartBuildResponse, error) {
	outputPath, scopeWalker := path.EmptyBuilder.Join(path.NewAbsoluteScopeWalker(path.VoidComponentWalker))
	if err := path.Resolve(request.OutputPathPrefix, scopeWalker); err != nil {
		return nil, util.StatusWrap(err, "Failed to resolve output path prefix")
	}
	outputPathSuffix, scopeWalker := path.EmptyBuilder.Join(path.VoidScopeWalker)
	outputPath, scopeWalker = outputPath.Join(scopeWalker)
	outputBaseID, ok := path.NewComponent(request.OutputBaseId)
	if !ok {
		return nil, status.Error(codes.InvalidArgument, "Output base ID is not a valid filename")
	}
	componentWalker, err := scopeWalker.OnScope(false)
	if err != nil {
		return nil, util.StatusWrap(err, "Failed to resolve output path")
	}
	if _, err := componentWalker.OnTerminal(outputBaseID); err != nil {
		return nil, util.StatusWrap(err, "Failed to resolve output path")
	}
	scopeWalkerFactory, err := path.NewVirtualRootScopeWalkerFactory(outputPath.String(), request.OutputPathAliases)
	if err != nil {
		return nil, err
	}
	instanceName, err := digest.NewInstanceName(request.InstanceName)
	if err != nil {
		return nil, util.StatusWrapf(err, "Failed to parse instance name %#v", request.InstanceName)
	}
	digestFunction, err := instanceName.GetDigestFunction(request.DigestFunction)
	if err != nil {
		return nil, err
	}
	d.lock.Lock()
	state, ok := d.buildIDs[request.BuildId]
	if !ok {
		state, ok = d.outputBaseIDs[outputBaseID]
		if ok {
			if buildState := state.buildState; buildState != nil {
				delete(d.buildIDs, buildState.id)
				state.buildState = nil
			}
		} else {
			errorLogger := util.DefaultErrorLogger
			inodeNumber := d.inodeNumberGenerator.Uint64()
			casFileFactory := re_fuse.NewBlobAccessCASFileFactory(
				context.Background(),
				d.retryingContentAddressableStorage,
				errorLogger)
			state = &outputPathState{
				rootDirectory:            d.outputPathFactory.StartInitialBuild(outputBaseID, casFileFactory, instanceName, errorLogger, inodeNumber),
				rootDirectoryInodeNumber: inodeNumber,
				casFileFactory:           casFileFactory,
			}
			d.outputBaseIDs[outputBaseID] = state
		}
		state.buildState = &buildState{
			id:                 request.BuildId,
			digestFunction:     digestFunction,
			scopeWalkerFactory: scopeWalkerFactory,
		}
		d.buildIDs[request.BuildId] = state
	}
	d.lock.Unlock()
	if err := d.filterMissingChildren(ctx, state.rootDirectory, digestFunction); err != nil {
		return nil, util.StatusWrap(err, "Failed to filter contents of the output path")
	}
	return &remoteoutputservice.StartBuildResponse{
		OutputPathSuffix: outputPathSuffix.String(),
	}, nil
}
func (d *RemoteOutputServiceDirectory) getOutputPathAndBuildState(buildID string) (*outputPathState, *buildState, error) {
	d.lock.Lock()
	defer d.lock.Unlock()
	outputPathState, ok := d.buildIDs[buildID]
	if !ok {
		return nil, nil, status.Error(codes.FailedPrecondition, "Build ID is not associated with any running build")
	}
	return outputPathState, outputPathState.buildState, nil
}
type directoryCreatingComponentWalker struct {
	stack []re_fuse.PrepopulatedDirectory
}
func (cw *directoryCreatingComponentWalker) OnDirectory(name path.Component) (path.GotDirectoryOrSymlink, error) {
	d := cw.stack[len(cw.stack)-1]
	child, err := d.CreateAndEnterPrepopulatedDirectory(name)
	if err != nil {
		return nil, err
	}
	cw.stack = append(cw.stack, child)
	return path.GotDirectory{
		Child:        cw,
		IsReversible: true,
	}, nil
}
func (cw *directoryCreatingComponentWalker) OnTerminal(name path.Component) (*path.GotSymlink, error) {
	return path.OnTerminalViaOnDirectory(cw, name)
}
func (cw *directoryCreatingComponentWalker) OnUp() (path.ComponentWalker, error) {
	if len(cw.stack) == 1 {
		return nil, status.Error(codes.InvalidArgument, "Path resolves to a location outside the output path")
	}
	cw.stack = cw.stack[:len(cw.stack)-1]
	return cw, nil
}
func (cw *directoryCreatingComponentWalker) createChild(outputPath string, initialNode re_fuse.InitialNode) error {
	outputParentCreator := parentDirectoryCreatingComponentWalker{
		stack: append([]re_fuse.PrepopulatedDirectory(nil), cw.stack...),
	}
	if err := path.Resolve(outputPath, path.NewRelativeScopeWalker(&outputParentCreator)); err != nil {
		return util.StatusWrap(err, "Failed to resolve path")
	}
	name := outputParentCreator.name
	if name == nil {
		return status.Errorf(codes.InvalidArgument, "Path resolves to a directory")
	}
	return outputParentCreator.stack[len(outputParentCreator.stack)-1].CreateChildren(
		map[path.Component]re_fuse.InitialNode{
			*name: initialNode,
		},
		true)
}
type parentDirectoryCreatingComponentWalker struct {
	stack []re_fuse.PrepopulatedDirectory
	name  *path.Component
}
func (cw *parentDirectoryCreatingComponentWalker) OnDirectory(name path.Component) (path.GotDirectoryOrSymlink, error) {
	d := cw.stack[len(cw.stack)-1]
	child, err := d.CreateAndEnterPrepopulatedDirectory(name)
	if err != nil {
		return nil, err
	}
	cw.stack = append(cw.stack, child)
	return path.GotDirectory{
		Child:        cw,
		IsReversible: true,
	}, nil
}
func (cw *parentDirectoryCreatingComponentWalker) OnTerminal(name path.Component) (*path.GotSymlink, error) {
	cw.name = &name
	return nil, nil
}
func (cw *parentDirectoryCreatingComponentWalker) OnUp() (path.ComponentWalker, error) {
	if len(cw.stack) == 1 {
		return nil, status.Error(codes.InvalidArgument, "Path resolves to a location outside the output path")
	}
	cw.stack = cw.stack[:len(cw.stack)-1]
	return cw, nil
}
func (d *RemoteOutputServiceDirectory) BatchCreate(ctx context.Context, request *remoteoutputservice.BatchCreateRequest) (*emptypb.Empty, error) {
	outputPathState, buildState, err := d.getOutputPathAndBuildState(request.BuildId)
	if err != nil {
		return nil, err
	}
	prefixCreator := directoryCreatingComponentWalker{
		stack: []re_fuse.PrepopulatedDirectory{outputPathState.rootDirectory},
	}
	if err := path.Resolve(request.PathPrefix, path.NewRelativeScopeWalker(&prefixCreator)); err != nil {
		return nil, util.StatusWrap(err, "Failed to create path prefix directory")
	}
	if request.CleanPathPrefix {
		if err := prefixCreator.stack[len(prefixCreator.stack)-1].RemoveAllChildren(false); err != nil {
			return nil, util.StatusWrap(err, "Failed to clean path prefix directory")
		}
	}
	instanceName := buildState.digestFunction.GetInstanceName()
	for _, entry := range request.Files {
		childDigest, err := instanceName.NewDigestFromProto(entry.Digest)
		if err != nil {
			return nil, util.StatusWrapf(err, "Invalid digest for file %#v", entry.Path)
		}
		var out fuse.Attr
		if err := prefixCreator.createChild(entry.Path, re_fuse.InitialNode{
			Leaf: outputPathState.casFileFactory.LookupFile(childDigest, entry.IsExecutable, &out),
		}); err != nil {
			return nil, util.StatusWrapf(err, "Failed to create file %#v", entry.Path)
		}
	}
	for _, entry := range request.Directories {
		childDigest, err := instanceName.NewDigestFromProto(entry.TreeDigest)
		if err != nil {
			return nil, util.StatusWrapf(err, "Invalid digest for directory %#v", entry.Path)
		}
		if err := prefixCreator.createChild(entry.Path, re_fuse.InitialNode{
			Directory: re_fuse.NewCASInitialContentsFetcher(
				context.Background(),
				cas.NewTreeDirectoryWalker(d.indexedTreeFetcher, childDigest),
				outputPathState.casFileFactory,
				instanceName),
		}); err != nil {
			return nil, util.StatusWrapf(err, "Failed to create directory %#v", entry.Path)
		}
	}
	for _, entry := range request.Symlinks {
		if err := prefixCreator.createChild(entry.Path, re_fuse.InitialNode{
			Leaf: re_fuse.NewSymlink(entry.Target),
		}); err != nil {
			return nil, util.StatusWrapf(err, "Failed to create symbolic link %#v", entry.Path)
		}
	}
	return &emptypb.Empty{}, nil
}
type statWalker struct {
	followSymlinks bool
	digestFunction *digest.Function
	stack      []re_fuse.PrepopulatedDirectory
	fileStatus *remoteoutputservice.FileStatus
}
func (cw *statWalker) OnScope(absolute bool) (path.ComponentWalker, error) {
	if absolute {
		cw.stack = cw.stack[:1]
	}
	cw.fileStatus = &remoteoutputservice.FileStatus{
		FileType: &remoteoutputservice.FileStatus_Directory{
			Directory: &emptypb.Empty{},
		},
	}
	return cw, nil
}
func (cw *statWalker) OnDirectory(name path.Component) (path.GotDirectoryOrSymlink, error) {
	d := cw.stack[len(cw.stack)-1]
	directory, leaf, err := d.LookupChild(name)
	if err != nil {
		return nil, err
	}
	if directory != nil {
		cw.stack = append(cw.stack, directory)
		return path.GotDirectory{
			Child:        cw,
			IsReversible: true,
		}, nil
	}
	target, err := leaf.Readlink()
	if err == syscall.EINVAL {
		return nil, syscall.ENOTDIR
	} else if err != nil {
		return nil, err
	}
	cw.fileStatus = nil
	return path.GotSymlink{
		Parent: cw,
		Target: target,
	}, nil
}
func (cw *statWalker) OnTerminal(name path.Component) (*path.GotSymlink, error) {
	d := cw.stack[len(cw.stack)-1]
	directory, leaf, err := d.LookupChild(name)
	if err != nil {
		return nil, err
	}
	if directory != nil {
		return nil, nil
	}
	if cw.followSymlinks {
		target, err := leaf.Readlink()
		if err == nil {
			cw.fileStatus = nil
			return &path.GotSymlink{
				Parent: cw,
				Target: target,
			}, nil
		}
		if err != syscall.EINVAL {
			return nil, err
		}
	}
	fileStatus, err := leaf.GetOutputServiceFileStatus(cw.digestFunction)
	if err != nil {
		return nil, err
	}
	cw.fileStatus = fileStatus
	return nil, nil
}
func (cw *statWalker) OnUp() (path.ComponentWalker, error) {
	if len(cw.stack) == 1 {
		cw.fileStatus = nil
		return path.VoidComponentWalker, nil
	}
	cw.stack = cw.stack[:len(cw.stack)-1]
	return cw, nil
}
func (d *RemoteOutputServiceDirectory) BatchStat(ctx context.Context, request *remoteoutputservice.BatchStatRequest) (*remoteoutputservice.BatchStatResponse, error) {
	outputPathState, buildState, err := d.getOutputPathAndBuildState(request.BuildId)
	if err != nil {
		return nil, err
	}
	response := remoteoutputservice.BatchStatResponse{
		Responses: make([]*remoteoutputservice.StatResponse, 0, len(request.Paths)),
	}
	for _, statPath := range request.Paths {
		statWalker := statWalker{
			followSymlinks: request.FollowSymlinks,
			stack:          []re_fuse.PrepopulatedDirectory{outputPathState.rootDirectory},
		}
		if request.IncludeFileDigest {
			statWalker.digestFunction = &buildState.digestFunction
		}
		resolvedPath, scopeWalker := path.EmptyBuilder.Join(
			buildState.scopeWalkerFactory.New(path.NewLoopDetectingScopeWalker(&statWalker)))
		if err := path.Resolve(statPath, scopeWalker); err == syscall.ENOENT {
			response.Responses = append(response.Responses, &remoteoutputservice.StatResponse{})
		} else if err != nil {
			return nil, util.StatusWrapf(err, "Failed to resolve path %#v beyond %#v", statPath, resolvedPath.String())
		} else if statWalker.fileStatus == nil {
			response.Responses = append(response.Responses, &remoteoutputservice.StatResponse{
				FileStatus: &remoteoutputservice.FileStatus{
					FileType: &remoteoutputservice.FileStatus_External_{
						External: &remoteoutputservice.FileStatus_External{
							NextPath: resolvedPath.String(),
						},
					},
				},
			})
		} else {
			response.Responses = append(response.Responses, &remoteoutputservice.StatResponse{
				FileStatus: statWalker.fileStatus,
			})
		}
	}
	return &response, nil
}
func (d *RemoteOutputServiceDirectory) FinalizeBuild(ctx context.Context, request *remoteoutputservice.FinalizeBuildRequest) (*emptypb.Empty, error) {
	d.lock.Lock()
	defer d.lock.Unlock()
	if outputPathState, ok := d.buildIDs[request.BuildId]; ok {
		outputPathState.rootDirectory.FinalizeBuild()
		buildState := outputPathState.buildState
		delete(d.buildIDs, buildState.id)
		outputPathState.buildState = nil
	}
	return &emptypb.Empty{}, nil
}
func (d *RemoteOutputServiceDirectory) FUSEAccess(mask uint32) fuse.Status {
	if mask&^(fuse.R_OK|fuse.X_OK) != 0 {
		return fuse.EACCES
	}
	return fuse.OK
}
func (d *RemoteOutputServiceDirectory) FUSEGetAttr(out *fuse.Attr) {
	out.Ino = d.inodeNumber
	out.Mode = fuse.S_IFDIR | 0o555
	d.lock.Lock()
	out.Nlink = re_fuse.EmptyDirectoryLinkCount + uint32(len(d.outputBaseIDs))
	d.lock.Unlock()
}
func (d *RemoteOutputServiceDirectory) FUSELookup(name path.Component, out *fuse.Attr) (re_fuse.Directory, re_fuse.Leaf, fuse.Status) {
	d.lock.Lock()
	outputPathState, ok := d.outputBaseIDs[name]
	d.lock.Unlock()
	if !ok {
		return nil, nil, fuse.ENOENT
	}
	outputPathState.rootDirectory.FUSEGetAttr(out)
	return outputPathState.rootDirectory, nil, fuse.OK
}