nodejs / nodejs/node

fs: cpSync copyDir fast path can terminate on filesystem errors

Offen
#63,970 1 Kommentar 2 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

fs
Vorherrschende Sprache
JavaScript
Sterne
122k
Forks
37.3k
Ø Merge
4 T. 2 Std.
Gemergte PRs (30 T.)
283

Beschreibung

Version

v22.17.0 and later; verified against v27.0.0-pre built from current main.

Platform
Microsoft Windows NT 10.0.26100.0 x64
Subsystem

fs

What steps will reproduce the bug?

Run this script on Windows:

'use strict';

const assert = require('node:assert');
const { execFileSync, spawnSync } = require('node:child_process');
const {
  cpSync,
  existsSync,
  mkdirSync,
  rmSync,
  writeFileSync,
} = require('node:fs');
const { tmpdir } = require('node:os');
const { join } = require('node:path');

function run(command, args) {
  return execFileSync(command, args, {
    encoding: 'utf8',
    stdio: ['ignore', 'pipe', 'pipe'],
  });
}

function currentWindowsUser() {
  return run('whoami', []).trim();
}

function restrictDirectory(dir) {
  run('icacls', [dir, '/deny', `${currentWindowsUser()}:(OI)(CI)(RX)`]);
}

function restoreDirectory(dir) {
  if (existsSync(dir)) {
    run('icacls', [dir, '/remove:d', currentWindowsUser()]);
  }
}

if (process.argv[2] === 'child') {
  assert.throws(() => {
    cpSync(process.argv[3], process.argv[4], { recursive: true });
  });
  process.exit(0);
}

const root = join(tmpdir(), `node-cpsync-${process.pid}`);
const src = join(root, 'src');
const dest = join(root, 'dest');
const restrictedDir = join(src, 'restricted');

mkdirSync(restrictedDir, { recursive: true });
writeFileSync(join(src, 'readable.txt'), 'readable\n');
writeFileSync(join(restrictedDir, 'blocked.txt'), 'blocked\n');

restrictDirectory(restrictedDir);

try {
  const child = spawnSync(process.execPath, [__filename, 'child', src, dest], {
    encoding: 'utf8',
  });
  console.log({
    status: child.status,
    signal: child.signal,
    stdout: child.stdout,
    stderr: child.stderr,
  });
} finally {
  restoreDirectory(restrictedDir);
  rmSync(root, { recursive: true, force: true });
}

The important part is that fs.cpSync(src, dest, { recursive: true }) is called without a filter option, so it uses the native copyDir fast path.

How often does it reproduce? Is there a required condition?

It reproduces when the native fs.cpSync() copyDir fast path encounters a filesystem error during directory iteration, path canonicalization, or file type checks.

The fast path is used for recursive directory copies when no filter option is provided. Passing filter: () => true avoids this path and falls back to the JavaScript implementation.

What is the expected behavior? Why is that the expected behavior?

fs.cpSync() should report the filesystem failure as a JavaScript exception that callers can catch with try/catch or assert.throws().

Filesystem APIs should convert native filesystem failures into JavaScript errors instead of terminating the process.

What do you see instead?

Some std::filesystem calls in the native copyDir implementation use throwing overloads. When those operations fail, the C++ exception can bypass Node's normal error conversion.

Instead of a catchable JavaScript exception, the process can terminate in the native layer.

Additional information

This appears to come from src/node_file.cc's CpSyncCopyDir implementation. Some calls already use std::error_code, but others use throwing std::filesystem overloads, including directory iteration and file type/path checks.

The fix is to use non-throwing std::filesystem overloads throughout the copyDir path and convert each failure with ThrowStdErrException.

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Reproduziere den Windows-Fehler mit dem Skript im Issue und untersuche anschließend src/node_file.cc und die Implementierung von CpSyncCopyDir. Verfolge die Verzeichnisiteration, die Pfadkanonisierung und die Prüfungen des Dateityps und stelle sicher, dass Dateisystemfehler zu abfangbaren JavaScript-Ausnahmen werden, statt den Prozess zu beenden.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
cpp, javascript, node.js
Bereich
backend, operating-systems
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Ruhig
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
64/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.