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> tmp_dest = path, upper_dest, real_dest; 19 20 // Remove component traversals, links, etc. 21 if (!sys::fs::real_path(path, tmp_dest)) 22 return true; // Current default value in vfs.yaml 23 path = tmp_dest; 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 upper_dest = path.upper(); 30 if (sys::fs::real_path(upper_dest, real_dest) && path.equals(real_dest)) 31 return false; 32 return true; 33 } 34 35 FileCollector::FileCollector(std::string root, std::string overlay_root) 36 : m_root(std::move(root)), m_overlay_root(std::move(overlay_root)) { 37 sys::fs::create_directories(this->m_root, true); 38 } 39 40 bool FileCollector::GetRealPath(StringRef src_path, 41 SmallVectorImpl<char> &result) { 42 SmallString<256> real_path; 43 StringRef FileName = sys::path::filename(src_path); 44 std::string directory = sys::path::parent_path(src_path).str(); 45 auto dir_with_symlink = m_symlink_map.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 49 // parent path directory. 50 if (dir_with_symlink == m_symlink_map.end()) { 51 auto ec = sys::fs::real_path(directory, real_path); 52 if (ec) 53 return false; 54 m_symlink_map[directory] = real_path.str(); 55 } else { 56 real_path = dir_with_symlink->second; 57 } 58 59 sys::path::append(real_path, FileName); 60 result.swap(real_path); 61 return true; 62 } 63 64 void FileCollector::AddFile(const Twine &file) { 65 std::lock_guard<std::mutex> lock(m_mutex); 66 std::string file_str = file.str(); 67 if (MarkAsSeen(file_str)) 68 AddFileImpl(file_str); 69 } 70 71 void FileCollector::AddFileImpl(StringRef src_path) { 72 // We need an absolute src path to append to the root. 73 SmallString<256> absolute_src = src_path; 74 sys::fs::make_absolute(absolute_src); 75 76 // Canonicalize src to a native path to avoid mixed separator styles. 77 sys::path::native(absolute_src); 78 79 // Remove redundant leading "./" pieces and consecutive separators. 80 absolute_src = sys::path::remove_leading_dotslash(absolute_src); 81 82 // Canonicalize the source path by removing "..", "." components. 83 SmallString<256> virtual_path = absolute_src; 84 sys::path::remove_dots(virtual_path, /*remove_dot_dot=*/true); 85 86 // If a ".." component is present after a symlink component, remove_dots may 87 // lead to the wrong real destination path. Let the source be canonicalized 88 // like that but make sure we always use the real path for the destination. 89 SmallString<256> copy_from; 90 if (!GetRealPath(absolute_src, copy_from)) 91 copy_from = virtual_path; 92 93 SmallString<256> dst_path = StringRef(m_root); 94 sys::path::append(dst_path, sys::path::relative_path(copy_from)); 95 96 // Always map a canonical src path to its real path into the YAML, by doing 97 // this we map different virtual src paths to the same entry in the VFS 98 // overlay, which is a way to emulate symlink inside the VFS; this is also 99 // needed for correctness, not doing that can lead to module redefinition 100 // errors. 101 AddFileToMapping(virtual_path, dst_path); 102 } 103 104 /// Set the access and modification time for the given file from the given 105 /// status object. 106 static std::error_code 107 CopyAccessAndModificationTime(StringRef filename, 108 const sys::fs::file_status &stat) { 109 int fd; 110 111 if (auto ec = 112 sys::fs::openFileForWrite(filename, fd, sys::fs::CD_OpenExisting)) 113 return ec; 114 115 if (auto ec = sys::fs::setLastAccessAndModificationTime( 116 fd, stat.getLastAccessedTime(), stat.getLastModificationTime())) 117 return ec; 118 119 if (auto ec = sys::Process::SafelyCloseFileDescriptor(fd)) 120 return ec; 121 122 return {}; 123 } 124 125 std::error_code FileCollector::CopyFiles(bool stop_on_error) { 126 for (auto &entry : m_vfs_writer.getMappings()) { 127 // Create directory tree. 128 if (std::error_code ec = 129 sys::fs::create_directories(sys::path::parent_path(entry.RPath), 130 /*IgnoreExisting=*/true)) { 131 if (stop_on_error) 132 return ec; 133 } 134 135 // Copy file over. 136 if (std::error_code ec = sys::fs::copy_file(entry.VPath, entry.RPath)) { 137 if (stop_on_error) 138 return ec; 139 } 140 141 // Copy over permissions. 142 if (auto perms = sys::fs::getPermissions(entry.VPath)) { 143 if (std::error_code ec = sys::fs::setPermissions(entry.RPath, *perms)) { 144 if (stop_on_error) 145 return ec; 146 } 147 } 148 149 // Copy over modification time. 150 sys::fs::file_status stat; 151 if (std::error_code ec = sys::fs::status(entry.VPath, stat)) { 152 if (stop_on_error) 153 return ec; 154 continue; 155 } 156 CopyAccessAndModificationTime(entry.RPath, stat); 157 } 158 return {}; 159 } 160 161 std::error_code FileCollector::WriteMapping(StringRef mapping_file) { 162 std::lock_guard<std::mutex> lock(m_mutex); 163 164 StringRef root = m_overlay_root; 165 m_vfs_writer.setOverlayDir(root); 166 m_vfs_writer.setCaseSensitivity(IsCaseSensitivePath(root)); 167 m_vfs_writer.setUseExternalNames(false); 168 169 std::error_code ec; 170 raw_fd_ostream os(mapping_file, ec, sys::fs::F_Text); 171 if (ec) 172 return ec; 173 174 m_vfs_writer.write(os); 175 176 return {}; 177 } 178