From: Dmitry Volyntsev Date: Wed, 26 Nov 2025 03:08:33 +0000 (-0800) Subject: FS: fixed path restoration in fs.mkdir() and friends on error. X-Git-Tag: 0.9.5~11 X-Git-Url: http://www.kaiwu.me/postgresql/commit/static/gitweb.js?a=commitdiff_plain;h=8ba1ecb414f6f6c77cb88fe2458869097ae6bb5f;p=njs.git FS: fixed path restoration in fs.mkdir() and friends on error. --- diff --git a/external/njs_fs_module.c b/external/njs_fs_module.c index 33fb6111..903c095c 100644 --- a/external/njs_fs_module.c +++ b/external/njs_fs_module.c @@ -2955,7 +2955,7 @@ njs_fs_make_path(njs_vm_t *vm, char *path, mode_t md, njs_bool_t recursive, case EACCES: case ENOTDIR: case EPERM: - goto failed; + goto failed_restore; case EEXIST: default: @@ -2963,13 +2963,13 @@ njs_fs_make_path(njs_vm_t *vm, char *path, mode_t md, njs_bool_t recursive, if (ret == 0) { if (!S_ISDIR(sb.st_mode)) { err = ENOTDIR; - goto failed; + goto failed_restore; } break; } - goto failed; + goto failed_restore; } } @@ -2983,6 +2983,12 @@ njs_fs_make_path(njs_vm_t *vm, char *path, mode_t md, njs_bool_t recursive, return NJS_OK; +failed_restore: + + if (p != end) { + path[p - path] = '/'; + } + failed: return njs_fs_error(vm, "mkdir", strerror(err), path, err, retval); diff --git a/external/qjs_fs_module.c b/external/qjs_fs_module.c index 9117072b..af929db3 100644 --- a/external/qjs_fs_module.c +++ b/external/qjs_fs_module.c @@ -518,7 +518,7 @@ qjs_fs_make_path(JSContext *cx, char *path, mode_t md, int recursive) case EACCES: case ENOTDIR: case EPERM: - goto failed; + goto failed_restore; case EEXIST: default: @@ -526,13 +526,13 @@ qjs_fs_make_path(JSContext *cx, char *path, mode_t md, int recursive) if (ret == 0) { if (!S_ISDIR(sb.st_mode)) { err = ENOTDIR; - goto failed; + goto failed_restore; } break; } - goto failed; + goto failed_restore; } } @@ -546,6 +546,12 @@ qjs_fs_make_path(JSContext *cx, char *path, mode_t md, int recursive) return JS_UNDEFINED; +failed_restore: + + if (p != end) { + path[p - path] = '/'; + } + failed: return qjs_fs_error(cx, "mkdir", strerror(err), path, err); diff --git a/test/fs/mkdir_error_path.t.mjs b/test/fs/mkdir_error_path.t.mjs new file mode 100644 index 00000000..1d8b33c8 --- /dev/null +++ b/test/fs/mkdir_error_path.t.mjs @@ -0,0 +1,122 @@ +/*--- +includes: [compareArray.js, compatFs.js] +flags: [async] +---*/ + +var dname = `${test_dir}/mkdir_error_path`; + +let stages = []; + +var testSync = () => new Promise((resolve, reject) => { + try { + try { fs.unlinkSync(dname + '/a/b'); } catch (e) {} + try { fs.rmdirSync(dname + '/a'); } catch (e) {} + try { fs.rmdirSync(dname); } catch (e) {} + + fs.mkdirSync(dname + '/a', {recursive: true}); + fs.writeFileSync(dname + '/a/b', 'blocking file'); + + try { + fs.mkdirSync(dname + '/a/b/c/d', {recursive: true}); + reject(new Error('Expected ENOTDIR')); + } catch (e) { + if (e.code != 'ENOTDIR') { + reject(e); + } + + if (!e.path.includes('/c/d')) { + reject(new Error('Path truncated: ' + e.path)); + } + } + + fs.unlinkSync(dname + '/a/b'); + fs.rmdirSync(dname + '/a'); + fs.rmdirSync(dname); + + stages.push("mkdirSync"); + + resolve(); + } catch (e) { + reject(e); + } +}); + +var testCallback = () => new Promise((resolve, reject) => { + try { + try { fs.unlinkSync(dname + '/a/b'); } catch (e) {} + try { fs.rmdirSync(dname + '/a'); } catch (e) {} + try { fs.rmdirSync(dname); } catch (e) {} + + fs.mkdir(dname + '/a', {recursive: true}, (err) => { + if (err) { + reject(err); + return; + } + + fs.writeFile(dname + '/a/b', 'blocking file', (err) => { + if (err) { + reject(err); + return; + } + + fs.mkdir(dname + '/a/b/c/d', {recursive: true}, (err) => { + if (!err || err.code != 'ENOTDIR') { + reject(new Error('Expected ENOTDIR')); + return; + } + + if (!err.path.includes('/c/d')) { + reject(new Error('Path truncated: ' + err.path)); + return; + } + + fs.unlinkSync(dname + '/a/b'); + fs.rmdirSync(dname + '/a'); + fs.rmdirSync(dname); + + stages.push("mkdir"); + + resolve(); + }); + }); + }); + } catch (e) { + reject(e); + } +}); + +let testFsp = () => Promise.resolve() +.then(() => { + try { fs.unlinkSync(dname + '/a/b'); } catch (e) {} + try { fs.rmdirSync(dname + '/a'); } catch (e) {} + try { fs.rmdirSync(dname); } catch (e) {} +}) +.then(() => fsp.mkdir(dname + '/a', {recursive: true})) +.then(() => fsp.writeFile(dname + '/a/b', 'blocking file')) +.then(() => fsp.mkdir(dname + '/a/b/c/d', {recursive: true})) +.then(() => { + throw new Error('Expected ENOTDIR'); +}) +.catch((e) => { + if (e.code != 'ENOTDIR') { + throw e; + } + + if (!e.path.includes('/c/d')) { + throw new Error('Path truncated: ' + e.path); + } +}) +.then(() => fsp.unlink(dname + '/a/b')) +.then(() => fsp.rmdir(dname + '/a')) +.then(() => fsp.rmdir(dname)) +.then(() => { + stages.push("fsp.mkdir"); +}) + +let p = Promise.resolve() + .then(testSync) + .then(testCallback) + .then(testFsp) + .then(() => assert.compareArray(stages, ['mkdirSync', 'mkdir', 'fsp.mkdir'])) + +p.then($DONE, $DONE);