From b57d946184e85c6fdf21ffca0f76a40500ca0685 Mon Sep 17 00:00:00 2001 From: Rasmus Andersson Date: Tue, 23 Feb 2010 01:46:03 +0100 Subject: [PATCH] added fs.link, fs.symlink, fs.readlink including sync versions and complete tests --- doc/api.txt | 24 +++++++++++ src/node.js | 24 +++++++++++ src/node_file.cc | 75 +++++++++++++++++++++++++++++++++ test/mjsunit/test-fs-symlink.js | 38 +++++++++++++++++ 4 files changed, 161 insertions(+) create mode 100644 test/mjsunit/test-fs-symlink.js diff --git a/doc/api.txt b/doc/api.txt index 6acd34feb8..4ea7c5a133 100644 --- a/doc/api.txt +++ b/doc/api.txt @@ -608,6 +608,30 @@ See the +fs.Stats+ section below for more information. Synchronous stat(2) or lstat(2). Returns an instance of +fs.Stats+. ++fs.link(srcpath, dstpath, callback)+ :: +Asynchronous link(2). +No arguments other than a possible exception are given to the completion callback. + ++fs.linkSync(dstpath, srcpath)+ :: +Synchronous link(2). + + ++fs.symlink(linkdata, path, callback)+ :: +Asynchronous symlink(2). +No arguments other than a possible exception are given to the completion callback. + ++fs.symlinkSync(linkdata, path)+ :: +Synchronous symlink(2). + + ++fs.readlink(path, callback)+ :: +Asynchronous readlink(2). +The callback gets two arguments +(err, resolvedPath)+. + ++fs.readlinkSync(path)+ :: +Synchronous readlink(2). Returns the resolved path. + + +fs.unlink(path, callback)+ :: Asynchronous unlink(2). No arguments other than a possible exception are given to the completion callback. diff --git a/src/node.js b/src/node.js index b9419510db..32f3b6d232 100644 --- a/src/node.js +++ b/src/node.js @@ -498,6 +498,30 @@ var fsModule = createInternalModule("fs", function (exports) { return process.fs.stat(path); }; + exports.readlink = function (path, callback) { + process.fs.readlink(path, callback || noop); + }; + + exports.readlinkSync = function (path) { + return process.fs.readlink(path); + }; + + exports.symlink = function (destination, path, callback) { + process.fs.symlink(destination, path, callback || noop); + }; + + exports.symlinkSync = function (destination, path) { + return process.fs.symlink(destination, path); + }; + + exports.link = function (srcpath, dstpath, callback) { + process.fs.link(srcpath, dstpath, callback || noop); + }; + + exports.linkSync = function (srcpath, dstpath) { + return process.fs.link(srcpath, dstpath); + }; + exports.unlink = function (path, callback) { process.fs.unlink(path, callback || noop); }; diff --git a/src/node_file.cc b/src/node_file.cc index d1e257a26f..ee2c2d6e4f 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -10,6 +10,12 @@ #include #include #include +#include + +/* used for readlink, AIX doesn't provide it */ +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif namespace node { @@ -55,6 +61,8 @@ static int After(eio_req *req) { case EIO_RMDIR: case EIO_MKDIR: case EIO_FTRUNCATE: + case EIO_LINK: + case EIO_SYMLINK: case EIO_CHMOD: argc = 0; break; @@ -78,6 +86,13 @@ static int After(eio_req *req) { argv[1] = BuildStatsObject(s); break; } + + case EIO_READLINK: + { + argc = 2; + argv[1] = String::New(static_cast(req->ptr2), req->result); + break; + } case EIO_READ: { @@ -207,6 +222,63 @@ static Handle LStat(const Arguments& args) { } } +static Handle Symlink(const Arguments& args) { + HandleScope scope; + + if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString()) { + return THROW_BAD_ARGS; + } + + String::Utf8Value dest(args[0]->ToString()); + String::Utf8Value path(args[1]->ToString()); + + if (args[2]->IsFunction()) { + ASYNC_CALL(symlink, args[2], *dest, *path) + } else { + int ret = symlink(*dest, *path); + if (ret != 0) return ThrowException(errno_exception(errno)); + return Undefined(); + } +} + +static Handle Link(const Arguments& args) { + HandleScope scope; + + if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString()) { + return THROW_BAD_ARGS; + } + + String::Utf8Value orig_path(args[0]->ToString()); + String::Utf8Value new_path(args[1]->ToString()); + + if (args[2]->IsFunction()) { + ASYNC_CALL(link, args[2], *orig_path, *new_path) + } else { + int ret = link(*orig_path, *new_path); + if (ret != 0) return ThrowException(errno_exception(errno)); + return Undefined(); + } +} + +static Handle ReadLink(const Arguments& args) { + HandleScope scope; + + if (args.Length() < 1 || !args[0]->IsString()) { + return THROW_BAD_ARGS; + } + + String::Utf8Value path(args[0]->ToString()); + + if (args[1]->IsFunction()) { + ASYNC_CALL(readlink, args[1], *path) + } else { + char buf[PATH_MAX]; + ssize_t bz = readlink(*path, buf, PATH_MAX); + if (bz == -1) return ThrowException(errno_exception(errno)); + return scope.Close(String::New(buf)); + } +} + static Handle Rename(const Arguments& args) { HandleScope scope; @@ -505,6 +577,9 @@ void File::Initialize(Handle target) { NODE_SET_METHOD(target, "readdir", ReadDir); NODE_SET_METHOD(target, "stat", Stat); NODE_SET_METHOD(target, "lstat", LStat); + NODE_SET_METHOD(target, "link", Link); + NODE_SET_METHOD(target, "symlink", Symlink); + NODE_SET_METHOD(target, "readlink", ReadLink); NODE_SET_METHOD(target, "unlink", Unlink); NODE_SET_METHOD(target, "write", Write); diff --git a/test/mjsunit/test-fs-symlink.js b/test/mjsunit/test-fs-symlink.js new file mode 100644 index 0000000000..2bf1e82df3 --- /dev/null +++ b/test/mjsunit/test-fs-symlink.js @@ -0,0 +1,38 @@ +process.mixin(require("./common")); + +var completed = 0; + +// test creating and reading symbolic link +var linkData = "../../cycles/root.js"; +var linkPath = path.join(fixturesDir, "nested-index", 'one', 'symlink1.js'); +try {fs.unlinkSync(linkPath);}catch(e){} +fs.symlink(linkData, linkPath, function(err){ + if (err) throw err; + puts('symlink done'); + // todo: fs.lstat? + fs.readlink(linkPath, function(err, destination) { + if (err) throw err; + assert.equal(destination, linkData); + completed++; + }) +}); + +// test creating and reading hard link +var srcPath = path.join(fixturesDir, "cycles", 'root.js'); +var dstPath = path.join(fixturesDir, "nested-index", 'one', 'link1.js'); +try {fs.unlinkSync(dstPath);}catch(e){} +fs.link(srcPath, dstPath, function(err){ + if (err) throw err; + puts('hard link done'); + var srcContent = fs.readFileSync(srcPath); + var dstContent = fs.readFileSync(dstPath); + assert.equal(srcContent, dstContent); + completed++; +}); + +process.addListener("exit", function () { + try {fs.unlinkSync(linkPath);}catch(e){} + try {fs.unlinkSync(dstPath);}catch(e){} + assert.equal(completed, 2); +}); +