diff --git a/go/protocol/keybase1/kbfsmount.go b/go/protocol/keybase1/kbfsmount.go index 7bc7e956c534..ff526fd8771d 100644 --- a/go/protocol/keybase1/kbfsmount.go +++ b/go/protocol/keybase1/kbfsmount.go @@ -12,6 +12,9 @@ import ( type GetCurrentMountDirArg struct { } +type WaitForMountsArg struct { +} + type GetPreferredMountDirsArg struct { } @@ -28,6 +31,7 @@ type GetKBFSPathInfoArg struct { type KbfsMountInterface interface { GetCurrentMountDir(context.Context) (string, error) + WaitForMounts(context.Context) (bool, error) GetPreferredMountDirs(context.Context) ([]string, error) GetAllAvailableMountDirs(context.Context) ([]string, error) SetCurrentMountDir(context.Context, string) error @@ -48,6 +52,16 @@ func KbfsMountProtocol(i KbfsMountInterface) rpc.Protocol { return }, }, + "WaitForMounts": { + MakeArg: func() interface{} { + var ret [1]WaitForMountsArg + return &ret + }, + Handler: func(ctx context.Context, args interface{}) (ret interface{}, err error) { + ret, err = i.WaitForMounts(ctx) + return + }, + }, "GetPreferredMountDirs": { MakeArg: func() interface{} { var ret [1]GetPreferredMountDirsArg @@ -111,6 +125,11 @@ func (c KbfsMountClient) GetCurrentMountDir(ctx context.Context) (res string, er return } +func (c KbfsMountClient) WaitForMounts(ctx context.Context) (res bool, err error) { + err = c.Cli.Call(ctx, "keybase.1.kbfsMount.WaitForMounts", []interface{}{WaitForMountsArg{}}, &res, 0*time.Millisecond) + return +} + func (c KbfsMountClient) GetPreferredMountDirs(ctx context.Context) (res []string, err error) { err = c.Cli.Call(ctx, "keybase.1.kbfsMount.GetPreferredMountDirs", []interface{}{GetPreferredMountDirsArg{}}, &res, 0*time.Millisecond) return diff --git a/go/service/kbfs_mount.go b/go/service/kbfs_mount.go index f2bb3c0d9c0e..42d5f1130e58 100644 --- a/go/service/kbfs_mount.go +++ b/go/service/kbfs_mount.go @@ -4,6 +4,10 @@ package service import ( + "os" + "path/filepath" + "time" + "golang.org/x/net/context" "github.com/keybase/client/go/libkb" @@ -27,6 +31,43 @@ func (h *KBFSMountHandler) GetCurrentMountDir(ctx context.Context) (res string, return h.G().Env.GetMountDir() } +const waitForDirectMountTimeout = 10 * time.Second +const waitForDirectMountPollInterval = time.Second + +func (h *KBFSMountHandler) WaitForMounts(ctx context.Context) (active bool, err error) { + ctx, cancel := context.WithTimeout(ctx, waitForDirectMountTimeout) + defer cancel() + mount, err := h.GetCurrentMountDir(ctx) + if err != nil { + return false, err + } + directMountFileToCheck := filepath.Join(mount, ".kbfs_error") + ticker := time.NewTicker(waitForDirectMountPollInterval) + defer ticker.Stop() + directMountFound, preferredMountFound := false, false + for !directMountFound || !preferredMountFound { + select { + case <-ticker.C: + if !directMountFound { + fi, err := os.Stat(directMountFileToCheck) + if err == nil && !fi.IsDir() { + directMountFound = true + } + // Not check os.IsNotExist here because it can be permission + // error too. So just wait it out. + } + if !preferredMountFound { + if len(libkb.FindPreferredKBFSMountDirs()) > 0 { + preferredMountFound = true + } + } + case <-ctx.Done(): + return false, nil + } + } + return true, nil +} + func (h *KBFSMountHandler) GetPreferredMountDirs(ctx context.Context) (res []string, err error) { res = libkb.FindPreferredKBFSMountDirs() directMount, err := h.G().Env.GetMountDir() diff --git a/protocol/avdl/keybase1/kbfsmount.avdl b/protocol/avdl/keybase1/kbfsmount.avdl index 5ad83d840406..36e8a89d0b4d 100644 --- a/protocol/avdl/keybase1/kbfsmount.avdl +++ b/protocol/avdl/keybase1/kbfsmount.avdl @@ -4,6 +4,8 @@ protocol kbfsMount { @lint("ignore") string GetCurrentMountDir(); @lint("ignore") + boolean WaitForMounts(); + @lint("ignore") array GetPreferredMountDirs(); @lint("ignore") array GetAllAvailableMountDirs(); diff --git a/protocol/bin/enabled-calls.json b/protocol/bin/enabled-calls.json index 0b60f9e31d76..f0df81531a79 100644 --- a/protocol/bin/enabled-calls.json +++ b/protocol/bin/enabled-calls.json @@ -185,6 +185,7 @@ "keybase.1.install.installKBFS": {"promise": true}, "keybase.1.install.uninstallKBFS": {"promise": true}, "keybase.1.kbfsMount.GetCurrentMountDir": {"promise": true}, + "keybase.1.kbfsMount.WaitForMounts": {"promise": true}, "keybase.1.kbfsMount.GetPreferredMountDirs": {"promise": true}, "keybase.1.kbfsMount.GetKBFSPathInfo": {"promise": true}, "keybase.1.login.accountDelete": {"promise": true}, diff --git a/protocol/json/keybase1/kbfsmount.json b/protocol/json/keybase1/kbfsmount.json index 177703aa6fb0..54d402b40da6 100644 --- a/protocol/json/keybase1/kbfsmount.json +++ b/protocol/json/keybase1/kbfsmount.json @@ -8,6 +8,11 @@ "response": "string", "lint": "ignore" }, + "WaitForMounts": { + "request": [], + "response": "boolean", + "lint": "ignore" + }, "GetPreferredMountDirs": { "request": [], "response": { diff --git a/shared/actions/fs/platform-specific.desktop.tsx b/shared/actions/fs/platform-specific.desktop.tsx index 2153c5d8f951..4e217fa02e5b 100644 --- a/shared/actions/fs/platform-specific.desktop.tsx +++ b/shared/actions/fs/platform-specific.desktop.tsx @@ -2,7 +2,6 @@ import * as I from 'immutable' import * as ConfigGen from '../config-gen' import * as FsGen from '../fs-gen' import * as Saga from '../../util/saga' -import * as Config from '../../constants/config' import * as RPCTypes from '../../constants/types/rpc-gen' import * as Types from '../../constants/types/fs' import * as Constants from '../../constants/fs' @@ -145,24 +144,6 @@ const openPathInSystemFileManager = (state: TypedState, action: FsGen.OpenPathIn } }) as Promise) -function waitForMount(attempt: number) { - return new Promise((resolve, reject) => { - // Read the KBFS path waiting for files to exist, which means it's mounted - // TODO: should handle current mount directory - fs.readdir(`${Config.defaultKBFSPath}${Config.defaultPrivatePrefix}`, (err, files) => { - if (!err && files.length > 0) { - resolve(true) - } else if (attempt > 15) { - reject(new Error(`${Config.defaultKBFSPath} is unavailable. Please try again.`)) - } else { - setTimeout(() => { - waitForMount(attempt + 1).then(resolve, reject) - }, 1000) - } - }) - }) -} - const fuseStatusToUninstallExecPath = isWindows ? (status: RPCTypes.FuseStatus) => { const field = @@ -257,7 +238,7 @@ const driverEnableFuse = async (_: TypedState, action: FsGen.DriverEnablePayload ] } else { await RPCTypes.installInstallKBFSRpcPromise() // restarts kbfsfuse - await waitForMount(0) + await RPCTypes.kbfsMountWaitForMountsRpcPromise() return FsGen.createRefreshDriverStatus() } } diff --git a/shared/constants/types/rpc-gen.tsx b/shared/constants/types/rpc-gen.tsx index 72fa72b714cb..cf7ce3756a1c 100644 --- a/shared/constants/types/rpc-gen.tsx +++ b/shared/constants/types/rpc-gen.tsx @@ -843,6 +843,10 @@ export type MessageTypes = { inParam: void outParam: Array | null } + 'keybase.1.kbfsMount.WaitForMounts': { + inParam: void + outParam: Boolean + } 'keybase.1.logUi.log': { inParam: {readonly level: LogLevel; readonly text: Text} outParam: void @@ -3285,6 +3289,7 @@ export const installUninstallKBFSRpcPromise = (params: MessageTypes['keybase.1.i export const kbfsMountGetCurrentMountDirRpcPromise = (params: MessageTypes['keybase.1.kbfsMount.GetCurrentMountDir']['inParam'], waitingKey?: WaitingKey) => new Promise((resolve, reject) => engine()._rpcOutgoing({method: 'keybase.1.kbfsMount.GetCurrentMountDir', params, callback: (error, result) => (error ? reject(error) : resolve(result)), waitingKey})) export const kbfsMountGetKBFSPathInfoRpcPromise = (params: MessageTypes['keybase.1.kbfsMount.GetKBFSPathInfo']['inParam'], waitingKey?: WaitingKey) => new Promise((resolve, reject) => engine()._rpcOutgoing({method: 'keybase.1.kbfsMount.GetKBFSPathInfo', params, callback: (error, result) => (error ? reject(error) : resolve(result)), waitingKey})) export const kbfsMountGetPreferredMountDirsRpcPromise = (params: MessageTypes['keybase.1.kbfsMount.GetPreferredMountDirs']['inParam'], waitingKey?: WaitingKey) => new Promise((resolve, reject) => engine()._rpcOutgoing({method: 'keybase.1.kbfsMount.GetPreferredMountDirs', params, callback: (error, result) => (error ? reject(error) : resolve(result)), waitingKey})) +export const kbfsMountWaitForMountsRpcPromise = (params: MessageTypes['keybase.1.kbfsMount.WaitForMounts']['inParam'], waitingKey?: WaitingKey) => new Promise((resolve, reject) => engine()._rpcOutgoing({method: 'keybase.1.kbfsMount.WaitForMounts', params, callback: (error, result) => (error ? reject(error) : resolve(result)), waitingKey})) export const loginAccountDeleteRpcPromise = (params: MessageTypes['keybase.1.login.accountDelete']['inParam'], waitingKey?: WaitingKey) => new Promise((resolve, reject) => engine()._rpcOutgoing({method: 'keybase.1.login.accountDelete', params, callback: (error, result) => (error ? reject(error) : resolve(result)), waitingKey})) export const loginDeprovisionRpcPromise = (params: MessageTypes['keybase.1.login.deprovision']['inParam'], waitingKey?: WaitingKey) => new Promise((resolve, reject) => engine()._rpcOutgoing({method: 'keybase.1.login.deprovision', params, callback: (error, result) => (error ? reject(error) : resolve(result)), waitingKey})) export const loginGetConfiguredAccountsRpcPromise = (params: MessageTypes['keybase.1.login.getConfiguredAccounts']['inParam'], waitingKey?: WaitingKey) => new Promise((resolve, reject) => engine()._rpcOutgoing({method: 'keybase.1.login.getConfiguredAccounts', params, callback: (error, result) => (error ? reject(error) : resolve(result)), waitingKey})) diff --git a/shared/fs/banner/system-file-manager-integration-banner/index.tsx b/shared/fs/banner/system-file-manager-integration-banner/index.tsx index 2d995ca68bb3..b483d773ad46 100644 --- a/shared/fs/banner/system-file-manager-integration-banner/index.tsx +++ b/shared/fs/banner/system-file-manager-integration-banner/index.tsx @@ -183,7 +183,7 @@ const JustEnabled = ({onDismiss}: JustEnabledProps) => { const displayingMountDir = preferredMountDirs.get(0) || '' const dispatch = Container.useDispatch() const open = displayingMountDir - ? () => dispatch(FsGen.createOpenPathInSystemFileManager({path: displayingMountDir})) + ? () => dispatch(FsGen.createOpenLocalPathInSystemFileManager({localPath: displayingMountDir})) : undefined return (