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