diff --git a/benchmark/fs/bench-realpath.js b/benchmark/fs/bench-realpath.js new file mode 100644 index 00000000000000..1a181935f14ec2 --- /dev/null +++ b/benchmark/fs/bench-realpath.js @@ -0,0 +1,46 @@ +'use strict'; + +const common = require('../common'); +const fs = require('fs'); +const path = require('path'); +const resolved_path = path.resolve(__dirname, '../../lib/'); +const relative_path = path.relative(__dirname, '../../lib/'); + +const bench = common.createBenchmark(main, { + n: [1e4], + type: ['relative', 'resolved'], +}); + + +function main(conf) { + const n = conf.n >>> 0; + const type = conf.type; + + bench.start(); + if (type === 'relative') + relativePath(n); + else if (type === 'resolved') + resolvedPath(n); + else + throw new Error('unknown "type": ' + type); +} + +function relativePath(n) { + (function r(cntr) { + if (--cntr <= 0) + return bench.end(n); + fs.realpath(relative_path, function() { + r(cntr); + }); + }(n)); +} + +function resolvedPath(n) { + (function r(cntr) { + if (--cntr <= 0) + return bench.end(n); + fs.realpath(resolved_path, function() { + r(cntr); + }); + }(n)); +} diff --git a/benchmark/fs/bench-realpathSync.js b/benchmark/fs/bench-realpathSync.js new file mode 100644 index 00000000000000..ae1c78d30d1b35 --- /dev/null +++ b/benchmark/fs/bench-realpathSync.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); +const fs = require('fs'); +const path = require('path'); +const resolved_path = path.resolve(__dirname, '../../lib/'); +const relative_path = path.relative(__dirname, '../../lib/'); + +const bench = common.createBenchmark(main, { + n: [1e4], + type: ['relative', 'resolved'], +}); + + +function main(conf) { + const n = conf.n >>> 0; + const type = conf.type; + + bench.start(); + if (type === 'relative') + relativePath(n); + else if (type === 'resolved') + resolvedPath(n); + else + throw new Error('unknown "type": ' + type); + bench.end(n); +} + +function relativePath(n) { + for (var i = 0; i < n; i++) { + fs.realpathSync(relative_path); + } +} + +function resolvedPath(n) { + for (var i = 0; i < n; i++) { + fs.realpathSync(resolved_path); + } +} diff --git a/test/parallel/test-fs-realpath.js b/test/parallel/test-fs-realpath.js index 29be3127aae7a6..9d20f169ae543d 100644 --- a/test/parallel/test-fs-realpath.js +++ b/test/parallel/test-fs-realpath.js @@ -204,7 +204,7 @@ function test_cyclic_link_protection(callback) { fs.symlinkSync(t[1], t[0], 'dir'); unlink.push(t[0]); }); - assert.throws(function() { fs.realpathSync(entry); }); + assert.throws(function() { fs.realpathSync(entry); }, /ELOOP/); asynctest(fs.realpath, [entry], callback, function(err, result) { assert.ok(err && true); return true; @@ -461,6 +461,76 @@ function test_abs_with_kids(cb) { }); } +function test_deep_symlink_eloop(callback) { + console.log('test_deep_symlink_eloop'); + if (skipSymlinks) { + common.skip('symlink test (no privs)'); + return runNextTest(); + } + + const deepsymPath = path.join(targetsAbsDir, 'deep-symlink'); + const aPath = path.join(deepsymPath, 'a'); + const bSympath = path.join(aPath, 'b'); + const cleanupPaths = [bSympath]; + const pRepeat = 33; + + function cleanup() { + while (cleanupPaths.length > 0) { + try {fs.unlinkSync(cleanupPaths.pop());} catch (e) {} + } + } + + fs.mkdirSync(deepsymPath); + fs.mkdirSync(aPath); + fs.mkdirSync(path.join(targetsAbsDir, 'deep-symlink', 'c')); + try {fs.unlinkSync(bSympath);} catch (e) {} + fs.symlinkSync(deepsymPath, bSympath); + + // First test sync calls. + + const testPath = aPath + '/b/a'.repeat(pRepeat) + '/b/c'; + const resolvedPath = fs.realpathSync(testPath); + assert.equal(path.relative(deepsymPath, resolvedPath), 'c'); + + var reallyBigSymPath = deepsymPath; + var prev = null; + + // Make massively deep set of symlinks + for (var i = 97; i < 105; i++) { + for (var j = 97; j < 101; j++) { + const link = String.fromCharCode(i) + String.fromCharCode(j); + const link_path = path.join(deepsymPath, link); + cleanupPaths.push(link_path); + try {fs.unlinkSync(link_path);} catch (e) {} + if (prev) + fs.symlinkSync(link_path, prev); + reallyBigSymPath += '/' + link; + prev = link_path; + } + } + fs.symlinkSync(deepsymPath, prev); + reallyBigSymPath += '/' + path.basename(prev); + + assert.throws(() => fs.realpathSync(reallyBigSymPath), /ELOOP/); + + // Now test async calls. + + fs.realpath(testPath, (err, resolvedPath) => { + if (err) throw err; + assert.equal(path.relative(deepsymPath, resolvedPath), 'c'); + checkAsyncReallyBigSymPath(); + }); + + function checkAsyncReallyBigSymPath() { + fs.realpath(reallyBigSymPath, (err, path) => { + assert.ok(err); + assert.ok(/ELOOP/.test(err.message)); + cleanup(); + runNextTest(); + }); + } +} + // ---------------------------------------------------------------------------- var tests = [ @@ -476,7 +546,8 @@ var tests = [ test_non_symlinks, test_escape_cwd, test_abs_with_kids, - test_up_multiple + test_up_multiple, + test_deep_symlink_eloop, ]; var numtests = tests.length; var testsRun = 0;