1 //===-- FileCollector.cpp ---------------------------------------*- C++ -*-===// 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 #include "llvm/Support/FileCollector.h" 10 #include "llvm/ADT/SmallString.h" 11 #include "llvm/ADT/Twine.h" 12 #include "llvm/Support/FileSystem.h" 13 #include "llvm/Support/Path.h" 14 #include "llvm/Support/Process.h" 15 16 using namespace llvm; 17 18 FileCollectorBase::FileCollectorBase() = default; 19 FileCollectorBase::~FileCollectorBase() = default; 20 21 void FileCollectorBase::addFile(const Twine &File) { 22 std::lock_guard<std::mutex> lock(Mutex); 23 std::string FileStr = File.str(); 24 if (markAsSeen(FileStr)) 25 addFileImpl(FileStr); 26 } 27 28 void FileCollectorBase::addDirectory(const Twine &Dir) { 29 assert(sys::fs::is_directory(Dir)); 30 std::error_code EC; 31 addDirectoryImpl(Dir, vfs::getRealFileSystem(), EC); 32 } 33 34 static bool isCaseSensitivePath(StringRef Path) { 35 SmallString<256> TmpDest = Path, UpperDest, RealDest; 36 37 // Remove component traversals, links, etc. 38 if (!sys::fs::real_path(Path, TmpDest)) 39 return true; // Current default value in vfs.yaml 40 Path = TmpDest; 41 42 // Change path to all upper case and ask for its real path, if the latter 43 // exists and is equal to path, it's not case sensitive. Default to case 44 // sensitive in the absence of real_path, since this is the YAMLVFSWriter 45 // default. 46 UpperDest = Path.upper(); 47 if (sys::fs::real_path(UpperDest, RealDest) && Path.equals(RealDest)) 48 return false; 49 return true; 50 } 51 52 FileCollector::FileCollector(std::string Root, std::string OverlayRoot) 53 : Root(std::move(Root)), OverlayRoot(std::move(OverlayRoot)) { 54 } 55 56 bool FileCollector::getRealPath(StringRef SrcPath, 57 SmallVectorImpl<char> &Result) { 58 SmallString<256> RealPath; 59 StringRef FileName = sys::path::filename(SrcPath); 60 std::string Directory = sys::path::parent_path(SrcPath).str(); 61 auto DirWithSymlink = SymlinkMap.find(Directory); 62 63 // Use real_path to fix any symbolic link component present in a path. 64 // Computing the real path is expensive, cache the search through the parent 65 // path Directory. 66 if (DirWithSymlink == SymlinkMap.end()) { 67 auto EC = sys::fs::real_path(Directory, RealPath); 68 if (EC) 69 return false; 70 SymlinkMap[Directory] = std::string(RealPath.str()); 71 } else { 72 RealPath = DirWithSymlink->second; 73 } 74 75 sys::path::append(RealPath, FileName); 76 Result.swap(RealPath); 77 return true; 78 } 79 80 void FileCollector::addFileImpl(StringRef SrcPath) { 81 // We need an absolute src path to append to the root. 82 SmallString<256> AbsoluteSrc = SrcPath; 83 sys::fs::make_absolute(AbsoluteSrc); 84 85 // Canonicalize src to a native path to avoid mixed separator styles. 86 sys::path::native(AbsoluteSrc); 87 88 // Remove redundant leading "./" pieces and consecutive separators. 89 StringRef TrimmedAbsoluteSrc = 90 sys::path::remove_leading_dotslash(AbsoluteSrc); 91 92 // Canonicalize the source path by removing "..", "." components. 93 SmallString<256> VirtualPath = TrimmedAbsoluteSrc; 94 sys::path::remove_dots(VirtualPath, /*remove_dot_dot=*/true); 95 96 // If a ".." component is present after a symlink component, remove_dots may 97 // lead to the wrong real destination path. Let the source be canonicalized 98 // like that but make sure we always use the real path for the destination. 99 SmallString<256> CopyFrom; 100 if (!getRealPath(TrimmedAbsoluteSrc, CopyFrom)) 101 CopyFrom = VirtualPath; 102 103 SmallString<256> DstPath = StringRef(Root); 104 sys::path::append(DstPath, sys::path::relative_path(CopyFrom)); 105 106 // Always map a canonical src path to its real path into the YAML, by doing 107 // this we map different virtual src paths to the same entry in the VFS 108 // overlay, which is a way to emulate symlink inside the VFS; this is also 109 // needed for correctness, not doing that can lead to module redefinition 110 // errors. 111 addFileToMapping(VirtualPath, DstPath); 112 } 113 114 llvm::vfs::directory_iterator 115 FileCollector::addDirectoryImpl(const llvm::Twine &Dir, 116 IntrusiveRefCntPtr<vfs::FileSystem> FS, 117 std::error_code &EC) { 118 auto It = FS->dir_begin(Dir, EC); 119 if (EC) 120 return It; 121 addFile(Dir); 122 for (; !EC && It != llvm::vfs::directory_iterator(); It.increment(EC)) { 123 if (It->type() == sys::fs::file_type::regular_file || 124 It->type() == sys::fs::file_type::directory_file || 125 It->type() == sys::fs::file_type::symlink_file) { 126 addFile(It->path()); 127 } 128 } 129 if (EC) 130 return It; 131 // Return a new iterator. 132 return FS->dir_begin(Dir, EC); 133 } 134 135 /// Set the access and modification time for the given file from the given 136 /// status object. 137 static std::error_code 138 copyAccessAndModificationTime(StringRef Filename, 139 const sys::fs::file_status &Stat) { 140 int FD; 141 142 if (auto EC = 143 sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) 144 return EC; 145 146 if (auto EC = sys::fs::setLastAccessAndModificationTime( 147 FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) 148 return EC; 149 150 if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) 151 return EC; 152 153 return {}; 154 } 155 156 std::error_code FileCollector::copyFiles(bool StopOnError) { 157 auto Err = sys::fs::create_directories(Root, /*IgnoreExisting=*/true); 158 if (Err) { 159 return Err; 160 } 161 162 std::lock_guard<std::mutex> lock(Mutex); 163 164 for (auto &entry : VFSWriter.getMappings()) { 165 // Get the status of the original file/directory. 166 sys::fs::file_status Stat; 167 if (std::error_code EC = sys::fs::status(entry.VPath, Stat)) { 168 if (StopOnError) 169 return EC; 170 continue; 171 } 172 173 // Continue if the file doesn't exist. 174 if (Stat.type() == sys::fs::file_type::file_not_found) 175 continue; 176 177 // Create directory tree. 178 if (std::error_code EC = 179 sys::fs::create_directories(sys::path::parent_path(entry.RPath), 180 /*IgnoreExisting=*/true)) { 181 if (StopOnError) 182 return EC; 183 } 184 185 if (Stat.type() == sys::fs::file_type::directory_file) { 186 // Construct a directory when it's just a directory entry. 187 if (std::error_code EC = 188 sys::fs::create_directories(entry.RPath, 189 /*IgnoreExisting=*/true)) { 190 if (StopOnError) 191 return EC; 192 } 193 continue; 194 } 195 196 // Copy file over. 197 if (std::error_code EC = sys::fs::copy_file(entry.VPath, entry.RPath)) { 198 if (StopOnError) 199 return EC; 200 } 201 202 // Copy over permissions. 203 if (auto perms = sys::fs::getPermissions(entry.VPath)) { 204 if (std::error_code EC = sys::fs::setPermissions(entry.RPath, *perms)) { 205 if (StopOnError) 206 return EC; 207 } 208 } 209 210 // Copy over modification time. 211 copyAccessAndModificationTime(entry.RPath, Stat); 212 } 213 return {}; 214 } 215 216 std::error_code FileCollector::writeMapping(StringRef MappingFile) { 217 std::lock_guard<std::mutex> lock(Mutex); 218 219 VFSWriter.setOverlayDir(OverlayRoot); 220 VFSWriter.setCaseSensitivity(isCaseSensitivePath(OverlayRoot)); 221 VFSWriter.setUseExternalNames(false); 222 223 std::error_code EC; 224 raw_fd_ostream os(MappingFile, EC, sys::fs::OF_Text); 225 if (EC) 226 return EC; 227 228 VFSWriter.write(os); 229 230 return {}; 231 } 232 233 namespace llvm { 234 235 class FileCollectorFileSystem : public vfs::FileSystem { 236 public: 237 explicit FileCollectorFileSystem(IntrusiveRefCntPtr<vfs::FileSystem> FS, 238 std::shared_ptr<FileCollector> Collector) 239 : FS(std::move(FS)), Collector(std::move(Collector)) {} 240 241 llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override { 242 auto Result = FS->status(Path); 243 if (Result && Result->exists()) 244 Collector->addFile(Path); 245 return Result; 246 } 247 248 llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> 249 openFileForRead(const Twine &Path) override { 250 auto Result = FS->openFileForRead(Path); 251 if (Result && *Result) 252 Collector->addFile(Path); 253 return Result; 254 } 255 256 llvm::vfs::directory_iterator dir_begin(const llvm::Twine &Dir, 257 std::error_code &EC) override { 258 return Collector->addDirectoryImpl(Dir, FS, EC); 259 } 260 261 std::error_code getRealPath(const Twine &Path, 262 SmallVectorImpl<char> &Output) const override { 263 auto EC = FS->getRealPath(Path, Output); 264 if (!EC) { 265 Collector->addFile(Path); 266 if (Output.size() > 0) 267 Collector->addFile(Output); 268 } 269 return EC; 270 } 271 272 std::error_code isLocal(const Twine &Path, bool &Result) override { 273 return FS->isLocal(Path, Result); 274 } 275 276 llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { 277 return FS->getCurrentWorkingDirectory(); 278 } 279 280 std::error_code setCurrentWorkingDirectory(const llvm::Twine &Path) override { 281 return FS->setCurrentWorkingDirectory(Path); 282 } 283 284 private: 285 IntrusiveRefCntPtr<vfs::FileSystem> FS; 286 std::shared_ptr<FileCollector> Collector; 287 }; 288 289 } // namespace llvm 290 291 IntrusiveRefCntPtr<vfs::FileSystem> 292 FileCollector::createCollectorVFS(IntrusiveRefCntPtr<vfs::FileSystem> BaseFS, 293 std::shared_ptr<FileCollector> Collector) { 294 return new FileCollectorFileSystem(std::move(BaseFS), std::move(Collector)); 295 } 296