1285101Semaste//===-- FileSystem.cpp ------------------------------------------*- C++ -*-===// 2285101Semaste// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6285101Semaste// 7285101Semaste//===----------------------------------------------------------------------===// 8285101Semaste 9285101Semaste#include "lldb/Host/FileSystem.h" 10285101Semaste 11344779Sdim#include "lldb/Utility/LLDBAssert.h" 12344779Sdim#include "lldb/Utility/TildeExpressionResolver.h" 13344779Sdim 14353358Sdim#include "llvm/Support/Errc.h" 15344779Sdim#include "llvm/Support/Errno.h" 16353358Sdim#include "llvm/Support/Error.h" 17314564Sdim#include "llvm/Support/FileSystem.h" 18344779Sdim#include "llvm/Support/Path.h" 19344779Sdim#include "llvm/Support/Program.h" 20344779Sdim#include "llvm/Support/Threading.h" 21285101Semaste 22344779Sdim#include <errno.h> 23344779Sdim#include <fcntl.h> 24344779Sdim#include <limits.h> 25344779Sdim#include <stdarg.h> 26344779Sdim#include <stdio.h> 27344779Sdim 28344779Sdim#ifdef _WIN32 29344779Sdim#include "lldb/Host/windows/windows.h" 30344779Sdim#else 31344779Sdim#include <sys/ioctl.h> 32344779Sdim#include <sys/stat.h> 33344779Sdim#include <termios.h> 34344779Sdim#include <unistd.h> 35344779Sdim#endif 36344779Sdim 37285101Semaste#include <algorithm> 38285101Semaste#include <fstream> 39285101Semaste#include <vector> 40285101Semaste 41285101Semasteusing namespace lldb; 42285101Semasteusing namespace lldb_private; 43344779Sdimusing namespace llvm; 44285101Semaste 45344779SdimFileSystem &FileSystem::Instance() { return *InstanceImpl(); } 46344779Sdim 47344779Sdimvoid FileSystem::Initialize() { 48344779Sdim lldbassert(!InstanceImpl() && "Already initialized."); 49344779Sdim InstanceImpl().emplace(); 50314564Sdim} 51344779Sdim 52360784Sdimvoid FileSystem::Initialize(std::shared_ptr<FileCollector> collector) { 53353358Sdim lldbassert(!InstanceImpl() && "Already initialized."); 54353358Sdim InstanceImpl().emplace(collector); 55353358Sdim} 56353358Sdim 57353358Sdimllvm::Error FileSystem::Initialize(const FileSpec &mapping) { 58353358Sdim lldbassert(!InstanceImpl() && "Already initialized."); 59353358Sdim 60353358Sdim llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer = 61353358Sdim llvm::vfs::getRealFileSystem()->getBufferForFile(mapping.GetPath()); 62353358Sdim 63353358Sdim if (!buffer) 64353358Sdim return llvm::errorCodeToError(buffer.getError()); 65353358Sdim 66353358Sdim InstanceImpl().emplace(llvm::vfs::getVFSFromYAML(std::move(buffer.get()), 67353358Sdim nullptr, mapping.GetPath()), 68353358Sdim true); 69353358Sdim 70353358Sdim return llvm::Error::success(); 71353358Sdim} 72353358Sdim 73344779Sdimvoid FileSystem::Initialize(IntrusiveRefCntPtr<vfs::FileSystem> fs) { 74344779Sdim lldbassert(!InstanceImpl() && "Already initialized."); 75344779Sdim InstanceImpl().emplace(fs); 76344779Sdim} 77344779Sdim 78344779Sdimvoid FileSystem::Terminate() { 79344779Sdim lldbassert(InstanceImpl() && "Already terminated."); 80344779Sdim InstanceImpl().reset(); 81344779Sdim} 82344779Sdim 83344779SdimOptional<FileSystem> &FileSystem::InstanceImpl() { 84344779Sdim static Optional<FileSystem> g_fs; 85344779Sdim return g_fs; 86344779Sdim} 87344779Sdim 88344779Sdimvfs::directory_iterator FileSystem::DirBegin(const FileSpec &file_spec, 89344779Sdim std::error_code &ec) { 90344779Sdim return DirBegin(file_spec.GetPath(), ec); 91344779Sdim} 92344779Sdim 93344779Sdimvfs::directory_iterator FileSystem::DirBegin(const Twine &dir, 94344779Sdim std::error_code &ec) { 95344779Sdim return m_fs->dir_begin(dir, ec); 96344779Sdim} 97344779Sdim 98344779Sdimllvm::ErrorOr<vfs::Status> 99344779SdimFileSystem::GetStatus(const FileSpec &file_spec) const { 100344779Sdim return GetStatus(file_spec.GetPath()); 101344779Sdim} 102344779Sdim 103344779Sdimllvm::ErrorOr<vfs::Status> FileSystem::GetStatus(const Twine &path) const { 104344779Sdim return m_fs->status(path); 105344779Sdim} 106344779Sdim 107344779Sdimsys::TimePoint<> 108344779SdimFileSystem::GetModificationTime(const FileSpec &file_spec) const { 109344779Sdim return GetModificationTime(file_spec.GetPath()); 110344779Sdim} 111344779Sdim 112344779Sdimsys::TimePoint<> FileSystem::GetModificationTime(const Twine &path) const { 113344779Sdim ErrorOr<vfs::Status> status = m_fs->status(path); 114344779Sdim if (!status) 115344779Sdim return sys::TimePoint<>(); 116344779Sdim return status->getLastModificationTime(); 117344779Sdim} 118344779Sdim 119344779Sdimuint64_t FileSystem::GetByteSize(const FileSpec &file_spec) const { 120344779Sdim return GetByteSize(file_spec.GetPath()); 121344779Sdim} 122344779Sdim 123344779Sdimuint64_t FileSystem::GetByteSize(const Twine &path) const { 124344779Sdim ErrorOr<vfs::Status> status = m_fs->status(path); 125344779Sdim if (!status) 126344779Sdim return 0; 127344779Sdim return status->getSize(); 128344779Sdim} 129344779Sdim 130344779Sdimuint32_t FileSystem::GetPermissions(const FileSpec &file_spec) const { 131344779Sdim return GetPermissions(file_spec.GetPath()); 132344779Sdim} 133344779Sdim 134344779Sdimuint32_t FileSystem::GetPermissions(const FileSpec &file_spec, 135344779Sdim std::error_code &ec) const { 136344779Sdim return GetPermissions(file_spec.GetPath(), ec); 137344779Sdim} 138344779Sdim 139344779Sdimuint32_t FileSystem::GetPermissions(const Twine &path) const { 140344779Sdim std::error_code ec; 141344779Sdim return GetPermissions(path, ec); 142344779Sdim} 143344779Sdim 144344779Sdimuint32_t FileSystem::GetPermissions(const Twine &path, 145344779Sdim std::error_code &ec) const { 146344779Sdim ErrorOr<vfs::Status> status = m_fs->status(path); 147344779Sdim if (!status) { 148344779Sdim ec = status.getError(); 149344779Sdim return sys::fs::perms::perms_not_known; 150344779Sdim } 151344779Sdim return status->getPermissions(); 152344779Sdim} 153344779Sdim 154344779Sdimbool FileSystem::Exists(const Twine &path) const { return m_fs->exists(path); } 155344779Sdim 156344779Sdimbool FileSystem::Exists(const FileSpec &file_spec) const { 157344779Sdim return Exists(file_spec.GetPath()); 158344779Sdim} 159344779Sdim 160344779Sdimbool FileSystem::Readable(const Twine &path) const { 161344779Sdim return GetPermissions(path) & sys::fs::perms::all_read; 162344779Sdim} 163344779Sdim 164344779Sdimbool FileSystem::Readable(const FileSpec &file_spec) const { 165344779Sdim return Readable(file_spec.GetPath()); 166344779Sdim} 167344779Sdim 168344779Sdimbool FileSystem::IsDirectory(const Twine &path) const { 169344779Sdim ErrorOr<vfs::Status> status = m_fs->status(path); 170344779Sdim if (!status) 171344779Sdim return false; 172344779Sdim return status->isDirectory(); 173344779Sdim} 174344779Sdim 175344779Sdimbool FileSystem::IsDirectory(const FileSpec &file_spec) const { 176344779Sdim return IsDirectory(file_spec.GetPath()); 177344779Sdim} 178344779Sdim 179344779Sdimbool FileSystem::IsLocal(const Twine &path) const { 180344779Sdim bool b = false; 181344779Sdim m_fs->isLocal(path, b); 182344779Sdim return b; 183344779Sdim} 184344779Sdim 185344779Sdimbool FileSystem::IsLocal(const FileSpec &file_spec) const { 186344779Sdim return IsLocal(file_spec.GetPath()); 187344779Sdim} 188344779Sdim 189344779Sdimvoid FileSystem::EnumerateDirectory(Twine path, bool find_directories, 190344779Sdim bool find_files, bool find_other, 191344779Sdim EnumerateDirectoryCallbackType callback, 192344779Sdim void *callback_baton) { 193344779Sdim std::error_code EC; 194344779Sdim vfs::recursive_directory_iterator Iter(*m_fs, path, EC); 195344779Sdim vfs::recursive_directory_iterator End; 196344779Sdim for (; Iter != End && !EC; Iter.increment(EC)) { 197344779Sdim const auto &Item = *Iter; 198344779Sdim ErrorOr<vfs::Status> Status = m_fs->status(Item.path()); 199344779Sdim if (!Status) 200344779Sdim break; 201344779Sdim if (!find_files && Status->isRegularFile()) 202344779Sdim continue; 203344779Sdim if (!find_directories && Status->isDirectory()) 204344779Sdim continue; 205344779Sdim if (!find_other && Status->isOther()) 206344779Sdim continue; 207344779Sdim 208344779Sdim auto Result = callback(callback_baton, Status->getType(), Item.path()); 209344779Sdim if (Result == eEnumerateDirectoryResultQuit) 210344779Sdim return; 211344779Sdim if (Result == eEnumerateDirectoryResultNext) { 212344779Sdim // Default behavior is to recurse. Opt out if the callback doesn't want 213344779Sdim // this behavior. 214344779Sdim Iter.no_push(); 215344779Sdim } 216344779Sdim } 217344779Sdim} 218344779Sdim 219344779Sdimstd::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const { 220344779Sdim return m_fs->makeAbsolute(path); 221344779Sdim} 222344779Sdim 223344779Sdimstd::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const { 224344779Sdim SmallString<128> path; 225344779Sdim file_spec.GetPath(path, false); 226344779Sdim 227344779Sdim auto EC = MakeAbsolute(path); 228344779Sdim if (EC) 229344779Sdim return EC; 230344779Sdim 231344779Sdim FileSpec new_file_spec(path, file_spec.GetPathStyle()); 232344779Sdim file_spec = new_file_spec; 233344779Sdim return {}; 234344779Sdim} 235344779Sdim 236344779Sdimstd::error_code FileSystem::GetRealPath(const Twine &path, 237344779Sdim SmallVectorImpl<char> &output) const { 238344779Sdim return m_fs->getRealPath(path, output); 239344779Sdim} 240344779Sdim 241344779Sdimvoid FileSystem::Resolve(SmallVectorImpl<char> &path) { 242344779Sdim if (path.empty()) 243344779Sdim return; 244344779Sdim 245353358Sdim // Resolve tilde in path. 246353358Sdim SmallString<128> resolved(path.begin(), path.end()); 247344779Sdim StandardTildeExpressionResolver Resolver; 248353358Sdim Resolver.ResolveFullPath(llvm::StringRef(path.begin(), path.size()), 249353358Sdim resolved); 250344779Sdim 251344779Sdim // Try making the path absolute if it exists. 252353358Sdim SmallString<128> absolute(resolved.begin(), resolved.end()); 253353358Sdim MakeAbsolute(absolute); 254353358Sdim 255353358Sdim path.clear(); 256353358Sdim if (Exists(absolute)) { 257353358Sdim path.append(absolute.begin(), absolute.end()); 258353358Sdim } else { 259353358Sdim path.append(resolved.begin(), resolved.end()); 260344779Sdim } 261344779Sdim} 262344779Sdim 263344779Sdimvoid FileSystem::Resolve(FileSpec &file_spec) { 264344779Sdim // Extract path from the FileSpec. 265344779Sdim SmallString<128> path; 266344779Sdim file_spec.GetPath(path); 267344779Sdim 268344779Sdim // Resolve the path. 269344779Sdim Resolve(path); 270344779Sdim 271344779Sdim // Update the FileSpec with the resolved path. 272353358Sdim if (file_spec.GetFilename().IsEmpty()) 273353358Sdim file_spec.GetDirectory().SetString(path); 274353358Sdim else 275353358Sdim file_spec.SetPath(path); 276344779Sdim file_spec.SetIsResolved(true); 277344779Sdim} 278344779Sdim 279344779Sdimstd::shared_ptr<DataBufferLLVM> 280344779SdimFileSystem::CreateDataBuffer(const llvm::Twine &path, uint64_t size, 281344779Sdim uint64_t offset) { 282353358Sdim if (m_collector) 283360784Sdim m_collector->addFile(path); 284353358Sdim 285344779Sdim const bool is_volatile = !IsLocal(path); 286353358Sdim const ErrorOr<std::string> external_path = GetExternalPath(path); 287344779Sdim 288353358Sdim if (!external_path) 289353358Sdim return nullptr; 290353358Sdim 291344779Sdim std::unique_ptr<llvm::WritableMemoryBuffer> buffer; 292344779Sdim if (size == 0) { 293344779Sdim auto buffer_or_error = 294353358Sdim llvm::WritableMemoryBuffer::getFile(*external_path, -1, is_volatile); 295344779Sdim if (!buffer_or_error) 296344779Sdim return nullptr; 297344779Sdim buffer = std::move(*buffer_or_error); 298344779Sdim } else { 299344779Sdim auto buffer_or_error = llvm::WritableMemoryBuffer::getFileSlice( 300353358Sdim *external_path, size, offset, is_volatile); 301344779Sdim if (!buffer_or_error) 302344779Sdim return nullptr; 303344779Sdim buffer = std::move(*buffer_or_error); 304344779Sdim } 305344779Sdim return std::shared_ptr<DataBufferLLVM>(new DataBufferLLVM(std::move(buffer))); 306344779Sdim} 307344779Sdim 308344779Sdimstd::shared_ptr<DataBufferLLVM> 309344779SdimFileSystem::CreateDataBuffer(const FileSpec &file_spec, uint64_t size, 310344779Sdim uint64_t offset) { 311344779Sdim return CreateDataBuffer(file_spec.GetPath(), size, offset); 312344779Sdim} 313344779Sdim 314344779Sdimbool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) { 315344779Sdim // If the directory is set there's nothing to do. 316353358Sdim ConstString directory = file_spec.GetDirectory(); 317344779Sdim if (directory) 318344779Sdim return false; 319344779Sdim 320344779Sdim // We cannot look for a file if there's no file name. 321353358Sdim ConstString filename = file_spec.GetFilename(); 322344779Sdim if (!filename) 323344779Sdim return false; 324344779Sdim 325344779Sdim // Search for the file on the host. 326344779Sdim const std::string filename_str(filename.GetCString()); 327344779Sdim llvm::ErrorOr<std::string> error_or_path = 328344779Sdim llvm::sys::findProgramByName(filename_str); 329344779Sdim if (!error_or_path) 330344779Sdim return false; 331344779Sdim 332344779Sdim // findProgramByName returns "." if it can't find the file. 333344779Sdim llvm::StringRef path = *error_or_path; 334344779Sdim llvm::StringRef parent = llvm::sys::path::parent_path(path); 335344779Sdim if (parent.empty() || parent == ".") 336344779Sdim return false; 337344779Sdim 338344779Sdim // Make sure that the result exists. 339344779Sdim FileSpec result(*error_or_path); 340344779Sdim if (!Exists(result)) 341344779Sdim return false; 342344779Sdim 343344779Sdim file_spec = result; 344344779Sdim return true; 345344779Sdim} 346344779Sdim 347344779Sdimstatic int OpenWithFS(const FileSystem &fs, const char *path, int flags, 348344779Sdim int mode) { 349344779Sdim return const_cast<FileSystem &>(fs).Open(path, flags, mode); 350344779Sdim} 351344779Sdim 352344779Sdimstatic int GetOpenFlags(uint32_t options) { 353344779Sdim const bool read = options & File::eOpenOptionRead; 354344779Sdim const bool write = options & File::eOpenOptionWrite; 355344779Sdim 356344779Sdim int open_flags = 0; 357344779Sdim if (write) { 358344779Sdim if (read) 359344779Sdim open_flags |= O_RDWR; 360344779Sdim else 361344779Sdim open_flags |= O_WRONLY; 362344779Sdim 363344779Sdim if (options & File::eOpenOptionAppend) 364344779Sdim open_flags |= O_APPEND; 365344779Sdim 366344779Sdim if (options & File::eOpenOptionTruncate) 367344779Sdim open_flags |= O_TRUNC; 368344779Sdim 369344779Sdim if (options & File::eOpenOptionCanCreate) 370344779Sdim open_flags |= O_CREAT; 371344779Sdim 372344779Sdim if (options & File::eOpenOptionCanCreateNewOnly) 373344779Sdim open_flags |= O_CREAT | O_EXCL; 374344779Sdim } else if (read) { 375344779Sdim open_flags |= O_RDONLY; 376344779Sdim 377344779Sdim#ifndef _WIN32 378344779Sdim if (options & File::eOpenOptionDontFollowSymlinks) 379344779Sdim open_flags |= O_NOFOLLOW; 380344779Sdim#endif 381344779Sdim } 382344779Sdim 383344779Sdim#ifndef _WIN32 384344779Sdim if (options & File::eOpenOptionNonBlocking) 385344779Sdim open_flags |= O_NONBLOCK; 386344779Sdim if (options & File::eOpenOptionCloseOnExec) 387344779Sdim open_flags |= O_CLOEXEC; 388344779Sdim#else 389344779Sdim open_flags |= O_BINARY; 390344779Sdim#endif 391344779Sdim 392344779Sdim return open_flags; 393344779Sdim} 394344779Sdim 395344779Sdimstatic mode_t GetOpenMode(uint32_t permissions) { 396344779Sdim mode_t mode = 0; 397344779Sdim if (permissions & lldb::eFilePermissionsUserRead) 398344779Sdim mode |= S_IRUSR; 399344779Sdim if (permissions & lldb::eFilePermissionsUserWrite) 400344779Sdim mode |= S_IWUSR; 401344779Sdim if (permissions & lldb::eFilePermissionsUserExecute) 402344779Sdim mode |= S_IXUSR; 403344779Sdim if (permissions & lldb::eFilePermissionsGroupRead) 404344779Sdim mode |= S_IRGRP; 405344779Sdim if (permissions & lldb::eFilePermissionsGroupWrite) 406344779Sdim mode |= S_IWGRP; 407344779Sdim if (permissions & lldb::eFilePermissionsGroupExecute) 408344779Sdim mode |= S_IXGRP; 409344779Sdim if (permissions & lldb::eFilePermissionsWorldRead) 410344779Sdim mode |= S_IROTH; 411344779Sdim if (permissions & lldb::eFilePermissionsWorldWrite) 412344779Sdim mode |= S_IWOTH; 413344779Sdim if (permissions & lldb::eFilePermissionsWorldExecute) 414344779Sdim mode |= S_IXOTH; 415344779Sdim return mode; 416344779Sdim} 417344779Sdim 418360784SdimExpected<FileUP> FileSystem::Open(const FileSpec &file_spec, 419360784Sdim File::OpenOptions options, 420360784Sdim uint32_t permissions, bool should_close_fd) { 421353358Sdim if (m_collector) 422360784Sdim m_collector->addFile(file_spec.GetPath()); 423353358Sdim 424344779Sdim const int open_flags = GetOpenFlags(options); 425344779Sdim const mode_t open_mode = 426344779Sdim (open_flags & O_CREAT) ? GetOpenMode(permissions) : 0; 427344779Sdim 428353358Sdim auto path = GetExternalPath(file_spec); 429353358Sdim if (!path) 430360784Sdim return errorCodeToError(path.getError()); 431353358Sdim 432344779Sdim int descriptor = llvm::sys::RetryAfterSignal( 433353358Sdim -1, OpenWithFS, *this, path->c_str(), open_flags, open_mode); 434344779Sdim 435360784Sdim if (!File::DescriptorIsValid(descriptor)) 436360784Sdim return llvm::errorCodeToError( 437360784Sdim std::error_code(errno, std::system_category())); 438360784Sdim 439360784Sdim auto file = std::unique_ptr<File>( 440360784Sdim new NativeFile(descriptor, options, should_close_fd)); 441360784Sdim assert(file->IsValid()); 442360784Sdim return std::move(file); 443344779Sdim} 444353358Sdim 445353358SdimErrorOr<std::string> FileSystem::GetExternalPath(const llvm::Twine &path) { 446353358Sdim if (!m_mapped) 447353358Sdim return path.str(); 448353358Sdim 449353358Sdim // If VFS mapped we know the underlying FS is a RedirectingFileSystem. 450353358Sdim ErrorOr<vfs::RedirectingFileSystem::Entry *> E = 451353358Sdim static_cast<vfs::RedirectingFileSystem &>(*m_fs).lookupPath(path); 452353358Sdim if (!E) { 453353358Sdim if (E.getError() == llvm::errc::no_such_file_or_directory) { 454353358Sdim return path.str(); 455353358Sdim } 456353358Sdim return E.getError(); 457353358Sdim } 458353358Sdim 459353358Sdim auto *F = dyn_cast<vfs::RedirectingFileSystem::RedirectingFileEntry>(*E); 460353358Sdim if (!F) 461353358Sdim return make_error_code(llvm::errc::not_supported); 462353358Sdim 463353358Sdim return F->getExternalContentsPath().str(); 464353358Sdim} 465353358Sdim 466353358SdimErrorOr<std::string> FileSystem::GetExternalPath(const FileSpec &file_spec) { 467353358Sdim return GetExternalPath(file_spec.GetPath()); 468353358Sdim} 469