github.com/keybase/client/go@v0.0.0-20240424154521-52f30ea26cb1/kbfs/libdokan/start.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libdokan
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"path"
    11  	"strings"
    12  
    13  	"github.com/keybase/client/go/kbfs/dokan"
    14  	"github.com/keybase/client/go/kbfs/libfs"
    15  	"github.com/keybase/client/go/kbfs/libgit"
    16  	"github.com/keybase/client/go/kbfs/libkbfs"
    17  	"github.com/keybase/client/go/kbfs/simplefs"
    18  	"github.com/keybase/client/go/libkb"
    19  	"github.com/keybase/client/go/logger"
    20  	"github.com/keybase/client/go/protocol/keybase1"
    21  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    22  	"golang.org/x/net/context"
    23  )
    24  
    25  // StartOptions are options for starting up
    26  type StartOptions struct {
    27  	KbfsParams  libkbfs.InitParams
    28  	RuntimeDir  string
    29  	Label       string
    30  	DokanConfig dokan.Config
    31  	ForceMount  bool
    32  	SkipMount   bool
    33  	MountPoint  string
    34  }
    35  
    36  func startMounting(options StartOptions,
    37  	log logger.Logger, mi *libfs.MountInterrupter) error {
    38  	log.Info("Starting mount with options: %#v", options)
    39  	var mounter = &mounter{options: options, log: log}
    40  	err := mi.MountAndSetUnmount(mounter)
    41  	if err != nil {
    42  		return err
    43  	}
    44  	log.Info("Mounting the filesystem was a success!")
    45  	return mounter.c.BlockTillDone()
    46  }
    47  
    48  // Start the filesystem
    49  func Start(options StartOptions, kbCtx libkbfs.Context) *libfs.Error {
    50  	// Hook simplefs implementation in.
    51  	shutdownSimpleFS := func(_ context.Context) error { return nil }
    52  	createSimpleFS := func(
    53  		libkbfsCtx libkbfs.Context, config libkbfs.Config) (rpc.Protocol, error) {
    54  		var simplefsIface keybase1.SimpleFSInterface
    55  		simplefsIface, shutdownSimpleFS = simplefs.NewSimpleFS(
    56  			libkbfsCtx, config)
    57  		return keybase1.SimpleFSProtocol(simplefsIface), nil
    58  	}
    59  	// Hook git implementation in.
    60  	shutdownGit := func() {}
    61  	createGitHandler := func(
    62  		libkbfsCtx libkbfs.Context, config libkbfs.Config) (rpc.Protocol, error) {
    63  		var handler keybase1.KBFSGitInterface
    64  		handler, shutdownGit = libgit.NewRPCHandlerWithCtx(
    65  			libkbfsCtx, config, &options.KbfsParams)
    66  		return keybase1.KBFSGitProtocol(handler), nil
    67  	}
    68  	defer func() {
    69  		err := shutdownSimpleFS(context.Background())
    70  		if err != nil {
    71  			fmt.Fprintf(os.Stderr, "Couldn't shut down SimpleFS: %+v\n", err)
    72  		}
    73  		shutdownGit()
    74  	}()
    75  
    76  	// Patch the kbfsParams to inject two additional protocols.
    77  	options.KbfsParams.AdditionalProtocolCreators = []libkbfs.AdditionalProtocolCreator{
    78  		createSimpleFS, createGitHandler,
    79  	}
    80  
    81  	log, err := libkbfs.InitLog(options.KbfsParams, kbCtx)
    82  	if err != nil {
    83  		return libfs.InitError(err.Error())
    84  	}
    85  
    86  	mi := libfs.NewMountInterrupter(log)
    87  	ctx := context.Background()
    88  	config, err := libkbfs.Init(
    89  		ctx, kbCtx, options.KbfsParams, nil, mi.Done, log)
    90  	if err != nil {
    91  		return libfs.InitError(err.Error())
    92  	}
    93  
    94  	defer libkbfs.Shutdown()
    95  
    96  	libfs.AddRootWrapper(config)
    97  
    98  	if options.RuntimeDir != "" {
    99  		err := os.MkdirAll(options.RuntimeDir, libkb.PermDir)
   100  		if err != nil {
   101  			return libfs.InitError(err.Error())
   102  		}
   103  		info := libkb.NewServiceInfo(libkb.Version, libkbfs.PrereleaseBuild, options.Label, os.Getpid())
   104  		err = info.WriteFile(path.Join(options.RuntimeDir, "kbfs.info"), log)
   105  		if err != nil {
   106  			return libfs.InitError(err.Error())
   107  		}
   108  	}
   109  
   110  	if options.KbfsParams.Debug {
   111  		// Turn on debugging.  TODO: allow a proper log file and
   112  		// style to be specified.
   113  		log.Configure("", true, "")
   114  	}
   115  
   116  	ctx, cancel := context.WithCancel(ctx)
   117  	defer cancel()
   118  
   119  	if options.MountPoint == "" {
   120  		// The mounter will detect this case and pick up the path from DokanConfig
   121  		options.MountPoint, err = config.KeybaseService().EstablishMountDir(ctx)
   122  		if err != nil {
   123  			return libfs.InitError(err.Error())
   124  		}
   125  		log.CInfof(ctx, "Got mount dir from service: -%s-", options.MountPoint)
   126  	}
   127  
   128  	// There is misleading documentation that causes people to set their mount point to
   129  	// 'K' instead of 'K:'. Fix this here, rather than correct all the users with problems.
   130  	if len(options.MountPoint) == 1 {
   131  		options.MountPoint += ":"
   132  		log.CInfof(ctx, "Invalid single letter mount point, patching to %q", options.MountPoint)
   133  	}
   134  
   135  	options.DokanConfig.Path = options.MountPoint
   136  
   137  	if !options.SkipMount && !strings.EqualFold(options.MountPoint, "none") {
   138  
   139  		fs, err := NewFS(ctx, config, log)
   140  		if err != nil {
   141  			return libfs.InitError(err.Error())
   142  		}
   143  		options.DokanConfig.FileSystem = fs
   144  
   145  		if newFolderNameErr != nil {
   146  			log.CWarningf(ctx, "Error guessing new folder name: %v", newFolderNameErr)
   147  		}
   148  		log.CDebugf(ctx, "New folder name guess: %q %q", newFolderName, newFolderAltName)
   149  
   150  		st, err := os.Lstat(options.MountPoint)
   151  		log.CDebugf(ctx, "Before mount check (should fail) Lstat(%q): %v,%v", options.MountPoint, st, err)
   152  
   153  		err = startMounting(options, log, mi)
   154  		if err != nil {
   155  			logDokanInfo(ctx, log)
   156  			// Abort on error if we were force mounting, otherwise continue.
   157  			if options.ForceMount {
   158  				// Cleanup when exiting in case the mount got dirty.
   159  				err = mi.Done()
   160  				if err != nil {
   161  					log.CErrorf(ctx, "Couldn't mount: %v", err)
   162  				}
   163  				return libfs.MountError(err.Error())
   164  			}
   165  			log.CErrorf(ctx, "Running KBFS without a filesystem mount due to: %v", err)
   166  		}
   167  	}
   168  
   169  	log.CDebugf(ctx, "Entering mount wait")
   170  	mi.Wait()
   171  	log.CDebugf(ctx, "Filesystem unmounted - mount wait returned - exiting")
   172  	return nil
   173  }