Skip to content

Commit 5a8a7ee

Browse files
authored
[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
1 parent 0ed7a5a commit 5a8a7ee

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
@@ -929,12 +929,12 @@ class RedirectingFileSystem
929929
/// Canonicalize path by removing ".", "..", "./", components. This is
930930
/// a VFS request, do not bother about symlinks in the path components
931931
/// but canonicalize in order to perform the correct entry search.
932-
std::error_code makeCanonical(SmallVectorImpl<char> &Path) const;
932+
std::error_code makeCanonicalForLookup(SmallVectorImpl<char> &Path) const;
933933

934934
/// Get the File status, or error, from the underlying external file system.
935935
/// This returns the status with the originally requested name, while looking
936-
/// up the entry using the canonical path.
937-
ErrorOr<Status> getExternalStatus(const Twine &CanonicalPath,
936+
/// up the entry using a potentially different path.
937+
ErrorOr<Status> getExternalStatus(const Twine &LookupPath,
938938
const Twine &OriginalPath) const;
939939

940940
/// Make \a Path an absolute path.
@@ -1022,7 +1022,7 @@ class RedirectingFileSystem
10221022
llvm::SmallVectorImpl<Entry *> &Entries) const;
10231023

10241024
/// Get the status for a path with the provided \c LookupResult.
1025-
ErrorOr<Status> status(const Twine &CanonicalPath, const Twine &OriginalPath,
1025+
ErrorOr<Status> status(const Twine &LookupPath, const Twine &OriginalPath,
10261026
const LookupResult &Result);
10271027

10281028
public:

llvm/lib/Support/VirtualFileSystem.cpp

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

1347-
if (makeCanonical(Path))
1347+
if (makeAbsolute(Path))
13481348
return {};
13491349

13501350
return ExternalFS->isLocal(Path, Result);
@@ -1411,7 +1411,7 @@ directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir,
14111411
SmallString<256> Path;
14121412
Dir.toVector(Path);
14131413

1414-
EC = makeCanonical(Path);
1414+
EC = makeAbsolute(Path);
14151415
if (EC)
14161416
return {};
14171417

@@ -2261,8 +2261,8 @@ void RedirectingFileSystem::LookupResult::getPath(
22612261
llvm::sys::path::append(Result, E->getName());
22622262
}
22632263

2264-
std::error_code
2265-
RedirectingFileSystem::makeCanonical(SmallVectorImpl<char> &Path) const {
2264+
std::error_code RedirectingFileSystem::makeCanonicalForLookup(
2265+
SmallVectorImpl<char> &Path) const {
22662266
if (std::error_code EC = makeAbsolute(Path))
22672267
return EC;
22682268

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

22782278
ErrorOr<RedirectingFileSystem::LookupResult>
22792279
RedirectingFileSystem::lookupPath(StringRef Path) const {
2280+
llvm::SmallString<128> CanonicalPath(Path);
2281+
if (std::error_code EC = makeCanonicalForLookup(CanonicalPath))
2282+
return EC;
2283+
22802284
// RedirectOnly means the VFS is always used.
22812285
if (UsageTrackingActive && Redirection == RedirectKind::RedirectOnly)
22822286
HasBeenUsed = true;
22832287

2284-
sys::path::const_iterator Start = sys::path::begin(Path);
2285-
sys::path::const_iterator End = sys::path::end(Path);
2288+
sys::path::const_iterator Start = sys::path::begin(CanonicalPath);
2289+
sys::path::const_iterator End = sys::path::end(CanonicalPath);
22862290
llvm::SmallVector<Entry *, 32> Entries;
22872291
for (const auto &Root : Roots) {
22882292
ErrorOr<RedirectingFileSystem::LookupResult> Result =
@@ -2358,14 +2362,14 @@ static Status getRedirectedFileStatus(const Twine &OriginalPath,
23582362
}
23592363

23602364
ErrorOr<Status> RedirectingFileSystem::status(
2361-
const Twine &CanonicalPath, const Twine &OriginalPath,
2365+
const Twine &LookupPath, const Twine &OriginalPath,
23622366
const RedirectingFileSystem::LookupResult &Result) {
23632367
if (std::optional<StringRef> ExtRedirect = Result.getExternalRedirect()) {
2364-
SmallString<256> CanonicalRemappedPath((*ExtRedirect).str());
2365-
if (std::error_code EC = makeCanonical(CanonicalRemappedPath))
2368+
SmallString<256> RemappedPath((*ExtRedirect).str());
2369+
if (std::error_code EC = makeAbsolute(RemappedPath))
23662370
return EC;
23672371

2368-
ErrorOr<Status> S = ExternalFS->status(CanonicalRemappedPath);
2372+
ErrorOr<Status> S = ExternalFS->status(RemappedPath);
23692373
if (!S)
23702374
return S;
23712375
S = Status::copyWithNewName(*S, *ExtRedirect);
@@ -2375,13 +2379,13 @@ ErrorOr<Status> RedirectingFileSystem::status(
23752379
}
23762380

23772381
auto *DE = cast<RedirectingFileSystem::DirectoryEntry>(Result.E);
2378-
return Status::copyWithNewName(DE->getStatus(), CanonicalPath);
2382+
return Status::copyWithNewName(DE->getStatus(), LookupPath);
23792383
}
23802384

23812385
ErrorOr<Status>
2382-
RedirectingFileSystem::getExternalStatus(const Twine &CanonicalPath,
2386+
RedirectingFileSystem::getExternalStatus(const Twine &LookupPath,
23832387
const Twine &OriginalPath) const {
2384-
auto Result = ExternalFS->status(CanonicalPath);
2388+
auto Result = ExternalFS->status(LookupPath);
23852389

23862390
// The path has been mapped by some nested VFS, don't override it with the
23872391
// original path.
@@ -2391,38 +2395,37 @@ RedirectingFileSystem::getExternalStatus(const Twine &CanonicalPath,
23912395
}
23922396

23932397
ErrorOr<Status> RedirectingFileSystem::status(const Twine &OriginalPath) {
2394-
SmallString<256> CanonicalPath;
2395-
OriginalPath.toVector(CanonicalPath);
2398+
SmallString<256> Path;
2399+
OriginalPath.toVector(Path);
23962400

2397-
if (std::error_code EC = makeCanonical(CanonicalPath))
2401+
if (std::error_code EC = makeAbsolute(Path))
23982402
return EC;
23992403

24002404
if (Redirection == RedirectKind::Fallback) {
24012405
// Attempt to find the original file first, only falling back to the
24022406
// mapped file if that fails.
2403-
ErrorOr<Status> S = getExternalStatus(CanonicalPath, OriginalPath);
2407+
ErrorOr<Status> S = getExternalStatus(Path, OriginalPath);
24042408
if (S)
24052409
return S;
24062410
}
24072411

2408-
ErrorOr<RedirectingFileSystem::LookupResult> Result =
2409-
lookupPath(CanonicalPath);
2412+
ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
24102413
if (!Result) {
24112414
// Was not able to map file, fallthrough to using the original path if
24122415
// that was the specified redirection type.
24132416
if (Redirection == RedirectKind::Fallthrough &&
24142417
isFileNotFound(Result.getError()))
2415-
return getExternalStatus(CanonicalPath, OriginalPath);
2418+
return getExternalStatus(Path, OriginalPath);
24162419
return Result.getError();
24172420
}
24182421

2419-
ErrorOr<Status> S = status(CanonicalPath, OriginalPath, *Result);
2422+
ErrorOr<Status> S = status(Path, OriginalPath, *Result);
24202423
if (!S && Redirection == RedirectKind::Fallthrough &&
24212424
isFileNotFound(S.getError(), Result->E)) {
24222425
// Mapped the file but it wasn't found in the underlying filesystem,
24232426
// fallthrough to using the original path if that was the specified
24242427
// redirection type.
2425-
return getExternalStatus(CanonicalPath, OriginalPath);
2428+
return getExternalStatus(Path, OriginalPath);
24262429
}
24272430

24282431
return S;
@@ -2471,53 +2474,49 @@ File::getWithPath(ErrorOr<std::unique_ptr<File>> Result, const Twine &P) {
24712474

24722475
ErrorOr<std::unique_ptr<File>>
24732476
RedirectingFileSystem::openFileForRead(const Twine &OriginalPath) {
2474-
SmallString<256> CanonicalPath;
2475-
OriginalPath.toVector(CanonicalPath);
2477+
SmallString<256> Path;
2478+
OriginalPath.toVector(Path);
24762479

2477-
if (std::error_code EC = makeCanonical(CanonicalPath))
2480+
if (std::error_code EC = makeAbsolute(Path))
24782481
return EC;
24792482

24802483
if (Redirection == RedirectKind::Fallback) {
24812484
// Attempt to find the original file first, only falling back to the
24822485
// mapped file if that fails.
2483-
auto F = File::getWithPath(ExternalFS->openFileForRead(CanonicalPath),
2484-
OriginalPath);
2486+
auto F = File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
24852487
if (F)
24862488
return F;
24872489
}
24882490

2489-
ErrorOr<RedirectingFileSystem::LookupResult> Result =
2490-
lookupPath(CanonicalPath);
2491+
ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
24912492
if (!Result) {
24922493
// Was not able to map file, fallthrough to using the original path if
24932494
// that was the specified redirection type.
24942495
if (Redirection == RedirectKind::Fallthrough &&
24952496
isFileNotFound(Result.getError()))
2496-
return File::getWithPath(ExternalFS->openFileForRead(CanonicalPath),
2497-
OriginalPath);
2497+
return File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
24982498
return Result.getError();
24992499
}
25002500

25012501
if (!Result->getExternalRedirect()) // FIXME: errc::not_a_file?
25022502
return make_error_code(llvm::errc::invalid_argument);
25032503

25042504
StringRef ExtRedirect = *Result->getExternalRedirect();
2505-
SmallString<256> CanonicalRemappedPath(ExtRedirect.str());
2506-
if (std::error_code EC = makeCanonical(CanonicalRemappedPath))
2505+
SmallString<256> RemappedPath(ExtRedirect.str());
2506+
if (std::error_code EC = makeAbsolute(RemappedPath))
25072507
return EC;
25082508

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

2511-
auto ExternalFile = File::getWithPath(
2512-
ExternalFS->openFileForRead(CanonicalRemappedPath), ExtRedirect);
2511+
auto ExternalFile =
2512+
File::getWithPath(ExternalFS->openFileForRead(RemappedPath), ExtRedirect);
25132513
if (!ExternalFile) {
25142514
if (Redirection == RedirectKind::Fallthrough &&
25152515
isFileNotFound(ExternalFile.getError(), Result->E)) {
25162516
// Mapped the file but it wasn't found in the underlying filesystem,
25172517
// fallthrough to using the original path if that was the specified
25182518
// redirection type.
2519-
return File::getWithPath(ExternalFS->openFileForRead(CanonicalPath),
2520-
OriginalPath);
2519+
return File::getWithPath(ExternalFS->openFileForRead(Path), OriginalPath);
25212520
}
25222521
return ExternalFile;
25232522
}
@@ -2537,28 +2536,27 @@ RedirectingFileSystem::openFileForRead(const Twine &OriginalPath) {
25372536
std::error_code
25382537
RedirectingFileSystem::getRealPath(const Twine &OriginalPath,
25392538
SmallVectorImpl<char> &Output) const {
2540-
SmallString<256> CanonicalPath;
2541-
OriginalPath.toVector(CanonicalPath);
2539+
SmallString<256> Path;
2540+
OriginalPath.toVector(Path);
25422541

2543-
if (std::error_code EC = makeCanonical(CanonicalPath))
2542+
if (std::error_code EC = makeAbsolute(Path))
25442543
return EC;
25452544

25462545
if (Redirection == RedirectKind::Fallback) {
25472546
// Attempt to find the original file first, only falling back to the
25482547
// mapped file if that fails.
2549-
std::error_code EC = ExternalFS->getRealPath(CanonicalPath, Output);
2548+
std::error_code EC = ExternalFS->getRealPath(Path, Output);
25502549
if (!EC)
25512550
return EC;
25522551
}
25532552

2554-
ErrorOr<RedirectingFileSystem::LookupResult> Result =
2555-
lookupPath(CanonicalPath);
2553+
ErrorOr<RedirectingFileSystem::LookupResult> Result = lookupPath(Path);
25562554
if (!Result) {
25572555
// Was not able to map file, fallthrough to using the original path if
25582556
// that was the specified redirection type.
25592557
if (Redirection == RedirectKind::Fallthrough &&
25602558
isFileNotFound(Result.getError()))
2561-
return ExternalFS->getRealPath(CanonicalPath, Output);
2559+
return ExternalFS->getRealPath(Path, Output);
25622560
return Result.getError();
25632561
}
25642562

@@ -2571,7 +2569,7 @@ RedirectingFileSystem::getRealPath(const Twine &OriginalPath,
25712569
// Mapped the file but it wasn't found in the underlying filesystem,
25722570
// fallthrough to using the original path if that was the specified
25732571
// redirection type.
2574-
return ExternalFS->getRealPath(CanonicalPath, Output);
2572+
return ExternalFS->getRealPath(Path, Output);
25752573
}
25762574
return P;
25772575
}

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"
@@ -3330,3 +3332,66 @@ TEST(RedirectingFileSystemTest, Used) {
33303332
EXPECT_TRUE(Redirecting1->hasBeenUsed());
33313333
EXPECT_FALSE(Redirecting2->hasBeenUsed());
33323334
}
3335+
3336+
// Check that paths looked up in the external filesystem are unmodified, except
3337+
// potentially to add the working directory. We cannot canonicalize away ..
3338+
// in the presence of symlinks in the external filesystem.
3339+
TEST(RedirectingFileSystemTest, ExternalPaths) {
3340+
struct InterceptorFS : llvm::vfs::ProxyFileSystem {
3341+
std::vector<std::string> SeenPaths;
3342+
3343+
InterceptorFS(IntrusiveRefCntPtr<FileSystem> UnderlyingFS)
3344+
: ProxyFileSystem(UnderlyingFS) {}
3345+
3346+
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
3347+
SeenPaths.push_back(Path.str());
3348+
return ProxyFileSystem::status(Path);
3349+
}
3350+
3351+
llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
3352+
openFileForRead(const Twine &Path) override {
3353+
SeenPaths.push_back(Path.str());
3354+
return ProxyFileSystem::openFileForRead(Path);
3355+
}
3356+
3357+
std::error_code isLocal(const Twine &Path, bool &Result) override {
3358+
SeenPaths.push_back(Path.str());
3359+
return ProxyFileSystem::isLocal(Path, Result);
3360+
}
3361+
3362+
vfs::directory_iterator dir_begin(const Twine &Dir,
3363+
std::error_code &EC) override {
3364+
SeenPaths.push_back(Dir.str());
3365+
return ProxyFileSystem::dir_begin(Dir, EC);
3366+
}
3367+
};
3368+
3369+
std::error_code EC;
3370+
auto BaseFS = makeIntrusiveRefCnt<DummyFileSystem>();
3371+
BaseFS->setCurrentWorkingDirectory("/cwd");
3372+
auto CheckFS = makeIntrusiveRefCnt<InterceptorFS>(BaseFS);
3373+
auto FS = vfs::RedirectingFileSystem::create({}, /*UseExternalNames=*/false,
3374+
*CheckFS);
3375+
3376+
FS->status("/a/../b");
3377+
FS->openFileForRead("c");
3378+
FS->exists("./d");
3379+
bool IsLocal = false;
3380+
FS->isLocal("/e/./../f", IsLocal);
3381+
FS->dir_begin(".././g", EC);
3382+
3383+
std::vector<std::string> Expected{"/a/../b", "/cwd/c", "/cwd/./d",
3384+
"/e/./../f", "/cwd/.././g"};
3385+
3386+
EXPECT_EQ(CheckFS->SeenPaths, Expected);
3387+
3388+
CheckFS->SeenPaths.clear();
3389+
FS->setRedirection(vfs::RedirectingFileSystem::RedirectKind::Fallback);
3390+
FS->status("/a/../b");
3391+
FS->openFileForRead("c");
3392+
FS->exists("./d");
3393+
FS->isLocal("/e/./../f", IsLocal);
3394+
FS->dir_begin(".././g", EC);
3395+
3396+
EXPECT_EQ(CheckFS->SeenPaths, Expected);
3397+
}

0 commit comments

Comments
 (0)