fs: optimize realpath using uv_fs_realpath()
Remove realpath() and realpathSync() cache. Use the native uv_fs_realpath() which is faster then the JS implementation by a few orders of magnitude. PR-URL: https://github.com/nodejs/node/pull/3594 Reviewed-By: Trevor Norris <trev.norris@gmail.com> Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Johan Bergström <bugs@bergstroem.nu>
This commit is contained in:
parent
81fd4581b9
commit
b488b19eaf
@ -916,26 +916,20 @@ object with an `encoding` property specifying the character encoding to use for
|
|||||||
the link path passed to the callback. If the `encoding` is set to `'buffer'`,
|
the link path passed to the callback. If the `encoding` is set to `'buffer'`,
|
||||||
the link path returned will be passed as a `Buffer` object.
|
the link path returned will be passed as a `Buffer` object.
|
||||||
|
|
||||||
## fs.realpath(path[, cache], callback)
|
## fs.realpath(path[, options], callback)
|
||||||
|
|
||||||
* `path` {String | Buffer}
|
* `path` {String | Buffer}
|
||||||
* `cache` {Object}
|
* `options` {String | Object}
|
||||||
|
* `encoding` {String} default = `'utf8'`
|
||||||
* `callback` {Function}
|
* `callback` {Function}
|
||||||
|
|
||||||
Asynchronous realpath(2). The `callback` gets two arguments `(err,
|
Asynchronous realpath(2). The `callback` gets two arguments `(err,
|
||||||
resolvedPath)`. May use `process.cwd` to resolve relative paths. `cache` is an
|
resolvedPath)`. May use `process.cwd` to resolve relative paths.
|
||||||
object literal of mapped paths that can be used to force a specific path
|
|
||||||
resolution or avoid additional `fs.stat` calls for known real paths.
|
|
||||||
|
|
||||||
Example:
|
The optional `options` argument can be a string specifying an encoding, or an
|
||||||
|
object with an `encoding` property specifying the character encoding to use for
|
||||||
```js
|
the path passed to the callback. If the `encoding` is set to `'buffer'`,
|
||||||
var cache = {'/etc':'/private/etc'};
|
the path returned will be passed as a `Buffer` object.
|
||||||
fs.realpath('/etc/passwd', cache, (err, resolvedPath) => {
|
|
||||||
if (err) throw err;
|
|
||||||
console.log(resolvedPath);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## fs.readSync(fd, buffer, offset, length, position)
|
## fs.readSync(fd, buffer, offset, length, position)
|
||||||
|
|
||||||
@ -947,14 +941,18 @@ fs.realpath('/etc/passwd', cache, (err, resolvedPath) => {
|
|||||||
|
|
||||||
Synchronous version of [`fs.read()`][]. Returns the number of `bytesRead`.
|
Synchronous version of [`fs.read()`][]. Returns the number of `bytesRead`.
|
||||||
|
|
||||||
## fs.realpathSync(path[, cache])
|
## fs.realpathSync(path[, options])
|
||||||
|
|
||||||
* `path` {String | Buffer};
|
* `path` {String | Buffer};
|
||||||
* `cache` {Object}
|
* `options` {String | Object}
|
||||||
|
* `encoding` {String} default = `'utf8'`
|
||||||
|
|
||||||
Synchronous realpath(2). Returns the resolved path. `cache` is an
|
Synchronous realpath(2). Returns the resolved path.
|
||||||
object literal of mapped paths that can be used to force a specific path
|
|
||||||
resolution or avoid additional `fs.stat` calls for known real paths.
|
The optional `options` argument can be a string specifying an encoding, or an
|
||||||
|
object with an `encoding` property specifying the character encoding to use for
|
||||||
|
the path passed to the callback. If the `encoding` is set to `'buffer'`,
|
||||||
|
the path returned will be passed as a `Buffer` object.
|
||||||
|
|
||||||
## fs.rename(oldPath, newPath, callback)
|
## fs.rename(oldPath, newPath, callback)
|
||||||
|
|
||||||
|
249
lib/fs.js
249
lib/fs.js
@ -1557,234 +1557,37 @@ fs.unwatchFile = function(filename, listener) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Regexp that finds the next partion of a (partial) path
|
|
||||||
// result is [base_with_slash, base], e.g. ['somedir/', 'somedir']
|
|
||||||
const nextPartRe = isWindows ?
|
|
||||||
/(.*?)(?:[\/\\]+|$)/g :
|
|
||||||
/(.*?)(?:[\/]+|$)/g;
|
|
||||||
|
|
||||||
// Regex to find the device root, including trailing slash. E.g. 'c:\\'.
|
fs.realpathSync = function realpathSync(path, options) {
|
||||||
const splitRootRe = isWindows ?
|
if (!options)
|
||||||
/^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/ :
|
options = {};
|
||||||
/^[\/]*/;
|
else if (typeof options === 'string')
|
||||||
|
options = {encoding: options};
|
||||||
fs.realpathSync = function realpathSync(p, cache) {
|
else if (typeof options !== 'object')
|
||||||
// make p is absolute
|
throw new TypeError('"options" must be a string or an object');
|
||||||
p = pathModule.resolve(p);
|
nullCheck(path);
|
||||||
|
return binding.realpath(pathModule._makeLong(path), options.encoding);
|
||||||
if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
|
|
||||||
return cache[p];
|
|
||||||
}
|
|
||||||
|
|
||||||
const original = p;
|
|
||||||
const seenLinks = {};
|
|
||||||
const knownHard = {};
|
|
||||||
|
|
||||||
// current character position in p
|
|
||||||
var pos;
|
|
||||||
// the partial path so far, including a trailing slash if any
|
|
||||||
var current;
|
|
||||||
// the partial path without a trailing slash (except when pointing at a root)
|
|
||||||
var base;
|
|
||||||
// the partial path scanned in the previous round, with slash
|
|
||||||
var previous;
|
|
||||||
|
|
||||||
start();
|
|
||||||
|
|
||||||
function start() {
|
|
||||||
// Skip over roots
|
|
||||||
var m = splitRootRe.exec(p);
|
|
||||||
pos = m[0].length;
|
|
||||||
current = m[0];
|
|
||||||
base = m[0];
|
|
||||||
previous = '';
|
|
||||||
|
|
||||||
// On windows, check that the root exists. On unix there is no need.
|
|
||||||
if (isWindows && !knownHard[base]) {
|
|
||||||
fs.lstatSync(base);
|
|
||||||
knownHard[base] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// walk down the path, swapping out linked pathparts for their real
|
|
||||||
// values
|
|
||||||
// NB: p.length changes.
|
|
||||||
while (pos < p.length) {
|
|
||||||
// find the next part
|
|
||||||
nextPartRe.lastIndex = pos;
|
|
||||||
var result = nextPartRe.exec(p);
|
|
||||||
previous = current;
|
|
||||||
current += result[0];
|
|
||||||
base = previous + result[1];
|
|
||||||
pos = nextPartRe.lastIndex;
|
|
||||||
|
|
||||||
// continue if not a symlink
|
|
||||||
if (knownHard[base] || (cache && cache[base] === base)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var resolvedLink;
|
|
||||||
if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
|
|
||||||
// some known symbolic link. no need to stat again.
|
|
||||||
resolvedLink = cache[base];
|
|
||||||
} else {
|
|
||||||
var stat = fs.lstatSync(base);
|
|
||||||
if (!stat.isSymbolicLink()) {
|
|
||||||
knownHard[base] = true;
|
|
||||||
if (cache) cache[base] = base;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// read the link if it wasn't read before
|
|
||||||
// dev/ino always return 0 on windows, so skip the check.
|
|
||||||
var linkTarget = null;
|
|
||||||
if (!isWindows) {
|
|
||||||
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
|
|
||||||
if (seenLinks.hasOwnProperty(id)) {
|
|
||||||
linkTarget = seenLinks[id];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (linkTarget === null) {
|
|
||||||
fs.statSync(base);
|
|
||||||
linkTarget = fs.readlinkSync(base);
|
|
||||||
}
|
|
||||||
resolvedLink = pathModule.resolve(previous, linkTarget);
|
|
||||||
// track this, if given a cache.
|
|
||||||
if (cache) cache[base] = resolvedLink;
|
|
||||||
if (!isWindows) seenLinks[id] = linkTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
// resolve the link, then start over
|
|
||||||
p = pathModule.resolve(resolvedLink, p.slice(pos));
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cache) cache[original] = p;
|
|
||||||
|
|
||||||
return p;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
fs.realpath = function realpath(p, cache, cb) {
|
fs.realpath = function realpath(path, options, callback) {
|
||||||
if (typeof cb !== 'function') {
|
if (!options) {
|
||||||
cb = maybeCallback(cache);
|
options = {};
|
||||||
cache = null;
|
} else if (typeof options === 'function') {
|
||||||
}
|
callback = options;
|
||||||
|
options = {};
|
||||||
// make p is absolute
|
} else if (typeof options === 'string') {
|
||||||
p = pathModule.resolve(p);
|
options = {encoding: options};
|
||||||
|
} else if (typeof options !== 'object') {
|
||||||
if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
|
throw new TypeError('"options" must be a string or an object');
|
||||||
return process.nextTick(cb.bind(null, null, cache[p]));
|
|
||||||
}
|
|
||||||
|
|
||||||
const original = p;
|
|
||||||
const seenLinks = {};
|
|
||||||
const knownHard = {};
|
|
||||||
|
|
||||||
// current character position in p
|
|
||||||
var pos;
|
|
||||||
// the partial path so far, including a trailing slash if any
|
|
||||||
var current;
|
|
||||||
// the partial path without a trailing slash (except when pointing at a root)
|
|
||||||
var base;
|
|
||||||
// the partial path scanned in the previous round, with slash
|
|
||||||
var previous;
|
|
||||||
|
|
||||||
start();
|
|
||||||
|
|
||||||
function start() {
|
|
||||||
// Skip over roots
|
|
||||||
var m = splitRootRe.exec(p);
|
|
||||||
pos = m[0].length;
|
|
||||||
current = m[0];
|
|
||||||
base = m[0];
|
|
||||||
previous = '';
|
|
||||||
|
|
||||||
// On windows, check that the root exists. On unix there is no need.
|
|
||||||
if (isWindows && !knownHard[base]) {
|
|
||||||
fs.lstat(base, function(err) {
|
|
||||||
if (err) return cb(err);
|
|
||||||
knownHard[base] = true;
|
|
||||||
LOOP();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
process.nextTick(LOOP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// walk down the path, swapping out linked pathparts for their real
|
|
||||||
// values
|
|
||||||
function LOOP() {
|
|
||||||
// stop if scanned past end of path
|
|
||||||
if (pos >= p.length) {
|
|
||||||
if (cache) cache[original] = p;
|
|
||||||
return cb(null, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
// find the next part
|
|
||||||
nextPartRe.lastIndex = pos;
|
|
||||||
var result = nextPartRe.exec(p);
|
|
||||||
previous = current;
|
|
||||||
current += result[0];
|
|
||||||
base = previous + result[1];
|
|
||||||
pos = nextPartRe.lastIndex;
|
|
||||||
|
|
||||||
// continue if not a symlink
|
|
||||||
if (knownHard[base] || (cache && cache[base] === base)) {
|
|
||||||
return process.nextTick(LOOP);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
|
|
||||||
// known symbolic link. no need to stat again.
|
|
||||||
return gotResolvedLink(cache[base]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fs.lstat(base, gotStat);
|
|
||||||
}
|
|
||||||
|
|
||||||
function gotStat(err, stat) {
|
|
||||||
if (err) return cb(err);
|
|
||||||
|
|
||||||
// if not a symlink, skip to the next path part
|
|
||||||
if (!stat.isSymbolicLink()) {
|
|
||||||
knownHard[base] = true;
|
|
||||||
if (cache) cache[base] = base;
|
|
||||||
return process.nextTick(LOOP);
|
|
||||||
}
|
|
||||||
|
|
||||||
// stat & read the link if not read before
|
|
||||||
// call gotTarget as soon as the link target is known
|
|
||||||
// dev/ino always return 0 on windows, so skip the check.
|
|
||||||
if (!isWindows) {
|
|
||||||
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
|
|
||||||
if (seenLinks.hasOwnProperty(id)) {
|
|
||||||
return gotTarget(null, seenLinks[id], base);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fs.stat(base, function(err) {
|
|
||||||
if (err) return cb(err);
|
|
||||||
|
|
||||||
fs.readlink(base, function(err, target) {
|
|
||||||
if (!isWindows) seenLinks[id] = target;
|
|
||||||
gotTarget(err, target);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function gotTarget(err, target, base) {
|
|
||||||
if (err) return cb(err);
|
|
||||||
|
|
||||||
var resolvedLink = pathModule.resolve(previous, target);
|
|
||||||
if (cache) cache[base] = resolvedLink;
|
|
||||||
gotResolvedLink(resolvedLink);
|
|
||||||
}
|
|
||||||
|
|
||||||
function gotResolvedLink(resolvedLink) {
|
|
||||||
// resolve the link, then start over
|
|
||||||
p = pathModule.resolve(resolvedLink, p.slice(pos));
|
|
||||||
start();
|
|
||||||
}
|
}
|
||||||
|
callback = makeCallback(callback);
|
||||||
|
if (!nullCheck(path, callback))
|
||||||
|
return;
|
||||||
|
var req = new FSReqWrap();
|
||||||
|
req.oncomplete = callback;
|
||||||
|
binding.realpath(pathModule._makeLong(path), options.encoding, req);
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -108,19 +108,10 @@ function tryPackage(requestPath, exts) {
|
|||||||
tryExtensions(path.resolve(filename, 'index'), exts);
|
tryExtensions(path.resolve(filename, 'index'), exts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In order to minimize unnecessary lstat() calls,
|
|
||||||
// this cache is a list of known-real paths.
|
|
||||||
// Set to an empty object to reset.
|
|
||||||
Module._realpathCache = {};
|
|
||||||
|
|
||||||
// check if the file exists and is not a directory
|
// check if the file exists and is not a directory
|
||||||
function tryFile(requestPath) {
|
function tryFile(requestPath) {
|
||||||
const rc = stat(requestPath);
|
const rc = stat(requestPath);
|
||||||
return rc === 0 && toRealPath(requestPath);
|
return rc === 0 && fs.realpathSync(requestPath);
|
||||||
}
|
|
||||||
|
|
||||||
function toRealPath(requestPath) {
|
|
||||||
return fs.realpathSync(requestPath, Module._realpathCache);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// given a path check a the file exists with any of the set extensions
|
// given a path check a the file exists with any of the set extensions
|
||||||
@ -163,7 +154,7 @@ Module._findPath = function(request, paths) {
|
|||||||
if (!trailingSlash) {
|
if (!trailingSlash) {
|
||||||
const rc = stat(basePath);
|
const rc = stat(basePath);
|
||||||
if (rc === 0) { // File.
|
if (rc === 0) { // File.
|
||||||
filename = toRealPath(basePath);
|
filename = fs.realpathSync(basePath);
|
||||||
} else if (rc === 1) { // Directory.
|
} else if (rc === 1) { // Directory.
|
||||||
if (exts === undefined)
|
if (exts === undefined)
|
||||||
exts = Object.keys(Module._extensions);
|
exts = Object.keys(Module._extensions);
|
||||||
|
@ -241,6 +241,22 @@ static void After(uv_fs_t *req) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case UV_FS_REALPATH:
|
||||||
|
link = StringBytes::Encode(env->isolate(),
|
||||||
|
static_cast<const char*>(req->ptr),
|
||||||
|
req_wrap->encoding_);
|
||||||
|
if (link.IsEmpty()) {
|
||||||
|
argv[0] = UVException(env->isolate(),
|
||||||
|
UV_EINVAL,
|
||||||
|
req_wrap->syscall(),
|
||||||
|
"Invalid character encoding for link",
|
||||||
|
req->path,
|
||||||
|
req_wrap->data());
|
||||||
|
} else {
|
||||||
|
argv[1] = link;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case UV_FS_READ:
|
case UV_FS_READ:
|
||||||
// Buffer interface
|
// Buffer interface
|
||||||
argv[1] = Integer::New(env->isolate(), req->result);
|
argv[1] = Integer::New(env->isolate(), req->result);
|
||||||
@ -863,6 +879,41 @@ static void MKDir(const FunctionCallbackInfo<Value>& args) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void RealPath(const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
|
||||||
|
const int argc = args.Length();
|
||||||
|
|
||||||
|
if (argc < 1)
|
||||||
|
return TYPE_ERROR("path required");
|
||||||
|
|
||||||
|
BufferValue path(env->isolate(), args[0]);
|
||||||
|
ASSERT_PATH(path)
|
||||||
|
|
||||||
|
const enum encoding encoding = ParseEncoding(env->isolate(), args[1], UTF8);
|
||||||
|
|
||||||
|
Local<Value> callback = Null(env->isolate());
|
||||||
|
if (argc == 3)
|
||||||
|
callback = args[2];
|
||||||
|
|
||||||
|
if (callback->IsObject()) {
|
||||||
|
ASYNC_CALL(realpath, callback, encoding, *path);
|
||||||
|
} else {
|
||||||
|
SYNC_CALL(realpath, *path, *path);
|
||||||
|
const char* link_path = static_cast<const char*>(SYNC_REQ.ptr);
|
||||||
|
Local<Value> rc = StringBytes::Encode(env->isolate(),
|
||||||
|
link_path,
|
||||||
|
encoding);
|
||||||
|
if (rc.IsEmpty()) {
|
||||||
|
return env->ThrowUVException(UV_EINVAL,
|
||||||
|
"realpath",
|
||||||
|
"Invalid character encoding for path",
|
||||||
|
*path);
|
||||||
|
}
|
||||||
|
args.GetReturnValue().Set(rc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ReadDir(const FunctionCallbackInfo<Value>& args) {
|
static void ReadDir(const FunctionCallbackInfo<Value>& args) {
|
||||||
Environment* env = Environment::GetCurrent(args);
|
Environment* env = Environment::GetCurrent(args);
|
||||||
|
|
||||||
@ -1432,6 +1483,7 @@ void InitFs(Local<Object> target,
|
|||||||
env->SetMethod(target, "writeBuffer", WriteBuffer);
|
env->SetMethod(target, "writeBuffer", WriteBuffer);
|
||||||
env->SetMethod(target, "writeBuffers", WriteBuffers);
|
env->SetMethod(target, "writeBuffers", WriteBuffers);
|
||||||
env->SetMethod(target, "writeString", WriteString);
|
env->SetMethod(target, "writeString", WriteString);
|
||||||
|
env->SetMethod(target, "realpath", RealPath);
|
||||||
|
|
||||||
env->SetMethod(target, "chmod", Chmod);
|
env->SetMethod(target, "chmod", Chmod);
|
||||||
env->SetMethod(target, "fchmod", FChmod);
|
env->SetMethod(target, "fchmod", FChmod);
|
||||||
|
@ -10,9 +10,13 @@ var skipSymlinks = false;
|
|||||||
common.refreshTmpDir();
|
common.refreshTmpDir();
|
||||||
|
|
||||||
var root = '/';
|
var root = '/';
|
||||||
|
var assertEqualPath = assert.equal;
|
||||||
if (common.isWindows) {
|
if (common.isWindows) {
|
||||||
// something like "C:\\"
|
// something like "C:\\"
|
||||||
root = process.cwd().substr(0, 3);
|
root = process.cwd().substr(0, 3);
|
||||||
|
assertEqualPath = function(path_left, path_right, message) {
|
||||||
|
assert.equal(path_left.toLowerCase(), path_right.toLowerCase(), message);
|
||||||
|
};
|
||||||
|
|
||||||
// On Windows, creating symlinks requires admin privileges.
|
// On Windows, creating symlinks requires admin privileges.
|
||||||
// We'll only try to run symlink test if we have enough privileges.
|
// We'll only try to run symlink test if we have enough privileges.
|
||||||
@ -96,9 +100,9 @@ function test_simple_relative_symlink(callback) {
|
|||||||
unlink.push(t[0]);
|
unlink.push(t[0]);
|
||||||
});
|
});
|
||||||
var result = fs.realpathSync(entry);
|
var result = fs.realpathSync(entry);
|
||||||
assert.equal(result, path.resolve(expected));
|
assertEqualPath(result, path.resolve(expected));
|
||||||
asynctest(fs.realpath, [entry], callback, function(err, result) {
|
asynctest(fs.realpath, [entry], callback, function(err, result) {
|
||||||
assert.equal(result, path.resolve(expected));
|
assertEqualPath(result, path.resolve(expected));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,9 +126,9 @@ function test_simple_absolute_symlink(callback) {
|
|||||||
unlink.push(t[0]);
|
unlink.push(t[0]);
|
||||||
});
|
});
|
||||||
var result = fs.realpathSync(entry);
|
var result = fs.realpathSync(entry);
|
||||||
assert.equal(result, path.resolve(expected));
|
assertEqualPath(result, path.resolve(expected));
|
||||||
asynctest(fs.realpath, [entry], callback, function(err, result) {
|
asynctest(fs.realpath, [entry], callback, function(err, result) {
|
||||||
assert.equal(result, path.resolve(expected));
|
assertEqualPath(result, path.resolve(expected));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,9 +155,9 @@ function test_deep_relative_file_symlink(callback) {
|
|||||||
unlink.push(linkPath1);
|
unlink.push(linkPath1);
|
||||||
unlink.push(entry);
|
unlink.push(entry);
|
||||||
|
|
||||||
assert.equal(fs.realpathSync(entry), path.resolve(expected));
|
assertEqualPath(fs.realpathSync(entry), path.resolve(expected));
|
||||||
asynctest(fs.realpath, [entry], callback, function(err, result) {
|
asynctest(fs.realpath, [entry], callback, function(err, result) {
|
||||||
assert.equal(result, path.resolve(expected));
|
assertEqualPath(result, path.resolve(expected));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,10 +182,10 @@ function test_deep_relative_dir_symlink(callback) {
|
|||||||
unlink.push(linkPath1b);
|
unlink.push(linkPath1b);
|
||||||
unlink.push(entry);
|
unlink.push(entry);
|
||||||
|
|
||||||
assert.equal(fs.realpathSync(entry), path.resolve(expected));
|
assertEqualPath(fs.realpathSync(entry), path.resolve(expected));
|
||||||
|
|
||||||
asynctest(fs.realpath, [entry], callback, function(err, result) {
|
asynctest(fs.realpath, [entry], callback, function(err, result) {
|
||||||
assert.equal(result, path.resolve(expected));
|
assertEqualPath(result, path.resolve(expected));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,9 +227,9 @@ function test_cyclic_link_overprotection(callback) {
|
|||||||
try {fs.unlinkSync(link);} catch (ex) {}
|
try {fs.unlinkSync(link);} catch (ex) {}
|
||||||
fs.symlinkSync(cycles, link, 'dir');
|
fs.symlinkSync(cycles, link, 'dir');
|
||||||
unlink.push(link);
|
unlink.push(link);
|
||||||
assert.equal(fs.realpathSync(testPath), path.resolve(expected));
|
assertEqualPath(fs.realpathSync(testPath), path.resolve(expected));
|
||||||
asynctest(fs.realpath, [testPath], callback, function(er, res) {
|
asynctest(fs.realpath, [testPath], callback, function(er, res) {
|
||||||
assert.equal(res, path.resolve(expected));
|
assertEqualPath(res, path.resolve(expected));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,10 +262,10 @@ function test_relative_input_cwd(callback) {
|
|||||||
|
|
||||||
var origcwd = process.cwd();
|
var origcwd = process.cwd();
|
||||||
process.chdir(entrydir);
|
process.chdir(entrydir);
|
||||||
assert.equal(fs.realpathSync(entry), path.resolve(expected));
|
assertEqualPath(fs.realpathSync(entry), path.resolve(expected));
|
||||||
asynctest(fs.realpath, [entry], callback, function(err, result) {
|
asynctest(fs.realpath, [entry], callback, function(err, result) {
|
||||||
process.chdir(origcwd);
|
process.chdir(origcwd);
|
||||||
assert.equal(result, path.resolve(expected));
|
assertEqualPath(result, path.resolve(expected));
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -311,9 +315,9 @@ function test_deep_symlink_mix(callback) {
|
|||||||
unlink.push(tmp('node-test-realpath-d2'));
|
unlink.push(tmp('node-test-realpath-d2'));
|
||||||
}
|
}
|
||||||
var expected = tmpAbsDir + '/cycles/root.js';
|
var expected = tmpAbsDir + '/cycles/root.js';
|
||||||
assert.equal(fs.realpathSync(entry), path.resolve(expected));
|
assertEqualPath(fs.realpathSync(entry), path.resolve(expected));
|
||||||
asynctest(fs.realpath, [entry], callback, function(err, result) {
|
asynctest(fs.realpath, [entry], callback, function(err, result) {
|
||||||
assert.equal(result, path.resolve(expected));
|
assertEqualPath(result, path.resolve(expected));
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -325,10 +329,10 @@ function test_non_symlinks(callback) {
|
|||||||
var expected = tmpAbsDir + '/cycles/root.js';
|
var expected = tmpAbsDir + '/cycles/root.js';
|
||||||
var origcwd = process.cwd();
|
var origcwd = process.cwd();
|
||||||
process.chdir(entrydir);
|
process.chdir(entrydir);
|
||||||
assert.equal(fs.realpathSync(entry), path.resolve(expected));
|
assertEqualPath(fs.realpathSync(entry), path.resolve(expected));
|
||||||
asynctest(fs.realpath, [entry], callback, function(err, result) {
|
asynctest(fs.realpath, [entry], callback, function(err, result) {
|
||||||
process.chdir(origcwd);
|
process.chdir(origcwd);
|
||||||
assert.equal(result, path.resolve(expected));
|
assertEqualPath(result, path.resolve(expected));
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -337,13 +341,13 @@ var upone = path.join(process.cwd(), '..');
|
|||||||
function test_escape_cwd(cb) {
|
function test_escape_cwd(cb) {
|
||||||
console.log('test_escape_cwd');
|
console.log('test_escape_cwd');
|
||||||
asynctest(fs.realpath, ['..'], cb, function(er, uponeActual) {
|
asynctest(fs.realpath, ['..'], cb, function(er, uponeActual) {
|
||||||
assert.equal(upone, uponeActual,
|
assertEqualPath(upone, uponeActual,
|
||||||
'realpath("..") expected: ' + path.resolve(upone) +
|
'realpath("..") expected: ' + path.resolve(upone) +
|
||||||
' actual:' + uponeActual);
|
' actual:' + uponeActual);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
var uponeActual = fs.realpathSync('..');
|
var uponeActual = fs.realpathSync('..');
|
||||||
assert.equal(upone, uponeActual,
|
assertEqualPath(upone, uponeActual,
|
||||||
'realpathSync("..") expected: ' + path.resolve(upone) +
|
'realpathSync("..") expected: ' + path.resolve(upone) +
|
||||||
' actual:' + uponeActual);
|
' actual:' + uponeActual);
|
||||||
|
|
||||||
@ -385,14 +389,14 @@ function test_up_multiple(cb) {
|
|||||||
var abedabeda = tmp('abedabeda'.split('').join('/'));
|
var abedabeda = tmp('abedabeda'.split('').join('/'));
|
||||||
var abedabeda_real = tmp('a');
|
var abedabeda_real = tmp('a');
|
||||||
|
|
||||||
assert.equal(fs.realpathSync(abedabeda), abedabeda_real);
|
assertEqualPath(fs.realpathSync(abedabeda), abedabeda_real);
|
||||||
assert.equal(fs.realpathSync(abedabed), abedabed_real);
|
assertEqualPath(fs.realpathSync(abedabed), abedabed_real);
|
||||||
fs.realpath(abedabeda, function(er, real) {
|
fs.realpath(abedabeda, function(er, real) {
|
||||||
if (er) throw er;
|
if (er) throw er;
|
||||||
assert.equal(abedabeda_real, real);
|
assertEqualPath(abedabeda_real, real);
|
||||||
fs.realpath(abedabed, function(er, real) {
|
fs.realpath(abedabed, function(er, real) {
|
||||||
if (er) throw er;
|
if (er) throw er;
|
||||||
assert.equal(abedabed_real, real);
|
assertEqualPath(abedabed_real, real);
|
||||||
cb();
|
cb();
|
||||||
cleanup();
|
cleanup();
|
||||||
});
|
});
|
||||||
@ -450,56 +454,14 @@ function test_abs_with_kids(cb) {
|
|||||||
var expectPath = root + '/a/b/c/x.txt';
|
var expectPath = root + '/a/b/c/x.txt';
|
||||||
var actual = fs.realpathSync(linkPath);
|
var actual = fs.realpathSync(linkPath);
|
||||||
// console.log({link:linkPath,expect:expectPath,actual:actual},'sync');
|
// console.log({link:linkPath,expect:expectPath,actual:actual},'sync');
|
||||||
assert.equal(actual, path.resolve(expectPath));
|
assertEqualPath(actual, path.resolve(expectPath));
|
||||||
asynctest(fs.realpath, [linkPath], cb, function(er, actual) {
|
asynctest(fs.realpath, [linkPath], cb, function(er, actual) {
|
||||||
// console.log({link:linkPath,expect:expectPath,actual:actual},'async');
|
// console.log({link:linkPath,expect:expectPath,actual:actual},'async');
|
||||||
assert.equal(actual, path.resolve(expectPath));
|
assertEqualPath(actual, path.resolve(expectPath));
|
||||||
cleanup();
|
cleanup();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_lying_cache_liar(cb) {
|
|
||||||
var n = 2;
|
|
||||||
|
|
||||||
// this should not require *any* stat calls, since everything
|
|
||||||
// checked by realpath will be found in the cache.
|
|
||||||
console.log('test_lying_cache_liar');
|
|
||||||
var cache = { '/foo/bar/baz/bluff' : '/foo/bar/bluff',
|
|
||||||
'/1/2/3/4/5/6/7' : '/1',
|
|
||||||
'/a' : '/a',
|
|
||||||
'/a/b' : '/a/b',
|
|
||||||
'/a/b/c' : '/a/b',
|
|
||||||
'/a/b/d' : '/a/b/d' };
|
|
||||||
if (common.isWindows) {
|
|
||||||
var wc = {};
|
|
||||||
Object.keys(cache).forEach(function(k) {
|
|
||||||
wc[ path.resolve(k) ] = path.resolve(cache[k]);
|
|
||||||
});
|
|
||||||
cache = wc;
|
|
||||||
}
|
|
||||||
|
|
||||||
var bluff = path.resolve('/foo/bar/baz/bluff');
|
|
||||||
var rps = fs.realpathSync(bluff, cache);
|
|
||||||
assert.equal(cache[bluff], rps);
|
|
||||||
var nums = path.resolve('/1/2/3/4/5/6/7');
|
|
||||||
var called = false; // no sync cb calling!
|
|
||||||
fs.realpath(nums, cache, function(er, rp) {
|
|
||||||
called = true;
|
|
||||||
assert.equal(cache[nums], rp);
|
|
||||||
if (--n === 0) cb();
|
|
||||||
});
|
|
||||||
assert(called === false);
|
|
||||||
|
|
||||||
const test = path.resolve('/a/b/c/d');
|
|
||||||
const expect = path.resolve('/a/b/d');
|
|
||||||
var actual = fs.realpathSync(test, cache);
|
|
||||||
assert.equal(expect, actual);
|
|
||||||
fs.realpath(test, cache, function(er, actual) {
|
|
||||||
assert.equal(expect, actual);
|
|
||||||
if (--n === 0) cb();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
var tests = [
|
var tests = [
|
||||||
@ -515,7 +477,6 @@ var tests = [
|
|||||||
test_non_symlinks,
|
test_non_symlinks,
|
||||||
test_escape_cwd,
|
test_escape_cwd,
|
||||||
test_abs_with_kids,
|
test_abs_with_kids,
|
||||||
test_lying_cache_liar,
|
|
||||||
test_up_multiple
|
test_up_multiple
|
||||||
];
|
];
|
||||||
var numtests = tests.length;
|
var numtests = tests.length;
|
||||||
@ -532,10 +493,10 @@ function runNextTest(err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
assert.equal(root, fs.realpathSync('/'));
|
assertEqualPath(root, fs.realpathSync('/'));
|
||||||
fs.realpath('/', function(err, result) {
|
fs.realpath('/', function(err, result) {
|
||||||
assert.equal(null, err);
|
assert.equal(null, err);
|
||||||
assert.equal(root, result);
|
assertEqualPath(root, result);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,21 +13,21 @@ if (!common.isWindows) {
|
|||||||
|
|
||||||
function test(p) {
|
function test(p) {
|
||||||
var result = fs.realpathSync(p);
|
var result = fs.realpathSync(p);
|
||||||
assert.strictEqual(result, path.resolve(p));
|
assert.strictEqual(result.toLowerCase(), path.resolve(p).toLowerCase());
|
||||||
|
|
||||||
fs.realpath(p, function(err, result) {
|
fs.realpath(p, function(err, result) {
|
||||||
assert.ok(!err);
|
assert.ok(!err);
|
||||||
assert.strictEqual(result, path.resolve(p));
|
assert.strictEqual(result.toLowerCase(), path.resolve(p).toLowerCase());
|
||||||
succeeded++;
|
succeeded++;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
test('//localhost/c$/windows/system32');
|
test('//localhost/c$/Windows/System32');
|
||||||
test('//localhost/c$/windows');
|
test('//localhost/c$/Windows');
|
||||||
test('//localhost/c$/');
|
test('//localhost/c$/');
|
||||||
test('\\\\localhost\\c$');
|
test('\\\\localhost\\c$\\');
|
||||||
test('c:\\');
|
test('C:\\');
|
||||||
test('c:');
|
test('C:');
|
||||||
test(process.env.windir);
|
test(process.env.windir);
|
||||||
|
|
||||||
process.on('exit', function() {
|
process.on('exit', function() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user