mount fixes

This commit is contained in:
FTCHD
2026-07-12 04:01:12 +03:00
parent 956f58c65b
commit d96cfdee00
3 changed files with 50 additions and 34 deletions
+46 -4
View File
@@ -29,6 +29,7 @@ import {
} from './requests'
const RE_BACKSLASH = /\\/g
const RE_DASH = /-/g
const RE_PATH_SEPARATOR = /[/\\]/
const RE_WINDOWS_EXTENDED_PATH = /(\/\/\?\/|\\\\\?\\)/
const RE_WINDOWS_DRIVE_LETTER = /^[a-zA-Z]:$/
@@ -410,13 +411,52 @@ async function startMountInner({
mountOptions.volname = `${sourcePath}-${Math.random().toString(36).substring(2, 3).toUpperCase()}`
}
// Only genuinely global flags can ride the connection string (`global.` keys) — mount and
// VFS options go through the dedicated mountOpt/vfsOpt params below instead. Filter options
// have no per-mount channel at all (rclone builds the mount's VFS on a background context,
// so per-call filters never reach it) and are kept here as a no-op until upstream fixes it.
const mergedOptions = {
...mountOptions,
...(options.config || {}),
...(options.vfs || {}),
...(options.filter || {}),
}
const vfsOptions = { ...(options.vfs || {}) }
// mountOpt/vfsOpt take JSON keyed by Go field names, so rekey the flag-name groups
// ("vfs_cache_mode" → "CacheMode") via the options/info registry before sending. Unknown
// keys pass through untouched — rclone ignores unrecognized fields.
const toStructOptions = (
flags: Record<string, FlagValue>,
infos: { Name: string; FieldName: string }[] | undefined
) => {
const fieldNames = new Map((infos || []).map((info) => [info.Name, info.FieldName]))
return JSON.stringify(
Object.fromEntries(
Object.entries(flags).map(([key, value]) => [
fieldNames.get(key.replace(RE_DASH, '_')) || key,
value,
])
)
)
}
let structOptions: { mountOpt?: string; vfsOpt?: string } = {}
if (Object.keys(mountOptions).length > 0 || Object.keys(vfsOptions).length > 0) {
const optionsInfo = await pRetry(
async () =>
await rclone('/options/info', { params: { query: { blocks: 'mount,vfs' } } }),
RETRY_OPTIONS
)
structOptions = {
...(Object.keys(mountOptions).length > 0
? { mountOpt: toStructOptions(mountOptions, optionsInfo?.mount) }
: {}),
...(Object.keys(vfsOptions).length > 0
? { vfsOpt: toStructOptions(vfsOptions, optionsInfo?.vfs) }
: {}),
}
}
const { fullDirPath: srcFullDirPath, remoteName: srcRemoteName } = getFsInfo(source)
const srcOptions =
@@ -435,7 +475,8 @@ async function startMountInner({
remote: srcOptions,
}),
mountPoint: '*',
mount: 'nfsmount',
// No mountType — Windows uses rclone's default resolution (cmount/WinFsp)
...structOptions,
},
},
}),
@@ -567,7 +608,8 @@ async function startMountInner({
}
return mp
})(),
mount: 'nfsmount',
...(currentPlatform === 'macos' ? { mountType: 'nfsmount' } : {}),
...structOptions,
},
},
}),
+2 -28
View File
@@ -9,14 +9,7 @@ export async function needsMountPlugin() {
console.log('[needsMountPlugin]')
const currentPlatform = platform()
if (currentPlatform === 'macos') {
// check fuse-t or osxfuse
const hasFuseT = await exists('/Library/Application Support/fuse-t')
console.log('[needsMountPlugin] hasFuseT', hasFuseT)
const hasOsxFuse = await exists('/Library/Filesystems/macfuse.fs')
console.log('[needsMountPlugin] hasOsxFuse', hasOsxFuse)
return !hasFuseT && !hasOsxFuse
}
if (currentPlatform === 'windows') {
// check winfsp
const hasWinFsp =
@@ -25,6 +18,7 @@ export async function needsMountPlugin() {
console.log('[needsMountPlugin] hasWinFsp', hasWinFsp)
return !hasWinFsp
}
return false
}
@@ -32,26 +26,6 @@ export async function dialogGetMountPlugin() {
console.log('[dialogGetMountPlugin]')
const currentPlatform = platform()
if (currentPlatform === 'macos') {
// download fuse-t or osxfuse
const wantsDownload = await ask(
"Fuse-t is required on macOS to mount remotes. You can continue the operation once you're done with the installation.",
{
title: 'Fuse-t not installed',
kind: 'warning',
okLabel: 'Download',
cancelLabel: 'Cancel',
}
)
if (wantsDownload) {
const fuseInstallerUrl =
'https://github.com/macos-fuse-t/fuse-t/releases/download/1.0.49/fuse-t-macos-installer-1.0.49.pkg'
const localPath = `${await downloadDir()}/fuse-t-installer.pkg`
const installer = await (await fetch(fuseInstallerUrl)).arrayBuffer()
await writeFile(localPath, new Uint8Array(installer))
await revealItemInDir(localPath)
}
}
if (currentPlatform === 'windows') {
// download winfsp
const wantsDownload = await ask(
+2 -2
View File
@@ -534,9 +534,9 @@ export default function Mount() {
</Button>
</Tooltip>
<CommandInfoButton
content={`Mounts a remote as a local file system using FUSE.
content={`Mounts a remote as a local file system.
Mount allows you to access any of rclone's cloud storage systems as if they were a local folder on your computer. Files appear in your file browser and can be opened directly by applications. This requires FUSE support on your system (macFUSE on macOS, WinFsp on Windows).
Mount allows you to access any of rclone's cloud storage systems as if they were a local folder on your computer. Files appear in your file browser and can be opened directly by applications. On Windows this requires WinFsp; macOS mounts use the system's built-in NFS client, so no extra software is needed.
On Linux/macOS/FreeBSD, the mount point must be an empty existing directory. On Windows, you can mount to an unused drive letter, or to a path representing a nonexistent subdirectory of an existing parent directory.