Skip to content

Commit e38cfad

Browse files
committed
[llvm][vfs] Preserve paths for fallback/fallthrough in RedirectingFileSystem (llvm#85307)
When we lookup in the external filesystem, do not remove . and .. components from the original path. For .. this is a correctness issue in the presence of symlinks, while for . it is simply better practice to preserve the original path to better match the behaviour of other filesystems. The only modification we need is to apply the working directory, since it could differ from the external filesystem. rdar://123655660 (cherry picked from commit 5a8a7ee)
1 parent e452404 commit e38cfad

File tree

3 files changed

+112
-49
lines changed

3 files changed

+112
-49
lines changed

llvm/include/llvm/Support/VirtualFileSystem.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,12 +1001,12 @@ class RedirectingFileSystem
10011001
/// Canonicalize path by removing ".", "..", "./", components. This is
10021002
/// a VFS request, do not bother about symlinks in the path components
10031003
/// but canonicalize in order to perform the correct entry search.
1004-
std::error_code makeCanonical(SmallVectorImpl<char> &Path) const;
1004+
std::error_code makeCanonicalForLookup(SmallVectorImpl<char> &Path) const;
10051005

10061006
/// Get the File status, or error, from the underlying external file system.
10071007
/// This returns the status with the originally requested name, while looking
1008-
/// up the entry using the canonical path.
1009-
ErrorOr<Status> getExternalStatus(const Twine &CanonicalPath,
1008+
/// up the entry using a potentially different path.
1009+
ErrorOr<Status> getExternalStatus(const Twine &LookupPath,
10101010
const Twine &OriginalPath) const;
10111011

10121012
/// Make \a Path an absolute path.
@@ -1094,7 +1094,7 @@ class RedirectingFileSystem
10941094
llvm::SmallVectorImpl<Entry *> &Entries) const;
10951095

10961096
/// Get the status for a path with the provided \c LookupResult.
1097-
ErrorOr<Status> status(const Twine &CanonicalPath, const Twine &OriginalPath,
1097+
ErrorOr<Status> status(const Twine &LookupPath, const Twine &OriginalPath,
10981098
const LookupResult &Result);
10991099

11001100
public:

llvm/lib/Support/VirtualFileSystem.cpp

Lines changed: 43 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,7 +1373,7 @@ std::error_code RedirectingFileSystem::isLocal(const Twine &Path_,
13731373
SmallString<256> Path;
13741374
Path_.toVector(Path);
13751375

1376-
if (std::error_code EC = makeCanonical(Path))
1376+
if (makeAbsolute(Path))
13771377
return {};
13781378

13791379
return ExternalFS->isLocal(Path, Result);
@@ -1440,7 +1440,7 @@ directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
14401440
SmallString<256> Path;
14411441
Dir.toVector(Path);
14421442

1443-
EC = makeCanonical(Path);
1443+
EC = makeAbsolute(Path);
14441444
if (EC)
14451445
return {};
14461446

@@ -2290,8 +2290,8 @@ void RedirectingFileSystem::LookupResult::getPath(
22902290
llvm::sys::path::append(Result, E->getName());
22912291
}
22922292

2293-
std::error_code
2294-
RedirectingFileSystem::makeCanonical(SmallVectorImpl<char> &Path) const {
2293+
std::error_code RedirectingFileSystem::makeCanonicalForLookup(
2294+
SmallVectorImpl<char> &Path) const {
22952295
if (std::error_code EC = makeAbsolute(Path))
22962296
return EC;
22972297

@@ -2306,12 +2306,16 @@ RedirectingFileSystem::makeCanonical(SmallVectorImpl<char> &Path) const {
23062306

23072307
ErrorOr<RedirectingFileSystem::LookupResult>
23082308
RedirectingFileSystem::lookupPath(StringRef Path) const {
2309+
llvm::SmallString<128> CanonicalPath(Path);
2310+
if (std::error_code EC = makeCanonicalForLookup(CanonicalPath))
2311+
return EC;
2312+
23092313
// RedirectOnly means the VFS is always used.
23102314
if (UsageTrackingActive && Redirection == RedirectKind::RedirectOnly)
23112315
HasBeenUsed = true;
23122316

2313-
sys::path::const_iterator Start = sys::path::begin(Path);
2314-
sys::path::const_iterator End = sys::path::end(Path);
2317+
sys::path::const_iterator Start = sys::path::begin(CanonicalPath);
2318+
sys::path::const_iterator End = sys::path::end(CanonicalPath);
23152319
llvm::SmallVector<Entry *, 32> Entries;
23162320
for (const auto &Root : Roots) {
23172321
ErrorOr<RedirectingFileSystem::LookupResult> Result =
@@ -2388,14 +2392,14 @@ static Status getRedirectedFileStatus(const Twine &OriginalPath,
23882392
}
23892393

23902394
ErrorOr<Status> RedirectingFileSystem::status(
2391-
const Twine &CanonicalPath, const Twine &OriginalPath,
2395+
const Twine &LookupPath, const Twine &OriginalPath,
23922396
const RedirectingFileSystem::LookupResult &Result) {
23932397
if (std::optional<StringRef> ExtRedirect = Result.getExternalRedirect()) {
2394-
SmallString<256> CanonicalRemappedPath((*ExtRedirect).str());
2395-
if (std::error_code EC = makeCanonical(CanonicalRemappedPath))
2398+
SmallString<256> RemappedPath((*ExtRedirect).str());
2399+
if (std::error_code EC = makeAbsolute(RemappedPath))
23962400
return EC;
23972401

2398-
ErrorOr<Status> S = ExternalFS->status(CanonicalRemappedPath);
2402+
ErrorOr<Status> S = ExternalFS->status(RemappedPath);
23992403
if (!S)
24002404
return S;
24012405
S = Status::copyWithNewName(*S, *ExtRedirect);
@@ -2405,13 +2409,13 @@ ErrorOr<Status> RedirectingFileSystem::status(
24052409
}
24062410

24072411
auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(Result.E);
2408-
return Status::copyWithNewName(DE->getStatus(), CanonicalPath);
2412+
return Status::copyWithNewName(DE->getStatus(), LookupPath);
24092413
}
24102414

24112415
ErrorOr<Status>
2412-
RedirectingFileSystem::getExternalStatus(const Twine &CanonicalPath,
2416+
RedirectingFileSystem::getExternalStatus(const Twine &LookupPath,
24132417
const Twine &OriginalPath) const {
2414-
auto Result = ExternalFS->status(CanonicalPath);
2418+
auto Result = ExternalFS->status(LookupPath);
24152419

24162420
// The path has been mapped by some nested VFS, don't override it with the
24172421
// original path.
@@ -2421,38 +2425,37 @@ RedirectingFileSystem::getExternalStatus(const Twine &CanonicalPath,
24212425
}
24222426

24232427
ErrorOr<Status> RedirectingFileSystem::status(const Twine &OriginalPath) {
2424-
SmallString<256> CanonicalPath;
2425-
OriginalPath.toVector(CanonicalPath);
2428+
SmallString<256> Path;
2429+
OriginalPath.toVector(Path);
24262430

2427-
if (std::error_code EC = makeCanonical(CanonicalPath))
2431+
if (std::error_code EC = makeAbsolute(Path))
24282432
return EC;
24292433

24302434
if (Redirection == RedirectKind::Fallback) {
24312435
// Attempt to find the original file first, only falling back to the
24322436
// mapped file if that fails.
2433-
ErrorOr<Status> S = getExternalStatus(CanonicalPath, OriginalPath);
2437+
ErrorOr<Status> S = getExternalStatus(Path, OriginalPath);
24342438
if (S)
24352439
return S;
24362440
}
24372441

2438-
ErrorOr<RedirectingFileSystem::LookupResult> Result =
2439-
lookupPath(CanonicalPath);
2442+
ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
24402443
if (!Result) {
24412444
// Was not able to map file, fallthrough to using the original path if
24422445
// that was the specified redirection type.
24432446
if (Redirection == RedirectKind::Fallthrough &&
24442447
isFileNotFound(Result.getError()))
2445-
return getExternalStatus(CanonicalPath, OriginalPath);
2448+
return getExternalStatus(Path, OriginalPath);
24462449
return Result.getError();
24472450
}
24482451

2449-
ErrorOr<Status> S = status(CanonicalPath, OriginalPath, *Result);
2452+
ErrorOr<Status> S = status(Path, OriginalPath, *Result);
24502453
if (!S && Redirection == RedirectKind::Fallthrough &&
24512454
isFileNotFound(S.getError(), Result->E)) {
24522455
// Mapped the file but it wasn't found in the underlying filesystem,
24532456
// fallthrough to using the original path if that was the specified
24542457
// redirection type.
2455-
return getExternalStatus(CanonicalPath, OriginalPath);
2458+
return getExternalStatus(Path, OriginalPath);
24562459
}
24572460

24582461
return S;
@@ -2549,53 +2552,49 @@ File::getWithPath(ErrorOr<std::unique_ptr<File>> Result, const Twine &P) {
25492552

25502553
ErrorOr<std::unique_ptr<File>>
25512554
RedirectingFileSystem::openFileForRead(const Twine &OriginalPath) {
2552-
SmallString<256> CanonicalPath;
2553-
OriginalPath.toVector(CanonicalPath);
2555+
SmallString<256> Path;
2556+
OriginalPath.toVector(Path);
25542557

2555-
if (std::error_code EC = makeCanonical(CanonicalPath))
2558+
if (std::error_code EC = makeAbsolute(Path))
25562559
return EC;
25572560

25582561
if (Redirection == RedirectKind::Fallback) {
25592562
// Attempt to find the original file first, only falling back to the
25602563
// mapped file if that fails.
2561-
auto F = File::getWithPath(ExternalFS->openFileForRead(CanonicalPath),
2562-
OriginalPath);
2564+
auto F = File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
25632565
if (F)
25642566
return F;
25652567
}
25662568

2567-
ErrorOr<RedirectingFileSystem::LookupResult> Result =
2568-
lookupPath(CanonicalPath);
2569+
ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
25692570
if (!Result) {
25702571
// Was not able to map file, fallthrough to using the original path if
25712572
// that was the specified redirection type.
25722573
if (Redirection == RedirectKind::Fallthrough &&
25732574
isFileNotFound(Result.getError()))
2574-
return File::getWithPath(ExternalFS->openFileForRead(CanonicalPath),
2575-
OriginalPath);
2575+
return File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
25762576
return Result.getError();
25772577
}
25782578

25792579
if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file?
25802580
return make_error_code(llvm::errc::invalid_argument);
25812581

25822582
StringRef ExtRedirect = *Result->getExternalRedirect();
2583-
SmallString<256> CanonicalRemappedPath(ExtRedirect.str());
2584-
if (std::error_code EC = makeCanonical(CanonicalRemappedPath))
2583+
SmallString<256> RemappedPath(ExtRedirect.str());
2584+
if (std::error_code EC = makeAbsolute(RemappedPath))
25852585
return EC;
25862586

25872587
auto *RE = cast<RedirectingFileSystem::RemapEntry>(Result->E);
25882588

2589-
auto ExternalFile = File::getWithPath(
2590-
ExternalFS->openFileForRead(CanonicalRemappedPath), ExtRedirect);
2589+
auto ExternalFile =
2590+
File::getWithPath(ExternalFS->openFileForRead(RemappedPath), ExtRedirect);
25912591
if (!ExternalFile) {
25922592
if (Redirection == RedirectKind::Fallthrough &&
25932593
isFileNotFound(ExternalFile.getError(), Result->E)) {
25942594
// Mapped the file but it wasn't found in the underlying filesystem,
25952595
// fallthrough to using the original path if that was the specified
25962596
// redirection type.
2597-
return File::getWithPath(ExternalFS->openFileForRead(CanonicalPath),
2598-
OriginalPath);
2597+
return File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
25992598
}
26002599
return ExternalFile;
26012600
}
@@ -2615,28 +2614,27 @@ RedirectingFileSystem::openFileForRead(const Twine &OriginalPath) {
26152614
std::error_code
26162615
RedirectingFileSystem::getRealPath(const Twine &OriginalPath,
26172616
SmallVectorImpl<char> &Output) const {
2618-
SmallString<256> CanonicalPath;
2619-
OriginalPath.toVector(CanonicalPath);
2617+
SmallString<256> Path;
2618+
OriginalPath.toVector(Path);
26202619

2621-
if (std::error_code EC = makeCanonical(CanonicalPath))
2620+
if (std::error_code EC = makeAbsolute(Path))
26222621
return EC;
26232622

26242623
if (Redirection == RedirectKind::Fallback) {
26252624
// Attempt to find the original file first, only falling back to the
26262625
// mapped file if that fails.
2627-
std::error_code EC = ExternalFS->getRealPath(CanonicalPath, Output);
2626+
std::error_code EC = ExternalFS->getRealPath(Path, Output);
26282627
if (!EC)
26292628
return EC;
26302629
}
26312630

2632-
ErrorOr<RedirectingFileSystem::LookupResult> Result =
2633-
lookupPath(CanonicalPath);
2631+
ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
26342632
if (!Result) {
26352633
// Was not able to map file, fallthrough to using the original path if
26362634
// that was the specified redirection type.
26372635
if (Redirection == RedirectKind::Fallthrough &&
26382636
isFileNotFound(Result.getError()))
2639-
return ExternalFS->getRealPath(CanonicalPath, Output);
2637+
return ExternalFS->getRealPath(Path, Output);
26402638
return Result.getError();
26412639
}
26422640

@@ -2649,7 +2647,7 @@ RedirectingFileSystem::getRealPath(const Twine &OriginalPath,
26492647
// Mapped the file but it wasn't found in the underlying filesystem,
26502648
// fallthrough to using the original path if that was the specified
26512649
// redirection type.
2652-
return ExternalFS->getRealPath(CanonicalPath, Output);
2650+
return ExternalFS->getRealPath(Path, Output);
26532651
}
26542652
return P;
26552653
}

llvm/unittests/Support/VirtualFileSystemTest.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "llvm/Support/VirtualFileSystem.h"
10+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
1011
#include "llvm/ADT/ScopeExit.h"
1112
#include "llvm/Config/llvm-config.h"
1213
#include "llvm/Support/Errc.h"
14+
#include "llvm/Support/FileSystem.h"
1315
#include "llvm/Support/MemoryBuffer.h"
1416
#include "llvm/Support/Path.h"
1517
#include "llvm/Support/SourceMgr.h"
@@ -3509,3 +3511,66 @@ TEST(RedirectingFileSystemTest, Used) {
35093511
EXPECT_TRUE(Redirecting1->hasBeenUsed());
35103512
EXPECT_FALSE(Redirecting2->hasBeenUsed());
35113513
}
3514+
3515+
// Check that paths looked up in the external filesystem are unmodified, except
3516+
// potentially to add the working directory. We cannot canonicalize away ..
3517+
// in the presence of symlinks in the external filesystem.
3518+
TEST(RedirectingFileSystemTest, ExternalPaths) {
3519+
struct InterceptorFS : llvm::vfs::ProxyFileSystem {
3520+
std::vector<std::string> SeenPaths;
3521+
3522+
InterceptorFS(IntrusiveRefCntPtr<FileSystem> UnderlyingFS)
3523+
: ProxyFileSystem(UnderlyingFS) {}
3524+
3525+
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
3526+
SeenPaths.push_back(Path.str());
3527+
return ProxyFileSystem::status(Path);
3528+
}
3529+
3530+
llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
3531+
openFileForRead(const Twine &Path) override {
3532+
SeenPaths.push_back(Path.str());
3533+
return ProxyFileSystem::openFileForRead(Path);
3534+
}
3535+
3536+
std::error_code isLocal(const Twine &Path, bool &Result) override {
3537+
SeenPaths.push_back(Path.str());
3538+
return ProxyFileSystem::isLocal(Path, Result);
3539+
}
3540+
3541+
vfs::directory_iterator dir_begin(const Twine &Dir,
3542+
std::error_code &EC) override {
3543+
SeenPaths.push_back(Dir.str());
3544+
return ProxyFileSystem::dir_begin(Dir, EC);
3545+
}
3546+
};
3547+
3548+
std::error_code EC;
3549+
auto BaseFS = makeIntrusiveRefCnt<DummyFileSystem>();
3550+
BaseFS->setCurrentWorkingDirectory("/cwd");
3551+
auto CheckFS = makeIntrusiveRefCnt<InterceptorFS>(BaseFS);
3552+
auto FS = vfs::RedirectingFileSystem::create({}, /*UseExternalNames=*/false,
3553+
*CheckFS);
3554+
3555+
FS->status("/a/../b");
3556+
FS->openFileForRead("c");
3557+
FS->exists("./d");
3558+
bool IsLocal = false;
3559+
FS->isLocal("/e/./../f", IsLocal);
3560+
FS->dir_begin(".././g", EC);
3561+
3562+
std::vector<std::string> Expected{"/a/../b", "/cwd/c", "/cwd/./d",
3563+
"/e/./../f", "/cwd/.././g"};
3564+
3565+
EXPECT_EQ(CheckFS->SeenPaths, Expected);
3566+
3567+
CheckFS->SeenPaths.clear();
3568+
FS->setRedirection(vfs::RedirectingFileSystem::RedirectKind::Fallback);
3569+
FS->status("/a/../b");
3570+
FS->openFileForRead("c");
3571+
FS->exists("./d");
3572+
FS->isLocal("/e/./../f", IsLocal);
3573+
FS->dir_begin(".././g", EC);
3574+
3575+
EXPECT_EQ(CheckFS->SeenPaths, Expected);
3576+
}

0 commit comments

Comments
 (0)