| //===-- FileSystem.cpp ------------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "lldb/Host/windows/windows.h" |
| |
| #include <shellapi.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| #include "lldb/Host/FileSystem.h" |
| #include "lldb/Host/windows/AutoHandle.h" |
| |
| #include "llvm/Support/ConvertUTF.h" |
| #include "llvm/Support/FileSystem.h" |
| |
| using namespace lldb_private; |
| |
| const char *FileSystem::DEV_NULL = "nul"; |
| |
| const char *FileSystem::PATH_CONVERSION_ERROR = |
| "Error converting path between UTF-8 and native encoding"; |
| |
| FileSpec::PathSyntax FileSystem::GetNativePathSyntax() { |
| return FileSpec::ePathSyntaxWindows; |
| } |
| |
| Error FileSystem::MakeDirectory(const FileSpec &file_spec, |
| uint32_t file_permissions) { |
| // On Win32, the mode parameter is ignored, as Windows files and directories |
| // support a |
| // different permission model than POSIX. |
| Error error; |
| const auto err_code = |
| llvm::sys::fs::create_directories(file_spec.GetPath(), true); |
| if (err_code) { |
| error.SetErrorString(err_code.message().c_str()); |
| } |
| |
| return error; |
| } |
| |
| Error FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse) { |
| Error error; |
| std::wstring path_buffer; |
| if (!llvm::ConvertUTF8toWide(file_spec.GetPath(), path_buffer)) { |
| error.SetErrorString(PATH_CONVERSION_ERROR); |
| return error; |
| } |
| if (!recurse) { |
| BOOL result = ::RemoveDirectoryW(path_buffer.c_str()); |
| if (!result) |
| error.SetError(::GetLastError(), lldb::eErrorTypeWin32); |
| } else { |
| // SHFileOperation() accepts a list of paths, and so must be |
| // double-null-terminated to |
| // indicate the end of the list. The first null terminator is there only in |
| // the backing |
| // store but not the actual vector contents, and so we need to push twice. |
| path_buffer.push_back(0); |
| path_buffer.push_back(0); |
| |
| SHFILEOPSTRUCTW shfos = {0}; |
| shfos.wFunc = FO_DELETE; |
| shfos.pFrom = (LPCWSTR)path_buffer.data(); |
| shfos.fFlags = FOF_NO_UI; |
| |
| int result = ::SHFileOperationW(&shfos); |
| // TODO(zturner): Correctly handle the intricacies of SHFileOperation return |
| // values. |
| if (result != 0) |
| error.SetErrorStringWithFormat("SHFileOperation failed"); |
| } |
| return error; |
| } |
| |
| Error FileSystem::GetFilePermissions(const FileSpec &file_spec, |
| uint32_t &file_permissions) { |
| Error error; |
| // Beware that Windows's permission model is different from Unix's, and it's |
| // not clear if this API is supposed to check ACLs. To match the caller's |
| // expectations as closely as possible, we'll use Microsoft's _stat, which |
| // attempts to emulate POSIX stat. This should be good enough for basic |
| // checks like FileSpec::Readable. |
| struct _stat file_stats; |
| if (::_stat(file_spec.GetCString(), &file_stats) == 0) { |
| // The owner permission bits in "st_mode" currently match the definitions |
| // for the owner file mode bits. |
| file_permissions = file_stats.st_mode & (_S_IREAD | _S_IWRITE | _S_IEXEC); |
| } else { |
| error.SetErrorToErrno(); |
| } |
| |
| return error; |
| } |
| |
| Error FileSystem::SetFilePermissions(const FileSpec &file_spec, |
| uint32_t file_permissions) { |
| Error error; |
| error.SetErrorStringWithFormat("%s is not supported on this host", |
| LLVM_PRETTY_FUNCTION); |
| return error; |
| } |
| |
| lldb::user_id_t FileSystem::GetFileSize(const FileSpec &file_spec) { |
| return file_spec.GetByteSize(); |
| } |
| |
| bool FileSystem::GetFileExists(const FileSpec &file_spec) { |
| return file_spec.Exists(); |
| } |
| |
| Error FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst) { |
| Error error; |
| std::wstring wsrc, wdst; |
| if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) || |
| !llvm::ConvertUTF8toWide(dst.GetCString(), wdst)) |
| error.SetErrorString(PATH_CONVERSION_ERROR); |
| else if (!::CreateHardLinkW(wsrc.c_str(), wdst.c_str(), nullptr)) |
| error.SetError(::GetLastError(), lldb::eErrorTypeWin32); |
| return error; |
| } |
| |
| int FileSystem::GetHardlinkCount(const FileSpec &file_spec) { |
| std::wstring path; |
| if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path)) |
| return -1; |
| |
| HANDLE file_handle = |
| ::CreateFileW(path.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ, |
| nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); |
| |
| if (file_handle == INVALID_HANDLE_VALUE) |
| return -1; |
| |
| AutoHandle auto_file_handle(file_handle); |
| BY_HANDLE_FILE_INFORMATION file_info; |
| if (::GetFileInformationByHandle(file_handle, &file_info)) |
| return file_info.nNumberOfLinks; |
| |
| return -1; |
| } |
| |
| Error FileSystem::Symlink(const FileSpec &src, const FileSpec &dst) { |
| Error error; |
| std::wstring wsrc, wdst; |
| if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) || |
| !llvm::ConvertUTF8toWide(dst.GetCString(), wdst)) |
| error.SetErrorString(PATH_CONVERSION_ERROR); |
| if (error.Fail()) |
| return error; |
| DWORD attrib = ::GetFileAttributesW(wdst.c_str()); |
| if (attrib == INVALID_FILE_ATTRIBUTES) { |
| error.SetError(::GetLastError(), lldb::eErrorTypeWin32); |
| return error; |
| } |
| bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY); |
| DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0; |
| BOOL result = ::CreateSymbolicLinkW(wsrc.c_str(), wdst.c_str(), flag); |
| if (!result) |
| error.SetError(::GetLastError(), lldb::eErrorTypeWin32); |
| return error; |
| } |
| |
| Error FileSystem::Unlink(const FileSpec &file_spec) { |
| Error error; |
| std::wstring path; |
| if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path)) { |
| error.SetErrorString(PATH_CONVERSION_ERROR); |
| return error; |
| } |
| BOOL result = ::DeleteFileW(path.c_str()); |
| if (!result) |
| error.SetError(::GetLastError(), lldb::eErrorTypeWin32); |
| return error; |
| } |
| |
| Error FileSystem::Readlink(const FileSpec &src, FileSpec &dst) { |
| Error error; |
| std::wstring wsrc; |
| if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc)) { |
| error.SetErrorString(PATH_CONVERSION_ERROR); |
| return error; |
| } |
| |
| HANDLE h = ::CreateFileW(wsrc.c_str(), GENERIC_READ, |
| FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, |
| OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, NULL); |
| if (h == INVALID_HANDLE_VALUE) { |
| error.SetError(::GetLastError(), lldb::eErrorTypeWin32); |
| return error; |
| } |
| |
| std::vector<wchar_t> buf(PATH_MAX + 1); |
| // Subtract 1 from the path length since this function does not add a null |
| // terminator. |
| DWORD result = ::GetFinalPathNameByHandleW( |
| h, buf.data(), buf.size() - 1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); |
| std::string path; |
| if (result == 0) |
| error.SetError(::GetLastError(), lldb::eErrorTypeWin32); |
| else if (!llvm::convertWideToUTF8(buf.data(), path)) |
| error.SetErrorString(PATH_CONVERSION_ERROR); |
| else |
| dst.SetFile(path, false); |
| |
| ::CloseHandle(h); |
| return error; |
| } |
| |
| Error FileSystem::ResolveSymbolicLink(const FileSpec &src, FileSpec &dst) { |
| return Error("ResolveSymbolicLink() isn't implemented on Windows"); |
| } |
| |
| bool FileSystem::IsLocal(const FileSpec &spec) { |
| if (spec) { |
| // TODO: return true if the file is on a locally mounted file system |
| return true; |
| } |
| |
| return false; |
| } |
| |
| FILE *FileSystem::Fopen(const char *path, const char *mode) { |
| std::wstring wpath, wmode; |
| if (!llvm::ConvertUTF8toWide(path, wpath)) |
| return nullptr; |
| if (!llvm::ConvertUTF8toWide(mode, wmode)) |
| return nullptr; |
| FILE *file; |
| if (_wfopen_s(&file, wpath.c_str(), wmode.c_str()) != 0) |
| return nullptr; |
| return file; |
| } |
| |
| int FileSystem::Stat(const char *path, struct stat *stats) { |
| std::wstring wpath; |
| if (!llvm::ConvertUTF8toWide(path, wpath)) { |
| errno = EINVAL; |
| return -EINVAL; |
| } |
| int stat_result; |
| #ifdef _USE_32BIT_TIME_T |
| struct _stat32 file_stats; |
| stat_result = ::_wstat32(wpath.c_str(), &file_stats); |
| #else |
| struct _stat64i32 file_stats; |
| stat_result = ::_wstat64i32(wpath.c_str(), &file_stats); |
| #endif |
| if (stat_result == 0) { |
| static_assert(sizeof(struct stat) == sizeof(file_stats), |
| "stat and _stat32/_stat64i32 must have the same layout"); |
| *stats = *reinterpret_cast<struct stat *>(&file_stats); |
| } |
| return stat_result; |
| } |