Path: blob/main/contrib/llvm-project/lldb/source/Host/common/FileSystem.cpp
39606 views
//===-- FileSystem.cpp ----------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Host/FileSystem.h"910#include "lldb/Utility/DataBufferLLVM.h"1112#include "llvm/Support/Errc.h"13#include "llvm/Support/Errno.h"14#include "llvm/Support/Error.h"15#include "llvm/Support/FileSystem.h"16#include "llvm/Support/Path.h"17#include "llvm/Support/Program.h"18#include "llvm/Support/Threading.h"1920#include <cerrno>21#include <climits>22#include <cstdarg>23#include <cstdio>24#include <fcntl.h>2526#ifdef _WIN3227#include "lldb/Host/windows/windows.h"28#else29#include <sys/ioctl.h>30#include <sys/stat.h>31#include <termios.h>32#include <unistd.h>33#endif3435#include <algorithm>36#include <fstream>37#include <optional>38#include <vector>3940using namespace lldb;41using namespace lldb_private;42using namespace llvm;4344FileSystem &FileSystem::Instance() { return *InstanceImpl(); }4546void FileSystem::Terminate() {47lldbassert(InstanceImpl() && "Already terminated.");48InstanceImpl().reset();49}5051std::optional<FileSystem> &FileSystem::InstanceImpl() {52static std::optional<FileSystem> g_fs;53return g_fs;54}5556vfs::directory_iterator FileSystem::DirBegin(const FileSpec &file_spec,57std::error_code &ec) {58if (!file_spec) {59ec = std::error_code(static_cast<int>(errc::no_such_file_or_directory),60std::system_category());61return {};62}63return DirBegin(file_spec.GetPath(), ec);64}6566vfs::directory_iterator FileSystem::DirBegin(const Twine &dir,67std::error_code &ec) {68return m_fs->dir_begin(dir, ec);69}7071llvm::ErrorOr<vfs::Status>72FileSystem::GetStatus(const FileSpec &file_spec) const {73if (!file_spec)74return std::error_code(static_cast<int>(errc::no_such_file_or_directory),75std::system_category());76return GetStatus(file_spec.GetPath());77}7879llvm::ErrorOr<vfs::Status> FileSystem::GetStatus(const Twine &path) const {80return m_fs->status(path);81}8283sys::TimePoint<>84FileSystem::GetModificationTime(const FileSpec &file_spec) const {85if (!file_spec)86return sys::TimePoint<>();87return GetModificationTime(file_spec.GetPath());88}8990sys::TimePoint<> FileSystem::GetModificationTime(const Twine &path) const {91ErrorOr<vfs::Status> status = m_fs->status(path);92if (!status)93return sys::TimePoint<>();94return status->getLastModificationTime();95}9697uint64_t FileSystem::GetByteSize(const FileSpec &file_spec) const {98if (!file_spec)99return 0;100return GetByteSize(file_spec.GetPath());101}102103uint64_t FileSystem::GetByteSize(const Twine &path) const {104ErrorOr<vfs::Status> status = m_fs->status(path);105if (!status)106return 0;107return status->getSize();108}109110uint32_t FileSystem::GetPermissions(const FileSpec &file_spec) const {111return GetPermissions(file_spec.GetPath());112}113114uint32_t FileSystem::GetPermissions(const FileSpec &file_spec,115std::error_code &ec) const {116if (!file_spec)117return sys::fs::perms::perms_not_known;118return GetPermissions(file_spec.GetPath(), ec);119}120121uint32_t FileSystem::GetPermissions(const Twine &path) const {122std::error_code ec;123return GetPermissions(path, ec);124}125126uint32_t FileSystem::GetPermissions(const Twine &path,127std::error_code &ec) const {128ErrorOr<vfs::Status> status = m_fs->status(path);129if (!status) {130ec = status.getError();131return sys::fs::perms::perms_not_known;132}133return status->getPermissions();134}135136bool FileSystem::Exists(const Twine &path) const { return m_fs->exists(path); }137138bool FileSystem::Exists(const FileSpec &file_spec) const {139return file_spec && Exists(file_spec.GetPath());140}141142bool FileSystem::Readable(const Twine &path) const {143return GetPermissions(path) & sys::fs::perms::all_read;144}145146bool FileSystem::Readable(const FileSpec &file_spec) const {147return file_spec && Readable(file_spec.GetPath());148}149150bool FileSystem::IsDirectory(const Twine &path) const {151ErrorOr<vfs::Status> status = m_fs->status(path);152if (!status)153return false;154return status->isDirectory();155}156157bool FileSystem::IsDirectory(const FileSpec &file_spec) const {158return file_spec && IsDirectory(file_spec.GetPath());159}160161bool FileSystem::IsLocal(const Twine &path) const {162bool b = false;163m_fs->isLocal(path, b);164return b;165}166167bool FileSystem::IsLocal(const FileSpec &file_spec) const {168return file_spec && IsLocal(file_spec.GetPath());169}170171void FileSystem::EnumerateDirectory(Twine path, bool find_directories,172bool find_files, bool find_other,173EnumerateDirectoryCallbackType callback,174void *callback_baton) {175std::error_code EC;176vfs::recursive_directory_iterator Iter(*m_fs, path, EC);177vfs::recursive_directory_iterator End;178for (; Iter != End && !EC; Iter.increment(EC)) {179const auto &Item = *Iter;180ErrorOr<vfs::Status> Status = m_fs->status(Item.path());181if (!Status)182continue;183if (!find_files && Status->isRegularFile())184continue;185if (!find_directories && Status->isDirectory())186continue;187if (!find_other && Status->isOther())188continue;189190auto Result = callback(callback_baton, Status->getType(), Item.path());191if (Result == eEnumerateDirectoryResultQuit)192return;193if (Result == eEnumerateDirectoryResultNext) {194// Default behavior is to recurse. Opt out if the callback doesn't want195// this behavior.196Iter.no_push();197}198}199}200201std::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const {202return m_fs->makeAbsolute(path);203}204205std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const {206SmallString<128> path;207file_spec.GetPath(path, false);208209auto EC = MakeAbsolute(path);210if (EC)211return EC;212213FileSpec new_file_spec(path, file_spec.GetPathStyle());214file_spec = new_file_spec;215return {};216}217218std::error_code FileSystem::GetRealPath(const Twine &path,219SmallVectorImpl<char> &output) const {220return m_fs->getRealPath(path, output);221}222223void FileSystem::Resolve(SmallVectorImpl<char> &path) {224if (path.empty())225return;226227// Resolve tilde in path.228SmallString<128> resolved(path.begin(), path.end());229assert(m_tilde_resolver && "must initialize tilde resolver in constructor");230m_tilde_resolver->ResolveFullPath(llvm::StringRef(path.begin(), path.size()),231resolved);232233// Try making the path absolute if it exists.234SmallString<128> absolute(resolved.begin(), resolved.end());235MakeAbsolute(absolute);236237path.clear();238if (Exists(absolute)) {239path.append(absolute.begin(), absolute.end());240} else {241path.append(resolved.begin(), resolved.end());242}243}244245void FileSystem::Resolve(FileSpec &file_spec) {246if (!file_spec)247return;248249// Extract path from the FileSpec.250SmallString<128> path;251file_spec.GetPath(path);252253// Resolve the path.254Resolve(path);255256// Update the FileSpec with the resolved path.257if (file_spec.GetFilename().IsEmpty())258file_spec.SetDirectory(path);259else260file_spec.SetPath(path);261}262263template <typename T>264static std::unique_ptr<T> GetMemoryBuffer(const llvm::Twine &path,265uint64_t size, uint64_t offset,266bool is_volatile) {267std::unique_ptr<T> buffer;268if (size == 0) {269auto buffer_or_error = T::getFile(path, is_volatile);270if (!buffer_or_error)271return nullptr;272buffer = std::move(*buffer_or_error);273} else {274auto buffer_or_error = T::getFileSlice(path, size, offset, is_volatile);275if (!buffer_or_error)276return nullptr;277buffer = std::move(*buffer_or_error);278}279return buffer;280}281282std::shared_ptr<WritableDataBuffer>283FileSystem::CreateWritableDataBuffer(const llvm::Twine &path, uint64_t size,284uint64_t offset) {285const bool is_volatile = !IsLocal(path);286auto buffer = GetMemoryBuffer<llvm::WritableMemoryBuffer>(path, size, offset,287is_volatile);288if (!buffer)289return {};290return std::shared_ptr<WritableDataBufferLLVM>(291new WritableDataBufferLLVM(std::move(buffer)));292}293294std::shared_ptr<DataBuffer>295FileSystem::CreateDataBuffer(const llvm::Twine &path, uint64_t size,296uint64_t offset) {297const bool is_volatile = !IsLocal(path);298auto buffer =299GetMemoryBuffer<llvm::MemoryBuffer>(path, size, offset, is_volatile);300if (!buffer)301return {};302return std::shared_ptr<DataBufferLLVM>(new DataBufferLLVM(std::move(buffer)));303}304305std::shared_ptr<WritableDataBuffer>306FileSystem::CreateWritableDataBuffer(const FileSpec &file_spec, uint64_t size,307uint64_t offset) {308return CreateWritableDataBuffer(file_spec.GetPath(), size, offset);309}310311std::shared_ptr<DataBuffer>312FileSystem::CreateDataBuffer(const FileSpec &file_spec, uint64_t size,313uint64_t offset) {314return CreateDataBuffer(file_spec.GetPath(), size, offset);315}316317bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) {318// If the directory is set there's nothing to do.319ConstString directory = file_spec.GetDirectory();320if (directory)321return false;322323// We cannot look for a file if there's no file name.324ConstString filename = file_spec.GetFilename();325if (!filename)326return false;327328// Search for the file on the host.329const std::string filename_str(filename.GetCString());330llvm::ErrorOr<std::string> error_or_path =331llvm::sys::findProgramByName(filename_str);332if (!error_or_path)333return false;334335// findProgramByName returns "." if it can't find the file.336llvm::StringRef path = *error_or_path;337llvm::StringRef parent = llvm::sys::path::parent_path(path);338if (parent.empty() || parent == ".")339return false;340341// Make sure that the result exists.342FileSpec result(*error_or_path);343if (!Exists(result))344return false;345346file_spec = result;347return true;348}349350bool FileSystem::GetHomeDirectory(SmallVectorImpl<char> &path) const {351if (!m_home_directory.empty()) {352path.assign(m_home_directory.begin(), m_home_directory.end());353return true;354}355return llvm::sys::path::home_directory(path);356}357358bool FileSystem::GetHomeDirectory(FileSpec &file_spec) const {359SmallString<128> home_dir;360if (!GetHomeDirectory(home_dir))361return false;362file_spec.SetPath(home_dir);363return true;364}365366static int OpenWithFS(const FileSystem &fs, const char *path, int flags,367int mode) {368return const_cast<FileSystem &>(fs).Open(path, flags, mode);369}370371static int GetOpenFlags(File::OpenOptions options) {372int open_flags = 0;373File::OpenOptions rw =374options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |375File::eOpenOptionReadWrite);376if (rw == File::eOpenOptionWriteOnly || rw == File::eOpenOptionReadWrite) {377if (rw == File::eOpenOptionReadWrite)378open_flags |= O_RDWR;379else380open_flags |= O_WRONLY;381382if (options & File::eOpenOptionAppend)383open_flags |= O_APPEND;384385if (options & File::eOpenOptionTruncate)386open_flags |= O_TRUNC;387388if (options & File::eOpenOptionCanCreate)389open_flags |= O_CREAT;390391if (options & File::eOpenOptionCanCreateNewOnly)392open_flags |= O_CREAT | O_EXCL;393} else if (rw == File::eOpenOptionReadOnly) {394open_flags |= O_RDONLY;395396#ifndef _WIN32397if (options & File::eOpenOptionDontFollowSymlinks)398open_flags |= O_NOFOLLOW;399#endif400}401402#ifndef _WIN32403if (options & File::eOpenOptionNonBlocking)404open_flags |= O_NONBLOCK;405if (options & File::eOpenOptionCloseOnExec)406open_flags |= O_CLOEXEC;407#else408open_flags |= O_BINARY;409#endif410411return open_flags;412}413414static mode_t GetOpenMode(uint32_t permissions) {415mode_t mode = 0;416if (permissions & lldb::eFilePermissionsUserRead)417mode |= S_IRUSR;418if (permissions & lldb::eFilePermissionsUserWrite)419mode |= S_IWUSR;420if (permissions & lldb::eFilePermissionsUserExecute)421mode |= S_IXUSR;422if (permissions & lldb::eFilePermissionsGroupRead)423mode |= S_IRGRP;424if (permissions & lldb::eFilePermissionsGroupWrite)425mode |= S_IWGRP;426if (permissions & lldb::eFilePermissionsGroupExecute)427mode |= S_IXGRP;428if (permissions & lldb::eFilePermissionsWorldRead)429mode |= S_IROTH;430if (permissions & lldb::eFilePermissionsWorldWrite)431mode |= S_IWOTH;432if (permissions & lldb::eFilePermissionsWorldExecute)433mode |= S_IXOTH;434return mode;435}436437Expected<FileUP> FileSystem::Open(const FileSpec &file_spec,438File::OpenOptions options,439uint32_t permissions, bool should_close_fd) {440const int open_flags = GetOpenFlags(options);441const mode_t open_mode =442(open_flags & O_CREAT) ? GetOpenMode(permissions) : 0;443444auto path = file_spec.GetPath();445446int descriptor = llvm::sys::RetryAfterSignal(447-1, OpenWithFS, *this, path.c_str(), open_flags, open_mode);448449if (!File::DescriptorIsValid(descriptor))450return llvm::errorCodeToError(451std::error_code(errno, std::system_category()));452453auto file = std::unique_ptr<File>(454new NativeFile(descriptor, options, should_close_fd));455assert(file->IsValid());456return std::move(file);457}458459void FileSystem::SetHomeDirectory(std::string home_directory) {460m_home_directory = std::move(home_directory);461}462463Status FileSystem::RemoveFile(const FileSpec &file_spec) {464return RemoveFile(file_spec.GetPath());465}466467Status FileSystem::RemoveFile(const llvm::Twine &path) {468return Status(llvm::sys::fs::remove(path));469}470471472