1 //===-- FileSystem.cpp ----------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "lldb/Host/FileSystem.h"
10
11 #include "lldb/Utility/DataBufferLLVM.h"
12
13 #include "llvm/Support/Errc.h"
14 #include "llvm/Support/Errno.h"
15 #include "llvm/Support/Error.h"
16 #include "llvm/Support/FileSystem.h"
17 #include "llvm/Support/Path.h"
18 #include "llvm/Support/Program.h"
19 #include "llvm/Support/Threading.h"
20
21 #include <cerrno>
22 #include <climits>
23 #include <cstdarg>
24 #include <cstdio>
25 #include <fcntl.h>
26
27 #ifdef _WIN32
28 #include "lldb/Host/windows/windows.h"
29 #else
30 #include <sys/ioctl.h>
31 #include <sys/stat.h>
32 #include <termios.h>
33 #include <unistd.h>
34 #endif
35
36 #include <algorithm>
37 #include <fstream>
38 #include <optional>
39 #include <vector>
40
41 using namespace lldb;
42 using namespace lldb_private;
43 using namespace llvm;
44
Instance()45 FileSystem &FileSystem::Instance() { return *InstanceImpl(); }
46
Terminate()47 void FileSystem::Terminate() {
48 lldbassert(InstanceImpl() && "Already terminated.");
49 InstanceImpl().reset();
50 }
51
InstanceImpl()52 std::optional<FileSystem> &FileSystem::InstanceImpl() {
53 static std::optional<FileSystem> g_fs;
54 return g_fs;
55 }
56
DirBegin(const FileSpec & file_spec,std::error_code & ec)57 vfs::directory_iterator FileSystem::DirBegin(const FileSpec &file_spec,
58 std::error_code &ec) {
59 if (!file_spec) {
60 ec = std::error_code(static_cast<int>(errc::no_such_file_or_directory),
61 std::system_category());
62 return {};
63 }
64 return DirBegin(file_spec.GetPath(), ec);
65 }
66
DirBegin(const Twine & dir,std::error_code & ec)67 vfs::directory_iterator FileSystem::DirBegin(const Twine &dir,
68 std::error_code &ec) {
69 return m_fs->dir_begin(dir, ec);
70 }
71
72 llvm::ErrorOr<vfs::Status>
GetStatus(const FileSpec & file_spec) const73 FileSystem::GetStatus(const FileSpec &file_spec) const {
74 if (!file_spec)
75 return std::error_code(static_cast<int>(errc::no_such_file_or_directory),
76 std::system_category());
77 return GetStatus(file_spec.GetPath());
78 }
79
GetStatus(const Twine & path) const80 llvm::ErrorOr<vfs::Status> FileSystem::GetStatus(const Twine &path) const {
81 return m_fs->status(path);
82 }
83
84 sys::TimePoint<>
GetModificationTime(const FileSpec & file_spec) const85 FileSystem::GetModificationTime(const FileSpec &file_spec) const {
86 if (!file_spec)
87 return sys::TimePoint<>();
88 return GetModificationTime(file_spec.GetPath());
89 }
90
GetModificationTime(const Twine & path) const91 sys::TimePoint<> FileSystem::GetModificationTime(const Twine &path) const {
92 ErrorOr<vfs::Status> status = m_fs->status(path);
93 if (!status)
94 return sys::TimePoint<>();
95 return status->getLastModificationTime();
96 }
97
GetByteSize(const FileSpec & file_spec) const98 uint64_t FileSystem::GetByteSize(const FileSpec &file_spec) const {
99 if (!file_spec)
100 return 0;
101 return GetByteSize(file_spec.GetPath());
102 }
103
GetByteSize(const Twine & path) const104 uint64_t FileSystem::GetByteSize(const Twine &path) const {
105 ErrorOr<vfs::Status> status = m_fs->status(path);
106 if (!status)
107 return 0;
108 return status->getSize();
109 }
110
GetPermissions(const FileSpec & file_spec) const111 uint32_t FileSystem::GetPermissions(const FileSpec &file_spec) const {
112 return GetPermissions(file_spec.GetPath());
113 }
114
GetPermissions(const FileSpec & file_spec,std::error_code & ec) const115 uint32_t FileSystem::GetPermissions(const FileSpec &file_spec,
116 std::error_code &ec) const {
117 if (!file_spec)
118 return sys::fs::perms::perms_not_known;
119 return GetPermissions(file_spec.GetPath(), ec);
120 }
121
GetPermissions(const Twine & path) const122 uint32_t FileSystem::GetPermissions(const Twine &path) const {
123 std::error_code ec;
124 return GetPermissions(path, ec);
125 }
126
GetPermissions(const Twine & path,std::error_code & ec) const127 uint32_t FileSystem::GetPermissions(const Twine &path,
128 std::error_code &ec) const {
129 ErrorOr<vfs::Status> status = m_fs->status(path);
130 if (!status) {
131 ec = status.getError();
132 return sys::fs::perms::perms_not_known;
133 }
134 return status->getPermissions();
135 }
136
Exists(const Twine & path) const137 bool FileSystem::Exists(const Twine &path) const { return m_fs->exists(path); }
138
Exists(const FileSpec & file_spec) const139 bool FileSystem::Exists(const FileSpec &file_spec) const {
140 return file_spec && Exists(file_spec.GetPath());
141 }
142
Readable(const Twine & path) const143 bool FileSystem::Readable(const Twine &path) const {
144 return GetPermissions(path) & sys::fs::perms::all_read;
145 }
146
Readable(const FileSpec & file_spec) const147 bool FileSystem::Readable(const FileSpec &file_spec) const {
148 return file_spec && Readable(file_spec.GetPath());
149 }
150
IsDirectory(const Twine & path) const151 bool FileSystem::IsDirectory(const Twine &path) const {
152 ErrorOr<vfs::Status> status = m_fs->status(path);
153 if (!status)
154 return false;
155 return status->isDirectory();
156 }
157
IsDirectory(const FileSpec & file_spec) const158 bool FileSystem::IsDirectory(const FileSpec &file_spec) const {
159 return file_spec && IsDirectory(file_spec.GetPath());
160 }
161
IsLocal(const Twine & path) const162 bool FileSystem::IsLocal(const Twine &path) const {
163 bool b = false;
164 m_fs->isLocal(path, b);
165 return b;
166 }
167
IsLocal(const FileSpec & file_spec) const168 bool FileSystem::IsLocal(const FileSpec &file_spec) const {
169 return file_spec && IsLocal(file_spec.GetPath());
170 }
171
EnumerateDirectory(Twine path,bool find_directories,bool find_files,bool find_other,EnumerateDirectoryCallbackType callback,void * callback_baton)172 void FileSystem::EnumerateDirectory(Twine path, bool find_directories,
173 bool find_files, bool find_other,
174 EnumerateDirectoryCallbackType callback,
175 void *callback_baton) {
176 std::error_code EC;
177 vfs::recursive_directory_iterator Iter(*m_fs, path, EC);
178 vfs::recursive_directory_iterator End;
179 for (; Iter != End && !EC; Iter.increment(EC)) {
180 const auto &Item = *Iter;
181 ErrorOr<vfs::Status> Status = m_fs->status(Item.path());
182 if (!Status)
183 continue;
184 if (!find_files && Status->isRegularFile())
185 continue;
186 if (!find_directories && Status->isDirectory())
187 continue;
188 if (!find_other && Status->isOther())
189 continue;
190
191 auto Result = callback(callback_baton, Status->getType(), Item.path());
192 if (Result == eEnumerateDirectoryResultQuit)
193 return;
194 if (Result == eEnumerateDirectoryResultNext) {
195 // Default behavior is to recurse. Opt out if the callback doesn't want
196 // this behavior.
197 Iter.no_push();
198 }
199 }
200 }
201
MakeAbsolute(SmallVectorImpl<char> & path) const202 std::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const {
203 return m_fs->makeAbsolute(path);
204 }
205
MakeAbsolute(FileSpec & file_spec) const206 std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const {
207 SmallString<128> path;
208 file_spec.GetPath(path, false);
209
210 auto EC = MakeAbsolute(path);
211 if (EC)
212 return EC;
213
214 FileSpec new_file_spec(path, file_spec.GetPathStyle());
215 file_spec = new_file_spec;
216 return {};
217 }
218
GetRealPath(const Twine & path,SmallVectorImpl<char> & output) const219 std::error_code FileSystem::GetRealPath(const Twine &path,
220 SmallVectorImpl<char> &output) const {
221 return m_fs->getRealPath(path, output);
222 }
223
Resolve(SmallVectorImpl<char> & path)224 void FileSystem::Resolve(SmallVectorImpl<char> &path) {
225 if (path.empty())
226 return;
227
228 // Resolve tilde in path.
229 SmallString<128> resolved(path.begin(), path.end());
230 assert(m_tilde_resolver && "must initialize tilde resolver in constructor");
231 m_tilde_resolver->ResolveFullPath(llvm::StringRef(path.begin(), path.size()),
232 resolved);
233
234 // Try making the path absolute if it exists.
235 SmallString<128> absolute(resolved.begin(), resolved.end());
236 MakeAbsolute(absolute);
237
238 path.clear();
239 if (Exists(absolute)) {
240 path.append(absolute.begin(), absolute.end());
241 } else {
242 path.append(resolved.begin(), resolved.end());
243 }
244 }
245
Resolve(FileSpec & file_spec)246 void FileSystem::Resolve(FileSpec &file_spec) {
247 if (!file_spec)
248 return;
249
250 // Extract path from the FileSpec.
251 SmallString<128> path;
252 file_spec.GetPath(path);
253
254 // Resolve the path.
255 Resolve(path);
256
257 // Update the FileSpec with the resolved path.
258 if (file_spec.GetFilename().IsEmpty())
259 file_spec.SetDirectory(path);
260 else
261 file_spec.SetPath(path);
262 }
263
264 template <typename T>
GetMemoryBuffer(const llvm::Twine & path,uint64_t size,uint64_t offset,bool is_volatile)265 static std::unique_ptr<T> GetMemoryBuffer(const llvm::Twine &path,
266 uint64_t size, uint64_t offset,
267 bool is_volatile) {
268 std::unique_ptr<T> buffer;
269 if (size == 0) {
270 auto buffer_or_error = T::getFile(path, is_volatile);
271 if (!buffer_or_error)
272 return nullptr;
273 buffer = std::move(*buffer_or_error);
274 } else {
275 auto buffer_or_error = T::getFileSlice(path, size, offset, is_volatile);
276 if (!buffer_or_error)
277 return nullptr;
278 buffer = std::move(*buffer_or_error);
279 }
280 return buffer;
281 }
282
283 std::shared_ptr<WritableDataBuffer>
CreateWritableDataBuffer(const llvm::Twine & path,uint64_t size,uint64_t offset)284 FileSystem::CreateWritableDataBuffer(const llvm::Twine &path, uint64_t size,
285 uint64_t offset) {
286 const bool is_volatile = !IsLocal(path);
287 auto buffer = GetMemoryBuffer<llvm::WritableMemoryBuffer>(path, size, offset,
288 is_volatile);
289 if (!buffer)
290 return {};
291 return std::shared_ptr<WritableDataBufferLLVM>(
292 new WritableDataBufferLLVM(std::move(buffer)));
293 }
294
295 std::shared_ptr<DataBuffer>
CreateDataBuffer(const llvm::Twine & path,uint64_t size,uint64_t offset)296 FileSystem::CreateDataBuffer(const llvm::Twine &path, uint64_t size,
297 uint64_t offset) {
298 const bool is_volatile = !IsLocal(path);
299 auto buffer =
300 GetMemoryBuffer<llvm::MemoryBuffer>(path, size, offset, is_volatile);
301 if (!buffer)
302 return {};
303 return std::shared_ptr<DataBufferLLVM>(new DataBufferLLVM(std::move(buffer)));
304 }
305
306 std::shared_ptr<WritableDataBuffer>
CreateWritableDataBuffer(const FileSpec & file_spec,uint64_t size,uint64_t offset)307 FileSystem::CreateWritableDataBuffer(const FileSpec &file_spec, uint64_t size,
308 uint64_t offset) {
309 return CreateWritableDataBuffer(file_spec.GetPath(), size, offset);
310 }
311
312 std::shared_ptr<DataBuffer>
CreateDataBuffer(const FileSpec & file_spec,uint64_t size,uint64_t offset)313 FileSystem::CreateDataBuffer(const FileSpec &file_spec, uint64_t size,
314 uint64_t offset) {
315 return CreateDataBuffer(file_spec.GetPath(), size, offset);
316 }
317
ResolveExecutableLocation(FileSpec & file_spec)318 bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) {
319 // If the directory is set there's nothing to do.
320 ConstString directory = file_spec.GetDirectory();
321 if (directory)
322 return false;
323
324 // We cannot look for a file if there's no file name.
325 ConstString filename = file_spec.GetFilename();
326 if (!filename)
327 return false;
328
329 // Search for the file on the host.
330 const std::string filename_str(filename.GetCString());
331 llvm::ErrorOr<std::string> error_or_path =
332 llvm::sys::findProgramByName(filename_str);
333 if (!error_or_path)
334 return false;
335
336 // findProgramByName returns "." if it can't find the file.
337 llvm::StringRef path = *error_or_path;
338 llvm::StringRef parent = llvm::sys::path::parent_path(path);
339 if (parent.empty() || parent == ".")
340 return false;
341
342 // Make sure that the result exists.
343 FileSpec result(*error_or_path);
344 if (!Exists(result))
345 return false;
346
347 file_spec = result;
348 return true;
349 }
350
GetHomeDirectory(SmallVectorImpl<char> & path) const351 bool FileSystem::GetHomeDirectory(SmallVectorImpl<char> &path) const {
352 if (!m_home_directory.empty()) {
353 path.assign(m_home_directory.begin(), m_home_directory.end());
354 return true;
355 }
356 return llvm::sys::path::home_directory(path);
357 }
358
GetHomeDirectory(FileSpec & file_spec) const359 bool FileSystem::GetHomeDirectory(FileSpec &file_spec) const {
360 SmallString<128> home_dir;
361 if (!GetHomeDirectory(home_dir))
362 return false;
363 file_spec.SetPath(home_dir);
364 return true;
365 }
366
OpenWithFS(const FileSystem & fs,const char * path,int flags,int mode)367 static int OpenWithFS(const FileSystem &fs, const char *path, int flags,
368 int mode) {
369 return const_cast<FileSystem &>(fs).Open(path, flags, mode);
370 }
371
GetOpenFlags(File::OpenOptions options)372 static int GetOpenFlags(File::OpenOptions options) {
373 int open_flags = 0;
374 File::OpenOptions rw =
375 options & (File::eOpenOptionReadOnly | File::eOpenOptionWriteOnly |
376 File::eOpenOptionReadWrite);
377 if (rw == File::eOpenOptionWriteOnly || rw == File::eOpenOptionReadWrite) {
378 if (rw == File::eOpenOptionReadWrite)
379 open_flags |= O_RDWR;
380 else
381 open_flags |= O_WRONLY;
382
383 if (options & File::eOpenOptionAppend)
384 open_flags |= O_APPEND;
385
386 if (options & File::eOpenOptionTruncate)
387 open_flags |= O_TRUNC;
388
389 if (options & File::eOpenOptionCanCreate)
390 open_flags |= O_CREAT;
391
392 if (options & File::eOpenOptionCanCreateNewOnly)
393 open_flags |= O_CREAT | O_EXCL;
394 } else if (rw == File::eOpenOptionReadOnly) {
395 open_flags |= O_RDONLY;
396
397 #ifndef _WIN32
398 if (options & File::eOpenOptionDontFollowSymlinks)
399 open_flags |= O_NOFOLLOW;
400 #endif
401 }
402
403 #ifndef _WIN32
404 if (options & File::eOpenOptionNonBlocking)
405 open_flags |= O_NONBLOCK;
406 if (options & File::eOpenOptionCloseOnExec)
407 open_flags |= O_CLOEXEC;
408 #else
409 open_flags |= O_BINARY;
410 #endif
411
412 return open_flags;
413 }
414
GetOpenMode(uint32_t permissions)415 static mode_t GetOpenMode(uint32_t permissions) {
416 mode_t mode = 0;
417 if (permissions & lldb::eFilePermissionsUserRead)
418 mode |= S_IRUSR;
419 if (permissions & lldb::eFilePermissionsUserWrite)
420 mode |= S_IWUSR;
421 if (permissions & lldb::eFilePermissionsUserExecute)
422 mode |= S_IXUSR;
423 if (permissions & lldb::eFilePermissionsGroupRead)
424 mode |= S_IRGRP;
425 if (permissions & lldb::eFilePermissionsGroupWrite)
426 mode |= S_IWGRP;
427 if (permissions & lldb::eFilePermissionsGroupExecute)
428 mode |= S_IXGRP;
429 if (permissions & lldb::eFilePermissionsWorldRead)
430 mode |= S_IROTH;
431 if (permissions & lldb::eFilePermissionsWorldWrite)
432 mode |= S_IWOTH;
433 if (permissions & lldb::eFilePermissionsWorldExecute)
434 mode |= S_IXOTH;
435 return mode;
436 }
437
Open(const FileSpec & file_spec,File::OpenOptions options,uint32_t permissions,bool should_close_fd)438 Expected<FileUP> FileSystem::Open(const FileSpec &file_spec,
439 File::OpenOptions options,
440 uint32_t permissions, bool should_close_fd) {
441 const int open_flags = GetOpenFlags(options);
442 const mode_t open_mode =
443 (open_flags & O_CREAT) ? GetOpenMode(permissions) : 0;
444
445 auto path = file_spec.GetPath();
446
447 int descriptor = llvm::sys::RetryAfterSignal(
448 -1, OpenWithFS, *this, path.c_str(), open_flags, open_mode);
449
450 if (!File::DescriptorIsValid(descriptor))
451 return llvm::errorCodeToError(
452 std::error_code(errno, std::system_category()));
453
454 auto file = std::unique_ptr<File>(
455 new NativeFile(descriptor, options, should_close_fd));
456 assert(file->IsValid());
457 return std::move(file);
458 }
459
SetHomeDirectory(std::string home_directory)460 void FileSystem::SetHomeDirectory(std::string home_directory) {
461 m_home_directory = std::move(home_directory);
462 }
463
RemoveFile(const FileSpec & file_spec)464 Status FileSystem::RemoveFile(const FileSpec &file_spec) {
465 return RemoveFile(file_spec.GetPath());
466 }
467
RemoveFile(const llvm::Twine & path)468 Status FileSystem::RemoveFile(const llvm::Twine &path) {
469 return Status(llvm::sys::fs::remove(path));
470 }
471