1 | //////////////////////////////////////////////////////////// |
---|
2 | // Copyright (C) Roman Ryltsov, 2008-2013 |
---|
3 | // Created by Roman Ryltsov roman@alax.info |
---|
4 | |
---|
5 | #pragma once |
---|
6 | |
---|
7 | #include "reparse.h" |
---|
8 | #include "roinifile.h" |
---|
9 | |
---|
10 | //////////////////////////////////////////////////////////// |
---|
11 | // CModule |
---|
12 | |
---|
13 | class CModule : |
---|
14 | public CAtlExeModuleT<CModule> |
---|
15 | { |
---|
16 | public: |
---|
17 | |
---|
18 | //////////////////////////////////////////////////////// |
---|
19 | // CReparsePointData |
---|
20 | |
---|
21 | class CReparsePointData |
---|
22 | { |
---|
23 | public: |
---|
24 | REPARSE_GUID_DATA_BUFFER m_ReparseGuidDataBufferHeader; |
---|
25 | REPARSE_DATA_BUFFER m_ReparseDataBufferHeader; |
---|
26 | CStringW m_sPrintName; |
---|
27 | CStringW m_sSubstituteName; |
---|
28 | |
---|
29 | static CStringW StringFromCharacters(const WCHAR* pszCharacters, SIZE_T nLength) |
---|
30 | { |
---|
31 | CStringW sString; |
---|
32 | if(nLength) |
---|
33 | { |
---|
34 | _A(pszCharacters); |
---|
35 | memcpy(sString.GetBufferSetLength((INT) nLength), pszCharacters, nLength * sizeof *pszCharacters); |
---|
36 | } |
---|
37 | return sString; |
---|
38 | } |
---|
39 | VOID Clear() |
---|
40 | { |
---|
41 | ZeroMemory(&m_ReparseGuidDataBufferHeader, sizeof m_ReparseGuidDataBufferHeader); |
---|
42 | ZeroMemory(&m_ReparseDataBufferHeader, sizeof m_ReparseDataBufferHeader); |
---|
43 | m_sPrintName.Empty(); |
---|
44 | m_sSubstituteName.Empty(); |
---|
45 | } |
---|
46 | |
---|
47 | public: |
---|
48 | // CReparsePointData |
---|
49 | CReparsePointData() |
---|
50 | { |
---|
51 | Clear(); |
---|
52 | } |
---|
53 | CReparsePointData(const CPath& sPath) |
---|
54 | { |
---|
55 | Initialize(sPath); |
---|
56 | } |
---|
57 | VOID Initialize(const CPath& sPath) |
---|
58 | { |
---|
59 | Clear(); |
---|
60 | _ATLTRY |
---|
61 | { |
---|
62 | CAtlFile Directory; |
---|
63 | HRESULT nCreateResult = Directory.Create(sPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT); |
---|
64 | if(FAILED(nCreateResult)) |
---|
65 | { |
---|
66 | _Z4(atlTraceGeneral, 4, _T("nCreateResult 0x%08x\n"), nCreateResult); |
---|
67 | return; |
---|
68 | } |
---|
69 | BYTE pnReparseBuffer[MAX_REPARSE_SIZE]; |
---|
70 | REPARSE_GUID_DATA_BUFFER* pReparseGuidDataBuffer = (REPARSE_GUID_DATA_BUFFER*) pnReparseBuffer; |
---|
71 | DWORD nReturnedLength; |
---|
72 | if(!DeviceIoControl(Directory, FSCTL_GET_REPARSE_POINT, NULL, 0, pReparseGuidDataBuffer, sizeof pnReparseBuffer, &nReturnedLength, NULL)) |
---|
73 | { |
---|
74 | HRESULT nResult = HRESULT_FROM_WIN32(GetLastError()); |
---|
75 | _Z4(atlTraceGeneral, 4, _T("nCreateResult 0x%08x\n"), nResult); |
---|
76 | return; |
---|
77 | } |
---|
78 | m_ReparseGuidDataBufferHeader = *pReparseGuidDataBuffer; |
---|
79 | _Z4(atlTraceGeneral, 4, _T("ReparseTag 0x%08x\n"), pReparseGuidDataBuffer->ReparseTag); |
---|
80 | if(IsReparseTagMicrosoft(pReparseGuidDataBuffer->ReparseTag)) |
---|
81 | { |
---|
82 | switch(pReparseGuidDataBuffer->ReparseTag & 0xFFFFFFFF) |
---|
83 | { |
---|
84 | case IO_REPARSE_TAG_SYMBOLIC_LINK: |
---|
85 | { |
---|
86 | REPARSE_DATA_BUFFER* pReparseDataBuffer = (REPARSE_DATA_BUFFER*) pReparseGuidDataBuffer; |
---|
87 | m_ReparseDataBufferHeader = *pReparseDataBuffer; |
---|
88 | CStringW sPrintName = StringFromCharacters((PWCHAR) ((BYTE*) pReparseDataBuffer->SymbolicLinkReparseBuffer.PathBuffer + pReparseDataBuffer->SymbolicLinkReparseBuffer.PrintNameOffset), pReparseDataBuffer->SymbolicLinkReparseBuffer.PrintNameLength); |
---|
89 | CStringW sSubstituteName = StringFromCharacters((PWCHAR) ((BYTE*) pReparseDataBuffer->SymbolicLinkReparseBuffer.PathBuffer + pReparseDataBuffer->SymbolicLinkReparseBuffer.SubstituteNameOffset), pReparseDataBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength); |
---|
90 | _Z4(atlTraceGeneral, 4, _T("sPrintName \"%ls\", sSubstituteName \"%ls\"\n"), sPrintName, sSubstituteName); |
---|
91 | m_sPrintName = sPrintName; |
---|
92 | m_sSubstituteName = sSubstituteName; |
---|
93 | } |
---|
94 | break; |
---|
95 | case IO_REPARSE_TAG_MOUNT_POINT: |
---|
96 | { |
---|
97 | REPARSE_DATA_BUFFER* pReparseDataBuffer = (REPARSE_DATA_BUFFER*) pReparseGuidDataBuffer; |
---|
98 | m_ReparseDataBufferHeader = *pReparseDataBuffer; |
---|
99 | CStringW sPrintName = StringFromCharacters((PWCHAR) ((BYTE*) pReparseDataBuffer->SymbolicLinkReparseBuffer.PathBuffer + pReparseDataBuffer->SymbolicLinkReparseBuffer.PrintNameOffset), pReparseDataBuffer->SymbolicLinkReparseBuffer.PrintNameLength); |
---|
100 | CStringW sSubstituteName = StringFromCharacters((PWCHAR) ((BYTE*) pReparseDataBuffer->SymbolicLinkReparseBuffer.PathBuffer + pReparseDataBuffer->SymbolicLinkReparseBuffer.SubstituteNameOffset), pReparseDataBuffer->SymbolicLinkReparseBuffer.SubstituteNameLength); |
---|
101 | _Z4(atlTraceGeneral, 4, _T("sPrintName \"%ls\", sSubstituteName \"%ls\"\n"), sPrintName, sSubstituteName); |
---|
102 | // NOTE: According to Mark, junctions contain ':' unlike mount points |
---|
103 | m_sPrintName = sPrintName; |
---|
104 | m_sSubstituteName = sSubstituteName; |
---|
105 | } |
---|
106 | break; |
---|
107 | //case IO_REPARSE_TAG_HSM: |
---|
108 | //case IO_REPARSE_TAG_SIS: |
---|
109 | //case IO_REPARSE_TAG_DFS: |
---|
110 | } |
---|
111 | } |
---|
112 | } |
---|
113 | _ATLCATCHALL() |
---|
114 | { |
---|
115 | _Z1(atlTraceGeneral, 1, _T("An exception has been caught\n")); |
---|
116 | } |
---|
117 | } |
---|
118 | CStringW GetFriendlySubstituteName() const |
---|
119 | { |
---|
120 | CStringW sSubstituteName = m_sSubstituteName; |
---|
121 | if(sSubstituteName.Left(4) == L"\\??\\") |
---|
122 | sSubstituteName.Delete(0, 4); |
---|
123 | return sSubstituteName; |
---|
124 | } |
---|
125 | }; |
---|
126 | |
---|
127 | public: |
---|
128 | //CPath m_sIniFilePath; |
---|
129 | //CObjectPtr<CIniFile::CSection> m_pSection; |
---|
130 | CPath m_sPath; |
---|
131 | UINT m_nDirectoryCount; |
---|
132 | UINT m_nReparsePointCount; |
---|
133 | |
---|
134 | public: |
---|
135 | // CModule |
---|
136 | CModule() |
---|
137 | { |
---|
138 | AtlTraceSetDefaultSettings(); |
---|
139 | _Z4_THIS(); |
---|
140 | _W(CExceptionFilter::Initialize()); |
---|
141 | } |
---|
142 | ~CModule() |
---|
143 | { |
---|
144 | _Z4_THIS(); |
---|
145 | CExceptionFilter::Terminate(); |
---|
146 | } |
---|
147 | HRESULT PreMessageLoop(INT nShowCommand) |
---|
148 | { |
---|
149 | const HRESULT nResult = __super::PreMessageLoop(nShowCommand); |
---|
150 | return SUCCEEDED(nResult) ? S_OK : nResult; |
---|
151 | } |
---|
152 | BOOL Syntax() |
---|
153 | { |
---|
154 | _tprintf(_T("Syntax: Junctions <path>\n")); |
---|
155 | return FALSE; |
---|
156 | } |
---|
157 | BOOL ParseCommandLineEx(SIZE_T argc, TCHAR* argv[]) |
---|
158 | { |
---|
159 | for(SIZE_T nIndex = 1; nIndex < argc; nIndex++) |
---|
160 | { |
---|
161 | CString sArgument = argv[nIndex]; |
---|
162 | _A(!sArgument.IsEmpty()); |
---|
163 | #pragma region Switches |
---|
164 | if(_tcschr(_T("-/"), sArgument[0])) |
---|
165 | { |
---|
166 | sArgument.Delete(0); |
---|
167 | #pragma region Switch Value/Specification |
---|
168 | const INT nSeparatorPosition = sArgument.Find(_T(':')); |
---|
169 | CString sArgumentValue; |
---|
170 | BOOL bIntegerArgumentValueValid = FALSE; |
---|
171 | INT nIntegerArgumentValue = 0; |
---|
172 | if(nSeparatorPosition > 0) |
---|
173 | { |
---|
174 | sArgumentValue = sArgument.Mid(nSeparatorPosition + 1); |
---|
175 | sArgument = sArgument.Left(nSeparatorPosition); |
---|
176 | if(!sArgumentValue.IsEmpty()) |
---|
177 | bIntegerArgumentValueValid = AtlStringToInteger(sArgumentValue, nIntegerArgumentValue); |
---|
178 | } |
---|
179 | #pragma endregion |
---|
180 | /* |
---|
181 | if(sArgument.CompareNoCase(_T("f")) == 0) // Flavor |
---|
182 | { |
---|
183 | __D(bIntegerArgumentValueValid, E_INVALIDARG); |
---|
184 | m_nFlavor = (UINT) nIntegerArgumentValue; |
---|
185 | m_bFlavorAvailable = TRUE; |
---|
186 | continue; |
---|
187 | } |
---|
188 | if(sArgument.CompareNoCase(_T("fl")) == 0) // Flavor List |
---|
189 | { |
---|
190 | m_bListFlavors = TRUE; |
---|
191 | continue; |
---|
192 | } |
---|
193 | */ |
---|
194 | } |
---|
195 | #pragma endregion |
---|
196 | #pragma region Non-switch |
---|
197 | if(sArgument.GetLength() >= 2 && sArgument[0] == _T('"') && sArgument[sArgument.GetLength() - 1] == _T('"')) |
---|
198 | sArgument = sArgument.Mid(1, sArgument.GetLength() - 2); |
---|
199 | if(_tcslen(m_sPath)) |
---|
200 | return Syntax(); |
---|
201 | m_sPath = (LPCTSTR) sArgument; |
---|
202 | #pragma endregion |
---|
203 | } |
---|
204 | #pragma region Validation |
---|
205 | if(!_tcslen(m_sPath)) |
---|
206 | return Syntax(); |
---|
207 | #pragma endregion |
---|
208 | return TRUE; |
---|
209 | } |
---|
210 | VOID ProcessDirectory(const CPath& sDirectory) |
---|
211 | { |
---|
212 | _Y1(AtlFormatString(_T("sDirectory \"%s\""), sDirectory)); |
---|
213 | CFindFiles FindFiles; |
---|
214 | for(BOOL bFound = FindFiles.FindFirstFile(sDirectory, _T("*.*")); bFound; bFound = FindFiles.FindNextFile()) |
---|
215 | { |
---|
216 | if(!(FindFiles.GetFindData().dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) |
---|
217 | continue; // Not a Directory |
---|
218 | if(_tcscmp(FindFiles.GetFindData().cFileName, _T(".")) == 0 || _tcscmp(FindFiles.GetFindData().cFileName, _T("..")) == 0) |
---|
219 | continue; // Not a Real Directory |
---|
220 | m_nDirectoryCount++; |
---|
221 | CPath sPath; |
---|
222 | sPath.Combine(sDirectory, FindFiles.GetFindData().cFileName); |
---|
223 | if(FindFiles.GetFindData().dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) |
---|
224 | _ATLTRY |
---|
225 | { |
---|
226 | CReparsePointData ReparsePointData(sPath); |
---|
227 | _tprintf(_T("%s -> %ls\n"), sPath, ReparsePointData.GetFriendlySubstituteName()); |
---|
228 | m_nReparsePointCount++; |
---|
229 | } |
---|
230 | _ATLCATCH(Exception) |
---|
231 | { |
---|
232 | _Z_ATLEXCEPTION(Exception); |
---|
233 | } |
---|
234 | else |
---|
235 | ProcessDirectory(sPath); |
---|
236 | } |
---|
237 | } |
---|
238 | VOID RunMessageLoop() |
---|
239 | { |
---|
240 | #pragma region .INI File |
---|
241 | //CPath sIniFilePath = CIniFile::GetDefaultPath(); |
---|
242 | //CIniFile::LookupParentDirectoryPath(sIniFilePath, 2); |
---|
243 | //const CObjectPtr<CIniFile::CSection> pSection = CIniFile::Open(sIniFilePath, CIniFile::GetDefaultSection()); |
---|
244 | //m_sIniFilePath = sIniFilePath; |
---|
245 | //m_pSection = pSection; |
---|
246 | #pragma endregion |
---|
247 | _tprintf(_T("Base path: %s\n"), m_sPath); |
---|
248 | m_nDirectoryCount = 0; |
---|
249 | m_nReparsePointCount = 0; |
---|
250 | ProcessDirectory(m_sPath); |
---|
251 | _tprintf(_T("Found directories: %d\n"), m_nDirectoryCount); |
---|
252 | _tprintf(_T("Found reparse points: %d\n"), m_nReparsePointCount); |
---|
253 | } |
---|
254 | }; |
---|
255 | |
---|