summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Ihrig <cjihrig@gmail.com>2014-11-11 17:43:18 -0500
committerTrevor Norris <trev.norris@gmail.com>2015-01-12 16:59:34 -0800
commit29449349da5ef7e23689396c21bae003d81e0081 (patch)
tree63898eb3b721a928134e85854975d00724baf9da
parent1fad3730c2d6130c13b46ad78ded808fe9bb4c10 (diff)
downloadnode-29449349da5ef7e23689396c21bae003d81e0081.tar.gz
fs: add access() and accessSync()
fs.exists() and fs.existsSync() do not follow the typical error first callback convention. access() and accessSync() are added as alternatives in this commit. PR-URL: https://github.com/joyent/node/pull/8714 Reviewed-by: Trevor Norris <trev.norris@gmail.com>
-rw-r--r--doc/api/fs.markdown33
-rw-r--r--lib/fs.js37
-rw-r--r--src/node_constants.cc16
-rw-r--r--src/node_file.cc28
-rw-r--r--test/simple/test-fs-access.js99
5 files changed, 213 insertions, 0 deletions
diff --git a/doc/api/fs.markdown b/doc/api/fs.markdown
index 124962c2a..5d3ef3bd1 100644
--- a/doc/api/fs.markdown
+++ b/doc/api/fs.markdown
@@ -656,10 +656,43 @@ that leaves you vulnerable to race conditions: another process may remove the
file between the calls to `fs.exists()` and `fs.open()`. Just open the file
and handle the error when it's not there.
+`fs.exists()` will be deprecated.
+
## fs.existsSync(path)
Synchronous version of `fs.exists`.
+`fs.existsSync()` will be deprecated.
+
+## fs.access(path[, mode], callback)
+
+Tests a user's permissions for the file specified by `path`. `mode` is an
+optional integer that specifies the accessibility checks to be performed. The
+following constants define the possible values of `mode`. It is possible to
+create a mask consisting of the bitwise OR of two or more values.
+
+- `fs.F_OK` - File is visible to the calling process. This is useful for
+determining if a file exists, but says nothing about `rwx` permissions.
+Default if no `mode` is specified.
+- `fs.R_OK` - File can be read by the calling process.
+- `fs.W_OK` - File can be written by the calling process.
+- `fs.X_OK` - File can be executed by the calling process. This has no effect
+on Windows (will behave like `fs.F_OK`).
+
+The final argument, `callback`, is a callback function that is invoked with
+a possible error argument. If any of the accessibility checks fail, the error
+argument will be populated. The following example checks if the file
+`/etc/passwd` can be read and written by the current process.
+
+ fs.access('/etc/passwd', fs.R_OK | fs.W_OK, function(err) {
+ util.debug(err ? 'no access!' : 'can read/write');
+ });
+
+## fs.accessSync(path[, mode])
+
+Synchronous version of `fs.access`. This throws if any accessibility checks
+fail, and does nothing otherwise.
+
## Class: fs.Stats
Objects returned from `fs.stat()`, `fs.lstat()` and `fs.fstat()` and their
diff --git a/lib/fs.js b/lib/fs.js
index a97ba3aa6..ca9157b88 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -49,6 +49,10 @@ var O_RDWR = constants.O_RDWR || 0;
var O_SYNC = constants.O_SYNC || 0;
var O_TRUNC = constants.O_TRUNC || 0;
var O_WRONLY = constants.O_WRONLY || 0;
+var F_OK = constants.F_OK || 0;
+var R_OK = constants.R_OK || 0;
+var W_OK = constants.W_OK || 0;
+var X_OK = constants.X_OK || 0;
var isWindows = process.platform === 'win32';
@@ -181,6 +185,39 @@ fs.Stats.prototype.isSocket = function() {
return this._checkModeProperty(constants.S_IFSOCK);
};
+fs.F_OK = F_OK;
+fs.R_OK = R_OK;
+fs.W_OK = W_OK;
+fs.X_OK = X_OK;
+
+fs.access = function(path, mode, callback) {
+ if (!nullCheck(path, callback))
+ return;
+
+ if (typeof mode === 'function') {
+ callback = mode;
+ mode = F_OK;
+ } else if (typeof callback !== 'function') {
+ throw new TypeError('callback must be a function');
+ }
+
+ mode = mode | 0;
+ var req = new FSReqWrap();
+ req.oncomplete = makeCallback(callback);
+ binding.access(pathModule._makeLong(path), mode, req);
+};
+
+fs.accessSync = function(path, mode) {
+ nullCheck(path);
+
+ if (mode === undefined)
+ mode = F_OK;
+ else
+ mode = mode | 0;
+
+ binding.access(pathModule._makeLong(path), mode);
+};
+
fs.exists = function(path, callback) {
if (!nullCheck(path, cb)) return;
var req = new FSReqWrap();
diff --git a/src/node_constants.cc b/src/node_constants.cc
index 45840d865..86fa544d1 100644
--- a/src/node_constants.cc
+++ b/src/node_constants.cc
@@ -1107,6 +1107,22 @@ void DefineSystemConstants(Handle<Object> target) {
#ifdef S_IXOTH
NODE_DEFINE_CONSTANT(target, S_IXOTH);
#endif
+
+#ifdef F_OK
+ NODE_DEFINE_CONSTANT(target, F_OK);
+#endif
+
+#ifdef R_OK
+ NODE_DEFINE_CONSTANT(target, R_OK);
+#endif
+
+#ifdef W_OK
+ NODE_DEFINE_CONSTANT(target, W_OK);
+#endif
+
+#ifdef X_OK
+ NODE_DEFINE_CONSTANT(target, X_OK);
+#endif
}
void DefineUVConstants(Handle<Object> target) {
diff --git a/src/node_file.cc b/src/node_file.cc
index 6736864bc..8a00b5d9e 100644
--- a/src/node_file.cc
+++ b/src/node_file.cc
@@ -184,6 +184,10 @@ static void After(uv_fs_t *req) {
argc = 1;
break;
+ case UV_FS_ACCESS:
+ argv[1] = Integer::New(env->isolate(), req->result);
+ break;
+
case UV_FS_UTIME:
case UV_FS_FUTIME:
argc = 0;
@@ -320,6 +324,29 @@ struct fs_req_wrap {
#define SYNC_RESULT err
+static void Access(const FunctionCallbackInfo<Value>& args) {
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
+ HandleScope scope(env->isolate());
+
+ if (args.Length() < 2)
+ return THROW_BAD_ARGS;
+ if (!args[0]->IsString())
+ return TYPE_ERROR("path must be a string");
+ if (!args[1]->IsInt32())
+ return TYPE_ERROR("mode must be an integer");
+
+ node::Utf8Value path(args[0]);
+ int mode = static_cast<int>(args[1]->Int32Value());
+
+ if (args[2]->IsObject()) {
+ ASYNC_CALL(access, args[2], *path, mode);
+ } else {
+ SYNC_CALL(access, *path, *path, mode);
+ args.GetReturnValue().Set(SYNC_RESULT);
+ }
+}
+
+
static void Close(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args.GetIsolate());
HandleScope scope(env->isolate());
@@ -1141,6 +1168,7 @@ void InitFs(Handle<Object> target,
FIXED_ONE_BYTE_STRING(env->isolate(), "FSInitialize"),
FunctionTemplate::New(env->isolate(), FSInitialize)->GetFunction());
+ NODE_SET_METHOD(target, "access", Access);
NODE_SET_METHOD(target, "close", Close);
NODE_SET_METHOD(target, "open", Open);
NODE_SET_METHOD(target, "read", Read);
diff --git a/test/simple/test-fs-access.js b/test/simple/test-fs-access.js
new file mode 100644
index 000000000..3c0ac6162
--- /dev/null
+++ b/test/simple/test-fs-access.js
@@ -0,0 +1,99 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var common = require('../common');
+var assert = require('assert');
+var fs = require('fs');
+var path = require('path');
+var doesNotExist = __filename + '__this_should_not_exist';
+var readOnlyFile = path.join(common.tmpDir, 'read_only_file');
+
+var removeFile = function(file) {
+ try {
+ fs.unlinkSync(file);
+ } catch (err) {
+ // Ignore error
+ }
+};
+
+var createReadOnlyFile = function(file) {
+ removeFile(file);
+ fs.writeFileSync(file, '');
+ fs.chmodSync(file, 0444);
+};
+
+createReadOnlyFile(readOnlyFile);
+
+assert(typeof fs.F_OK === 'number');
+assert(typeof fs.R_OK === 'number');
+assert(typeof fs.W_OK === 'number');
+assert(typeof fs.X_OK === 'number');
+
+fs.access(__filename, function(err) {
+ assert.strictEqual(err, null, 'error should not exist');
+});
+
+fs.access(__filename, fs.R_OK, function(err) {
+ assert.strictEqual(err, null, 'error should not exist');
+});
+
+fs.access(doesNotExist, function(err) {
+ assert.notEqual(err, null, 'error should exist');
+ assert.strictEqual(err.code, 'ENOENT');
+ assert.strictEqual(err.path, doesNotExist);
+});
+
+fs.access(readOnlyFile, fs.F_OK | fs.R_OK, function(err) {
+ assert.strictEqual(err, null, 'error should not exist');
+});
+
+fs.access(readOnlyFile, fs.W_OK, function(err) {
+ assert.notEqual(err, null, 'error should exist');
+ assert.strictEqual(err.path, readOnlyFile);
+});
+
+assert.throws(function() {
+ fs.access(100, fs.F_OK, function(err) {});
+}, /path must be a string/);
+
+assert.throws(function() {
+ fs.access(__filename, fs.F_OK);
+}, /callback must be a function/);
+
+assert.doesNotThrow(function() {
+ fs.accessSync(__filename);
+});
+
+assert.doesNotThrow(function() {
+ var mode = fs.F_OK | fs.R_OK | fs.W_OK;
+
+ fs.accessSync(__filename, mode);
+});
+
+assert.throws(function() {
+ fs.accessSync(doesNotExist);
+}, function (err) {
+ return err.code === 'ENOENT' && err.path === doesNotExist;
+});
+
+process.on('exit', function() {
+ removeFile(readOnlyFile);
+});