1 //===-- FileSystem.cpp ------------------------------------------*- C++ -*-===// 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/LLDBAssert.h" 12 #include "lldb/Utility/TildeExpressionResolver.h" 13 14 #include "llvm/Support/Errno.h" 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 <errno.h> 21 #include <fcntl.h> 22 #include <limits.h> 23 #include <stdarg.h> 24 #include <stdio.h> 25 26 #ifdef _WIN32 27 #include "lldb/Host/windows/windows.h" 28 #else 29 #include <sys/ioctl.h> 30 #include <sys/stat.h> 31 #include <termios.h> 32 #include <unistd.h> 33 #endif 34 35 #include <algorithm> 36 #include <fstream> 37 #include <vector> 38 39 using namespace lldb; 40 using namespace lldb_private; 41 using namespace llvm; 42 43 FileSystem &FileSystem::Instance() { return *InstanceImpl(); } 44 45 void FileSystem::Initialize() { 46 lldbassert(!InstanceImpl() && "Already initialized."); 47 InstanceImpl().emplace(); 48 } 49 50 void FileSystem::Initialize(IntrusiveRefCntPtr<vfs::FileSystem> fs) { 51 lldbassert(!InstanceImpl() && "Already initialized."); 52 InstanceImpl().emplace(fs); 53 } 54 55 void FileSystem::Terminate() { 56 lldbassert(InstanceImpl() && "Already terminated."); 57 InstanceImpl().reset(); 58 } 59 60 Optional<FileSystem> &FileSystem::InstanceImpl() { 61 static Optional<FileSystem> g_fs; 62 return g_fs; 63 } 64 65 vfs::directory_iterator FileSystem::DirBegin(const FileSpec &file_spec, 66 std::error_code &ec) { 67 return DirBegin(file_spec.GetPath(), ec); 68 } 69 70 vfs::directory_iterator FileSystem::DirBegin(const Twine &dir, 71 std::error_code &ec) { 72 return m_fs->dir_begin(dir, ec); 73 } 74 75 llvm::ErrorOr<vfs::Status> 76 FileSystem::GetStatus(const FileSpec &file_spec) const { 77 return GetStatus(file_spec.GetPath()); 78 } 79 80 llvm::ErrorOr<vfs::Status> FileSystem::GetStatus(const Twine &path) const { 81 return m_fs->status(path); 82 } 83 84 sys::TimePoint<> 85 FileSystem::GetModificationTime(const FileSpec &file_spec) const { 86 return GetModificationTime(file_spec.GetPath()); 87 } 88 89 sys::TimePoint<> FileSystem::GetModificationTime(const Twine &path) const { 90 ErrorOr<vfs::Status> status = m_fs->status(path); 91 if (!status) 92 return sys::TimePoint<>(); 93 return status->getLastModificationTime(); 94 } 95 96 uint64_t FileSystem::GetByteSize(const FileSpec &file_spec) const { 97 return GetByteSize(file_spec.GetPath()); 98 } 99 100 uint64_t FileSystem::GetByteSize(const Twine &path) const { 101 ErrorOr<vfs::Status> status = m_fs->status(path); 102 if (!status) 103 return 0; 104 return status->getSize(); 105 } 106 107 uint32_t FileSystem::GetPermissions(const FileSpec &file_spec) const { 108 return GetPermissions(file_spec.GetPath()); 109 } 110 111 uint32_t FileSystem::GetPermissions(const FileSpec &file_spec, 112 std::error_code &ec) const { 113 return GetPermissions(file_spec.GetPath(), ec); 114 } 115 116 uint32_t FileSystem::GetPermissions(const Twine &path) const { 117 std::error_code ec; 118 return GetPermissions(path, ec); 119 } 120 121 uint32_t FileSystem::GetPermissions(const Twine &path, 122 std::error_code &ec) const { 123 ErrorOr<vfs::Status> status = m_fs->status(path); 124 if (!status) { 125 ec = status.getError(); 126 return sys::fs::perms::perms_not_known; 127 } 128 return status->getPermissions(); 129 } 130 131 bool FileSystem::Exists(const Twine &path) const { return m_fs->exists(path); } 132 133 bool FileSystem::Exists(const FileSpec &file_spec) const { 134 return Exists(file_spec.GetPath()); 135 } 136 137 bool FileSystem::Readable(const Twine &path) const { 138 return GetPermissions(path) & sys::fs::perms::all_read; 139 } 140 141 bool FileSystem::Readable(const FileSpec &file_spec) const { 142 return Readable(file_spec.GetPath()); 143 } 144 145 bool FileSystem::IsDirectory(const Twine &path) const { 146 ErrorOr<vfs::Status> status = m_fs->status(path); 147 if (!status) 148 return false; 149 return status->isDirectory(); 150 } 151 152 bool FileSystem::IsDirectory(const FileSpec &file_spec) const { 153 return IsDirectory(file_spec.GetPath()); 154 } 155 156 bool FileSystem::IsLocal(const Twine &path) const { 157 bool b = false; 158 m_fs->isLocal(path, b); 159 return b; 160 } 161 162 bool FileSystem::IsLocal(const FileSpec &file_spec) const { 163 return IsLocal(file_spec.GetPath()); 164 } 165 166 void FileSystem::EnumerateDirectory(Twine path, bool find_directories, 167 bool find_files, bool find_other, 168 EnumerateDirectoryCallbackType callback, 169 void *callback_baton) { 170 std::error_code EC; 171 vfs::recursive_directory_iterator Iter(*m_fs, path, EC); 172 vfs::recursive_directory_iterator End; 173 for (; Iter != End && !EC; Iter.increment(EC)) { 174 const auto &Item = *Iter; 175 ErrorOr<vfs::Status> Status = m_fs->status(Item.path()); 176 if (!Status) 177 break; 178 if (!find_files && Status->isRegularFile()) 179 continue; 180 if (!find_directories && Status->isDirectory()) 181 continue; 182 if (!find_other && Status->isOther()) 183 continue; 184 185 auto Result = callback(callback_baton, Status->getType(), Item.path()); 186 if (Result == eEnumerateDirectoryResultQuit) 187 return; 188 if (Result == eEnumerateDirectoryResultNext) { 189 // Default behavior is to recurse. Opt out if the callback doesn't want 190 // this behavior. 191 Iter.no_push(); 192 } 193 } 194 } 195 196 std::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const { 197 return m_fs->makeAbsolute(path); 198 } 199 200 std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const { 201 SmallString<128> path; 202 file_spec.GetPath(path, false); 203 204 auto EC = MakeAbsolute(path); 205 if (EC) 206 return EC; 207 208 FileSpec new_file_spec(path, file_spec.GetPathStyle()); 209 file_spec = new_file_spec; 210 return {}; 211 } 212 213 std::error_code FileSystem::GetRealPath(const Twine &path, 214 SmallVectorImpl<char> &output) const { 215 return m_fs->getRealPath(path, output); 216 } 217 218 void FileSystem::Resolve(SmallVectorImpl<char> &path) { 219 if (path.empty()) 220 return; 221 222 // Resolve tilde. 223 SmallString<128> original_path(path.begin(), path.end()); 224 StandardTildeExpressionResolver Resolver; 225 Resolver.ResolveFullPath(original_path, path); 226 227 // Try making the path absolute if it exists. 228 SmallString<128> absolute_path(path.begin(), path.end()); 229 MakeAbsolute(path); 230 if (!Exists(path)) { 231 path.clear(); 232 path.append(original_path.begin(), original_path.end()); 233 } 234 } 235 236 void FileSystem::Resolve(FileSpec &file_spec) { 237 // Extract path from the FileSpec. 238 SmallString<128> path; 239 file_spec.GetPath(path); 240 241 // Resolve the path. 242 Resolve(path); 243 244 // Update the FileSpec with the resolved path. 245 file_spec.SetPath(path); 246 file_spec.SetIsResolved(true); 247 } 248 249 std::shared_ptr<DataBufferLLVM> 250 FileSystem::CreateDataBuffer(const llvm::Twine &path, uint64_t size, 251 uint64_t offset) { 252 const bool is_volatile = !IsLocal(path); 253 254 std::unique_ptr<llvm::WritableMemoryBuffer> buffer; 255 if (size == 0) { 256 auto buffer_or_error = 257 llvm::WritableMemoryBuffer::getFile(path, -1, is_volatile); 258 if (!buffer_or_error) 259 return nullptr; 260 buffer = std::move(*buffer_or_error); 261 } else { 262 auto buffer_or_error = llvm::WritableMemoryBuffer::getFileSlice( 263 path, size, offset, is_volatile); 264 if (!buffer_or_error) 265 return nullptr; 266 buffer = std::move(*buffer_or_error); 267 } 268 return std::shared_ptr<DataBufferLLVM>(new DataBufferLLVM(std::move(buffer))); 269 } 270 271 std::shared_ptr<DataBufferLLVM> 272 FileSystem::CreateDataBuffer(const FileSpec &file_spec, uint64_t size, 273 uint64_t offset) { 274 return CreateDataBuffer(file_spec.GetPath(), size, offset); 275 } 276 277 bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) { 278 // If the directory is set there's nothing to do. 279 const ConstString &directory = file_spec.GetDirectory(); 280 if (directory) 281 return false; 282 283 // We cannot look for a file if there's no file name. 284 const ConstString &filename = file_spec.GetFilename(); 285 if (!filename) 286 return false; 287 288 // Search for the file on the host. 289 const std::string filename_str(filename.GetCString()); 290 llvm::ErrorOr<std::string> error_or_path = 291 llvm::sys::findProgramByName(filename_str); 292 if (!error_or_path) 293 return false; 294 295 // findProgramByName returns "." if it can't find the file. 296 llvm::StringRef path = *error_or_path; 297 llvm::StringRef parent = llvm::sys::path::parent_path(path); 298 if (parent.empty() || parent == ".") 299 return false; 300 301 // Make sure that the result exists. 302 FileSpec result(*error_or_path); 303 if (!Exists(result)) 304 return false; 305 306 file_spec = result; 307 return true; 308 } 309 310 static int OpenWithFS(const FileSystem &fs, const char *path, int flags, 311 int mode) { 312 return const_cast<FileSystem &>(fs).Open(path, flags, mode); 313 } 314 315 static int GetOpenFlags(uint32_t options) { 316 const bool read = options & File::eOpenOptionRead; 317 const bool write = options & File::eOpenOptionWrite; 318 319 int open_flags = 0; 320 if (write) { 321 if (read) 322 open_flags |= O_RDWR; 323 else 324 open_flags |= O_WRONLY; 325 326 if (options & File::eOpenOptionAppend) 327 open_flags |= O_APPEND; 328 329 if (options & File::eOpenOptionTruncate) 330 open_flags |= O_TRUNC; 331 332 if (options & File::eOpenOptionCanCreate) 333 open_flags |= O_CREAT; 334 335 if (options & File::eOpenOptionCanCreateNewOnly) 336 open_flags |= O_CREAT | O_EXCL; 337 } else if (read) { 338 open_flags |= O_RDONLY; 339 340 #ifndef _WIN32 341 if (options & File::eOpenOptionDontFollowSymlinks) 342 open_flags |= O_NOFOLLOW; 343 #endif 344 } 345 346 #ifndef _WIN32 347 if (options & File::eOpenOptionNonBlocking) 348 open_flags |= O_NONBLOCK; 349 if (options & File::eOpenOptionCloseOnExec) 350 open_flags |= O_CLOEXEC; 351 #else 352 open_flags |= O_BINARY; 353 #endif 354 355 return open_flags; 356 } 357 358 static mode_t GetOpenMode(uint32_t permissions) { 359 mode_t mode = 0; 360 if (permissions & lldb::eFilePermissionsUserRead) 361 mode |= S_IRUSR; 362 if (permissions & lldb::eFilePermissionsUserWrite) 363 mode |= S_IWUSR; 364 if (permissions & lldb::eFilePermissionsUserExecute) 365 mode |= S_IXUSR; 366 if (permissions & lldb::eFilePermissionsGroupRead) 367 mode |= S_IRGRP; 368 if (permissions & lldb::eFilePermissionsGroupWrite) 369 mode |= S_IWGRP; 370 if (permissions & lldb::eFilePermissionsGroupExecute) 371 mode |= S_IXGRP; 372 if (permissions & lldb::eFilePermissionsWorldRead) 373 mode |= S_IROTH; 374 if (permissions & lldb::eFilePermissionsWorldWrite) 375 mode |= S_IWOTH; 376 if (permissions & lldb::eFilePermissionsWorldExecute) 377 mode |= S_IXOTH; 378 return mode; 379 } 380 381 Status FileSystem::Open(File &File, const FileSpec &file_spec, uint32_t options, 382 uint32_t permissions) { 383 if (File.IsValid()) 384 File.Close(); 385 386 const int open_flags = GetOpenFlags(options); 387 const mode_t open_mode = 388 (open_flags & O_CREAT) ? GetOpenMode(permissions) : 0; 389 const std::string path = file_spec.GetPath(); 390 391 int descriptor = llvm::sys::RetryAfterSignal( 392 -1, OpenWithFS, *this, path.c_str(), open_flags, open_mode); 393 394 Status error; 395 if (!File::DescriptorIsValid(descriptor)) { 396 File.SetDescriptor(descriptor, false); 397 error.SetErrorToErrno(); 398 } else { 399 File.SetDescriptor(descriptor, true); 400 File.SetOptions(options); 401 } 402 return error; 403 } 404