*** to conform to clang-format’s LLVM style. This kind of mass change has
*** two obvious implications:
Firstly, merging this particular commit into a downstream fork may be a huge
effort. Alternatively, it may be worth merging all changes up to this commit,
performing the same reformatting operation locally, and then discarding the
merge for this particular commit. The commands used to accomplish this
reformatting were as follows (with current working directory as the root of
the repository):
find . \( -iname "*.c" -or -iname "*.cpp" -or -iname "*.h" -or -iname "*.mm" \) -exec clang-format -i {} +
find . -iname "*.py" -exec autopep8 --in-place --aggressive --aggressive {} + ;
The version of clang-format used was 3.9.0, and autopep8 was 1.2.4.
Secondly, “blame” style tools will generally point to this commit instead of
a meaningful prior commit. There are alternatives available that will attempt
to look through this change and find the appropriate prior commit. YMMV.
llvm-svn: 280751
262 lines
8.2 KiB
C++
262 lines
8.2 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;
|
|
}
|