Files
clang-p2996/lldb/source/Host/windows/FileSystem.cpp
Zachary Turner f343968f5d Delete Host/windows/win32.h
It's always hard to remember when to include this file, and
when you do include it it's hard to remember what preprocessor
check it needs to be behind, and then you further have to remember
whether it's windows.h or win32.h which you need to include.

This patch changes the name to PosixApi.h, which is more appropriately
named, and makes it independent of any preprocessor setting.

There's still the issue of people not knowing when to include this,
because there's not a well-defined set of things it exposes other
than "whatever is missing on Windows", but at least this should
make it less painful to fix when problems arise.

This patch depends on LLVM revision r278170.

llvm-svn: 278177
2016-08-09 23:06:08 +00:00

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