FileCollector.cpp 8.6 KB

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