[njs] Added fs.symlink(), fs.unlink(), fs.realpath() and friends.
Dmitry Volyntsev
xeioex at nginx.com
Tue Feb 18 17:39:24 UTC 2020
details: https://hg.nginx.org/njs/rev/f5adc2ea2d53
branches:
changeset: 1333:f5adc2ea2d53
user: Artem S. Povalyukhin <artem.povaluhin at gmail.com>
date: Tue Feb 11 01:48:24 2020 +0300
description:
Added fs.symlink(), fs.unlink(), fs.realpath() and friends.
diffstat:
src/njs_fs.c | 266 ++++++++++++++++++++++++++++++++++++++++
src/test/njs_interactive_test.c | 9 +-
src/test/njs_unit_test.c | 9 +
test/js/fs_promises_002.js | 2 +-
test/js/fs_promises_003.js | 108 ++++++++++++++++
test/js/fs_promises_004.js | 200 ++++++++++++++++++++++++++++++
test/njs_expect_test.exp | 10 +
7 files changed, 602 insertions(+), 2 deletions(-)
diffs (679 lines):
diff -r c60911765952 -r f5adc2ea2d53 src/njs_fs.c
--- a/src/njs_fs.c Tue Feb 18 18:55:50 2020 +0300
+++ b/src/njs_fs.c Tue Feb 11 01:48:24 2020 +0300
@@ -528,6 +528,201 @@ 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;
+ }
+
+ 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)
+{
+ u_char *resolved_path;
+ size_t size;
+ ssize_t length;
+ njs_int_t ret;
+ const char *file_path;
+ njs_value_t encoding, retval, *path, *callback, *options;
+ njs_fs_encoding_t enc;
+ char path_buf[MAXPATHLEN];
+
+ 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 = (u_char *) realpath(file_path, path_buf);
+ if (njs_slow_path(resolved_path == NULL)) {
+ ret = njs_fs_error(vm, "realpath", strerror(errno), path, errno,
+ &retval);
+ goto done;
+ }
+
+ size = njs_strlen(resolved_path);
+ length = njs_utf8_length(resolved_path, size);
+ if (njs_slow_path(length < 0)) {
+ length = 0;
+ }
+
+ ret = njs_string_new(vm, &retval, resolved_path, size, length);
+ 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 +1075,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 +1258,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 c60911765952 -r f5adc2ea2d53 src/test/njs_interactive_test.c
--- a/src/test/njs_interactive_test.c Tue Feb 18 18:55:50 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 c60911765952 -r f5adc2ea2d53 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Tue Feb 18 18:55:50 2020 +0300
+++ b/src/test/njs_unit_test.c Tue Feb 11 01:48:24 2020 +0300
@@ -16159,6 +16159,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')]"
@@ -16181,6 +16187,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 c60911765952 -r f5adc2ea2d53 test/js/fs_promises_002.js
--- a/test/js/fs_promises_002.js Tue Feb 18 18:55:50 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 c60911765952 -r f5adc2ea2d53 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 c60911765952 -r f5adc2ea2d53 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,200 @@
+var fs = require('fs');
+var fsp = fs.promises;
+var dname = './build/test/';
+var fname = dname + 'fs_promises_004';
+var fname_utf8 = 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);
+ fs.writeFileSync(fname_utf8, fname_utf8);
+
+ 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');
+ }
+
+ var rname_utf8 = fs.realpathSync(fname_utf8);
+ if (rname_utf8.slice(-7,-4) != 'αβγ') {
+ throw new Error('fs.realpathSync error 2');
+ }
+
+ fs.unlinkSync(lname);
+ fs.accessSync(fname);
+ fs.unlinkSync(fname);
+ fs.unlinkSync(fname_utf8);
+
+ 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 c60911765952 -r f5adc2ea2d53 test/njs_expect_test.exp
--- a/test/njs_expect_test.exp Tue Feb 18 18:55:50 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"
More information about the nginx-devel
mailing list