in fixups/FileRedirectionFixup/PathRedirection.cpp [1194:1442]
static path_redirect_info ShouldRedirectImpl(const CharT* path, redirect_flags flags, DWORD inst)
{
path_redirect_info result;
if (!path)
{
return result;
}
LogString(inst, L"\tFRF Should: for path", widen(path).c_str());
bool c_presense = flag_set(flags, redirect_flags::check_file_presence);
bool c_copy = flag_set(flags, redirect_flags::copy_file);
bool c_ensure = flag_set(flags, redirect_flags::ensure_directory_structure);
Log(L"[%d]\t\tFRF flags CheckPresense:%d CopyFile:%d EnsureDirectory:%d", inst, c_presense, c_copy, c_ensure);
// normalizedPath represents the requested path, redirected to the external system if relevant, or just as requested if not.
// vfsPath represents this as a package relative path
auto normalizedPath = NormalizePath(path);
std::filesystem::path destinationTargetBase;
if (normalizedPath.path_type == psf::dos_path_type::local_device)
{
LogString(L"\t\tFRF: Path is of type local device so FRF should ignore.", path);
return result;
}
if (!normalizedPath.drive_absolute_path)
{
// FUTURE: We could do better about canonicalising paths, but the cost/benefit doesn't make it worth it right now
return result;
}
LogString(inst, L"\t\tFRF Normalized", normalizedPath.drive_absolute_path);
// To be consistent in where we redirect files, we need to map VFS paths to their non-package-relative equivalent
normalizedPath = DeVirtualizePath(std::move(normalizedPath));
LogString(inst, L"\t\tFRF DeVirtualized", normalizedPath.drive_absolute_path);
// If you change the below logic, or
// you you change what goes into RedirectedPath
// you need to mirror all changes in FindFirstFileFixup.cpp
// Basically, what goes into RedirectedPath here also needs to go into
// FindFirstFileFixup.cpp
auto vfspath = NormalizePath(path);
vfspath = VirtualizePath(std::move(vfspath),inst);
if (vfspath.drive_absolute_path != NULL)
{
LogString(inst, L"\t\tFRF Virtualized", vfspath.drive_absolute_path);
}
// Figure out if this is something we need to redirect
for (auto& redirectSpec : g_redirectionSpecs)
{
//LogString(inst, L"\t\tFRF Check against: base", redirectSpec.base_path.c_str());
if (path_relative_to(vfspath.drive_absolute_path, redirectSpec.base_path))
{
LogString(inst, L"\t\tFRF In ball park of base", redirectSpec.base_path.c_str());
auto relativePath = vfspath.drive_absolute_path + redirectSpec.base_path.native().length();
if (psf::is_path_separator(relativePath[0]))
{
++relativePath;
}
else if (relativePath[0])
{
// Otherwise, just a substring match (e.g. we're trying to match against 'foo' but input was 'foobar')
continue;
}
// Otherwise exact match. Assume an implicit directory separator at the end (e.g. for matches to satisfy the
// first call to CreateDirectory
LogString(inst, L"\t\t\tFRF relativePath",relativePath);
if (std::regex_match(relativePath, redirectSpec.pattern))
{
if (redirectSpec.isExclusion)
{
// The impact on isExclusion is that redirection is not needed.
result.should_redirect = false;
LogString(inst, L"\t\tFRF CASE:Exclusion for path", path);
}
else
{
result.should_redirect = true;
result.shouldReadonly = (redirectSpec.isReadOnly == true);
// Check if file exists as VFS path in the package
if (impl::PathExists(vfspath.drive_absolute_path))
{
Log(L"[%d]\t\t\tFRF CASE:match, existing in package.", inst);
destinationTargetBase = redirectSpec.redirect_targetbase;
result.redirect_path = RedirectedPath(vfspath, flag_set(flags, redirect_flags::ensure_directory_structure), destinationTargetBase,inst);
}
else
{
Log(L"[%d]\t\t\tFRF CASE:match, not existing in package.",inst);
// If the folder above it exists, we might want to redirect anyway?
std::filesystem::path abs = vfspath.drive_absolute_path;
if (impl::PathExists(abs.parent_path().c_str()))
{
Log(L"[%d]\t\t\tFRF SUBCASE: parent folder is in package.",inst);
destinationTargetBase = redirectSpec.redirect_targetbase;
//result.redirect_path = RedirectedPath(normalizedPath, flag_set(flags, redirect_flags::ensure_directory_structure), destinationTargetBase);
result.redirect_path = RedirectedPath(vfspath, flag_set(flags, redirect_flags::ensure_directory_structure), destinationTargetBase,inst);
}
else
{
Log(L"[%d]\t\t\tFRF SUBCASE: parent folder is also not in package, but since relative should redirect.", inst);
//result.should_redirect = false;
destinationTargetBase = redirectSpec.redirect_targetbase;
result.redirect_path = RedirectedPath(vfspath, flag_set(flags, redirect_flags::ensure_directory_structure), destinationTargetBase,inst);
}
}
if (result.should_redirect)
LogString(inst, L"\t\tFRF CASE:match on redirect_path", result.redirect_path.c_str());
}
break;
}
else
{
LogString(inst, L"\t\tFRF no match on parse relativePath", relativePath);
}
}
else
{
LogString(inst, L"\t\tFRF Not in ball park of base", redirectSpec.base_path.c_str());
}
}
Log(L"[%d]\t\tFRF post check 1",inst);
if (!result.should_redirect)
{
LogString(inst, L"\tFRF no redirect rule for path", path);
return result;
}
Log(L"[%d]\t\tFRF post check 2",inst);
if (flag_set(flags, redirect_flags::check_file_presence))
{
if (!impl::PathExists(result.redirect_path.c_str()) &&
!impl::PathExists(vfspath.drive_absolute_path) &&
!impl::PathExists(normalizedPath.drive_absolute_path))
{
result.should_redirect = false;
result.redirect_path.clear();
LogString(inst, L"\tFRF skipped (redirected not present check failed) for path", path);
return result;
}
}
Log(L"[%d]\t\tFRF post check 3",inst);
if (flag_set(flags, redirect_flags::copy_file))
{
Log(L"[%d]\t\tFRF copy_file flag is set",inst);
[[maybe_unused]] BOOL copyResult = false;
if (impl::PathExists(result.redirect_path.c_str()))
{
Log(L"[%d]\t\tFRF Found that a copy exists in the redirected area so we skip the folder creation.",inst);
}
else
{
std::filesystem::path CopySource = normalizedPath.drive_absolute_path;
if (impl::PathExists(vfspath.drive_absolute_path))
{
CopySource = vfspath.drive_absolute_path;
}
auto attr = impl::GetFileAttributes(CopySource.c_str()); //normalizedPath.drive_absolute_path);
Log(L"[%d]\t\tFRF source %ls attributes=0x%x", inst, CopySource.c_str(), attr);
if (attr != INVALID_FILE_ATTRIBUTES)
{
if ((attr & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)
{
Log(L"[%d]FRF we have a file to be copied to %ls", inst, result.redirect_path.c_str());
copyResult = impl::CopyFileEx(
CopySource.c_str(), //normalizedPath.drive_absolute_path,
result.redirect_path.c_str(),
nullptr,
nullptr,
nullptr,
COPY_FILE_FAIL_IF_EXISTS | COPY_FILE_NO_BUFFERING);
if (copyResult)
{
LogString(inst, L"\t\tFRF CopyFile Success From", CopySource.c_str());
LogString(inst, L"\t\tFRF CopyFile Success To", result.redirect_path.c_str());
}
else
{
auto err = ::GetLastError();
Log("[%d]\t\tFRF CopyFile Fail=0x%x", inst, err);
LogString(inst, L"\t\tFRF CopyFile Fail From", CopySource.c_str());
LogString(inst, L"\t\tFRF CopyFile Fail To", result.redirect_path.c_str());
switch (err)
{
case ERROR_FILE_EXISTS:
Log(L"[%d]\t\tFRF was ERROR_FILE_EXISTS", inst);
break;
case ERROR_PATH_NOT_FOUND:
Log(L"[%d]\t\tFRF was ERROR_PATH_NOT_FOUND", inst);
break;
case ERROR_FILE_NOT_FOUND:
Log(L"[%d]\t\tFRF was ERROR_FILE_NOT_FOUND", inst);
break;
case ERROR_ALREADY_EXISTS:
Log(L"[%d]\t\tFRF was ERROR_ALREADY_EXISTS", inst);
break;
default:
Log(L"[%d]\t\tFRF was 0x%x", inst, err);
break;
}
}
}
else
{
Log(L"[%d]FRF we have a directory to be copied to %ls.", inst, result.redirect_path.c_str());
copyResult = impl::CreateDirectoryEx(CopySource.c_str(), result.redirect_path.c_str(), nullptr);
if (copyResult)
{
LogString(inst, L"\t\tFRF CreateDir Success From", CopySource.c_str());
LogString(inst, L"\t\tFRF CreateDir Success To", result.redirect_path.c_str());
}
else
{
Log("[%d]\t\tFRF CreateDir Fail=0x%x", inst, ::GetLastError());
LogString(inst, L"\t\tFRF CreateDir Fail From", CopySource.c_str());
LogString(inst, L"\t\tFRF CreateDir Fail To", result.redirect_path.c_str());
}
#if _DEBUG
auto err = ::GetLastError();
assert(copyResult || (err == ERROR_FILE_EXISTS) || (err == ERROR_PATH_NOT_FOUND) || (err == ERROR_FILE_NOT_FOUND) || (err == ERROR_ALREADY_EXISTS));
#endif
}
}
else
{
//There is no source to copy, so we just want to allow it to be created in the redirected area.
Log(L"[%d]FRF there is no package file to be copied to %ls.", inst, result.redirect_path.c_str());
}
}
}
LogString(inst, L"\tFRF Should: Redirect to result", result.redirect_path.c_str());
return result;
}