12754fe60SDimitry Andric //===--- FileManager.cpp - File System Probing and Caching ----------------===//
2f22ef01cSRoman Divacky //
3f22ef01cSRoman Divacky // The LLVM Compiler Infrastructure
4f22ef01cSRoman Divacky //
5f22ef01cSRoman Divacky // This file is distributed under the University of Illinois Open Source
6f22ef01cSRoman Divacky // License. See LICENSE.TXT for details.
7f22ef01cSRoman Divacky //
8f22ef01cSRoman Divacky //===----------------------------------------------------------------------===//
9f22ef01cSRoman Divacky //
10f22ef01cSRoman Divacky // This file implements the FileManager interface.
11f22ef01cSRoman Divacky //
12f22ef01cSRoman Divacky //===----------------------------------------------------------------------===//
13f22ef01cSRoman Divacky //
14f22ef01cSRoman Divacky // TODO: This should index all interesting directories with dirent calls.
15f22ef01cSRoman Divacky // getdirentries ?
16f22ef01cSRoman Divacky // opendir/readdir_r/closedir ?
17f22ef01cSRoman Divacky //
18f22ef01cSRoman Divacky //===----------------------------------------------------------------------===//
19f22ef01cSRoman Divacky
20f22ef01cSRoman Divacky #include "clang/Basic/FileManager.h"
212754fe60SDimitry Andric #include "clang/Basic/FileSystemStatCache.h"
22f22ef01cSRoman Divacky #include "llvm/ADT/SmallString.h"
23139f7f9bSDimitry Andric #include "llvm/Config/llvm-config.h"
240623d748SDimitry Andric #include "llvm/ADT/STLExtras.h"
252754fe60SDimitry Andric #include "llvm/Support/FileSystem.h"
262754fe60SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
272754fe60SDimitry Andric #include "llvm/Support/Path.h"
28139f7f9bSDimitry Andric #include "llvm/Support/raw_ostream.h"
2944290647SDimitry Andric #include <algorithm>
3044290647SDimitry Andric #include <cassert>
3144290647SDimitry Andric #include <climits>
3244290647SDimitry Andric #include <cstdint>
3344290647SDimitry Andric #include <cstdlib>
34f22ef01cSRoman Divacky #include <string>
3544290647SDimitry Andric #include <utility>
362754fe60SDimitry Andric
37f22ef01cSRoman Divacky using namespace clang;
38f22ef01cSRoman Divacky
39f22ef01cSRoman Divacky /// NON_EXISTENT_DIR - A special value distinct from null that is used to
40f22ef01cSRoman Divacky /// represent a dir name that doesn't exist on the disk.
41f22ef01cSRoman Divacky #define NON_EXISTENT_DIR reinterpret_cast<DirectoryEntry*>((intptr_t)-1)
42f22ef01cSRoman Divacky
432754fe60SDimitry Andric /// NON_EXISTENT_FILE - A special value distinct from null that is used to
442754fe60SDimitry Andric /// represent a filename that doesn't exist on the disk.
452754fe60SDimitry Andric #define NON_EXISTENT_FILE reinterpret_cast<FileEntry*>((intptr_t)-1)
462754fe60SDimitry Andric
47f22ef01cSRoman Divacky //===----------------------------------------------------------------------===//
48f22ef01cSRoman Divacky // Common logic.
49f22ef01cSRoman Divacky //===----------------------------------------------------------------------===//
50f22ef01cSRoman Divacky
FileManager(const FileSystemOptions & FSO,IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)5159d1ed5bSDimitry Andric FileManager::FileManager(const FileSystemOptions &FSO,
52*b5893f02SDimitry Andric IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
5320e90f04SDimitry Andric : FS(std::move(FS)), FileSystemOpts(FSO), SeenDirEntries(64),
5420e90f04SDimitry Andric SeenFileEntries(64), NextFileUID(0) {
55f22ef01cSRoman Divacky NumDirLookups = NumFileLookups = 0;
56f22ef01cSRoman Divacky NumDirCacheMisses = NumFileCacheMisses = 0;
5759d1ed5bSDimitry Andric
5859d1ed5bSDimitry Andric // If the caller doesn't provide a virtual file system, just grab the real
5959d1ed5bSDimitry Andric // file system.
6020e90f04SDimitry Andric if (!this->FS)
61*b5893f02SDimitry Andric this->FS = llvm::vfs::getRealFileSystem();
62f22ef01cSRoman Divacky }
63f22ef01cSRoman Divacky
640623d748SDimitry Andric FileManager::~FileManager() = default;
65f22ef01cSRoman Divacky
setStatCache(std::unique_ptr<FileSystemStatCache> statCache)66*b5893f02SDimitry Andric void FileManager::setStatCache(std::unique_ptr<FileSystemStatCache> statCache) {
67f22ef01cSRoman Divacky assert(statCache && "No stat cache provided?");
6839d628a0SDimitry Andric StatCache = std::move(statCache);
69f22ef01cSRoman Divacky }
70f22ef01cSRoman Divacky
clearStatCache()71*b5893f02SDimitry Andric void FileManager::clearStatCache() { StatCache.reset(); }
727ae0e2c9SDimitry Andric
734ba319b5SDimitry Andric /// Retrieve the directory that the given file name resides in.
742754fe60SDimitry Andric /// Filename can point to either a real file or a virtual file.
getDirectoryFromFile(FileManager & FileMgr,StringRef Filename,bool CacheFailure)75f22ef01cSRoman Divacky static const DirectoryEntry *getDirectoryFromFile(FileManager &FileMgr,
766122f3e6SDimitry Andric StringRef Filename,
776122f3e6SDimitry Andric bool CacheFailure) {
782754fe60SDimitry Andric if (Filename.empty())
7959d1ed5bSDimitry Andric return nullptr;
80f22ef01cSRoman Divacky
812754fe60SDimitry Andric if (llvm::sys::path::is_separator(Filename[Filename.size() - 1]))
8259d1ed5bSDimitry Andric return nullptr; // If Filename is a directory.
832754fe60SDimitry Andric
846122f3e6SDimitry Andric StringRef DirName = llvm::sys::path::parent_path(Filename);
85f22ef01cSRoman Divacky // Use the current directory if file has no path component.
862754fe60SDimitry Andric if (DirName.empty())
872754fe60SDimitry Andric DirName = ".";
882754fe60SDimitry Andric
896122f3e6SDimitry Andric return FileMgr.getDirectory(DirName, CacheFailure);
90f22ef01cSRoman Divacky }
91f22ef01cSRoman Divacky
922754fe60SDimitry Andric /// Add all ancestors of the given path (pointing to either a file or
932754fe60SDimitry Andric /// a directory) as virtual directories.
addAncestorsAsVirtualDirs(StringRef Path)946122f3e6SDimitry Andric void FileManager::addAncestorsAsVirtualDirs(StringRef Path) {
956122f3e6SDimitry Andric StringRef DirName = llvm::sys::path::parent_path(Path);
962754fe60SDimitry Andric if (DirName.empty())
97e7145dcbSDimitry Andric DirName = ".";
982754fe60SDimitry Andric
9939d628a0SDimitry Andric auto &NamedDirEnt =
10039d628a0SDimitry Andric *SeenDirEntries.insert(std::make_pair(DirName, nullptr)).first;
1012754fe60SDimitry Andric
1022754fe60SDimitry Andric // When caching a virtual directory, we always cache its ancestors
1032754fe60SDimitry Andric // at the same time. Therefore, if DirName is already in the cache,
1042754fe60SDimitry Andric // we don't need to recurse as its ancestors must also already be in
1052754fe60SDimitry Andric // the cache.
1060623d748SDimitry Andric if (NamedDirEnt.second && NamedDirEnt.second != NON_EXISTENT_DIR)
1072754fe60SDimitry Andric return;
1082754fe60SDimitry Andric
1092754fe60SDimitry Andric // Add the virtual directory to the cache.
1100623d748SDimitry Andric auto UDE = llvm::make_unique<DirectoryEntry>();
11144290647SDimitry Andric UDE->Name = NamedDirEnt.first();
1120623d748SDimitry Andric NamedDirEnt.second = UDE.get();
1130623d748SDimitry Andric VirtualDirectoryEntries.push_back(std::move(UDE));
1142754fe60SDimitry Andric
1152754fe60SDimitry Andric // Recursively add the other ancestors.
1162754fe60SDimitry Andric addAncestorsAsVirtualDirs(DirName);
1172754fe60SDimitry Andric }
1182754fe60SDimitry Andric
getDirectory(StringRef DirName,bool CacheFailure)1196122f3e6SDimitry Andric const DirectoryEntry *FileManager::getDirectory(StringRef DirName,
1206122f3e6SDimitry Andric bool CacheFailure) {
1217ae0e2c9SDimitry Andric // stat doesn't like trailing separators except for root directory.
122dff0c46cSDimitry Andric // At least, on Win32 MSVCRT, stat() cannot strip trailing '/'.
123dff0c46cSDimitry Andric // (though it can strip '\\')
1247ae0e2c9SDimitry Andric if (DirName.size() > 1 &&
1257ae0e2c9SDimitry Andric DirName != llvm::sys::path::root_path(DirName) &&
1267ae0e2c9SDimitry Andric llvm::sys::path::is_separator(DirName.back()))
127dff0c46cSDimitry Andric DirName = DirName.substr(0, DirName.size()-1);
1284ba319b5SDimitry Andric #ifdef _WIN32
129f785676fSDimitry Andric // Fixing a problem with "clang C:test.c" on Windows.
130f785676fSDimitry Andric // Stat("C:") does not recognize "C:" as a valid directory
131f785676fSDimitry Andric std::string DirNameStr;
132f785676fSDimitry Andric if (DirName.size() > 1 && DirName.back() == ':' &&
133f785676fSDimitry Andric DirName.equals_lower(llvm::sys::path::root_name(DirName))) {
134f785676fSDimitry Andric DirNameStr = DirName.str() + '.';
135f785676fSDimitry Andric DirName = DirNameStr;
136f785676fSDimitry Andric }
137f785676fSDimitry Andric #endif
138dff0c46cSDimitry Andric
139f22ef01cSRoman Divacky ++NumDirLookups;
14039d628a0SDimitry Andric auto &NamedDirEnt =
14139d628a0SDimitry Andric *SeenDirEntries.insert(std::make_pair(DirName, nullptr)).first;
142f22ef01cSRoman Divacky
1432754fe60SDimitry Andric // See if there was already an entry in the map. Note that the map
1442754fe60SDimitry Andric // contains both virtual and real directories.
14539d628a0SDimitry Andric if (NamedDirEnt.second)
14639d628a0SDimitry Andric return NamedDirEnt.second == NON_EXISTENT_DIR ? nullptr
14739d628a0SDimitry Andric : NamedDirEnt.second;
148f22ef01cSRoman Divacky
149f22ef01cSRoman Divacky ++NumDirCacheMisses;
150f22ef01cSRoman Divacky
151f22ef01cSRoman Divacky // By default, initialize it to invalid.
15239d628a0SDimitry Andric NamedDirEnt.second = NON_EXISTENT_DIR;
153f22ef01cSRoman Divacky
154f22ef01cSRoman Divacky // Get the null-terminated directory name as stored as the key of the
1552754fe60SDimitry Andric // SeenDirEntries map.
15644290647SDimitry Andric StringRef InterndDirName = NamedDirEnt.first();
157f22ef01cSRoman Divacky
158f22ef01cSRoman Divacky // Check to see if the directory exists.
159f785676fSDimitry Andric FileData Data;
16059d1ed5bSDimitry Andric if (getStatValue(InterndDirName, Data, false, nullptr /*directory lookup*/)) {
1612754fe60SDimitry Andric // There's no real directory at the given path.
1626122f3e6SDimitry Andric if (!CacheFailure)
1636122f3e6SDimitry Andric SeenDirEntries.erase(DirName);
16459d1ed5bSDimitry Andric return nullptr;
1652754fe60SDimitry Andric }
166f22ef01cSRoman Divacky
1672754fe60SDimitry Andric // It exists. See if we have already opened a directory with the
1682754fe60SDimitry Andric // same inode (this occurs on Unix-like systems when one dir is
1692754fe60SDimitry Andric // symlinked to another, for example) or the same path (on
1702754fe60SDimitry Andric // Windows).
17159d1ed5bSDimitry Andric DirectoryEntry &UDE = UniqueRealDirs[Data.UniqueID];
172f22ef01cSRoman Divacky
17339d628a0SDimitry Andric NamedDirEnt.second = &UDE;
17444290647SDimitry Andric if (UDE.getName().empty()) {
1752754fe60SDimitry Andric // We don't have this directory yet, add it. We use the string
1762754fe60SDimitry Andric // key from the SeenDirEntries map as the string.
177f22ef01cSRoman Divacky UDE.Name = InterndDirName;
1782754fe60SDimitry Andric }
1792754fe60SDimitry Andric
180f22ef01cSRoman Divacky return &UDE;
181f22ef01cSRoman Divacky }
182f22ef01cSRoman Divacky
getFile(StringRef Filename,bool openFile,bool CacheFailure)1836122f3e6SDimitry Andric const FileEntry *FileManager::getFile(StringRef Filename, bool openFile,
1846122f3e6SDimitry Andric bool CacheFailure) {
185f22ef01cSRoman Divacky ++NumFileLookups;
186f22ef01cSRoman Divacky
187f22ef01cSRoman Divacky // See if there is already an entry in the map.
18839d628a0SDimitry Andric auto &NamedFileEnt =
18939d628a0SDimitry Andric *SeenFileEntries.insert(std::make_pair(Filename, nullptr)).first;
190f22ef01cSRoman Divacky
191f22ef01cSRoman Divacky // See if there is already an entry in the map.
19239d628a0SDimitry Andric if (NamedFileEnt.second)
19339d628a0SDimitry Andric return NamedFileEnt.second == NON_EXISTENT_FILE ? nullptr
19439d628a0SDimitry Andric : NamedFileEnt.second;
195f22ef01cSRoman Divacky
196f22ef01cSRoman Divacky ++NumFileCacheMisses;
197f22ef01cSRoman Divacky
198f22ef01cSRoman Divacky // By default, initialize it to invalid.
19939d628a0SDimitry Andric NamedFileEnt.second = NON_EXISTENT_FILE;
200f22ef01cSRoman Divacky
201f22ef01cSRoman Divacky // Get the null-terminated file name as stored as the key of the
2022754fe60SDimitry Andric // SeenFileEntries map.
20344290647SDimitry Andric StringRef InterndFileName = NamedFileEnt.first();
204f22ef01cSRoman Divacky
2052754fe60SDimitry Andric // Look up the directory for the file. When looking up something like
2062754fe60SDimitry Andric // sys/foo.h we'll discover all of the search directories that have a 'sys'
2072754fe60SDimitry Andric // subdirectory. This will let us avoid having to waste time on known-to-fail
2082754fe60SDimitry Andric // searches when we go to find sys/bar.h, because all the search directories
2092754fe60SDimitry Andric // without a 'sys' subdir will get a cached failure result.
2106122f3e6SDimitry Andric const DirectoryEntry *DirInfo = getDirectoryFromFile(*this, Filename,
2116122f3e6SDimitry Andric CacheFailure);
21259d1ed5bSDimitry Andric if (DirInfo == nullptr) { // Directory doesn't exist, file can't exist.
2136122f3e6SDimitry Andric if (!CacheFailure)
2146122f3e6SDimitry Andric SeenFileEntries.erase(Filename);
2156122f3e6SDimitry Andric
21659d1ed5bSDimitry Andric return nullptr;
2176122f3e6SDimitry Andric }
218f22ef01cSRoman Divacky
219f22ef01cSRoman Divacky // FIXME: Use the directory info to prune this, before doing the stat syscall.
220f22ef01cSRoman Divacky // FIXME: This will reduce the # syscalls.
221f22ef01cSRoman Divacky
222f22ef01cSRoman Divacky // Nope, there isn't. Check to see if the file exists.
223*b5893f02SDimitry Andric std::unique_ptr<llvm::vfs::File> F;
224f785676fSDimitry Andric FileData Data;
22559d1ed5bSDimitry Andric if (getStatValue(InterndFileName, Data, true, openFile ? &F : nullptr)) {
2262754fe60SDimitry Andric // There's no real file at the given path.
2276122f3e6SDimitry Andric if (!CacheFailure)
2286122f3e6SDimitry Andric SeenFileEntries.erase(Filename);
2296122f3e6SDimitry Andric
23059d1ed5bSDimitry Andric return nullptr;
231f22ef01cSRoman Divacky }
232f22ef01cSRoman Divacky
23359d1ed5bSDimitry Andric assert((openFile || !F) && "undesired open file");
2343b0f4066SDimitry Andric
235f22ef01cSRoman Divacky // It exists. See if we have already opened a file with the same inode.
236f22ef01cSRoman Divacky // This occurs when one dir is symlinked to another, for example.
23759d1ed5bSDimitry Andric FileEntry &UFE = UniqueRealFiles[Data.UniqueID];
238f22ef01cSRoman Divacky
23939d628a0SDimitry Andric NamedFileEnt.second = &UFE;
24039d628a0SDimitry Andric
24139d628a0SDimitry Andric // If the name returned by getStatValue is different than Filename, re-intern
24239d628a0SDimitry Andric // the name.
24339d628a0SDimitry Andric if (Data.Name != Filename) {
24439d628a0SDimitry Andric auto &NamedFileEnt =
24539d628a0SDimitry Andric *SeenFileEntries.insert(std::make_pair(Data.Name, nullptr)).first;
24639d628a0SDimitry Andric if (!NamedFileEnt.second)
24739d628a0SDimitry Andric NamedFileEnt.second = &UFE;
24839d628a0SDimitry Andric else
24939d628a0SDimitry Andric assert(NamedFileEnt.second == &UFE &&
25039d628a0SDimitry Andric "filename from getStatValue() refers to wrong file");
25139d628a0SDimitry Andric InterndFileName = NamedFileEnt.first().data();
25239d628a0SDimitry Andric }
25339d628a0SDimitry Andric
25459d1ed5bSDimitry Andric if (UFE.isValid()) { // Already have an entry with this inode, return it.
25559d1ed5bSDimitry Andric
25659d1ed5bSDimitry Andric // FIXME: this hack ensures that if we look up a file by a virtual path in
25759d1ed5bSDimitry Andric // the VFS that the getDir() will have the virtual path, even if we found
25859d1ed5bSDimitry Andric // the file by a 'real' path first. This is required in order to find a
25959d1ed5bSDimitry Andric // module's structure when its headers/module map are mapped in the VFS.
26059d1ed5bSDimitry Andric // We should remove this as soon as we can properly support a file having
26159d1ed5bSDimitry Andric // multiple names.
26259d1ed5bSDimitry Andric if (DirInfo != UFE.Dir && Data.IsVFSMapped)
26359d1ed5bSDimitry Andric UFE.Dir = DirInfo;
2642754fe60SDimitry Andric
26539d628a0SDimitry Andric // Always update the name to use the last name by which a file was accessed.
26639d628a0SDimitry Andric // FIXME: Neither this nor always using the first name is correct; we want
26739d628a0SDimitry Andric // to switch towards a design where we return a FileName object that
26839d628a0SDimitry Andric // encapsulates both the name by which the file was accessed and the
26939d628a0SDimitry Andric // corresponding FileEntry.
27039d628a0SDimitry Andric UFE.Name = InterndFileName;
27139d628a0SDimitry Andric
272f22ef01cSRoman Divacky return &UFE;
2732754fe60SDimitry Andric }
274f22ef01cSRoman Divacky
27559d1ed5bSDimitry Andric // Otherwise, we don't have this file yet, add it.
27639d628a0SDimitry Andric UFE.Name = InterndFileName;
277f785676fSDimitry Andric UFE.Size = Data.Size;
278f785676fSDimitry Andric UFE.ModTime = Data.ModTime;
279f22ef01cSRoman Divacky UFE.Dir = DirInfo;
280f22ef01cSRoman Divacky UFE.UID = NextFileUID++;
28159d1ed5bSDimitry Andric UFE.UniqueID = Data.UniqueID;
28259d1ed5bSDimitry Andric UFE.IsNamedPipe = Data.IsNamedPipe;
28359d1ed5bSDimitry Andric UFE.InPCH = Data.InPCH;
28459d1ed5bSDimitry Andric UFE.File = std::move(F);
28559d1ed5bSDimitry Andric UFE.IsValid = true;
286*b5893f02SDimitry Andric
287*b5893f02SDimitry Andric if (UFE.File) {
288*b5893f02SDimitry Andric if (auto PathName = UFE.File->getName())
289*b5893f02SDimitry Andric fillRealPathName(&UFE, *PathName);
290*b5893f02SDimitry Andric }
291f22ef01cSRoman Divacky return &UFE;
292f22ef01cSRoman Divacky }
293f22ef01cSRoman Divacky
294f22ef01cSRoman Divacky const FileEntry *
getVirtualFile(StringRef Filename,off_t Size,time_t ModificationTime)2956122f3e6SDimitry Andric FileManager::getVirtualFile(StringRef Filename, off_t Size,
296ffd1746dSEd Schouten time_t ModificationTime) {
297f22ef01cSRoman Divacky ++NumFileLookups;
298f22ef01cSRoman Divacky
299f22ef01cSRoman Divacky // See if there is already an entry in the map.
30039d628a0SDimitry Andric auto &NamedFileEnt =
30139d628a0SDimitry Andric *SeenFileEntries.insert(std::make_pair(Filename, nullptr)).first;
302f22ef01cSRoman Divacky
303f22ef01cSRoman Divacky // See if there is already an entry in the map.
30439d628a0SDimitry Andric if (NamedFileEnt.second && NamedFileEnt.second != NON_EXISTENT_FILE)
30539d628a0SDimitry Andric return NamedFileEnt.second;
306f22ef01cSRoman Divacky
307f22ef01cSRoman Divacky ++NumFileCacheMisses;
308f22ef01cSRoman Divacky
309f22ef01cSRoman Divacky // By default, initialize it to invalid.
31039d628a0SDimitry Andric NamedFileEnt.second = NON_EXISTENT_FILE;
311f22ef01cSRoman Divacky
3122754fe60SDimitry Andric addAncestorsAsVirtualDirs(Filename);
31359d1ed5bSDimitry Andric FileEntry *UFE = nullptr;
314f22ef01cSRoman Divacky
3152754fe60SDimitry Andric // Now that all ancestors of Filename are in the cache, the
3162754fe60SDimitry Andric // following call is guaranteed to find the DirectoryEntry from the
3172754fe60SDimitry Andric // cache.
3186122f3e6SDimitry Andric const DirectoryEntry *DirInfo = getDirectoryFromFile(*this, Filename,
3196122f3e6SDimitry Andric /*CacheFailure=*/true);
3202754fe60SDimitry Andric assert(DirInfo &&
3212754fe60SDimitry Andric "The directory of a virtual file should already be in the cache.");
3222754fe60SDimitry Andric
3232754fe60SDimitry Andric // Check to see if the file exists. If so, drop the virtual file
324f785676fSDimitry Andric FileData Data;
32539d628a0SDimitry Andric const char *InterndFileName = NamedFileEnt.first().data();
32659d1ed5bSDimitry Andric if (getStatValue(InterndFileName, Data, true, nullptr) == 0) {
327f785676fSDimitry Andric Data.Size = Size;
328f785676fSDimitry Andric Data.ModTime = ModificationTime;
32959d1ed5bSDimitry Andric UFE = &UniqueRealFiles[Data.UniqueID];
3302754fe60SDimitry Andric
33139d628a0SDimitry Andric NamedFileEnt.second = UFE;
332f22ef01cSRoman Divacky
3332754fe60SDimitry Andric // If we had already opened this file, close it now so we don't
3342754fe60SDimitry Andric // leak the descriptor. We're not going to use the file
3352754fe60SDimitry Andric // descriptor anyway, since this is a virtual file.
33659d1ed5bSDimitry Andric if (UFE->File)
33759d1ed5bSDimitry Andric UFE->closeFile();
3382754fe60SDimitry Andric
3392754fe60SDimitry Andric // If we already have an entry with this inode, return it.
34059d1ed5bSDimitry Andric if (UFE->isValid())
3412754fe60SDimitry Andric return UFE;
34259d1ed5bSDimitry Andric
34359d1ed5bSDimitry Andric UFE->UniqueID = Data.UniqueID;
34459d1ed5bSDimitry Andric UFE->IsNamedPipe = Data.IsNamedPipe;
34559d1ed5bSDimitry Andric UFE->InPCH = Data.InPCH;
346*b5893f02SDimitry Andric fillRealPathName(UFE, Data.Name);
3472754fe60SDimitry Andric }
3482754fe60SDimitry Andric
3492754fe60SDimitry Andric if (!UFE) {
3500623d748SDimitry Andric VirtualFileEntries.push_back(llvm::make_unique<FileEntry>());
3510623d748SDimitry Andric UFE = VirtualFileEntries.back().get();
35239d628a0SDimitry Andric NamedFileEnt.second = UFE;
3532754fe60SDimitry Andric }
3542754fe60SDimitry Andric
3552754fe60SDimitry Andric UFE->Name = InterndFileName;
356f22ef01cSRoman Divacky UFE->Size = Size;
357f22ef01cSRoman Divacky UFE->ModTime = ModificationTime;
358f22ef01cSRoman Divacky UFE->Dir = DirInfo;
359f22ef01cSRoman Divacky UFE->UID = NextFileUID++;
36020e90f04SDimitry Andric UFE->IsValid = true;
36159d1ed5bSDimitry Andric UFE->File.reset();
362f22ef01cSRoman Divacky return UFE;
363f22ef01cSRoman Divacky }
364f22ef01cSRoman Divacky
FixupRelativePath(SmallVectorImpl<char> & path) const3650623d748SDimitry Andric bool FileManager::FixupRelativePath(SmallVectorImpl<char> &path) const {
3666122f3e6SDimitry Andric StringRef pathRef(path.data(), path.size());
3673b0f4066SDimitry Andric
3683b0f4066SDimitry Andric if (FileSystemOpts.WorkingDir.empty()
3693b0f4066SDimitry Andric || llvm::sys::path::is_absolute(pathRef))
3700623d748SDimitry Andric return false;
3712754fe60SDimitry Andric
372dff0c46cSDimitry Andric SmallString<128> NewPath(FileSystemOpts.WorkingDir);
3733b0f4066SDimitry Andric llvm::sys::path::append(NewPath, pathRef);
3742754fe60SDimitry Andric path = NewPath;
3750623d748SDimitry Andric return true;
3760623d748SDimitry Andric }
3770623d748SDimitry Andric
makeAbsolutePath(SmallVectorImpl<char> & Path) const3780623d748SDimitry Andric bool FileManager::makeAbsolutePath(SmallVectorImpl<char> &Path) const {
3790623d748SDimitry Andric bool Changed = FixupRelativePath(Path);
3800623d748SDimitry Andric
3810623d748SDimitry Andric if (!llvm::sys::path::is_absolute(StringRef(Path.data(), Path.size()))) {
3829a199699SDimitry Andric FS->makeAbsolute(Path);
3830623d748SDimitry Andric Changed = true;
3840623d748SDimitry Andric }
3850623d748SDimitry Andric
3860623d748SDimitry Andric return Changed;
3872754fe60SDimitry Andric }
3882754fe60SDimitry Andric
fillRealPathName(FileEntry * UFE,llvm::StringRef FileName)389*b5893f02SDimitry Andric void FileManager::fillRealPathName(FileEntry *UFE, llvm::StringRef FileName) {
390*b5893f02SDimitry Andric llvm::SmallString<128> AbsPath(FileName);
391*b5893f02SDimitry Andric // This is not the same as `VFS::getRealPath()`, which resolves symlinks
392*b5893f02SDimitry Andric // but can be very expensive on real file systems.
393*b5893f02SDimitry Andric // FIXME: the semantic of RealPathName is unclear, and the name might be
394*b5893f02SDimitry Andric // misleading. We need to clean up the interface here.
395*b5893f02SDimitry Andric makeAbsolutePath(AbsPath);
396*b5893f02SDimitry Andric llvm::sys::path::remove_dots(AbsPath, /*remove_dot_dot=*/true);
397*b5893f02SDimitry Andric UFE->RealPathName = AbsPath.str();
398*b5893f02SDimitry Andric }
399*b5893f02SDimitry Andric
40039d628a0SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBufferForFile(const FileEntry * Entry,bool isVolatile,bool ShouldCloseOpenFile)40139d628a0SDimitry Andric FileManager::getBufferForFile(const FileEntry *Entry, bool isVolatile,
40239d628a0SDimitry Andric bool ShouldCloseOpenFile) {
4037ae0e2c9SDimitry Andric uint64_t FileSize = Entry->getSize();
4047ae0e2c9SDimitry Andric // If there's a high enough chance that the file have changed since we
4057ae0e2c9SDimitry Andric // got its size, force a stat before opening it.
4067ae0e2c9SDimitry Andric if (isVolatile)
4077ae0e2c9SDimitry Andric FileSize = -1;
4087ae0e2c9SDimitry Andric
40944290647SDimitry Andric StringRef Filename = Entry->getName();
4102754fe60SDimitry Andric // If the file is already open, use the open file descriptor.
41159d1ed5bSDimitry Andric if (Entry->File) {
41239d628a0SDimitry Andric auto Result =
41339d628a0SDimitry Andric Entry->File->getBuffer(Filename, FileSize,
41459d1ed5bSDimitry Andric /*RequiresNullTerminator=*/true, isVolatile);
41559d1ed5bSDimitry Andric // FIXME: we need a set of APIs that can make guarantees about whether a
41659d1ed5bSDimitry Andric // FileEntry is open or not.
41759d1ed5bSDimitry Andric if (ShouldCloseOpenFile)
41859d1ed5bSDimitry Andric Entry->closeFile();
41939d628a0SDimitry Andric return Result;
4202754fe60SDimitry Andric }
4212754fe60SDimitry Andric
4222754fe60SDimitry Andric // Otherwise, open the file.
4233b0f4066SDimitry Andric
42439d628a0SDimitry Andric if (FileSystemOpts.WorkingDir.empty())
42539d628a0SDimitry Andric return FS->getBufferForFile(Filename, FileSize,
42659d1ed5bSDimitry Andric /*RequiresNullTerminator=*/true, isVolatile);
4272754fe60SDimitry Andric
428dff0c46cSDimitry Andric SmallString<128> FilePath(Entry->getName());
4293b0f4066SDimitry Andric FixupRelativePath(FilePath);
43033956c43SDimitry Andric return FS->getBufferForFile(FilePath, FileSize,
43159d1ed5bSDimitry Andric /*RequiresNullTerminator=*/true, isVolatile);
4322754fe60SDimitry Andric }
4332754fe60SDimitry Andric
43439d628a0SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBufferForFile(StringRef Filename,bool isVolatile)4354ba319b5SDimitry Andric FileManager::getBufferForFile(StringRef Filename, bool isVolatile) {
43639d628a0SDimitry Andric if (FileSystemOpts.WorkingDir.empty())
4374ba319b5SDimitry Andric return FS->getBufferForFile(Filename, -1, true, isVolatile);
4382754fe60SDimitry Andric
439dff0c46cSDimitry Andric SmallString<128> FilePath(Filename);
4403b0f4066SDimitry Andric FixupRelativePath(FilePath);
4414ba319b5SDimitry Andric return FS->getBufferForFile(FilePath.c_str(), -1, true, isVolatile);
4422754fe60SDimitry Andric }
4432754fe60SDimitry Andric
4442754fe60SDimitry Andric /// getStatValue - Get the 'stat' information for the specified path,
4452754fe60SDimitry Andric /// using the cache to accelerate it if possible. This returns true
4462754fe60SDimitry Andric /// if the path points to a virtual file or does not exist, or returns
4472754fe60SDimitry Andric /// false if it's an existent real file. If FileDescriptor is NULL,
4482754fe60SDimitry Andric /// do directory look-up instead of file look-up.
getStatValue(StringRef Path,FileData & Data,bool isFile,std::unique_ptr<llvm::vfs::File> * F)44944290647SDimitry Andric bool FileManager::getStatValue(StringRef Path, FileData &Data, bool isFile,
450*b5893f02SDimitry Andric std::unique_ptr<llvm::vfs::File> *F) {
4512754fe60SDimitry Andric // FIXME: FileSystemOpts shouldn't be passed in here, all paths should be
4522754fe60SDimitry Andric // absolute!
4532754fe60SDimitry Andric if (FileSystemOpts.WorkingDir.empty())
45459d1ed5bSDimitry Andric return FileSystemStatCache::get(Path, Data, isFile, F,StatCache.get(), *FS);
4552754fe60SDimitry Andric
456dff0c46cSDimitry Andric SmallString<128> FilePath(Path);
4573b0f4066SDimitry Andric FixupRelativePath(FilePath);
4582754fe60SDimitry Andric
45959d1ed5bSDimitry Andric return FileSystemStatCache::get(FilePath.c_str(), Data, isFile, F,
46059d1ed5bSDimitry Andric StatCache.get(), *FS);
4612754fe60SDimitry Andric }
4622754fe60SDimitry Andric
getNoncachedStatValue(StringRef Path,llvm::vfs::Status & Result)4636122f3e6SDimitry Andric bool FileManager::getNoncachedStatValue(StringRef Path,
464*b5893f02SDimitry Andric llvm::vfs::Status &Result) {
465dff0c46cSDimitry Andric SmallString<128> FilePath(Path);
4663b0f4066SDimitry Andric FixupRelativePath(FilePath);
4673b0f4066SDimitry Andric
468*b5893f02SDimitry Andric llvm::ErrorOr<llvm::vfs::Status> S = FS->status(FilePath.c_str());
46959d1ed5bSDimitry Andric if (!S)
47059d1ed5bSDimitry Andric return true;
47159d1ed5bSDimitry Andric Result = *S;
47259d1ed5bSDimitry Andric return false;
4733b0f4066SDimitry Andric }
4743b0f4066SDimitry Andric
invalidateCache(const FileEntry * Entry)4757ae0e2c9SDimitry Andric void FileManager::invalidateCache(const FileEntry *Entry) {
4767ae0e2c9SDimitry Andric assert(Entry && "Cannot invalidate a NULL FileEntry");
4777ae0e2c9SDimitry Andric
4787ae0e2c9SDimitry Andric SeenFileEntries.erase(Entry->getName());
4797ae0e2c9SDimitry Andric
4807ae0e2c9SDimitry Andric // FileEntry invalidation should not block future optimizations in the file
4817ae0e2c9SDimitry Andric // caches. Possible alternatives are cache truncation (invalidate last N) or
4827ae0e2c9SDimitry Andric // invalidation of the whole cache.
48359d1ed5bSDimitry Andric UniqueRealFiles.erase(Entry->getUniqueID());
4847ae0e2c9SDimitry Andric }
4857ae0e2c9SDimitry Andric
GetUniqueIDMapping(SmallVectorImpl<const FileEntry * > & UIDToFiles) const4862754fe60SDimitry Andric void FileManager::GetUniqueIDMapping(
4876122f3e6SDimitry Andric SmallVectorImpl<const FileEntry *> &UIDToFiles) const {
4882754fe60SDimitry Andric UIDToFiles.clear();
4892754fe60SDimitry Andric UIDToFiles.resize(NextFileUID);
4902754fe60SDimitry Andric
4912754fe60SDimitry Andric // Map file entries
4922754fe60SDimitry Andric for (llvm::StringMap<FileEntry*, llvm::BumpPtrAllocator>::const_iterator
4932754fe60SDimitry Andric FE = SeenFileEntries.begin(), FEEnd = SeenFileEntries.end();
4942754fe60SDimitry Andric FE != FEEnd; ++FE)
4952754fe60SDimitry Andric if (FE->getValue() && FE->getValue() != NON_EXISTENT_FILE)
4962754fe60SDimitry Andric UIDToFiles[FE->getValue()->getUID()] = FE->getValue();
4972754fe60SDimitry Andric
4982754fe60SDimitry Andric // Map virtual file entries
4990623d748SDimitry Andric for (const auto &VFE : VirtualFileEntries)
5000623d748SDimitry Andric if (VFE && VFE.get() != NON_EXISTENT_FILE)
5010623d748SDimitry Andric UIDToFiles[VFE->getUID()] = VFE.get();
5022754fe60SDimitry Andric }
5032754fe60SDimitry Andric
modifyFileEntry(FileEntry * File,off_t Size,time_t ModificationTime)5047ae0e2c9SDimitry Andric void FileManager::modifyFileEntry(FileEntry *File,
5057ae0e2c9SDimitry Andric off_t Size, time_t ModificationTime) {
5067ae0e2c9SDimitry Andric File->Size = Size;
5077ae0e2c9SDimitry Andric File->ModTime = ModificationTime;
5087ae0e2c9SDimitry Andric }
5097ae0e2c9SDimitry Andric
getCanonicalName(const DirectoryEntry * Dir)510139f7f9bSDimitry Andric StringRef FileManager::getCanonicalName(const DirectoryEntry *Dir) {
511139f7f9bSDimitry Andric // FIXME: use llvm::sys::fs::canonical() when it gets implemented
512139f7f9bSDimitry Andric llvm::DenseMap<const DirectoryEntry *, llvm::StringRef>::iterator Known
513139f7f9bSDimitry Andric = CanonicalDirNames.find(Dir);
514139f7f9bSDimitry Andric if (Known != CanonicalDirNames.end())
515139f7f9bSDimitry Andric return Known->second;
516139f7f9bSDimitry Andric
517139f7f9bSDimitry Andric StringRef CanonicalName(Dir->getName());
51839d628a0SDimitry Andric
5194ba319b5SDimitry Andric SmallString<4096> CanonicalNameBuf;
5204ba319b5SDimitry Andric if (!FS->getRealPath(Dir->getName(), CanonicalNameBuf))
5210623d748SDimitry Andric CanonicalName = StringRef(CanonicalNameBuf).copy(CanonicalNameStorage);
522139f7f9bSDimitry Andric
523139f7f9bSDimitry Andric CanonicalDirNames.insert(std::make_pair(Dir, CanonicalName));
524139f7f9bSDimitry Andric return CanonicalName;
525139f7f9bSDimitry Andric }
5262754fe60SDimitry Andric
PrintStats() const527f22ef01cSRoman Divacky void FileManager::PrintStats() const {
528f22ef01cSRoman Divacky llvm::errs() << "\n*** File Manager Stats:\n";
5292754fe60SDimitry Andric llvm::errs() << UniqueRealFiles.size() << " real files found, "
5302754fe60SDimitry Andric << UniqueRealDirs.size() << " real dirs found.\n";
5312754fe60SDimitry Andric llvm::errs() << VirtualFileEntries.size() << " virtual files found, "
5322754fe60SDimitry Andric << VirtualDirectoryEntries.size() << " virtual dirs found.\n";
533f22ef01cSRoman Divacky llvm::errs() << NumDirLookups << " dir lookups, "
534f22ef01cSRoman Divacky << NumDirCacheMisses << " dir cache misses.\n";
535f22ef01cSRoman Divacky llvm::errs() << NumFileLookups << " file lookups, "
536f22ef01cSRoman Divacky << NumFileCacheMisses << " file cache misses.\n";
537f22ef01cSRoman Divacky
538f22ef01cSRoman Divacky //llvm::errs() << PagesMapped << BytesOfPagesMapped << FSLookups;
539f22ef01cSRoman Divacky }
540