From a436abfb25eae7fbc1e461d1311ff5d6b72357da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 21 Jul 2021 18:52:12 +0200 Subject: [PATCH] normalizeCLIPathForVFS(): Add an option for resolving symlinks --- libsolidity/interface/FileReader.cpp | 32 ++- libsolidity/interface/FileReader.h | 14 +- test/libsolidity/interface/FileReader.cpp | 318 +++++++++++++--------- 3 files changed, 234 insertions(+), 130 deletions(-) diff --git a/libsolidity/interface/FileReader.cpp b/libsolidity/interface/FileReader.cpp index 3064d5b89..28f52d834 100644 --- a/libsolidity/interface/FileReader.cpp +++ b/libsolidity/interface/FileReader.cpp @@ -111,7 +111,10 @@ ReadCallback::Result FileReader::readFile(string const& _kind, string const& _so } } -boost::filesystem::path FileReader::normalizeCLIPathForVFS(boost::filesystem::path const& _path) +boost::filesystem::path FileReader::normalizeCLIPathForVFS( + boost::filesystem::path const& _path, + SymlinkResolution _symlinkResolution +) { // Detailed normalization rules: // - Makes the path either be absolute or have slash as root (note that on Windows paths with @@ -125,7 +128,8 @@ boost::filesystem::path FileReader::normalizeCLIPathForVFS(boost::filesystem::pa // path to the current working directory. // // Also note that this function: - // - Does NOT resolve symlinks (except for symlinks in the path to the current working directory). + // - Does NOT resolve symlinks (except for symlinks in the path to the current working directory) + // unless explicitly requested. // - Does NOT check if the path refers to a file or a directory. If the path ends with a slash, // the slash is preserved even if it's a file. // - The only exception are paths where the file name is a dot (e.g. '.' or 'a/b/.'). These @@ -139,9 +143,27 @@ boost::filesystem::path FileReader::normalizeCLIPathForVFS(boost::filesystem::pa // Windows it does not. To get consistent results we resolve them on all platforms. boost::filesystem::path absolutePath = boost::filesystem::absolute(_path, canonicalWorkDir); - // NOTE: boost path preserves certain differences that are ignored by its operator ==. - // E.g. "a//b" vs "a/b" or "a/b/" vs "a/b/.". lexically_normal() does remove these differences. - boost::filesystem::path normalizedPath = absolutePath.lexically_normal(); + boost::filesystem::path normalizedPath; + if (_symlinkResolution == SymlinkResolution::Enabled) + { + // NOTE: weakly_canonical() will not convert a relative path into an absolute one if no + // directory included in the path actually exists. + normalizedPath = boost::filesystem::weakly_canonical(absolutePath); + + // The three corner cases in which lexically_normal() includes a trailing slash in the + // normalized path but weakly_canonical() does not. Note that the trailing slash is not + // ignored when comparing paths with ==. + if ((_path == "." || _path == "./" || _path == "../") && !boost::ends_with(normalizedPath.generic_string(), "/")) + normalizedPath = normalizedPath.parent_path() / (normalizedPath.filename().string() + "/"); + } + else + { + solAssert(_symlinkResolution == SymlinkResolution::Disabled, ""); + + // NOTE: boost path preserves certain differences that are ignored by its operator ==. + // E.g. "a//b" vs "a/b" or "a/b/" vs "a/b/.". lexically_normal() does remove these differences. + normalizedPath = absolutePath.lexically_normal(); + } solAssert(normalizedPath.is_absolute() || normalizedPath.root_path() == "/", ""); // If the path is on the same drive as the working dir, for portability we prefer not to diff --git a/libsolidity/interface/FileReader.h b/libsolidity/interface/FileReader.h index bbe24d264..53d4ef9b8 100644 --- a/libsolidity/interface/FileReader.h +++ b/libsolidity/interface/FileReader.h @@ -39,6 +39,11 @@ public: using PathMap = std::map; using FileSystemPathSet = std::set; + enum SymlinkResolution { + Disabled, ///< Do not resolve symbolic links in the path. + Enabled, ///< Follow symbolic links. The path should contain no symlinks. + }; + /// Constructs a FileReader with a base path and a set of allowed directories that /// will be used when requesting files from this file reader instance. explicit FileReader( @@ -90,11 +95,16 @@ public: /// Normalizes a filesystem path to make it include all components up to the filesystem root, /// remove small, inconsequential differences that do not affect the meaning and make it look - /// the same on all platforms (if possible). Symlinks in the path are not resolved. + /// the same on all platforms (if possible). /// The resulting path uses forward slashes as path separators, has no redundant separators, /// has no redundant . or .. segments and has no root name if removing it does not change the meaning. /// The path does not have to actually exist. - static boost::filesystem::path normalizeCLIPathForVFS(boost::filesystem::path const& _path); + /// @param _path Path to normalize. + /// @param _symlinkResolution If @a Disabled, any symlinks present in @a _path are preserved. + static boost::filesystem::path normalizeCLIPathForVFS( + boost::filesystem::path const& _path, + SymlinkResolution _symlinkResolution = SymlinkResolution::Disabled + ); /// @returns true if all the path components of @a _prefix are present at the beginning of @a _path. /// Both paths must be absolute (or have slash as root) and normalized (no . or .. segments, no diff --git a/test/libsolidity/interface/FileReader.cpp b/test/libsolidity/interface/FileReader.cpp index fd1eb52cc..866a4435a 100644 --- a/test/libsolidity/interface/FileReader.cpp +++ b/test/libsolidity/interface/FileReader.cpp @@ -36,31 +36,36 @@ using namespace solidity::test; namespace solidity::frontend::test { +using SymlinkResolution = FileReader::SymlinkResolution; + BOOST_AUTO_TEST_SUITE(FileReaderTest) BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_absolute_path) { - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/"), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./"), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./."), "/"); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/.", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./.", resolveSymlinks), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a"), "/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/"), "/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/."), "/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./a"), "/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./a/"), "/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./a/."), "/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b"), "/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/"), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a", resolveSymlinks), "/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/", resolveSymlinks), "/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/.", resolveSymlinks), "/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./a", resolveSymlinks), "/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./a/", resolveSymlinks), "/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./a/.", resolveSymlinks), "/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b", resolveSymlinks), "/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/", resolveSymlinks), "/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/./b/"), "/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/../a/b/"), "/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/c/.."), "/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/c/../"), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/./b/", resolveSymlinks), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/../a/b/", resolveSymlinks), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/c/..", resolveSymlinks), "/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/c/../", resolveSymlinks), "/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/c/../../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/c/../../../"), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/c/../../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/c/../../../", resolveSymlinks), "/"); + } } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_relative_path) @@ -75,54 +80,60 @@ BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_relative_path) expectedPrefix = "/" / expectedPrefix.relative_path(); soltestAssert(expectedPrefix.is_absolute() || expectedPrefix.root_path() == "/", ""); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("."), expectedPrefix / "x/y/z/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./"), expectedPrefix / "x/y/z/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(".//"), expectedPrefix / "x/y/z/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(".."), expectedPrefix / "x/y"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../"), expectedPrefix / "x/y/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("..//"), expectedPrefix / "x/y/"); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(".", resolveSymlinks), expectedPrefix / "x/y/z/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./", resolveSymlinks), expectedPrefix / "x/y/z/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(".//", resolveSymlinks), expectedPrefix / "x/y/z/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("..", resolveSymlinks), expectedPrefix / "x/y"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../", resolveSymlinks), expectedPrefix / "x/y/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("..//", resolveSymlinks), expectedPrefix / "x/y/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a"), expectedPrefix / "x/y/z/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/"), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/."), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a"), expectedPrefix / "x/y/z/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/"), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/."), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/./"), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/.//"), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/./."), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/././"), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/././/"), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b"), expectedPrefix / "x/y/z/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/"), expectedPrefix / "x/y/z/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a", resolveSymlinks), expectedPrefix / "x/y/z/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/.", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a", resolveSymlinks), expectedPrefix / "x/y/z/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/.", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/./", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/.//", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/./.", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/././", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/././/", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b", resolveSymlinks), expectedPrefix / "x/y/z/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/", resolveSymlinks), expectedPrefix / "x/y/z/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../a/b"), expectedPrefix / "x/y/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a/b"), expectedPrefix / "x/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/b"), expectedPrefix / "x/y/z/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("././a/b"), expectedPrefix / "x/y/z/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../a/b", resolveSymlinks), expectedPrefix / "x/y/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a/b", resolveSymlinks), expectedPrefix / "x/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/b", resolveSymlinks), expectedPrefix / "x/y/z/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("././a/b", resolveSymlinks), expectedPrefix / "x/y/z/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/./b/"), expectedPrefix / "x/y/z/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/../a/b/"), expectedPrefix / "x/y/z/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/.."), expectedPrefix / "x/y/z/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/../"), expectedPrefix / "x/y/z/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/..//"), expectedPrefix / "x/y/z/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/../.."), expectedPrefix / "x/y/z/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/../../"), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/../..//"), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/./b/", resolveSymlinks), expectedPrefix / "x/y/z/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/../a/b/", resolveSymlinks), expectedPrefix / "x/y/z/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/..", resolveSymlinks), expectedPrefix / "x/y/z/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/../", resolveSymlinks), expectedPrefix / "x/y/z/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/..//", resolveSymlinks), expectedPrefix / "x/y/z/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/../..", resolveSymlinks), expectedPrefix / "x/y/z/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/../../", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/../..//", resolveSymlinks), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a/.././../p/../q/../a/b"), expectedPrefix / "a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a/.././../p/../q/../a/b", resolveSymlinks), expectedPrefix / "a/b"); + } } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_redundant_slashes) { - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("///"), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("////"), "/"); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("///", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("////", resolveSymlinks), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("////a/b/"), "/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a//b/"), "/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a////b/"), "/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b//"), "/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b////"), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("////a/b/", resolveSymlinks), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a//b/", resolveSymlinks), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a////b/", resolveSymlinks), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b//", resolveSymlinks), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b////", resolveSymlinks), "/a/b/"); + } } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_unc_path) @@ -134,23 +145,26 @@ BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_unc_path) boost::filesystem::path expectedWorkDir = "/" / boost::filesystem::current_path().relative_path(); soltestAssert(expectedWorkDir.is_absolute() || expectedWorkDir.root_path() == "/", ""); - // UNC paths start with // or \\ followed by a name. They are used for network shares on Windows. - // On UNIX systems they are not supported but still treated in a special way. - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//host/"), "//host/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//host/a/b"), "//host/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//host/a/b/"), "//host/a/b/"); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + // UNC paths start with // or \\ followed by a name. They are used for network shares on Windows. + // On UNIX systems they are not supported but still treated in a special way. + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//host/", resolveSymlinks), "//host/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//host/a/b", resolveSymlinks), "//host/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//host/a/b/", resolveSymlinks), "//host/a/b/"); #if defined(_WIN32) - // On Windows an UNC path can also start with \\ instead of // - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/"), "//host/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/a/b"), "//host/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/a/b/"), "//host/a/b/"); + // On Windows an UNC path can also start with \\ instead of // + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/", resolveSymlinks), "//host/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/a/b", resolveSymlinks), "//host/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/a/b/", resolveSymlinks), "//host/a/b/"); #else - // On UNIX systems it's just a fancy relative path instead - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/"), expectedWorkDir / "\\\\host/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/a/b"), expectedWorkDir / "\\\\host/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/a/b/"), expectedWorkDir / "\\\\host/a/b/"); + // On UNIX systems it's just a fancy relative path instead + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/", resolveSymlinks), expectedWorkDir / "\\\\host/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/a/b", resolveSymlinks), expectedWorkDir / "\\\\host/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/a/b/", resolveSymlinks), expectedWorkDir / "\\\\host/a/b/"); #endif + } } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_root_name_only) @@ -167,20 +181,23 @@ BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_root_name_only) // C:\ represents the root directory of drive C: but C: on its own refers to the current working // directory. - // UNC paths - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//"), "//" / expectedWorkDir); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//host"), "//host" / expectedWorkDir); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + // UNC paths + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//", resolveSymlinks), "//" / expectedWorkDir); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//host", resolveSymlinks), "//host" / expectedWorkDir); - // On UNIX systems root name is empty. - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(""), expectedWorkDir); + // On UNIX systems root name is empty. + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("", resolveSymlinks), expectedWorkDir); #if defined(_WIN32) - boost::filesystem::path driveLetter = boost::filesystem::current_path().root_name(); - solAssert(!driveLetter.empty(), ""); - solAssert(driveLetter.is_relative(), ""); + boost::filesystem::path driveLetter = boost::filesystem::current_path().root_name(); + solAssert(!driveLetter.empty(), ""); + solAssert(driveLetter.is_relative(), ""); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(driveLetter), expectedWorkDir); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(driveLetter, resolveSymlinks), expectedWorkDir); #endif + } } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_stripping_root_name) @@ -193,41 +210,50 @@ BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_stripping_root_name) soltestAssert(!boost::filesystem::current_path().root_name().empty(), ""); #endif - boost::filesystem::path normalizedPath = FileReader::normalizeCLIPathForVFS(boost::filesystem::current_path()); - BOOST_CHECK_EQUAL(normalizedPath, "/" / boost::filesystem::current_path().relative_path()); - BOOST_TEST(normalizedPath.root_name().empty()); - BOOST_CHECK_EQUAL(normalizedPath.root_directory(), "/"); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + boost::filesystem::path normalizedPath = FileReader::normalizeCLIPathForVFS( + boost::filesystem::current_path(), + resolveSymlinks + ); + BOOST_CHECK_EQUAL(normalizedPath, "/" / boost::filesystem::current_path().relative_path()); + BOOST_TEST(normalizedPath.root_name().empty()); + BOOST_CHECK_EQUAL(normalizedPath.root_directory(), "/"); + } } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_path_beyond_root) { TemporaryWorkingDirectory tempWorkDir("/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../"), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../a"), "/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../a/.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../a/../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../../a"), "/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../../a/.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../../a/../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/../../b/../.."), "/"); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../.", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../a", resolveSymlinks), "/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../a/..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../a/../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../../a", resolveSymlinks), "/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../../a/..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../../a/../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/../../b/../..", resolveSymlinks), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(".."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../"), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../a"), "/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../a/.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../a/../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a"), "/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a/.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a/../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/../../b/../.."), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../.", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../a", resolveSymlinks), "/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../a/..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../a/../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a", resolveSymlinks), "/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a/..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a/../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/../../b/../..", resolveSymlinks), "/"); + } } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_case_sensitivity) @@ -235,22 +261,31 @@ BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_case_sensitivity) TemporaryDirectory tempDir(TEST_CASE_NAME); TemporaryWorkingDirectory tempWorkDir(tempDir); - boost::filesystem::path expectedPrefix = "/" / tempDir.path().relative_path(); - soltestAssert(expectedPrefix.is_absolute() || expectedPrefix.root_path() == "/", ""); + boost::filesystem::path workDirNoSymlinks = boost::filesystem::weakly_canonical(tempDir); + boost::filesystem::path expectedPrefix = "/" / workDirNoSymlinks.relative_path(); - BOOST_TEST(FileReader::normalizeCLIPathForVFS(tempDir.path() / "abc") == expectedPrefix / "abc"); - BOOST_TEST(FileReader::normalizeCLIPathForVFS(tempDir.path() / "abc") != expectedPrefix / "ABC"); - BOOST_TEST(FileReader::normalizeCLIPathForVFS(tempDir.path() / "ABC") != expectedPrefix / "abc"); - BOOST_TEST(FileReader::normalizeCLIPathForVFS(tempDir.path() / "ABC") == expectedPrefix / "ABC"); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + BOOST_TEST(FileReader::normalizeCLIPathForVFS(workDirNoSymlinks / "abc", resolveSymlinks) == expectedPrefix / "abc"); + BOOST_TEST(FileReader::normalizeCLIPathForVFS(workDirNoSymlinks / "abc", resolveSymlinks) != expectedPrefix / "ABC"); + BOOST_TEST(FileReader::normalizeCLIPathForVFS(workDirNoSymlinks / "ABC", resolveSymlinks) != expectedPrefix / "abc"); + BOOST_TEST(FileReader::normalizeCLIPathForVFS(workDirNoSymlinks / "ABC", resolveSymlinks) == expectedPrefix / "ABC"); + } } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_path_separators) { - // Even on Windows we want / as a separator. - BOOST_TEST((FileReader::normalizeCLIPathForVFS("/a/b/c").native() == boost::filesystem::path("/a/b/c").native())); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + // Even on Windows we want / as a separator. + BOOST_TEST(( + FileReader::normalizeCLIPathForVFS("/a/b/c", resolveSymlinks).native() == + boost::filesystem::path("/a/b/c").native() + )); + } } -BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_should_not_resolve_symlinks) +BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_should_not_resolve_symlinks_unless_requested) { TemporaryDirectory tempDir({"abc/"}, TEST_CASE_NAME); soltestAssert(tempDir.path().is_absolute(), ""); @@ -258,11 +293,26 @@ BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_should_not_resolve_symlinks) if (!createSymlinkIfSupportedByFilesystem(tempDir.path() / "abc", tempDir.path() / "sym", true)) return; - boost::filesystem::path expectedPrefix = "/" / tempDir.path().relative_path(); - soltestAssert(expectedPrefix.is_absolute() || expectedPrefix.root_path() == "/", ""); + boost::filesystem::path expectedPrefixWithSymlinks = "/" / tempDir.path().relative_path(); + boost::filesystem::path expectedPrefixWithoutSymlinks = "/" / boost::filesystem::weakly_canonical(tempDir).relative_path(); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(tempDir.path() / "sym/contract.sol"), expectedPrefix / "sym/contract.sol"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(tempDir.path() / "abc/contract.sol"), expectedPrefix / "abc/contract.sol"); + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS(tempDir.path() / "sym/contract.sol", SymlinkResolution::Disabled), + expectedPrefixWithSymlinks / "sym/contract.sol" + ); + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS(tempDir.path() / "abc/contract.sol", SymlinkResolution::Disabled), + expectedPrefixWithSymlinks / "abc/contract.sol" + ); + + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS(tempDir.path() / "sym/contract.sol", SymlinkResolution::Enabled), + expectedPrefixWithoutSymlinks / "abc/contract.sol" + ); + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS(tempDir.path() / "abc/contract.sol", SymlinkResolution::Enabled), + expectedPrefixWithoutSymlinks / "abc/contract.sol" + ); } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_should_resolve_symlinks_in_workdir_when_path_is_relative) @@ -280,9 +330,31 @@ BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_should_resolve_symlinks_in_workdir_w boost::filesystem::path expectedPrefix = "/" / tempDir.path().relative_path(); soltestAssert(expectedPrefix.is_absolute() || expectedPrefix.root_path() == "/", ""); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("contract.sol"), expectedWorkDir / "contract.sol"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(tempDir.path() / "sym/contract.sol"), expectedPrefix / "sym/contract.sol"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(tempDir.path() / "abc/contract.sol"), expectedPrefix / "abc/contract.sol"); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS("contract.sol", resolveSymlinks), + expectedWorkDir / "contract.sol" + ); + } + + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS(tempDir.path() / "sym/contract.sol", SymlinkResolution::Disabled), + expectedPrefix / "sym/contract.sol" + ); + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS(tempDir.path() / "abc/contract.sol", SymlinkResolution::Disabled), + expectedPrefix / "abc/contract.sol" + ); + + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS(tempDir.path() / "sym/contract.sol", SymlinkResolution::Enabled), + expectedWorkDir / "contract.sol" + ); + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS(tempDir.path() / "abc/contract.sol", SymlinkResolution::Enabled), + expectedWorkDir / "contract.sol" + ); } BOOST_AUTO_TEST_CASE(isPathPrefix_file_prefix)