blob: 39febffbb151702d90289f3692c934db15eff8e3 [file] [log] [blame]
// Boost reparse_tag_file_placeholder.cpp ---------------------------------------------------------//
// Copyright Roman Savchenko 2020
// Distributed under the Boost Software License, Version 1.0.
// See http://www.boost.org/LICENSE_1_0.txt
// Library home page: http://www.boost.org/libs/filesystem
#include <iostream>
#if defined(BOOST_FILESYSTEM_HAS_MKLINK)
#include <boost/filesystem.hpp>
#include <boost/core/lightweight_test.hpp>
#include <cstddef>
# include <windows.h>
# include <winnt.h>
#ifdef _MSC_VER
# pragma comment(lib, "Advapi32.lib")
#endif
// Test correct boost::filesystem::status when reparse point ReparseTag set to IO_REPARSE_TAG_FILE_PLACEHOLDER
// https://docs.microsoft.com/en-us/windows/compatibility/placeholder-files?redirectedfrom=MSDN
#if !defined(__MINGW32__) || defined(__MINGW64__)
typedef struct _REPARSE_DATA_BUFFER {
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
union {
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct {
UCHAR DataBuffer[1];
} GenericReparseBuffer;
};
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
#endif
#ifndef IO_REPARSE_TAG_FILE_PLACEHOLDER
# define IO_REPARSE_TAG_FILE_PLACEHOLDER (0x80000015L)
#endif
#ifndef FSCTL_SET_REPARSE_POINT
# define FSCTL_SET_REPARSE_POINT (0x000900a4)
#endif
#ifndef REPARSE_DATA_BUFFER_HEADER_SIZE
# define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
#endif
bool obtain_restore_privilege()
{
HANDLE hToken;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
std::cout << "OpenProcessToken() failed with: " << GetLastError() << std::endl;
return false;
}
TOKEN_PRIVILEGES tp;
if (!LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &tp.Privileges[0].Luid))
{
CloseHandle(hToken);
std::cout << "LookupPrivilegeValue() failed with: " << GetLastError() << std::endl;
return false;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
{
CloseHandle(hToken);
std::cout << "AdjustTokenPrivileges() failed with: " << GetLastError() << std::endl;
return false;
}
CloseHandle(hToken);
return true;
}
bool create_io_reparse_file_placeholder(const wchar_t* name)
{
if (!obtain_restore_privilege())
{
return false;
}
HANDLE hHandle = CreateFileW(name, GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
FILE_FLAG_OPEN_REPARSE_POINT, 0);
if (hHandle == INVALID_HANDLE_VALUE)
{
std::cout << "CreateFile() failed with: " << GetLastError() << std::endl;
return false;
}
PREPARSE_DATA_BUFFER pReparse = reinterpret_cast<PREPARSE_DATA_BUFFER>(GlobalAlloc(GPTR, MAXIMUM_REPARSE_DATA_BUFFER_SIZE));
//note: IO_REPARSE_TAG_FILE_PLACEHOLDER - just to show that reparse point could be not only symlink or junction
pReparse->ReparseTag = IO_REPARSE_TAG_FILE_PLACEHOLDER;
DWORD dwLen;
bool ret = DeviceIoControl(hHandle, FSCTL_SET_REPARSE_POINT, pReparse,
pReparse->ReparseDataLength + REPARSE_DATA_BUFFER_HEADER_SIZE,
NULL, 0, &dwLen, NULL) != 0;
if (!ret)
{
std::cout << "DeviceIoControl() failed with: " << GetLastError() << std::endl;
}
CloseHandle(hHandle);
GlobalFree(pReparse);
return ret;
}
int main()
{
boost::filesystem::path rpt = boost::filesystem::temp_directory_path() / "reparse_point_test.txt";
BOOST_TEST(create_io_reparse_file_placeholder(rpt.native().c_str()));
BOOST_TEST(boost::filesystem::status(rpt).type() == boost::filesystem::reparse_file);
BOOST_TEST(boost::filesystem::remove(rpt));
return boost::report_errors();
}
#else // defined(BOOST_FILESYSTEM_HAS_MKLINK)
int main()
{
std::cout << "Skipping test as the target system does not support mklink." << std::endl;
return 0;
}
#endif // defined(BOOST_FILESYSTEM_HAS_MKLINK)