Skip to content

Commit 11b60ed

Browse files
committed
src: unlink compile cache temp file on persist failure
Signed-off-by: Lazizbek Ergashev <lazerg2@gmail.com>
1 parent 5fc9f1f commit 11b60ed

2 files changed

Lines changed: 50 additions & 0 deletions

File tree

src/compile_cache.cc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,23 @@ void CompileCacheHandler::Persist() {
464464
continue;
465465
}
466466
Debug(" -> %s\n", mkstemp_req.path);
467+
468+
// Avoid leaking the descriptor or the temporary file if persistence
469+
// does not complete below.
470+
bool tmp_fd_needs_close = true;
471+
bool tmp_renamed = false;
472+
auto cleanup_tmp_file = OnScopeLeave([&]() {
473+
if (tmp_fd_needs_close) {
474+
uv_fs_t req;
475+
uv_fs_close(nullptr, &req, mkstemp_req.result, nullptr);
476+
uv_fs_req_cleanup(&req);
477+
}
478+
if (!tmp_renamed) {
479+
uv_fs_t req;
480+
uv_fs_unlink(nullptr, &req, mkstemp_req.path, nullptr);
481+
uv_fs_req_cleanup(&req);
482+
}
483+
});
467484
Debug("[compile cache] writing cache for %s %s to temporary file %s [%d "
468485
"%d %d "
469486
"%d %d]...",
@@ -496,6 +513,7 @@ void CompileCacheHandler::Persist() {
496513
auto cleanup_close =
497514
OnScopeLeave([&close_req]() { uv_fs_req_cleanup(&close_req); });
498515
err = uv_fs_close(nullptr, &close_req, mkstemp_req.result, nullptr);
516+
tmp_fd_needs_close = false;
499517

500518
if (err < 0) {
501519
Debug("failed: %s\n", uv_strerror(err));
@@ -521,6 +539,7 @@ void CompileCacheHandler::Persist() {
521539
Debug("failed: %s\n", uv_strerror(err));
522540
continue;
523541
}
542+
tmp_renamed = true;
524543
Debug("success\n");
525544
entry->persisted = true;
526545
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
3+
// This tests that a compile cache persistence failure does not leave the
4+
// temporary file used to write it behind.
5+
6+
const common = require('../common');
7+
if (common.isWindows)
8+
common.skip('no RLIMIT_FSIZE on Windows');
9+
if (process.config.variables.node_shared)
10+
common.skip('SIGXFSZ signal handler not installed in shared library mode');
11+
12+
const assert = require('assert');
13+
const fs = require('fs');
14+
const path = require('path');
15+
const { spawnSync } = require('child_process');
16+
const tmpdir = require('../common/tmpdir');
17+
18+
tmpdir.refresh();
19+
const cacheDir = tmpdir.resolve('compile_cache');
20+
fs.writeFileSync(tmpdir.resolve('fixture.cjs'), 'module.exports = 42;\n');
21+
22+
const [cmd, opts] = common.escapePOSIXShell`ulimit -f 0 && "${process.execPath}" -e "require('./fixture.cjs')"`;
23+
opts.env.NODE_COMPILE_CACHE = cacheDir;
24+
opts.cwd = tmpdir.path;
25+
const result = spawnSync('/bin/sh', ['-c', cmd], opts);
26+
assert.strictEqual(result.status, 0, result.stderr.toString());
27+
28+
const subdirs = fs.readdirSync(cacheDir);
29+
assert.strictEqual(subdirs.length, 1);
30+
const leftover = fs.readdirSync(path.join(cacheDir, subdirs[0]));
31+
assert.deepStrictEqual(leftover, []);

0 commit comments

Comments
 (0)