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