Repository navigation
Measurements on the exFAT hard-link publication failure #7774
Replies: 1 comment 1 reply
2026-10-19 复核:桌面版 44.7.0 仍然没有修;并附一个已在真实安装上跑通的热补丁English TL;DR — Re-verified against 1. 用户为什么普遍误判成"中文路径坏了"失败信息里的路径多半带中文(工作区在移动硬盘/共享盘上),但纯 ASCII 路径同样失败,一字不差: 不需要 Harness 也能复现(同一台机器,两个卷各一次): node -e "const fs=require('fs');const d=process.argv[1];fs.mkdirSync(d+'/.t.tmpdir',{recursive:true});fs.writeFileSync(d+'/.t.tmpdir/t.tmp','x');try{fs.linkSync(d+'/.t.tmpdir/t.tmp',d+'/t');console.log('LINK OK')}catch(e){console.log('LINK',e.code,e.errno,e.syscall)}" 'E:\work\tmp'
# exFAT -> LINK EISDIR -4068 link | C:\...(NTFS) -> LINK OK
2. 已在生产安装上验证可用的最小补丁(就地、字节等长、可回滚)在硬链接失败且错误码表明该卷不支持硬链接时,退到独占创建(同样是不覆盖已有文件的 no-replace 语义): if (createIfAbsent !== undefined) {
try {
await linkFile(tempPath, absolutePath)
} catch (error) {
if (isHardLinkUnavailable(error)) {
try {
const publishHandle = await open(absolutePath, 'wx', 0o600)
try {
await publishHandle.writeFile(content, { encoding: 'utf8', ...signal ? { signal } : {} })
await publishHandle.sync()
} finally { await publishHandle.close() }
} catch (fallbackError) {
await throwGuardedCreateFailure(fallbackError, absolutePath, createIfAbsent.displayPath, inspectPublicationTarget)
}
} else {
await throwGuardedCreateFailure(error, absolutePath, createIfAbsent.displayPath, inspectPublicationTarget)
}
}
}
function isHardLinkUnavailable(error: unknown): boolean {
return ['EISDIR', 'EPERM', 'ENOSYS', 'ENOTSUP', 'EOPNOTSUPP'].includes((error as NodeJS.ErrnoException)?.code)
}自检结果(把补丁后的字节从已安装的 app.asar 里重新读出来加载,不是从工作副本):
补充两点给官方:Electron 的 3. 建议直接用你们仓库里已有的先例,而不是新发明
export async function publishNewFileWin32(existing: string, replacement: string): Promise<void> {
const api = await win32()
const ok = api.moveFileExW(toNamespacedPath(existing), toNamespacedPath(replacement), MOVEFILE_WRITE_THROUGH)
if (ok === 0) throw win32Error('MoveFileExW', api.getLastError(), existing, replacement)
}
另外同型缺陷还在 最后,建议在 Host 启动时对当前工作卷做一次能力探测并明确提示(我在全仓库里找不到任何
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
exFAT volume: hard-link publication fails, rename fallback verified
writecannot create a new file on a volume whose filesystem has no hard links —exFAT, FAT32, some network shares — while updating an existing file and
editcontinue to work. The defect and its root cause are covered in #3884 and #5704
(
EISDIRoriginates in Node/libuv; upstream: nodejs/node#65817).This is what a fixed (non-removable) exFAT volume actually does.
dsh@deepseek-ai/dsh-fs-localThe failure tracks the filesystem, not whether the drive is a USB device.
The failure
Deterministic for every new file, in the workspace root and in subdirectories
alike. Overwriting an existing file reports
Updated file, andeditsucceeds.The primitives
With the staged temp file already written, against the same target path, in one run:
mkdirof the staging directory,open(tempPath, 'wx'),writeFile,chmodandsyncall succeed in the same run.fs.renameSyncequally succeeds into asubdirectory that had to be created first.
Falling back to rename
Attempting the link, and degrading to
renameonly while the target is stillabsent, behaves as intended on this volume — one case per row:
rename; file published; content correctThe collision path is deliberately outside the fallback:
EEXISTis not ahard-link-unsupported signal, so a genuine race still reports contention rather
than overwriting.
NTFS
On an NTFS volume the same call succeeds and the two paths share an inode — a
real hard link, not a copy. The existing create-if-absent publication therefore
behaves as before on NTFS, and the fallback never engages there.
Drive roots fail independently of the filesystem
writeFileAtomicbegins withmkdir(dirname(target), { recursive: true }). OnWindows a drive root already exists, but
mkdirreports it asEPERM, andrecursive: truetolerates onlyEEXIST:Not a hard-link problem and not exFAT-specific — it reproduces on both filesystems,
so it blocks writing a new file directly at a drive root on any Windows volume. It
also propagates into any workaround that completes the write through the same
service, because that
mkdirruns there too.Other capabilities of the volume
fs.symlinkSyncalso fails withEISDIR. ADSH_HOMEplaced on such a volumecannot boot:
dsh-app-boot'shealProfilesModuleFallbacksymlinks the install intothe profile during composition.
All reactions