1 // Boost reparse_tag_file_placeholder.cpp ---------------------------------------------------------//
2
3 // Copyright Roman Savchenko 2020
4
5 // Distributed under the Boost Software License, Version 1.0.
6 // See http://www.boost.org/LICENSE_1_0.txt
7
8 // Library home page: http://www.boost.org/libs/filesystem
9
10 #include <iostream>
11
12 #if defined(BOOST_FILESYSTEM_HAS_MKLINK)
13
14 #include <boost/filesystem.hpp>
15 #include <boost/core/lightweight_test.hpp>
16
17 #include <cstddef>
18
19 # include <windows.h>
20 # include <winnt.h>
21
22 #ifdef _MSC_VER
23 # pragma comment(lib, "Advapi32.lib")
24 #endif
25
26 // Test correct boost::filesystem::status when reparse point ReparseTag set to IO_REPARSE_TAG_FILE_PLACEHOLDER
27 // https://docs.microsoft.com/en-us/windows/compatibility/placeholder-files?redirectedfrom=MSDN
28
29 #if !defined(__MINGW32__) || defined(__MINGW64__)
30 typedef struct _REPARSE_DATA_BUFFER {
31 ULONG ReparseTag;
32 USHORT ReparseDataLength;
33 USHORT Reserved;
34 union {
35 struct {
36 USHORT SubstituteNameOffset;
37 USHORT SubstituteNameLength;
38 USHORT PrintNameOffset;
39 USHORT PrintNameLength;
40 ULONG Flags;
41 WCHAR PathBuffer[1];
42 } SymbolicLinkReparseBuffer;
43 struct {
44 USHORT SubstituteNameOffset;
45 USHORT SubstituteNameLength;
46 USHORT PrintNameOffset;
47 USHORT PrintNameLength;
48 WCHAR PathBuffer[1];
49 } MountPointReparseBuffer;
50 struct {
51 UCHAR DataBuffer[1];
52 } GenericReparseBuffer;
53 };
54 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
55 #endif
56
57 #ifndef IO_REPARSE_TAG_FILE_PLACEHOLDER
58 # define IO_REPARSE_TAG_FILE_PLACEHOLDER (0x80000015L)
59 #endif
60
61 #ifndef FSCTL_SET_REPARSE_POINT
62 # define FSCTL_SET_REPARSE_POINT (0x000900a4)
63 #endif
64
65 #ifndef REPARSE_DATA_BUFFER_HEADER_SIZE
66 # define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
67 #endif
68
obtain_restore_privilege()69 bool obtain_restore_privilege()
70 {
71 HANDLE hToken;
72 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
73 {
74 std::cout << "OpenProcessToken() failed with: " << GetLastError() << std::endl;
75 return false;
76 }
77
78 TOKEN_PRIVILEGES tp;
79 if (!LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &tp.Privileges[0].Luid))
80 {
81 CloseHandle(hToken);
82 std::cout << "LookupPrivilegeValue() failed with: " << GetLastError() << std::endl;
83 return false;
84 }
85
86 tp.PrivilegeCount = 1;
87 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
88
89 if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
90 {
91 CloseHandle(hToken);
92 std::cout << "AdjustTokenPrivileges() failed with: " << GetLastError() << std::endl;
93 return false;
94 }
95
96 CloseHandle(hToken);
97 return true;
98 }
99
create_io_reparse_file_placeholder(const wchar_t * name)100 bool create_io_reparse_file_placeholder(const wchar_t* name)
101 {
102 if (!obtain_restore_privilege())
103 {
104 return false;
105 }
106
107 HANDLE hHandle = CreateFileW(name, GENERIC_WRITE,
108 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
109 FILE_FLAG_OPEN_REPARSE_POINT, 0);
110
111 if (hHandle == INVALID_HANDLE_VALUE)
112 {
113 std::cout << "CreateFile() failed with: " << GetLastError() << std::endl;
114 return false;
115 }
116
117 PREPARSE_DATA_BUFFER pReparse = reinterpret_cast<PREPARSE_DATA_BUFFER>(GlobalAlloc(GPTR, MAXIMUM_REPARSE_DATA_BUFFER_SIZE));
118 //note: IO_REPARSE_TAG_FILE_PLACEHOLDER - just to show that reparse point could be not only symlink or junction
119 pReparse->ReparseTag = IO_REPARSE_TAG_FILE_PLACEHOLDER;
120
121 DWORD dwLen;
122 bool ret = DeviceIoControl(hHandle, FSCTL_SET_REPARSE_POINT, pReparse,
123 pReparse->ReparseDataLength + REPARSE_DATA_BUFFER_HEADER_SIZE,
124 NULL, 0, &dwLen, NULL) != 0;
125
126 if (!ret)
127 {
128 std::cout << "DeviceIoControl() failed with: " << GetLastError() << std::endl;
129 }
130
131 CloseHandle(hHandle);
132 GlobalFree(pReparse);
133 return ret;
134 }
135
main()136 int main()
137 {
138 boost::filesystem::path rpt = boost::filesystem::temp_directory_path() / "reparse_point_test.txt";
139
140 BOOST_TEST(create_io_reparse_file_placeholder(rpt.native().c_str()));
141 BOOST_TEST(boost::filesystem::status(rpt).type() == boost::filesystem::reparse_file);
142 BOOST_TEST(boost::filesystem::remove(rpt));
143
144 return boost::report_errors();
145 }
146
147 #else // defined(BOOST_FILESYSTEM_HAS_MKLINK)
148
main()149 int main()
150 {
151 std::cout << "Skipping test as the target system does not support mklink." << std::endl;
152 return 0;
153 }
154
155 #endif // defined(BOOST_FILESYSTEM_HAS_MKLINK)
156