clang 23.0.0git
FileManager.cpp
Go to the documentation of this file.
1//===--- FileManager.cpp - File System Probing and Caching ----------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the FileManager interface.
10//
11//===----------------------------------------------------------------------===//
12//
13// TODO: This should index all interesting directories with dirent calls.
14// getdirentries ?
15// opendir/readdir_r/closedir ?
16//
17//===----------------------------------------------------------------------===//
18
20#include "llvm/ADT/SmallString.h"
21#include "llvm/ADT/Statistic.h"
22#include "llvm/Config/llvm-config.h"
23#include "llvm/Support/Error.h"
24#include "llvm/Support/FileSystem.h"
25#include "llvm/Support/IOSandbox.h"
26#include "llvm/Support/MemoryBuffer.h"
27#include "llvm/Support/Path.h"
28#include "llvm/Support/raw_ostream.h"
29#include <cassert>
30#include <climits>
31#include <cstdint>
32#include <cstdlib>
33#include <optional>
34#include <string>
35#include <utility>
36
37using namespace clang;
38
39#define DEBUG_TYPE "file-search"
40
41static void normalizeCacheKey(StringRef &Path,
42 std::optional<std::string> &Storage) {
43 using namespace llvm::sys::path;
44
45 // Drop trailing separators for non-root paths so that cache keys and `stat`
46 // queries use a single spelling. Keep root paths (`/`, `[A-Z]:\`) unchanged.
47 if (Path.size() > 1 && root_path(Path) != Path && is_separator(Path.back()))
48 Path = Path.drop_back();
49
50 // A bare drive path like "[A-Z]:" is drive-relative (current directory on the
51 // drive). As `[A-Z]:` is not a path specification, we must canonicalise it
52 // to `[A-Z]:.`.
53 if (is_style_windows(Style::native)) {
54 if (Path.size() > 1 && Path.back() == ':' &&
55 Path.equals_insensitive(root_name(Path))) {
56 Storage = Path.str() + ".";
57 Path = *Storage;
58 }
59 }
60}
61
62//===----------------------------------------------------------------------===//
63// Common logic.
64//===----------------------------------------------------------------------===//
65
68 : FS(std::move(FS)), FileSystemOpts(FSO), SeenDirEntries(64),
69 SeenFileEntries(64), NextFileUID(0) {
70 // If the caller doesn't provide a virtual file system, just grab the real
71 // file system.
72 if (!this->FS)
73 this->FS = llvm::vfs::getRealFileSystem();
74}
75
77
78llvm::ErrorOr<DirectoryEntryRef>
79FileManager::getDirectoryFromFile(StringRef Filename, bool CacheFailure) {
80 if (Filename.empty())
81 return make_error_code(std::errc::no_such_file_or_directory);
82
83 if (llvm::sys::path::is_separator(Filename[Filename.size() - 1]))
84 return make_error_code(std::errc::is_a_directory);
85
86 StringRef DirName = llvm::sys::path::parent_path(Filename);
87 // Use the current directory if file has no path component.
88 if (DirName.empty())
89 DirName = ".";
90
91 return getDirectoryRefImpl(DirName, CacheFailure);
92}
93
94DirectoryEntry *&FileManager::getRealDirEntry(const llvm::vfs::Status &Status) {
95 assert(Status.isDirectory() && "The directory should exist!");
96 // See if we have already opened a directory with the
97 // same inode (this occurs on Unix-like systems when one dir is
98 // symlinked to another, for example) or the same path (on
99 // Windows).
100 DirectoryEntry *&UDE = UniqueRealDirs[Status.getUniqueID()];
101
102 if (!UDE) {
103 // We don't have this directory yet, add it. We use the string
104 // key from the SeenDirEntries map as the string.
105 UDE = new (DirsAlloc.Allocate()) DirectoryEntry();
106 }
107 return UDE;
108}
109
110/// Add all ancestors of the given path (pointing to either a file or
111/// a directory) as virtual directories.
112void FileManager::addAncestorsAsVirtualDirs(StringRef Path) {
113 StringRef DirName = llvm::sys::path::parent_path(Path);
114 if (DirName.empty())
115 DirName = ".";
116
117 // Normalize the key for cache lookup/insert, but keep the original DirName
118 // for recursive processing since normalization can create paths that don't
119 // work well with parent_path() (e.g., "C:" -> "C:.").
120 std::optional<std::string> Storage;
121 StringRef OriginalDirName = DirName;
122 normalizeCacheKey(DirName, Storage);
123
124 auto &NamedDirEnt = *SeenDirEntries.insert(
125 {DirName, std::errc::no_such_file_or_directory}).first;
126
127 // When caching a virtual directory, we always cache its ancestors
128 // at the same time. Therefore, if DirName is already in the cache,
129 // we don't need to recurse as its ancestors must also already be in
130 // the cache (or it's a known non-virtual directory).
131 if (NamedDirEnt.second)
132 return;
133
134 // Check to see if the directory exists.
135 llvm::vfs::Status Status;
136 auto statError =
137 getStatValue(DirName, Status, false, nullptr /*directory lookup*/);
138 if (statError) {
139 // There's no real directory at the given path.
140 // Add the virtual directory to the cache.
141 auto *UDE = new (DirsAlloc.Allocate()) DirectoryEntry();
142 NamedDirEnt.second = *UDE;
143 VirtualDirectoryEntries.push_back(UDE);
144 } else {
145 // There is the real directory
146 DirectoryEntry *&UDE = getRealDirEntry(Status);
147 NamedDirEnt.second = *UDE;
148 }
149
150 // Recursively add the other ancestors.
151 addAncestorsAsVirtualDirs(OriginalDirName);
152}
153
154llvm::ErrorOr<DirectoryEntryRef>
155FileManager::getDirectoryRefImpl(StringRef DirName, bool CacheFailure) {
156 std::optional<std::string> DirNameStr;
157 normalizeCacheKey(DirName, DirNameStr);
158
159 ++NumDirLookups;
160
161 // See if there was already an entry in the map. Note that the map
162 // contains both virtual and real directories.
163 auto SeenDirInsertResult =
164 SeenDirEntries.insert({DirName, std::errc::no_such_file_or_directory});
165 if (!SeenDirInsertResult.second) {
166 if (SeenDirInsertResult.first->second)
167 return DirectoryEntryRef(*SeenDirInsertResult.first);
168 return SeenDirInsertResult.first->second.getError();
169 }
170
171 // We've not seen this before. Fill it in.
172 ++NumDirCacheMisses;
173 auto &NamedDirEnt = *SeenDirInsertResult.first;
174 assert(!NamedDirEnt.second && "should be newly-created");
175
176 // Get the null-terminated directory name as stored as the key of the
177 // SeenDirEntries map.
178 StringRef InterndDirName = NamedDirEnt.first();
179
180 // Check to see if the directory exists.
181 llvm::vfs::Status Status;
182 auto statError = getStatValue(InterndDirName, Status, false,
183 nullptr /*directory lookup*/);
184 if (statError) {
185 // There's no real directory at the given path.
186 if (CacheFailure)
187 NamedDirEnt.second = statError;
188 else
189 SeenDirEntries.erase(DirName);
190 return statError;
191 }
192
193 // It exists.
194 DirectoryEntry *&UDE = getRealDirEntry(Status);
195 NamedDirEnt.second = *UDE;
196
197 return DirectoryEntryRef(NamedDirEnt);
198}
199
200llvm::ErrorOr<FileEntryRef> FileManager::getFileRefImpl(StringRef Filename,
201 bool openFile,
202 bool CacheFailure,
203 bool IsText) {
204 ++NumFileLookups;
205
206 // See if there is already an entry in the map.
207 auto SeenFileInsertResult =
208 SeenFileEntries.insert({Filename, std::errc::no_such_file_or_directory});
209 if (!SeenFileInsertResult.second) {
210 if (!SeenFileInsertResult.first->second)
211 return SeenFileInsertResult.first->second.getError();
212 return FileEntryRef(*SeenFileInsertResult.first);
213 }
214
215 // We've not seen this before. Fill it in.
216 ++NumFileCacheMisses;
217 auto *NamedFileEnt = &*SeenFileInsertResult.first;
218 assert(!NamedFileEnt->second && "should be newly-created");
219
220 // Get the null-terminated file name as stored as the key of the
221 // SeenFileEntries map.
222 StringRef InterndFileName = NamedFileEnt->first();
223
224 // Look up the directory for the file. When looking up something like
225 // sys/foo.h we'll discover all of the search directories that have a 'sys'
226 // subdirectory. This will let us avoid having to waste time on known-to-fail
227 // searches when we go to find sys/bar.h, because all the search directories
228 // without a 'sys' subdir will get a cached failure result.
229 auto DirInfoOrErr = getDirectoryFromFile(Filename, CacheFailure);
230 if (!DirInfoOrErr) { // Directory doesn't exist, file can't exist.
231 std::error_code Err = DirInfoOrErr.getError();
232 if (CacheFailure)
233 NamedFileEnt->second = Err;
234 else
235 SeenFileEntries.erase(Filename);
236
237 return Err;
238 }
239 DirectoryEntryRef DirInfo = *DirInfoOrErr;
240
241 // FIXME: Use the directory info to prune this, before doing the stat syscall.
242 // FIXME: This will reduce the # syscalls.
243
244 // Check to see if the file exists.
245 std::unique_ptr<llvm::vfs::File> F;
246 llvm::vfs::Status Status;
247 auto statError = getStatValue(InterndFileName, Status, true,
248 openFile ? &F : nullptr, IsText);
249 if (statError) {
250 // There's no real file at the given path.
251 if (CacheFailure)
252 NamedFileEnt->second = statError;
253 else
254 SeenFileEntries.erase(Filename);
255
256 return statError;
257 }
258
259 assert((openFile || !F) && "undesired open file");
260
261 // It exists. See if we have already opened a file with the same inode.
262 // This occurs when one dir is symlinked to another, for example.
263 FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()];
264 bool ReusingEntry = UFE != nullptr;
265 if (!UFE)
266 UFE = new (FilesAlloc.Allocate()) FileEntry();
267
268 if (!Status.ExposesExternalVFSPath || Status.getName() == Filename) {
269 // Use the requested name. Set the FileEntry.
270 NamedFileEnt->second = FileEntryRef::MapValue(*UFE, DirInfo);
271 } else {
272 // Name mismatch. We need a redirect. First grab the actual entry we want
273 // to return.
274 //
275 // This redirection logic intentionally leaks the external name of a
276 // redirected file that uses 'use-external-name' in \a
277 // vfs::RedirectionFileSystem. This allows clang to report the external
278 // name to users (in diagnostics) and to tools that don't have access to
279 // the VFS (in debug info and dependency '.d' files).
280 //
281 // FIXME: This is pretty complex and has some very complicated interactions
282 // with the rest of clang. It's also inconsistent with how "real"
283 // filesystems behave and confuses parts of clang expect to see the
284 // name-as-accessed on the \a FileEntryRef.
285 //
286 // A potential plan to remove this is as follows -
287 // - Update callers such as `HeaderSearch::findUsableModuleForHeader()`
288 // to explicitly use the `getNameAsRequested()` rather than just using
289 // `getName()`.
290 // - Add a `FileManager::getExternalPath` API for explicitly getting the
291 // remapped external filename when there is one available. Adopt it in
292 // callers like diagnostics/deps reporting instead of calling
293 // `getName()` directly.
294 // - Switch the meaning of `FileEntryRef::getName()` to get the requested
295 // name, not the external name. Once that sticks, revert callers that
296 // want the requested name back to calling `getName()`.
297 // - Update the VFS to always return the requested name. This could also
298 // return the external name, or just have an API to request it
299 // lazily. The latter has the benefit of making accesses of the
300 // external path easily tracked, but may also require extra work than
301 // just returning up front.
302 // - (Optionally) Add an API to VFS to get the external filename lazily
303 // and update `FileManager::getExternalPath()` to use it instead. This
304 // has the benefit of making such accesses easily tracked, though isn't
305 // necessarily required (and could cause extra work than just adding to
306 // eg. `vfs::Status` up front).
307 auto &Redirection =
308 *SeenFileEntries
309 .insert({Status.getName(), FileEntryRef::MapValue(*UFE, DirInfo)})
310 .first;
311 assert(isa<FileEntry *>(Redirection.second->V) &&
312 "filename redirected to a non-canonical filename?");
313 assert(cast<FileEntry *>(Redirection.second->V) == UFE &&
314 "filename from getStatValue() refers to wrong file");
315
316 // Cache the redirection in the previously-inserted entry, still available
317 // in the tentative return value.
318 NamedFileEnt->second = FileEntryRef::MapValue(Redirection, DirInfo);
319 }
320
321 FileEntryRef ReturnedRef(*NamedFileEnt);
322 if (ReusingEntry) { // Already have an entry with this inode, return it.
323 return ReturnedRef;
324 }
325
326 // Otherwise, we don't have this file yet, add it.
327 UFE->Size = Status.getSize();
328 UFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
329 UFE->Dir = &DirInfo.getDirEntry();
330 UFE->UID = NextFileUID++;
331 UFE->UniqueID = Status.getUniqueID();
332 UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
333 UFE->IsDeviceFile =
334 Status.getType() == llvm::sys::fs::file_type::character_file;
335 UFE->File = std::move(F);
336
337 if (UFE->File) {
338 if (auto PathName = UFE->File->getName())
339 fillRealPathName(UFE, *PathName);
340 } else if (!openFile) {
341 // We should still fill the path even if we aren't opening the file.
342 fillRealPathName(UFE, InterndFileName);
343 }
344 return ReturnedRef;
345}
346
348 // Only read stdin once.
349 if (STDIN)
350 return *STDIN;
351
352 auto ContentOrError = [] {
353 auto BypassSandbox = llvm::sys::sandbox::scopedDisable();
354 return llvm::MemoryBuffer::getSTDIN();
355 }();
356
357 if (!ContentOrError)
358 return llvm::createFileError("-", ContentOrError.getError());
359
360 auto Content = std::move(*ContentOrError);
361 STDIN = getVirtualFileRef(Content->getBufferIdentifier(),
362 Content->getBufferSize(), 0);
363 FileEntry &FE = const_cast<FileEntry &>(STDIN->getFileEntry());
364 FE.Content = std::move(Content);
365 FE.IsNamedPipe = true;
366 return *STDIN;
367}
368
369void FileManager::trackVFSUsage(bool Active) {
370 FS->visit([Active](llvm::vfs::FileSystem &FileSys) {
371 if (auto *RFS = dyn_cast<llvm::vfs::RedirectingFileSystem>(&FileSys))
372 RFS->setUsageTrackingActive(Active);
373 });
374}
375
376FileEntryRef FileManager::getVirtualFileRef(StringRef Filename, off_t Size,
377 time_t ModificationTime) {
378 ++NumFileLookups;
379
380 // See if there is already an entry in the map for an existing file.
381 auto &NamedFileEnt = *SeenFileEntries.insert(
382 {Filename, std::errc::no_such_file_or_directory}).first;
383 if (NamedFileEnt.second) {
384 FileEntryRef::MapValue Value = *NamedFileEnt.second;
385 if (LLVM_LIKELY(isa<FileEntry *>(Value.V)))
386 return FileEntryRef(NamedFileEnt);
388 }
389
390 // We've not seen this before, or the file is cached as non-existent.
391 ++NumFileCacheMisses;
392 addAncestorsAsVirtualDirs(Filename);
393 FileEntry *UFE = nullptr;
394
395 // Now that all ancestors of Filename are in the cache, the
396 // following call is guaranteed to find the DirectoryEntry from the
397 // cache. A virtual file can also have an empty filename, that could come
398 // from a source location preprocessor directive with an empty filename as
399 // an example, so we need to pretend it has a name to ensure a valid directory
400 // entry can be returned.
401 auto DirInfo = getDirectoryFromFile(Filename.empty() ? "." : Filename,
402 /*CacheFailure=*/true);
403 assert(DirInfo &&
404 "The directory of a virtual file should already be in the cache.");
405
406 // Check to see if the file exists. If so, drop the virtual file
407 llvm::vfs::Status Status;
408 const char *InterndFileName = NamedFileEnt.first().data();
409 if (!getStatValue(InterndFileName, Status, true, nullptr)) {
410 Status = llvm::vfs::Status(
411 Status.getName(), Status.getUniqueID(),
412 llvm::sys::toTimePoint(ModificationTime),
413 Status.getUser(), Status.getGroup(), Size,
414 Status.getType(), Status.getPermissions());
415
416 auto &RealFE = UniqueRealFiles[Status.getUniqueID()];
417 if (RealFE) {
418 // If we had already opened this file, close it now so we don't
419 // leak the descriptor. We're not going to use the file
420 // descriptor anyway, since this is a virtual file.
421 if (RealFE->File)
422 RealFE->closeFile();
423 // If we already have an entry with this inode, return it.
424 //
425 // FIXME: Surely this should add a reference by the new name, and return
426 // it instead...
427 NamedFileEnt.second = FileEntryRef::MapValue(*RealFE, *DirInfo);
428 return FileEntryRef(NamedFileEnt);
429 }
430 // File exists, but no entry - create it.
431 RealFE = new (FilesAlloc.Allocate()) FileEntry();
432 RealFE->UniqueID = Status.getUniqueID();
433 RealFE->IsNamedPipe =
434 Status.getType() == llvm::sys::fs::file_type::fifo_file;
435 fillRealPathName(RealFE, Status.getName());
436
437 UFE = RealFE;
438 } else {
439 // File does not exist, create a virtual entry.
440 UFE = new (FilesAlloc.Allocate()) FileEntry();
441 VirtualFileEntries.push_back(UFE);
442 }
443
444 NamedFileEnt.second = FileEntryRef::MapValue(*UFE, *DirInfo);
445 UFE->Size = Size;
446 UFE->ModTime = ModificationTime;
447 UFE->Dir = &DirInfo->getDirEntry();
448 UFE->UID = NextFileUID++;
449 UFE->File.reset();
450 return FileEntryRef(NamedFileEnt);
451}
452
454 // Stat of the file and return nullptr if it doesn't exist.
455 llvm::vfs::Status Status;
456 if (getStatValue(VF.getName(), Status, /*isFile=*/true, /*F=*/nullptr))
457 return std::nullopt;
458
459 if (!SeenBypassFileEntries)
460 SeenBypassFileEntries = std::make_unique<
461 llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>>>();
462
463 // If we've already bypassed just use the existing one.
464 auto Insertion = SeenBypassFileEntries->insert(
465 {VF.getName(), std::errc::no_such_file_or_directory});
466 if (!Insertion.second)
467 return FileEntryRef(*Insertion.first);
468
469 // Fill in the new entry from the stat.
470 FileEntry *BFE = new (FilesAlloc.Allocate()) FileEntry();
471 BypassFileEntries.push_back(BFE);
472 Insertion.first->second = FileEntryRef::MapValue(*BFE, VF.getDir());
473 BFE->Size = Status.getSize();
474 BFE->Dir = VF.getFileEntry().Dir;
475 BFE->ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
476 BFE->UID = NextFileUID++;
477
478 // Save the entry in the bypass table and return.
479 return FileEntryRef(*Insertion.first);
480}
481
483 SmallVectorImpl<char> &Path) {
484 StringRef pathRef(Path.data(), Path.size());
485
486 if (FileSystemOpts.WorkingDir.empty()
487 || llvm::sys::path::is_absolute(pathRef))
488 return false;
489
490 SmallString<128> NewPath(FileSystemOpts.WorkingDir);
491 llvm::sys::path::append(NewPath, pathRef);
492 Path = std::move(NewPath);
493 return true;
494}
495
497 bool Canonicalize) const {
498 bool Changed = FixupRelativePath(Path);
499
500 if (!llvm::sys::path::is_absolute(StringRef(Path.data(), Path.size()))) {
501 FS->makeAbsolute(Path);
502 Changed = true;
503 }
504
505 if (Canonicalize)
506 Changed |= llvm::sys::path::remove_dots(Path);
507
508 return Changed;
509}
510
511void FileManager::fillRealPathName(FileEntry *UFE, llvm::StringRef FileName) {
513 // This is not the same as `VFS::getRealPath()`, which resolves symlinks
514 // but can be very expensive on real file systems.
515 // FIXME: the semantic of RealPathName is unclear, and the name might be
516 // misleading. We need to clean up the interface here.
517 makeAbsolutePath(AbsPath);
518 llvm::sys::path::remove_dots(AbsPath, /*remove_dot_dot=*/true);
519 UFE->RealPathName = std::string(AbsPath);
520}
521
522llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
524 bool RequiresNullTerminator,
525 std::optional<int64_t> MaybeLimit, bool IsText) {
526 const FileEntry *Entry = &FE.getFileEntry();
527 // If the content is living on the file entry, return a reference to it.
528 if (Entry->Content)
529 return llvm::MemoryBuffer::getMemBuffer(Entry->Content->getMemBufferRef());
530
531 uint64_t FileSize = Entry->getSize();
532
533 if (MaybeLimit)
534 FileSize = *MaybeLimit;
535
536 // If there's a high enough chance that the file have changed since we
537 // got its size, force a stat before opening it.
538 if (isVolatile || Entry->isNamedPipe())
539 FileSize = -1;
540
541 StringRef Filename = FE.getName();
542 // If the file is already open, use the open file descriptor.
543 if (Entry->File) {
544 auto Result = Entry->File->getBuffer(Filename, FileSize,
545 RequiresNullTerminator, isVolatile);
546 Entry->closeFile();
547 return Result;
548 }
549
550 // Otherwise, open the file.
551 return getBufferForFileImpl(Filename, FileSize, isVolatile,
552 RequiresNullTerminator, IsText);
553}
554
555llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
556FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize,
557 bool isVolatile, bool RequiresNullTerminator,
558 bool IsText) const {
559 if (FileSystemOpts.WorkingDir.empty())
560 return FS->getBufferForFile(Filename, FileSize, RequiresNullTerminator,
561 isVolatile, IsText);
562
563 SmallString<128> FilePath(Filename);
564 FixupRelativePath(FilePath);
565 return FS->getBufferForFile(FilePath, FileSize, RequiresNullTerminator,
566 isVolatile, IsText);
567}
568
569std::error_code FileManager::getStatValue(StringRef Path,
570 llvm::vfs::Status &Status,
571 bool isFile,
572 std::unique_ptr<llvm::vfs::File> *F,
573 bool IsText) {
574 SmallString<128> FilePath;
575
576 // FIXME: FileSystemOpts shouldn't be passed in here, all paths should be
577 // absolute!
578 if (!FileSystemOpts.WorkingDir.empty()) {
579 FilePath = Path;
580 FixupRelativePath(FilePath);
581 Path = FilePath;
582 }
583
584 bool isForDir = !isFile;
585 std::error_code RetCode;
586
587 if (isForDir || !F) {
588 // If this is a directory or a file descriptor is not needed, just go to the
589 // file system.
590 llvm::ErrorOr<llvm::vfs::Status> StatusOrErr = FS->status(Path);
591 if (!StatusOrErr) {
592 RetCode = StatusOrErr.getError();
593 } else {
594 Status = *StatusOrErr;
595 }
596 } else {
597 // We can always just use 'stat' here, but (for files) the client is asking
598 // whether the file exists because it wants to turn around and *open* it.
599 // It is more efficient to do "open+fstat" on success than it is to do
600 // "stat+open".
601 //
602 // Because of this, check to see if the file exists with 'open'. If the
603 // open succeeds, use fstat to get the stat info.
604 auto OwnedFile =
605 IsText ? FS->openFileForRead(Path) : FS->openFileForReadBinary(Path);
606
607 if (!OwnedFile) {
608 // If the open fails, our "stat" fails.
609 RetCode = OwnedFile.getError();
610 } else {
611 // Otherwise, the open succeeded. Do an fstat to get the information
612 // about the file. We'll end up returning the open file descriptor to the
613 // client to do what they please with it.
614 llvm::ErrorOr<llvm::vfs::Status> StatusOrErr = (*OwnedFile)->status();
615 if (StatusOrErr) {
616 Status = *StatusOrErr;
617 *F = std::move(*OwnedFile);
618 } else {
619 // fstat rarely fails. If it does, claim the initial open didn't
620 // succeed.
621 *F = nullptr;
622 RetCode = StatusOrErr.getError();
623 }
624 }
625 }
626
627 // If the path doesn't exist, return failure.
628 if (RetCode)
629 return RetCode;
630
631 // If the path exists, make sure that its "directoryness" matches the clients
632 // demands.
633 if (Status.isDirectory() != isForDir) {
634 // If not, close the file if opened.
635 if (F)
636 *F = nullptr;
637 return std::make_error_code(Status.isDirectory()
638 ? std::errc::is_a_directory
639 : std::errc::not_a_directory);
640 }
641
642 return std::error_code();
643}
644
646 return getCanonicalName(Dir, Dir.getName());
647}
648
652
653StringRef FileManager::getCanonicalName(const void *Entry, StringRef Name) {
654 llvm::DenseMap<const void *, llvm::StringRef>::iterator Known =
655 CanonicalNames.find(Entry);
656 if (Known != CanonicalNames.end())
657 return Known->second;
658
659 // Name comes from FileEntry/DirectoryEntry::getName(), so it is safe to
660 // store it in the DenseMap below.
661 StringRef CanonicalName(Name);
662
663 SmallString<256> AbsPathBuf;
664 SmallString<256> RealPathBuf;
665 if (!FS->getRealPath(Name, RealPathBuf)) {
666 if (is_style_windows(llvm::sys::path::Style::native)) {
667 // For Windows paths, only use the real path if it doesn't resolve
668 // a substitute drive, as those are used to avoid MAX_PATH issues.
669 AbsPathBuf = Name;
670 if (!FS->makeAbsolute(AbsPathBuf)) {
671 if (llvm::sys::path::root_name(RealPathBuf) ==
672 llvm::sys::path::root_name(AbsPathBuf)) {
673 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
674 } else {
675 // Fallback to using the absolute path.
676 // Simplifying /../ is semantically valid on Windows even in the
677 // presence of symbolic links.
678 llvm::sys::path::remove_dots(AbsPathBuf, /*remove_dot_dot=*/true);
679 CanonicalName = AbsPathBuf.str().copy(CanonicalNameStorage);
680 }
681 }
682 } else {
683 CanonicalName = RealPathBuf.str().copy(CanonicalNameStorage);
684 }
685 }
686
687 CanonicalNames.insert({Entry, CanonicalName});
688 return CanonicalName;
689}
690
692 assert(&Other != this && "Collecting stats into the same FileManager");
693 NumDirLookups += Other.NumDirLookups;
694 NumFileLookups += Other.NumFileLookups;
695 NumDirCacheMisses += Other.NumDirCacheMisses;
696 NumFileCacheMisses += Other.NumFileCacheMisses;
697}
698
700 llvm::errs() << "\n*** File Manager Stats:\n";
701 llvm::errs() << UniqueRealFiles.size() << " real files found, "
702 << UniqueRealDirs.size() << " real dirs found.\n";
703 llvm::errs() << VirtualFileEntries.size() << " virtual files found, "
704 << VirtualDirectoryEntries.size() << " virtual dirs found.\n";
705 llvm::errs() << NumDirLookups << " dir lookups, "
706 << NumDirCacheMisses << " dir cache misses.\n";
707 llvm::errs() << NumFileLookups << " file lookups, "
708 << NumFileCacheMisses << " file cache misses.\n";
709
710 getVirtualFileSystem().visit([](llvm::vfs::FileSystem &VFS) {
711 if (auto *T = dyn_cast_or_null<llvm::vfs::TracingFileSystem>(&VFS))
712 llvm::errs() << "\n*** Virtual File System Stats:\n"
713 << T->NumStatusCalls << " status() calls\n"
714 << T->NumOpenFileForReadCalls << " openFileForRead() calls\n"
715 << T->NumDirBeginCalls << " dir_begin() calls\n"
716 << T->NumGetRealPathCalls << " getRealPath() calls\n"
717 << T->NumExistsCalls << " exists() calls\n"
718 << T->NumIsLocalCalls << " isLocal() calls\n";
719 });
720
721 //llvm::errs() << PagesMapped << BytesOfPagesMapped << FSLookups;
722}
static void normalizeCacheKey(StringRef &Path, std::optional< std::string > &Storage)
Defines the clang::FileManager interface and associated types.
A reference to a DirectoryEntry that includes the name of the directory as it was accessed by the Fil...
StringRef getName() const
const DirectoryEntry & getDirEntry() const
Cached information about one directory (either on disk or in the virtual file system).
A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...
Definition FileEntry.h:57
const FileEntry & getFileEntry() const
Definition FileEntry.h:70
StringRef getName() const
The name of this FileEntry.
Definition FileEntry.h:61
DirectoryEntryRef getDir() const
Definition FileEntry.h:78
Cached information about one file (either on disk or in the virtual file system).
Definition FileEntry.h:294
bool isNamedPipe() const
Check whether the file is a named pipe (and thus can't be opened by the native FileManager methods).
Definition FileEntry.h:332
void closeFile() const
Definition FileEntry.cpp:23
off_t getSize() const
Definition FileEntry.h:320
void AddStats(const FileManager &Other)
Import statistics from a child FileManager and add them to this current FileManager.
void trackVFSUsage(bool Active)
Enable or disable tracking of VFS usage.
llvm::vfs::FileSystem & getVirtualFileSystem() const
llvm::ErrorOr< std::unique_ptr< llvm::MemoryBuffer > > getBufferForFile(FileEntryRef Entry, bool isVolatile=false, bool RequiresNullTerminator=true, std::optional< int64_t > MaybeLimit=std::nullopt, bool IsText=true)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
FileManager(const FileSystemOptions &FileSystemOpts, IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS=nullptr)
Construct a file manager, optionally with a custom VFS.
llvm::Expected< FileEntryRef > getSTDIN()
Get the FileEntryRef for stdin, returning an error if stdin cannot be read.
StringRef getCanonicalName(DirectoryEntryRef Dir)
Retrieve the canonical name for a given directory.
FileEntryRef getVirtualFileRef(StringRef Filename, off_t Size, time_t ModificationTime)
Retrieve a file entry for a "virtual" file that acts as if there were a file with the given name on d...
bool FixupRelativePath(SmallVectorImpl< char > &Path) const
If path is not absolute and FileSystemOptions set the working directory, the path is modified to be r...
bool makeAbsolutePath(SmallVectorImpl< char > &Path, bool Canonicalize=false) const
Makes Path absolute taking into account FileSystemOptions and the working directory option,...
void PrintStats() const
OptionalFileEntryRef getBypassFile(FileEntryRef VFE)
Retrieve a FileEntry that bypasses VFE, which is expected to be a virtual file entry,...
static bool fixupRelativePath(const FileSystemOptions &FileSystemOpts, SmallVectorImpl< char > &Path)
Keeps track of options that affect how file operations are performed.
std::string WorkingDir
If set, paths are resolved as if the working directory was set to the value of WorkingDir.
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
CustomizableOptional< FileEntryRef > OptionalFileEntryRef
Definition FileEntry.h:204
std::error_code make_error_code(BuildPreambleError Error)
@ Result
The result type of a method or function.
Definition TypeBase.h:905
U cast(CodeGen::Address addr)
Definition Address.h:327
@ Other
Other implicit parameter.
Definition Decl.h:1763
Type stored in the StringMap.
Definition FileEntry.h:121