blob: 05bea6618f230f936625c5f12c79e2a95f7e693c [file] [log] [blame]
Jonas Devlieghere46575172019-01-29 20:36:38 +00001//===-- 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
Alex Lorenz86814bf2019-07-24 22:59:20 +00009#include "llvm/Support/FileCollector.h"
Jonas Devlieghere46575172019-01-29 20:36:38 +000010#include "llvm/ADT/SmallString.h"
11#include "llvm/Support/FileSystem.h"
12#include "llvm/Support/Path.h"
Jonas Devlieghere27789ce2019-06-26 18:14:31 +000013#include "llvm/Support/Process.h"
Jonas Devlieghere46575172019-01-29 20:36:38 +000014
Jonas Devlieghere46575172019-01-29 20:36:38 +000015using namespace llvm;
16
17static 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
Alex Lorenz86814bf2019-07-24 22:59:20 +000035FileCollector::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);
Jonas Devlieghere46575172019-01-29 20:36:38 +000038}
39
40bool 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
64void 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
71void FileCollector::AddFileImpl(StringRef src_path) {
Jonas Devlieghere46575172019-01-29 20:36:38 +000072 // 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
Alex Lorenz86814bf2019-07-24 22:59:20 +000093 SmallString<256> dst_path = StringRef(m_root);
Jonas Devlieghere46575172019-01-29 20:36:38 +000094 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
Jonas Devlieghere27789ce2019-06-26 18:14:31 +0000104/// Set the access and modification time for the given file from the given
105/// status object.
106static std::error_code
107CopyAccessAndModificationTime(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
Jonas Devlieghere46575172019-01-29 20:36:38 +0000125std::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 }
Jonas Devlieghere27789ce2019-06-26 18:14:31 +0000148
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);
Jonas Devlieghere46575172019-01-29 20:36:38 +0000157 }
158 return {};
159}
160
Alex Lorenz86814bf2019-07-24 22:59:20 +0000161std::error_code FileCollector::WriteMapping(StringRef mapping_file) {
Jonas Devlieghere46575172019-01-29 20:36:38 +0000162 std::lock_guard<std::mutex> lock(m_mutex);
163
Alex Lorenz86814bf2019-07-24 22:59:20 +0000164 StringRef root = m_overlay_root;
Jonas Devliegherec470ac52019-06-18 16:20:17 +0000165 m_vfs_writer.setOverlayDir(root);
Jonas Devlieghere46575172019-01-29 20:36:38 +0000166 m_vfs_writer.setCaseSensitivity(IsCaseSensitivePath(root));
167 m_vfs_writer.setUseExternalNames(false);
168
169 std::error_code ec;
Alex Lorenz86814bf2019-07-24 22:59:20 +0000170 raw_fd_ostream os(mapping_file, ec, sys::fs::F_Text);
Jonas Devlieghere46575172019-01-29 20:36:38 +0000171 if (ec)
172 return ec;
173
174 m_vfs_writer.write(os);
175
176 return {};
177}