Skip to content

Commit ce330cf

Browse files
committed
test: add test for mkdir recursive on read-only fs
mkdirSync with { recursive: true } on a read-only filesystem incorrectly throws ENOENT instead of the expected EROFS error. This test demonstrates the bug by mounting a read-only tmpfs via sudo and verifying the error code. The test requires passwordless sudo to mount a read-only tmpfs. In environments without this capability (such as ci.nodejs.org), the test is skipped with a warning message explaining why. Refs: nodejs#47098 Refs: nodejs#48105 Signed-off-by: Kenny Yeo <[email protected]>
1 parent 43d5058 commit ce330cf

1 file changed

Lines changed: 32 additions & 0 deletions

File tree

test/parallel/test-fs-mkdir.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
'use strict';
2323
const common = require('../common');
2424
const assert = require('assert');
25+
const child_process = require('child_process');
2526
const fs = require('fs');
2627
const path = require('path');
2728
const { isMainThread } = require('worker_threads');
@@ -184,6 +185,37 @@ function nextdir() {
184185
}));
185186
}
186187

188+
// `mkdirp` when folder was readonly.
189+
if (common.isLinux) {
190+
const roTmpfsPath = path.join(tmpdir.path, 'ro-tmpfs');
191+
fs.mkdirSync(roTmpfsPath);
192+
193+
const { status, stderr } = child_process.spawnSync(
194+
'sudo', ['-n', 'mount', '-t', 'tmpfs', '-o', 'ro', 'tmpfs', roTmpfsPath],
195+
{ stdio: 'pipe', encoding: 'utf8' }
196+
);
197+
198+
if (status !== 0) {
199+
console.warn(
200+
'Cannot test EROFS: passwordless sudo required to mount read-only tmpfs. ' +
201+
`Mount failed with status ${status}: ${stderr.trim()}`
202+
);
203+
} else {
204+
const pathname = path.join(roTmpfsPath, nextdir());
205+
assert.throws(
206+
() => { fs.mkdirSync(pathname, { recursive: true }); },
207+
{
208+
code: 'EROFS',
209+
message: /EROFS:.*mkdir/,
210+
name: 'Error',
211+
syscall: 'mkdir',
212+
}
213+
);
214+
child_process.spawnSync('sudo', ['-n', 'umount', roTmpfsPath]);
215+
}
216+
fs.rmdirSync(roTmpfsPath);
217+
}
218+
187219
// `mkdirp` when path is a file.
188220
{
189221
const pathname = tmpdir.resolve(nextdir(), nextdir());

0 commit comments

Comments
 (0)