1 //===-- FileCollector.cpp ---------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "llvm/Support/FileCollector.h"
10 #include "llvm/ADT/SmallString.h"
11 #include "llvm/ADT/Twine.h"
12 #include "llvm/Support/FileSystem.h"
13 #include "llvm/Support/Path.h"
14 #include "llvm/Support/Process.h"
18 FileCollectorBase::FileCollectorBase() = default;
19 FileCollectorBase::~FileCollectorBase() = default;
21 void FileCollectorBase::addFile(const Twine
&File
) {
22 std::lock_guard
<std::mutex
> lock(Mutex
);
23 std::string FileStr
= File
.str();
24 if (markAsSeen(FileStr
))
28 void FileCollectorBase::addDirectory(const Twine
&Dir
) {
29 assert(sys::fs::is_directory(Dir
));
31 addDirectoryImpl(Dir
, vfs::getRealFileSystem(), EC
);
34 static bool isCaseSensitivePath(StringRef Path
) {
35 SmallString
<256> TmpDest
= Path
, UpperDest
, RealDest
;
37 // Remove component traversals, links, etc.
38 if (sys::fs::real_path(Path
, TmpDest
))
39 return true; // Current default value in vfs.yaml
42 // Change path to all upper case and ask for its real path, if the latter
43 // exists and is equal to path, it's not case sensitive. Default to case
44 // sensitive in the absence of real_path, since this is the YAMLVFSWriter
46 UpperDest
= Path
.upper();
47 if (!sys::fs::real_path(UpperDest
, RealDest
) && Path
.equals(RealDest
))
52 FileCollector::FileCollector(std::string Root
, std::string OverlayRoot
)
53 : Root(Root
), OverlayRoot(OverlayRoot
) {
54 assert(sys::path::is_absolute(Root
) && "Root not absolute");
55 assert(sys::path::is_absolute(OverlayRoot
) && "OverlayRoot not absolute");
58 void FileCollector::PathCanonicalizer::updateWithRealPath(
59 SmallVectorImpl
<char> &Path
) {
60 StringRef
SrcPath(Path
.begin(), Path
.size());
61 StringRef Filename
= sys::path::filename(SrcPath
);
62 StringRef Directory
= sys::path::parent_path(SrcPath
);
64 // Use real_path to fix any symbolic link component present in the directory
65 // part of the path, caching the search because computing the real path is
67 SmallString
<256> RealPath
;
68 auto DirWithSymlink
= CachedDirs
.find(Directory
);
69 if (DirWithSymlink
== CachedDirs
.end()) {
70 // FIXME: Should this be a call to FileSystem::getRealpath(), in some
71 // cases? What if there is nothing on disk?
72 if (sys::fs::real_path(Directory
, RealPath
))
74 CachedDirs
[Directory
] = std::string(RealPath
);
76 RealPath
= DirWithSymlink
->second
;
79 // Finish recreating the path by appending the original filename, since we
80 // don't need to resolve symlinks in the filename.
82 // FIXME: If we can cope with this, maybe we can cope without calling
83 // getRealPath() at all when there's no ".." component.
84 sys::path::append(RealPath
, Filename
);
86 // Swap to create the output.
90 /// Make Path absolute.
91 static void makeAbsolute(SmallVectorImpl
<char> &Path
) {
92 // We need an absolute src path to append to the root.
93 sys::fs::make_absolute(Path
);
95 // Canonicalize src to a native path to avoid mixed separator styles.
96 sys::path::native(Path
);
98 // Remove redundant leading "./" pieces and consecutive separators.
99 Path
.erase(Path
.begin(), sys::path::remove_leading_dotslash(
100 StringRef(Path
.begin(), Path
.size()))
104 FileCollector::PathCanonicalizer::PathStorage
105 FileCollector::PathCanonicalizer::canonicalize(StringRef SrcPath
) {
107 Paths
.VirtualPath
= SrcPath
;
108 makeAbsolute(Paths
.VirtualPath
);
110 // If a ".." component is present after a symlink component, remove_dots may
111 // lead to the wrong real destination path. Let the source be canonicalized
112 // like that but make sure we always use the real path for the destination.
113 Paths
.CopyFrom
= Paths
.VirtualPath
;
114 updateWithRealPath(Paths
.CopyFrom
);
116 // Canonicalize the virtual path by removing "..", "." components.
117 sys::path::remove_dots(Paths
.VirtualPath
, /*remove_dot_dot=*/true);
122 void FileCollector::addFileImpl(StringRef SrcPath
) {
123 PathCanonicalizer::PathStorage Paths
= Canonicalizer
.canonicalize(SrcPath
);
125 SmallString
<256> DstPath
= StringRef(Root
);
126 sys::path::append(DstPath
, sys::path::relative_path(Paths
.CopyFrom
));
128 // Always map a canonical src path to its real path into the YAML, by doing
129 // this we map different virtual src paths to the same entry in the VFS
130 // overlay, which is a way to emulate symlink inside the VFS; this is also
131 // needed for correctness, not doing that can lead to module redefinition
133 addFileToMapping(Paths
.VirtualPath
, DstPath
);
136 llvm::vfs::directory_iterator
137 FileCollector::addDirectoryImpl(const llvm::Twine
&Dir
,
138 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
,
139 std::error_code
&EC
) {
140 auto It
= FS
->dir_begin(Dir
, EC
);
144 for (; !EC
&& It
!= llvm::vfs::directory_iterator(); It
.increment(EC
)) {
145 if (It
->type() == sys::fs::file_type::regular_file
||
146 It
->type() == sys::fs::file_type::directory_file
||
147 It
->type() == sys::fs::file_type::symlink_file
) {
153 // Return a new iterator.
154 return FS
->dir_begin(Dir
, EC
);
157 /// Set the access and modification time for the given file from the given
159 static std::error_code
160 copyAccessAndModificationTime(StringRef Filename
,
161 const sys::fs::file_status
&Stat
) {
165 sys::fs::openFileForWrite(Filename
, FD
, sys::fs::CD_OpenExisting
))
168 if (auto EC
= sys::fs::setLastAccessAndModificationTime(
169 FD
, Stat
.getLastAccessedTime(), Stat
.getLastModificationTime()))
172 if (auto EC
= sys::Process::SafelyCloseFileDescriptor(FD
))
178 std::error_code
FileCollector::copyFiles(bool StopOnError
) {
179 auto Err
= sys::fs::create_directories(Root
, /*IgnoreExisting=*/true);
184 std::lock_guard
<std::mutex
> lock(Mutex
);
186 for (auto &entry
: VFSWriter
.getMappings()) {
187 // Get the status of the original file/directory.
188 sys::fs::file_status Stat
;
189 if (std::error_code EC
= sys::fs::status(entry
.VPath
, Stat
)) {
195 // Continue if the file doesn't exist.
196 if (Stat
.type() == sys::fs::file_type::file_not_found
)
199 // Create directory tree.
200 if (std::error_code EC
=
201 sys::fs::create_directories(sys::path::parent_path(entry
.RPath
),
202 /*IgnoreExisting=*/true)) {
207 if (Stat
.type() == sys::fs::file_type::directory_file
) {
208 // Construct a directory when it's just a directory entry.
209 if (std::error_code EC
=
210 sys::fs::create_directories(entry
.RPath
,
211 /*IgnoreExisting=*/true)) {
219 if (std::error_code EC
= sys::fs::copy_file(entry
.VPath
, entry
.RPath
)) {
224 // Copy over permissions.
225 if (auto perms
= sys::fs::getPermissions(entry
.VPath
)) {
226 if (std::error_code EC
= sys::fs::setPermissions(entry
.RPath
, *perms
)) {
232 // Copy over modification time.
233 copyAccessAndModificationTime(entry
.RPath
, Stat
);
238 std::error_code
FileCollector::writeMapping(StringRef MappingFile
) {
239 std::lock_guard
<std::mutex
> lock(Mutex
);
241 VFSWriter
.setOverlayDir(OverlayRoot
);
242 VFSWriter
.setCaseSensitivity(isCaseSensitivePath(OverlayRoot
));
243 VFSWriter
.setUseExternalNames(false);
246 raw_fd_ostream
os(MappingFile
, EC
, sys::fs::OF_TextWithCRLF
);
257 class FileCollectorFileSystem
: public vfs::FileSystem
{
259 explicit FileCollectorFileSystem(IntrusiveRefCntPtr
<vfs::FileSystem
> FS
,
260 std::shared_ptr
<FileCollector
> Collector
)
261 : FS(std::move(FS
)), Collector(std::move(Collector
)) {}
263 llvm::ErrorOr
<llvm::vfs::Status
> status(const Twine
&Path
) override
{
264 auto Result
= FS
->status(Path
);
265 if (Result
&& Result
->exists())
266 Collector
->addFile(Path
);
270 llvm::ErrorOr
<std::unique_ptr
<llvm::vfs::File
>>
271 openFileForRead(const Twine
&Path
) override
{
272 auto Result
= FS
->openFileForRead(Path
);
273 if (Result
&& *Result
)
274 Collector
->addFile(Path
);
278 llvm::vfs::directory_iterator
dir_begin(const llvm::Twine
&Dir
,
279 std::error_code
&EC
) override
{
280 return Collector
->addDirectoryImpl(Dir
, FS
, EC
);
283 std::error_code
getRealPath(const Twine
&Path
,
284 SmallVectorImpl
<char> &Output
) const override
{
285 auto EC
= FS
->getRealPath(Path
, Output
);
287 Collector
->addFile(Path
);
288 if (Output
.size() > 0)
289 Collector
->addFile(Output
);
294 std::error_code
isLocal(const Twine
&Path
, bool &Result
) override
{
295 return FS
->isLocal(Path
, Result
);
298 llvm::ErrorOr
<std::string
> getCurrentWorkingDirectory() const override
{
299 return FS
->getCurrentWorkingDirectory();
302 std::error_code
setCurrentWorkingDirectory(const llvm::Twine
&Path
) override
{
303 return FS
->setCurrentWorkingDirectory(Path
);
307 IntrusiveRefCntPtr
<vfs::FileSystem
> FS
;
308 std::shared_ptr
<FileCollector
> Collector
;
313 IntrusiveRefCntPtr
<vfs::FileSystem
>
314 FileCollector::createCollectorVFS(IntrusiveRefCntPtr
<vfs::FileSystem
> BaseFS
,
315 std::shared_ptr
<FileCollector
> Collector
) {
316 return new FileCollectorFileSystem(std::move(BaseFS
), std::move(Collector
));