(sourceDir, destDir, verifier = verifyCandidate)
| 379 | } |
| 380 | |
| 381 | function installWindowsBinaryAtomically(sourceDir, destDir, verifier = verifyCandidate) { |
| 382 | // Authenticate the source binary before it can contend for the cache. |
| 383 | verifier(path.join(sourceDir, WINDOWS_BINARY_NAME)); |
| 384 | const lock = acquireWindowsBinaryLock(destDir); |
| 385 | let operationError = null; |
| 386 | try { |
| 387 | // A contender may have completed while this process waited. Its executable |
| 388 | // wins and is never moved or deleted. |
| 389 | if (windowsBinaryReady(destDir, verifier)) return; |
| 390 | |
| 391 | const transaction = crypto.randomBytes(16).toString('hex'); |
| 392 | const stagedPaths = new Map(); |
| 393 | const stagedDigests = new Map(); |
| 394 | const backups = new Map(); |
| 395 | const publishedDigests = new Map(); |
| 396 | try { |
| 397 | for (const name of [WINDOWS_BINARY_NAME]) { |
| 398 | const staged = path.join(destDir, `.cbm-pair-stage-${transaction}-${name}`); |
| 399 | fs.copyFileSync(path.join(sourceDir, name), staged, fs.constants.COPYFILE_EXCL); |
| 400 | fs.chmodSync(staged, 0o755); |
| 401 | stagedPaths.set(name, staged); |
| 402 | stagedDigests.set(name, fileSha256(staged)); |
| 403 | } |
| 404 | |
| 405 | for (const name of [WINDOWS_BINARY_NAME]) { |
| 406 | const target = path.join(destDir, name); |
| 407 | const status = fs.lstatSync(target, { throwIfNoEntry: false }); |
| 408 | if (!status) continue; |
| 409 | if (!status.isFile() || status.isSymbolicLink()) { |
| 410 | throw new Error(`refusing unsafe Windows package-cache target: ${target}`); |
| 411 | } |
| 412 | const backup = path.join(destDir, `.cbm-pair-backup-${transaction}-${name}`); |
| 413 | fs.renameSync(target, backup); |
| 414 | backups.set(name, backup); |
| 415 | } |
| 416 | |
| 417 | for (const name of [WINDOWS_BINARY_NAME]) { |
| 418 | const target = path.join(destDir, name); |
| 419 | fs.renameSync(stagedPaths.get(name), target); |
| 420 | stagedPaths.delete(name); |
| 421 | publishedDigests.set(name, stagedDigests.get(name)); |
| 422 | } |
| 423 | if (!windowsBinaryReady(destDir, verifier)) { |
| 424 | throw new Error('published Windows package-cache binary failed verification'); |
| 425 | } |
| 426 | for (const backup of backups.values()) { |
| 427 | try { fs.unlinkSync(backup); } catch (_) { /* valid binary is already committed */ } |
| 428 | } |
| 429 | return; |
| 430 | } catch (err) { |
| 431 | // If a non-cooperating contender nevertheless published a valid binary, |
| 432 | // preserve that winner. Otherwise remove only our exact staged bytes and |
| 433 | // restore the prior files when their target names are still absent. |
| 434 | if (windowsBinaryReady(destDir, verifier)) { |
| 435 | for (const backup of backups.values()) { |
| 436 | try { fs.unlinkSync(backup); } catch (_) { /* winner remains authoritative */ } |
| 437 | } |
| 438 | return; |
no test coverage detected