Files
NetMesh/electron/bridges/localFilePublish.cjs
zhaolei 3c72efcb7f
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
[Init] Initial commit - NetMesh terminal manager
2026-09-13 18:24:01 +08:00

74 lines
3.1 KiB
JavaScript

"use strict";
const fs = require("node:fs");
// Publish a prepared regular file without replacing any destination entry.
// Hardlinks make complete bytes visible atomically. FAT-like filesystems use an
// exclusively created handle instead: partial contents can be visible there,
// but no pathname cleanup may remove a concurrent writer's replacement.
// Resolves with the published inode identity ({ dev, ino, size }) so callers can
// verify an open destination handle before later metadata stamping.
async function publishLocalFileExclusive(source, target, assertNotCancelled = () => {}, preparedHandle) {
assertNotCancelled();
try {
await fs.promises.link(source, target);
// The hardlink shares the published inode; stat either name.
const linkedStat = await fs.promises.lstat(source);
return { dev: linkedStat.dev, ino: linkedStat.ino, size: linkedStat.size };
} catch (error) {
if (!["ENOTSUP", "EOPNOTSUPP", "ENOSYS", "EPERM", "EACCES", "EXDEV"].includes(error?.code)) throw error;
}
let input;
let output;
let failure;
let publishedIdentity;
try {
input = preparedHandle ?? await fs.promises.open(source, "r");
const stat = await input.stat();
assertNotCancelled();
output = await fs.promises.open(target, "wx", stat.mode & 0o7777);
const buffer = Buffer.allocUnsafe(1024 * 1024);
let position = 0;
while (position < stat.size) {
assertNotCancelled();
const { bytesRead } = await input.read(buffer, 0, Math.min(buffer.length, stat.size - position), position);
if (!bytesRead) throw new Error("Prepared local file ended before publication completed");
let written = 0;
while (written < bytesRead) {
assertNotCancelled();
const { bytesWritten } = await output.write(buffer, written, bytesRead - written, position + written);
if (!bytesWritten) throw new Error("Local publication made no write progress");
written += bytesWritten;
}
position += bytesRead;
}
assertNotCancelled();
// Restore metadata after writes (which may clear special permission bits),
// through the owned handle rather than a potentially replaced pathname.
await output.chmod(stat.mode & 0o7777);
await output.utimes(stat.atime, stat.mtime);
const ownedStat = await output.stat();
const targetStat = await fs.promises.lstat(target);
if (!targetStat.isFile() || targetStat.dev !== ownedStat.dev || targetStat.ino !== ownedStat.ino) {
throw new Error("Local download target changed during replacement");
}
publishedIdentity = { dev: ownedStat.dev, ino: ownedStat.ino, size: ownedStat.size };
} catch (error) {
failure = error;
} finally {
for (const handle of [output, preparedHandle ? undefined : input]) {
try { await handle?.close(); } catch (error) { failure ??= error; }
}
}
if (failure) {
// Once exclusive creation succeeded, leave the partial destination intact.
// The caller must retain its complete prepared file and any original backup.
if (output) failure.localPublicationIncomplete = true;
throw failure;
}
return publishedIdentity;
}
module.exports = { publishLocalFileExclusive };