[njs] Improved fs.mkdir() to support recursive directory creation.
Dmitry Volyntsev
xeioex at nginx.com
Fri Jul 24 16:25:22 UTC 2020
details: https://hg.nginx.org/njs/rev/cb2ff67e595d
branches:
changeset: 1480:cb2ff67e595d
user: Artem S. Povalyukhin <artem.povaluhin at gmail.com>
date: Wed Jul 15 15:51:06 2020 +0300
description:
Improved fs.mkdir() to support recursive directory creation.
diffstat:
src/njs_fs.c | 112 ++++++++++++++++++++++++++++++++++++++++----
test/js/fs_promises_008.js | 79 +++++++++++++++++++++++++++++++
test/njs_expect_test.exp | 3 +
3 files changed, 183 insertions(+), 11 deletions(-)
diffs (230 lines):
diff -r 354318f9b00f -r cb2ff67e595d src/njs_fs.c
--- a/src/njs_fs.c Fri Jul 24 14:02:04 2020 +0000
+++ b/src/njs_fs.c Wed Jul 15 15:51:06 2020 +0300
@@ -70,6 +70,9 @@ static njs_int_t njs_fs_error(njs_vm_t *
static njs_int_t njs_fs_result(njs_vm_t *vm, njs_value_t *result,
njs_index_t calltype, const njs_value_t* callback, njs_uint_t nargs);
+static njs_int_t njs_fs_make_path(njs_vm_t *vm, const char *path, mode_t md,
+ njs_bool_t recursive, njs_value_t *retval);
+
static int njs_fs_flags(njs_vm_t *vm, njs_value_t *value, int default_flags);
static mode_t njs_fs_mode(njs_vm_t *vm, njs_value_t *value,
mode_t default_mode);
@@ -828,18 +831,8 @@ njs_fs_mkdir(njs_vm_t *vm, njs_value_t *
return NJS_ERROR;
}
- if (njs_is_true(&recursive)) {
- njs_type_error(vm, "\"options.recursive\" is not supported");
- return NJS_ERROR;
- }
-
- njs_set_undefined(&retval);
-
- ret = mkdir(file_path, md);
- if (njs_slow_path(ret != 0)) {
- ret = njs_fs_error(vm, "mkdir", strerror(errno), path, errno,
+ ret = njs_fs_make_path(vm, file_path, md, njs_is_true(&recursive),
&retval);
- }
if (ret == NJS_OK) {
return njs_fs_result(vm, &retval, calltype, callback, 1);
@@ -1138,6 +1131,103 @@ njs_fs_fd_read(njs_vm_t *vm, int fd, njs
}
+static njs_int_t
+njs_fs_make_path(njs_vm_t *vm, const char *path, mode_t md,
+ njs_bool_t recursive, njs_value_t *retval)
+{
+ size_t size;
+ ssize_t length;
+ njs_int_t ret;
+ const char *p, *prev;
+ njs_value_t value;
+ struct stat sb;
+ char path_buf[MAXPATHLEN];
+
+ njs_set_undefined(retval);
+
+ if (!recursive) {
+ ret = mkdir(path, md);
+ if (ret != 0) {
+ goto failed;
+ }
+
+ return NJS_OK;
+ }
+
+ ret = stat(path, &sb);
+ if (ret == 0) {
+ if (!S_ISDIR(sb.st_mode)) {
+ errno = ENOTDIR;
+ goto failed;
+ }
+
+ return NJS_OK;
+ }
+
+ if (errno != ENOENT) {
+ goto failed;
+ }
+
+ p = path;
+ prev = p;
+
+ for ( ;; ) {
+ p = strchr(prev + 1, '/');
+ if (p == NULL) {
+ break;
+ }
+
+ if (njs_slow_path((p - path) > MAXPATHLEN)) {
+ njs_internal_error(vm, "too large path");
+ return NJS_OK;
+ }
+
+ memcpy(&path_buf[prev - path], &path[prev - path], p - prev);
+ path_buf[p - path] = '\0';
+
+ ret = stat(path_buf, &sb);
+ if (ret == 0) {
+ if (!S_ISDIR(sb.st_mode)) {
+ errno = ENOTDIR;
+ goto failed;
+ }
+
+ } else {
+ ret = mkdir(path_buf, md);
+ if (ret != 0) {
+ goto failed;
+ }
+ }
+
+ path_buf[p - path] = '/';
+ prev = p;
+ }
+
+ ret = mkdir(path, md);
+ if (ret != 0 && errno != EEXIST) {
+ goto failed;
+ }
+
+ return NJS_OK;
+
+failed:
+
+ size = njs_strlen(path);
+ length = njs_utf8_length((u_char *) path, size);
+ if (njs_slow_path(length < 0)) {
+ length = 0;
+ }
+
+ ret = njs_string_new(vm, &value, (u_char *) path, size, length);
+ if (ret != NJS_OK) {
+ return NJS_ERROR;
+ }
+
+ return njs_fs_error(vm, "mkdir", strerror(errno), &value, errno,
+ retval);
+}
+
+
static int
njs_fs_flags(njs_vm_t *vm, njs_value_t *value, int default_flags)
{
diff -r 354318f9b00f -r cb2ff67e595d test/js/fs_promises_008.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/js/fs_promises_008.js Wed Jul 15 15:51:06 2020 +0300
@@ -0,0 +1,79 @@
+var fs = require('fs');
+var fsp = fs.promises;
+var dname = './build/test/fs_promises_αβγ_008/';
+var path = 'one/two/three/αβγ';
+
+var wipePath = (root, path, nofail) => {
+ path
+ .split('/')
+ .map((x, i, a) => {
+ return root + a.slice(0, i + 1).join('/');
+ })
+ .reverse()
+ .map((dir) => {
+ try {
+ fs.rmdirSync(dir);
+ } catch (e) {
+ if (!nofail) {
+ throw e;
+ }
+ }
+ });
+};
+
+var testSync = () => new Promise((resolve, reject) => {
+ try {
+ wipePath(dname, path + '/' + path, true);
+ fs.rmdirSync(dname);
+ } catch (e) {
+ }
+
+ try {
+ fs.mkdirSync(dname);
+
+ fs.mkdirSync(dname, { recursive: true });
+ fs.mkdirSync(dname + '/', { recursive: true });
+ fs.mkdirSync(dname + '////', { recursive: true });
+
+ fs.mkdirSync(dname + path, { recursive: true });
+ wipePath(dname, path);
+
+ fs.mkdirSync(dname + '////' + path + '////' + path + '////', { recursive: true });
+ wipePath(dname, path + '/' + path);
+
+ try {
+ fs.mkdirSync(dname + path, { recursive: true, mode: 0 });
+ } catch (e) {
+ if (e.code != 'EACCES') {
+ reject(e);
+ }
+ }
+ wipePath(dname, path, true);
+
+ try {
+ fs.mkdirSync(dname + path, { recursive: true });
+ fs.writeFileSync(dname + path + '/one', 'not dir');
+ fs.mkdirSync(dname + path + '/' + path, { recursive: true });
+ } catch (e) {
+ if (e.code != 'ENOTDIR') {
+ reject(e);
+ }
+ }
+ fs.unlinkSync(dname + path + '/one');
+ wipePath(dname, path);
+
+ fs.rmdirSync(dname);
+ resolve();
+ } catch (e) {
+ reject(e);
+ }
+});
+
+Promise.resolve()
+.then(testSync)
+.then(() => {
+ console.log('test recursive fs.mkdirSync');
+})
+.catch((e) => {
+ console.log('test failed recursive fs.mkdirSync', JSON.stringify(e));
+})
diff -r 354318f9b00f -r cb2ff67e595d test/njs_expect_test.exp
--- a/test/njs_expect_test.exp Fri Jul 24 14:02:04 2020 +0000
+++ b/test/njs_expect_test.exp Wed Jul 15 15:51:06 2020 +0300
@@ -1140,3 +1140,6 @@ njs_run {"./test/js/fs_promises_007.js"}
"test fs.readdirSync
test fs.readdir
test fsp.readdir"
+
+njs_run {"./test/js/fs_promises_008.js"} \
+"test recursive fs.mkdirSync"
More information about the nginx-devel
mailing list