github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/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 sfs *simplefs.SimpleFS
    55  		sfs, shutdownSimpleFS = simplefs.NewSimpleFS(
    56  			libkbfsCtx, config)
    57  		config.AddResetForLoginTarget(sfs)
    58  		return keybase1.SimpleFSProtocol(sfs), nil
    59  	}
    60  	// Hook git implementation in.
    61  	shutdownGit := func() {}
    62  	createGitHandler := func(
    63  		libkbfsCtx libkbfs.Context, config libkbfs.Config) (rpc.Protocol, error) {
    64  		var handler keybase1.KBFSGitInterface
    65  		handler, shutdownGit = libgit.NewRPCHandlerWithCtx(
    66  			libkbfsCtx, config, &options.KbfsParams)
    67  		return keybase1.KBFSGitProtocol(handler), nil
    68  	}
    69  	defer func() {
    70  		err := shutdownSimpleFS(context.Background())
    71  		if err != nil {
    72  			fmt.Fprintf(os.Stderr, "Couldn't shut down SimpleFS: %+v\n", err)
    73  		}
    74  		shutdownGit()
    75  	}()
    76  
    77  	// Patch the kbfsParams to inject two additional protocols.
    78  	options.KbfsParams.AdditionalProtocolCreators = []libkbfs.AdditionalProtocolCreator{
    79  		createSimpleFS, createGitHandler,
    80  	}
    81  
    82  	log, err := libkbfs.InitLog(options.KbfsParams, kbCtx)
    83  	if err != nil {
    84  		return libfs.InitError(err.Error())
    85  	}
    86  
    87  	mi := libfs.NewMountInterrupter(log)
    88  	ctx := context.Background()
    89  	config, err := libkbfs.Init(
    90  		ctx, kbCtx, options.KbfsParams, nil, mi.Done, log)
    91  	if err != nil {
    92  		return libfs.InitError(err.Error())
    93  	}
    94  
    95  	defer libkbfs.Shutdown()
    96  
    97  	libfs.AddRootWrapper(config)
    98  
    99  	if options.RuntimeDir != "" {
   100  		err := os.MkdirAll(options.RuntimeDir, libkb.PermDir)
   101  		if err != nil {
   102  			return libfs.InitError(err.Error())
   103  		}
   104  		info := libkb.NewServiceInfo(libkb.Version, libkbfs.PrereleaseBuild, options.Label, os.Getpid())
   105  		err = info.WriteFile(path.Join(options.RuntimeDir, "kbfs.info"), log)
   106  		if err != nil {
   107  			return libfs.InitError(err.Error())
   108  		}
   109  	}
   110  
   111  	if options.KbfsParams.Debug {
   112  		// Turn on debugging.  TODO: allow a proper log file and
   113  		// style to be specified.
   114  		log.Configure("", true, "")
   115  	}
   116  
   117  	ctx, cancel := context.WithCancel(ctx)
   118  	defer cancel()
   119  
   120  	if options.MountPoint == "" {
   121  		// The mounter will detect this case and pick up the path from DokanConfig
   122  		options.MountPoint, err = config.KeybaseService().EstablishMountDir(ctx)
   123  		if err != nil {
   124  			return libfs.InitError(err.Error())
   125  		}
   126  		log.CInfof(ctx, "Got mount dir from service: -%s-", options.MountPoint)
   127  	}
   128  
   129  	// There is misleading documentation that causes people to set their mount point to
   130  	// 'K' instead of 'K:'. Fix this here, rather than correct all the users with problems.
   131  	if len(options.MountPoint) == 1 {
   132  		options.MountPoint += ":"
   133  		log.CInfof(ctx, "Invalid single letter mount point, patching to %q", options.MountPoint)
   134  	}
   135  
   136  	options.DokanConfig.Path = options.MountPoint
   137  
   138  	if !options.SkipMount && !strings.EqualFold(options.MountPoint, "none") {
   139  
   140  		fs, err := NewFS(ctx, config, log)
   141  		if err != nil {
   142  			return libfs.InitError(err.Error())
   143  		}
   144  		options.DokanConfig.FileSystem = fs
   145  
   146  		if newFolderNameErr != nil {
   147  			log.CWarningf(ctx, "Error guessing new folder name: %v", newFolderNameErr)
   148  		}
   149  		log.CDebugf(ctx, "New folder name guess: %q %q", newFolderName, newFolderAltName)
   150  
   151  		st, err := os.Lstat(options.MountPoint)
   152  		log.CDebugf(ctx, "Before mount check (should fail) Lstat(%q): %v,%v", options.MountPoint, st, err)
   153  
   154  		err = startMounting(options, log, mi)
   155  		if err != nil {
   156  			logDokanInfo(ctx, log)
   157  			// Abort on error if we were force mounting, otherwise continue.
   158  			if options.ForceMount {
   159  				// Cleanup when exiting in case the mount got dirty.
   160  				err = mi.Done()
   161  				if err != nil {
   162  					log.CErrorf(ctx, "Couldn't mount: %v", err)
   163  				}
   164  				return libfs.MountError(err.Error())
   165  			}
   166  			log.CErrorf(ctx, "Running KBFS without a filesystem mount due to: %v", err)
   167  		}
   168  	}
   169  
   170  	log.CDebugf(ctx, "Entering mount wait")
   171  	mi.Wait()
   172  	log.CDebugf(ctx, "Filesystem unmounted - mount wait returned - exiting")
   173  	return nil
   174  }