Skip to content

Commit 2944934

Browse files
cjihrigtrevnorris
authored andcommitted
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: nodejs/node-v0.x-archive#8714 Reviewed-by: Trevor Norris <[email protected]>
1 parent 1fad373 commit 2944934

File tree

5 files changed

+213
-0
lines changed

5 files changed

+213
-0
lines changed

doc/api/fs.markdown

+33
Original file line numberDiff line numberDiff line change
@@ -656,10 +656,43 @@ that leaves you vulnerable to race conditions: another process may remove the
656656
file between the calls to `fs.exists()` and `fs.open()`. Just open the file
657657
and handle the error when it's not there.
658658

659+
`fs.exists()` will be deprecated.
660+
659661
## fs.existsSync(path)
660662

661663
Synchronous version of `fs.exists`.
662664

665+
`fs.existsSync()` will be deprecated.
666+
667+
## fs.access(path[, mode], callback)
668+
669+
Tests a user's permissions for the file specified by `path`. `mode` is an
670+
optional integer that specifies the accessibility checks to be performed. The
671+
following constants define the possible values of `mode`. It is possible to
672+
create a mask consisting of the bitwise OR of two or more values.
673+
674+
- `fs.F_OK` - File is visible to the calling process. This is useful for
675+
determining if a file exists, but says nothing about `rwx` permissions.
676+
Default if no `mode` is specified.
677+
- `fs.R_OK` - File can be read by the calling process.
678+
- `fs.W_OK` - File can be written by the calling process.
679+
- `fs.X_OK` - File can be executed by the calling process. This has no effect
680+
on Windows (will behave like `fs.F_OK`).
681+
682+
The final argument, `callback`, is a callback function that is invoked with
683+
a possible error argument. If any of the accessibility checks fail, the error
684+
argument will be populated. The following example checks if the file
685+
`/etc/passwd` can be read and written by the current process.
686+
687+
fs.access('/etc/passwd', fs.R_OK | fs.W_OK, function(err) {
688+
util.debug(err ? 'no access!' : 'can read/write');
689+
});
690+
691+
## fs.accessSync(path[, mode])
692+
693+
Synchronous version of `fs.access`. This throws if any accessibility checks
694+
fail, and does nothing otherwise.
695+
663696
## Class: fs.Stats
664697

665698
Objects returned from `fs.stat()`, `fs.lstat()` and `fs.fstat()` and their

lib/fs.js

+37
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ var O_RDWR = constants.O_RDWR || 0;
4949
var O_SYNC = constants.O_SYNC || 0;
5050
var O_TRUNC = constants.O_TRUNC || 0;
5151
var O_WRONLY = constants.O_WRONLY || 0;
52+
var F_OK = constants.F_OK || 0;
53+
var R_OK = constants.R_OK || 0;
54+
var W_OK = constants.W_OK || 0;
55+
var X_OK = constants.X_OK || 0;
5256

5357
var isWindows = process.platform === 'win32';
5458

@@ -181,6 +185,39 @@ fs.Stats.prototype.isSocket = function() {
181185
return this._checkModeProperty(constants.S_IFSOCK);
182186
};
183187

188+
fs.F_OK = F_OK;
189+
fs.R_OK = R_OK;
190+
fs.W_OK = W_OK;
191+
fs.X_OK = X_OK;
192+
193+
fs.access = function(path, mode, callback) {
194+
if (!nullCheck(path, callback))
195+
return;
196+
197+
if (typeof mode === 'function') {
198+
callback = mode;
199+
mode = F_OK;
200+
} else if (typeof callback !== 'function') {
201+
throw new TypeError('callback must be a function');
202+
}
203+
204+
mode = mode | 0;
205+
var req = new FSReqWrap();
206+
req.oncomplete = makeCallback(callback);
207+
binding.access(pathModule._makeLong(path), mode, req);
208+
};
209+
210+
fs.accessSync = function(path, mode) {
211+
nullCheck(path);
212+
213+
if (mode === undefined)
214+
mode = F_OK;
215+
else
216+
mode = mode | 0;
217+
218+
binding.access(pathModule._makeLong(path), mode);
219+
};
220+
184221
fs.exists = function(path, callback) {
185222
if (!nullCheck(path, cb)) return;
186223
var req = new FSReqWrap();

src/node_constants.cc

+16
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,22 @@ void DefineSystemConstants(Handle<Object> target) {
11071107
#ifdef S_IXOTH
11081108
NODE_DEFINE_CONSTANT(target, S_IXOTH);
11091109
#endif
1110+
1111+
#ifdef F_OK
1112+
NODE_DEFINE_CONSTANT(target, F_OK);
1113+
#endif
1114+
1115+
#ifdef R_OK
1116+
NODE_DEFINE_CONSTANT(target, R_OK);
1117+
#endif
1118+
1119+
#ifdef W_OK
1120+
NODE_DEFINE_CONSTANT(target, W_OK);
1121+
#endif
1122+
1123+
#ifdef X_OK
1124+
NODE_DEFINE_CONSTANT(target, X_OK);
1125+
#endif
11101126
}
11111127

11121128
void DefineUVConstants(Handle<Object> target) {

src/node_file.cc

+28
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ static void After(uv_fs_t *req) {
184184
argc = 1;
185185
break;
186186

187+
case UV_FS_ACCESS:
188+
argv[1] = Integer::New(env->isolate(), req->result);
189+
break;
190+
187191
case UV_FS_UTIME:
188192
case UV_FS_FUTIME:
189193
argc = 0;
@@ -320,6 +324,29 @@ struct fs_req_wrap {
320324
#define SYNC_RESULT err
321325

322326

327+
static void Access(const FunctionCallbackInfo<Value>& args) {
328+
Environment* env = Environment::GetCurrent(args.GetIsolate());
329+
HandleScope scope(env->isolate());
330+
331+
if (args.Length() < 2)
332+
return THROW_BAD_ARGS;
333+
if (!args[0]->IsString())
334+
return TYPE_ERROR("path must be a string");
335+
if (!args[1]->IsInt32())
336+
return TYPE_ERROR("mode must be an integer");
337+
338+
node::Utf8Value path(args[0]);
339+
int mode = static_cast<int>(args[1]->Int32Value());
340+
341+
if (args[2]->IsObject()) {
342+
ASYNC_CALL(access, args[2], *path, mode);
343+
} else {
344+
SYNC_CALL(access, *path, *path, mode);
345+
args.GetReturnValue().Set(SYNC_RESULT);
346+
}
347+
}
348+
349+
323350
static void Close(const FunctionCallbackInfo<Value>& args) {
324351
Environment* env = Environment::GetCurrent(args.GetIsolate());
325352
HandleScope scope(env->isolate());
@@ -1141,6 +1168,7 @@ void InitFs(Handle<Object> target,
11411168
FIXED_ONE_BYTE_STRING(env->isolate(), "FSInitialize"),
11421169
FunctionTemplate::New(env->isolate(), FSInitialize)->GetFunction());
11431170

1171+
NODE_SET_METHOD(target, "access", Access);
11441172
NODE_SET_METHOD(target, "close", Close);
11451173
NODE_SET_METHOD(target, "open", Open);
11461174
NODE_SET_METHOD(target, "read", Read);

test/simple/test-fs-access.js

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright Joyent, Inc. and other Node contributors.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the
5+
// "Software"), to deal in the Software without restriction, including
6+
// without limitation the rights to use, copy, modify, merge, publish,
7+
// distribute, sublicense, and/or sell copies of the Software, and to permit
8+
// persons to whom the Software is furnished to do so, subject to the
9+
// following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included
12+
// in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
var common = require('../common');
23+
var assert = require('assert');
24+
var fs = require('fs');
25+
var path = require('path');
26+
var doesNotExist = __filename + '__this_should_not_exist';
27+
var readOnlyFile = path.join(common.tmpDir, 'read_only_file');
28+
29+
var removeFile = function(file) {
30+
try {
31+
fs.unlinkSync(file);
32+
} catch (err) {
33+
// Ignore error
34+
}
35+
};
36+
37+
var createReadOnlyFile = function(file) {
38+
removeFile(file);
39+
fs.writeFileSync(file, '');
40+
fs.chmodSync(file, 0444);
41+
};
42+
43+
createReadOnlyFile(readOnlyFile);
44+
45+
assert(typeof fs.F_OK === 'number');
46+
assert(typeof fs.R_OK === 'number');
47+
assert(typeof fs.W_OK === 'number');
48+
assert(typeof fs.X_OK === 'number');
49+
50+
fs.access(__filename, function(err) {
51+
assert.strictEqual(err, null, 'error should not exist');
52+
});
53+
54+
fs.access(__filename, fs.R_OK, function(err) {
55+
assert.strictEqual(err, null, 'error should not exist');
56+
});
57+
58+
fs.access(doesNotExist, function(err) {
59+
assert.notEqual(err, null, 'error should exist');
60+
assert.strictEqual(err.code, 'ENOENT');
61+
assert.strictEqual(err.path, doesNotExist);
62+
});
63+
64+
fs.access(readOnlyFile, fs.F_OK | fs.R_OK, function(err) {
65+
assert.strictEqual(err, null, 'error should not exist');
66+
});
67+
68+
fs.access(readOnlyFile, fs.W_OK, function(err) {
69+
assert.notEqual(err, null, 'error should exist');
70+
assert.strictEqual(err.path, readOnlyFile);
71+
});
72+
73+
assert.throws(function() {
74+
fs.access(100, fs.F_OK, function(err) {});
75+
}, /path must be a string/);
76+
77+
assert.throws(function() {
78+
fs.access(__filename, fs.F_OK);
79+
}, /callback must be a function/);
80+
81+
assert.doesNotThrow(function() {
82+
fs.accessSync(__filename);
83+
});
84+
85+
assert.doesNotThrow(function() {
86+
var mode = fs.F_OK | fs.R_OK | fs.W_OK;
87+
88+
fs.accessSync(__filename, mode);
89+
});
90+
91+
assert.throws(function() {
92+
fs.accessSync(doesNotExist);
93+
}, function (err) {
94+
return err.code === 'ENOENT' && err.path === doesNotExist;
95+
});
96+
97+
process.on('exit', function() {
98+
removeFile(readOnlyFile);
99+
});

0 commit comments

Comments
 (0)