• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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