1c00cf4a0SZachary Turner //===-- FileSystem.cpp ------------------------------------------*- C++ -*-===// 2c00cf4a0SZachary Turner // 3c00cf4a0SZachary Turner // The LLVM Compiler Infrastructure 4c00cf4a0SZachary Turner // 5c00cf4a0SZachary Turner // This file is distributed under the University of Illinois Open Source 6c00cf4a0SZachary Turner // License. See LICENSE.TXT for details. 7c00cf4a0SZachary Turner // 8c00cf4a0SZachary Turner //===----------------------------------------------------------------------===// 9c00cf4a0SZachary Turner 10c00cf4a0SZachary Turner #include "lldb/Host/windows/windows.h" 11c00cf4a0SZachary Turner 12c00cf4a0SZachary Turner #include <shellapi.h> 13633594c2SAdrian McCarthy #include <sys/stat.h> 14633594c2SAdrian McCarthy #include <sys/types.h> 15c00cf4a0SZachary Turner 16c00cf4a0SZachary Turner #include "lldb/Host/FileSystem.h" 172b38f33bSOleksiy Vyalov #include "lldb/Host/windows/AutoHandle.h" 18*190fadcdSZachary Turner 19*190fadcdSZachary Turner #include "llvm/Support/ConvertUTF.h" 20cc815568SOleksiy Vyalov #include "llvm/Support/FileSystem.h" 21c00cf4a0SZachary Turner 22c00cf4a0SZachary Turner using namespace lldb_private; 23c00cf4a0SZachary Turner 244eff2d31SZachary Turner const char * 254eff2d31SZachary Turner FileSystem::DEV_NULL = "nul"; 264eff2d31SZachary Turner 27*190fadcdSZachary Turner const char *FileSystem::PATH_CONVERSION_ERROR = "Error converting path between UTF-8 and native encoding"; 28*190fadcdSZachary Turner 29c00cf4a0SZachary Turner FileSpec::PathSyntax 30c00cf4a0SZachary Turner FileSystem::GetNativePathSyntax() 31c00cf4a0SZachary Turner { 32c00cf4a0SZachary Turner return FileSpec::ePathSyntaxWindows; 33c00cf4a0SZachary Turner } 34c00cf4a0SZachary Turner 35c00cf4a0SZachary Turner Error 36d3173f34SChaoren Lin FileSystem::MakeDirectory(const FileSpec &file_spec, uint32_t file_permissions) 37c00cf4a0SZachary Turner { 38c00cf4a0SZachary Turner // On Win32, the mode parameter is ignored, as Windows files and directories support a 39c00cf4a0SZachary Turner // different permission model than POSIX. 40c00cf4a0SZachary Turner Error error; 41d3173f34SChaoren Lin const auto err_code = llvm::sys::fs::create_directories(file_spec.GetPath(), true); 42cc815568SOleksiy Vyalov if (err_code) 43cc815568SOleksiy Vyalov { 44cc815568SOleksiy Vyalov error.SetErrorString(err_code.message().c_str()); 45cc815568SOleksiy Vyalov } 46cc815568SOleksiy Vyalov 47c00cf4a0SZachary Turner return error; 48c00cf4a0SZachary Turner } 49c00cf4a0SZachary Turner 50c00cf4a0SZachary Turner Error 51d3173f34SChaoren Lin FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse) 52c00cf4a0SZachary Turner { 53c00cf4a0SZachary Turner Error error; 54*190fadcdSZachary Turner std::wstring path_buffer; 55*190fadcdSZachary Turner if (!llvm::ConvertUTF8toWide(file_spec.GetPath(), path_buffer)) 56*190fadcdSZachary Turner { 57*190fadcdSZachary Turner error.SetErrorString(PATH_CONVERSION_ERROR); 58*190fadcdSZachary Turner return error; 59*190fadcdSZachary Turner } 60c00cf4a0SZachary Turner if (!recurse) 61c00cf4a0SZachary Turner { 62*190fadcdSZachary Turner BOOL result = ::RemoveDirectoryW(path_buffer.c_str()); 63c00cf4a0SZachary Turner if (!result) 64c00cf4a0SZachary Turner error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 65c00cf4a0SZachary Turner } 66c00cf4a0SZachary Turner else 67c00cf4a0SZachary Turner { 68c00cf4a0SZachary Turner // SHFileOperation() accepts a list of paths, and so must be double-null-terminated to 69*190fadcdSZachary Turner // indicate the end of the list. The first null terminator is there only in the backing 70*190fadcdSZachary Turner // store but not the actual vector contents, and so we need to push twice. 71*190fadcdSZachary Turner path_buffer.push_back(0); 72c00cf4a0SZachary Turner path_buffer.push_back(0); 73c00cf4a0SZachary Turner 74*190fadcdSZachary Turner SHFILEOPSTRUCTW shfos = {0}; 75c00cf4a0SZachary Turner shfos.wFunc = FO_DELETE; 76*190fadcdSZachary Turner shfos.pFrom = (LPCWSTR)path_buffer.data(); 77c00cf4a0SZachary Turner shfos.fFlags = FOF_NO_UI; 78c00cf4a0SZachary Turner 79*190fadcdSZachary Turner int result = ::SHFileOperationW(&shfos); 80c00cf4a0SZachary Turner // TODO(zturner): Correctly handle the intricacies of SHFileOperation return values. 81c00cf4a0SZachary Turner if (result != 0) 82c00cf4a0SZachary Turner error.SetErrorStringWithFormat("SHFileOperation failed"); 83c00cf4a0SZachary Turner } 84c00cf4a0SZachary Turner return error; 85c00cf4a0SZachary Turner } 86c00cf4a0SZachary Turner 87c00cf4a0SZachary Turner Error 88d3173f34SChaoren Lin FileSystem::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions) 89c00cf4a0SZachary Turner { 90c00cf4a0SZachary Turner Error error; 91633594c2SAdrian McCarthy // Beware that Windows's permission model is different from Unix's, and it's 92633594c2SAdrian McCarthy // not clear if this API is supposed to check ACLs. To match the caller's 93633594c2SAdrian McCarthy // expectations as closely as possible, we'll use Microsoft's _stat, which 94633594c2SAdrian McCarthy // attempts to emulate POSIX stat. This should be good enough for basic 95633594c2SAdrian McCarthy // checks like FileSpec::Readable. 96633594c2SAdrian McCarthy struct _stat file_stats; 97633594c2SAdrian McCarthy if (::_stat(file_spec.GetCString(), &file_stats) == 0) 98633594c2SAdrian McCarthy { 99633594c2SAdrian McCarthy // The owner permission bits in "st_mode" currently match the definitions 100633594c2SAdrian McCarthy // for the owner file mode bits. 101633594c2SAdrian McCarthy file_permissions = file_stats.st_mode & (_S_IREAD | _S_IWRITE | _S_IEXEC); 102633594c2SAdrian McCarthy } 103633594c2SAdrian McCarthy else 104633594c2SAdrian McCarthy { 105633594c2SAdrian McCarthy error.SetErrorToErrno(); 106633594c2SAdrian McCarthy } 107633594c2SAdrian McCarthy 108c00cf4a0SZachary Turner return error; 109c00cf4a0SZachary Turner } 110c00cf4a0SZachary Turner 111c00cf4a0SZachary Turner Error 112d3173f34SChaoren Lin FileSystem::SetFilePermissions(const FileSpec &file_spec, uint32_t file_permissions) 113c00cf4a0SZachary Turner { 114c00cf4a0SZachary Turner Error error; 115c00cf4a0SZachary Turner error.SetErrorStringWithFormat("%s is not supported on this host", __PRETTY_FUNCTION__); 116c00cf4a0SZachary Turner return error; 117c00cf4a0SZachary Turner } 118c00cf4a0SZachary Turner 119c00cf4a0SZachary Turner lldb::user_id_t 120c00cf4a0SZachary Turner FileSystem::GetFileSize(const FileSpec &file_spec) 121c00cf4a0SZachary Turner { 122c00cf4a0SZachary Turner return file_spec.GetByteSize(); 123c00cf4a0SZachary Turner } 124c00cf4a0SZachary Turner 125c00cf4a0SZachary Turner bool 126c00cf4a0SZachary Turner FileSystem::GetFileExists(const FileSpec &file_spec) 127c00cf4a0SZachary Turner { 128c00cf4a0SZachary Turner return file_spec.Exists(); 129c00cf4a0SZachary Turner } 130c00cf4a0SZachary Turner 131c00cf4a0SZachary Turner Error 132d3173f34SChaoren Lin FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst) 133a9ea0711SOleksiy Vyalov { 134a9ea0711SOleksiy Vyalov Error error; 135*190fadcdSZachary Turner std::wstring wsrc, wdst; 136*190fadcdSZachary Turner if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) || !llvm::ConvertUTF8toWide(dst.GetCString(), wdst)) 137*190fadcdSZachary Turner error.SetErrorString(PATH_CONVERSION_ERROR); 138*190fadcdSZachary Turner else if (!::CreateHardLinkW(wsrc.c_str(), wdst.c_str(), nullptr)) 139a9ea0711SOleksiy Vyalov error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 140a9ea0711SOleksiy Vyalov return error; 141a9ea0711SOleksiy Vyalov } 142a9ea0711SOleksiy Vyalov 1432b38f33bSOleksiy Vyalov int 1442b38f33bSOleksiy Vyalov FileSystem::GetHardlinkCount(const FileSpec &file_spec) 1452b38f33bSOleksiy Vyalov { 146*190fadcdSZachary Turner std::wstring path; 147*190fadcdSZachary Turner if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path)) 148*190fadcdSZachary Turner return -1; 149*190fadcdSZachary Turner 150*190fadcdSZachary Turner HANDLE file_handle = ::CreateFileW(path.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 151*190fadcdSZachary Turner FILE_ATTRIBUTE_NORMAL, nullptr); 1522b38f33bSOleksiy Vyalov 1532b38f33bSOleksiy Vyalov if (file_handle == INVALID_HANDLE_VALUE) 1542b38f33bSOleksiy Vyalov return -1; 1552b38f33bSOleksiy Vyalov 1562b38f33bSOleksiy Vyalov AutoHandle auto_file_handle(file_handle); 1572b38f33bSOleksiy Vyalov BY_HANDLE_FILE_INFORMATION file_info; 1582b38f33bSOleksiy Vyalov if (::GetFileInformationByHandle(file_handle, &file_info)) 1592b38f33bSOleksiy Vyalov return file_info.nNumberOfLinks; 1602b38f33bSOleksiy Vyalov 1612b38f33bSOleksiy Vyalov return -1; 1622b38f33bSOleksiy Vyalov } 1632b38f33bSOleksiy Vyalov 164a9ea0711SOleksiy Vyalov Error 165d3173f34SChaoren Lin FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) 166c00cf4a0SZachary Turner { 167c00cf4a0SZachary Turner Error error; 168*190fadcdSZachary Turner std::wstring wsrc, wdst; 169*190fadcdSZachary Turner if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) || !llvm::ConvertUTF8toWide(dst.GetCString(), wdst)) 170*190fadcdSZachary Turner error.SetErrorString(PATH_CONVERSION_ERROR); 171*190fadcdSZachary Turner if (error.Fail()) 172*190fadcdSZachary Turner return error; 173*190fadcdSZachary Turner DWORD attrib = ::GetFileAttributesW(wdst.c_str()); 174c00cf4a0SZachary Turner if (attrib == INVALID_FILE_ATTRIBUTES) 175c00cf4a0SZachary Turner { 176c00cf4a0SZachary Turner error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 177c00cf4a0SZachary Turner return error; 178c00cf4a0SZachary Turner } 179c00cf4a0SZachary Turner bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY); 180c00cf4a0SZachary Turner DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0; 181*190fadcdSZachary Turner BOOL result = ::CreateSymbolicLinkW(wsrc.c_str(), wdst.c_str(), flag); 182c00cf4a0SZachary Turner if (!result) 183c00cf4a0SZachary Turner error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 184c00cf4a0SZachary Turner return error; 185c00cf4a0SZachary Turner } 186c00cf4a0SZachary Turner 187c00cf4a0SZachary Turner Error 188d3173f34SChaoren Lin FileSystem::Unlink(const FileSpec &file_spec) 189c00cf4a0SZachary Turner { 190c00cf4a0SZachary Turner Error error; 191*190fadcdSZachary Turner std::wstring path; 192*190fadcdSZachary Turner if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path)) 193*190fadcdSZachary Turner { 194*190fadcdSZachary Turner error.SetErrorString(PATH_CONVERSION_ERROR); 195*190fadcdSZachary Turner return error; 196*190fadcdSZachary Turner } 197*190fadcdSZachary Turner BOOL result = ::DeleteFileW(path.c_str()); 198c00cf4a0SZachary Turner if (!result) 199c00cf4a0SZachary Turner error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 200c00cf4a0SZachary Turner return error; 201c00cf4a0SZachary Turner } 202c00cf4a0SZachary Turner 203c00cf4a0SZachary Turner Error 204d3173f34SChaoren Lin FileSystem::Readlink(const FileSpec &src, FileSpec &dst) 205c00cf4a0SZachary Turner { 206c00cf4a0SZachary Turner Error error; 207*190fadcdSZachary Turner std::wstring wsrc; 208*190fadcdSZachary Turner if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc)) 209*190fadcdSZachary Turner { 210*190fadcdSZachary Turner error.SetErrorString(PATH_CONVERSION_ERROR); 211*190fadcdSZachary Turner return error; 212*190fadcdSZachary Turner } 213*190fadcdSZachary Turner 214*190fadcdSZachary Turner HANDLE h = ::CreateFileW(wsrc.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 215c00cf4a0SZachary Turner FILE_FLAG_OPEN_REPARSE_POINT, NULL); 216c00cf4a0SZachary Turner if (h == INVALID_HANDLE_VALUE) 217c00cf4a0SZachary Turner { 218c00cf4a0SZachary Turner error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 219c00cf4a0SZachary Turner return error; 220c00cf4a0SZachary Turner } 221c00cf4a0SZachary Turner 222*190fadcdSZachary Turner std::vector<wchar_t> buf(PATH_MAX + 1); 223c00cf4a0SZachary Turner // Subtract 1 from the path length since this function does not add a null terminator. 224*190fadcdSZachary Turner DWORD result = ::GetFinalPathNameByHandleW(h, buf.data(), buf.size() - 1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); 225*190fadcdSZachary Turner std::string path; 226c00cf4a0SZachary Turner if (result == 0) 227c00cf4a0SZachary Turner error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 228*190fadcdSZachary Turner else if (!llvm::convertWideToUTF8(buf.data(), path)) 229*190fadcdSZachary Turner error.SetErrorString(PATH_CONVERSION_ERROR); 230d3173f34SChaoren Lin else 231*190fadcdSZachary Turner dst.SetFile(path, false); 232c00cf4a0SZachary Turner 233c00cf4a0SZachary Turner ::CloseHandle(h); 234c00cf4a0SZachary Turner return error; 235c00cf4a0SZachary Turner } 236736888c8SGreg Clayton 2379077e9f8SSean Callanan Error 2389077e9f8SSean Callanan FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst) 2399077e9f8SSean Callanan { 2409077e9f8SSean Callanan return Error("ResolveSymbolicLink() isn't implemented on Windows"); 2419077e9f8SSean Callanan } 2429077e9f8SSean Callanan 243736888c8SGreg Clayton bool 244736888c8SGreg Clayton FileSystem::IsLocal(const FileSpec &spec) 245736888c8SGreg Clayton { 246736888c8SGreg Clayton if (spec) 247736888c8SGreg Clayton { 248736888c8SGreg Clayton // TODO: return true if the file is on a locally mounted file system 249736888c8SGreg Clayton return true; 250736888c8SGreg Clayton } 251736888c8SGreg Clayton 252736888c8SGreg Clayton return false; 253736888c8SGreg Clayton } 254*190fadcdSZachary Turner 255*190fadcdSZachary Turner FILE * 256*190fadcdSZachary Turner FileSystem::Fopen(const char *path, const char *mode) 257*190fadcdSZachary Turner { 258*190fadcdSZachary Turner std::wstring wpath, wmode; 259*190fadcdSZachary Turner if (!llvm::ConvertUTF8toWide(path, wpath)) 260*190fadcdSZachary Turner return nullptr; 261*190fadcdSZachary Turner if (!llvm::ConvertUTF8toWide(mode, wmode)) 262*190fadcdSZachary Turner return nullptr; 263*190fadcdSZachary Turner FILE *file; 264*190fadcdSZachary Turner if (_wfopen_s(&file, wpath.c_str(), wmode.c_str()) != 0) 265*190fadcdSZachary Turner return nullptr; 266*190fadcdSZachary Turner return file; 267*190fadcdSZachary Turner } 268*190fadcdSZachary Turner 269*190fadcdSZachary Turner int 270*190fadcdSZachary Turner FileSystem::Stat(const char *path, struct stat *stats) 271*190fadcdSZachary Turner { 272*190fadcdSZachary Turner std::wstring wpath; 273*190fadcdSZachary Turner if (!llvm::ConvertUTF8toWide(path, wpath)) 274*190fadcdSZachary Turner { 275*190fadcdSZachary Turner errno = EINVAL; 276*190fadcdSZachary Turner return -EINVAL; 277*190fadcdSZachary Turner } 278*190fadcdSZachary Turner int stat_result; 279*190fadcdSZachary Turner #ifdef _USE_32BIT_TIME_T 280*190fadcdSZachary Turner struct _stat32 file_stats; 281*190fadcdSZachary Turner stat_result = ::_wstat32(wpath.c_str(), &file_stats); 282*190fadcdSZachary Turner #else 283*190fadcdSZachary Turner struct _stat64i32 file_stats; 284*190fadcdSZachary Turner stat_result = ::_wstat64i32(wpath.c_str(), &file_stats); 285*190fadcdSZachary Turner #endif 286*190fadcdSZachary Turner if (stat_result == 0) 287*190fadcdSZachary Turner { 288*190fadcdSZachary Turner static_assert(sizeof(struct stat) == sizeof(file_stats), 289*190fadcdSZachary Turner "stat and _stat32/_stat64i32 must have the same layout"); 290*190fadcdSZachary Turner *stats = *reinterpret_cast<struct stat *>(&file_stats); 291*190fadcdSZachary Turner } 292*190fadcdSZachary Turner return stat_result; 293*190fadcdSZachary Turner } 294