14ba319b5SDimitry Andric //===- FileSystemStatCache.cpp - Caching for 'stat' calls -----------------===//
22754fe60SDimitry Andric //
32754fe60SDimitry Andric //                     The LLVM Compiler Infrastructure
42754fe60SDimitry Andric //
52754fe60SDimitry Andric // This file is distributed under the University of Illinois Open Source
62754fe60SDimitry Andric // License. See LICENSE.TXT for details.
72754fe60SDimitry Andric //
82754fe60SDimitry Andric //===----------------------------------------------------------------------===//
92754fe60SDimitry Andric //
102754fe60SDimitry Andric //  This file defines the FileSystemStatCache interface.
112754fe60SDimitry Andric //
122754fe60SDimitry Andric //===----------------------------------------------------------------------===//
132754fe60SDimitry Andric 
142754fe60SDimitry Andric #include "clang/Basic/FileSystemStatCache.h"
154ba319b5SDimitry Andric #include "llvm/Support/Chrono.h"
164ba319b5SDimitry Andric #include "llvm/Support/ErrorOr.h"
172754fe60SDimitry Andric #include "llvm/Support/Path.h"
18*b5893f02SDimitry Andric #include "llvm/Support/VirtualFileSystem.h"
194ba319b5SDimitry Andric #include <utility>
202754fe60SDimitry Andric 
212754fe60SDimitry Andric using namespace clang;
222754fe60SDimitry Andric 
anchor()23dff0c46cSDimitry Andric void FileSystemStatCache::anchor() {}
24dff0c46cSDimitry Andric 
copyStatusToFileData(const llvm::vfs::Status & Status,FileData & Data)25*b5893f02SDimitry Andric static void copyStatusToFileData(const llvm::vfs::Status &Status,
26f785676fSDimitry Andric                                  FileData &Data) {
2759d1ed5bSDimitry Andric   Data.Name = Status.getName();
28f785676fSDimitry Andric   Data.Size = Status.getSize();
2944290647SDimitry Andric   Data.ModTime = llvm::sys::toTimeT(Status.getLastModificationTime());
30f785676fSDimitry Andric   Data.UniqueID = Status.getUniqueID();
3159d1ed5bSDimitry Andric   Data.IsDirectory = Status.isDirectory();
3259d1ed5bSDimitry Andric   Data.IsNamedPipe = Status.getType() == llvm::sys::fs::file_type::fifo_file;
33f785676fSDimitry Andric   Data.InPCH = false;
3459d1ed5bSDimitry Andric   Data.IsVFSMapped = Status.IsVFSMapped;
35f785676fSDimitry Andric }
36f785676fSDimitry Andric 
372754fe60SDimitry Andric /// FileSystemStatCache::get - Get the 'stat' information for the specified
382754fe60SDimitry Andric /// path, using the cache to accelerate it if possible.  This returns true if
392754fe60SDimitry Andric /// the path does not exist or false if it exists.
402754fe60SDimitry Andric ///
41139f7f9bSDimitry Andric /// If isFile is true, then this lookup should only return success for files
42139f7f9bSDimitry Andric /// (not directories).  If it is false this lookup should only return
432754fe60SDimitry Andric /// success for directories (not files).  On a successful file lookup, the
442754fe60SDimitry Andric /// implementation can optionally fill in FileDescriptor with a valid
452754fe60SDimitry Andric /// descriptor and the client guarantees that it will close it.
get(StringRef Path,FileData & Data,bool isFile,std::unique_ptr<llvm::vfs::File> * F,FileSystemStatCache * Cache,llvm::vfs::FileSystem & FS)4644290647SDimitry Andric bool FileSystemStatCache::get(StringRef Path, FileData &Data, bool isFile,
47*b5893f02SDimitry Andric                               std::unique_ptr<llvm::vfs::File> *F,
48*b5893f02SDimitry Andric                               FileSystemStatCache *Cache,
49*b5893f02SDimitry Andric                               llvm::vfs::FileSystem &FS) {
502754fe60SDimitry Andric   LookupResult R;
51139f7f9bSDimitry Andric   bool isForDir = !isFile;
522754fe60SDimitry Andric 
532754fe60SDimitry Andric   // If we have a cache, use it to resolve the stat query.
542754fe60SDimitry Andric   if (Cache)
5559d1ed5bSDimitry Andric     R = Cache->getStat(Path, Data, isFile, F, FS);
5659d1ed5bSDimitry Andric   else if (isForDir || !F) {
57139f7f9bSDimitry Andric     // If this is a directory or a file descriptor is not needed and we have
58139f7f9bSDimitry Andric     // no cache, just go to the file system.
59*b5893f02SDimitry Andric     llvm::ErrorOr<llvm::vfs::Status> Status = FS.status(Path);
6059d1ed5bSDimitry Andric     if (!Status) {
61f785676fSDimitry Andric       R = CacheMissing;
62f785676fSDimitry Andric     } else {
63f785676fSDimitry Andric       R = CacheExists;
6459d1ed5bSDimitry Andric       copyStatusToFileData(*Status, Data);
65f785676fSDimitry Andric     }
662754fe60SDimitry Andric   } else {
672754fe60SDimitry Andric     // Otherwise, we have to go to the filesystem.  We can always just use
682754fe60SDimitry Andric     // 'stat' here, but (for files) the client is asking whether the file exists
692754fe60SDimitry Andric     // because it wants to turn around and *open* it.  It is more efficient to
702754fe60SDimitry Andric     // do "open+fstat" on success than it is to do "stat+open".
712754fe60SDimitry Andric     //
722754fe60SDimitry Andric     // Because of this, check to see if the file exists with 'open'.  If the
732754fe60SDimitry Andric     // open succeeds, use fstat to get the stat info.
7439d628a0SDimitry Andric     auto OwnedFile = FS.openFileForRead(Path);
752754fe60SDimitry Andric 
7639d628a0SDimitry Andric     if (!OwnedFile) {
772754fe60SDimitry Andric       // If the open fails, our "stat" fails.
782754fe60SDimitry Andric       R = CacheMissing;
792754fe60SDimitry Andric     } else {
802754fe60SDimitry Andric       // Otherwise, the open succeeded.  Do an fstat to get the information
812754fe60SDimitry Andric       // about the file.  We'll end up returning the open file descriptor to the
822754fe60SDimitry Andric       // client to do what they please with it.
83*b5893f02SDimitry Andric       llvm::ErrorOr<llvm::vfs::Status> Status = (*OwnedFile)->status();
8459d1ed5bSDimitry Andric       if (Status) {
852754fe60SDimitry Andric         R = CacheExists;
8659d1ed5bSDimitry Andric         copyStatusToFileData(*Status, Data);
8739d628a0SDimitry Andric         *F = std::move(*OwnedFile);
88f785676fSDimitry Andric       } else {
892754fe60SDimitry Andric         // fstat rarely fails.  If it does, claim the initial open didn't
902754fe60SDimitry Andric         // succeed.
912754fe60SDimitry Andric         R = CacheMissing;
9259d1ed5bSDimitry Andric         *F = nullptr;
932754fe60SDimitry Andric       }
942754fe60SDimitry Andric     }
952754fe60SDimitry Andric   }
962754fe60SDimitry Andric 
972754fe60SDimitry Andric   // If the path doesn't exist, return failure.
982754fe60SDimitry Andric   if (R == CacheMissing) return true;
992754fe60SDimitry Andric 
1002754fe60SDimitry Andric   // If the path exists, make sure that its "directoryness" matches the clients
1012754fe60SDimitry Andric   // demands.
102f785676fSDimitry Andric   if (Data.IsDirectory != isForDir) {
1032754fe60SDimitry Andric     // If not, close the file if opened.
10459d1ed5bSDimitry Andric     if (F)
10559d1ed5bSDimitry Andric       *F = nullptr;
1062754fe60SDimitry Andric 
1072754fe60SDimitry Andric     return true;
1082754fe60SDimitry Andric   }
1092754fe60SDimitry Andric 
1102754fe60SDimitry Andric   return false;
1112754fe60SDimitry Andric }
1122754fe60SDimitry Andric 
1132754fe60SDimitry Andric MemorizeStatCalls::LookupResult
getStat(StringRef Path,FileData & Data,bool isFile,std::unique_ptr<llvm::vfs::File> * F,llvm::vfs::FileSystem & FS)11444290647SDimitry Andric MemorizeStatCalls::getStat(StringRef Path, FileData &Data, bool isFile,
115*b5893f02SDimitry Andric                            std::unique_ptr<llvm::vfs::File> *F,
116*b5893f02SDimitry Andric                            llvm::vfs::FileSystem &FS) {
117*b5893f02SDimitry Andric   if (get(Path, Data, isFile, F, nullptr, FS)) {
1182754fe60SDimitry Andric     // Do not cache failed stats, it is easy to construct common inconsistent
119*b5893f02SDimitry Andric     // situations if we do, and they are not important for PCH performance
120*b5893f02SDimitry Andric     // (which currently only needs the stats to construct the initial
121*b5893f02SDimitry Andric     // FileManager entries).
122*b5893f02SDimitry Andric     return CacheMissing;
123*b5893f02SDimitry Andric   }
1242754fe60SDimitry Andric 
1252754fe60SDimitry Andric   // Cache file 'stat' results and directories with absolutely paths.
126f785676fSDimitry Andric   if (!Data.IsDirectory || llvm::sys::path::is_absolute(Path))
127f785676fSDimitry Andric     StatCalls[Path] = Data;
1282754fe60SDimitry Andric 
129*b5893f02SDimitry Andric   return CacheExists;
1302754fe60SDimitry Andric }
131