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 sys::TimePoint<>
51 FileSystem::GetModificationTime(const FileSpec &file_spec) const {
52   return GetModificationTime(file_spec.GetPath());
53 }
54 
55 sys::TimePoint<> FileSystem::GetModificationTime(const Twine &path) const {
56   ErrorOr<vfs::Status> status = m_fs->status(path);
57   if (!status)
58     return sys::TimePoint<>();
59   return status->getLastModificationTime();
60 }
61 
62 uint64_t FileSystem::GetByteSize(const FileSpec &file_spec) const {
63   return GetByteSize(file_spec.GetPath());
64 }
65 
66 uint64_t FileSystem::GetByteSize(const Twine &path) const {
67   ErrorOr<vfs::Status> status = m_fs->status(path);
68   if (!status)
69     return 0;
70   return status->getSize();
71 }
72 
73 uint32_t FileSystem::GetPermissions(const FileSpec &file_spec) const {
74   return GetPermissions(file_spec.GetPath());
75 }
76 
77 uint32_t FileSystem::GetPermissions(const FileSpec &file_spec,
78                                     std::error_code &ec) const {
79   return GetPermissions(file_spec.GetPath(), ec);
80 }
81 
82 uint32_t FileSystem::GetPermissions(const Twine &path) const {
83   std::error_code ec;
84   return GetPermissions(path, ec);
85 }
86 
87 uint32_t FileSystem::GetPermissions(const Twine &path,
88                                     std::error_code &ec) const {
89   ErrorOr<vfs::Status> status = m_fs->status(path);
90   if (!status) {
91     ec = status.getError();
92     return sys::fs::perms::perms_not_known;
93   }
94   return status->getPermissions();
95 }
96 
97 bool FileSystem::Exists(const Twine &path) const { return m_fs->exists(path); }
98 
99 bool FileSystem::Exists(const FileSpec &file_spec) const {
100   return Exists(file_spec.GetPath());
101 }
102 
103 bool FileSystem::Readable(const Twine &path) const {
104   return GetPermissions(path) & sys::fs::perms::all_read;
105 }
106 
107 bool FileSystem::Readable(const FileSpec &file_spec) const {
108   return Readable(file_spec.GetPath());
109 }
110 
111 void FileSystem::EnumerateDirectory(Twine path, bool find_directories,
112                                     bool find_files, bool find_other,
113                                     EnumerateDirectoryCallbackType callback,
114                                     void *callback_baton) {
115   std::error_code EC;
116   vfs::recursive_directory_iterator Iter(*m_fs, path, EC);
117   vfs::recursive_directory_iterator End;
118   for (; Iter != End && !EC; Iter.increment(EC)) {
119     const auto &Item = *Iter;
120     ErrorOr<vfs::Status> Status = m_fs->status(Item.path());
121     if (!Status)
122       break;
123     if (!find_files && Status->isRegularFile())
124       continue;
125     if (!find_directories && Status->isDirectory())
126       continue;
127     if (!find_other && Status->isOther())
128       continue;
129 
130     auto Result = callback(callback_baton, Status->getType(), Item.path());
131     if (Result == eEnumerateDirectoryResultQuit)
132       return;
133     if (Result == eEnumerateDirectoryResultNext) {
134       // Default behavior is to recurse. Opt out if the callback doesn't want
135       // this behavior.
136       Iter.no_push();
137     }
138   }
139 }
140 
141 std::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const {
142   return m_fs->makeAbsolute(path);
143 }
144 
145 std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const {
146   SmallString<128> path;
147   file_spec.GetPath(path, false);
148 
149   auto EC = MakeAbsolute(path);
150   if (EC)
151     return EC;
152 
153   FileSpec new_file_spec(path, file_spec.GetPathStyle());
154   file_spec = new_file_spec;
155   return {};
156 }
157 
158 std::error_code FileSystem::GetRealPath(const Twine &path,
159                                         SmallVectorImpl<char> &output) const {
160   return m_fs->getRealPath(path, output);
161 }
162 
163 void FileSystem::Resolve(SmallVectorImpl<char> &path) {
164   if (path.empty())
165     return;
166 
167   // Resolve tilde.
168   SmallString<128> original_path(path.begin(), path.end());
169   StandardTildeExpressionResolver Resolver;
170   Resolver.ResolveFullPath(original_path, path);
171 
172   // Try making the path absolute if it exists.
173   SmallString<128> absolute_path(path.begin(), path.end());
174   MakeAbsolute(path);
175   if (!Exists(path)) {
176     path.clear();
177     path.append(original_path.begin(), original_path.end());
178   }
179 }
180 
181 void FileSystem::Resolve(FileSpec &file_spec) {
182   // Extract path from the FileSpec.
183   SmallString<128> path;
184   file_spec.GetPath(path);
185 
186   // Resolve the path.
187   Resolve(path);
188 
189   // Update the FileSpec with the resolved path.
190   file_spec.SetPath(path);
191   file_spec.SetIsResolved(true);
192 }
193 
194 bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) {
195   // If the directory is set there's nothing to do.
196   const ConstString &directory = file_spec.GetDirectory();
197   if (directory)
198     return false;
199 
200   // We cannot look for a file if there's no file name.
201   const ConstString &filename = file_spec.GetFilename();
202   if (!filename)
203     return false;
204 
205   // Search for the file on the host.
206   const std::string filename_str(filename.GetCString());
207   llvm::ErrorOr<std::string> error_or_path =
208       llvm::sys::findProgramByName(filename_str);
209   if (!error_or_path)
210     return false;
211 
212   // findProgramByName returns "." if it can't find the file.
213   llvm::StringRef path = *error_or_path;
214   llvm::StringRef parent = llvm::sys::path::parent_path(path);
215   if (parent.empty() || parent == ".")
216     return false;
217 
218   // Make sure that the result exists.
219   FileSpec result(*error_or_path);
220   if (!Exists(result))
221     return false;
222 
223   file_spec = result;
224   return true;
225 }
226