Last active
February 10, 2020 23:13
-
-
Save drsm/3d0402f41dc332b3c87bbe5dfcb7892e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# HG changeset patch | |
# User Artem S. Povalyukhin <artem.povaluhin@gmail.com> | |
# Date 1581374904 -10800 | |
# Tue Feb 11 01:48:24 2020 +0300 | |
# Node ID 43930d21f5c0283aad0a5527314040c356879738 | |
# Parent 5bd15bd3766cf2f9be0b66a21a9796b44212e306 | |
Added fs.symlink(), fs.unlink(), fs.realpath() and friends. | |
diff -r 5bd15bd3766c -r 43930d21f5c0 src/njs_fs.c | |
--- a/src/njs_fs.c Mon Feb 10 17:39:41 2020 +0300 | |
+++ b/src/njs_fs.c Tue Feb 11 01:48:24 2020 +0300 | |
@@ -528,6 +528,194 @@ done: | |
static njs_int_t | |
+njs_fs_symlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, | |
+ njs_index_t calltype) | |
+{ | |
+ njs_int_t ret; | |
+ const char *target_path, *file_path; | |
+ njs_value_t retval, *target, *path, *callback, *type; | |
+ | |
+ target = njs_arg(args, nargs, 1); | |
+ ret = njs_fs_path_arg(vm, &target_path, target, &njs_str_value("target")); | |
+ if (njs_slow_path(ret != NJS_OK)) { | |
+ return ret; | |
+ } | |
+ | |
+ path = njs_arg(args, nargs, 2); | |
+ ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path")); | |
+ if (njs_slow_path(ret != NJS_OK)) { | |
+ return ret; | |
+ } | |
+ | |
+ callback = NULL; | |
+ type = njs_arg(args, nargs, 3); | |
+ | |
+ if (calltype == NJS_FS_CALLBACK) { | |
+ callback = njs_arg(args, nargs, njs_min(nargs - 1, 4)); | |
+ if (!njs_is_function(callback)) { | |
+ njs_type_error(vm, "\"callback\" must be a function"); | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ if (type == callback) { | |
+ type = njs_value_arg(&njs_value_undefined); | |
+ } | |
+ } | |
+ | |
+ if (njs_slow_path(!njs_is_undefined(type) && !njs_is_string(type))) { | |
+ njs_type_error(vm, "\"type\" must be a string"); | |
+ return NJS_ERROR; | |
+ } | |
+ /* type is ignored */ | |
+ | |
+ ret = symlink(target_path, file_path); | |
+ if (njs_slow_path(ret != 0)) { | |
+ ret = njs_fs_error(vm, "symlink", strerror(errno), path, errno, &retval); | |
+ goto done; | |
+ } | |
+ | |
+ njs_set_undefined(&retval); | |
+ | |
+done: | |
+ | |
+ if (ret == NJS_OK) { | |
+ return njs_fs_result(vm, &retval, calltype, callback, 1); | |
+ } | |
+ | |
+ return NJS_ERROR; | |
+} | |
+ | |
+ | |
+static njs_int_t | |
+njs_fs_unlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, | |
+ njs_index_t calltype) | |
+{ | |
+ njs_int_t ret; | |
+ const char *file_path; | |
+ njs_value_t retval, *path, *callback; | |
+ | |
+ path = njs_arg(args, nargs, 1); | |
+ ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path")); | |
+ if (njs_slow_path(ret != NJS_OK)) { | |
+ return ret; | |
+ } | |
+ | |
+ callback = NULL; | |
+ | |
+ if (calltype == NJS_FS_CALLBACK) { | |
+ callback = njs_arg(args, nargs, 2); | |
+ if (!njs_is_function(callback)) { | |
+ njs_type_error(vm, "\"callback\" must be a function"); | |
+ return NJS_ERROR; | |
+ } | |
+ } | |
+ | |
+ ret = unlink(file_path); | |
+ if (njs_slow_path(ret != 0)) { | |
+ ret = njs_fs_error(vm, "unlink", strerror(errno), path, errno, &retval); | |
+ goto done; | |
+ } | |
+ | |
+ njs_set_undefined(&retval); | |
+ | |
+done: | |
+ | |
+ if (ret == NJS_OK) { | |
+ return njs_fs_result(vm, &retval, calltype, callback, 1); | |
+ } | |
+ | |
+ return NJS_ERROR; | |
+} | |
+ | |
+ | |
+static njs_int_t | |
+njs_fs_realpath(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, | |
+ njs_index_t calltype) | |
+{ | |
+ njs_int_t ret; | |
+ const char *file_path; | |
+ char *resolved_path; | |
+ njs_value_t encoding, retval, *path, *callback, *options; | |
+ njs_fs_encoding_t enc; | |
+ | |
+ static const njs_value_t string_encoding = njs_string("encoding"); | |
+ | |
+ path = njs_arg(args, nargs, 1); | |
+ ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path")); | |
+ if (njs_slow_path(ret != NJS_OK)) { | |
+ return ret; | |
+ } | |
+ | |
+ callback = NULL; | |
+ options = njs_arg(args, nargs, 2); | |
+ | |
+ if (calltype == NJS_FS_CALLBACK) { | |
+ callback = njs_arg(args, nargs, njs_min(nargs - 1, 3)); | |
+ if (!njs_is_function(callback)) { | |
+ njs_type_error(vm, "\"callback\" must be a function"); | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ if (options == callback) { | |
+ options = njs_value_arg(&njs_value_undefined); | |
+ } | |
+ } | |
+ | |
+ njs_set_undefined(&encoding); | |
+ | |
+ switch (options->type) { | |
+ case NJS_STRING: | |
+ encoding = *options; | |
+ break; | |
+ | |
+ case NJS_UNDEFINED: | |
+ break; | |
+ | |
+ default: | |
+ if (!njs_is_object(options)) { | |
+ njs_type_error(vm, "Unknown options type: \"%s\" " | |
+ "(a string or object required)", | |
+ njs_type_string(options->type)); | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ ret = njs_value_property(vm, options, njs_value_arg(&string_encoding), | |
+ &encoding); | |
+ if (njs_slow_path(ret == NJS_ERROR)) { | |
+ return ret; | |
+ } | |
+ } | |
+ | |
+ enc = njs_fs_encoding(vm, &encoding); | |
+ if (njs_slow_path(enc == NJS_FS_ENC_INVALID)) { | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ resolved_path = realpath(file_path, NULL); | |
+ if (njs_slow_path(resolved_path == NULL)) { | |
+ ret = njs_fs_error(vm, "realpath", strerror(errno), path, errno, &retval); | |
+ goto done; | |
+ } | |
+ | |
+ // XXX: utf8 paths | |
+ ret = njs_string_new(vm, &retval, (const u_char*) resolved_path, | |
+ strlen(resolved_path), strlen(resolved_path)); | |
+ free(resolved_path); | |
+ if (njs_slow_path(ret != NJS_OK)) { | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+done: | |
+ | |
+ if (ret == NJS_OK) { | |
+ return njs_fs_result(vm, &retval, calltype, callback, 2); | |
+ } | |
+ | |
+ return NJS_ERROR; | |
+} | |
+ | |
+ | |
+static njs_int_t | |
njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data) | |
{ | |
u_char *p, *end, *start; | |
@@ -880,6 +1068,30 @@ static const njs_object_prop_t njs_fs_p | |
.writable = 1, | |
.configurable = 1, | |
}, | |
+ | |
+ { | |
+ .type = NJS_PROPERTY, | |
+ .name = njs_string("symlink"), | |
+ .value = njs_native_function2(njs_fs_symlink, 0, NJS_FS_PROMISE), | |
+ .writable = 1, | |
+ .configurable = 1, | |
+ }, | |
+ | |
+ { | |
+ .type = NJS_PROPERTY, | |
+ .name = njs_string("unlink"), | |
+ .value = njs_native_function2(njs_fs_unlink, 0, NJS_FS_PROMISE), | |
+ .writable = 1, | |
+ .configurable = 1, | |
+ }, | |
+ | |
+ { | |
+ .type = NJS_PROPERTY, | |
+ .name = njs_string("realpath"), | |
+ .value = njs_native_function2(njs_fs_realpath, 0, NJS_FS_PROMISE), | |
+ .writable = 1, | |
+ .configurable = 1, | |
+ }, | |
}; | |
@@ -1039,6 +1251,53 @@ static const njs_object_prop_t njs_fs_o | |
.configurable = 1, | |
}, | |
+ { | |
+ .type = NJS_PROPERTY, | |
+ .name = njs_string("symlink"), | |
+ .value = njs_native_function2(njs_fs_symlink, 0, NJS_FS_CALLBACK), | |
+ .writable = 1, | |
+ .configurable = 1, | |
+ }, | |
+ | |
+ { | |
+ .type = NJS_PROPERTY, | |
+ .name = njs_string("symlinkSync"), | |
+ .value = njs_native_function2(njs_fs_symlink, 0, NJS_FS_DIRECT), | |
+ .writable = 1, | |
+ .configurable = 1, | |
+ }, | |
+ | |
+ { | |
+ .type = NJS_PROPERTY, | |
+ .name = njs_string("unlink"), | |
+ .value = njs_native_function2(njs_fs_unlink, 0, NJS_FS_CALLBACK), | |
+ .writable = 1, | |
+ .configurable = 1, | |
+ }, | |
+ | |
+ { | |
+ .type = NJS_PROPERTY, | |
+ .name = njs_string("unlinkSync"), | |
+ .value = njs_native_function2(njs_fs_unlink, 0, NJS_FS_DIRECT), | |
+ .writable = 1, | |
+ .configurable = 1, | |
+ }, | |
+ | |
+ { | |
+ .type = NJS_PROPERTY, | |
+ .name = njs_string("realpath"), | |
+ .value = njs_native_function2(njs_fs_realpath, 0, NJS_FS_CALLBACK), | |
+ .writable = 1, | |
+ .configurable = 1, | |
+ }, | |
+ | |
+ { | |
+ .type = NJS_PROPERTY, | |
+ .name = njs_string("realpathSync"), | |
+ .value = njs_native_function2(njs_fs_realpath, 0, NJS_FS_DIRECT), | |
+ .writable = 1, | |
+ .configurable = 1, | |
+ }, | |
}; | |
diff -r 5bd15bd3766c -r 43930d21f5c0 src/test/njs_interactive_test.c | |
--- a/src/test/njs_interactive_test.c Mon Feb 10 17:39:41 2020 +0300 | |
+++ b/src/test/njs_interactive_test.c Tue Feb 11 01:48:24 2020 +0300 | |
@@ -241,7 +241,14 @@ static njs_interactive_test_t njs_test[ | |
" 'writeFile'," | |
" 'writeFileSync'," | |
" 'appendFile'," | |
- " 'appendFileSync']" | |
+ " 'appendFileSync'," | |
+ " 'symlink'," | |
+ " 'symlinkSync'," | |
+ " 'unlink'," | |
+ " 'unlinkSync'," | |
+ " 'realpath'," | |
+ " 'realpathSync'," | |
+ "]" | |
".every(v=>{ try {fs[v]();} catch (e) { return e.stack.search(`fs.${v} `) >= 0}})" ENTER), | |
njs_str("true") }, | |
diff -r 5bd15bd3766c -r 43930d21f5c0 src/test/njs_unit_test.c | |
--- a/src/test/njs_unit_test.c Mon Feb 10 17:39:41 2020 +0300 | |
+++ b/src/test/njs_unit_test.c Tue Feb 11 01:48:24 2020 +0300 | |
@@ -16030,6 +16030,12 @@ static njs_unit_test_t njs_test[] = | |
"'writeFileSync'," | |
"'appendFile'," | |
"'appendFileSync'," | |
+ "'symlink'," | |
+ "'symlinkSync'," | |
+ "'unlink'," | |
+ "'unlinkSync'," | |
+ "'realpath'," | |
+ "'realpathSync'," | |
"]," | |
"test = (fname) =>" | |
"[undefined, null, false, NaN, Symbol(), {}, Object('/njs_unknown_path')]" | |
@@ -16052,6 +16058,9 @@ static njs_unit_test_t njs_test[] = | |
"'readFile'," | |
"'writeFile'," | |
"'appendFile'," | |
+ "'symlink'," | |
+ "'unlink'," | |
+ "'realpath'," | |
"];" | |
"func.every((x) => typeof fs[x] == 'function')"), | |
njs_str("true")}, | |
diff -r 5bd15bd3766c -r 43930d21f5c0 test/js/fs_promises_002.js | |
--- a/test/js/fs_promises_002.js Mon Feb 10 17:39:41 2020 +0300 | |
+++ b/test/js/fs_promises_002.js Tue Feb 11 01:48:24 2020 +0300 | |
@@ -1,6 +1,6 @@ | |
var fs = require('fs'); | |
var fsp = fs.promises; | |
-var fname = '/tmp/njs_fs_promises_002'; | |
+var fname = './build/test/fs_promises_002'; | |
var testSync = new Promise((resolve, reject) => { | |
var failed = false; | |
diff -r 5bd15bd3766c -r 43930d21f5c0 test/js/fs_promises_003.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/fs_promises_003.js Tue Feb 11 01:48:24 2020 +0300 | |
@@ -0,0 +1,108 @@ | |
+var fs = require('fs'); | |
+var fsp = fs.promises; | |
+var fname = './build/test/fs_promises_003'; | |
+ | |
+ | |
+var testSync = () => new Promise((resolve, reject) => { | |
+ try { | |
+ try { | |
+ fs.unlinkSync(fname); | |
+ } catch (e) { | |
+ void e; | |
+ } | |
+ | |
+ try { | |
+ fs.unlinkSync(fname); | |
+ throw new Error('unlinkSync error 1'); | |
+ } catch (e) { | |
+ if (e.syscall != 'unlink') { | |
+ throw e; | |
+ } | |
+ } | |
+ | |
+ fs.writeFileSync(fname, fname); | |
+ fs.unlinkSync(fname); | |
+ try { | |
+ fs.accessSync(fname); | |
+ reject(new Error('unlinkSync error 2')); | |
+ return; | |
+ } catch (e) { | |
+ void e; | |
+ } | |
+ | |
+ resolve(); | |
+ } catch (e) { | |
+ reject(e); | |
+ } | |
+}); | |
+ | |
+ | |
+var testCallback = () => new Promise((resolve, reject) => { | |
+ fs.unlink(fname, () => { | |
+ fs.unlink(fname, (err) => { | |
+ if (!err) { | |
+ reject(new Error('fs.unlink error 1')); | |
+ return; | |
+ } | |
+ if (err.syscall != 'unlink') { | |
+ reject(err); | |
+ return; | |
+ } | |
+ | |
+ fs.writeFileSync(fname, fname); | |
+ fs.unlink(fname, (err) => { | |
+ if (err) { | |
+ reject(err); | |
+ return; | |
+ } | |
+ try { | |
+ fs.accessSync(fname); | |
+ reject(new Error('fs.unlink error 2')); | |
+ return; | |
+ } catch (e) { | |
+ void e; | |
+ } | |
+ resolve(); | |
+ }); | |
+ }); | |
+ }); | |
+}); | |
+ | |
+ | |
+Promise.resolve() | |
+.then(testSync) | |
+.then(() => { | |
+ console.log('test fs.unlinkSync'); | |
+}) | |
+.catch((e) => { | |
+ console.log('test fs.unlinkSync failed', e); | |
+}) | |
+ | |
+.then(testCallback) | |
+.then(() => { | |
+ console.log('test fs.unlink'); | |
+}) | |
+.catch((e) => { | |
+ console.log('test fs.unlink failed', e); | |
+}) | |
+ | |
+.then(() => fsp.unlink(fname) | |
+ .catch(() => {})) | |
+.then(() => fsp.unlink(fname)) | |
+ .then(() => { throw new Error('fsp.unlink error 1'); }) | |
+.catch((e) => { if (e.syscall != 'unlink') { throw e; } }) | |
+ | |
+.then(() => { | |
+ fs.writeFileSync(fname, fname); | |
+ return fsp.unlink(fname); | |
+}) | |
+.then(() => fsp.access(fname)) | |
+ .then(() => { throw new Error('fsp.unlink error 2'); }) | |
+.catch((e) => { if (e.syscall != 'access') { throw e; } }) | |
+ | |
+.then(() => { | |
+ console.log('test fsp.unlink'); | |
+}) | |
+.catch((e) => { | |
+ console.log('test fsp.unlink failed', e); | |
+}); | |
diff -r 5bd15bd3766c -r 43930d21f5c0 test/js/fs_promises_004.js | |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 | |
+++ b/test/js/fs_promises_004.js Tue Feb 11 01:48:24 2020 +0300 | |
@@ -0,0 +1,192 @@ | |
+var fs = require('fs'); | |
+var fsp = fs.promises; | |
+var dname = './build/test/'; | |
+var fname = dname + 'fs_promises_004'; | |
+var lname = dname + 'fs_promises_004_lnk'; | |
+ | |
+ | |
+var testSync = () => new Promise((resolve, reject) => { | |
+ try { | |
+ try { | |
+ fs.unlinkSync(fname); | |
+ } catch (e) { | |
+ void e; | |
+ } | |
+ try { | |
+ fs.unlinkSync(lname); | |
+ } catch (e) { | |
+ void e; | |
+ } | |
+ | |
+ try { | |
+ fs.realpathSync(fname); | |
+ throw new Error('fs.realpathSync error 1'); | |
+ } catch (e) { | |
+ if (e.syscall != 'realpath') { // e.code | |
+ throw e; | |
+ } | |
+ } | |
+ | |
+ fs.writeFileSync(fname, fname); | |
+ | |
+ var rname = fs.realpathSync(fname); | |
+ | |
+ fs.symlinkSync(rname, lname); | |
+ | |
+ if (fs.realpathSync(lname) != rname) { | |
+ throw new Error('fs.symlinkSync error 2'); | |
+ } | |
+ | |
+ if (fs.readFileSync(lname) != fname) { | |
+ throw new Error('fs.symlinkSync error 3'); | |
+ } | |
+ | |
+ fs.unlinkSync(lname); | |
+ fs.accessSync(fname); | |
+ fs.unlinkSync(fname); | |
+ | |
+ resolve(); | |
+ | |
+ } catch (e) { | |
+ reject(e); | |
+ } | |
+}); | |
+ | |
+ | |
+var testCallback = () => new Promise((resolve, reject) => { | |
+ try { | |
+ try { | |
+ fs.unlinkSync(fname); | |
+ } catch (e) { | |
+ void e; | |
+ } | |
+ try { | |
+ fs.unlinkSync(lname); | |
+ } catch (e) { | |
+ void e; | |
+ } | |
+ | |
+ fs.realpath(fname, (err) => { | |
+ if (!err) { | |
+ reject(new Error('fs.realpath error 1')); | |
+ return; | |
+ } | |
+ if (err.syscall != 'realpath') { | |
+ reject(err); | |
+ return; | |
+ } | |
+ | |
+ try { | |
+ fs.writeFileSync(fname, fname); | |
+ } catch (e) { | |
+ reject(e); | |
+ return; | |
+ } | |
+ | |
+ fs.realpath(fname, (err, rname) => { | |
+ if (err) { | |
+ reject(err); | |
+ return; | |
+ } | |
+ | |
+ fs.symlink(rname, lname, (err) => { | |
+ if (err) { | |
+ reject(err); | |
+ return; | |
+ } | |
+ | |
+ fs.realpath(lname, undefined, (err, xname) => { | |
+ if (err) { | |
+ reject(err); | |
+ return; | |
+ } | |
+ | |
+ if (rname != xname) { | |
+ reject(new Error('fs.symlink error 1')); | |
+ return; | |
+ } | |
+ | |
+ try { | |
+ if (fs.readFileSync(lname) != fname) { | |
+ reject(new Error('fs.symlink error 2')); | |
+ return; | |
+ } | |
+ | |
+ fs.unlinkSync(lname); | |
+ fs.accessSync(fname); | |
+ fs.unlinkSync(fname); | |
+ | |
+ } catch (e) { | |
+ reject(e); | |
+ return; | |
+ } | |
+ | |
+ resolve(); | |
+ }); | |
+ }); | |
+ }); | |
+ }); | |
+ | |
+ } catch (e) { | |
+ reject(e); | |
+ } | |
+}); | |
+ | |
+ | |
+Promise.resolve() | |
+.then(testSync) | |
+.then(() => { | |
+ console.log('test fs.symlinkSync'); | |
+}) | |
+.catch((e) => { | |
+ console.log('test fs.symlinkSync failed', e); | |
+}) | |
+ | |
+.then(testCallback) | |
+.then(() => { | |
+ console.log('test fs.symlink'); | |
+}) | |
+.catch((e) => { | |
+ console.log('test fs.symlink failed', e); | |
+}) | |
+ | |
+.then(() => fsp.unlink(fname) | |
+ .catch(() => {})) | |
+.then(() => fsp.unlink(lname) | |
+ .catch(() => {})) | |
+.then(() => fsp.realpath(fname) | |
+ .then(() => { throw new Error('fsp.realpath error 1') })) | |
+.catch((e) => { | |
+ if (e.syscall != 'realpath') { | |
+ throw e; | |
+ } | |
+}) | |
+.then(() => { | |
+ fs.writeFileSync(fname, fname); | |
+ | |
+ return fsp.realpath(fname); | |
+}) | |
+.then((rname) => fsp.symlink(rname, lname) | |
+ .then(() => rname)) | |
+.then((rname) => fsp.realpath(lname) | |
+ .then((xname) => { | |
+ if (rname != xname) { | |
+ throw new Error('fsp.symlink error 2'); | |
+ } | |
+ })) | |
+.then(() => { | |
+ if (fs.readFileSync(lname) != fname) { | |
+ throw new Error('fsp.symlink error 3'); | |
+ } | |
+ | |
+ fs.unlinkSync(lname); | |
+ fs.accessSync(fname); | |
+ fs.unlinkSync(fname); | |
+}) | |
+ | |
+.then(() => { | |
+ console.log('test fsp.symlink'); | |
+}) | |
+.catch((e) => { | |
+ console.log('test fsp.symlink failed', e); | |
+}); | |
diff -r 5bd15bd3766c -r 43930d21f5c0 test/njs_expect_test.exp | |
--- a/test/njs_expect_test.exp Mon Feb 10 17:39:41 2020 +0300 | |
+++ b/test/njs_expect_test.exp Tue Feb 11 01:48:24 2020 +0300 | |
@@ -1096,3 +1096,13 @@ njs_run {"./test/js/fs_promises_002.js"} | |
"testSync ok true | |
testCallback ok true | |
testPromise ok true" | |
+ | |
+njs_run {"./test/js/fs_promises_003.js"} \ | |
+"test fs.unlinkSync | |
+test fs.unlink | |
+test fsp.unlink" | |
+ | |
+njs_run {"./test/js/fs_promises_004.js"} \ | |
+"test fs.symlinkSync | |
+test fs.symlink | |
+test fsp.symlink" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment