1 //===-- FileSystem.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 "lldb/Host/FileSystem.h"
11 #include "lldb/Utility/LLDBAssert.h"
12 #include "lldb/Utility/TildeExpressionResolver.h"
14 #include "llvm/Support/Errc.h"
15 #include "llvm/Support/Errno.h"
16 #include "llvm/Support/Error.h"
17 #include "llvm/Support/FileSystem.h"
18 #include "llvm/Support/Path.h"
19 #include "llvm/Support/Program.h"
20 #include "llvm/Support/Threading.h"
29 #include "lldb/Host/windows/windows.h"
31 #include <sys/ioctl.h>
42 using namespace lldb_private;
45 FileSystem &FileSystem::Instance() { return *InstanceImpl(); }
47 void FileSystem::Initialize() {
48 lldbassert(!InstanceImpl() && "Already initialized.");
49 InstanceImpl().emplace();
52 void FileSystem::Initialize(FileCollector &collector) {
53 lldbassert(!InstanceImpl() && "Already initialized.");
54 InstanceImpl().emplace(collector);
57 llvm::Error FileSystem::Initialize(const FileSpec &mapping) {
58 lldbassert(!InstanceImpl() && "Already initialized.");
60 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer =
61 llvm::vfs::getRealFileSystem()->getBufferForFile(mapping.GetPath());
64 return llvm::errorCodeToError(buffer.getError());
66 InstanceImpl().emplace(llvm::vfs::getVFSFromYAML(std::move(buffer.get()),
67 nullptr, mapping.GetPath()),
70 return llvm::Error::success();
73 void FileSystem::Initialize(IntrusiveRefCntPtr<vfs::FileSystem> fs) {
74 lldbassert(!InstanceImpl() && "Already initialized.");
75 InstanceImpl().emplace(fs);
78 void FileSystem::Terminate() {
79 lldbassert(InstanceImpl() && "Already terminated.");
80 InstanceImpl().reset();
83 Optional<FileSystem> &FileSystem::InstanceImpl() {
84 static Optional<FileSystem> g_fs;
88 vfs::directory_iterator FileSystem::DirBegin(const FileSpec &file_spec,
89 std::error_code &ec) {
90 return DirBegin(file_spec.GetPath(), ec);
93 vfs::directory_iterator FileSystem::DirBegin(const Twine &dir,
94 std::error_code &ec) {
95 return m_fs->dir_begin(dir, ec);
98 llvm::ErrorOr<vfs::Status>
99 FileSystem::GetStatus(const FileSpec &file_spec) const {
100 return GetStatus(file_spec.GetPath());
103 llvm::ErrorOr<vfs::Status> FileSystem::GetStatus(const Twine &path) const {
104 return m_fs->status(path);
108 FileSystem::GetModificationTime(const FileSpec &file_spec) const {
109 return GetModificationTime(file_spec.GetPath());
112 sys::TimePoint<> FileSystem::GetModificationTime(const Twine &path) const {
113 ErrorOr<vfs::Status> status = m_fs->status(path);
115 return sys::TimePoint<>();
116 return status->getLastModificationTime();
119 uint64_t FileSystem::GetByteSize(const FileSpec &file_spec) const {
120 return GetByteSize(file_spec.GetPath());
123 uint64_t FileSystem::GetByteSize(const Twine &path) const {
124 ErrorOr<vfs::Status> status = m_fs->status(path);
127 return status->getSize();
130 uint32_t FileSystem::GetPermissions(const FileSpec &file_spec) const {
131 return GetPermissions(file_spec.GetPath());
134 uint32_t FileSystem::GetPermissions(const FileSpec &file_spec,
135 std::error_code &ec) const {
136 return GetPermissions(file_spec.GetPath(), ec);
139 uint32_t FileSystem::GetPermissions(const Twine &path) const {
141 return GetPermissions(path, ec);
144 uint32_t FileSystem::GetPermissions(const Twine &path,
145 std::error_code &ec) const {
146 ErrorOr<vfs::Status> status = m_fs->status(path);
148 ec = status.getError();
149 return sys::fs::perms::perms_not_known;
151 return status->getPermissions();
154 bool FileSystem::Exists(const Twine &path) const { return m_fs->exists(path); }
156 bool FileSystem::Exists(const FileSpec &file_spec) const {
157 return Exists(file_spec.GetPath());
160 bool FileSystem::Readable(const Twine &path) const {
161 return GetPermissions(path) & sys::fs::perms::all_read;
164 bool FileSystem::Readable(const FileSpec &file_spec) const {
165 return Readable(file_spec.GetPath());
168 bool FileSystem::IsDirectory(const Twine &path) const {
169 ErrorOr<vfs::Status> status = m_fs->status(path);
172 return status->isDirectory();
175 bool FileSystem::IsDirectory(const FileSpec &file_spec) const {
176 return IsDirectory(file_spec.GetPath());
179 bool FileSystem::IsLocal(const Twine &path) const {
181 m_fs->isLocal(path, b);
185 bool FileSystem::IsLocal(const FileSpec &file_spec) const {
186 return IsLocal(file_spec.GetPath());
189 void FileSystem::EnumerateDirectory(Twine path, bool find_directories,
190 bool find_files, bool find_other,
191 EnumerateDirectoryCallbackType callback,
192 void *callback_baton) {
194 vfs::recursive_directory_iterator Iter(*m_fs, path, EC);
195 vfs::recursive_directory_iterator End;
196 for (; Iter != End && !EC; Iter.increment(EC)) {
197 const auto &Item = *Iter;
198 ErrorOr<vfs::Status> Status = m_fs->status(Item.path());
201 if (!find_files && Status->isRegularFile())
203 if (!find_directories && Status->isDirectory())
205 if (!find_other && Status->isOther())
208 auto Result = callback(callback_baton, Status->getType(), Item.path());
209 if (Result == eEnumerateDirectoryResultQuit)
211 if (Result == eEnumerateDirectoryResultNext) {
212 // Default behavior is to recurse. Opt out if the callback doesn't want
219 std::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const {
220 return m_fs->makeAbsolute(path);
223 std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const {
224 SmallString<128> path;
225 file_spec.GetPath(path, false);
227 auto EC = MakeAbsolute(path);
231 FileSpec new_file_spec(path, file_spec.GetPathStyle());
232 file_spec = new_file_spec;
236 std::error_code FileSystem::GetRealPath(const Twine &path,
237 SmallVectorImpl<char> &output) const {
238 return m_fs->getRealPath(path, output);
241 void FileSystem::Resolve(SmallVectorImpl<char> &path) {
245 // Resolve tilde in path.
246 SmallString<128> resolved(path.begin(), path.end());
247 StandardTildeExpressionResolver Resolver;
248 Resolver.ResolveFullPath(llvm::StringRef(path.begin(), path.size()),
251 // Try making the path absolute if it exists.
252 SmallString<128> absolute(resolved.begin(), resolved.end());
253 MakeAbsolute(absolute);
256 if (Exists(absolute)) {
257 path.append(absolute.begin(), absolute.end());
259 path.append(resolved.begin(), resolved.end());
263 void FileSystem::Resolve(FileSpec &file_spec) {
264 // Extract path from the FileSpec.
265 SmallString<128> path;
266 file_spec.GetPath(path);
271 // Update the FileSpec with the resolved path.
272 if (file_spec.GetFilename().IsEmpty())
273 file_spec.GetDirectory().SetString(path);
275 file_spec.SetPath(path);
276 file_spec.SetIsResolved(true);
279 std::shared_ptr<DataBufferLLVM>
280 FileSystem::CreateDataBuffer(const llvm::Twine &path, uint64_t size,
283 m_collector->AddFile(path);
285 const bool is_volatile = !IsLocal(path);
286 const ErrorOr<std::string> external_path = GetExternalPath(path);
291 std::unique_ptr<llvm::WritableMemoryBuffer> buffer;
293 auto buffer_or_error =
294 llvm::WritableMemoryBuffer::getFile(*external_path, -1, is_volatile);
295 if (!buffer_or_error)
297 buffer = std::move(*buffer_or_error);
299 auto buffer_or_error = llvm::WritableMemoryBuffer::getFileSlice(
300 *external_path, size, offset, is_volatile);
301 if (!buffer_or_error)
303 buffer = std::move(*buffer_or_error);
305 return std::shared_ptr<DataBufferLLVM>(new DataBufferLLVM(std::move(buffer)));
308 std::shared_ptr<DataBufferLLVM>
309 FileSystem::CreateDataBuffer(const FileSpec &file_spec, uint64_t size,
311 return CreateDataBuffer(file_spec.GetPath(), size, offset);
314 bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) {
315 // If the directory is set there's nothing to do.
316 ConstString directory = file_spec.GetDirectory();
320 // We cannot look for a file if there's no file name.
321 ConstString filename = file_spec.GetFilename();
325 // Search for the file on the host.
326 const std::string filename_str(filename.GetCString());
327 llvm::ErrorOr<std::string> error_or_path =
328 llvm::sys::findProgramByName(filename_str);
332 // findProgramByName returns "." if it can't find the file.
333 llvm::StringRef path = *error_or_path;
334 llvm::StringRef parent = llvm::sys::path::parent_path(path);
335 if (parent.empty() || parent == ".")
338 // Make sure that the result exists.
339 FileSpec result(*error_or_path);
347 static int OpenWithFS(const FileSystem &fs, const char *path, int flags,
349 return const_cast<FileSystem &>(fs).Open(path, flags, mode);
352 static int GetOpenFlags(uint32_t options) {
353 const bool read = options & File::eOpenOptionRead;
354 const bool write = options & File::eOpenOptionWrite;
359 open_flags |= O_RDWR;
361 open_flags |= O_WRONLY;
363 if (options & File::eOpenOptionAppend)
364 open_flags |= O_APPEND;
366 if (options & File::eOpenOptionTruncate)
367 open_flags |= O_TRUNC;
369 if (options & File::eOpenOptionCanCreate)
370 open_flags |= O_CREAT;
372 if (options & File::eOpenOptionCanCreateNewOnly)
373 open_flags |= O_CREAT | O_EXCL;
375 open_flags |= O_RDONLY;
378 if (options & File::eOpenOptionDontFollowSymlinks)
379 open_flags |= O_NOFOLLOW;
384 if (options & File::eOpenOptionNonBlocking)
385 open_flags |= O_NONBLOCK;
386 if (options & File::eOpenOptionCloseOnExec)
387 open_flags |= O_CLOEXEC;
389 open_flags |= O_BINARY;
395 static mode_t GetOpenMode(uint32_t permissions) {
397 if (permissions & lldb::eFilePermissionsUserRead)
399 if (permissions & lldb::eFilePermissionsUserWrite)
401 if (permissions & lldb::eFilePermissionsUserExecute)
403 if (permissions & lldb::eFilePermissionsGroupRead)
405 if (permissions & lldb::eFilePermissionsGroupWrite)
407 if (permissions & lldb::eFilePermissionsGroupExecute)
409 if (permissions & lldb::eFilePermissionsWorldRead)
411 if (permissions & lldb::eFilePermissionsWorldWrite)
413 if (permissions & lldb::eFilePermissionsWorldExecute)
418 Status FileSystem::Open(File &File, const FileSpec &file_spec, uint32_t options,
419 uint32_t permissions, bool should_close_fd) {
421 m_collector->AddFile(file_spec);
426 const int open_flags = GetOpenFlags(options);
427 const mode_t open_mode =
428 (open_flags & O_CREAT) ? GetOpenMode(permissions) : 0;
430 auto path = GetExternalPath(file_spec);
432 return Status(path.getError());
434 int descriptor = llvm::sys::RetryAfterSignal(
435 -1, OpenWithFS, *this, path->c_str(), open_flags, open_mode);
438 if (!File::DescriptorIsValid(descriptor)) {
439 File.SetDescriptor(descriptor, false);
440 error.SetErrorToErrno();
442 File.SetDescriptor(descriptor, should_close_fd);
443 File.SetOptions(options);
448 ErrorOr<std::string> FileSystem::GetExternalPath(const llvm::Twine &path) {
452 // If VFS mapped we know the underlying FS is a RedirectingFileSystem.
453 ErrorOr<vfs::RedirectingFileSystem::Entry *> E =
454 static_cast<vfs::RedirectingFileSystem &>(*m_fs).lookupPath(path);
456 if (E.getError() == llvm::errc::no_such_file_or_directory) {
462 auto *F = dyn_cast<vfs::RedirectingFileSystem::RedirectingFileEntry>(*E);
464 return make_error_code(llvm::errc::not_supported);
466 return F->getExternalContentsPath().str();
469 ErrorOr<std::string> FileSystem::GetExternalPath(const FileSpec &file_spec) {
470 return GetExternalPath(file_spec.GetPath());