mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 00:45:18 +03:00
566ca81dc0
Summary: As the string is created with MAX_PATH wide characters, a lot of them are going to stay nul. A future diff adds a sanity check to the PathComponent that validates that it doesn't contain any nul bytes, which chokes on these paths. Reviewed By: chadaustin Differential Revision: D24479715 fbshipit-source-id: e51c4b1c53a3f375664c0c0a1e325ebe302b0cd3
248 lines
6.4 KiB
C++
248 lines
6.4 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This software may be used and distributed according to the terms of the
|
|
* GNU General Public License version 2.
|
|
*/
|
|
|
|
#include "eden/fs/utils/FileUtils.h"
|
|
#include <fmt/format.h>
|
|
#include <openssl/sha.h>
|
|
#include "folly/FileUtil.h"
|
|
|
|
#ifdef _WIN32
|
|
#include "eden/fs/utils/Handle.h"
|
|
#endif
|
|
|
|
namespace facebook {
|
|
namespace eden {
|
|
|
|
#ifndef _WIN32
|
|
|
|
folly::Try<std::string> readFile(AbsolutePathPiece path, size_t num_bytes) {
|
|
std::string ret;
|
|
|
|
if (!folly::readFile(path.stringPiece().data(), ret, num_bytes)) {
|
|
return folly::Try<std::string>{folly::makeSystemError(
|
|
fmt::format(FMT_STRING("couldn't read {}"), path))};
|
|
}
|
|
|
|
return folly::Try{ret};
|
|
}
|
|
|
|
folly::Try<void> writeFile(AbsolutePathPiece path, folly::ByteRange data) {
|
|
if (!folly::writeFile(data, path.stringPiece().data())) {
|
|
return folly::Try<void>{folly::makeSystemError(
|
|
fmt::format(FMT_STRING("couldn't write {}"), path))};
|
|
}
|
|
|
|
return folly::Try<void>{};
|
|
}
|
|
|
|
folly::Try<void> writeFileAtomic(
|
|
AbsolutePathPiece path,
|
|
folly::ByteRange data) {
|
|
iovec iov;
|
|
iov.iov_base = const_cast<unsigned char*>(data.data());
|
|
iov.iov_len = data.size();
|
|
|
|
if (auto err = folly::writeFileAtomicNoThrow(path.stringPiece(), &iov, 1)) {
|
|
return folly::Try<void>{folly::makeSystemErrorExplicit(
|
|
err, fmt::format(FMT_STRING("couldn't update {}"), path))};
|
|
}
|
|
|
|
return folly::Try<void>{};
|
|
}
|
|
|
|
#else
|
|
|
|
namespace {
|
|
/*
|
|
* Following is a traits class for File System handles with its handle value and
|
|
* close function.
|
|
*/
|
|
struct FileHandleTraits {
|
|
using Type = HANDLE;
|
|
|
|
static Type invalidHandleValue() noexcept {
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
static void close(Type handle) noexcept {
|
|
CloseHandle(handle);
|
|
}
|
|
};
|
|
|
|
using FileHandle = HandleBase<FileHandleTraits>;
|
|
|
|
enum class OpenMode {
|
|
READ,
|
|
WRITE,
|
|
};
|
|
|
|
folly::Try<FileHandle> openHandle(AbsolutePathPiece path, OpenMode mode) {
|
|
DWORD dwDesiredAccess;
|
|
DWORD dwCreationDisposition;
|
|
|
|
if (mode == OpenMode::READ) {
|
|
dwDesiredAccess = GENERIC_READ;
|
|
dwCreationDisposition = OPEN_EXISTING;
|
|
} else {
|
|
dwDesiredAccess = GENERIC_WRITE;
|
|
dwCreationDisposition = CREATE_ALWAYS;
|
|
}
|
|
|
|
auto widePath = path.wide();
|
|
FileHandle fileHandle{CreateFileW(
|
|
widePath.c_str(),
|
|
dwDesiredAccess,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
nullptr,
|
|
dwCreationDisposition,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
nullptr)};
|
|
if (!fileHandle) {
|
|
return folly::Try<FileHandle>{makeWin32ErrorExplicit(
|
|
GetLastError(), fmt::format(FMT_STRING("couldn't open {}"), path))};
|
|
} else {
|
|
return folly::Try{std::move(fileHandle)};
|
|
}
|
|
}
|
|
|
|
folly::Try<void> writeToHandle(
|
|
FileHandle& handle,
|
|
folly::ByteRange data,
|
|
AbsolutePathPiece path) {
|
|
// TODO(xavierd): This can only write up to 4GB.
|
|
if (data.size() > std::numeric_limits<DWORD>::max()) {
|
|
return folly::Try<void>{std::invalid_argument(fmt::format(
|
|
FMT_STRING("files over 4GB can't be written to, size={}"),
|
|
data.size()))};
|
|
}
|
|
|
|
DWORD written = 0;
|
|
if (!WriteFile(
|
|
handle.get(),
|
|
data.data(),
|
|
folly::to_narrow(data.size()),
|
|
&written,
|
|
nullptr)) {
|
|
return folly::Try<void>{makeWin32ErrorExplicit(
|
|
GetLastError(), fmt::format(FMT_STRING("couldn't write {}"), path))};
|
|
}
|
|
|
|
return folly::Try<void>{};
|
|
}
|
|
|
|
} // namespace
|
|
|
|
folly::Try<std::string> readFile(AbsolutePathPiece path, size_t num_bytes) {
|
|
auto tryFileHandle = openHandle(path, OpenMode::READ);
|
|
if (tryFileHandle.hasException()) {
|
|
return folly::Try<std::string>{std::move(tryFileHandle).exception()};
|
|
}
|
|
auto fileHandle = std::move(tryFileHandle).value();
|
|
|
|
if (num_bytes == std::numeric_limits<size_t>::max()) {
|
|
LARGE_INTEGER fileSize;
|
|
if (!GetFileSizeEx(fileHandle.get(), &fileSize)) {
|
|
return folly::Try<std::string>{makeWin32ErrorExplicit(
|
|
GetLastError(),
|
|
fmt::format(
|
|
FMT_STRING("couldn't obtain the file size of {}"), path))};
|
|
}
|
|
num_bytes = fileSize.QuadPart;
|
|
}
|
|
|
|
// TODO(xavierd): this can only read up to 4GB.
|
|
if (num_bytes > std::numeric_limits<DWORD>::max()) {
|
|
return folly::Try<std::string>{std::invalid_argument(fmt::format(
|
|
FMT_STRING("files over 4GB can't be read, filesize={}"), num_bytes))};
|
|
}
|
|
|
|
std::string ret(num_bytes, 0);
|
|
DWORD read = 0;
|
|
if (!ReadFile(
|
|
fileHandle.get(),
|
|
ret.data(),
|
|
folly::to_narrow(num_bytes),
|
|
&read,
|
|
nullptr)) {
|
|
return folly::Try<std::string>{makeWin32ErrorExplicit(
|
|
GetLastError(), fmt::format(FMT_STRING("couldn't read {}"), path))};
|
|
}
|
|
|
|
return folly::Try{ret};
|
|
}
|
|
|
|
folly::Try<void> writeFile(AbsolutePathPiece path, folly::ByteRange data) {
|
|
auto tryFileHandle = openHandle(path, OpenMode::WRITE);
|
|
if (tryFileHandle.hasException()) {
|
|
return folly::Try<void>{std::move(tryFileHandle).exception()};
|
|
}
|
|
|
|
return writeToHandle(tryFileHandle.value(), data, path);
|
|
}
|
|
|
|
folly::Try<void> writeFileAtomic(
|
|
AbsolutePathPiece path,
|
|
folly::ByteRange data) {
|
|
auto parent = path.dirname();
|
|
wchar_t tmpFile[MAX_PATH];
|
|
|
|
if (GetTempFileNameW(parent.wide().c_str(), L"tmp", 0, tmpFile) == 0) {
|
|
auto err = GetLastError();
|
|
return folly::Try<void>{makeWin32ErrorExplicit(
|
|
err,
|
|
fmt::format(
|
|
FMT_STRING("couldn't create a temporary file for {}"), path))};
|
|
}
|
|
|
|
auto tryTmpFileWrite = writeFile(AbsolutePath(tmpFile), data);
|
|
if (tryTmpFileWrite.hasException()) {
|
|
return tryTmpFileWrite;
|
|
}
|
|
|
|
if (!MoveFileExW(tmpFile, path.wide().c_str(), MOVEFILE_REPLACE_EXISTING)) {
|
|
auto err = GetLastError();
|
|
return folly::Try<void>{makeWin32ErrorExplicit(
|
|
err, fmt::format(FMT_STRING("couldn't replace {}"), path))};
|
|
}
|
|
|
|
return folly::Try<void>{};
|
|
}
|
|
|
|
Hash getFileSha1(AbsolutePathPiece filePath) {
|
|
auto file = openHandle(filePath, OpenMode::READ).value();
|
|
|
|
SHA_CTX ctx;
|
|
SHA1_Init(&ctx);
|
|
while (true) {
|
|
uint8_t buf[8192];
|
|
|
|
DWORD bytesRead;
|
|
if (!ReadFile(file.get(), buf, sizeof(buf), &bytesRead, nullptr)) {
|
|
throw makeWin32ErrorExplicit(
|
|
GetLastError(),
|
|
fmt::format(
|
|
FMT_STRING("Error while computing SHA1 of {}"), filePath));
|
|
}
|
|
|
|
if (bytesRead == 0) {
|
|
break;
|
|
}
|
|
|
|
SHA1_Update(&ctx, buf, bytesRead);
|
|
}
|
|
|
|
static_assert(Hash::RAW_SIZE == SHA_DIGEST_LENGTH);
|
|
Hash sha1;
|
|
SHA1_Final(sha1.mutableBytes().begin(), &ctx);
|
|
|
|
return sha1;
|
|
}
|
|
|
|
#endif
|
|
|
|
} // namespace eden
|
|
} // namespace facebook
|