github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libfuse/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 //go:build !windows 6 // +build !windows 7 8 package libfuse 9 10 import ( 11 "fmt" 12 "os" 13 "path" 14 15 "bazil.org/fuse" 16 "github.com/keybase/client/go/kbfs/libfs" 17 "github.com/keybase/client/go/kbfs/libgit" 18 "github.com/keybase/client/go/kbfs/libkbfs" 19 "github.com/keybase/client/go/kbfs/simplefs" 20 "github.com/keybase/client/go/libkb" 21 "github.com/keybase/client/go/logger" 22 "github.com/keybase/client/go/protocol/keybase1" 23 "github.com/keybase/client/go/systemd" 24 "github.com/keybase/go-framed-msgpack-rpc/rpc" 25 "golang.org/x/net/context" 26 ) 27 28 // StartOptions are options for starting up 29 type StartOptions struct { 30 KbfsParams libkbfs.InitParams 31 PlatformParams PlatformParams 32 RuntimeDir string 33 Label string 34 ForceMount bool 35 MountErrorIsFatal bool 36 SkipMount bool 37 MountPoint string 38 } 39 40 func startMounting(ctx context.Context, 41 kbCtx libkbfs.Context, config libkbfs.Config, options StartOptions, 42 log logger.Logger, mi *libfs.MountInterrupter) error { 43 log.CDebugf(ctx, "Mounting: %q", options.MountPoint) 44 45 var mounter = &mounter{ 46 options: options, 47 log: log, 48 runMode: kbCtx.GetRunMode(), 49 } 50 err := mi.MountAndSetUnmount(mounter) 51 if err != nil { 52 return err 53 } 54 55 log.CDebugf(ctx, "Creating filesystem") 56 fs := NewFS(config, mounter.c, options.KbfsParams.Debug, 57 options.PlatformParams) 58 ctx, cancel := context.WithCancel(ctx) 59 defer cancel() 60 ctx = context.WithValue(ctx, libfs.CtxAppIDKey, fs) 61 62 go func() { 63 select { 64 case <-mounter.c.Ready: 65 // We wait for the mounter to finish asynchronously with 66 // calling fs.Serve() below, for the rare osxfuse case 67 // where `mount(2)` makes a blocking STATFS call before 68 // completing. If we aren't listening for the STATFS call 69 // when this happens, there will be a deadlock, and the 70 // mount will silently fail after two minutes. See 71 // KBFS-2409. 72 err = mounter.c.MountError 73 if err != nil { 74 log.CWarningf(ctx, "Mount error: %+v", err) 75 cancel() 76 return 77 } 78 log.CDebugf(ctx, "Mount ready") 79 case <-ctx.Done(): 80 } 81 }() 82 83 log.CDebugf(ctx, "Serving filesystem") 84 if err = fs.Serve(ctx); err != nil { 85 return err 86 } 87 88 log.CDebugf(ctx, "Ending") 89 return nil 90 } 91 92 // Start the filesystem 93 func Start(options StartOptions, kbCtx libkbfs.Context) *libfs.Error { 94 // Hook simplefs implementation in. 95 shutdownSimpleFS := func(_ context.Context) error { return nil } 96 createSimpleFS := func( 97 libkbfsCtx libkbfs.Context, config libkbfs.Config) (rpc.Protocol, error) { 98 var sfs *simplefs.SimpleFS 99 sfs, shutdownSimpleFS = simplefs.NewSimpleFS( 100 libkbfsCtx, config) 101 config.AddResetForLoginTarget(sfs) 102 return keybase1.SimpleFSProtocol(sfs), nil 103 } 104 // Hook git implementation in. 105 shutdownGit := func() {} 106 createGitHandler := func( 107 libkbfsCtx libkbfs.Context, config libkbfs.Config) (rpc.Protocol, error) { 108 var handler keybase1.KBFSGitInterface 109 handler, shutdownGit = libgit.NewRPCHandlerWithCtx( 110 libkbfsCtx, config, &options.KbfsParams) 111 return keybase1.KBFSGitProtocol(handler), nil 112 } 113 defer func() { 114 err := shutdownSimpleFS(context.Background()) 115 if err != nil { 116 fmt.Fprintf(os.Stderr, "Couldn't shut down SimpleFS: %+v\n", err) 117 } 118 shutdownGit() 119 }() 120 121 // Patch the kbfsParams to inject two additional protocols. 122 options.KbfsParams.AdditionalProtocolCreators = []libkbfs.AdditionalProtocolCreator{ 123 createSimpleFS, createGitHandler, 124 } 125 126 log, err := libkbfs.InitLog(options.KbfsParams, kbCtx) 127 if err != nil { 128 return libfs.InitError(err.Error()) 129 } 130 131 if options.RuntimeDir != "" { 132 err := os.MkdirAll(options.RuntimeDir, libkb.PermDir) 133 if err != nil { 134 return libfs.InitError(err.Error()) 135 } 136 info := libkb.NewServiceInfo(libkb.Version, libkbfs.PrereleaseBuild, options.Label, os.Getpid()) 137 err = info.WriteFile(path.Join(options.RuntimeDir, "kbfs.info"), log) 138 if err != nil { 139 return libfs.InitError(err.Error()) 140 } 141 } 142 143 log.Debug("Initializing") 144 mi := libfs.NewMountInterrupter(log) 145 ctx := context.Background() 146 config, err := libkbfs.Init( 147 ctx, kbCtx, options.KbfsParams, nil, mi.Done, log) 148 if err != nil { 149 return libfs.InitError(err.Error()) 150 } 151 defer libkbfs.Shutdown() 152 153 libfs.AddRootWrapper(config) 154 155 if options.KbfsParams.Debug { 156 fuseLog := config.MakeLogger("FUSE").CloneWithAddedDepth(1) 157 fuse.Debug = MakeFuseVDebugFn( 158 config.MakeVLogger(fuseLog), false /* superVerbose */) 159 } 160 161 // Report "startup successful" to the supervisor (currently just systemd on 162 // Linux). This isn't necessary for correctness, but it allows commands 163 // like "systemctl start kbfs.service" to report startup errors to the 164 // terminal, by delaying their return until they get this notification 165 // (Type=notify, in systemd lingo). 166 systemd.NotifyStartupFinished() 167 168 if options.SkipMount { 169 log.Debug("Skipping mounting filesystem") 170 } else { 171 err = startMounting(ctx, kbCtx, config, options, log, mi) 172 if err != nil { 173 // Abort on error if we were force mounting, otherwise continue. 174 if options.MountErrorIsFatal { 175 // If we exit we might want to clean a mount behind us. 176 _ = mi.Done() 177 return libfs.MountError(err.Error()) 178 } 179 } 180 } 181 mi.Wait() 182 return nil 183 }