blob: ed93591922056d454e09942fb27daa9f50f7bede [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
9#include "lldb/Utility/FileCollector.h"
10
11#include "llvm/ADT/SmallString.h"
12#include "llvm/Support/FileSystem.h"
13#include "llvm/Support/Path.h"
Jonas Devlieghere27789ce2019-06-26 18:14:31 +000014#include "llvm/Support/Process.h"
Jonas Devlieghere46575172019-01-29 20:36:38 +000015
16using namespace lldb_private;
17using namespace llvm;
18
19static bool IsCaseSensitivePath(StringRef path) {
20 SmallString<256> tmp_dest = path, upper_dest, real_dest;
21
22 // Remove component traversals, links, etc.
23 if (!sys::fs::real_path(path, tmp_dest))
24 return true; // Current default value in vfs.yaml
25 path = tmp_dest;
26
27 // Change path to all upper case and ask for its real path, if the latter
28 // exists and is equal to path, it's not case sensitive. Default to case
29 // sensitive in the absence of real_path, since this is the YAMLVFSWriter
30 // default.
31 upper_dest = path.upper();
32 if (sys::fs::real_path(upper_dest, real_dest) && path.equals(real_dest))
33 return false;
34 return true;
35}
36
Jonas Devliegherec470ac52019-06-18 16:20:17 +000037FileCollector::FileCollector(const FileSpec &root, const FileSpec &overlay_root)
38 : m_root(root), m_overlay_root(overlay_root) {
Jonas Devlieghere46575172019-01-29 20:36:38 +000039 sys::fs::create_directories(m_root.GetPath(), true);
40}
41
42bool FileCollector::GetRealPath(StringRef src_path,
43 SmallVectorImpl<char> &result) {
44 SmallString<256> real_path;
45 StringRef FileName = sys::path::filename(src_path);
46 std::string directory = sys::path::parent_path(src_path).str();
47 auto dir_with_symlink = m_symlink_map.find(directory);
48
49 // Use real_path to fix any symbolic link component present in a path.
50 // Computing the real path is expensive, cache the search through the
51 // parent path directory.
52 if (dir_with_symlink == m_symlink_map.end()) {
53 auto ec = sys::fs::real_path(directory, real_path);
54 if (ec)
55 return false;
56 m_symlink_map[directory] = real_path.str();
57 } else {
58 real_path = dir_with_symlink->second;
59 }
60
61 sys::path::append(real_path, FileName);
62 result.swap(real_path);
63 return true;
64}
65
66void FileCollector::AddFile(const Twine &file) {
67 std::lock_guard<std::mutex> lock(m_mutex);
68 std::string file_str = file.str();
69 if (MarkAsSeen(file_str))
70 AddFileImpl(file_str);
71}
72
73void FileCollector::AddFileImpl(StringRef src_path) {
74 std::string root = m_root.GetPath();
75
76 // We need an absolute src path to append to the root.
77 SmallString<256> absolute_src = src_path;
78 sys::fs::make_absolute(absolute_src);
79
80 // Canonicalize src to a native path to avoid mixed separator styles.
81 sys::path::native(absolute_src);
82
83 // Remove redundant leading "./" pieces and consecutive separators.
84 absolute_src = sys::path::remove_leading_dotslash(absolute_src);
85
86 // Canonicalize the source path by removing "..", "." components.
87 SmallString<256> virtual_path = absolute_src;
88 sys::path::remove_dots(virtual_path, /*remove_dot_dot=*/true);
89
90 // If a ".." component is present after a symlink component, remove_dots may
91 // lead to the wrong real destination path. Let the source be canonicalized
92 // like that but make sure we always use the real path for the destination.
93 SmallString<256> copy_from;
94 if (!GetRealPath(absolute_src, copy_from))
95 copy_from = virtual_path;
96
97 SmallString<256> dst_path = StringRef(root);
98 sys::path::append(dst_path, sys::path::relative_path(copy_from));
99
100 // Always map a canonical src path to its real path into the YAML, by doing
101 // this we map different virtual src paths to the same entry in the VFS
102 // overlay, which is a way to emulate symlink inside the VFS; this is also
103 // needed for correctness, not doing that can lead to module redefinition
104 // errors.
105 AddFileToMapping(virtual_path, dst_path);
106}
107
Jonas Devlieghere27789ce2019-06-26 18:14:31 +0000108/// Set the access and modification time for the given file from the given
109/// status object.
110static std::error_code
111CopyAccessAndModificationTime(StringRef filename,
112 const sys::fs::file_status &stat) {
113 int fd;
114
115 if (auto ec =
116 sys::fs::openFileForWrite(filename, fd, sys::fs::CD_OpenExisting))
117 return ec;
118
119 if (auto ec = sys::fs::setLastAccessAndModificationTime(
120 fd, stat.getLastAccessedTime(), stat.getLastModificationTime()))
121 return ec;
122
123 if (auto ec = sys::Process::SafelyCloseFileDescriptor(fd))
124 return ec;
125
126 return {};
127}
128
Jonas Devlieghere46575172019-01-29 20:36:38 +0000129std::error_code FileCollector::CopyFiles(bool stop_on_error) {
130 for (auto &entry : m_vfs_writer.getMappings()) {
131 // Create directory tree.
132 if (std::error_code ec =
133 sys::fs::create_directories(sys::path::parent_path(entry.RPath),
134 /*IgnoreExisting=*/true)) {
135 if (stop_on_error)
136 return ec;
137 }
138
139 // Copy file over.
140 if (std::error_code ec = sys::fs::copy_file(entry.VPath, entry.RPath)) {
141 if (stop_on_error)
142 return ec;
143 }
144
145 // Copy over permissions.
146 if (auto perms = sys::fs::getPermissions(entry.VPath)) {
147 if (std::error_code ec = sys::fs::setPermissions(entry.RPath, *perms)) {
148 if (stop_on_error)
149 return ec;
150 }
151 }
Jonas Devlieghere27789ce2019-06-26 18:14:31 +0000152
153 // Copy over modification time.
154 sys::fs::file_status stat;
155 if (std::error_code ec = sys::fs::status(entry.VPath, stat)) {
156 if (stop_on_error)
157 return ec;
158 continue;
159 }
160 CopyAccessAndModificationTime(entry.RPath, stat);
Jonas Devlieghere46575172019-01-29 20:36:38 +0000161 }
162 return {};
163}
164
165std::error_code FileCollector::WriteMapping(const FileSpec &mapping_file) {
166 std::lock_guard<std::mutex> lock(m_mutex);
167
Pavel Labath73a28f02019-06-19 08:09:56 +0000168 std::string root = m_overlay_root.GetPath();
Jonas Devliegherec470ac52019-06-18 16:20:17 +0000169
170 m_vfs_writer.setOverlayDir(root);
Jonas Devlieghere46575172019-01-29 20:36:38 +0000171 m_vfs_writer.setCaseSensitivity(IsCaseSensitivePath(root));
172 m_vfs_writer.setUseExternalNames(false);
173
174 std::error_code ec;
175 raw_fd_ostream os(mapping_file.GetPath(), ec, sys::fs::F_Text);
176 if (ec)
177 return ec;
178
179 m_vfs_writer.write(os);
180
181 return {};
182}