diff --git a/lib/internal/fs/rimraf.js b/lib/internal/fs/rimraf.js index 675c2448c4568c..b2307fd14ead99 100644 --- a/lib/internal/fs/rimraf.js +++ b/lib/internal/fs/rimraf.js @@ -231,7 +231,7 @@ function _rmdirSync(path, options, originalErr) { } catch (err) { if (err.code === 'ENOENT') return; - if (err.code === 'ENOTDIR') + if (err.code === 'EACCES' || err.code === 'ENOTDIR') throw originalErr; if (notEmptyErrorCodes.has(err.code)) { diff --git a/test/parallel/test-recursive-rm-busy-loop-windows.js b/test/parallel/test-recursive-rm-busy-loop-windows.js new file mode 100644 index 00000000000000..7ca7f75bc1f820 --- /dev/null +++ b/test/parallel/test-recursive-rm-busy-loop-windows.js @@ -0,0 +1,55 @@ +'use strict'; +const common = require('../common'); + +// This test ensures that recursive rm throws EACCES instead of +// going into a busy-loop for this scenario: +// https://github.com/nodejs/node/issues/34580 + +const assert = require('assert'); +const fs = require('fs'); +const { execSync } = require('child_process'); +const tmpdir = require('../common/tmpdir'); +const { join } = require('path'); + +if (common.isIBMi) + common.skip('IBMi has a different access permission mechanism'); + +if (!(common.isWindows || process.getuid() !== 0)) + common.skip('Test is not supposed to be run as root.'); + +tmpdir.refresh(); + +function makeDirectoryReadOnly(dir) { + if (common.isWindows) { + execSync(`icacls ${dir} /deny "everyone:(OI)(CI)(DE,DC)"`); + } else { + fs.chmodSync(dir, 0o444); + } +} + +function makeDirectoryWritable(dir) { + if (common.isWindows) { + execSync(`icacls ${dir} /remove:d "everyone"`); + } else { + fs.chmodSync(dir, 0o777); + } +} + +function rmdirRecursiveSync() { + const root = fs.mkdtempSync(tmpdir.path); + + const middle = join(root, 'middle'); + fs.mkdirSync(middle); + fs.mkdirSync(join(middle, 'leaf')); // Make `middle` non-empty + makeDirectoryReadOnly(middle); + + try { + assert.throws(() => { + fs.rmSync(root, { recursive: true }); + }, /EACCES/); + } finally { + makeDirectoryWritable(middle); + } +} + +rmdirRecursiveSync();