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 | |
19 | #include "clang/Basic/FileManager.h" |
20 | #include "clang/Basic/FileSystemStatCache.h" |
21 | #include "llvm/ADT/STLExtras.h" |
22 | #include "llvm/ADT/SmallString.h" |
23 | #include "llvm/ADT/Statistic.h" |
24 | #include "llvm/Config/llvm-config.h" |
25 | #include "llvm/Support/FileSystem.h" |
26 | #include "llvm/Support/MemoryBuffer.h" |
27 | #include "llvm/Support/Path.h" |
28 | #include "llvm/Support/raw_ostream.h" |
29 | #include <algorithm> |
30 | #include <cassert> |
31 | #include <climits> |
32 | #include <cstdint> |
33 | #include <cstdlib> |
34 | #include <optional> |
35 | #include <string> |
36 | #include <utility> |
37 | |
38 | using namespace clang; |
39 | |
40 | #define DEBUG_TYPE "file-search" |
41 | |
42 | //===----------------------------------------------------------------------===// |
43 | // Common logic. |
44 | //===----------------------------------------------------------------------===// |
45 | |
46 | FileManager::FileManager(const FileSystemOptions &FSO, |
47 | IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) |
48 | : FS(std::move(FS)), FileSystemOpts(FSO), SeenDirEntries(64), |
49 | SeenFileEntries(64), NextFileUID(0) { |
50 | // If the caller doesn't provide a virtual file system, just grab the real |
51 | // file system. |
52 | if (!this->FS) |
53 | this->FS = llvm::vfs::getRealFileSystem(); |
54 | } |
55 | |
56 | FileManager::~FileManager() = default; |
57 | |
58 | void FileManager::setStatCache(std::unique_ptr<FileSystemStatCache> statCache) { |
59 | assert(statCache && "No stat cache provided?" ); |
60 | StatCache = std::move(statCache); |
61 | } |
62 | |
63 | void FileManager::clearStatCache() { StatCache.reset(); } |
64 | |
65 | /// Retrieve the directory that the given file name resides in. |
66 | /// Filename can point to either a real file or a virtual file. |
67 | static llvm::Expected<DirectoryEntryRef> |
68 | getDirectoryFromFile(FileManager &FileMgr, StringRef Filename, |
69 | bool CacheFailure) { |
70 | if (Filename.empty()) |
71 | return llvm::errorCodeToError( |
72 | EC: make_error_code(e: std::errc::no_such_file_or_directory)); |
73 | |
74 | if (llvm::sys::path::is_separator(value: Filename[Filename.size() - 1])) |
75 | return llvm::errorCodeToError(EC: make_error_code(e: std::errc::is_a_directory)); |
76 | |
77 | StringRef DirName = llvm::sys::path::parent_path(path: Filename); |
78 | // Use the current directory if file has no path component. |
79 | if (DirName.empty()) |
80 | DirName = "." ; |
81 | |
82 | return FileMgr.getDirectoryRef(DirName, CacheFailure); |
83 | } |
84 | |
85 | DirectoryEntry *&FileManager::getRealDirEntry(const llvm::vfs::Status &Status) { |
86 | assert(Status.isDirectory() && "The directory should exist!" ); |
87 | // See if we have already opened a directory with the |
88 | // same inode (this occurs on Unix-like systems when one dir is |
89 | // symlinked to another, for example) or the same path (on |
90 | // Windows). |
91 | DirectoryEntry *&UDE = UniqueRealDirs[Status.getUniqueID()]; |
92 | |
93 | if (!UDE) { |
94 | // We don't have this directory yet, add it. We use the string |
95 | // key from the SeenDirEntries map as the string. |
96 | UDE = new (DirsAlloc.Allocate()) DirectoryEntry(); |
97 | } |
98 | return UDE; |
99 | } |
100 | |
101 | /// Add all ancestors of the given path (pointing to either a file or |
102 | /// a directory) as virtual directories. |
103 | void FileManager::addAncestorsAsVirtualDirs(StringRef Path) { |
104 | StringRef DirName = llvm::sys::path::parent_path(path: Path); |
105 | if (DirName.empty()) |
106 | DirName = "." ; |
107 | |
108 | auto &NamedDirEnt = *SeenDirEntries.insert( |
109 | KV: {DirName, std::errc::no_such_file_or_directory}).first; |
110 | |
111 | // When caching a virtual directory, we always cache its ancestors |
112 | // at the same time. Therefore, if DirName is already in the cache, |
113 | // we don't need to recurse as its ancestors must also already be in |
114 | // the cache (or it's a known non-virtual directory). |
115 | if (NamedDirEnt.second) |
116 | return; |
117 | |
118 | // Check to see if the directory exists. |
119 | llvm::vfs::Status Status; |
120 | auto statError = |
121 | getStatValue(Path: DirName, Status, isFile: false, F: nullptr /*directory lookup*/); |
122 | if (statError) { |
123 | // There's no real directory at the given path. |
124 | // Add the virtual directory to the cache. |
125 | auto *UDE = new (DirsAlloc.Allocate()) DirectoryEntry(); |
126 | NamedDirEnt.second = *UDE; |
127 | VirtualDirectoryEntries.push_back(Elt: UDE); |
128 | } else { |
129 | // There is the real directory |
130 | DirectoryEntry *&UDE = getRealDirEntry(Status); |
131 | NamedDirEnt.second = *UDE; |
132 | } |
133 | |
134 | // Recursively add the other ancestors. |
135 | addAncestorsAsVirtualDirs(Path: DirName); |
136 | } |
137 | |
138 | llvm::Expected<DirectoryEntryRef> |
139 | FileManager::getDirectoryRef(StringRef DirName, bool CacheFailure) { |
140 | // stat doesn't like trailing separators except for root directory. |
141 | // At least, on Win32 MSVCRT, stat() cannot strip trailing '/'. |
142 | // (though it can strip '\\') |
143 | if (DirName.size() > 1 && |
144 | DirName != llvm::sys::path::root_path(path: DirName) && |
145 | llvm::sys::path::is_separator(value: DirName.back())) |
146 | DirName = DirName.substr(Start: 0, N: DirName.size()-1); |
147 | std::optional<std::string> DirNameStr; |
148 | if (is_style_windows(S: llvm::sys::path::Style::native)) { |
149 | // Fixing a problem with "clang C:test.c" on Windows. |
150 | // Stat("C:") does not recognize "C:" as a valid directory |
151 | if (DirName.size() > 1 && DirName.back() == ':' && |
152 | DirName.equals_insensitive(RHS: llvm::sys::path::root_name(path: DirName))) { |
153 | DirNameStr = DirName.str() + '.'; |
154 | DirName = *DirNameStr; |
155 | } |
156 | } |
157 | |
158 | ++NumDirLookups; |
159 | |
160 | // See if there was already an entry in the map. Note that the map |
161 | // contains both virtual and real directories. |
162 | auto SeenDirInsertResult = |
163 | SeenDirEntries.insert(KV: {DirName, std::errc::no_such_file_or_directory}); |
164 | if (!SeenDirInsertResult.second) { |
165 | if (SeenDirInsertResult.first->second) |
166 | return DirectoryEntryRef(*SeenDirInsertResult.first); |
167 | return llvm::errorCodeToError(EC: SeenDirInsertResult.first->second.getError()); |
168 | } |
169 | |
170 | // We've not seen this before. Fill it in. |
171 | ++NumDirCacheMisses; |
172 | auto &NamedDirEnt = *SeenDirInsertResult.first; |
173 | assert(!NamedDirEnt.second && "should be newly-created" ); |
174 | |
175 | // Get the null-terminated directory name as stored as the key of the |
176 | // SeenDirEntries map. |
177 | StringRef InterndDirName = NamedDirEnt.first(); |
178 | |
179 | // Check to see if the directory exists. |
180 | llvm::vfs::Status Status; |
181 | auto statError = getStatValue(Path: InterndDirName, Status, isFile: false, |
182 | F: nullptr /*directory lookup*/); |
183 | if (statError) { |
184 | // There's no real directory at the given path. |
185 | if (CacheFailure) |
186 | NamedDirEnt.second = statError; |
187 | else |
188 | SeenDirEntries.erase(Key: DirName); |
189 | return llvm::errorCodeToError(EC: statError); |
190 | } |
191 | |
192 | // It exists. |
193 | DirectoryEntry *&UDE = getRealDirEntry(Status); |
194 | NamedDirEnt.second = *UDE; |
195 | |
196 | return DirectoryEntryRef(NamedDirEnt); |
197 | } |
198 | |
199 | llvm::ErrorOr<const DirectoryEntry *> |
200 | FileManager::getDirectory(StringRef DirName, bool CacheFailure) { |
201 | auto Result = getDirectoryRef(DirName, CacheFailure); |
202 | if (Result) |
203 | return &Result->getDirEntry(); |
204 | return llvm::errorToErrorCode(Err: Result.takeError()); |
205 | } |
206 | |
207 | llvm::ErrorOr<const FileEntry *> |
208 | FileManager::getFile(StringRef Filename, bool openFile, bool CacheFailure) { |
209 | auto Result = getFileRef(Filename, OpenFile: openFile, CacheFailure); |
210 | if (Result) |
211 | return &Result->getFileEntry(); |
212 | return llvm::errorToErrorCode(Err: Result.takeError()); |
213 | } |
214 | |
215 | llvm::Expected<FileEntryRef> |
216 | FileManager::getFileRef(StringRef Filename, bool openFile, bool CacheFailure) { |
217 | ++NumFileLookups; |
218 | |
219 | // See if there is already an entry in the map. |
220 | auto SeenFileInsertResult = |
221 | SeenFileEntries.insert(KV: {Filename, std::errc::no_such_file_or_directory}); |
222 | if (!SeenFileInsertResult.second) { |
223 | if (!SeenFileInsertResult.first->second) |
224 | return llvm::errorCodeToError( |
225 | EC: SeenFileInsertResult.first->second.getError()); |
226 | return FileEntryRef(*SeenFileInsertResult.first); |
227 | } |
228 | |
229 | // We've not seen this before. Fill it in. |
230 | ++NumFileCacheMisses; |
231 | auto *NamedFileEnt = &*SeenFileInsertResult.first; |
232 | assert(!NamedFileEnt->second && "should be newly-created" ); |
233 | |
234 | // Get the null-terminated file name as stored as the key of the |
235 | // SeenFileEntries map. |
236 | StringRef InterndFileName = NamedFileEnt->first(); |
237 | |
238 | // Look up the directory for the file. When looking up something like |
239 | // sys/foo.h we'll discover all of the search directories that have a 'sys' |
240 | // subdirectory. This will let us avoid having to waste time on known-to-fail |
241 | // searches when we go to find sys/bar.h, because all the search directories |
242 | // without a 'sys' subdir will get a cached failure result. |
243 | auto DirInfoOrErr = getDirectoryFromFile(FileMgr&: *this, Filename, CacheFailure); |
244 | if (!DirInfoOrErr) { // Directory doesn't exist, file can't exist. |
245 | std::error_code Err = errorToErrorCode(Err: DirInfoOrErr.takeError()); |
246 | if (CacheFailure) |
247 | NamedFileEnt->second = Err; |
248 | else |
249 | SeenFileEntries.erase(Key: Filename); |
250 | |
251 | return llvm::errorCodeToError(EC: Err); |
252 | } |
253 | DirectoryEntryRef DirInfo = *DirInfoOrErr; |
254 | |
255 | // FIXME: Use the directory info to prune this, before doing the stat syscall. |
256 | // FIXME: This will reduce the # syscalls. |
257 | |
258 | // Check to see if the file exists. |
259 | std::unique_ptr<llvm::vfs::File> F; |
260 | llvm::vfs::Status Status; |
261 | auto statError = getStatValue(Path: InterndFileName, Status, isFile: true, |
262 | F: openFile ? &F : nullptr); |
263 | if (statError) { |
264 | // There's no real file at the given path. |
265 | if (CacheFailure) |
266 | NamedFileEnt->second = statError; |
267 | else |
268 | SeenFileEntries.erase(Key: Filename); |
269 | |
270 | return llvm::errorCodeToError(EC: statError); |
271 | } |
272 | |
273 | assert((openFile || !F) && "undesired open file" ); |
274 | |
275 | // It exists. See if we have already opened a file with the same inode. |
276 | // This occurs when one dir is symlinked to another, for example. |
277 | FileEntry *&UFE = UniqueRealFiles[Status.getUniqueID()]; |
278 | bool ReusingEntry = UFE != nullptr; |
279 | if (!UFE) |
280 | UFE = new (FilesAlloc.Allocate()) FileEntry(); |
281 | |
282 | if (!Status.ExposesExternalVFSPath || Status.getName() == Filename) { |
283 | // Use the requested name. Set the FileEntry. |
284 | NamedFileEnt->second = FileEntryRef::MapValue(*UFE, DirInfo); |
285 | } else { |
286 | // Name mismatch. We need a redirect. First grab the actual entry we want |
287 | // to return. |
288 | // |
289 | // This redirection logic intentionally leaks the external name of a |
290 | // redirected file that uses 'use-external-name' in \a |
291 | // vfs::RedirectionFileSystem. This allows clang to report the external |
292 | // name to users (in diagnostics) and to tools that don't have access to |
293 | // the VFS (in debug info and dependency '.d' files). |
294 | // |
295 | // FIXME: This is pretty complex and has some very complicated interactions |
296 | // with the rest of clang. It's also inconsistent with how "real" |
297 | // filesystems behave and confuses parts of clang expect to see the |
298 | // name-as-accessed on the \a FileEntryRef. |
299 | // |
300 | // A potential plan to remove this is as follows - |
301 | // - Update callers such as `HeaderSearch::findUsableModuleForHeader()` |
302 | // to explicitly use the `getNameAsRequested()` rather than just using |
303 | // `getName()`. |
304 | // - Add a `FileManager::getExternalPath` API for explicitly getting the |
305 | // remapped external filename when there is one available. Adopt it in |
306 | // callers like diagnostics/deps reporting instead of calling |
307 | // `getName()` directly. |
308 | // - Switch the meaning of `FileEntryRef::getName()` to get the requested |
309 | // name, not the external name. Once that sticks, revert callers that |
310 | // want the requested name back to calling `getName()`. |
311 | // - Update the VFS to always return the requested name. This could also |
312 | // return the external name, or just have an API to request it |
313 | // lazily. The latter has the benefit of making accesses of the |
314 | // external path easily tracked, but may also require extra work than |
315 | // just returning up front. |
316 | // - (Optionally) Add an API to VFS to get the external filename lazily |
317 | // and update `FileManager::getExternalPath()` to use it instead. This |
318 | // has the benefit of making such accesses easily tracked, though isn't |
319 | // necessarily required (and could cause extra work than just adding to |
320 | // eg. `vfs::Status` up front). |
321 | auto &Redirection = |
322 | *SeenFileEntries |
323 | .insert(KV: {Status.getName(), FileEntryRef::MapValue(*UFE, DirInfo)}) |
324 | .first; |
325 | assert(Redirection.second->V.is<FileEntry *>() && |
326 | "filename redirected to a non-canonical filename?" ); |
327 | assert(Redirection.second->V.get<FileEntry *>() == UFE && |
328 | "filename from getStatValue() refers to wrong file" ); |
329 | |
330 | // Cache the redirection in the previously-inserted entry, still available |
331 | // in the tentative return value. |
332 | NamedFileEnt->second = FileEntryRef::MapValue(Redirection, DirInfo); |
333 | } |
334 | |
335 | FileEntryRef ReturnedRef(*NamedFileEnt); |
336 | if (ReusingEntry) { // Already have an entry with this inode, return it. |
337 | return ReturnedRef; |
338 | } |
339 | |
340 | // Otherwise, we don't have this file yet, add it. |
341 | UFE->Size = Status.getSize(); |
342 | UFE->ModTime = llvm::sys::toTimeT(TP: Status.getLastModificationTime()); |
343 | UFE->Dir = &DirInfo.getDirEntry(); |
344 | UFE->UID = NextFileUID++; |
345 | UFE->UniqueID = Status.getUniqueID(); |
346 | UFE->IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file; |
347 | UFE->File = std::move(F); |
348 | |
349 | if (UFE->File) { |
350 | if (auto PathName = UFE->File->getName()) |
351 | fillRealPathName(UFE, FileName: *PathName); |
352 | } else if (!openFile) { |
353 | // We should still fill the path even if we aren't opening the file. |
354 | fillRealPathName(UFE, FileName: InterndFileName); |
355 | } |
356 | return ReturnedRef; |
357 | } |
358 | |
359 | llvm::Expected<FileEntryRef> FileManager::getSTDIN() { |
360 | // Only read stdin once. |
361 | if (STDIN) |
362 | return *STDIN; |
363 | |
364 | std::unique_ptr<llvm::MemoryBuffer> Content; |
365 | if (auto ContentOrError = llvm::MemoryBuffer::getSTDIN()) |
366 | Content = std::move(*ContentOrError); |
367 | else |
368 | return llvm::errorCodeToError(EC: ContentOrError.getError()); |
369 | |
370 | STDIN = getVirtualFileRef(Filename: Content->getBufferIdentifier(), |
371 | Size: Content->getBufferSize(), ModificationTime: 0); |
372 | FileEntry &FE = const_cast<FileEntry &>(STDIN->getFileEntry()); |
373 | FE.Content = std::move(Content); |
374 | FE.IsNamedPipe = true; |
375 | return *STDIN; |
376 | } |
377 | |
378 | void FileManager::trackVFSUsage(bool Active) { |
379 | FS->visit(Callback: [Active](llvm::vfs::FileSystem &FileSys) { |
380 | if (auto *RFS = dyn_cast<llvm::vfs::RedirectingFileSystem>(Val: &FileSys)) |
381 | RFS->setUsageTrackingActive(Active); |
382 | }); |
383 | } |
384 | |
385 | const FileEntry *FileManager::getVirtualFile(StringRef Filename, off_t Size, |
386 | time_t ModificationTime) { |
387 | return &getVirtualFileRef(Filename, Size, ModificationTime).getFileEntry(); |
388 | } |
389 | |
390 | FileEntryRef FileManager::getVirtualFileRef(StringRef Filename, off_t Size, |
391 | time_t ModificationTime) { |
392 | ++NumFileLookups; |
393 | |
394 | // See if there is already an entry in the map for an existing file. |
395 | auto &NamedFileEnt = *SeenFileEntries.insert( |
396 | KV: {Filename, std::errc::no_such_file_or_directory}).first; |
397 | if (NamedFileEnt.second) { |
398 | FileEntryRef::MapValue Value = *NamedFileEnt.second; |
399 | if (LLVM_LIKELY(Value.V.is<FileEntry *>())) |
400 | return FileEntryRef(NamedFileEnt); |
401 | return FileEntryRef(*Value.V.get<const FileEntryRef::MapEntry *>()); |
402 | } |
403 | |
404 | // We've not seen this before, or the file is cached as non-existent. |
405 | ++NumFileCacheMisses; |
406 | addAncestorsAsVirtualDirs(Path: Filename); |
407 | FileEntry *UFE = nullptr; |
408 | |
409 | // Now that all ancestors of Filename are in the cache, the |
410 | // following call is guaranteed to find the DirectoryEntry from the |
411 | // cache. A virtual file can also have an empty filename, that could come |
412 | // from a source location preprocessor directive with an empty filename as |
413 | // an example, so we need to pretend it has a name to ensure a valid directory |
414 | // entry can be returned. |
415 | auto DirInfo = expectedToOptional(E: getDirectoryFromFile( |
416 | FileMgr&: *this, Filename: Filename.empty() ? "." : Filename, /*CacheFailure=*/true)); |
417 | assert(DirInfo && |
418 | "The directory of a virtual file should already be in the cache." ); |
419 | |
420 | // Check to see if the file exists. If so, drop the virtual file |
421 | llvm::vfs::Status Status; |
422 | const char *InterndFileName = NamedFileEnt.first().data(); |
423 | if (!getStatValue(Path: InterndFileName, Status, isFile: true, F: nullptr)) { |
424 | Status = llvm::vfs::Status( |
425 | Status.getName(), Status.getUniqueID(), |
426 | llvm::sys::toTimePoint(T: ModificationTime), |
427 | Status.getUser(), Status.getGroup(), Size, |
428 | Status.getType(), Status.getPermissions()); |
429 | |
430 | auto &RealFE = UniqueRealFiles[Status.getUniqueID()]; |
431 | if (RealFE) { |
432 | // If we had already opened this file, close it now so we don't |
433 | // leak the descriptor. We're not going to use the file |
434 | // descriptor anyway, since this is a virtual file. |
435 | if (RealFE->File) |
436 | RealFE->closeFile(); |
437 | // If we already have an entry with this inode, return it. |
438 | // |
439 | // FIXME: Surely this should add a reference by the new name, and return |
440 | // it instead... |
441 | NamedFileEnt.second = FileEntryRef::MapValue(*RealFE, *DirInfo); |
442 | return FileEntryRef(NamedFileEnt); |
443 | } |
444 | // File exists, but no entry - create it. |
445 | RealFE = new (FilesAlloc.Allocate()) FileEntry(); |
446 | RealFE->UniqueID = Status.getUniqueID(); |
447 | RealFE->IsNamedPipe = |
448 | Status.getType() == llvm::sys::fs::file_type::fifo_file; |
449 | fillRealPathName(UFE: RealFE, FileName: Status.getName()); |
450 | |
451 | UFE = RealFE; |
452 | } else { |
453 | // File does not exist, create a virtual entry. |
454 | UFE = new (FilesAlloc.Allocate()) FileEntry(); |
455 | VirtualFileEntries.push_back(Elt: UFE); |
456 | } |
457 | |
458 | NamedFileEnt.second = FileEntryRef::MapValue(*UFE, *DirInfo); |
459 | UFE->Size = Size; |
460 | UFE->ModTime = ModificationTime; |
461 | UFE->Dir = &DirInfo->getDirEntry(); |
462 | UFE->UID = NextFileUID++; |
463 | UFE->File.reset(); |
464 | return FileEntryRef(NamedFileEnt); |
465 | } |
466 | |
467 | OptionalFileEntryRef FileManager::getBypassFile(FileEntryRef VF) { |
468 | // Stat of the file and return nullptr if it doesn't exist. |
469 | llvm::vfs::Status Status; |
470 | if (getStatValue(Path: VF.getName(), Status, /*isFile=*/true, /*F=*/nullptr)) |
471 | return std::nullopt; |
472 | |
473 | if (!SeenBypassFileEntries) |
474 | SeenBypassFileEntries = std::make_unique< |
475 | llvm::StringMap<llvm::ErrorOr<FileEntryRef::MapValue>>>(); |
476 | |
477 | // If we've already bypassed just use the existing one. |
478 | auto Insertion = SeenBypassFileEntries->insert( |
479 | KV: {VF.getName(), std::errc::no_such_file_or_directory}); |
480 | if (!Insertion.second) |
481 | return FileEntryRef(*Insertion.first); |
482 | |
483 | // Fill in the new entry from the stat. |
484 | FileEntry *BFE = new (FilesAlloc.Allocate()) FileEntry(); |
485 | BypassFileEntries.push_back(Elt: BFE); |
486 | Insertion.first->second = FileEntryRef::MapValue(*BFE, VF.getDir()); |
487 | BFE->Size = Status.getSize(); |
488 | BFE->Dir = VF.getFileEntry().Dir; |
489 | BFE->ModTime = llvm::sys::toTimeT(TP: Status.getLastModificationTime()); |
490 | BFE->UID = NextFileUID++; |
491 | |
492 | // Save the entry in the bypass table and return. |
493 | return FileEntryRef(*Insertion.first); |
494 | } |
495 | |
496 | bool FileManager::FixupRelativePath(SmallVectorImpl<char> &path) const { |
497 | StringRef pathRef(path.data(), path.size()); |
498 | |
499 | if (FileSystemOpts.WorkingDir.empty() |
500 | || llvm::sys::path::is_absolute(path: pathRef)) |
501 | return false; |
502 | |
503 | SmallString<128> NewPath(FileSystemOpts.WorkingDir); |
504 | llvm::sys::path::append(path&: NewPath, a: pathRef); |
505 | path = NewPath; |
506 | return true; |
507 | } |
508 | |
509 | bool FileManager::makeAbsolutePath(SmallVectorImpl<char> &Path) const { |
510 | bool Changed = FixupRelativePath(path&: Path); |
511 | |
512 | if (!llvm::sys::path::is_absolute(path: StringRef(Path.data(), Path.size()))) { |
513 | FS->makeAbsolute(Path); |
514 | Changed = true; |
515 | } |
516 | |
517 | return Changed; |
518 | } |
519 | |
520 | void FileManager::fillRealPathName(FileEntry *UFE, llvm::StringRef FileName) { |
521 | llvm::SmallString<128> AbsPath(FileName); |
522 | // This is not the same as `VFS::getRealPath()`, which resolves symlinks |
523 | // but can be very expensive on real file systems. |
524 | // FIXME: the semantic of RealPathName is unclear, and the name might be |
525 | // misleading. We need to clean up the interface here. |
526 | makeAbsolutePath(Path&: AbsPath); |
527 | llvm::sys::path::remove_dots(path&: AbsPath, /*remove_dot_dot=*/true); |
528 | UFE->RealPathName = std::string(AbsPath); |
529 | } |
530 | |
531 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> |
532 | FileManager::getBufferForFile(FileEntryRef FE, bool isVolatile, |
533 | bool RequiresNullTerminator, |
534 | std::optional<int64_t> MaybeLimit) { |
535 | const FileEntry *Entry = &FE.getFileEntry(); |
536 | // If the content is living on the file entry, return a reference to it. |
537 | if (Entry->Content) |
538 | return llvm::MemoryBuffer::getMemBuffer(Ref: Entry->Content->getMemBufferRef()); |
539 | |
540 | uint64_t FileSize = Entry->getSize(); |
541 | |
542 | if (MaybeLimit) |
543 | FileSize = *MaybeLimit; |
544 | |
545 | // If there's a high enough chance that the file have changed since we |
546 | // got its size, force a stat before opening it. |
547 | if (isVolatile || Entry->isNamedPipe()) |
548 | FileSize = -1; |
549 | |
550 | StringRef Filename = FE.getName(); |
551 | // If the file is already open, use the open file descriptor. |
552 | if (Entry->File) { |
553 | auto Result = Entry->File->getBuffer(Name: Filename, FileSize, |
554 | RequiresNullTerminator, IsVolatile: isVolatile); |
555 | Entry->closeFile(); |
556 | return Result; |
557 | } |
558 | |
559 | // Otherwise, open the file. |
560 | return getBufferForFileImpl(Filename, FileSize, isVolatile, |
561 | RequiresNullTerminator); |
562 | } |
563 | |
564 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> |
565 | FileManager::getBufferForFileImpl(StringRef Filename, int64_t FileSize, |
566 | bool isVolatile, |
567 | bool RequiresNullTerminator) const { |
568 | if (FileSystemOpts.WorkingDir.empty()) |
569 | return FS->getBufferForFile(Name: Filename, FileSize, RequiresNullTerminator, |
570 | IsVolatile: isVolatile); |
571 | |
572 | SmallString<128> FilePath(Filename); |
573 | FixupRelativePath(path&: FilePath); |
574 | return FS->getBufferForFile(Name: FilePath, FileSize, RequiresNullTerminator, |
575 | IsVolatile: isVolatile); |
576 | } |
577 | |
578 | /// getStatValue - Get the 'stat' information for the specified path, |
579 | /// using the cache to accelerate it if possible. This returns true |
580 | /// if the path points to a virtual file or does not exist, or returns |
581 | /// false if it's an existent real file. If FileDescriptor is NULL, |
582 | /// do directory look-up instead of file look-up. |
583 | std::error_code |
584 | FileManager::getStatValue(StringRef Path, llvm::vfs::Status &Status, |
585 | bool isFile, std::unique_ptr<llvm::vfs::File> *F) { |
586 | // FIXME: FileSystemOpts shouldn't be passed in here, all paths should be |
587 | // absolute! |
588 | if (FileSystemOpts.WorkingDir.empty()) |
589 | return FileSystemStatCache::get(Path, Status, isFile, F, |
590 | Cache: StatCache.get(), FS&: *FS); |
591 | |
592 | SmallString<128> FilePath(Path); |
593 | FixupRelativePath(path&: FilePath); |
594 | |
595 | return FileSystemStatCache::get(Path: FilePath.c_str(), Status, isFile, F, |
596 | Cache: StatCache.get(), FS&: *FS); |
597 | } |
598 | |
599 | std::error_code |
600 | FileManager::getNoncachedStatValue(StringRef Path, |
601 | llvm::vfs::Status &Result) { |
602 | SmallString<128> FilePath(Path); |
603 | FixupRelativePath(path&: FilePath); |
604 | |
605 | llvm::ErrorOr<llvm::vfs::Status> S = FS->status(Path: FilePath.c_str()); |
606 | if (!S) |
607 | return S.getError(); |
608 | Result = *S; |
609 | return std::error_code(); |
610 | } |
611 | |
612 | void FileManager::GetUniqueIDMapping( |
613 | SmallVectorImpl<OptionalFileEntryRef> &UIDToFiles) const { |
614 | UIDToFiles.clear(); |
615 | UIDToFiles.resize(N: NextFileUID); |
616 | |
617 | for (const auto &Entry : SeenFileEntries) { |
618 | // Only return files that exist and are not redirected. |
619 | if (!Entry.getValue() || !Entry.getValue()->V.is<FileEntry *>()) |
620 | continue; |
621 | FileEntryRef FE(Entry); |
622 | // Add this file if it's the first one with the UID, or if its name is |
623 | // better than the existing one. |
624 | OptionalFileEntryRef &ExistingFE = UIDToFiles[FE.getUID()]; |
625 | if (!ExistingFE || FE.getName() < ExistingFE->getName()) |
626 | ExistingFE = FE; |
627 | } |
628 | } |
629 | |
630 | StringRef FileManager::getCanonicalName(DirectoryEntryRef Dir) { |
631 | return getCanonicalName(Entry: Dir, Name: Dir.getName()); |
632 | } |
633 | |
634 | StringRef FileManager::getCanonicalName(FileEntryRef File) { |
635 | return getCanonicalName(Entry: File, Name: File.getName()); |
636 | } |
637 | |
638 | StringRef FileManager::getCanonicalName(const void *Entry, StringRef Name) { |
639 | llvm::DenseMap<const void *, llvm::StringRef>::iterator Known = |
640 | CanonicalNames.find(Val: Entry); |
641 | if (Known != CanonicalNames.end()) |
642 | return Known->second; |
643 | |
644 | // Name comes from FileEntry/DirectoryEntry::getName(), so it is safe to |
645 | // store it in the DenseMap below. |
646 | StringRef CanonicalName(Name); |
647 | |
648 | SmallString<256> AbsPathBuf; |
649 | SmallString<256> RealPathBuf; |
650 | if (!FS->getRealPath(Path: Name, Output&: RealPathBuf)) { |
651 | if (is_style_windows(S: llvm::sys::path::Style::native)) { |
652 | // For Windows paths, only use the real path if it doesn't resolve |
653 | // a substitute drive, as those are used to avoid MAX_PATH issues. |
654 | AbsPathBuf = Name; |
655 | if (!FS->makeAbsolute(Path&: AbsPathBuf)) { |
656 | if (llvm::sys::path::root_name(path: RealPathBuf) == |
657 | llvm::sys::path::root_name(path: AbsPathBuf)) { |
658 | CanonicalName = RealPathBuf.str().copy(A&: CanonicalNameStorage); |
659 | } else { |
660 | // Fallback to using the absolute path. |
661 | // Simplifying /../ is semantically valid on Windows even in the |
662 | // presence of symbolic links. |
663 | llvm::sys::path::remove_dots(path&: AbsPathBuf, /*remove_dot_dot=*/true); |
664 | CanonicalName = AbsPathBuf.str().copy(A&: CanonicalNameStorage); |
665 | } |
666 | } |
667 | } else { |
668 | CanonicalName = RealPathBuf.str().copy(A&: CanonicalNameStorage); |
669 | } |
670 | } |
671 | |
672 | CanonicalNames.insert(KV: {Entry, CanonicalName}); |
673 | return CanonicalName; |
674 | } |
675 | |
676 | void FileManager::AddStats(const FileManager &Other) { |
677 | assert(&Other != this && "Collecting stats into the same FileManager" ); |
678 | NumDirLookups += Other.NumDirLookups; |
679 | NumFileLookups += Other.NumFileLookups; |
680 | NumDirCacheMisses += Other.NumDirCacheMisses; |
681 | NumFileCacheMisses += Other.NumFileCacheMisses; |
682 | } |
683 | |
684 | void FileManager::PrintStats() const { |
685 | llvm::errs() << "\n*** File Manager Stats:\n" ; |
686 | llvm::errs() << UniqueRealFiles.size() << " real files found, " |
687 | << UniqueRealDirs.size() << " real dirs found.\n" ; |
688 | llvm::errs() << VirtualFileEntries.size() << " virtual files found, " |
689 | << VirtualDirectoryEntries.size() << " virtual dirs found.\n" ; |
690 | llvm::errs() << NumDirLookups << " dir lookups, " |
691 | << NumDirCacheMisses << " dir cache misses.\n" ; |
692 | llvm::errs() << NumFileLookups << " file lookups, " |
693 | << NumFileCacheMisses << " file cache misses.\n" ; |
694 | |
695 | //llvm::errs() << PagesMapped << BytesOfPagesMapped << FSLookups; |
696 | } |
697 | |