brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.2 KiB · 1e5de2c Raw
319 lines · cpp
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-exception6//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 16using namespace llvm;17 18FileCollectorBase::FileCollectorBase() = default;19FileCollectorBase::~FileCollectorBase() = default;20 21void 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 28void 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 34static 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.yaml40  Path = TmpDest;41 42  // Change path to all upper case and ask for its real path, if the latter43  // exists and is equal to path, it's not case sensitive. Default to case44  // sensitive in the absence of real_path, since this is the YAMLVFSWriter45  // default.46  UpperDest = Path.upper();47  if (!sys::fs::real_path(UpperDest, RealDest) && Path == RealDest)48    return false;49  return true;50}51 52FileCollector::FileCollector(std::string Root, std::string OverlayRoot,53                             IntrusiveRefCntPtr<vfs::FileSystem> VFS)54    : Root(Root), OverlayRoot(OverlayRoot), Canonicalizer(std::move(VFS)) {55  assert(sys::path::is_absolute(Root) && "Root not absolute");56  assert(sys::path::is_absolute(OverlayRoot) && "OverlayRoot not absolute");57}58 59void FileCollector::PathCanonicalizer::updateWithRealPath(60    SmallVectorImpl<char> &Path) {61  StringRef SrcPath(Path.begin(), Path.size());62  StringRef Filename = sys::path::filename(SrcPath);63  StringRef Directory = sys::path::parent_path(SrcPath);64 65  // Use real_path to fix any symbolic link component present in the directory66  // part of the path, caching the search because computing the real path is67  // expensive.68  SmallString<256> RealPath;69  auto DirWithSymlink = CachedDirs.find(Directory);70  if (DirWithSymlink == CachedDirs.end()) {71    // FIXME: What if there is nothing on disk?72    if (VFS->getRealPath(Directory, RealPath))73      return;74    CachedDirs[Directory] = std::string(RealPath);75  } else {76    RealPath = DirWithSymlink->second;77  }78 79  // Finish recreating the path by appending the original filename, since we80  // don't need to resolve symlinks in the filename.81  //82  // FIXME: If we can cope with this, maybe we can cope without calling83  // getRealPath() at all when there's no ".." component.84  sys::path::append(RealPath, Filename);85 86  // Swap to create the output.87  Path.swap(RealPath);88}89 90/// Make Path absolute.91static void makeAbsolute(vfs::FileSystem &VFS, SmallVectorImpl<char> &Path) {92  // We need an absolute src path to append to the root.93  VFS.makeAbsolute(Path);94 95  // Canonicalize src to a native path to avoid mixed separator styles.96  sys::path::native(Path);97 98  // Remove redundant leading "./" pieces and consecutive separators.99  Path.erase(Path.begin(), sys::path::remove_leading_dotslash(100                               StringRef(Path.begin(), Path.size()))101                               .begin());102}103 104FileCollector::PathCanonicalizer::PathStorage105FileCollector::PathCanonicalizer::canonicalize(StringRef SrcPath) {106  PathStorage Paths;107  Paths.VirtualPath = SrcPath;108  makeAbsolute(*VFS, Paths.VirtualPath);109 110  // If a ".." component is present after a symlink component, remove_dots may111  // lead to the wrong real destination path. Let the source be canonicalized112  // like that but make sure we always use the real path for the destination.113  Paths.CopyFrom = Paths.VirtualPath;114  updateWithRealPath(Paths.CopyFrom);115 116  // Canonicalize the virtual path by removing "..", "." components.117  sys::path::remove_dots(Paths.VirtualPath, /*remove_dot_dot=*/true);118 119  return Paths;120}121 122void FileCollector::addFileImpl(StringRef SrcPath) {123  PathCanonicalizer::PathStorage Paths = Canonicalizer.canonicalize(SrcPath);124 125  SmallString<256> DstPath = StringRef(Root);126  sys::path::append(DstPath, sys::path::relative_path(Paths.CopyFrom));127 128  // Always map a canonical src path to its real path into the YAML, by doing129  // this we map different virtual src paths to the same entry in the VFS130  // overlay, which is a way to emulate symlink inside the VFS; this is also131  // needed for correctness, not doing that can lead to module redefinition132  // errors.133  addFileToMapping(Paths.VirtualPath, DstPath);134}135 136llvm::vfs::directory_iterator137FileCollector::addDirectoryImpl(const llvm::Twine &Dir,138                                IntrusiveRefCntPtr<vfs::FileSystem> FS,139                                std::error_code &EC) {140  auto It = FS->dir_begin(Dir, EC);141  if (EC)142    return It;143  addFile(Dir);144  for (; !EC && It != llvm::vfs::directory_iterator(); It.increment(EC)) {145    if (It->type() == sys::fs::file_type::regular_file ||146        It->type() == sys::fs::file_type::directory_file ||147        It->type() == sys::fs::file_type::symlink_file) {148      addFile(It->path());149    }150  }151  if (EC)152    return It;153  // Return a new iterator.154  return FS->dir_begin(Dir, EC);155}156 157/// Set the access and modification time for the given file from the given158/// status object.159static std::error_code160copyAccessAndModificationTime(StringRef Filename,161                              const sys::fs::file_status &Stat) {162  int FD;163 164  if (auto EC =165          sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting))166    return EC;167 168  if (auto EC = sys::fs::setLastAccessAndModificationTime(169          FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime()))170    return EC;171 172  if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD))173    return EC;174 175  return {};176}177 178std::error_code FileCollector::copyFiles(bool StopOnError) {179  auto Err = sys::fs::create_directories(Root, /*IgnoreExisting=*/true);180  if (Err) {181    return Err;182  }183 184  std::lock_guard<std::mutex> lock(Mutex);185 186  for (auto &entry : VFSWriter.getMappings()) {187    // Get the status of the original file/directory.188    sys::fs::file_status Stat;189    if (std::error_code EC = sys::fs::status(entry.VPath, Stat)) {190      if (StopOnError)191        return EC;192      continue;193    }194 195    // Continue if the file doesn't exist.196    if (Stat.type() == sys::fs::file_type::file_not_found)197      continue;198 199    // Create directory tree.200    if (std::error_code EC =201            sys::fs::create_directories(sys::path::parent_path(entry.RPath),202                                        /*IgnoreExisting=*/true)) {203      if (StopOnError)204        return EC;205    }206 207    if (Stat.type() == sys::fs::file_type::directory_file) {208      // Construct a directory when it's just a directory entry.209      if (std::error_code EC =210              sys::fs::create_directories(entry.RPath,211                                          /*IgnoreExisting=*/true)) {212        if (StopOnError)213          return EC;214      }215      continue;216    }217 218    // Copy file over.219    if (std::error_code EC = sys::fs::copy_file(entry.VPath, entry.RPath)) {220      if (StopOnError)221        return EC;222    }223 224    // Copy over permissions.225    if (auto perms = sys::fs::getPermissions(entry.VPath)) {226      if (std::error_code EC = sys::fs::setPermissions(entry.RPath, *perms)) {227        if (StopOnError)228          return EC;229      }230    }231 232    // Copy over modification time.233    copyAccessAndModificationTime(entry.RPath, Stat);234  }235  return {};236}237 238std::error_code FileCollector::writeMapping(StringRef MappingFile) {239  std::lock_guard<std::mutex> lock(Mutex);240 241  VFSWriter.setOverlayDir(OverlayRoot);242  VFSWriter.setCaseSensitivity(isCaseSensitivePath(OverlayRoot));243  VFSWriter.setUseExternalNames(false);244 245  std::error_code EC;246  raw_fd_ostream os(MappingFile, EC, sys::fs::OF_TextWithCRLF);247  if (EC)248    return EC;249 250  VFSWriter.write(os);251 252  return {};253}254 255namespace llvm {256 257class FileCollectorFileSystem : public vfs::FileSystem {258public:259  explicit FileCollectorFileSystem(IntrusiveRefCntPtr<vfs::FileSystem> FS,260                                   std::shared_ptr<FileCollector> Collector)261      : FS(std::move(FS)), Collector(std::move(Collector)) {}262 263  llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {264    auto Result = FS->status(Path);265    if (Result && Result->exists())266      Collector->addFile(Path);267    return Result;268  }269 270  llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>271  openFileForRead(const Twine &Path) override {272    auto Result = FS->openFileForRead(Path);273    if (Result && *Result)274      Collector->addFile(Path);275    return Result;276  }277 278  llvm::vfs::directory_iterator dir_begin(const llvm::Twine &Dir,279                                          std::error_code &EC) override {280    return Collector->addDirectoryImpl(Dir, FS, EC);281  }282 283  std::error_code getRealPath(const Twine &Path,284                              SmallVectorImpl<char> &Output) override {285    auto EC = FS->getRealPath(Path, Output);286    if (!EC) {287      Collector->addFile(Path);288      if (Output.size() > 0)289        Collector->addFile(Output);290    }291    return EC;292  }293 294  std::error_code isLocal(const Twine &Path, bool &Result) override {295    return FS->isLocal(Path, Result);296  }297 298  llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {299    return FS->getCurrentWorkingDirectory();300  }301 302  std::error_code setCurrentWorkingDirectory(const llvm::Twine &Path) override {303    return FS->setCurrentWorkingDirectory(Path);304  }305 306private:307  IntrusiveRefCntPtr<vfs::FileSystem> FS;308  std::shared_ptr<FileCollector> Collector;309};310 311} // namespace llvm312 313IntrusiveRefCntPtr<vfs::FileSystem>314FileCollector::createCollectorVFS(IntrusiveRefCntPtr<vfs::FileSystem> BaseFS,315                                  std::shared_ptr<FileCollector> Collector) {316  return makeIntrusiveRefCnt<FileCollectorFileSystem>(std::move(BaseFS),317                                                      std::move(Collector));318}319