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 void FileSystem::EnumerateDirectory(Twine path, bool find_directories, 128 bool find_files, bool find_other, 129 EnumerateDirectoryCallbackType callback, 130 void *callback_baton) { 131 std::error_code EC; 132 vfs::recursive_directory_iterator Iter(*m_fs, path, EC); 133 vfs::recursive_directory_iterator End; 134 for (; Iter != End && !EC; Iter.increment(EC)) { 135 const auto &Item = *Iter; 136 ErrorOr<vfs::Status> Status = m_fs->status(Item.path()); 137 if (!Status) 138 break; 139 if (!find_files && Status->isRegularFile()) 140 continue; 141 if (!find_directories && Status->isDirectory()) 142 continue; 143 if (!find_other && Status->isOther()) 144 continue; 145 146 auto Result = callback(callback_baton, Status->getType(), Item.path()); 147 if (Result == eEnumerateDirectoryResultQuit) 148 return; 149 if (Result == eEnumerateDirectoryResultNext) { 150 // Default behavior is to recurse. Opt out if the callback doesn't want 151 // this behavior. 152 Iter.no_push(); 153 } 154 } 155 } 156 157 std::error_code FileSystem::MakeAbsolute(SmallVectorImpl<char> &path) const { 158 return m_fs->makeAbsolute(path); 159 } 160 161 std::error_code FileSystem::MakeAbsolute(FileSpec &file_spec) const { 162 SmallString<128> path; 163 file_spec.GetPath(path, false); 164 165 auto EC = MakeAbsolute(path); 166 if (EC) 167 return EC; 168 169 FileSpec new_file_spec(path, file_spec.GetPathStyle()); 170 file_spec = new_file_spec; 171 return {}; 172 } 173 174 std::error_code FileSystem::GetRealPath(const Twine &path, 175 SmallVectorImpl<char> &output) const { 176 return m_fs->getRealPath(path, output); 177 } 178 179 void FileSystem::Resolve(SmallVectorImpl<char> &path) { 180 if (path.empty()) 181 return; 182 183 // Resolve tilde. 184 SmallString<128> original_path(path.begin(), path.end()); 185 StandardTildeExpressionResolver Resolver; 186 Resolver.ResolveFullPath(original_path, path); 187 188 // Try making the path absolute if it exists. 189 SmallString<128> absolute_path(path.begin(), path.end()); 190 MakeAbsolute(path); 191 if (!Exists(path)) { 192 path.clear(); 193 path.append(original_path.begin(), original_path.end()); 194 } 195 } 196 197 void FileSystem::Resolve(FileSpec &file_spec) { 198 // Extract path from the FileSpec. 199 SmallString<128> path; 200 file_spec.GetPath(path); 201 202 // Resolve the path. 203 Resolve(path); 204 205 // Update the FileSpec with the resolved path. 206 file_spec.SetPath(path); 207 file_spec.SetIsResolved(true); 208 } 209 210 bool FileSystem::ResolveExecutableLocation(FileSpec &file_spec) { 211 // If the directory is set there's nothing to do. 212 const ConstString &directory = file_spec.GetDirectory(); 213 if (directory) 214 return false; 215 216 // We cannot look for a file if there's no file name. 217 const ConstString &filename = file_spec.GetFilename(); 218 if (!filename) 219 return false; 220 221 // Search for the file on the host. 222 const std::string filename_str(filename.GetCString()); 223 llvm::ErrorOr<std::string> error_or_path = 224 llvm::sys::findProgramByName(filename_str); 225 if (!error_or_path) 226 return false; 227 228 // findProgramByName returns "." if it can't find the file. 229 llvm::StringRef path = *error_or_path; 230 llvm::StringRef parent = llvm::sys::path::parent_path(path); 231 if (parent.empty() || parent == ".") 232 return false; 233 234 // Make sure that the result exists. 235 FileSpec result(*error_or_path); 236 if (!Exists(result)) 237 return false; 238 239 file_spec = result; 240 return true; 241 } 242 243 static int OpenWithFS(const FileSystem &fs, const char *path, int flags, 244 int mode) { 245 return const_cast<FileSystem &>(fs).Open(path, flags, mode); 246 } 247 248 static int GetOpenFlags(uint32_t options) { 249 const bool read = options & File::eOpenOptionRead; 250 const bool write = options & File::eOpenOptionWrite; 251 252 int open_flags = 0; 253 if (write) { 254 if (read) 255 open_flags |= O_RDWR; 256 else 257 open_flags |= O_WRONLY; 258 259 if (options & File::eOpenOptionAppend) 260 open_flags |= O_APPEND; 261 262 if (options & File::eOpenOptionTruncate) 263 open_flags |= O_TRUNC; 264 265 if (options & File::eOpenOptionCanCreate) 266 open_flags |= O_CREAT; 267 268 if (options & File::eOpenOptionCanCreateNewOnly) 269 open_flags |= O_CREAT | O_EXCL; 270 } else if (read) { 271 open_flags |= O_RDONLY; 272 273 #ifndef _WIN32 274 if (options & File::eOpenOptionDontFollowSymlinks) 275 open_flags |= O_NOFOLLOW; 276 #endif 277 } 278 279 #ifndef _WIN32 280 if (options & File::eOpenOptionNonBlocking) 281 open_flags |= O_NONBLOCK; 282 if (options & File::eOpenOptionCloseOnExec) 283 open_flags |= O_CLOEXEC; 284 #else 285 open_flags |= O_BINARY; 286 #endif 287 288 return open_flags; 289 } 290 291 static mode_t GetOpenMode(uint32_t permissions) { 292 mode_t mode = 0; 293 if (permissions & lldb::eFilePermissionsUserRead) 294 mode |= S_IRUSR; 295 if (permissions & lldb::eFilePermissionsUserWrite) 296 mode |= S_IWUSR; 297 if (permissions & lldb::eFilePermissionsUserExecute) 298 mode |= S_IXUSR; 299 if (permissions & lldb::eFilePermissionsGroupRead) 300 mode |= S_IRGRP; 301 if (permissions & lldb::eFilePermissionsGroupWrite) 302 mode |= S_IWGRP; 303 if (permissions & lldb::eFilePermissionsGroupExecute) 304 mode |= S_IXGRP; 305 if (permissions & lldb::eFilePermissionsWorldRead) 306 mode |= S_IROTH; 307 if (permissions & lldb::eFilePermissionsWorldWrite) 308 mode |= S_IWOTH; 309 if (permissions & lldb::eFilePermissionsWorldExecute) 310 mode |= S_IXOTH; 311 return mode; 312 } 313 314 Status FileSystem::Open(File &File, const FileSpec &file_spec, uint32_t options, 315 uint32_t permissions) { 316 if (File.IsValid()) 317 File.Close(); 318 319 const int open_flags = GetOpenFlags(options); 320 const mode_t open_mode = 321 (open_flags & O_CREAT) ? GetOpenMode(permissions) : 0; 322 const std::string path = file_spec.GetPath(); 323 324 int descriptor = llvm::sys::RetryAfterSignal( 325 -1, OpenWithFS, *this, path.c_str(), open_flags, open_mode); 326 327 Status error; 328 if (!File::DescriptorIsValid(descriptor)) { 329 File.SetDescriptor(descriptor, false); 330 error.SetErrorToErrno(); 331 } else { 332 File.SetDescriptor(descriptor, true); 333 File.SetOptions(options); 334 } 335 return error; 336 } 337