From 6f3f7e681a3f400c2332b92105812339e4dbf1f4 Mon Sep 17 00:00:00 2001 From: Rob Hogan Date: Sat, 16 Sep 2023 00:49:15 -0700 Subject: [PATCH] Fix bug in symlink traversal with indirect targets (#1084) Summary: Pull Request resolved: https://github.com/facebook/metro/pull/1084 Fixes a bug where symlinks beginning with indirections (`../`) may fail to traverse in some `projectRoot` / `watchFolders` configurations. This occurs due to incomplete "normalisation" of a symlink target in `metro-file-map`, relative to the project root. Given a project root of `/project` and a symlink `/project/src/link-to-foo -> ../../project/foo.js`, we would normalise the target to `../project/foo.js` rather than simply `foo.js` - we were not taking into account that further normalisation could be possible after joining with `projectRoot`. "Normal" paths are defined as being *fully* normalised relative to the project root. The unnecessary indirection out and back into the project root fails because `TreeFS` is a DAG, and internally there is no entry for `rootNode.get('..').get('project')` - this would be a cycle back to `rootNode`. Changelog: ``` * **[Fix]:** Symlinks with indirections may not be resolvable ``` Reviewed By: yungsters Differential Revision: D49323614 fbshipit-source-id: e01a10a0455b0af22ebc3cdd7c34ca8fd888d0f4 --- packages/metro-file-map/src/lib/TreeFS.js | 14 +++++++------- .../src/lib/__tests__/TreeFS-test.js | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/metro-file-map/src/lib/TreeFS.js b/packages/metro-file-map/src/lib/TreeFS.js index bb0dd649fa..819d59bcc9 100644 --- a/packages/metro-file-map/src/lib/TreeFS.js +++ b/packages/metro-file-map/src/lib/TreeFS.js @@ -559,13 +559,13 @@ export default class TreeFS implements MutableFileSystem { typeof literalSymlinkTarget === 'string', 'Expected symlink target to be populated.', ); - if (path.isAbsolute(literalSymlinkTarget)) { - normalSymlinkTarget = path.relative(this.#rootDir, literalSymlinkTarget); - } else { - normalSymlinkTarget = path.normalize( - path.join(path.dirname(canonicalPathOfSymlink), literalSymlinkTarget), - ); - } + const absoluteSymlinkTarget = path.resolve( + this.#rootDir, + canonicalPathOfSymlink, + '..', // Symlink target is relative to its containing directory. + literalSymlinkTarget, // May be absolute, in which case the above are ignored + ); + normalSymlinkTarget = path.relative(this.#rootDir, absoluteSymlinkTarget); this.#cachedNormalSymlinkTargets.set(symlinkNode, normalSymlinkTarget); return normalSymlinkTarget; } diff --git a/packages/metro-file-map/src/lib/__tests__/TreeFS-test.js b/packages/metro-file-map/src/lib/__tests__/TreeFS-test.js index dad4e0948b..c611d2bc7a 100644 --- a/packages/metro-file-map/src/lib/__tests__/TreeFS-test.js +++ b/packages/metro-file-map/src/lib/__tests__/TreeFS-test.js @@ -37,7 +37,7 @@ describe.each([['win32'], ['posix']])('TreeFS on %s', platform => { [p('foo/link-to-another.js'), ['', 0, 0, 0, '', '', p('another.js')]], [p('../outside/external.js'), ['', 0, 0, 0, '', '', 0]], [p('bar.js'), ['bar', 234, 0, 0, '', '', 0]], - [p('link-to-foo'), ['', 456, 0, 0, '', '', p('././abnormal/../foo')]], + [p('link-to-foo'), ['', 456, 0, 0, '', '', p('./../project/foo')]], [p('abs-link-out'), ['', 456, 0, 0, '', '', p('/outside/./baz/..')]], [p('root'), ['', 0, 0, 0, '', '', '..']], [p('link-to-nowhere'), ['', 123, 0, 0, '', '', p('./nowhere')]],