/*
Userspace Virtual Filesystem
Copyright (C) 2015 Sebastian Herbord. All rights reserved.
This file is part of usvfs.
usvfs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
usvfs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with usvfs. If not, see .
*/
#include "hookcontext.h"
#include "exceptionex.h"
#include "hookcallcontext.h"
#include "loghelpers.h"
#include "usvfs.h"
#include
#include
#include
#include
namespace bi = boost::interprocess;
using usvfs::shared::SharedMemoryT;
using usvfs::shared::VoidAllocatorT;
using namespace usvfs;
namespace ush = usvfs::shared;
HookContext* HookContext::s_Instance = nullptr;
void printBuffer(const char* buffer, size_t size)
{
static const int bufferSize = 16 * 3;
char temp[bufferSize + 1];
temp[bufferSize] = '\0';
for (size_t i = 0; i < size; ++i) {
size_t offset = i % 16;
_snprintf(&temp[offset * 3], 3, "%02x ", (unsigned char)buffer[i]);
if (offset == 15) {
spdlog::get("hooks")->info("{0:x} - {1}", i - offset, temp);
}
}
spdlog::get("hooks")->info(temp);
}
HookContext::HookContext(const usvfsParameters& params, HMODULE module)
: m_ConfigurationSHM(bi::open_or_create, params.instanceName, 64 * 1024),
m_Parameters(retrieveParameters(params)),
m_Tree(m_Parameters->currentSHMName(),
4 * 1024 * 1024) // 4 MiB empirically covers most small setups without
// need to resize
,
m_InverseTree(
m_Parameters->currentInverseSHMName(),
128 * 1024) // 128 KiB should cover reverse tree for even larger setups
,
m_DLLModule(module)
{
if (s_Instance != nullptr) {
throw std::runtime_error("singleton duplicate instantiation (HookContext)");
}
const auto userCount = m_Parameters->userConnected();
spdlog::get("usvfs")->debug("context current shm: {0} (now {1} connections)",
m_Parameters->currentSHMName(), userCount);
s_Instance = this;
if (m_Tree.get() == nullptr) {
USVFS_THROW_EXCEPTION(usage_error()
<< ex_msg("shm not found") << ex_msg(params.instanceName));
}
}
void HookContext::remove(const char* instanceName)
{
bi::shared_memory_object::remove(instanceName);
}
HookContext::~HookContext()
{
spdlog::get("usvfs")->info("releasing hook context");
s_Instance = nullptr;
const auto userCount = m_Parameters->userDisconnected();
if (userCount == 0) {
spdlog::get("usvfs")->info("removing tree {}", m_Parameters->instanceName());
bi::shared_memory_object::remove(m_Parameters->instanceName().c_str());
} else {
spdlog::get("usvfs")->info("{} users left", userCount);
}
}
SharedParameters* HookContext::retrieveParameters(const usvfsParameters& params)
{
std::pair res =
m_ConfigurationSHM.find("parameters");
if (res.first == nullptr) {
// not configured yet
spdlog::get("usvfs")->info("create config in {}", ::GetCurrentProcessId());
res.first = m_ConfigurationSHM.construct("parameters")(
params, VoidAllocatorT(m_ConfigurationSHM.get_segment_manager()));
if (res.first == nullptr) {
USVFS_THROW_EXCEPTION(bi::bad_alloc());
}
} else {
spdlog::get("usvfs")->info("access existing config in {}", ::GetCurrentProcessId());
}
spdlog::get("usvfs")->info("{} processes", res.first->registeredProcessCount());
return res.first;
}
HookContext::ConstPtr HookContext::readAccess(const char*)
{
BOOST_ASSERT(s_Instance != nullptr);
// TODO: this should be a shared mutex!
s_Instance->m_Mutex.wait(200);
return ConstPtr(s_Instance, unlockShared);
}
HookContext::Ptr HookContext::writeAccess(const char*)
{
BOOST_ASSERT(s_Instance != nullptr);
s_Instance->m_Mutex.wait(200);
return Ptr(s_Instance, unlock);
}
void HookContext::setDebugParameters(LogLevel level, CrashDumpsType dumpType,
const std::string& dumpPath,
std::chrono::milliseconds delayProcess)
{
m_Parameters->setDebugParameters(level, dumpType, dumpPath, delayProcess);
}
void HookContext::updateParameters() const
{
m_Parameters->setSHMNames(m_Tree.shmName(), m_InverseTree.shmName());
}
usvfsParameters HookContext::callParameters() const
{
updateParameters();
return m_Parameters->makeLocal();
}
std::wstring HookContext::dllPath() const
{
std::wstring path = winapi::wide::getModuleFileName(m_DLLModule);
return boost::filesystem::path(path).parent_path().make_preferred().wstring();
}
void HookContext::registerProcess(DWORD pid)
{
m_Parameters->registerProcess(pid);
}
void HookContext::unregisterCurrentProcess()
{
m_Parameters->unregisterProcess(::GetCurrentProcessId());
}
std::vector HookContext::registeredProcesses() const
{
return m_Parameters->registeredProcesses();
}
void HookContext::blacklistExecutable(const std::wstring& wexe)
{
const auto exe = shared::string_cast(wexe, shared::CodePage::UTF8);
spdlog::get("usvfs")->debug("blacklisting '{}'", exe);
m_Parameters->blacklistExecutable(exe);
}
void HookContext::clearExecutableBlacklist()
{
spdlog::get("usvfs")->debug("clearing blacklist");
m_Parameters->clearExecutableBlacklist();
}
BOOL HookContext::executableBlacklisted(LPCWSTR wapp, LPCWSTR wcmd) const
{
std::string app;
if (wapp) {
app = ush::string_cast(wapp, ush::CodePage::UTF8);
}
std::string cmd;
if (wcmd) {
cmd = ush::string_cast(wcmd, ush::CodePage::UTF8);
}
return m_Parameters->executableBlacklisted(app, cmd);
}
void usvfs::HookContext::addSkipFileSuffix(const std::wstring& fileSuffix)
{
const auto fsuffix =
shared::string_cast(fileSuffix, shared::CodePage::UTF8);
if (fsuffix.empty()) {
return;
}
spdlog::get("usvfs")->debug("added skip file suffix '{}'", fsuffix);
m_Parameters->addSkipFileSuffix(fsuffix);
}
void usvfs::HookContext::clearSkipFileSuffixes()
{
spdlog::get("usvfs")->debug("clearing skip file suffixes");
m_Parameters->clearSkipFileSuffixes();
}
std::vector usvfs::HookContext::skipFileSuffixes() const
{
return m_Parameters->skipFileSuffixes();
}
void usvfs::HookContext::addSkipDirectory(const std::wstring& directory)
{
const auto dir = shared::string_cast(directory, shared::CodePage::UTF8);
if (dir.empty()) {
return;
}
spdlog::get("usvfs")->debug("added skip directory '{}'", dir);
m_Parameters->addSkipDirectory(dir);
}
void usvfs::HookContext::clearSkipDirectories()
{
spdlog::get("usvfs")->debug("clearing skip directories");
m_Parameters->clearSkipDirectories();
}
std::vector usvfs::HookContext::skipDirectories() const
{
return m_Parameters->skipDirectories();
}
void HookContext::forceLoadLibrary(const std::wstring& wprocess,
const std::wstring& wpath)
{
const auto process =
shared::string_cast(wprocess, shared::CodePage::UTF8);
const auto path = shared::string_cast(wpath, shared::CodePage::UTF8);
spdlog::get("usvfs")->debug("adding forced library '{}' for process '{}'", path,
process);
m_Parameters->addForcedLibrary(process, path);
}
void HookContext::clearLibraryForceLoads()
{
spdlog::get("usvfs")->debug("clearing forced libraries");
m_Parameters->clearForcedLibraries();
}
std::vector
HookContext::librariesToForceLoad(const std::wstring& processName)
{
const auto v = m_Parameters->forcedLibraries(
shared::string_cast(processName, shared::CodePage::UTF8));
std::vector wv;
for (const auto& s : v) {
wv.push_back(shared::string_cast(s, shared::CodePage::UTF8));
}
return wv;
}
void HookContext::registerDelayed(std::future delayed)
{
m_Futures.push_back(std::move(delayed));
}
std::vector>& HookContext::delayed()
{
return m_Futures;
}
void HookContext::unlock(HookContext* instance)
{
instance->m_Mutex.signal();
}
void HookContext::unlockShared(const HookContext* instance)
{
instance->m_Mutex.signal();
}
// deprecated
//
extern "C" DLLEXPORT HookContext* __cdecl CreateHookContext(
const USVFSParameters& oldParams, HMODULE module)
{
const usvfsParameters p(oldParams);
return usvfsCreateHookContext(p, module);
}
extern "C" DLLEXPORT usvfs::HookContext* WINAPI
usvfsCreateHookContext(const usvfsParameters& params, HMODULE module)
{
return new HookContext(params, module);
}