}
+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)
{
.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,
+ },
};
.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,
+ },
};
--- /dev/null
+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);
+});
--- /dev/null
+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);
+});