diff --git a/src/node_file.cc b/src/node_file.cc index aedd8d09a7edad..6dd8ff5ce746c3 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -1751,6 +1751,7 @@ int MKDirpSync(uv_loop_t* loop, case UV_EACCES: case UV_ENOSPC: case UV_ENOTDIR: + case UV_EROFS: case UV_EPERM: { return err; } @@ -1830,6 +1831,7 @@ int MKDirpAsync(uv_loop_t* loop, } case UV_EACCES: case UV_ENOTDIR: + case UV_EROFS: case UV_EPERM: { req_wrap->continuation_data()->Done(err); break; diff --git a/test/parallel/test-fs-mkdir.js b/test/parallel/test-fs-mkdir.js index f7685c7de0a962..f123c0bb2f1b06 100644 --- a/test/parallel/test-fs-mkdir.js +++ b/test/parallel/test-fs-mkdir.js @@ -24,6 +24,7 @@ const common = require('../common'); const assert = require('assert'); const fs = require('fs'); const path = require('path'); +const child_process = require('child_process'); const { isMainThread } = require('worker_threads'); const tmpdir = require('../common/tmpdir'); @@ -173,6 +174,31 @@ function nextdir() { ); } +// mkdirpSync when interacting with read-only filesystem. +if (common.isLinux && process.getuid() === 0) // Mounting filesystem requires root privilege. +{ + const roTmpfsPath = path.join(tmpdir.path, 'ro-tmpfs'); + const pathname = path.join(roTmpfsPath, nextdir()); + + fs.mkdirSync(roTmpfsPath); + child_process.execSync(`sudo mount -t tmpfs -o ro tmpfs ${roTmpfsPath}`); + + try { + assert.throws( + () => { fs.mkdirSync(pathname, common.mustNotMutateObjectDeep({ recursive: true })); }, + { + code: 'EROFS', + message: /EROFS: .*mkdir/, + name: 'Error', + syscall: 'mkdir', + } + ); + } finally { + child_process.execSync(`sudo umount ${roTmpfsPath}`); + fs.rmdirSync(roTmpfsPath); + } +} + // `mkdirp` when folder does not yet exist. { const pathname = tmpdir.resolve(nextdir(), nextdir());