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