llvm-project/lldb/source/Host/windows/FileSystem.cpp

294 lines
8.5 KiB
C++

//===-- 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", __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;
}