1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4// ============================================================
5//
6// Utils.cpp
7//
8
9
10//
11// Implements a bunch of binder auxilary functions
12//
13// ============================================================
14
15#define DISABLE_BINDER_DEBUG_LOGGING
16
17#include "utils.hpp"
18
19#include <shlwapi.h>
20
21#include "strongname.h"
22#include "corpriv.h"
23
24namespace BINDER_SPACE
25{
26 namespace
27 {
28 inline BOOL IsPathSeparator(WCHAR wcChar)
29 {
30 // Invariant: Valid only for MutateUrlToPath treated pathes
31 return (wcChar == W('\\'));
32 }
33
34 inline const WCHAR *GetPlatformPathSeparator()
35 {
36#ifdef PLATFORM_UNIX
37 return W("/");
38#else
39 return W("\\");
40#endif // PLATFORM_UNIX
41 }
42
43 inline WCHAR ToPlatformPathSepator(WCHAR wcChar)
44 {
45#ifdef PLATFORM_UNIX
46 if (IsPathSeparator(wcChar))
47 {
48 wcChar = W('/');
49 }
50#endif // PLATFORM_UNIX
51
52 return wcChar;
53 }
54
55 inline BOOL IsDoublePathSeparator(SString::CIterator &cur)
56 {
57 return (IsPathSeparator(cur[0]) && IsPathSeparator(cur[1]));
58 }
59
60 bool NeedToRemoveDoubleAndNormalizePathSeparators(SString const &path)
61 {
62#ifdef PLATFORM_UNIX
63 return true;
64#else
65 SString::CIterator begin = path.Begin();
66 SString::CIterator end = path.End();
67 SString::CIterator cur = path.Begin();
68
69 while (cur < end)
70 {
71 if ((cur != begin) && ((cur + 2) < end) && IsDoublePathSeparator(cur))
72 {
73 return true;
74 }
75
76 cur++;
77 }
78
79 return false;
80#endif
81 }
82
83 void RemoveDoubleAndNormalizePathSeparators(SString &path)
84 {
85 BINDER_LOG_ENTER(W("Utils::RemoveDoubleAndNormalizePathSeparators"));
86
87 SString::Iterator begin = path.Begin();
88 SString::Iterator end = path.End();
89 SString::Iterator cur = path.Begin();
90 PathString resultPath;
91
92 BINDER_LOG_STRING(W("path"), path);
93
94 while (cur < end)
95 {
96 if ((cur != begin) && ((cur + 2) < end) && IsDoublePathSeparator(cur))
97 {
98 // Skip the doublette
99 cur++;
100 }
101
102 resultPath.Append(ToPlatformPathSepator(cur[0]));
103 cur++;
104 }
105
106 BINDER_LOG_STRING(W("resultPath"), resultPath);
107
108 path.Set(resultPath);
109
110 BINDER_LOG_LEAVE(W("Utils::RemoveDoubleAndNormalizePathSeparators"));
111 }
112 }
113
114 HRESULT FileOrDirectoryExists(PathString &path)
115 {
116 HRESULT hr = S_FALSE;
117
118 DWORD dwFileAttributes = WszGetFileAttributes(path.GetUnicode());
119 if (dwFileAttributes == INVALID_FILE_ATTRIBUTES)
120 {
121 hr = HRESULT_FROM_GetLastError();
122
123 if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) ||
124 (hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)))
125 {
126 hr = S_FALSE;
127 }
128 }
129 else
130 {
131 hr = S_TRUE;
132 }
133
134 return hr;
135 }
136
137 HRESULT FileOrDirectoryExistsLog(PathString &path)
138 {
139 HRESULT hr = S_FALSE;
140 BINDER_LOG_ENTER(W("Utils::FileOrDirectoryExistsLog"));
141 BINDER_LOG_STRING(W("path"), path);
142
143 hr = FileOrDirectoryExists(path);
144
145 BINDER_LOG_LEAVE_HR(W("Utils::FileOrDirectoryExistsLog"), hr);
146 return hr;
147 }
148
149 void MutateUrlToPath(SString &urlOrPath)
150 {
151 BINDER_LOG_ENTER(W("Utils::MutateUrlToPath"));
152 const SString fileUrlPrefix(SString::Literal, W("file://"));
153 SString::Iterator i = urlOrPath.Begin();
154
155 BINDER_LOG_STRING(W("URL"), urlOrPath);
156
157 if (urlOrPath.MatchCaseInsensitive(i, fileUrlPrefix))
158 {
159 urlOrPath.Delete(i, fileUrlPrefix.GetCount());
160
161 i = urlOrPath.Begin() + 1;
162 if (i[0] == W(':'))
163 {
164 // CLR erroneously passes in file:// prepended to file paths,
165 // so we can't tell the difference between UNC and local file.
166 goto Exit;
167 }
168
169 i = urlOrPath.Begin();
170#if !defined(PLATFORM_UNIX)
171 if (i[0] == W('/'))
172 {
173 // Disk path file:///
174 urlOrPath.Delete(i, 1);
175 }
176 else if (i[0] != W('\\'))
177 {
178 // UNC Path, re-insert "//" if not the wrong file://\\...
179 urlOrPath.Insert(i, W("//"));
180 }
181#else
182 // Unix doesn't have a distinction between local and network path
183 _ASSERTE(i[0] == W('\\') || i[0] == W('/'));
184#endif
185 }
186
187 Exit:
188 while (urlOrPath.Find(i, W('/')))
189 {
190 urlOrPath.Replace(i, W('\\'));
191 }
192
193 BINDER_LOG_STRING(W("Path"), urlOrPath);
194 BINDER_LOG_LEAVE(W("Utils::MutateUrlToPath"));
195 }
196
197 void MutatePathToUrl(SString &pathOrUrl)
198 {
199 BINDER_LOG_ENTER(W("Utils::MutatePathToUrl"));
200 SString::Iterator i = pathOrUrl.Begin();
201
202 BINDER_LOG_STRING(W("Path"), pathOrUrl);
203
204#if !defined(PLATFORM_UNIX)
205 // Network path \\server --> file://server
206 // Disk path c:\dir --> file:///c:/dir
207 if (i[0] == W('\\'))
208 {
209 const SString networkUrlPrefix(SString::Literal, W("file:"));
210
211 // Network path
212 pathOrUrl.Insert(i, networkUrlPrefix);
213 pathOrUrl.Skip(i, networkUrlPrefix);
214 }
215 else
216 {
217 const SString diskPathUrlPrefix(SString::Literal, W("file:///"));
218
219 // Disk path
220 pathOrUrl.Insert(i, diskPathUrlPrefix);
221 pathOrUrl.Skip(i, diskPathUrlPrefix);
222 }
223#else
224 // Unix doesn't have a distinction between a network or a local path
225 _ASSERTE(i[0] == W('\\') || i[0] == W('/'));
226 const SString fileUrlPrefix(SString::Literal, W("file://"));
227
228 pathOrUrl.Insert(i, fileUrlPrefix);
229 pathOrUrl.Skip(i, fileUrlPrefix);
230#endif
231
232 while (pathOrUrl.Find(i, W('\\')))
233 {
234 pathOrUrl.Replace(i, W('/'));
235 }
236
237 BINDER_LOG_STRING(W("URL"), pathOrUrl);
238 BINDER_LOG_LEAVE(W("Utils::MutatePathToUrl"));
239 }
240
241 void PlatformPath(SString &path)
242 {
243 BINDER_LOG_ENTER(W("Utils::PlatformPath"));
244 BINDER_LOG_STRING(W("input path"), path);
245
246 // Create platform representation
247 MutateUrlToPath(path);
248 if (NeedToRemoveDoubleAndNormalizePathSeparators(path))
249 RemoveDoubleAndNormalizePathSeparators(path);
250
251 BINDER_LOG_STRING(W("platform path"), path);
252
253 BINDER_LOG_LEAVE(W("Utils::PlatformPath"));
254 }
255
256 void CombinePath(SString &pathA,
257 SString &pathB,
258 SString &combinedPath)
259 {
260 BINDER_LOG_ENTER(W("Utils::CombinePath"));
261
262 BINDER_LOG_STRING(W("path A"), pathA);
263 BINDER_LOG_STRING(W("path B"), pathB);
264
265 SString platformPathSeparator(SString::Literal, GetPlatformPathSeparator());
266 combinedPath.Set(pathA);
267
268 if (!combinedPath.EndsWith(platformPathSeparator))
269 {
270 combinedPath.Append(platformPathSeparator);
271 }
272
273 combinedPath.Append(pathB);
274
275 BINDER_LOG_LEAVE(W("Utils::CombinePath"));
276 }
277
278 HRESULT GetTokenFromPublicKey(SBuffer &publicKeyBLOB,
279 SBuffer &publicKeyTokenBLOB)
280 {
281 HRESULT hr = S_OK;
282 BINDER_LOG_ENTER(W("GetTokenFromPublicKey"));
283
284 const BYTE *pByteKey = publicKeyBLOB;
285 DWORD dwKeyLen = publicKeyBLOB.GetSize();
286 BYTE *pByteToken = NULL;
287 DWORD dwTokenLen = 0;
288
289 if (!StrongNameTokenFromPublicKey(const_cast<BYTE *>(pByteKey),
290 dwKeyLen,
291 &pByteToken,
292 &dwTokenLen))
293 {
294 BINDER_LOG(W("StrongNameTokenFromPublicKey failed!"));
295 IF_FAIL_GO(StrongNameErrorInfo());
296 }
297 else
298 {
299 _ASSERTE(pByteToken != NULL);
300 publicKeyTokenBLOB.Set(pByteToken, dwTokenLen);
301 StrongNameFreeBuffer(pByteToken);
302 }
303
304 Exit:
305 BINDER_LOG_LEAVE_HR(W("GetTokenFromPublicKey"), hr);
306 return hr;
307 }
308
309 BOOL IsFileNotFound(HRESULT hr)
310 {
311 return RuntimeFileNotFound(hr);
312 }
313};
314