| // 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) |