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