Fix issue #262. Allow fs.realpath to traverse above the current working directory.
This commit is contained in:
parent
9a6d2c35a1
commit
ba0c32e2e1
219
lib/fs.js
219
lib/fs.js
@ -469,132 +469,119 @@ var path = require('path');
|
|||||||
var normalize = path.normalize;
|
var normalize = path.normalize;
|
||||||
var normalizeArray = path.normalizeArray;
|
var normalizeArray = path.normalizeArray;
|
||||||
|
|
||||||
fs.realpathSync = function (path) {
|
// realpath
|
||||||
var seen_links = {}, knownHards = {}, buf, i = 0, part, x, stats;
|
// Not using realpath(2) because it's bad.
|
||||||
if (path.charAt(0) !== '/') {
|
// See: http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
|
||||||
var cwd = process.cwd().split('/');
|
fs.realpathSync = realpathSync;
|
||||||
path = cwd.concat(path.split('/'));
|
fs.realpath = realpath;
|
||||||
path = normalizeArray(path);
|
function realpathSync (p) {
|
||||||
i = cwd.length;
|
if (p.charAt(0) !== '/') {
|
||||||
buf = [].concat(cwd);
|
p = path.join(process.cwd(), p);
|
||||||
} else {
|
|
||||||
path = normalizeArray(path.split('/'));
|
|
||||||
buf = [''];
|
|
||||||
}
|
}
|
||||||
for (; i<path.length; i++) {
|
p = p.split('/');
|
||||||
part = path.slice(0, i+1).join('/');
|
var buf = [ '' ];
|
||||||
if (part.length !== 0) {
|
var seenLinks = {};
|
||||||
if (part in knownHards) {
|
var knownHard = {};
|
||||||
buf.push(path[i]);
|
// walk down the path, swapping out linked pathparts for their real
|
||||||
} else {
|
// values, and pushing non-link path bits onto the buffer.
|
||||||
stats = fs.lstatSync(part);
|
// then return the buffer.
|
||||||
if (stats.isSymbolicLink()) {
|
// NB: path.length changes.
|
||||||
x = stats.dev.toString(32)+":"+stats.ino.toString(32);
|
for (var i = 0; i < p.length; i ++) {
|
||||||
if (x in seen_links)
|
// skip over empty path parts.
|
||||||
throw new Error("cyclic link at "+part);
|
if (p[i] === '') continue;
|
||||||
seen_links[x] = true;
|
var part = buf.join('/')+'/'+p[i];
|
||||||
part = fs.readlinkSync(part);
|
if (knownHard[part]) {
|
||||||
if (part.charAt(0) === '/') {
|
buf.push( p[i] );
|
||||||
// absolute
|
continue;
|
||||||
path = normalizeArray(part.split('/'));
|
|
||||||
buf = [''];
|
|
||||||
i = 0;
|
|
||||||
} else {
|
|
||||||
// relative
|
|
||||||
Array.prototype.splice.apply(path, [i, 1].concat(part.split('/')));
|
|
||||||
part = normalizeArray(path);
|
|
||||||
var y = 0, L = Math.max(path.length, part.length), delta;
|
|
||||||
for (; y<L && path[y] === part[y]; y++);
|
|
||||||
if (y !== L) {
|
|
||||||
path = part;
|
|
||||||
delta = i-y;
|
|
||||||
i = y-1;
|
|
||||||
if (delta > 0) buf.splice(y, delta);
|
|
||||||
} else {
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
buf.push(path[i]);
|
|
||||||
knownHards[buf.join('/')] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
var stat = fs.lstatSync(part);
|
||||||
|
if (!stat.isSymbolicLink()) {
|
||||||
|
// not a symlink. easy.
|
||||||
|
knownHard[ part ] = true;
|
||||||
|
buf.push(p[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var id = stat.dev.toString(32)+':'+stat.ino.toString(32);
|
||||||
|
if (seenLinks[id]) throw new Error("cyclic link at "+part);
|
||||||
|
seenLinks[id] = true;
|
||||||
|
var target = fs.readlinkSync(part);
|
||||||
|
if (target.charAt(0) === '/') {
|
||||||
|
// absolute. Start over.
|
||||||
|
buf = [''];
|
||||||
|
p = path.normalizeArray(target.split('/'));
|
||||||
|
i = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// not absolute. join and splice.
|
||||||
|
target = target.split('/');
|
||||||
|
Array.prototype.splice.apply(p, [i, 1].concat(target));
|
||||||
|
p = path.normalizeArray(p);
|
||||||
|
i = 0;
|
||||||
|
buf = [''];
|
||||||
}
|
}
|
||||||
return buf.join('/');
|
return buf.join('/');
|
||||||
}
|
}
|
||||||
|
function realpath (p, cb) {
|
||||||
|
if (p.charAt(0) !== '/') {
|
||||||
fs.realpath = function (path, callback) {
|
p = path.join(process.cwd(), p);
|
||||||
var seen_links = {}, knownHards = {}, buf = [''], i = 0, part, x;
|
|
||||||
if (path.charAt(0) !== '/') {
|
|
||||||
// assumes cwd is canonical
|
|
||||||
var cwd = process.cwd().split('/');
|
|
||||||
path = cwd.concat(path.split('/'));
|
|
||||||
path = normalizeArray(path);
|
|
||||||
i = cwd.length-1;
|
|
||||||
buf = [].concat(cwd);
|
|
||||||
} else {
|
|
||||||
path = normalizeArray(path.split('/'));
|
|
||||||
}
|
}
|
||||||
function done(err) {
|
p = p.split('/');
|
||||||
if (callback) {
|
var buf = [ '' ];
|
||||||
if (!err) callback(err, buf.join('/'));
|
var seenLinks = {};
|
||||||
else callback(err);
|
var knownHard = {};
|
||||||
|
// walk down the path, swapping out linked pathparts for their real
|
||||||
|
// values, and pushing non-link path bits onto the buffer.
|
||||||
|
// then return the buffer.
|
||||||
|
// NB: path.length changes.
|
||||||
|
var i = 0;
|
||||||
|
var part;
|
||||||
|
LOOP();
|
||||||
|
function LOOP () {
|
||||||
|
i ++;
|
||||||
|
if (!(i < p.length)) return exit();
|
||||||
|
// skip over empty path parts.
|
||||||
|
if (p[i] === '') return process.nextTick(LOOP);
|
||||||
|
part = buf.join('/')+'/'+p[i];
|
||||||
|
if (knownHard[part]) {
|
||||||
|
buf.push( p[i] );
|
||||||
|
return process.nextTick(LOOP);
|
||||||
}
|
}
|
||||||
|
return fs.lstat(part, gotStat);
|
||||||
}
|
}
|
||||||
function next() {
|
function gotStat (er, stat) {
|
||||||
if (++i === path.length) return done();
|
if (er) return cb(er);
|
||||||
part = path.slice(0, i+1).join('/');
|
if (!stat.isSymbolicLink()) {
|
||||||
if (part.length === 0) return next();
|
// not a symlink. easy.
|
||||||
if (part in knownHards) {
|
knownHard[ part ] = true;
|
||||||
buf.push(path[i]);
|
buf.push(p[i]);
|
||||||
next();
|
return process.nextTick(LOOP);
|
||||||
} else {
|
|
||||||
fs.lstat(part, function(err, stats){
|
|
||||||
if (err) return done(err);
|
|
||||||
if (stats.isSymbolicLink()) {
|
|
||||||
x = stats.dev.toString(32)+":"+stats.ino.toString(32);
|
|
||||||
if (x in seen_links)
|
|
||||||
return done(new Error("cyclic link at "+part));
|
|
||||||
seen_links[x] = true;
|
|
||||||
fs.readlink(part, function(err, npart){
|
|
||||||
if (err) return done(err);
|
|
||||||
part = npart;
|
|
||||||
if (part.charAt(0) === '/') {
|
|
||||||
// absolute
|
|
||||||
path = normalizeArray(part.split('/'));
|
|
||||||
buf = [''];
|
|
||||||
i = 0;
|
|
||||||
} else {
|
|
||||||
// relative
|
|
||||||
Array.prototype.splice.apply(path, [i, 1].concat(part.split('/')));
|
|
||||||
part = normalizeArray(path);
|
|
||||||
var y = 0, L = Math.max(path.length, part.length), delta;
|
|
||||||
for (; y<L && path[y] === part[y]; y++);
|
|
||||||
if (y !== L) {
|
|
||||||
path = part;
|
|
||||||
delta = i-y;
|
|
||||||
i = y-1; // resolve new node if needed
|
|
||||||
if (delta > 0) buf.splice(y, delta);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
i--; // resolve new node if needed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
}); // binding.readlink
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
buf.push(path[i]);
|
|
||||||
knownHards[buf.join('/')] = true;
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}); // binding.lstat
|
|
||||||
}
|
}
|
||||||
|
var id = stat.dev.toString(32)+':'+stat.ino.toString(32);
|
||||||
|
if (seenLinks[id]) return cb(new Error("cyclic link at "+part));
|
||||||
|
seenLinks[id] = true;
|
||||||
|
fs.readlink(part, gotTarget);
|
||||||
}
|
}
|
||||||
next();
|
function gotTarget (er, target) {
|
||||||
};
|
if (er) return cb(er);
|
||||||
|
if (target.charAt(0) === '/') {
|
||||||
|
// absolute. Start over.
|
||||||
|
buf = [''];
|
||||||
|
p = path.normalizeArray(target.split('/'));
|
||||||
|
i = 0;
|
||||||
|
return process.nextTick(LOOP);
|
||||||
|
}
|
||||||
|
// not absolute. join and splice.
|
||||||
|
target = target.split('/');
|
||||||
|
Array.prototype.splice.apply(p, [i, 1].concat(target));
|
||||||
|
p = path.normalizeArray(p);
|
||||||
|
i = 0;
|
||||||
|
buf = [''];
|
||||||
|
return process.nextTick(LOOP);
|
||||||
|
}
|
||||||
|
function exit () {
|
||||||
|
cb(null, buf.join('/') || '/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var pool;
|
var pool;
|
||||||
function allocNewPool () {
|
function allocNewPool () {
|
||||||
|
@ -6,12 +6,11 @@ var async_completed = 0, async_expected = 0, unlink = [];
|
|||||||
|
|
||||||
function asynctest(testBlock, args, callback, assertBlock) {
|
function asynctest(testBlock, args, callback, assertBlock) {
|
||||||
async_expected++;
|
async_expected++;
|
||||||
testBlock.apply(testBlock, args.concat([function(err){
|
testBlock.apply(testBlock, args.concat(function(err){
|
||||||
var ignoreError = false;
|
var ignoreError = false;
|
||||||
if (assertBlock) {
|
if (assertBlock) {
|
||||||
try {
|
try {
|
||||||
ignoreError = assertBlock.apply(assertBlock,
|
ignoreError = assertBlock.apply(assertBlock, arguments);
|
||||||
Array.prototype.slice.call(arguments));
|
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
err = e;
|
err = e;
|
||||||
@ -19,7 +18,7 @@ function asynctest(testBlock, args, callback, assertBlock) {
|
|||||||
}
|
}
|
||||||
async_completed++;
|
async_completed++;
|
||||||
callback(ignoreError ? null : err);
|
callback(ignoreError ? null : err);
|
||||||
}]));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function bashRealpath(path, callback) {
|
function bashRealpath(path, callback) {
|
||||||
@ -227,6 +226,18 @@ function test_non_symlinks(callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var upone = path.join(process.cwd(), "..");
|
||||||
|
function test_escape_cwd (cb) {
|
||||||
|
asynctest(fs.realpath, [".."], cb, function(er, uponeActual){
|
||||||
|
assert.equal(upone, uponeActual,
|
||||||
|
"realpath('..') expected: "+upone+" actual:"+uponeActual);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
var uponeActual = fs.realpathSync("..");
|
||||||
|
assert.equal(upone, uponeActual,
|
||||||
|
"realpathSync('..') expected: "+upone+" actual:"+uponeActual);
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
var tests = [
|
var tests = [
|
||||||
@ -238,6 +249,7 @@ var tests = [
|
|||||||
test_relative_input_cwd,
|
test_relative_input_cwd,
|
||||||
test_deep_symlink_mix,
|
test_deep_symlink_mix,
|
||||||
test_non_symlinks,
|
test_non_symlinks,
|
||||||
|
test_escape_cwd
|
||||||
];
|
];
|
||||||
var numtests = tests.length;
|
var numtests = tests.length;
|
||||||
function runNextTest(err) {
|
function runNextTest(err) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user