Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add File & Directory skipping #61

Merged
merged 12 commits into from
Jun 8, 2024
16 changes: 16 additions & 0 deletions include/sharedparameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ class DLLEXPORT SharedParameters
void clearExecutableBlacklist();
bool executableBlacklisted(const std::string& app, const std::string& cmd) const;

void addSkipFileSuffix(const std::string& fileSuffix);
void clearSkipFileSuffixes();
std::vector<std::string> skipFileSuffixes() const;

void addSkipDirectory(const std::string& directory);
void clearSkipDirectories();
std::vector<std::string> skipDirectories() const;

void addForcedLibrary(const std::string& process, const std::string& path);
std::vector<std::string> forcedLibraries(const std::string& processName);
void clearForcedLibraries();
Expand All @@ -77,6 +85,12 @@ class DLLEXPORT SharedParameters
using ProcessList = boost::container::flat_set<
DWORD, std::less<DWORD>, DWORDAllocatorT>;

using FileSuffixSkipList = boost::container::flat_set<
shared::StringT, std::less<shared::StringT>, StringAllocatorT>;

using DirectorySkipList = boost::container::flat_set<
shared::StringT, std::less<shared::StringT>, StringAllocatorT>;

using ForcedLibraries = boost::container::slist<
ForcedLibrary, ForcedLibraryAllocatorT>;

Expand All @@ -93,6 +107,8 @@ class DLLEXPORT SharedParameters
uint32_t m_userCount;
ProcessBlacklist m_processBlacklist;
ProcessList m_processList;
FileSuffixSkipList m_fileSuffixSkipList;
DirectorySkipList m_directorySkipList;
ForcedLibraries m_forcedLibraries;
};

Expand Down
33 changes: 32 additions & 1 deletion include/usvfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ static const unsigned int LINKFLAG_CREATETARGET = 0x00000004; // if set, file
// If there different create-target have been set for an element and one of its
// ancestors, the inner-most create-target is used
static const unsigned int LINKFLAG_RECURSIVE = 0x00000008; // if set, directories are linked recursively

static const unsigned int LINKFLAG_FAILIFSKIPPED = 0x00000010; // if set, linking fails if the file or directory is skipped
Twinki14 marked this conversation as resolved.
Show resolved Hide resolved
// files or directories are skipped depending on whats been added to
// the skip file suffixes or skip directories list in
// the sharedparameters class, those lists are checked during virtual linking

extern "C" {

Expand Down Expand Up @@ -154,6 +157,34 @@ DLLEXPORT VOID WINAPI BlacklistExecutable(LPWSTR executableName);
*/
DLLEXPORT VOID WINAPI ClearExecutableBlacklist();

/**
* adds a file suffix to a list to skip during file linking
* .txt and some_file.txt are both valid file suffixes,
* not to be confused with file extensions
* @param fileSuffix a valid file suffix
*/
DLLEXPORT VOID WINAPI usvfsAddSkipFileSuffix(LPWSTR fileSuffix);

/**
* clears the file suffix skip-list
*/
DLLEXPORT VOID WINAPI usvfsClearSkipFileSuffixes();

/**
* adds a directory name that will be skipped during directory linking.
* Not a path. Any directory matching the name will be skipped,
* regardless of it's path, for example if .git is added,
* any sub-path or root-path containing a .git directory
* will have the .git directory skipped during directory linking
* @param directory name of the directory
*/
DLLEXPORT VOID WINAPI usvfsAddSkipDirectory(LPWSTR directory);

/**
* clears the directory skip-list
*/
DLLEXPORT VOID WINAPI usvfsClearSkipDirectories();

/**
* adds a library to be force loaded when the given process is injected
* @param
Expand Down
46 changes: 46 additions & 0 deletions src/usvfs_dll/hookcontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,52 @@ BOOL HookContext::executableBlacklisted(LPCWSTR wapp, LPCWSTR wcmd) const
return m_Parameters->executableBlacklisted(app, cmd);
}

void usvfs::HookContext::addSkipFileSuffix(const std::wstring& fileSuffix)
{
const auto fsuffix = shared::string_cast<std::string>(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<std::string> usvfs::HookContext::skipFileSuffixes() const
{
return m_Parameters->skipFileSuffixes();
}

void usvfs::HookContext::addSkipDirectory(const std::wstring& directory)
{
const auto dir = shared::string_cast<std::string>(directory, shared::CodePage::UTF8);
Al12rs marked this conversation as resolved.
Show resolved Hide resolved

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<std::string> usvfs::HookContext::skipDirectories() const
{
return m_Parameters->skipDirectories();
}

void HookContext::forceLoadLibrary(
const std::wstring& wprocess, const std::wstring& wpath)
{
Expand Down
8 changes: 8 additions & 0 deletions src/usvfs_dll/hookcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,14 @@ class HookContext
void clearExecutableBlacklist();
BOOL HookContext::executableBlacklisted(LPCWSTR lpApplicationName, LPCWSTR lpCommandLine) const;

void addSkipFileSuffix(const std::wstring& fileSuffix);
void clearSkipFileSuffixes();
std::vector<std::string> skipFileSuffixes() const;

void addSkipDirectory(const std::wstring& directory);
void clearSkipDirectories();
std::vector<std::string> skipDirectories () const;

void forceLoadLibrary(const std::wstring &processName, const std::wstring &libraryPath);
void clearLibraryForceLoads();
std::vector<std::wstring> librariesToForceLoad(const std::wstring &processName);
Expand Down
42 changes: 42 additions & 0 deletions src/usvfs_dll/sharedparameters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ SharedParameters::SharedParameters(const usvfsParameters& reference,
, m_userCount(1)
, m_processBlacklist(allocator)
, m_processList(allocator)
, m_fileSuffixSkipList(allocator)
, m_directorySkipList(allocator)
, m_forcedLibraries(allocator)
{
}
Expand Down Expand Up @@ -198,6 +200,46 @@ bool SharedParameters::executableBlacklisted(
return false;
}

void SharedParameters::addSkipFileSuffix(const std::string& fileSuffix)
{
bi::scoped_lock lock(m_mutex);

m_fileSuffixSkipList.insert(shared::StringT(fileSuffix.begin(), fileSuffix.end(),
m_fileSuffixSkipList.get_allocator()));
}

void SharedParameters::clearSkipFileSuffixes()
{
bi::scoped_lock lock(m_mutex);
m_fileSuffixSkipList.clear();
}

std::vector<std::string> SharedParameters::skipFileSuffixes() const
Al12rs marked this conversation as resolved.
Show resolved Hide resolved
{
bi::scoped_lock lock(m_mutex);
Al12rs marked this conversation as resolved.
Show resolved Hide resolved
return { m_fileSuffixSkipList.begin(), m_fileSuffixSkipList.end() };
}

void SharedParameters::addSkipDirectory(const std::string& directory)
{
bi::scoped_lock lock(m_mutex);

m_directorySkipList.insert(shared::StringT(directory.begin(), directory.end(),
m_directorySkipList.get_allocator()));
}

void SharedParameters::clearSkipDirectories()
{
bi::scoped_lock lock(m_mutex);
m_directorySkipList.clear();
}

std::vector<std::string> SharedParameters::skipDirectories() const
{
bi::scoped_lock lock(m_mutex);
return { m_directorySkipList.begin(), m_directorySkipList.end() };
}

void SharedParameters::addForcedLibrary(
const std::string& processName, const std::string& libraryPath)
{
Expand Down
98 changes: 91 additions & 7 deletions src/usvfs_dll/usvfs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,33 @@ bool assertPathExists(usvfs::RedirectionTreeContainer &table, LPCWSTR path)
return true;
}

static bool fileNameInSkipSuffixes(const std::string& fileNameUtf8,
const std::vector<std::string>& skipFileSuffixes)
{
for (const auto& skipFileSuffix : skipFileSuffixes) {
if (boost::algorithm::iends_with(fileNameUtf8, skipFileSuffix)) {
spdlog::get("usvfs")->debug(
"file '{}' should be skipped, matches file suffix '{}'", fileNameUtf8,
skipFileSuffix);
return true;
}
}
return false;
}

static bool fileNameInSkipDirectories(const std::string& directoryNameUtf8,
const std::vector<std::string>& skipDirectories)
{
for (const auto& skipDir : skipDirectories) {
if (boost::algorithm::equals(directoryNameUtf8, skipDir)) {
spdlog::get("usvfs")->debug("directory '{}' should be skipped",
directoryNameUtf8);
return true;
}
}
return false;
}

BOOL WINAPI VirtualLinkFile(LPCWSTR source, LPCWSTR destination,
unsigned int flags)
{
Expand All @@ -683,8 +710,16 @@ BOOL WINAPI VirtualLinkFile(LPCWSTR source, LPCWSTR destination,
return FALSE;
}

std::string sourceU8
= ush::string_cast<std::string>(source, ush::CodePage::UTF8);
const auto skipFileSuffixes = context->skipFileSuffixes();

std::string sourceU8 = ush::string_cast<std::string>(source, ush::CodePage::UTF8);

// Check if the file should be skipped
if (fileNameInSkipSuffixes(sourceU8, skipFileSuffixes)) {
// return false when we want to fail when the file is skipped
return (flags & LINKFLAG_FAILIFSKIPPED) ? FALSE : TRUE;
}

auto res = context->redirectionTable().addFile(
bfs::path(destination), usvfs::RedirectionDataLocal(sourceU8),
!(flags & LINKFLAG_FAILIFEXISTS));
Expand Down Expand Up @@ -728,7 +763,6 @@ static usvfs::shared::TreeFlags convertRedirectionFlags(unsigned int flags)
return result;
}


BOOL WINAPI VirtualLinkDirectoryStatic(LPCWSTR source, LPCWSTR destination, unsigned int flags)
{
// TODO change notification not yet implemented
Expand All @@ -752,6 +786,9 @@ BOOL WINAPI VirtualLinkDirectoryStatic(LPCWSTR source, LPCWSTR destination, unsi
usvfs::shared::FLAG_DIRECTORY | convertRedirectionFlags(flags),
(flags & LINKFLAG_CREATETARGET) != 0);

const auto skipDirectories = context->skipDirectories();
const auto skipFileSuffixes = context->skipFileSuffixes();

if ((flags & LINKFLAG_RECURSIVE) != 0) {
std::wstring sourceP(source);
std::wstring sourceW = sourceP + L"\\";
Expand All @@ -763,13 +800,36 @@ BOOL WINAPI VirtualLinkDirectoryStatic(LPCWSTR source, LPCWSTR destination, unsi
winapi::ex::wide::quickFindFiles(sourceP.c_str(), L"*")) {
if (file.attributes & FILE_ATTRIBUTE_DIRECTORY) {
if ((file.fileName != L".") && (file.fileName != L"..")) {

const auto nameU8 = ush::string_cast<std::string>(file.fileName.c_str(),
ush::CodePage::UTF8);
// Check if the directory should be skipped
if (fileNameInSkipDirectories(nameU8, skipDirectories)) {
// Fail if we desire to fail when a dir/file is skipped
if (flags & LINKFLAG_FAILIFSKIPPED) {
spdlog::get("usvfs")->debug("directory '{}' skipped, failing as defined by link flags", nameU8);
return FALSE;
}

continue;
}

VirtualLinkDirectoryStatic((sourceW + file.fileName).c_str(),
(destinationW + file.fileName).c_str(),
flags);
(destinationW + file.fileName).c_str(), flags);
}
} else {
std::string nameU8 = ush::string_cast<std::string>(
file.fileName.c_str(), ush::CodePage::UTF8);
const auto nameU8 = ush::string_cast<std::string>(file.fileName.c_str(), ush::CodePage::UTF8);

// Check if the file should be skipped
if (fileNameInSkipSuffixes(nameU8, skipFileSuffixes)) {
// Fail if we desire to fail when a dir/file is skipped
if (flags & LINKFLAG_FAILIFSKIPPED) {
spdlog::get("usvfs")->debug("file '{}' skipped, failing as defined by link flags", nameU8);
return FALSE;
}

continue;
}

// TODO could save memory here by storing only the file name for the
// source and constructing the full name using the parent directory
Expand Down Expand Up @@ -878,6 +938,30 @@ VOID WINAPI ClearExecutableBlacklist()
}


VOID WINAPI usvfsAddSkipFileSuffix(LPWSTR fileSuffix)
{
context->addSkipFileSuffix(fileSuffix);
}


VOID WINAPI usvfsClearSkipFileSuffixes()
{
context->clearSkipFileSuffixes();
}


VOID WINAPI usvfsAddSkipDirectory(LPWSTR directory)
{
context->addSkipDirectory(directory);
}


VOID WINAPI usvfsClearSkipDirectories()
{
context->clearSkipDirectories();
}


VOID WINAPI ForceLoadLibrary(LPWSTR processName, LPWSTR libraryPath)
{
context->forceLoadLibrary(processName, libraryPath);
Expand Down