blob: 1758ad8c82c90a00ff0141ffb3ea0f83da511fbc [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"
14
15using namespace lldb_private;
16using namespace llvm;
17
18static bool IsCaseSensitivePath(StringRef path) {
19 SmallString<256> tmp_dest = path, upper_dest, real_dest;
20
21 // Remove component traversals, links, etc.
22 if (!sys::fs::real_path(path, tmp_dest))
23 return true; // Current default value in vfs.yaml
24 path = tmp_dest;
25
26 // Change path to all upper case and ask for its real path, if the latter
27 // exists and is equal to path, it's not case sensitive. Default to case
28 // sensitive in the absence of real_path, since this is the YAMLVFSWriter
29 // default.
30 upper_dest = path.upper();
31 if (sys::fs::real_path(upper_dest, real_dest) && path.equals(real_dest))
32 return false;
33 return true;
34}
35
36FileCollector::FileCollector(const FileSpec &root) : m_root(root) {
37 sys::fs::create_directories(m_root.GetPath(), true);
38}
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) {
72 std::string root = m_root.GetPath();
73
74 // We need an absolute src path to append to the root.
75 SmallString<256> absolute_src = src_path;
76 sys::fs::make_absolute(absolute_src);
77
78 // Canonicalize src to a native path to avoid mixed separator styles.
79 sys::path::native(absolute_src);
80
81 // Remove redundant leading "./" pieces and consecutive separators.
82 absolute_src = sys::path::remove_leading_dotslash(absolute_src);
83
84 // Canonicalize the source path by removing "..", "." components.
85 SmallString<256> virtual_path = absolute_src;
86 sys::path::remove_dots(virtual_path, /*remove_dot_dot=*/true);
87
88 // If a ".." component is present after a symlink component, remove_dots may
89 // lead to the wrong real destination path. Let the source be canonicalized
90 // like that but make sure we always use the real path for the destination.
91 SmallString<256> copy_from;
92 if (!GetRealPath(absolute_src, copy_from))
93 copy_from = virtual_path;
94
95 SmallString<256> dst_path = StringRef(root);
96 sys::path::append(dst_path, sys::path::relative_path(copy_from));
97
98 // Always map a canonical src path to its real path into the YAML, by doing
99 // this we map different virtual src paths to the same entry in the VFS
100 // overlay, which is a way to emulate symlink inside the VFS; this is also
101 // needed for correctness, not doing that can lead to module redefinition
102 // errors.
103 AddFileToMapping(virtual_path, dst_path);
104}
105
106std::error_code FileCollector::CopyFiles(bool stop_on_error) {
107 for (auto &entry : m_vfs_writer.getMappings()) {
108 // Create directory tree.
109 if (std::error_code ec =
110 sys::fs::create_directories(sys::path::parent_path(entry.RPath),
111 /*IgnoreExisting=*/true)) {
112 if (stop_on_error)
113 return ec;
114 }
115
116 // Copy file over.
117 if (std::error_code ec = sys::fs::copy_file(entry.VPath, entry.RPath)) {
118 if (stop_on_error)
119 return ec;
120 }
121
122 // Copy over permissions.
123 if (auto perms = sys::fs::getPermissions(entry.VPath)) {
124 if (std::error_code ec = sys::fs::setPermissions(entry.RPath, *perms)) {
125 if (stop_on_error)
126 return ec;
127 }
128 }
129 }
130 return {};
131}
132
133std::error_code FileCollector::WriteMapping(const FileSpec &mapping_file) {
134 std::lock_guard<std::mutex> lock(m_mutex);
135
136 const std::string root = m_root.GetPath();
137 m_vfs_writer.setCaseSensitivity(IsCaseSensitivePath(root));
138 m_vfs_writer.setUseExternalNames(false);
139
140 std::error_code ec;
141 raw_fd_ostream os(mapping_file.GetPath(), ec, sys::fs::F_Text);
142 if (ec)
143 return ec;
144
145 m_vfs_writer.write(os);
146
147 return {};
148}