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