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" 18190fadcdSZachary Turner 19190fadcdSZachary Turner #include "llvm/Support/ConvertUTF.h" 20cc815568SOleksiy Vyalov #include "llvm/Support/FileSystem.h" 21c00cf4a0SZachary Turner 22c00cf4a0SZachary Turner using namespace lldb_private; 23c00cf4a0SZachary Turner 24*b9c1b51eSKate Stone const char *FileSystem::DEV_NULL = "nul"; 254eff2d31SZachary Turner 26*b9c1b51eSKate Stone const char *FileSystem::PATH_CONVERSION_ERROR = 27*b9c1b51eSKate Stone "Error converting path between UTF-8 and native encoding"; 28190fadcdSZachary Turner 29*b9c1b51eSKate Stone FileSpec::PathSyntax FileSystem::GetNativePathSyntax() { 30c00cf4a0SZachary Turner return FileSpec::ePathSyntaxWindows; 31c00cf4a0SZachary Turner } 32c00cf4a0SZachary Turner 33*b9c1b51eSKate Stone Error FileSystem::MakeDirectory(const FileSpec &file_spec, 34*b9c1b51eSKate Stone uint32_t file_permissions) { 35*b9c1b51eSKate Stone // On Win32, the mode parameter is ignored, as Windows files and directories 36*b9c1b51eSKate Stone // support a 37c00cf4a0SZachary Turner // different permission model than POSIX. 38c00cf4a0SZachary Turner Error error; 39*b9c1b51eSKate Stone const auto err_code = 40*b9c1b51eSKate Stone llvm::sys::fs::create_directories(file_spec.GetPath(), true); 41*b9c1b51eSKate Stone if (err_code) { 42cc815568SOleksiy Vyalov error.SetErrorString(err_code.message().c_str()); 43cc815568SOleksiy Vyalov } 44cc815568SOleksiy Vyalov 45c00cf4a0SZachary Turner return error; 46c00cf4a0SZachary Turner } 47c00cf4a0SZachary Turner 48*b9c1b51eSKate Stone Error FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse) { 49c00cf4a0SZachary Turner Error error; 50190fadcdSZachary Turner std::wstring path_buffer; 51*b9c1b51eSKate Stone if (!llvm::ConvertUTF8toWide(file_spec.GetPath(), path_buffer)) { 52190fadcdSZachary Turner error.SetErrorString(PATH_CONVERSION_ERROR); 53190fadcdSZachary Turner return error; 54190fadcdSZachary Turner } 55*b9c1b51eSKate Stone if (!recurse) { 56190fadcdSZachary Turner BOOL result = ::RemoveDirectoryW(path_buffer.c_str()); 57c00cf4a0SZachary Turner if (!result) 58c00cf4a0SZachary Turner error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 59*b9c1b51eSKate Stone } else { 60*b9c1b51eSKate Stone // SHFileOperation() accepts a list of paths, and so must be 61*b9c1b51eSKate Stone // double-null-terminated to 62*b9c1b51eSKate Stone // indicate the end of the list. The first null terminator is there only in 63*b9c1b51eSKate Stone // the backing 64190fadcdSZachary Turner // store but not the actual vector contents, and so we need to push twice. 65190fadcdSZachary Turner path_buffer.push_back(0); 66c00cf4a0SZachary Turner path_buffer.push_back(0); 67c00cf4a0SZachary Turner 68190fadcdSZachary Turner SHFILEOPSTRUCTW shfos = {0}; 69c00cf4a0SZachary Turner shfos.wFunc = FO_DELETE; 70190fadcdSZachary Turner shfos.pFrom = (LPCWSTR)path_buffer.data(); 71c00cf4a0SZachary Turner shfos.fFlags = FOF_NO_UI; 72c00cf4a0SZachary Turner 73190fadcdSZachary Turner int result = ::SHFileOperationW(&shfos); 74*b9c1b51eSKate Stone // TODO(zturner): Correctly handle the intricacies of SHFileOperation return 75*b9c1b51eSKate Stone // values. 76c00cf4a0SZachary Turner if (result != 0) 77c00cf4a0SZachary Turner error.SetErrorStringWithFormat("SHFileOperation failed"); 78c00cf4a0SZachary Turner } 79c00cf4a0SZachary Turner return error; 80c00cf4a0SZachary Turner } 81c00cf4a0SZachary Turner 82*b9c1b51eSKate Stone Error FileSystem::GetFilePermissions(const FileSpec &file_spec, 83*b9c1b51eSKate Stone uint32_t &file_permissions) { 84c00cf4a0SZachary Turner Error error; 85633594c2SAdrian McCarthy // Beware that Windows's permission model is different from Unix's, and it's 86633594c2SAdrian McCarthy // not clear if this API is supposed to check ACLs. To match the caller's 87633594c2SAdrian McCarthy // expectations as closely as possible, we'll use Microsoft's _stat, which 88633594c2SAdrian McCarthy // attempts to emulate POSIX stat. This should be good enough for basic 89633594c2SAdrian McCarthy // checks like FileSpec::Readable. 90633594c2SAdrian McCarthy struct _stat file_stats; 91*b9c1b51eSKate Stone if (::_stat(file_spec.GetCString(), &file_stats) == 0) { 92633594c2SAdrian McCarthy // The owner permission bits in "st_mode" currently match the definitions 93633594c2SAdrian McCarthy // for the owner file mode bits. 94633594c2SAdrian McCarthy file_permissions = file_stats.st_mode & (_S_IREAD | _S_IWRITE | _S_IEXEC); 95*b9c1b51eSKate Stone } else { 96633594c2SAdrian McCarthy error.SetErrorToErrno(); 97633594c2SAdrian McCarthy } 98633594c2SAdrian McCarthy 99c00cf4a0SZachary Turner return error; 100c00cf4a0SZachary Turner } 101c00cf4a0SZachary Turner 102*b9c1b51eSKate Stone Error FileSystem::SetFilePermissions(const FileSpec &file_spec, 103*b9c1b51eSKate Stone uint32_t file_permissions) { 104c00cf4a0SZachary Turner Error error; 105*b9c1b51eSKate Stone error.SetErrorStringWithFormat("%s is not supported on this host", 106*b9c1b51eSKate Stone LLVM_PRETTY_FUNCTION); 107c00cf4a0SZachary Turner return error; 108c00cf4a0SZachary Turner } 109c00cf4a0SZachary Turner 110*b9c1b51eSKate Stone lldb::user_id_t FileSystem::GetFileSize(const FileSpec &file_spec) { 111c00cf4a0SZachary Turner return file_spec.GetByteSize(); 112c00cf4a0SZachary Turner } 113c00cf4a0SZachary Turner 114*b9c1b51eSKate Stone bool FileSystem::GetFileExists(const FileSpec &file_spec) { 115c00cf4a0SZachary Turner return file_spec.Exists(); 116c00cf4a0SZachary Turner } 117c00cf4a0SZachary Turner 118*b9c1b51eSKate Stone Error FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst) { 119a9ea0711SOleksiy Vyalov Error error; 120190fadcdSZachary Turner std::wstring wsrc, wdst; 121*b9c1b51eSKate Stone if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) || 122*b9c1b51eSKate Stone !llvm::ConvertUTF8toWide(dst.GetCString(), wdst)) 123190fadcdSZachary Turner error.SetErrorString(PATH_CONVERSION_ERROR); 124190fadcdSZachary Turner else if (!::CreateHardLinkW(wsrc.c_str(), wdst.c_str(), nullptr)) 125a9ea0711SOleksiy Vyalov error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 126a9ea0711SOleksiy Vyalov return error; 127a9ea0711SOleksiy Vyalov } 128a9ea0711SOleksiy Vyalov 129*b9c1b51eSKate Stone int FileSystem::GetHardlinkCount(const FileSpec &file_spec) { 130190fadcdSZachary Turner std::wstring path; 131190fadcdSZachary Turner if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path)) 132190fadcdSZachary Turner return -1; 133190fadcdSZachary Turner 134*b9c1b51eSKate Stone HANDLE file_handle = 135*b9c1b51eSKate Stone ::CreateFileW(path.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ, 136*b9c1b51eSKate Stone nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); 1372b38f33bSOleksiy Vyalov 1382b38f33bSOleksiy Vyalov if (file_handle == INVALID_HANDLE_VALUE) 1392b38f33bSOleksiy Vyalov return -1; 1402b38f33bSOleksiy Vyalov 1412b38f33bSOleksiy Vyalov AutoHandle auto_file_handle(file_handle); 1422b38f33bSOleksiy Vyalov BY_HANDLE_FILE_INFORMATION file_info; 1432b38f33bSOleksiy Vyalov if (::GetFileInformationByHandle(file_handle, &file_info)) 1442b38f33bSOleksiy Vyalov return file_info.nNumberOfLinks; 1452b38f33bSOleksiy Vyalov 1462b38f33bSOleksiy Vyalov return -1; 1472b38f33bSOleksiy Vyalov } 1482b38f33bSOleksiy Vyalov 149*b9c1b51eSKate Stone Error FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) { 150c00cf4a0SZachary Turner Error error; 151190fadcdSZachary Turner std::wstring wsrc, wdst; 152*b9c1b51eSKate Stone if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) || 153*b9c1b51eSKate Stone !llvm::ConvertUTF8toWide(dst.GetCString(), wdst)) 154190fadcdSZachary Turner error.SetErrorString(PATH_CONVERSION_ERROR); 155190fadcdSZachary Turner if (error.Fail()) 156190fadcdSZachary Turner return error; 157190fadcdSZachary Turner DWORD attrib = ::GetFileAttributesW(wdst.c_str()); 158*b9c1b51eSKate Stone if (attrib == INVALID_FILE_ATTRIBUTES) { 159c00cf4a0SZachary Turner error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 160c00cf4a0SZachary Turner return error; 161c00cf4a0SZachary Turner } 162c00cf4a0SZachary Turner bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY); 163c00cf4a0SZachary Turner DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0; 164190fadcdSZachary Turner BOOL result = ::CreateSymbolicLinkW(wsrc.c_str(), wdst.c_str(), flag); 165c00cf4a0SZachary Turner if (!result) 166c00cf4a0SZachary Turner error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 167c00cf4a0SZachary Turner return error; 168c00cf4a0SZachary Turner } 169c00cf4a0SZachary Turner 170*b9c1b51eSKate Stone Error FileSystem::Unlink(const FileSpec &file_spec) { 171c00cf4a0SZachary Turner Error error; 172190fadcdSZachary Turner std::wstring path; 173*b9c1b51eSKate Stone if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path)) { 174190fadcdSZachary Turner error.SetErrorString(PATH_CONVERSION_ERROR); 175190fadcdSZachary Turner return error; 176190fadcdSZachary Turner } 177190fadcdSZachary Turner BOOL result = ::DeleteFileW(path.c_str()); 178c00cf4a0SZachary Turner if (!result) 179c00cf4a0SZachary Turner error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 180c00cf4a0SZachary Turner return error; 181c00cf4a0SZachary Turner } 182c00cf4a0SZachary Turner 183*b9c1b51eSKate Stone Error FileSystem::Readlink(const FileSpec &src, FileSpec &dst) { 184c00cf4a0SZachary Turner Error error; 185190fadcdSZachary Turner std::wstring wsrc; 186*b9c1b51eSKate Stone if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc)) { 187190fadcdSZachary Turner error.SetErrorString(PATH_CONVERSION_ERROR); 188190fadcdSZachary Turner return error; 189190fadcdSZachary Turner } 190190fadcdSZachary Turner 191*b9c1b51eSKate Stone HANDLE h = ::CreateFileW(wsrc.c_str(), GENERIC_READ, 192*b9c1b51eSKate Stone FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, 193*b9c1b51eSKate Stone OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, NULL); 194*b9c1b51eSKate Stone if (h == INVALID_HANDLE_VALUE) { 195c00cf4a0SZachary Turner error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 196c00cf4a0SZachary Turner return error; 197c00cf4a0SZachary Turner } 198c00cf4a0SZachary Turner 199190fadcdSZachary Turner std::vector<wchar_t> buf(PATH_MAX + 1); 200*b9c1b51eSKate Stone // Subtract 1 from the path length since this function does not add a null 201*b9c1b51eSKate Stone // terminator. 202*b9c1b51eSKate Stone DWORD result = ::GetFinalPathNameByHandleW( 203*b9c1b51eSKate Stone h, buf.data(), buf.size() - 1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); 204190fadcdSZachary Turner std::string path; 205c00cf4a0SZachary Turner if (result == 0) 206c00cf4a0SZachary Turner error.SetError(::GetLastError(), lldb::eErrorTypeWin32); 207190fadcdSZachary Turner else if (!llvm::convertWideToUTF8(buf.data(), path)) 208190fadcdSZachary Turner error.SetErrorString(PATH_CONVERSION_ERROR); 209d3173f34SChaoren Lin else 210190fadcdSZachary Turner dst.SetFile(path, false); 211c00cf4a0SZachary Turner 212c00cf4a0SZachary Turner ::CloseHandle(h); 213c00cf4a0SZachary Turner return error; 214c00cf4a0SZachary Turner } 215736888c8SGreg Clayton 216*b9c1b51eSKate Stone Error FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst) { 2179077e9f8SSean Callanan return Error("ResolveSymbolicLink() isn't implemented on Windows"); 2189077e9f8SSean Callanan } 2199077e9f8SSean Callanan 220*b9c1b51eSKate Stone bool FileSystem::IsLocal(const FileSpec &spec) { 221*b9c1b51eSKate Stone if (spec) { 222736888c8SGreg Clayton // TODO: return true if the file is on a locally mounted file system 223736888c8SGreg Clayton return true; 224736888c8SGreg Clayton } 225736888c8SGreg Clayton 226736888c8SGreg Clayton return false; 227736888c8SGreg Clayton } 228190fadcdSZachary Turner 229*b9c1b51eSKate Stone FILE *FileSystem::Fopen(const char *path, const char *mode) { 230190fadcdSZachary Turner std::wstring wpath, wmode; 231190fadcdSZachary Turner if (!llvm::ConvertUTF8toWide(path, wpath)) 232190fadcdSZachary Turner return nullptr; 233190fadcdSZachary Turner if (!llvm::ConvertUTF8toWide(mode, wmode)) 234190fadcdSZachary Turner return nullptr; 235190fadcdSZachary Turner FILE *file; 236190fadcdSZachary Turner if (_wfopen_s(&file, wpath.c_str(), wmode.c_str()) != 0) 237190fadcdSZachary Turner return nullptr; 238190fadcdSZachary Turner return file; 239190fadcdSZachary Turner } 240190fadcdSZachary Turner 241*b9c1b51eSKate Stone int FileSystem::Stat(const char *path, struct stat *stats) { 242190fadcdSZachary Turner std::wstring wpath; 243*b9c1b51eSKate Stone if (!llvm::ConvertUTF8toWide(path, wpath)) { 244190fadcdSZachary Turner errno = EINVAL; 245190fadcdSZachary Turner return -EINVAL; 246190fadcdSZachary Turner } 247190fadcdSZachary Turner int stat_result; 248190fadcdSZachary Turner #ifdef _USE_32BIT_TIME_T 249190fadcdSZachary Turner struct _stat32 file_stats; 250190fadcdSZachary Turner stat_result = ::_wstat32(wpath.c_str(), &file_stats); 251190fadcdSZachary Turner #else 252190fadcdSZachary Turner struct _stat64i32 file_stats; 253190fadcdSZachary Turner stat_result = ::_wstat64i32(wpath.c_str(), &file_stats); 254190fadcdSZachary Turner #endif 255*b9c1b51eSKate Stone if (stat_result == 0) { 256190fadcdSZachary Turner static_assert(sizeof(struct stat) == sizeof(file_stats), 257190fadcdSZachary Turner "stat and _stat32/_stat64i32 must have the same layout"); 258190fadcdSZachary Turner *stats = *reinterpret_cast<struct stat *>(&file_stats); 259190fadcdSZachary Turner } 260190fadcdSZachary Turner return stat_result; 261190fadcdSZachary Turner } 262