问题描述
用户在菜单中点击"检查更新"并下载安装时,macOS 上报错:
Cross-device link (os error 18)
根因分析
tauri-plugin-updater 2.10.1 的 macOS 安装逻辑 (updater.rs install_inner) 使用 tempfile::Builder::new().tempdir() 在系统临时目录(/var/folders/...)创建备份和解压目录,然后用 std::fs::rename 移动 .app bundle。
当 App 不在系统卷时(外置硬盘、非系统分区、DMG 等),rename 无法跨文件系统操作,触发 POSIX EXDEV 错误。
插件只对 PermissionDenied 做了 admin fallback(AppleScript mv -f),EXDEV 没有 fallback,错误直接暴露给前端。
出错位置
tauri-plugin-updater/src/updater.rs:1255
std::fs::rename(&self.extract_path, tmp_backup_dir.path().join("current_app"))
上游状态
解决方案
对 tauri-plugin-updater 的 macOS install_inner 做两处补丁,通过 [patch.crates-io] 集成。
补丁 1:临时目录优先同卷创建
将 tempdir() 改为 tempdir_in(app_parent),优先在 .app 同目录创建临时文件,确保 rename 在同一文件系统内操作。父目录不可写时回退到系统 temp。
// 原始
let tmp_backup_dir = tempfile::Builder::new()
.prefix("tauri_current_app")
.tempdir()?;
let tmp_extract_dir = tempfile::Builder::new()
.prefix("tauri_updated_app")
.tempdir()?;
// 修改后
let app_parent = self.extract_path.parent();
let tmp_backup_dir = app_parent
.and_then(|p| {
tempfile::Builder::new()
.prefix(".tauri_current_app")
.tempdir_in(p)
.ok()
})
.map_or_else(
|| tempfile::Builder::new().prefix("tauri_current_app").tempdir(),
Ok,
)?;
let tmp_extract_dir = app_parent
.and_then(|p| {
tempfile::Builder::new()
.prefix(".tauri_updated_app")
.tempdir_in(p)
.ok()
})
.map_or_else(
|| tempfile::Builder::new().prefix("tauri_updated_app").tempdir(),
Ok,
)?;
补丁 2:EXDEV 走管理员 fallback
将 EXDEV 错误归入 admin authorization 路径,利用 AppleScript mv -f 天然支持跨设备移动。
// 原始
let need_authorization = if let Err(err) = move_result {
if err.kind() == std::io::ErrorKind::PermissionDenied {
true
} else {
std::fs::remove_dir_all(tmp_extract_dir.path()).ok();
return Err(err.into());
}
} else {
false
};
// 修改后
let need_authorization = if let Err(err) = move_result {
if err.kind() == std::io::ErrorKind::PermissionDenied
|| err.raw_os_error() == Some(18) // libc::EXDEV
{
true
} else {
std::fs::remove_dir_all(tmp_extract_dir.path()).ok();
return Err(err.into());
}
} else {
false
};
场景覆盖
| 场景 |
补丁 1 效果 |
补丁 2 效果 |
| App 在外置盘、用户有写权限 |
tempdir_in 同卷成功,rename 正常 |
不触发 |
App 在 /Applications、无写权限 |
tempdir_in 失败,回退系统 temp |
EXDEV → admin mv -f |
| App 在系统盘(正常情况) |
tempdir_in 同卷成功 |
不触发 |
集成方式
在 src-tauri/Cargo.toml 中添加:
[patch.crates-io]
tauri-plugin-updater = { git = "https://github.com/TiyAgents/plugins-workspace", branch = "fix/macos-exdev-updater" }
上游 #1983 合并发布后移除 patch。
影响范围
- 仅影响 macOS 更新安装流程
- 不影响 Windows / Linux 更新路径
- 不影响检查更新、下载流程
问题描述
用户在菜单中点击"检查更新"并下载安装时,macOS 上报错:
根因分析
tauri-plugin-updater 2.10.1的 macOS 安装逻辑 (updater.rsinstall_inner) 使用tempfile::Builder::new().tempdir()在系统临时目录(/var/folders/...)创建备份和解压目录,然后用std::fs::rename移动.appbundle。当 App 不在系统卷时(外置硬盘、非系统分区、DMG 等),
rename无法跨文件系统操作,触发 POSIXEXDEV错误。插件只对
PermissionDenied做了 admin fallback(AppleScriptmv -f),EXDEV 没有 fallback,错误直接暴露给前端。出错位置
上游状态
2.10.1,尚无修复版本解决方案
对
tauri-plugin-updater的 macOSinstall_inner做两处补丁,通过[patch.crates-io]集成。补丁 1:临时目录优先同卷创建
将
tempdir()改为tempdir_in(app_parent),优先在.app同目录创建临时文件,确保rename在同一文件系统内操作。父目录不可写时回退到系统 temp。补丁 2:EXDEV 走管理员 fallback
将 EXDEV 错误归入 admin authorization 路径,利用 AppleScript
mv -f天然支持跨设备移动。场景覆盖
tempdir_in同卷成功,rename正常/Applications、无写权限tempdir_in失败,回退系统 tempmv -ftempdir_in同卷成功集成方式
在
src-tauri/Cargo.toml中添加:上游 #1983 合并发布后移除 patch。
影响范围