1218887Sdim//===--- FileSystemStatCache.cpp - Caching for 'stat' calls ---------------===// 2218887Sdim// 3218887Sdim// The LLVM Compiler Infrastructure 4218887Sdim// 5218887Sdim// This file is distributed under the University of Illinois Open Source 6218887Sdim// License. See LICENSE.TXT for details. 7218887Sdim// 8218887Sdim//===----------------------------------------------------------------------===// 9218887Sdim// 10218887Sdim// This file defines the FileSystemStatCache interface. 11218887Sdim// 12218887Sdim//===----------------------------------------------------------------------===// 13218887Sdim 14218887Sdim#include "clang/Basic/FileSystemStatCache.h" 15263509Sdim#include "llvm/Support/FileSystem.h" 16218887Sdim#include "llvm/Support/Path.h" 17218887Sdim 18218887Sdim// FIXME: This is terrible, we need this for ::close. 19218887Sdim#if !defined(_MSC_VER) && !defined(__MINGW32__) 20218887Sdim#include <unistd.h> 21218887Sdim#include <sys/uio.h> 22218887Sdim#else 23218887Sdim#include <io.h> 24218887Sdim#endif 25218887Sdimusing namespace clang; 26218887Sdim 27218887Sdim#if defined(_MSC_VER) 28218887Sdim#define S_ISDIR(s) ((_S_IFDIR & s) !=0) 29218887Sdim#endif 30218887Sdim 31235633Sdimvoid FileSystemStatCache::anchor() { } 32235633Sdim 33263509Sdimstatic void copyStatusToFileData(const llvm::sys::fs::file_status &Status, 34263509Sdim FileData &Data) { 35263509Sdim Data.Size = Status.getSize(); 36263509Sdim Data.ModTime = Status.getLastModificationTime().toEpochTime(); 37263509Sdim Data.UniqueID = Status.getUniqueID(); 38263509Sdim Data.IsDirectory = is_directory(Status); 39263509Sdim Data.IsNamedPipe = Status.type() == llvm::sys::fs::file_type::fifo_file; 40263509Sdim Data.InPCH = false; 41263509Sdim} 42263509Sdim 43218887Sdim/// FileSystemStatCache::get - Get the 'stat' information for the specified 44218887Sdim/// path, using the cache to accelerate it if possible. This returns true if 45218887Sdim/// the path does not exist or false if it exists. 46218887Sdim/// 47252723Sdim/// If isFile is true, then this lookup should only return success for files 48252723Sdim/// (not directories). If it is false this lookup should only return 49218887Sdim/// success for directories (not files). On a successful file lookup, the 50218887Sdim/// implementation can optionally fill in FileDescriptor with a valid 51218887Sdim/// descriptor and the client guarantees that it will close it. 52263509Sdimbool FileSystemStatCache::get(const char *Path, FileData &Data, bool isFile, 53263509Sdim int *FileDescriptor, FileSystemStatCache *Cache) { 54218887Sdim LookupResult R; 55252723Sdim bool isForDir = !isFile; 56218887Sdim 57218887Sdim // If we have a cache, use it to resolve the stat query. 58218887Sdim if (Cache) 59263509Sdim R = Cache->getStat(Path, Data, isFile, FileDescriptor); 60252723Sdim else if (isForDir || !FileDescriptor) { 61252723Sdim // If this is a directory or a file descriptor is not needed and we have 62252723Sdim // no cache, just go to the file system. 63263509Sdim llvm::sys::fs::file_status Status; 64263509Sdim if (llvm::sys::fs::status(Path, Status)) { 65263509Sdim R = CacheMissing; 66263509Sdim } else { 67263509Sdim R = CacheExists; 68263509Sdim copyStatusToFileData(Status, Data); 69263509Sdim } 70218887Sdim } else { 71218887Sdim // Otherwise, we have to go to the filesystem. We can always just use 72218887Sdim // 'stat' here, but (for files) the client is asking whether the file exists 73218887Sdim // because it wants to turn around and *open* it. It is more efficient to 74218887Sdim // do "open+fstat" on success than it is to do "stat+open". 75218887Sdim // 76218887Sdim // Because of this, check to see if the file exists with 'open'. If the 77218887Sdim // open succeeds, use fstat to get the stat info. 78263509Sdim llvm::error_code EC = llvm::sys::fs::openFileForRead(Path, *FileDescriptor); 79263509Sdim 80263509Sdim if (EC) { 81218887Sdim // If the open fails, our "stat" fails. 82218887Sdim R = CacheMissing; 83218887Sdim } else { 84218887Sdim // Otherwise, the open succeeded. Do an fstat to get the information 85218887Sdim // about the file. We'll end up returning the open file descriptor to the 86218887Sdim // client to do what they please with it. 87263509Sdim llvm::sys::fs::file_status Status; 88263509Sdim if (!llvm::sys::fs::status(*FileDescriptor, Status)) { 89218887Sdim R = CacheExists; 90263509Sdim copyStatusToFileData(Status, Data); 91263509Sdim } else { 92218887Sdim // fstat rarely fails. If it does, claim the initial open didn't 93218887Sdim // succeed. 94218887Sdim R = CacheMissing; 95218887Sdim ::close(*FileDescriptor); 96218887Sdim *FileDescriptor = -1; 97218887Sdim } 98218887Sdim } 99218887Sdim } 100218887Sdim 101218887Sdim // If the path doesn't exist, return failure. 102218887Sdim if (R == CacheMissing) return true; 103218887Sdim 104218887Sdim // If the path exists, make sure that its "directoryness" matches the clients 105218887Sdim // demands. 106263509Sdim if (Data.IsDirectory != isForDir) { 107218887Sdim // If not, close the file if opened. 108218887Sdim if (FileDescriptor && *FileDescriptor != -1) { 109218887Sdim ::close(*FileDescriptor); 110218887Sdim *FileDescriptor = -1; 111218887Sdim } 112218887Sdim 113218887Sdim return true; 114218887Sdim } 115218887Sdim 116218887Sdim return false; 117218887Sdim} 118218887Sdim 119263509SdimMemorizeStatCalls::LookupResult 120263509SdimMemorizeStatCalls::getStat(const char *Path, FileData &Data, bool isFile, 121263509Sdim int *FileDescriptor) { 122263509Sdim LookupResult Result = statChained(Path, Data, isFile, FileDescriptor); 123218887Sdim 124218887Sdim // Do not cache failed stats, it is easy to construct common inconsistent 125218887Sdim // situations if we do, and they are not important for PCH performance (which 126218887Sdim // currently only needs the stats to construct the initial FileManager 127218887Sdim // entries). 128218887Sdim if (Result == CacheMissing) 129218887Sdim return Result; 130218887Sdim 131218887Sdim // Cache file 'stat' results and directories with absolutely paths. 132263509Sdim if (!Data.IsDirectory || llvm::sys::path::is_absolute(Path)) 133263509Sdim StatCalls[Path] = Data; 134263509Sdim 135218887Sdim return Result; 136218887Sdim} 137