1 //===-- FileSystem.cpp ------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "lldb/Host/FileSystem.h"
11 
12 #include "lldb/Utility/LLDBAssert.h"
13 #include "lldb/Utility/TildeExpressionResolver.h"
14 
15 #include "llvm/Support/FileSystem.h"
16 #include "llvm/Support/Path.h"
17 #include "llvm/Support/Program.h"
18 #include "llvm/Support/Threading.h"
19 
20 #include <algorithm>
21 #include <fstream>
22 #include <vector>
23 
24 using namespace lldb;
25 using namespace lldb_private;
26 using namespace llvm;
27 
28 FileSystem &FileSystem::Instance() { return *InstanceImpl(); }
29 
30 void FileSystem::Initialize() {
31   lldbassert(!InstanceImpl() && "Already initialized.");
32   InstanceImpl().emplace();
33 }
34 
35 void FileSystem::Initialize(IntrusiveRefCntPtr<vfs::FileSystem> fs) {
36   lldbassert(!InstanceImpl() && "Already initialized.");
37   InstanceImpl().emplace(fs);
38 }
39 
40 void FileSystem::Terminate() {
41   lldbassert(InstanceImpl() && "Already terminated.");
42   InstanceImpl().reset();
43 }
44 
45 Optional<FileSystem> &FileSystem::InstanceImpl() {
46   static Optional<FileSystem> g_fs;
47   return g_fs;
48 }
49 
50 void FileSystem::SetFileSystem(IntrusiveRefCntPtr<vfs::FileSystem> fs) {
51   m_fs = fs;
52 }
53 
54 sys::TimePoint<>
55 FileSystem::GetModificationTime(const FileSpec &file_spec) const {
56   return GetModificationTime(file_spec.GetPath());
57 }
58 
59 sys::TimePoint<> FileSystem::GetModificationTime(const Twine &path) const {
60   ErrorOr<vfs::Status> status = m_fs->status(path);
61   if (!status)
62     return sys::TimePoint<>();
63   return status->getLastModificationTime();
64 }
65 
66 uint64_t FileSystem::GetByteSize(const FileSpec &file_spec) const {
67   return GetByteSize(file_spec.GetPath());
68 }
69 
70 uint64_t FileSystem::GetByteSize(const Twine &path) const {
71   ErrorOr<vfs::Status> status = m_fs->status(path);
72   if (!status)
73     return 0;
74   return status->getSize();
75 }
76 
77 uint32_t FileSystem::GetPermissions(const FileSpec &file_spec) const {
78   return GetPermissions(file_spec.GetPath());
79 }
80 
81 uint32_t FileSystem::GetPermissions(const Twine &path) const {
82   ErrorOr<vfs::Status> status = m_fs->status(path);
83   if (!status)
84     return sys::fs::perms::perms_not_known;
85   return status->getPermissions();
86 }
87 
88 bool FileSystem::Exists(const Twine &path) const { return m_fs->exists(path); }
89 
90 bool FileSystem::Exists(const FileSpec &file_spec) const {
91   return Exists(file_spec.GetPath());
92 }
93 
94 bool FileSystem::Readable(const Twine &path) const {
95   return GetPermissions(path) & sys::fs::perms::all_read;
96 }
97 
98 bool FileSystem::Readable(const FileSpec &file_spec) const {
99   return Readable(file_spec.GetPath());
100 }
101 
102 void FileSystem::EnumerateDirectory(Twine path, bool find_directories,
103                                     bool find_files, bool find_other,
104                                     EnumerateDirectoryCallbackType callback,
105                                     void *callback_baton) {
106   std::error_code EC;
107   vfs::recursive_directory_iterator Iter(*m_fs, path, EC);
108   vfs::recursive_directory_iterator End;
109   for (; Iter != End && !EC; Iter.increment(EC)) {
110     const auto &Item = *Iter;
111     ErrorOr<vfs::Status> Status = m_fs->status(Item.path());
112     if (!Status)
113       break;
114     if (!find_files && Status->isRegularFile())
115       continue;
116     if (!find_directories && Status->isDirectory())
117       continue;
118     if (!find_other && Status->isOther())
119       continue;
120 
121     auto Result = callback(callback_baton, Status->getType(), Item.path());
122     if (Result == eEnumerateDirectoryResultQuit)
123       return;
124     if (Result == eEnumerateDirectoryResultNext) {
125       // Default behavior is to recurse. Opt out if the callback doesn't want
126       // this behavior.
127       Iter.no_push();
128     }
129   }
130 }
131 
132 std::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const {
133   return m_fs->makeAbsolute(path);
134 }
135 
136 std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const {
137   SmallString<128> path;
138   file_spec.GetPath(path, false);
139 
140   auto EC = MakeAbsolute(path);
141   if (EC)
142     return EC;
143 
144   FileSpec new_file_spec(path, file_spec.GetPathStyle());
145   file_spec = new_file_spec;
146   return {};
147 }
148 
149 std::error_code FileSystem::GetRealPath(const Twine &path,
150                                         SmallVectorImpl<char> &output) const {
151   return m_fs->getRealPath(path, output);
152 }
153 
154 void FileSystem::Resolve(SmallVectorImpl<char> &path) {
155   if (path.empty())
156     return;
157 
158   // Resolve tilde.
159   SmallString<128> original_path(path.begin(), path.end());
160   StandardTildeExpressionResolver Resolver;
161   Resolver.ResolveFullPath(original_path, path);
162 
163   // Try making the path absolute if it exists.
164   SmallString<128> absolute_path(path.begin(), path.end());
165   MakeAbsolute(path);
166   if (!Exists(path)) {
167     path.clear();
168     path.append(original_path.begin(), original_path.end());
169   }
170 }
171 
172 void FileSystem::Resolve(FileSpec &file_spec) {
173   // Extract path from the FileSpec.
174   SmallString<128> path;
175   file_spec.GetPath(path);
176 
177   // Resolve the path.
178   Resolve(path);
179 
180   // Update the FileSpec with the resolved path.
181   file_spec.SetPath(path);
182   file_spec.SetIsResolved(true);
183 }
184 
185 bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) {
186   // If the directory is set there's nothing to do.
187   const ConstString &directory = file_spec.GetDirectory();
188   if (directory)
189     return false;
190 
191   // We cannot look for a file if there's no file name.
192   const ConstString &filename = file_spec.GetFilename();
193   if (!filename)
194     return false;
195 
196   // Search for the file on the host.
197   const std::string filename_str(filename.GetCString());
198   llvm::ErrorOr<std::string> error_or_path =
199       llvm::sys::findProgramByName(filename_str);
200   if (!error_or_path)
201     return false;
202 
203   // findProgramByName returns "." if it can't find the file.
204   llvm::StringRef path = *error_or_path;
205   llvm::StringRef parent = llvm::sys::path::parent_path(path);
206   if (parent.empty() || parent == ".")
207     return false;
208 
209   // Make sure that the result exists.
210   FileSpec result(*error_or_path);
211   if (!Exists(result))
212     return false;
213 
214   file_spec = result;
215   return true;
216 }
217