Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 18a3064

Browse filesBrowse files
bzozcjihrig
authored andcommitted
fs: restore JS implementation of realpath
This reverts parts of b488b19 restoring javascript implementation of realpath and realpathSync. Fixes: #7175 Fixes: #6861 Fixes: #7294 Fixes: #7192 Fixes: #7044 Fixes: #6624 Fixes: #6978 PR-URL: #7899 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
1 parent 60d6e04 commit 18a3064
Copy full SHA for 18a3064

File tree

Expand file treeCollapse file tree

4 files changed

+360
-14
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

4 files changed

+360
-14
lines changed
Open diff view settings
Collapse file

‎doc/api/fs.md‎

Copy file name to clipboardExpand all lines: doc/api/fs.md
+6-2Lines changed: 6 additions & 2 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1222,6 +1222,8 @@ added: v0.1.31
12221222
Asynchronous realpath(3). The `callback` gets two arguments `(err,
12231223
resolvedPath)`. May use `process.cwd` to resolve relative paths.
12241224

1225+
Only paths that can be converted to UTF8 strings are supported.
1226+
12251227
The optional `options` argument can be a string specifying an encoding, or an
12261228
object with an `encoding` property specifying the character encoding to use for
12271229
the path passed to the callback. If the `encoding` is set to `'buffer'`,
@@ -1238,10 +1240,12 @@ added: v0.1.31
12381240

12391241
Synchronous realpath(3). Returns the resolved path.
12401242

1243+
Only paths that can be converted to UTF8 strings are supported.
1244+
12411245
The optional `options` argument can be a string specifying an encoding, or an
12421246
object with an `encoding` property specifying the character encoding to use for
1243-
the path passed to the callback. If the `encoding` is set to `'buffer'`,
1244-
the path returned will be passed as a `Buffer` object.
1247+
the returned value. If the `encoding` is set to `'buffer'`, the path returned
1248+
will be passed as a `Buffer` object.
12451249

12461250
## fs.rename(oldPath, newPath, callback)
12471251
<!-- YAML
Collapse file

‎lib/fs.js‎

Copy file name to clipboardExpand all lines: lib/fs.js
+213-12Lines changed: 213 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1563,38 +1563,239 @@ fs.unwatchFile = function(filename, listener) {
15631563
};
15641564

15651565

1566-
fs.realpathSync = function realpathSync(path, options) {
1566+
// Regexp that finds the next portion of a (partial) path
1567+
// result is [base_with_slash, base], e.g. ['somedir/', 'somedir']
1568+
const nextPartRe = isWindows ?
1569+
/(.*?)(?:[\/\\]+|$)/g :
1570+
/(.*?)(?:[\/]+|$)/g;
1571+
1572+
// Regex to find the device root, including trailing slash. E.g. 'c:\\'.
1573+
const splitRootRe = isWindows ?
1574+
/^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/ :
1575+
/^[\/]*/;
1576+
1577+
function encodeRealpathResult(result, options, err) {
1578+
if (!options || !options.encoding || options.encoding === 'utf8' || err)
1579+
return result;
1580+
const asBuffer = Buffer.from(result);
1581+
if (options.encoding === 'buffer') {
1582+
return asBuffer;
1583+
} else {
1584+
return asBuffer.toString(options.encoding);
1585+
}
1586+
}
1587+
1588+
fs.realpathSync = function realpathSync(p, options) {
15671589
if (!options)
15681590
options = {};
15691591
else if (typeof options === 'string')
15701592
options = {encoding: options};
15711593
else if (typeof options !== 'object')
15721594
throw new TypeError('"options" must be a string or an object');
1573-
nullCheck(path);
1574-
return binding.realpath(pathModule._makeLong(path), options.encoding);
1595+
nullCheck(p);
1596+
1597+
p = p.toString('utf8');
1598+
p = pathModule.resolve(p);
1599+
1600+
const seenLinks = {};
1601+
const knownHard = {};
1602+
1603+
// current character position in p
1604+
var pos;
1605+
// the partial path so far, including a trailing slash if any
1606+
var current;
1607+
// the partial path without a trailing slash (except when pointing at a root)
1608+
var base;
1609+
// the partial path scanned in the previous round, with slash
1610+
var previous;
1611+
1612+
start();
1613+
1614+
function start() {
1615+
// Skip over roots
1616+
var m = splitRootRe.exec(p);
1617+
pos = m[0].length;
1618+
current = m[0];
1619+
base = m[0];
1620+
previous = '';
1621+
1622+
// On windows, check that the root exists. On unix there is no need.
1623+
if (isWindows && !knownHard[base]) {
1624+
fs.lstatSync(base);
1625+
knownHard[base] = true;
1626+
}
1627+
}
1628+
1629+
// walk down the path, swapping out linked pathparts for their real
1630+
// values
1631+
// NB: p.length changes.
1632+
while (pos < p.length) {
1633+
// find the next part
1634+
nextPartRe.lastIndex = pos;
1635+
var result = nextPartRe.exec(p);
1636+
previous = current;
1637+
current += result[0];
1638+
base = previous + result[1];
1639+
pos = nextPartRe.lastIndex;
1640+
1641+
// continue if not a symlink
1642+
if (knownHard[base]) {
1643+
continue;
1644+
}
1645+
1646+
var resolvedLink;
1647+
var stat = fs.lstatSync(base);
1648+
if (!stat.isSymbolicLink()) {
1649+
knownHard[base] = true;
1650+
continue;
1651+
}
1652+
1653+
// read the link if it wasn't read before
1654+
// dev/ino always return 0 on windows, so skip the check.
1655+
var linkTarget = null;
1656+
if (!isWindows) {
1657+
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
1658+
if (seenLinks.hasOwnProperty(id)) {
1659+
linkTarget = seenLinks[id];
1660+
}
1661+
}
1662+
if (linkTarget === null) {
1663+
fs.statSync(base);
1664+
linkTarget = fs.readlinkSync(base);
1665+
}
1666+
resolvedLink = pathModule.resolve(previous, linkTarget);
1667+
1668+
if (!isWindows) seenLinks[id] = linkTarget;
1669+
1670+
// resolve the link, then start over
1671+
p = pathModule.resolve(resolvedLink, p.slice(pos));
1672+
start();
1673+
}
1674+
1675+
return encodeRealpathResult(p, options);
15751676
};
15761677

15771678

1578-
fs.realpath = function realpath(path, options, callback) {
1679+
fs.realpath = function realpath(p, options, callback) {
1680+
if (typeof callback !== 'function') {
1681+
callback = maybeCallback(options);
1682+
options = {};
1683+
}
1684+
15791685
if (!options) {
15801686
options = {};
15811687
} else if (typeof options === 'function') {
1582-
callback = options;
15831688
options = {};
15841689
} else if (typeof options === 'string') {
15851690
options = {encoding: options};
15861691
} else if (typeof options !== 'object') {
15871692
throw new TypeError('"options" must be a string or an object');
15881693
}
1589-
callback = makeCallback(callback);
1590-
if (!nullCheck(path, callback))
1694+
if (!nullCheck(p, callback))
15911695
return;
1592-
var req = new FSReqWrap();
1593-
req.oncomplete = callback;
1594-
binding.realpath(pathModule._makeLong(path), options.encoding, req);
1595-
return;
1596-
};
15971696

1697+
p = p.toString('utf8');
1698+
p = pathModule.resolve(p);
1699+
1700+
const seenLinks = {};
1701+
const knownHard = {};
1702+
1703+
// current character position in p
1704+
var pos;
1705+
// the partial path so far, including a trailing slash if any
1706+
var current;
1707+
// the partial path without a trailing slash (except when pointing at a root)
1708+
var base;
1709+
// the partial path scanned in the previous round, with slash
1710+
var previous;
1711+
1712+
start();
1713+
1714+
function start() {
1715+
// Skip over roots
1716+
var m = splitRootRe.exec(p);
1717+
pos = m[0].length;
1718+
current = m[0];
1719+
base = m[0];
1720+
previous = '';
1721+
1722+
// On windows, check that the root exists. On unix there is no need.
1723+
if (isWindows && !knownHard[base]) {
1724+
fs.lstat(base, function(err) {
1725+
if (err) return callback(err);
1726+
knownHard[base] = true;
1727+
LOOP();
1728+
});
1729+
} else {
1730+
process.nextTick(LOOP);
1731+
}
1732+
}
1733+
1734+
// walk down the path, swapping out linked pathparts for their real
1735+
// values
1736+
function LOOP() {
1737+
// stop if scanned past end of path
1738+
if (pos >= p.length) {
1739+
return callback(null, encodeRealpathResult(p, options));
1740+
}
1741+
1742+
// find the next part
1743+
nextPartRe.lastIndex = pos;
1744+
var result = nextPartRe.exec(p);
1745+
previous = current;
1746+
current += result[0];
1747+
base = previous + result[1];
1748+
pos = nextPartRe.lastIndex;
1749+
1750+
// continue if not a symlink
1751+
if (knownHard[base]) {
1752+
return process.nextTick(LOOP);
1753+
}
1754+
1755+
return fs.lstat(base, gotStat);
1756+
}
1757+
1758+
function gotStat(err, stat) {
1759+
if (err) return callback(err);
1760+
1761+
// if not a symlink, skip to the next path part
1762+
if (!stat.isSymbolicLink()) {
1763+
knownHard[base] = true;
1764+
return process.nextTick(LOOP);
1765+
}
1766+
1767+
// stat & read the link if not read before
1768+
// call gotTarget as soon as the link target is known
1769+
// dev/ino always return 0 on windows, so skip the check.
1770+
if (!isWindows) {
1771+
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
1772+
if (seenLinks.hasOwnProperty(id)) {
1773+
return gotTarget(null, seenLinks[id], base);
1774+
}
1775+
}
1776+
fs.stat(base, function(err) {
1777+
if (err) return callback(err);
1778+
1779+
fs.readlink(base, function(err, target) {
1780+
if (!isWindows) seenLinks[id] = target;
1781+
gotTarget(err, target);
1782+
});
1783+
});
1784+
}
1785+
1786+
function gotTarget(err, target, base) {
1787+
if (err) return callback(err);
1788+
1789+
var resolvedLink = pathModule.resolve(previous, target);
1790+
gotResolvedLink(resolvedLink);
1791+
}
1792+
1793+
function gotResolvedLink(resolvedLink) {
1794+
// resolve the link, then start over
1795+
p = pathModule.resolve(resolvedLink, p.slice(pos));
1796+
start();
1797+
}
1798+
};
15981799

15991800
fs.mkdtemp = function(prefix, options, callback) {
16001801
if (!prefix || typeof prefix !== 'string')
Collapse file
+88Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const fs = require('fs');
5+
6+
const string_dir = fs.realpathSync(common.fixturesDir);
7+
const buffer_dir = Buffer.from(string_dir);
8+
9+
const encodings = ['ascii', 'utf8', 'utf16le', 'ucs2',
10+
'base64', 'binary', 'hex'];
11+
var expected = {};
12+
encodings.forEach((encoding) => {
13+
expected[encoding] = buffer_dir.toString(encoding);
14+
});
15+
16+
17+
// test sync version
18+
for (var encoding in expected) {
19+
const expected_value = expected[encoding];
20+
let result;
21+
22+
result = fs.realpathSync(string_dir, {encoding: encoding});
23+
assert.strictEqual(result, expected_value);
24+
25+
result = fs.realpathSync(string_dir, encoding);
26+
assert.strictEqual(result, expected_value);
27+
28+
result = fs.realpathSync(buffer_dir, {encoding: encoding});
29+
assert.strictEqual(result, expected_value);
30+
31+
result = fs.realpathSync(buffer_dir, encoding);
32+
assert.strictEqual(result, expected_value);
33+
}
34+
35+
let buffer_result;
36+
buffer_result = fs.realpathSync(string_dir, {encoding: 'buffer'});
37+
assert.deepStrictEqual(buffer_result, buffer_dir);
38+
39+
buffer_result = fs.realpathSync(string_dir, 'buffer');
40+
assert.deepStrictEqual(buffer_result, buffer_dir);
41+
42+
buffer_result = fs.realpathSync(buffer_dir, {encoding: 'buffer'});
43+
assert.deepStrictEqual(buffer_result, buffer_dir);
44+
45+
buffer_result = fs.realpathSync(buffer_dir, 'buffer');
46+
assert.deepStrictEqual(buffer_result, buffer_dir);
47+
48+
// test async version
49+
for (encoding in expected) {
50+
const expected_value = expected[encoding];
51+
52+
fs.realpath(string_dir, {encoding: encoding}, common.mustCall((err, res) => {
53+
assert(!err);
54+
assert.strictEqual(res, expected_value);
55+
}));
56+
fs.realpath(string_dir, encoding, common.mustCall((err, res) => {
57+
assert(!err);
58+
assert.strictEqual(res, expected_value);
59+
}));
60+
fs.realpath(buffer_dir, {encoding: encoding}, common.mustCall((err, res) => {
61+
assert(!err);
62+
assert.strictEqual(res, expected_value);
63+
}));
64+
fs.realpath(buffer_dir, encoding, common.mustCall((err, res) => {
65+
assert(!err);
66+
assert.strictEqual(res, expected_value);
67+
}));
68+
}
69+
70+
fs.realpath(string_dir, {encoding: 'buffer'}, common.mustCall((err, res) => {
71+
assert(!err);
72+
assert.deepStrictEqual(res, buffer_dir);
73+
}));
74+
75+
fs.realpath(string_dir, 'buffer', common.mustCall((err, res) => {
76+
assert(!err);
77+
assert.deepStrictEqual(res, buffer_dir);
78+
}));
79+
80+
fs.realpath(buffer_dir, {encoding: 'buffer'}, common.mustCall((err, res) => {
81+
assert(!err);
82+
assert.deepStrictEqual(res, buffer_dir);
83+
}));
84+
85+
fs.realpath(buffer_dir, 'buffer', common.mustCall((err, res) => {
86+
assert(!err);
87+
assert.deepStrictEqual(res, buffer_dir);
88+
}));

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.