github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/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 simplefsIface keybase1.SimpleFSInterface 99 simplefsIface, shutdownSimpleFS = simplefs.NewSimpleFS( 100 libkbfsCtx, config) 101 return keybase1.SimpleFSProtocol(simplefsIface), nil 102 } 103 // Hook git implementation in. 104 shutdownGit := func() {} 105 createGitHandler := func( 106 libkbfsCtx libkbfs.Context, config libkbfs.Config) (rpc.Protocol, error) { 107 var handler keybase1.KBFSGitInterface 108 handler, shutdownGit = libgit.NewRPCHandlerWithCtx( 109 libkbfsCtx, config, &options.KbfsParams) 110 return keybase1.KBFSGitProtocol(handler), nil 111 } 112 defer func() { 113 err := shutdownSimpleFS(context.Background()) 114 if err != nil { 115 fmt.Fprintf(os.Stderr, "Couldn't shut down SimpleFS: %+v\n", err) 116 } 117 shutdownGit() 118 }() 119 120 // Patch the kbfsParams to inject two additional protocols. 121 options.KbfsParams.AdditionalProtocolCreators = []libkbfs.AdditionalProtocolCreator{ 122 createSimpleFS, createGitHandler, 123 } 124 125 log, err := libkbfs.InitLog(options.KbfsParams, kbCtx) 126 if err != nil { 127 return libfs.InitError(err.Error()) 128 } 129 130 if options.RuntimeDir != "" { 131 err := os.MkdirAll(options.RuntimeDir, libkb.PermDir) 132 if err != nil { 133 return libfs.InitError(err.Error()) 134 } 135 info := libkb.NewServiceInfo(libkb.Version, libkbfs.PrereleaseBuild, options.Label, os.Getpid()) 136 err = info.WriteFile(path.Join(options.RuntimeDir, "kbfs.info"), log) 137 if err != nil { 138 return libfs.InitError(err.Error()) 139 } 140 } 141 142 log.Debug("Initializing") 143 mi := libfs.NewMountInterrupter(log) 144 ctx := context.Background() 145 config, err := libkbfs.Init( 146 ctx, kbCtx, options.KbfsParams, nil, mi.Done, log) 147 if err != nil { 148 return libfs.InitError(err.Error()) 149 } 150 defer libkbfs.Shutdown() 151 152 libfs.AddRootWrapper(config) 153 154 if options.KbfsParams.Debug { 155 fuseLog := config.MakeLogger("FUSE").CloneWithAddedDepth(1) 156 fuse.Debug = MakeFuseVDebugFn( 157 config.MakeVLogger(fuseLog), false /* superVerbose */) 158 } 159 160 // Report "startup successful" to the supervisor (currently just systemd on 161 // Linux). This isn't necessary for correctness, but it allows commands 162 // like "systemctl start kbfs.service" to report startup errors to the 163 // terminal, by delaying their return until they get this notification 164 // (Type=notify, in systemd lingo). 165 systemd.NotifyStartupFinished() 166 167 if options.SkipMount { 168 log.Debug("Skipping mounting filesystem") 169 } else { 170 err = startMounting(ctx, kbCtx, config, options, log, mi) 171 if err != nil { 172 // Abort on error if we were force mounting, otherwise continue. 173 if options.MountErrorIsFatal { 174 // If we exit we might want to clean a mount behind us. 175 _ = mi.Done() 176 return libfs.MountError(err.Error()) 177 } 178 } 179 } 180 mi.Wait() 181 return nil 182 }