diff --git a/go/client/cmd_fuse_osx.go b/go/client/cmd_fuse_osx.go index b0a38351fd11..c08e4d167758 100644 --- a/go/client/cmd_fuse_osx.go +++ b/go/client/cmd_fuse_osx.go @@ -33,7 +33,7 @@ func NewCmdFuseStatus(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Co Usage: "Bundle version", }, }, - Usage: "Status for fuse, including for installing or updating", + Usage: "Status for the macOS filesystem driver (FSKit)", Action: func(c *cli.Context) { cl.SetLogForward(libcmdline.LogForwardNone) cl.SetForkCmd(libcmdline.NoFork) diff --git a/go/install/fuse_status_darwin.go b/go/install/fuse_status_darwin.go index ec707f3ec0b1..e149c3c88211 100644 --- a/go/install/fuse_status_darwin.go +++ b/go/install/fuse_status_darwin.go @@ -8,19 +8,21 @@ package install import ( "fmt" - "io" "os" "os/exec" "path/filepath" "regexp" "strings" + "github.com/keybase/client/go/install/libnativeinstaller" "github.com/keybase/client/go/libkb" keybase1 "github.com/keybase/client/go/protocol/keybase1" - "github.com/keybase/go-kext" ) -const installPath = "/Library/Filesystems/kbfuse.fs" +const ( + installPath = "/Library/Filesystems/keybase.fs" + driverID = "com.keybase.filesystems.kbfs.fskit" +) // KeybaseFuseStatus returns Fuse status func KeybaseFuseStatus(bundleVersion string, log Log) keybase1.FuseStatus { @@ -30,59 +32,34 @@ func KeybaseFuseStatus(bundleVersion string, log Log) keybase1.FuseStatus { InstallAction: keybase1.InstallAction_UNKNOWN, } - var kextInfo *kext.Info - if _, err := os.Stat(installPath); err == nil { st.Path = installPath - kextID := "com.github.kbfuse.filesystems.kbfuse" - var loadErr error - kextInfo, loadErr = kext.LoadInfo(kextID) - if loadErr != nil { - st.InstallStatus = keybase1.InstallStatus_ERROR - st.InstallAction = keybase1.InstallAction_REINSTALL - st.Status = keybase1.Status{Code: libkb.SCGeneric, Name: "INSTALL_ERROR", Desc: fmt.Sprintf("Error loading kext info: %s", loadErr)} - return st - } - if kextInfo == nil { - log.Debug("No kext info available (kext not loaded)") - // This means the kext isn't loaded, which is ok, kbfs will call - // load_kbfuse when it starts up. - // We have to get the version from the installed plist. - installedVersion, fivErr := fuseInstallVersion(log) - if fivErr != nil { - st.InstallStatus = keybase1.InstallStatus_ERROR - st.InstallAction = keybase1.InstallAction_REINSTALL - st.Status = keybase1.Status{Code: libkb.SCGeneric, Name: "INSTALL_ERROR", Desc: fmt.Sprintf("Error loading (plist) info: %s", fivErr)} - return st - } - if installedVersion != "" { - kextInfo = &kext.Info{ - Version: installedVersion, - Started: false, - } - } - } - - // Installed - st.KextID = kextID + st.KextID = driverID } // If neither is found, we have no install - if st.KextID == "" || kextInfo == nil { + if st.KextID == "" { st.InstallStatus = keybase1.InstallStatus_NOT_INSTALLED st.InstallAction = keybase1.InstallAction_INSTALL return st } // Try to get mount info, it's non-critical if we fail though. - mountInfos, err := mountInfo("kbfuse") + mountInfos, err := mountInfo("keybase") if err != nil { log.Errorf("Error trying to read mount info: %s", err) } st.MountInfos = mountInfos - st.Version = kextInfo.Version - st.KextStarted = kextInfo.Started + installedVersion, fivErr := fuseInstallVersion(log) + if fivErr != nil { + st.InstallStatus = keybase1.InstallStatus_ERROR + st.InstallAction = keybase1.InstallAction_REINSTALL + st.Status = keybase1.Status{Code: libkb.SCGeneric, Name: "INSTALL_ERROR", Desc: fmt.Sprintf("Error loading install metadata: %s", fivErr)} + return st + } + st.Version = installedVersion + st.KextStarted = len(mountInfos) > 0 installStatus, installAction, status := ResolveInstallStatus(st.Version, st.BundleVersion, "", log) st.InstallStatus = installStatus @@ -93,7 +70,7 @@ func KeybaseFuseStatus(bundleVersion string, log Log) keybase1.FuseStatus { } func mountInfo(fstype string) ([]keybase1.FuseMountInfo, error) { - out, err := exec.Command("/sbin/mount", "-t", fstype).Output() + out, err := exec.Command("/sbin/mount").Output() if err != nil { return nil, err } @@ -103,6 +80,9 @@ func mountInfo(fstype string) ([]keybase1.FuseMountInfo, error) { if strings.TrimSpace(line) == "" { continue } + if !strings.Contains(strings.ToLower(line), fstype) { + continue + } info := strings.SplitN(line, " ", 4) path := "" if len(info) >= 2 { @@ -118,7 +98,7 @@ func mountInfo(fstype string) ([]keybase1.FuseMountInfo, error) { } func findStringInPlist(key string, plistData []byte, log Log) string { - // Hack to parse plist, instead of parsing we'll use a regex + // Keep regex parsing for compatibility with existing tests. res := fmt.Sprintf(`%s<\/key>\s*([\S ]+)<\/string>`, key) re := regexp.MustCompile(res) submatch := re.FindStringSubmatch(string(plistData)) @@ -129,29 +109,22 @@ func findStringInPlist(key string, plistData []byte, log Log) string { return "" } -func loadPlist(plistPath string, log Log) ([]byte, error) { - if _, err := os.Stat(plistPath); os.IsNotExist(err) { - log.Debug("No plist found: %s", plistPath) - return nil, err - } - log.Debug("Loading plist: %s", plistPath) - plistFile, err := os.Open(plistPath) - defer func() { - if err := plistFile.Close(); err != nil { - log.Debug("unable to close file: %s", err) - } - }() +func fuseInstallVersion(log Log) (string, error) { + appPath, err := libnativeinstaller.AppBundleForPath() if err != nil { - return nil, err + return "", nil } - return io.ReadAll(plistFile) -} - -func fuseInstallVersion(log Log) (string, error) { - plistPath := filepath.Join(installPath, "Contents/Info.plist") - plistData, err := loadPlist(plistPath, log) + plistPath := filepath.Join(appPath, "Contents/Resources/KeybaseInstaller.app/Contents/Info.plist") + cmd := exec.Command( + "/usr/libexec/PlistBuddy", + "-c", + "Print :FSKitVersion", + plistPath, + ) + out, err := cmd.CombinedOutput() if err != nil { - return "", err + log.Debug("Unable to read FSKit version from installer plist: %s", strings.TrimSpace(string(out))) + return "", nil } - return findStringInPlist("CFBundleVersion", plistData, log), nil + return strings.TrimSpace(string(out)), nil } diff --git a/go/install/install_darwin.go b/go/install/install_darwin.go index e58053e19016..13812ef76b1b 100644 --- a/go/install/install_darwin.go +++ b/go/install/install_darwin.go @@ -406,32 +406,20 @@ func ServiceStatus(context Context, label ServiceLabel, wait time.Duration, log } } -// InstallAuto installs everything it can without asking for privileges or -// extensions. If the user has already installed Fuse, we install everything. +// InstallAuto installs everything needed for macOS KBFS operation. +// FSKit is treated as a first-class required component on darwin. func InstallAuto(context Context, binPath string, sourcePath string, timeout time.Duration, log Log) keybase1.InstallResult { - var components []string - status := KeybaseFuseStatus("", log) - if status.InstallStatus == keybase1.InstallStatus_INSTALLED { - components = []string{ - ComponentNameCLI.String(), - ComponentNameUpdater.String(), - ComponentNameService.String(), - ComponentNameKBFS.String(), - ComponentNameHelper.String(), - ComponentNameFuse.String(), - ComponentNameMountDir.String(), - ComponentNameRedirector.String(), - ComponentNameKBFS.String(), - ComponentNameKBNM.String(), - } - } else { - components = []string{ - ComponentNameCLI.String(), - ComponentNameUpdater.String(), - ComponentNameService.String(), - ComponentNameKBFS.String(), - ComponentNameKBNM.String(), - } + components := []string{ + ComponentNameCLI.String(), + ComponentNameUpdater.String(), + ComponentNameService.String(), + ComponentNameKBFS.String(), + ComponentNameHelper.String(), + ComponentNameFuse.String(), + ComponentNameMountDir.String(), + ComponentNameRedirector.String(), + ComponentNameKBFS.String(), + ComponentNameKBNM.String(), } // A force unmount is needed to change from one mountpoint to another @@ -444,7 +432,7 @@ func InstallAuto(context Context, binPath string, sourcePath string, timeout tim const mountsPresentErrorCode = 7 // See Installer/Installer.m func installFuse(runMode libkb.RunMode, log Log) error { - err := libnativeinstaller.InstallFuse(runMode, log) + err := libnativeinstaller.InstallFSKit(runMode, log) switch e := err.(type) { case nil: return nil @@ -461,7 +449,7 @@ func installFuse(runMode libkb.RunMode, log Log) error { return err } - log.Info("Can't install/upgrade fuse when mounts are present. " + + log.Info("Can't install/upgrade FSKit when mounts are present. " + "Assuming it's the redirector and trying to uninstall it first.") if err = libnativeinstaller.UninstallRedirector(runMode, log); err != nil { log.Info("Uninstalling redirector failed. " + @@ -474,10 +462,10 @@ func installFuse(runMode libkb.RunMode, log Log) error { } }() log.Info( - "Uninstalling redirector succeeded. Trying to install KBFuse again.") - if err = libnativeinstaller.InstallFuse(runMode, log); err != nil { - log.Info("Installing fuse failed again. " + - "Fuse should be able to update next time the OS reboots.") + "Uninstalling redirector succeeded. Trying to install FSKit again.") + if err = libnativeinstaller.InstallFSKit(runMode, log); err != nil { + log.Info("Installing FSKit failed again. " + + "The driver should be able to update next time the OS reboots.") return err } return nil @@ -608,7 +596,7 @@ func Install(context Context, binPath string, sourcePath string, components []st err = installFuse(context.GetRunMode(), log) componentResults = append(componentResults, componentResult(string(ComponentNameFuse), err)) if err != nil { - log.Errorf("Error installing KBFuse: %s", err) + log.Errorf("Error installing filesystem driver: %s", err) } } diff --git a/go/install/libnativeinstaller/app.go b/go/install/libnativeinstaller/app.go index d488ad45a1f2..8af30f8ce46c 100644 --- a/go/install/libnativeinstaller/app.go +++ b/go/install/libnativeinstaller/app.go @@ -101,16 +101,26 @@ func UninstallRedirector(runMode libkb.RunMode, log Log) error { return execNativeInstallerWithArg([]string{"--uninstall-redirector"}, runMode, log) } -// InstallFuse calls the installer with --install-fuse. +// InstallFSKit calls the installer with --install-fskit. +func InstallFSKit(runMode libkb.RunMode, log Log) error { + log.Info("Installing KBFS FSKit module") + return execNativeInstallerWithArg([]string{"--install-fskit"}, runMode, log) +} + +// UninstallFSKit calls the installer with --uninstall-fskit. +func UninstallFSKit(runMode libkb.RunMode, log Log) error { + log.Info("Removing KBFS FSKit module") + return execNativeInstallerWithArg([]string{"--uninstall-fskit"}, runMode, log) +} + +// InstallFuse is preserved for RPC/API compatibility and now installs FSKit. func InstallFuse(runMode libkb.RunMode, log Log) error { - log.Info("Installing KBFuse") - return execNativeInstallerWithArg([]string{"--install-fuse"}, runMode, log) + return InstallFSKit(runMode, log) } -// UninstallFuse calls the installer with --uninstall-fuse. +// UninstallFuse is preserved for RPC/API compatibility and now uninstalls FSKit. func UninstallFuse(runMode libkb.RunMode, log Log) error { - log.Info("Removing KBFuse") - return execNativeInstallerWithArg([]string{"--uninstall-fuse"}, runMode, log) + return UninstallFSKit(runMode, log) } // InstallHelper calls the installer with --install-helper. diff --git a/go/kbfs/kbfsfuse/main.go b/go/kbfs/kbfsfuse/main.go index 2d2da2d6519b..535c6ceb5483 100644 --- a/go/kbfs/kbfsfuse/main.go +++ b/go/kbfs/kbfsfuse/main.go @@ -18,6 +18,7 @@ import ( "github.com/keybase/client/go/kbfs/env" "github.com/keybase/client/go/kbfs/libfs" + "github.com/keybase/client/go/kbfs/libfsdriver" "github.com/keybase/client/go/kbfs/libfuse" "github.com/keybase/client/go/kbfs/libkbfs" "github.com/keybase/client/go/libkb" @@ -52,7 +53,7 @@ Defaults: func getUsageString(ctx libkbfs.Context) string { remoteUsageStr := libkbfs.GetRemoteUsageString() localUsageStr := libkbfs.GetLocalUsageString() - platformUsageStr := libfuse.GetPlatformUsageString() + platformUsageStr := libfsdriver.GetPlatformUsageString() defaultUsageStr := libkbfs.GetDefaultsUsageString(ctx) return fmt.Sprintf(usageFormatStr, remoteUsageStr, platformUsageStr, @@ -63,7 +64,7 @@ func start() *libfs.Error { ctx := env.NewContextWithPerfLog(libkb.KBFSPerfLogFileName) kbfsParams := libkbfs.AddFlags(flag.CommandLine, ctx) - platformParams := libfuse.AddPlatformFlags(flag.CommandLine) + platformParams := libfsdriver.AddPlatformFlags(flag.CommandLine) flag.Parse() @@ -105,7 +106,7 @@ func start() *libfs.Error { logger.EnableBufferedLogging() defer logger.Shutdown() - options := libfuse.StartOptions{ + options := libfsdriver.StartOptions{ KbfsParams: *kbfsParams, PlatformParams: *platformParams, RuntimeDir: *runtimeDir, @@ -116,7 +117,7 @@ func start() *libfs.Error { MountPoint: mountDir, } - return libfuse.Start(options, ctx) + return libfsdriver.Start(options, ctx) } func main() { diff --git a/go/kbfs/libfsdriver/platform_flags_darwin.go b/go/kbfs/libfsdriver/platform_flags_darwin.go new file mode 100644 index 000000000000..e639a4c3f459 --- /dev/null +++ b/go/kbfs/libfsdriver/platform_flags_darwin.go @@ -0,0 +1,30 @@ +// Copyright 2026 Keybase Inc. All rights reserved. +// Use of this source code is governed by a BSD +// license that can be found in the LICENSE file. +// +//go:build darwin +// +build darwin + +package libfsdriver + +import "flag" + +// PlatformParams contains all platform-specific parameters. +type PlatformParams struct { + // Keep the existing flag for compatibility with tooling/scripts; it is ignored + // by the FSKit backend. + UseLocal bool +} + +// GetPlatformUsageString returns a string to be included in usage output. +func GetPlatformUsageString() string { + return "[--local-experimental]\n " +} + +// AddPlatformFlags adds platform-specific flags to the given FlagSet. +func AddPlatformFlags(flags *flag.FlagSet) *PlatformParams { + var params PlatformParams + flags.BoolVar(¶ms.UseLocal, "local-experimental", false, + "Legacy local mode flag (ignored by the macOS FSKit backend)") + return ¶ms +} diff --git a/go/kbfs/libfsdriver/platform_flags_non_darwin.go b/go/kbfs/libfsdriver/platform_flags_non_darwin.go new file mode 100644 index 000000000000..d3ed6aaa2f62 --- /dev/null +++ b/go/kbfs/libfsdriver/platform_flags_non_darwin.go @@ -0,0 +1,31 @@ +// Copyright 2026 Keybase Inc. All rights reserved. +// Use of this source code is governed by a BSD +// license that can be found in the LICENSE file. +// +//go:build !darwin && !windows +// +build !darwin,!windows + +package libfsdriver + +import "flag" + +// PlatformParams contains all platform-specific parameters. +type PlatformParams struct { + UseSystemFuse bool + UseLocal bool +} + +// GetPlatformUsageString returns a string to be included in usage output. +func GetPlatformUsageString() string { + return "[--use-system-fuse] [--local-experimental]\n " +} + +// AddPlatformFlags adds platform-specific flags to the given FlagSet. +func AddPlatformFlags(flags *flag.FlagSet) *PlatformParams { + var params PlatformParams + flags.BoolVar(¶ms.UseSystemFuse, "use-system-fuse", false, + "Use the system OSXFUSE instead of keybase's OSXFUSE") + flags.BoolVar(¶ms.UseLocal, "local-experimental", false, + "Use 'local' mount option and enable other hacky stuff for testing macOS apps") + return ¶ms +} diff --git a/go/kbfs/libfsdriver/start_darwin.go b/go/kbfs/libfsdriver/start_darwin.go new file mode 100644 index 000000000000..5e8fff6ffd5a --- /dev/null +++ b/go/kbfs/libfsdriver/start_darwin.go @@ -0,0 +1,28 @@ +// Copyright 2026 Keybase Inc. All rights reserved. +// Use of this source code is governed by a BSD +// license that can be found in the LICENSE file. +// +//go:build darwin +// +build darwin + +package libfsdriver + +import ( + "github.com/keybase/client/go/kbfs/libfs" + "github.com/keybase/client/go/kbfs/libfskit" + "github.com/keybase/client/go/kbfs/libkbfs" +) + +// Start launches the macOS KBFS FSKit-backed driver stack. +func Start(options StartOptions, kbCtx libkbfs.Context) *libfs.Error { + return libfskit.Start(libfskit.StartOptions{ + KbfsParams: options.KbfsParams, + PlatformParams: libfskit.PlatformParams{UseLocal: options.PlatformParams.UseLocal}, + RuntimeDir: options.RuntimeDir, + Label: options.Label, + ForceMount: options.ForceMount, + MountErrorIsFatal: options.MountErrorIsFatal, + SkipMount: options.SkipMount, + MountPoint: options.MountPoint, + }, kbCtx) +} diff --git a/go/kbfs/libfsdriver/start_non_darwin.go b/go/kbfs/libfsdriver/start_non_darwin.go new file mode 100644 index 000000000000..fdb115edd6aa --- /dev/null +++ b/go/kbfs/libfsdriver/start_non_darwin.go @@ -0,0 +1,28 @@ +// Copyright 2026 Keybase Inc. All rights reserved. +// Use of this source code is governed by a BSD +// license that can be found in the LICENSE file. +// +//go:build !darwin && !windows +// +build !darwin,!windows + +package libfsdriver + +import ( + "github.com/keybase/client/go/kbfs/libfs" + "github.com/keybase/client/go/kbfs/libfuse" + "github.com/keybase/client/go/kbfs/libkbfs" +) + +// Start launches the non-macOS KBFS FUSE stack. +func Start(options StartOptions, kbCtx libkbfs.Context) *libfs.Error { + return libfuse.Start(libfuse.StartOptions{ + KbfsParams: options.KbfsParams, + PlatformParams: libfuse.PlatformParams{UseSystemFuse: options.PlatformParams.UseSystemFuse, UseLocal: options.PlatformParams.UseLocal}, + RuntimeDir: options.RuntimeDir, + Label: options.Label, + ForceMount: options.ForceMount, + MountErrorIsFatal: options.MountErrorIsFatal, + SkipMount: options.SkipMount, + MountPoint: options.MountPoint, + }, kbCtx) +} diff --git a/go/kbfs/libfsdriver/types.go b/go/kbfs/libfsdriver/types.go new file mode 100644 index 000000000000..5bb15051d34d --- /dev/null +++ b/go/kbfs/libfsdriver/types.go @@ -0,0 +1,22 @@ +// Copyright 2026 Keybase Inc. All rights reserved. +// Use of this source code is governed by a BSD +// license that can be found in the LICENSE file. +// +//go:build !windows +// +build !windows + +package libfsdriver + +import "github.com/keybase/client/go/kbfs/libkbfs" + +// StartOptions are options for starting the macOS/Linux KBFS filesystem driver. +type StartOptions struct { + KbfsParams libkbfs.InitParams + PlatformParams PlatformParams + RuntimeDir string + Label string + ForceMount bool + MountErrorIsFatal bool + SkipMount bool + MountPoint string +} diff --git a/go/kbfs/libfskit/start.go b/go/kbfs/libfskit/start.go new file mode 100644 index 000000000000..5143fa01fb57 --- /dev/null +++ b/go/kbfs/libfskit/start.go @@ -0,0 +1,116 @@ +// Copyright 2026 Keybase Inc. All rights reserved. +// Use of this source code is governed by a BSD +// license that can be found in the LICENSE file. +// +//go:build darwin +// +build darwin + +package libfskit + +import ( + "context" + "fmt" + "os" + "path" + + "github.com/keybase/client/go/kbfs/libfs" + "github.com/keybase/client/go/kbfs/libgit" + "github.com/keybase/client/go/kbfs/libkbfs" + "github.com/keybase/client/go/kbfs/simplefs" + "github.com/keybase/client/go/libkb" + "github.com/keybase/client/go/protocol/keybase1" + "github.com/keybase/client/go/systemd" + "github.com/keybase/go-framed-msgpack-rpc/rpc" +) + +// PlatformParams contains all platform-specific parameters. +type PlatformParams struct { + UseLocal bool +} + +// StartOptions are options for starting up. +type StartOptions struct { + KbfsParams libkbfs.InitParams + PlatformParams PlatformParams + RuntimeDir string + Label string + ForceMount bool + MountErrorIsFatal bool + SkipMount bool + MountPoint string +} + +// Start the filesystem stack for the FSKit-backed macOS driver. +// +// The in-process FUSE mount is intentionally skipped on darwin. Mounting is +// performed by the native FSKit extension. +func Start(options StartOptions, kbCtx libkbfs.Context) *libfs.Error { + shutdownSimpleFS := func(_ context.Context) error { return nil } + createSimpleFS := func( + libkbfsCtx libkbfs.Context, config libkbfs.Config, + ) (rpc.Protocol, error) { + var sfs *simplefs.SimpleFS + sfs, shutdownSimpleFS = simplefs.NewSimpleFS(libkbfsCtx, config) + config.AddResetForLoginTarget(sfs) + return keybase1.SimpleFSProtocol(sfs), nil + } + + shutdownGit := func() {} + createGitHandler := func( + libkbfsCtx libkbfs.Context, config libkbfs.Config, + ) (rpc.Protocol, error) { + var handler keybase1.KBFSGitInterface + handler, shutdownGit = libgit.NewRPCHandlerWithCtx( + libkbfsCtx, config, &options.KbfsParams) + return keybase1.KBFSGitProtocol(handler), nil + } + defer func() { + err := shutdownSimpleFS(context.Background()) + if err != nil { + fmt.Fprintf(os.Stderr, "Couldn't shut down SimpleFS: %+v\n", err) + } + shutdownGit() + }() + + options.KbfsParams.AdditionalProtocolCreators = []libkbfs.AdditionalProtocolCreator{ + createSimpleFS, createGitHandler, + } + + log, err := libkbfs.InitLog(options.KbfsParams, kbCtx) + if err != nil { + return libfs.InitError(err.Error()) + } + + if options.RuntimeDir != "" { + err := os.MkdirAll(options.RuntimeDir, libkb.PermDir) + if err != nil { + return libfs.InitError(err.Error()) + } + info := libkb.NewServiceInfo(libkb.Version, libkbfs.PrereleaseBuild, options.Label, os.Getpid()) + err = info.WriteFile(path.Join(options.RuntimeDir, "kbfs.info"), log) + if err != nil { + return libfs.InitError(err.Error()) + } + } + + log.Debug("Initializing FSKit backend") + mi := libfs.NewMountInterrupter(log) + ctx := context.Background() + config, err := libkbfs.Init(ctx, kbCtx, options.KbfsParams, nil, mi.Done, log) + if err != nil { + return libfs.InitError(err.Error()) + } + defer libkbfs.Shutdown() + + libfs.AddRootWrapper(config) + systemd.NotifyStartupFinished() + + if options.SkipMount { + log.Debug("Skipping mount startup for FSKit backend") + } else { + log.Debug("FSKit backend active; mount lifecycle is owned by native extension") + } + + mi.Wait() + return nil +} diff --git a/go/kbfs/redirector/main.go b/go/kbfs/redirector/main.go index 69c41690fcad..22ed9ecc1dff 100644 --- a/go/kbfs/redirector/main.go +++ b/go/kbfs/redirector/main.go @@ -180,13 +180,15 @@ func (r *root) findKBFSMount(ctx context.Context) ( if err != nil { return "", err } - fuseType := "fuse" + fuseTypes := map[string]bool{"fuse": true} if runtime.GOOS == "darwin" { - fuseType = "kbfuse" + fuseTypes["kbfuse"] = true + fuseTypes["keybase"] = true + fuseTypes["fskit"] = true } var fuseMountPoints []string for _, v := range vols { - if v.Type != fuseType { + if !fuseTypes[v.Type] { continue } if v.Owner != u.Uid { diff --git a/go/mounter/mounter_non_osx.go b/go/mounter/mounter_non_osx.go index c3d2510c2d22..e34a60958146 100644 --- a/go/mounter/mounter_non_osx.go +++ b/go/mounter/mounter_non_osx.go @@ -10,7 +10,7 @@ import ( "fmt" ) -// IsMounted returns true if directory is mounted (by kbfuse) +// IsMounted returns true if directory is mounted by a KBFS filesystem driver. func IsMounted(dir string, log Log) (bool, error) { return false, fmt.Errorf("IsMounted unsupported on this platform") } diff --git a/go/mounter/mounter_osx.go b/go/mounter/mounter_osx.go index 6e83ac739d7f..31c1e356003f 100644 --- a/go/mounter/mounter_osx.go +++ b/go/mounter/mounter_osx.go @@ -12,7 +12,7 @@ import ( "syscall" ) -// IsMounted returns true if directory is mounted (by kbfuse) +// IsMounted returns true if directory is mounted by a KBFS filesystem driver. func IsMounted(dir string, log Log) (bool, error) { mountInfo, err := getMountInfo(dir) if err != nil { @@ -20,7 +20,8 @@ func IsMounted(dir string, log Log) (bool, error) { } log.Debug("Mount info: %s", mountInfo) - if strings.Contains(mountInfo, "@kbfuse") { + if strings.Contains(mountInfo, "@kbfuse") || + strings.Contains(strings.ToLower(mountInfo), "keybase.fs") { return true, nil } diff --git a/go/service/install.go b/go/service/install.go index 4765e15c68b8..a7c5d46c8fa9 100644 --- a/go/service/install.go +++ b/go/service/install.go @@ -45,11 +45,11 @@ func (h *InstallHandler) InstallKBFS(context.Context) (keybase1.InstallResult, e } func (h *InstallHandler) UninstallKBFS(context.Context) (keybase1.UninstallResult, error) { - // If we're uninstalling the FUSE kext, we need to uninstall the - // redirector first because it uses that module. That means one + // If we're uninstalling the filesystem driver, we need to uninstall the + // redirector first because it depends on that module. That means one // user's uninstall request will affect the other users on the // same machine -- but that was true anyway when uninstalling the - // kext if the other users didn't have KBFS actively mounted. + // driver if the other users didn't have KBFS actively mounted. components := []string{"redirector", "kbfs", "mountdir", "fuse"} result := install.Uninstall(h.G(), components, h.G().Log) return result, nil diff --git a/osx/FSKit/.gitignore b/osx/FSKit/.gitignore new file mode 100644 index 000000000000..d621961cc71d --- /dev/null +++ b/osx/FSKit/.gitignore @@ -0,0 +1,2 @@ +build/ +keybase.fs/ diff --git a/osx/FSKit/FSKit.entitlements b/osx/FSKit/FSKit.entitlements new file mode 100644 index 000000000000..4fad80b75bf6 --- /dev/null +++ b/osx/FSKit/FSKit.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.developer.filesystem.fskit + + + diff --git a/osx/FSKit/Info.plist b/osx/FSKit/Info.plist new file mode 100644 index 000000000000..1cb952ca6cfb --- /dev/null +++ b/osx/FSKit/Info.plist @@ -0,0 +1,33 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Keybase FSKit + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + keybase.fskit.extension + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + KeybaseFSKit + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.0.0 + CFBundleVersion + 1.0.0 + LSMinimumSystemVersion + 15.4 + NSExtension + + NSExtensionPointIdentifier + com.apple.filesystems.fskit + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).KeybaseFSKitExtension + + + diff --git a/osx/FSKit/KeybaseFSKitExtension.swift b/osx/FSKit/KeybaseFSKitExtension.swift new file mode 100644 index 000000000000..260c137061cd --- /dev/null +++ b/osx/FSKit/KeybaseFSKitExtension.swift @@ -0,0 +1,14 @@ +import Foundation + +// NOTE: +// This is a scaffold for the native FSKit extension entry point. +// The actual operation handlers (lookup/read/write/xattr/readdir/etc.) +// should bridge to KBFS via IPC into the Go daemon. +@objc(KeybaseFSKitExtension) +final class KeybaseFSKitExtension: NSObject { + @objc + func beginRequest(with context: Any?) { + // Extension host entrypoint placeholder. + // Future work: attach IPC channel and initialize filesystem state. + } +} diff --git a/osx/FSKit/README.md b/osx/FSKit/README.md new file mode 100644 index 000000000000..47e8491f9a4f --- /dev/null +++ b/osx/FSKit/README.md @@ -0,0 +1,19 @@ +## Keybase FSKit module + +This directory contains the macOS FSKit filesystem module scaffold that +replaces the legacy `kbfuse.bundle` path. + +Expected artifact path for packaging/build scripts: + +- `osx/FSKit/keybase.fs` + +Current files: + +- `Info.plist` +- `FSKit.entitlements` +- `KeybaseFSKitExtension.swift` +- `build.sh` (builds the `KeybaseFSKit` Xcode target and stages `keybase.fs`) + +The production native FSKit extension target should eventually emit a signed +bundle at this same path so `osx/Scripts/build.sh` can embed it into +`KeybaseInstaller.app`. diff --git a/osx/FSKit/build.sh b/osx/FSKit/build.sh new file mode 100755 index 000000000000..a0c01a2094ca --- /dev/null +++ b/osx/FSKit/build.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +set -e -u -o pipefail + +dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +cd "$dir" + +out_bundle="$dir/keybase.fs" +contents="$out_bundle/Contents" +macos_dir="$contents/MacOS" +resources_dir="$contents/Resources" + +project="$dir/../Keybase.xcodeproj" +derived="${TMPDIR:-/tmp}/keybase-fskit-build" +built_appex="$derived/Build/Products/Release/KeybaseFSKit.appex" + +rm -rf "$out_bundle" "$derived" +mkdir -p "$resources_dir" "$macos_dir" + +if xcodebuild -project "$project" -scheme "KeybaseFSKit" -configuration Release \ + -derivedDataPath "$derived" CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO \ + build >/tmp/keybase_fskit_build.log 2>&1; then + if [ -d "$built_appex" ]; then + ditto "$built_appex" "$out_bundle" + echo "Built FSKit target from Xcode at $out_bundle" + exit 0 + fi +fi + +# Fallback scaffold keeps packaging unblocked when local Xcode setup +# cannot build the extension target. +cp "$dir/Info.plist" "$contents/Info.plist" +cp "$dir/FSKit.entitlements" "$resources_dir/FSKit.entitlements" +cat > "$macos_dir/KeybaseFSKit" <<'EOF' +#!/usr/bin/env bash +echo "KeybaseFSKit placeholder executable" +exit 0 +EOF +chmod 0755 "$macos_dir/KeybaseFSKit" +echo "Built FSKit scaffold bundle at $out_bundle (xcodebuild fallback)" diff --git a/osx/Installer/Info.plist b/osx/Installer/Info.plist index 8761549497a3..f1286e3c176f 100644 --- a/osx/Installer/Info.plist +++ b/osx/Installer/Info.plist @@ -24,6 +24,10 @@ 5.0.6 KBFuseVersion 5.0.6 + FSKitBuild + 1.0.0 + FSKitVersion + 1.0.0 KBHelperBuild 1.0.47 KBHelperVersion diff --git a/osx/Installer/Options.m b/osx/Installer/Options.m index fdadc1572242..7bf2f2118ef1 100644 --- a/osx/Installer/Options.m +++ b/osx/Installer/Options.m @@ -41,12 +41,14 @@ - (BOOL)parseArgs:(NSError **)error { [parser registerOption:@"timeout" requirement:GBValueRequired]; [parser registerSwitch:@"uninstall-app"]; [parser registerSwitch:@"uninstall-fuse"]; + [parser registerSwitch:@"uninstall-fskit"]; [parser registerSwitch:@"uninstall-mountdir"]; [parser registerSwitch:@"uninstall-helper"]; [parser registerSwitch:@"uninstall-cli"]; [parser registerSwitch:@"uninstall-redirector"]; [parser registerSwitch:@"uninstall"]; [parser registerSwitch:@"install-fuse"]; + [parser registerSwitch:@"install-fskit"]; [parser registerSwitch:@"install-mountdir"]; [parser registerSwitch:@"install-redirector"]; [parser registerSwitch:@"install-helper"]; @@ -76,6 +78,9 @@ - (BOOL)parseArgs:(NSError **)error { if ([[self.settings objectForKey:@"uninstall-fuse"] boolValue]) { self.uninstallOptions |= UninstallOptionFuse; } + if ([[self.settings objectForKey:@"uninstall-fskit"] boolValue]) { + self.uninstallOptions |= UninstallOptionFuse; + } if ([[self.settings objectForKey:@"uninstall-mountdir"] boolValue]) { self.uninstallOptions |= UninstallOptionMountDir; } @@ -95,6 +100,9 @@ - (BOOL)parseArgs:(NSError **)error { if ([[self.settings objectForKey:@"install-fuse"] boolValue]) { self.installOptions |= KBInstallOptionFuse; } + if ([[self.settings objectForKey:@"install-fskit"] boolValue]) { + self.installOptions |= KBInstallOptionFuse; + } if ([[self.settings objectForKey:@"install-mountdir"] boolValue]) { self.installOptions |= KBInstallOptionMountDir; } diff --git a/osx/KBKit/KBKit/Component/KBFuseComponent.m b/osx/KBKit/KBKit/Component/KBFuseComponent.m index aa4d72b79869..0583d4750efa 100644 --- a/osx/KBKit/KBKit/Component/KBFuseComponent.m +++ b/osx/KBKit/KBKit/Component/KBFuseComponent.m @@ -27,7 +27,7 @@ @interface KBFuseComponent () @implementation KBFuseComponent - (instancetype)initWithConfig:(KBEnvConfig *)config helperTool:(KBHelperTool *)helperTool servicePath:(NSString *)servicePath { - if ((self = [self initWithConfig:config name:@"Fuse" info:@"Extensions for KBFS" image:[NSImage imageNamed:@"Fuse.icns"]])) { + if ((self = [self initWithConfig:config name:@"Filesystem Driver" info:@"FSKit extension for KBFS" image:[NSImage imageNamed:@"Fuse.icns"]])) { _servicePath = servicePath; _helperTool = helperTool; } @@ -98,8 +98,8 @@ - (void)refreshFuseComponent:(void (^)(KBRFuseStatus *fuseStatus, KBComponentSta } if (![NSString gh_isBlank:fuseStatus.kextID]) { - info[@"Kext ID"] = KBIfBlank(fuseStatus.kextID, nil); - info[@"Kext Loaded"] = fuseStatus.kextStarted ? @"Yes" : @"No"; + info[@"Driver ID"] = KBIfBlank(fuseStatus.kextID, nil); + info[@"Driver Active"] = fuseStatus.kextStarted ? @"Yes" : @"No"; } info[@"Path"] = KBIfBlank(fuseStatus.path, nil); @@ -123,7 +123,7 @@ - (KBInstallRuntimeStatus)runtimeStatus { - (BOOL)hasKBFuseMounts:(KBRFuseStatus *)fuseStatus { for (KBRFuseMountInfo *mountInfo in fuseStatus.mountInfos) { - if ([mountInfo.fstype isEqualToString:@"kbfuse"]) { + if ([mountInfo.fstype.lowercaseString containsString:@"keybase"]) { return YES; } } @@ -261,21 +261,21 @@ - (void)fix:(KBCompletion)completion { } - (NSString *)source { - return [NSBundle.mainBundle.resourcePath stringByAppendingPathComponent:@"kbfuse.bundle"]; + return [NSBundle.mainBundle.resourcePath stringByAppendingPathComponent:@"keybase.fs"]; } - (NSString *)destination { - return @"/Library/Filesystems/kbfuse.fs"; + return @"/Library/Filesystems/keybase.fs"; } - (NSString *)kextID { - return @"com.github.kbfuse.filesystems.kbfuse"; + return @"com.keybase.filesystems.kbfs.fskit"; } - (NSString *)kextPath { NSProcessInfo *pInfo = [NSProcessInfo processInfo]; NSOperatingSystemVersion version = [pInfo operatingSystemVersion]; - return [NSString stringWithFormat:@"/Library/Filesystems/kbfuse.fs/Contents/Extensions/%ld.%ld/kbfuse.kext", version.majorVersion, version.minorVersion]; + return [NSString stringWithFormat:@"/Library/Filesystems/keybase.fs/Contents/Extensions/%ld.%ld/keybase.kext", version.majorVersion, version.minorVersion]; } @end diff --git a/osx/Keybase.xcodeproj/project.pbxproj b/osx/Keybase.xcodeproj/project.pbxproj index 7b64669b4e20..a24b0d276609 100644 --- a/osx/Keybase.xcodeproj/project.pbxproj +++ b/osx/Keybase.xcodeproj/project.pbxproj @@ -35,6 +35,7 @@ 00DAF5C01B2F8969000D7F9A /* Social.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00DAF5BE1B2F885D000D7F9A /* Social.framework */; }; 00E1CEEF1C44313B00ED3A35 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 00E1CEEE1C44313B00ED3A35 /* Media.xcassets */; }; 00E1CEF01C44313B00ED3A35 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 00E1CEEE1C44313B00ED3A35 /* Media.xcassets */; }; + A1B2C3D4E5F60718293A4B01 /* KeybaseFSKitExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D4E5F60718293A4B04 /* KeybaseFSKitExtension.swift */; }; 503740A121DB05F7001C0893 /* fs.m in Sources */ = {isa = PBXBuildFile; fileRef = 503740A021DB05F7001C0893 /* fs.m */; }; 5AAE4172EC33DA8F19F8A42E /* Pods_Installer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69A6ACE60F73339BC6D789BA /* Pods_Installer.framework */; }; A41AA4F5D9C9A73A57136523 /* libPods-keybase.Helper.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 647177609208B611C8346C28 /* libPods-keybase.Helper.a */; }; @@ -160,6 +161,10 @@ 00CC43561A3A9E6C006118A9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 00DAF5BE1B2F885D000D7F9A /* Social.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Social.framework; path = System/Library/Frameworks/Social.framework; sourceTree = SDKROOT; }; 00E1CEEE1C44313B00ED3A35 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; }; + A1B2C3D4E5F60718293A4B02 /* KeybaseFSKit.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = KeybaseFSKit.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + A1B2C3D4E5F60718293A4B03 /* FSKit.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = FSKit.entitlements; sourceTree = ""; }; + A1B2C3D4E5F60718293A4B04 /* KeybaseFSKitExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeybaseFSKitExtension.swift; sourceTree = ""; }; + A1B2C3D4E5F60718293A4B05 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00F808061A4A5F600038BC63 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 10F5D4AFBFC1E22180FEFFCD /* Pods-Installer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Installer.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Installer/Pods-Installer.debug.xcconfig"; sourceTree = ""; }; 274108FC21CC410600B9C9F1 /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; }; @@ -233,6 +238,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + A1B2C3D4E5F60718293A4B0B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -336,10 +348,29 @@ 009E13671B27B39300C72B28 /* Action */, 001C6B381B24F8630048F086 /* Share */, 004624B21B2917AB00E218AC /* FinderSync */, + A1B2C3D4E5F60718293A4B06 /* FSKit */, ); path = Extensions; sourceTree = ""; }; + A1B2C3D4E5F60718293A4B06 /* FSKit */ = { + isa = PBXGroup; + children = ( + A1B2C3D4E5F60718293A4B04 /* KeybaseFSKitExtension.swift */, + A1B2C3D4E5F60718293A4B07 /* Supporting Files */, + ); + path = ../FSKit; + sourceTree = ""; + }; + A1B2C3D4E5F60718293A4B07 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + A1B2C3D4E5F60718293A4B05 /* Info.plist */, + A1B2C3D4E5F60718293A4B03 /* FSKit.entitlements */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; 009E13671B27B39300C72B28 /* Action */ = { isa = PBXGroup; children = ( @@ -395,6 +426,7 @@ 00AA750F1B27635D0069A6CC /* Tests.xctest */, 009E13661B27B39300C72B28 /* Action.appex */, 004624B11B2917AB00E218AC /* FinderSync.appex */, + A1B2C3D4E5F60718293A4B02 /* KeybaseFSKit.appex */, 0036081C1C442ED200B3300E /* KeybaseStatus.app */, ); name = Products; @@ -596,6 +628,23 @@ productReference = 00CC43491A3A9E6C006118A9 /* Keybase.app */; productType = "com.apple.product-type.application"; }; + A1B2C3D4E5F60718293A4B08 /* KeybaseFSKit */ = { + isa = PBXNativeTarget; + buildConfigurationList = A1B2C3D4E5F60718293A4B09 /* Build configuration list for PBXNativeTarget "KeybaseFSKit" */; + buildPhases = ( + A1B2C3D4E5F60718293A4B0A /* Sources */, + A1B2C3D4E5F60718293A4B0B /* Frameworks */, + A1B2C3D4E5F60718293A4B0C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = KeybaseFSKit; + productName = KeybaseFSKit; + productReference = A1B2C3D4E5F60718293A4B02 /* KeybaseFSKit.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -690,6 +739,18 @@ }; }; }; + A1B2C3D4E5F60718293A4B08 = { + CreatedOnToolsVersion = 16.0; + DevelopmentTeam = 99229SGT5K; + SystemCapabilities = { + com.apple.ApplicationGroups.Mac = { + enabled = 1; + }; + com.apple.Keychain = { + enabled = 1; + }; + }; + }; }; }; buildConfigurationList = 00CC43441A3A9E6C006118A9 /* Build configuration list for PBXProject "Keybase" */; @@ -712,6 +773,7 @@ 001C6B361B24F8630048F086 /* Share */, 009E13651B27B39300C72B28 /* Action */, 004624B01B2917AB00E218AC /* FinderSync */, + A1B2C3D4E5F60718293A4B08 /* KeybaseFSKit */, 00AA750E1B27635D0069A6CC /* Tests */, ); }; @@ -769,6 +831,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + A1B2C3D4E5F60718293A4B0C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -997,6 +1066,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + A1B2C3D4E5F60718293A4B0A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A1B2C3D4E5F60718293A4B01 /* KeybaseFSKitExtension.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -1156,6 +1233,46 @@ }; name = Release; }; + A1B2C3D4E5F60718293A4B0D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = FSKit/FSKit.entitlements; + CODE_SIGN_IDENTITY = "Mac Developer"; + COMBINE_HIDPI_IMAGES = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = FSKit/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "keybase.Keybase.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = KeybaseFSKit; + PROVISIONING_PROFILE = ""; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + A1B2C3D4E5F60718293A4B0E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = FSKit/FSKit.entitlements; + CODE_SIGN_IDENTITY = "Mac Developer"; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = FSKit/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "keybase.Keybase.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = KeybaseFSKit; + PROVISIONING_PROFILE = ""; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; 0057B0B81AE57A4900BFB0E7 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = DF54D7C94B2D7756B784F46B /* Pods-keybase.Helper.debug.xcconfig */; @@ -1507,6 +1624,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + A1B2C3D4E5F60718293A4B09 /* Build configuration list for PBXNativeTarget "KeybaseFSKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A1B2C3D4E5F60718293A4B0D /* Debug */, + A1B2C3D4E5F60718293A4B0E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 00CC43411A3A9E6C006118A9 /* Project object */; diff --git a/osx/Scripts/README.md b/osx/Scripts/README.md index 06cbabf157c2..1885b254f94c 100644 --- a/osx/Scripts/README.md +++ b/osx/Scripts/README.md @@ -10,7 +10,7 @@ ### Build the Installer You probably want to bump the version of the installer in both the "Bundle version" (CFBundleVersion) -and the "Bundle version string, short" (CFBundleShortVersionString) in [Installer/Info.plist](/osx/Installer/Info.plist). If you updated KBFuse, bump those versions too. +and the "Bundle version string, short" (CFBundleShortVersionString) in [Installer/Info.plist](/osx/Installer/Info.plist). If you updated the FSKit module, bump those versions too. Install the developer certificate chain by cloning the `keybase://team/keybase.keymasters/apple-dev` repo, and installing the @@ -52,6 +52,6 @@ Upload the build KeybaseInstaller-x.y.z-darwin.tgz to the s3://prerelease.keybas Update the scripts that reference the older version such to include this version: * [packaging/desktop/package_darwin.sh](/packaging/desktop/package_darwin.sh) -* [packaging/desktop/kbfuse.sh](/packaging/desktop/kbfuse.sh) +* [packaging/desktop/fskit.sh](/packaging/desktop/fskit.sh) The installer is bundled into the Keybase.app, in `Keybase.app/Contents/Resources/KeybaseInstaller.app`. diff --git a/osx/Scripts/build.sh b/osx/Scripts/build.sh index 2de4b04cedc9..19bb68483c02 100755 --- a/osx/Scripts/build.sh +++ b/osx/Scripts/build.sh @@ -59,8 +59,11 @@ echo "" cd "$build_dest" -echo "Copying kbfuse.bundle..." -ditto ../../Fuse/kbfuse.bundle $app_name.app/Contents/Resources/kbfuse.bundle +echo "Building FSKit module scaffold..." +"$dir/../FSKit/build.sh" + +echo "Copying FSKit module..." +ditto ../../FSKit/keybase.fs $app_name.app/Contents/Resources/keybase.fs echo " diff --git a/osx/Scripts/versions.sh b/osx/Scripts/versions.sh index 308e7a1e5efd..e3483a93616a 100755 --- a/osx/Scripts/versions.sh +++ b/osx/Scripts/versions.sh @@ -17,18 +17,18 @@ helper_list=$dir/../Helper/Info.plist kb_helper_version="`/usr/libexec/plistBuddy -c "Print :CFBundleShortVersionString" $helper_list`" kb_helper_build="`/usr/libexec/plistBuddy -c "Print :CFBundleVersion" $helper_list`" -fuse_plist=$dir/../Fuse/kbfuse.bundle/Contents/Info.plist -fuse_version="`/usr/libexec/plistBuddy -c "Print :CFBundleShortVersionString" $fuse_plist`" -fuse_build="`/usr/libexec/plistBuddy -c "Print :CFBundleVersion" $fuse_plist`" +fskit_plist=$dir/../FSKit/keybase.fs/Contents/Info.plist +fskit_version="`/usr/libexec/plistBuddy -c "Print :CFBundleShortVersionString" $fskit_plist`" +fskit_build="`/usr/libexec/plistBuddy -c "Print :CFBundleVersion" $fskit_plist`" echo "Version (Build):" echo " keybase.Helper: $kb_helper_version ($kb_helper_build)" -echo " Fuse3: $fuse_version ($fuse_build)" +echo " FSKit: $fskit_version ($fskit_build)" echo "" echo "Updating plist..." /usr/libexec/plistBuddy -c "Set :KBHelperVersion '${kb_helper_version}'" $plist /usr/libexec/plistBuddy -c "Set :KBHelperBuild '${kb_helper_build}'" $plist -/usr/libexec/plistBuddy -c "Set :KBFuseVersion '${fuse_version}'" $plist -/usr/libexec/plistBuddy -c "Set :KBFuseBuild '${fuse_build}'" $plist +/usr/libexec/plistBuddy -c "Set :FSKitVersion '${fskit_version}'" $plist +/usr/libexec/plistBuddy -c "Set :FSKitBuild '${fskit_build}'" $plist echo " " diff --git a/packaging/desktop/fskit.sh b/packaging/desktop/fskit.sh new file mode 100644 index 000000000000..7a9dc9773e60 --- /dev/null +++ b/packaging/desktop/fskit.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +# +# This script stages the Keybase FSKit filesystem module from the native +# installer bundle into /Library/Filesystems for CI/build hosts. +# + +set -e -u -o pipefail # Fail on error + +dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +cd "$dir" + +tmp_dir="/tmp/desktop-fskit" +# TODO build and publish arm64 version +installer_url="https://prerelease.keybase.io/darwin-package/KeybaseInstaller-1.1.94-darwin.tgz" + +if [ "$EUID" -ne 0 ]; then + echo "Please run as root" + exit 1 +fi + +rm -rf "$tmp_dir" +mkdir -p "$tmp_dir" +cd "$tmp_dir" + +echo "Downloading installer from $installer_url" +curl -J -L -Ss "$installer_url" | tar zx + +echo "Installing FSKit module..." +bundle="$tmp_dir/KeybaseInstaller.app/Contents/Resources/keybase.fs" +dest="/Library/Filesystems/keybase.fs" +rm -rf "$dest" +cp -R "$bundle" "$dest" diff --git a/packaging/desktop/package_darwin.sh b/packaging/desktop/package_darwin.sh index cf4645c0f981..c86aca4236d0 100755 --- a/packaging/desktop/package_darwin.sh +++ b/packaging/desktop/package_darwin.sh @@ -114,7 +114,7 @@ shared_support_dir="$out_dir/Keybase.app/Contents/SharedSupport" resources_dir="$out_dir/Keybase.app/Contents/Resources/" # TODO build and publish an arm64 version -# The KeybaseInstaller.app installs KBFuse, keybase.Helper, services and CLI via a native app +# The KeybaseInstaller.app installs the FSKit driver, keybase.Helper, services and CLI via a native app installer_url="https://prerelease.keybase.io/darwin-package/KeybaseInstaller-1.1.94-darwin.tgz" # KeybaseUpdater.app is the native updater UI (prompt dialogs) updater_url="https://prerelease.keybase.io/darwin-package/KeybaseUpdater-1.0.7-darwin.tgz" @@ -193,7 +193,7 @@ get_deps() { ( echo "Using local updater binpath: $updater_binpath" cp "$updater_binpath" . - echo "Using installer from $installer_url" +echo "Using installer (with FSKit driver) from $installer_url" curl -J -L -Ss "$installer_url" | tar zx echo "Using updater from $updater_url" diff --git a/protocol/avdl/keybase1/install.avdl b/protocol/avdl/keybase1/install.avdl index bb83931bcfc1..32dc6f49b04f 100644 --- a/protocol/avdl/keybase1/install.avdl +++ b/protocol/avdl/keybase1/install.avdl @@ -77,13 +77,14 @@ protocol install { } /* - Return status for Fuse install. + Return status for the filesystem driver install on this platform. + On macOS this now refers to the FSKit-based driver. If you specify bundleVersion, it will tell you if an upgrade is needed. */ FuseStatus fuseStatus(int sessionID, string bundleVersion); /* - Install Fuse. + Install the filesystem driver (FSKit on macOS). */ InstallResult installFuse(); diff --git a/shared/constants/fs/platform-specific.desktop.tsx b/shared/constants/fs/platform-specific.desktop.tsx index b9e372eded7c..3b7862865a6a 100644 --- a/shared/constants/fs/platform-specific.desktop.tsx +++ b/shared/constants/fs/platform-specific.desktop.tsx @@ -46,7 +46,11 @@ const fuseStatusToActions = return } - if (status.kextStarted) { + const driverActive = + status.installStatus === T.RPCGen.InstallStatus.installed && + (status.kextStarted || (status.mountInfos?.length ?? 0) > 0) + + if (driverActive) { useFSState.getState().dispatch.setDriverStatus({ ...Constants.emptyDriverStatusEnabled, dokanOutdated: status.installAction === T.RPCGen.InstallAction.upgrade, @@ -56,7 +60,7 @@ const fuseStatusToActions = useFSState.getState().dispatch.setDriverStatus(Constants.emptyDriverStatusDisabled) } - if (status.kextStarted && previousStatusType === T.FS.DriverStatusType.Disabled) { + if (driverActive && previousStatusType === T.FS.DriverStatusType.Disabled) { useFSState .getState() .dispatch.dynamic.openPathInSystemFileManagerDesktop?.(T.FS.stringToPath('/keybase'))