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