471 lines · cpp
1//===-- FileSystem.cpp ----------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Host/FileSystem.h"10 11#include "lldb/Utility/DataBufferLLVM.h"12 13#include "llvm/Support/Errc.h"14#include "llvm/Support/Errno.h"15#include "llvm/Support/Error.h"16#include "llvm/Support/FileSystem.h"17#include "llvm/Support/Path.h"18#include "llvm/Support/Program.h"19#include "llvm/Support/Threading.h"20 21#include <cerrno>22#include <climits>23#include <cstdarg>24#include <cstdio>25#include <fcntl.h>26 27#ifdef _WIN3228#include "lldb/Host/windows/windows.h"29#else30#include <sys/ioctl.h>31#include <sys/stat.h>32#include <termios.h>33#include <unistd.h>34#endif35 36#include <algorithm>37#include <fstream>38#include <memory>39#include <optional>40#include <vector>41 42using namespace lldb;43using namespace lldb_private;44using namespace llvm;45 46FileSystem &FileSystem::Instance() { return *InstanceImpl(); }47 48void FileSystem::Terminate() {49 lldbassert(InstanceImpl() && "Already terminated.");50 InstanceImpl().reset();51}52 53std::optional<FileSystem> &FileSystem::InstanceImpl() {54 static std::optional<FileSystem> g_fs;55 return g_fs;56}57 58vfs::directory_iterator FileSystem::DirBegin(const FileSpec &file_spec,59 std::error_code &ec) {60 if (!file_spec) {61 ec = std::error_code(static_cast<int>(errc::no_such_file_or_directory),62 std::system_category());63 return {};64 }65 return DirBegin(file_spec.GetPath(), ec);66}67 68vfs::directory_iterator FileSystem::DirBegin(const Twine &dir,69 std::error_code &ec) {70 return m_fs->dir_begin(dir, ec);71}72 73llvm::ErrorOr<vfs::Status>74FileSystem::GetStatus(const FileSpec &file_spec) const {75 if (!file_spec)76 return std::error_code(static_cast<int>(errc::no_such_file_or_directory),77 std::system_category());78 return GetStatus(file_spec.GetPath());79}80 81llvm::ErrorOr<vfs::Status> FileSystem::GetStatus(const Twine &path) const {82 return m_fs->status(path);83}84 85sys::TimePoint<>86FileSystem::GetModificationTime(const FileSpec &file_spec) const {87 if (!file_spec)88 return sys::TimePoint<>();89 return GetModificationTime(file_spec.GetPath());90}91 92sys::TimePoint<> FileSystem::GetModificationTime(const Twine &path) const {93 ErrorOr<vfs::Status> status = m_fs->status(path);94 if (!status)95 return sys::TimePoint<>();96 return status->getLastModificationTime();97}98 99uint64_t FileSystem::GetByteSize(const FileSpec &file_spec) const {100 if (!file_spec)101 return 0;102 return GetByteSize(file_spec.GetPath());103}104 105uint64_t FileSystem::GetByteSize(const Twine &path) const {106 ErrorOr<vfs::Status> status = m_fs->status(path);107 if (!status)108 return 0;109 return status->getSize();110}111 112uint32_t FileSystem::GetPermissions(const FileSpec &file_spec) const {113 return GetPermissions(file_spec.GetPath());114}115 116uint32_t FileSystem::GetPermissions(const FileSpec &file_spec,117 std::error_code &ec) const {118 if (!file_spec)119 return sys::fs::perms::perms_not_known;120 return GetPermissions(file_spec.GetPath(), ec);121}122 123uint32_t FileSystem::GetPermissions(const Twine &path) const {124 std::error_code ec;125 return GetPermissions(path, ec);126}127 128uint32_t FileSystem::GetPermissions(const Twine &path,129 std::error_code &ec) const {130 ErrorOr<vfs::Status> status = m_fs->status(path);131 if (!status) {132 ec = status.getError();133 return sys::fs::perms::perms_not_known;134 }135 return status->getPermissions();136}137 138bool FileSystem::Exists(const Twine &path) const { return m_fs->exists(path); }139 140bool FileSystem::Exists(const FileSpec &file_spec) const {141 return file_spec && Exists(file_spec.GetPath());142}143 144bool FileSystem::Readable(const Twine &path) const {145 return GetPermissions(path) & sys::fs::perms::all_read;146}147 148bool FileSystem::Readable(const FileSpec &file_spec) const {149 return file_spec && Readable(file_spec.GetPath());150}151 152bool FileSystem::IsDirectory(const Twine &path) const {153 ErrorOr<vfs::Status> status = m_fs->status(path);154 if (!status)155 return false;156 return status->isDirectory();157}158 159bool FileSystem::IsDirectory(const FileSpec &file_spec) const {160 return file_spec && IsDirectory(file_spec.GetPath());161}162 163bool FileSystem::IsLocal(const Twine &path) const {164 bool b = false;165 m_fs->isLocal(path, b);166 return b;167}168 169bool FileSystem::IsLocal(const FileSpec &file_spec) const {170 return file_spec && IsLocal(file_spec.GetPath());171}172 173void FileSystem::EnumerateDirectory(Twine path, bool find_directories,174 bool find_files, bool find_other,175 EnumerateDirectoryCallbackType callback,176 void *callback_baton) {177 std::error_code EC;178 vfs::recursive_directory_iterator Iter(*m_fs, path, EC);179 vfs::recursive_directory_iterator End;180 for (; Iter != End && !EC; Iter.increment(EC)) {181 const auto &Item = *Iter;182 ErrorOr<vfs::Status> Status = m_fs->status(Item.path());183 if (!Status)184 continue;185 if (!find_files && Status->isRegularFile())186 continue;187 if (!find_directories && Status->isDirectory())188 continue;189 if (!find_other && Status->isOther())190 continue;191 192 auto Result = callback(callback_baton, Status->getType(), Item.path());193 if (Result == eEnumerateDirectoryResultQuit)194 return;195 if (Result == eEnumerateDirectoryResultNext) {196 // Default behavior is to recurse. Opt out if the callback doesn't want197 // this behavior.198 Iter.no_push();199 }200 }201}202 203std::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const {204 return m_fs->makeAbsolute(path);205}206 207std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const {208 SmallString<128> path;209 file_spec.GetPath(path, false);210 211 auto EC = MakeAbsolute(path);212 if (EC)213 return EC;214 215 FileSpec new_file_spec(path, file_spec.GetPathStyle());216 file_spec = new_file_spec;217 return {};218}219 220std::error_code FileSystem::GetRealPath(const Twine &path,221 SmallVectorImpl<char> &output) const {222 return m_fs->getRealPath(path, output);223}224 225void FileSystem::Resolve(SmallVectorImpl<char> &path) {226 if (path.empty())227 return;228 229 // Resolve tilde in path.230 SmallString<128> resolved(path.begin(), path.end());231 assert(m_tilde_resolver && "must initialize tilde resolver in constructor");232 m_tilde_resolver->ResolveFullPath(llvm::StringRef(path.begin(), path.size()),233 resolved);234 235 // Try making the path absolute if it exists.236 SmallString<128> absolute(resolved.begin(), resolved.end());237 MakeAbsolute(absolute);238 239 path.clear();240 if (Exists(absolute)) {241 path.append(absolute.begin(), absolute.end());242 } else {243 path.append(resolved.begin(), resolved.end());244 }245}246 247void FileSystem::Resolve(FileSpec &file_spec) {248 if (!file_spec)249 return;250 251 // Extract path from the FileSpec.252 SmallString<128> path;253 file_spec.GetPath(path);254 255 // Resolve the path.256 Resolve(path);257 258 // Update the FileSpec with the resolved path.259 if (file_spec.GetFilename().IsEmpty())260 file_spec.SetDirectory(path);261 else262 file_spec.SetPath(path);263}264 265template <typename T>266static std::unique_ptr<T> GetMemoryBuffer(const llvm::Twine &path,267 uint64_t size, uint64_t offset,268 bool is_volatile) {269 std::unique_ptr<T> buffer;270 if (size == 0) {271 auto buffer_or_error = T::getFile(path, is_volatile);272 if (!buffer_or_error)273 return nullptr;274 buffer = std::move(*buffer_or_error);275 } else {276 auto buffer_or_error = T::getFileSlice(path, size, offset, is_volatile);277 if (!buffer_or_error)278 return nullptr;279 buffer = std::move(*buffer_or_error);280 }281 return buffer;282}283 284std::shared_ptr<WritableDataBuffer>285FileSystem::CreateWritableDataBuffer(const llvm::Twine &path, uint64_t size,286 uint64_t offset) {287 const bool is_volatile = !IsLocal(path);288 auto buffer = GetMemoryBuffer<llvm::WritableMemoryBuffer>(path, size, offset,289 is_volatile);290 if (!buffer)291 return {};292 return std::make_shared<WritableDataBufferLLVM>(std::move(buffer));293}294 295std::shared_ptr<DataBuffer>296FileSystem::CreateDataBuffer(const llvm::Twine &path, uint64_t size,297 uint64_t offset) {298 const bool is_volatile = !IsLocal(path);299 auto buffer =300 GetMemoryBuffer<llvm::MemoryBuffer>(path, size, offset, is_volatile);301 if (!buffer)302 return {};303 return std::make_shared<DataBufferLLVM>(std::move(buffer));304}305 306std::shared_ptr<WritableDataBuffer>307FileSystem::CreateWritableDataBuffer(const FileSpec &file_spec, uint64_t size,308 uint64_t offset) {309 return CreateWritableDataBuffer(file_spec.GetPath(), size, offset);310}311 312std::shared_ptr<DataBuffer>313FileSystem::CreateDataBuffer(const FileSpec &file_spec, uint64_t size,314 uint64_t offset) {315 return CreateDataBuffer(file_spec.GetPath(), size, offset);316}317 318bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) {319 // If the directory is set there's nothing to do.320 ConstString directory = file_spec.GetDirectory();321 if (directory)322 return false;323 324 // We cannot look for a file if there's no file name.325 ConstString filename = file_spec.GetFilename();326 if (!filename)327 return false;328 329 // Search for the file on the host.330 const std::string filename_str(filename.GetCString());331 llvm::ErrorOr<std::string> error_or_path =332 llvm::sys::findProgramByName(filename_str);333 if (!error_or_path)334 return false;335 336 // findProgramByName returns "." if it can't find the file.337 llvm::StringRef path = *error_or_path;338 llvm::StringRef parent = llvm::sys::path::parent_path(path);339 if (parent.empty() || parent == ".")340 return false;341 342 // Make sure that the result exists.343 FileSpec result(*error_or_path);344 if (!Exists(result))345 return false;346 347 file_spec = result;348 return true;349}350 351bool FileSystem::GetHomeDirectory(SmallVectorImpl<char> &path) const {352 if (!m_home_directory.empty()) {353 path.assign(m_home_directory.begin(), m_home_directory.end());354 return true;355 }356 return llvm::sys::path::home_directory(path);357}358 359bool FileSystem::GetHomeDirectory(FileSpec &file_spec) const {360 SmallString<128> home_dir;361 if (!GetHomeDirectory(home_dir))362 return false;363 file_spec.SetPath(home_dir);364 return true;365}366 367static int OpenWithFS(const FileSystem &fs, const char *path, int flags,368 int mode) {369 return const_cast<FileSystem &>(fs).Open(path, flags, mode);370}371 372static int GetOpenFlags(File::OpenOptions options) {373 int open_flags = 0;374 File::OpenOptions rw =375 options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |376 File::eOpenOptionReadWrite);377 if (rw == File::eOpenOptionWriteOnly || rw == File::eOpenOptionReadWrite) {378 if (rw == File::eOpenOptionReadWrite)379 open_flags |= O_RDWR;380 else381 open_flags |= O_WRONLY;382 383 if (options & File::eOpenOptionAppend)384 open_flags |= O_APPEND;385 386 if (options & File::eOpenOptionTruncate)387 open_flags |= O_TRUNC;388 389 if (options & File::eOpenOptionCanCreate)390 open_flags |= O_CREAT;391 392 if (options & File::eOpenOptionCanCreateNewOnly)393 open_flags |= O_CREAT | O_EXCL;394 } else if (rw == File::eOpenOptionReadOnly) {395 open_flags |= O_RDONLY;396 397#ifndef _WIN32398 if (options & File::eOpenOptionDontFollowSymlinks)399 open_flags |= O_NOFOLLOW;400#endif401 }402 403#ifndef _WIN32404 if (options & File::eOpenOptionNonBlocking)405 open_flags |= O_NONBLOCK;406 if (options & File::eOpenOptionCloseOnExec)407 open_flags |= O_CLOEXEC;408#else409 open_flags |= O_BINARY;410#endif411 412 return open_flags;413}414 415static mode_t GetOpenMode(uint32_t permissions) {416 mode_t mode = 0;417 if (permissions & lldb::eFilePermissionsUserRead)418 mode |= S_IRUSR;419 if (permissions & lldb::eFilePermissionsUserWrite)420 mode |= S_IWUSR;421 if (permissions & lldb::eFilePermissionsUserExecute)422 mode |= S_IXUSR;423 if (permissions & lldb::eFilePermissionsGroupRead)424 mode |= S_IRGRP;425 if (permissions & lldb::eFilePermissionsGroupWrite)426 mode |= S_IWGRP;427 if (permissions & lldb::eFilePermissionsGroupExecute)428 mode |= S_IXGRP;429 if (permissions & lldb::eFilePermissionsWorldRead)430 mode |= S_IROTH;431 if (permissions & lldb::eFilePermissionsWorldWrite)432 mode |= S_IWOTH;433 if (permissions & lldb::eFilePermissionsWorldExecute)434 mode |= S_IXOTH;435 return mode;436}437 438Expected<FileUP> FileSystem::Open(const FileSpec &file_spec,439 File::OpenOptions options,440 uint32_t permissions, bool should_close_fd) {441 const int open_flags = GetOpenFlags(options);442 const mode_t open_mode =443 (open_flags & O_CREAT) ? GetOpenMode(permissions) : 0;444 445 auto path = file_spec.GetPath();446 447 int descriptor = llvm::sys::RetryAfterSignal(448 -1, OpenWithFS, *this, path.c_str(), open_flags, open_mode);449 450 if (!File::DescriptorIsValid(descriptor))451 return llvm::errorCodeToError(452 std::error_code(errno, std::system_category()));453 454 auto file = std::unique_ptr<File>(455 new NativeFile(descriptor, options, should_close_fd));456 assert(file->IsValid());457 return std::move(file);458}459 460void FileSystem::SetHomeDirectory(std::string home_directory) {461 m_home_directory = std::move(home_directory);462}463 464Status FileSystem::RemoveFile(const FileSpec &file_spec) {465 return RemoveFile(file_spec.GetPath());466}467 468Status FileSystem::RemoveFile(const llvm::Twine &path) {469 return Status(llvm::sys::fs::remove(path));470}471