VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/nt/pathint-nt.cpp@ 77231

Last change on this file since 77231 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.5 KB
Line 
1/* $Id: pathint-nt.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * IPRT - Native NT, Internal Path stuff.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_FS
32#include "internal-r3-nt.h"
33
34#include <iprt/assert.h>
35#include <iprt/err.h>
36#include <iprt/mem.h>
37#include <iprt/path.h>
38#include <iprt/string.h>
39#include <iprt/utf16.h>
40
41
42/*********************************************************************************************************************************
43* Global Variables *
44*********************************************************************************************************************************/
45static char const g_szPrefixUnc[] = "\\??\\UNC\\";
46static char const g_szPrefix[] = "\\??\\";
47static char const g_szPrefixNt3xUnc[] = "\\DosDevices\\UNC\\";
48static char const g_szPrefixNt3x[] = "\\DosDevices\\";
49
50
51/**
52 * Handles the pass thru case for UTF-8 input.
53 * Win32 path uses "\\?\" prefix which is converted to "\??\" NT prefix.
54 *
55 * @returns IPRT status code.
56 * @param pNtName Where to return the NT name.
57 * @param phRootDir Where to return the root handle, if applicable.
58 * @param pszPath The UTF-8 path.
59 */
60static int rtNtPathFromWinUtf8PassThru(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
61{
62 PRTUTF16 pwszPath = NULL;
63 size_t cwcLen;
64 int rc = RTStrToUtf16Ex(pszPath, RTSTR_MAX, &pwszPath, 0, &cwcLen);
65 if (RT_SUCCESS(rc))
66 {
67 if (cwcLen < _32K - 1)
68 {
69 *phRootDir = NULL;
70 if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) >= RT_MAKE_U64(0, 4))
71 {
72 pwszPath[0] = '\\';
73 pwszPath[1] = '?';
74 pwszPath[2] = '?';
75 pwszPath[3] = '\\';
76
77 pNtName->Buffer = pwszPath;
78 pNtName->Length = (uint16_t)(cwcLen * sizeof(RTUTF16));
79 pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
80 return VINF_SUCCESS;
81 }
82
83 rc = RTUtf16Realloc(&pwszPath, cwcLen + sizeof(g_szPrefixNt3x));
84 if (RT_SUCCESS(rc))
85 {
86 memmove(&pwszPath[sizeof(g_szPrefixNt3x) - 1], &pwszPath[4], (cwcLen - 4 + 1) * sizeof(RTUTF16));
87 for (uint32_t i = 0; i < sizeof(g_szPrefixNt3x) - 1; i++)
88 pwszPath[i] = g_szPrefixNt3x[i];
89
90 pNtName->Buffer = pwszPath;
91 pNtName->Length = (uint16_t)((cwcLen - 4 + sizeof(g_szPrefixNt3x) - 1) * sizeof(RTUTF16));
92 pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
93 return VINF_SUCCESS;
94 }
95 }
96
97 RTUtf16Free(pwszPath);
98 rc = VERR_FILENAME_TOO_LONG;
99 }
100 return rc;
101}
102
103
104/**
105 * Handles the pass thru case for UTF-16 input.
106 * Win32 path uses "\\?\" prefix which is converted to "\??\" NT prefix.
107 *
108 * @returns IPRT status code.
109 * @param pNtName Where to return the NT name.
110 * @param phRootDir Stores NULL here, as we don't use it.
111 * @param pwszWinPath The UTF-16 windows-style path.
112 * @param cwcWinPath The length of the windows-style input path.
113 */
114static int rtNtPathFromWinUtf16PassThru(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir,
115 PCRTUTF16 pwszWinPath, size_t cwcWinPath)
116{
117 /* Check length and allocate memory for it. */
118 int rc;
119 if (cwcWinPath < _32K - 1)
120 {
121
122 size_t const cwcExtraPrefix = RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion)
123 >= RT_MAKE_U64(0, 4)
124 ? 0 : sizeof(g_szPrefixNt3x) - 1 - 4;
125 PRTUTF16 pwszNtPath = (PRTUTF16)RTUtf16Alloc((cwcExtraPrefix + cwcWinPath + 1) * sizeof(RTUTF16));
126 if (pwszNtPath)
127 {
128 /* Intialize the path. */
129 if (!cwcExtraPrefix)
130 {
131 pwszNtPath[0] = '\\';
132 pwszNtPath[1] = '?';
133 pwszNtPath[2] = '?';
134 pwszNtPath[3] = '\\';
135 }
136 else
137 for (uint32_t i = 0; i < sizeof(g_szPrefixNt3x) - 1; i++)
138 pwszNtPath[i] = g_szPrefixNt3x[i];
139 memcpy(pwszNtPath + cwcExtraPrefix + 4, pwszWinPath + 4, (cwcWinPath - 4) * sizeof(RTUTF16));
140 pwszNtPath[cwcExtraPrefix + cwcWinPath] = '\0';
141
142 /* Initialize the return values. */
143 pNtName->Buffer = pwszNtPath;
144 pNtName->Length = (uint16_t)(cwcExtraPrefix + cwcWinPath * sizeof(RTUTF16));
145 pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
146 *phRootDir = NULL;
147
148 rc = VINF_SUCCESS;
149 }
150 else
151 rc = VERR_NO_UTF16_MEMORY;
152 }
153 else
154 rc = VERR_FILENAME_TOO_LONG;
155 return rc;
156}
157
158
159
160
161
162/**
163 * Converts the path to UTF-16 and sets all the return values.
164 *
165 * @returns IPRT status code.
166 * @param pNtName Where to return the NT name.
167 * @param phRootDir Where to return the root handle, if applicable.
168 * @param pszPath The UTF-8 path.
169 */
170static int rtNtPathUtf8ToUniStr(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
171{
172 PRTUTF16 pwszPath = NULL;
173 size_t cwcLen;
174 int rc = RTStrToUtf16Ex(pszPath, RTSTR_MAX, &pwszPath, 0, &cwcLen);
175 if (RT_SUCCESS(rc))
176 {
177 if (cwcLen < _32K - 1)
178 {
179 pNtName->Buffer = pwszPath;
180 pNtName->Length = (uint16_t)(cwcLen * sizeof(RTUTF16));
181 pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
182 *phRootDir = NULL;
183 return VINF_SUCCESS;
184 }
185
186 RTUtf16Free(pwszPath);
187 rc = VERR_FILENAME_TOO_LONG;
188 }
189 return rc;
190}
191
192
193/**
194 * Converts a windows-style path to NT format and encoding.
195 *
196 * @returns IPRT status code.
197 * @param pNtName Where to return the NT name. Free using
198 * rtTNtPathToNative.
199 * @param phRootDir Where to return the root handle, if applicable.
200 * @param pszPath The UTF-8 path.
201 */
202static int rtNtPathToNative(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
203{
204/** @todo This code sucks a bit performance wise, esp. calling
205 * generic RTPathAbs. Too many buffers involved, I think. */
206
207 /*
208 * Very simple conversion of a win32-like path into an NT path.
209 */
210 const char *pszPrefix;
211 size_t cchPrefix;
212 if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) >= RT_MAKE_U64(0, 4))
213 {
214 pszPrefix = g_szPrefix;
215 cchPrefix = sizeof(g_szPrefix) - 1;
216 }
217 else
218 {
219 pszPrefix = g_szPrefixNt3x;
220 cchPrefix = sizeof(g_szPrefixNt3x) - 1;
221 }
222
223 size_t cchSkip = 0;
224 if ( RTPATH_IS_SLASH(pszPath[0])
225 && RTPATH_IS_SLASH(pszPath[1])
226 && !RTPATH_IS_SLASH(pszPath[2])
227 && pszPath[2])
228 {
229#ifdef IPRT_WITH_NT_PATH_PASSTHRU
230 /*
231 * Special trick: The path starts with RTPATH_NT_PASSTHRU_PREFIX, we will skip past the bang and pass it thru.
232 */
233 if ( pszPath[2] == ':'
234 && pszPath[3] == 'i'
235 && pszPath[4] == 'p'
236 && pszPath[5] == 'r'
237 && pszPath[6] == 't'
238 && pszPath[7] == 'n'
239 && pszPath[8] == 't'
240 && pszPath[9] == ':'
241 && RTPATH_IS_SLASH(pszPath[10]))
242 return rtNtPathUtf8ToUniStr(pNtName, phRootDir, pszPath + 10);
243#endif
244
245 if ( pszPath[2] == '?'
246 && RTPATH_IS_SLASH(pszPath[3]))
247 return rtNtPathFromWinUtf8PassThru(pNtName, phRootDir, pszPath);
248
249 if ( pszPath[2] == '.'
250 && RTPATH_IS_SLASH(pszPath[3]))
251 {
252 /*
253 * Device path.
254 * Note! I suspect \\.\stuff\..\otherstuff may be handled differently by windows.
255 */
256 cchSkip = 4;
257 }
258 else
259 {
260 /* UNC */
261 if (RT_MAKE_U64(RTNtCurrentPeb()->OSMinorVersion, RTNtCurrentPeb()->OSMajorVersion) >= RT_MAKE_U64(0, 4))
262 {
263 pszPrefix = g_szPrefixUnc;
264 cchPrefix = sizeof(g_szPrefixUnc) - 1;
265 }
266 else
267 {
268 pszPrefix = g_szPrefixNt3xUnc;
269 cchPrefix = sizeof(g_szPrefixNt3xUnc) - 1;
270 }
271 cchSkip = 2;
272 }
273 }
274
275 /*
276 * Straighten out all .. and uncessary . references and convert slashes.
277 */
278 char szPath[RTPATH_MAX];
279 int rc = RTPathAbs(pszPath, &szPath[cchPrefix - cchSkip], sizeof(szPath) - (cchPrefix - cchSkip));
280 if (RT_FAILURE(rc))
281 return rc;
282
283 /*
284 * Add prefix and convert it to UTF16.
285 */
286 memcpy(szPath, pszPrefix, cchPrefix);
287 return rtNtPathUtf8ToUniStr(pNtName, phRootDir, szPath);
288}
289
290
291/**
292 * Converts a windows-style path to NT format and encoding.
293 *
294 * @returns IPRT status code.
295 * @param pNtName Where to return the NT name. Free using
296 * RTNtPathToNative.
297 * @param phRootDir Where to return the root handle, if applicable.
298 * @param pszPath The UTF-8 path.
299 */
300RTDECL(int) RTNtPathFromWinUtf8(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
301{
302 return rtNtPathToNative(pNtName, phRootDir, pszPath);
303}
304
305
306/**
307 * Converts a UTF-16 windows-style path to NT format.
308 *
309 * @returns IPRT status code.
310 * @param pNtName Where to return the NT name. Free using
311 * RTNtPathFree.
312 * @param phRootDir Where to return the root handle, if applicable.
313 * @param pwszWinPath The UTF-16 windows-style path.
314 * @param cwcWinPath The max length of the windows-style path in
315 * RTUTF16 units. Use RTSTR_MAX if unknown and @a
316 * pwszWinPath is correctly terminated.
317 */
318RTDECL(int) RTNtPathFromWinUtf16Ex(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir, PCRTUTF16 pwszWinPath, size_t cwcWinPath)
319{
320 /*
321 * Validate the input, calculating the correct length.
322 */
323 if (cwcWinPath == 0 || *pwszWinPath == '\0')
324 return VERR_INVALID_NAME;
325
326 RTUtf16NLenEx(pwszWinPath, cwcWinPath, &cwcWinPath);
327 int rc = RTUtf16ValidateEncodingEx(pwszWinPath, cwcWinPath, 0);
328 if (RT_FAILURE(rc))
329 return rc;
330
331 /*
332 * Very simple conversion of a win32-like path into an NT path.
333 */
334 const char *pszPrefix = g_szPrefix;
335 size_t cchPrefix = sizeof(g_szPrefix) - 1;
336 size_t cchSkip = 0;
337
338 if ( RTPATH_IS_SLASH(pwszWinPath[0])
339 && cwcWinPath >= 3
340 && RTPATH_IS_SLASH(pwszWinPath[1])
341 && !RTPATH_IS_SLASH(pwszWinPath[2]) )
342 {
343#ifdef IPRT_WITH_NT_PATH_PASSTHRU
344 /*
345 * Special trick: The path starts with RTPATH_NT_PASSTHRU_PREFIX, we will skip past the bang and pass it thru.
346 */
347 if ( cwcWinPath >= sizeof(RTPATH_NT_PASSTHRU_PREFIX) - 1U
348 && pwszWinPath[2] == ':'
349 && pwszWinPath[3] == 'i'
350 && pwszWinPath[4] == 'p'
351 && pwszWinPath[5] == 'r'
352 && pwszWinPath[6] == 't'
353 && pwszWinPath[7] == 'n'
354 && pwszWinPath[8] == 't'
355 && pwszWinPath[9] == ':'
356 && RTPATH_IS_SLASH(pwszWinPath[10]) )
357 {
358 pwszWinPath += 10;
359 cwcWinPath -= 10;
360 if (cwcWinPath < _32K - 1)
361 {
362 PRTUTF16 pwszNtPath = RTUtf16Alloc((cwcWinPath + 1) * sizeof(RTUTF16));
363 if (pwszNtPath)
364 {
365 memcpy(pwszNtPath, pwszWinPath, cwcWinPath * sizeof(RTUTF16));
366 pwszNtPath[cwcWinPath] = '\0';
367 pNtName->Buffer = pwszNtPath;
368 pNtName->Length = (uint16_t)(cwcWinPath * sizeof(RTUTF16));
369 pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
370 *phRootDir = NULL;
371 return VINF_SUCCESS;
372 }
373 rc = VERR_NO_UTF16_MEMORY;
374 }
375 else
376 rc = VERR_FILENAME_TOO_LONG;
377 return rc;
378 }
379#endif
380
381 if ( pwszWinPath[2] == '?'
382 && cwcWinPath >= 4
383 && RTPATH_IS_SLASH(pwszWinPath[3]))
384 return rtNtPathFromWinUtf16PassThru(pNtName, phRootDir, pwszWinPath, cwcWinPath);
385
386 if ( pwszWinPath[2] == '.'
387 && cwcWinPath >= 4
388 && RTPATH_IS_SLASH(pwszWinPath[3]))
389 {
390 /*
391 * Device path.
392 * Note! I suspect \\.\stuff\..\otherstuff may be handled differently by windows.
393 */
394 cchSkip = 4;
395 }
396 else
397 {
398 /* UNC */
399 pszPrefix = g_szPrefixUnc;
400 cchPrefix = sizeof(g_szPrefixUnc) - 1;
401 cchSkip = 2;
402 }
403 }
404
405 /*
406 * Straighten out all .. and unnecessary . references and convert slashes.
407 */
408 char szAbsPath[RTPATH_MAX];
409 char szRelPath[RTPATH_MAX];
410 char *pszRelPath = szRelPath;
411 size_t cchRelPath;
412 rc = RTUtf16ToUtf8Ex(pwszWinPath, cwcWinPath, &pszRelPath, sizeof(szRelPath), &cchRelPath);
413 if (RT_SUCCESS(rc))
414 rc = RTPathAbs(szRelPath, &szAbsPath[cchPrefix - cchSkip], sizeof(szAbsPath) - (cchPrefix - cchSkip));
415 if (RT_SUCCESS(rc))
416 {
417 /*
418 * Add prefix
419 */
420 memcpy(szAbsPath, pszPrefix, cchPrefix);
421
422 /*
423 * Remove trailing '.' that is used to specify no extension in the Win32/DOS world.
424 */
425 size_t cchAbsPath = strlen(szAbsPath);
426 if ( cchAbsPath > 2
427 && szAbsPath[cchAbsPath - 1] == '.')
428 {
429 char const ch = szAbsPath[cchAbsPath - 2];
430 if ( ch != '/'
431 && ch != '\\'
432 && ch != ':'
433 && ch != '.')
434 szAbsPath[--cchAbsPath] = '\0';
435 }
436
437 /*
438 * Finally convert to UNICODE_STRING.
439 */
440 return rtNtPathUtf8ToUniStr(pNtName, phRootDir, szAbsPath);
441 }
442 return rc;
443}
444
445
446/**
447 * Ensures that the NT string has sufficient storage to hold @a cwcMin RTUTF16
448 * chars plus a terminator.
449 *
450 * The NT string must have been returned by RTNtPathFromWinUtf8 or
451 * RTNtPathFromWinUtf16Ex.
452 *
453 * @returns IPRT status code.
454 * @param pNtName The NT path string.
455 * @param cwcMin The minimum number of RTUTF16 chars. Max 32767.
456 * @sa RTNtPathFree
457 */
458RTDECL(int) RTNtPathEnsureSpace(struct _UNICODE_STRING *pNtName, size_t cwcMin)
459{
460 if (pNtName->MaximumLength / sizeof(RTUTF16) > cwcMin)
461 return VINF_SUCCESS;
462
463 AssertReturn(cwcMin < _64K / sizeof(RTUTF16), VERR_OUT_OF_RANGE);
464
465 size_t const cbMin = (cwcMin + 1) * sizeof(RTUTF16);
466 int rc = RTUtf16Realloc(&pNtName->Buffer, cbMin);
467 if (RT_SUCCESS(rc))
468 pNtName->MaximumLength = (uint16_t)cbMin;
469 return rc;
470}
471
472
473/**
474 * Gets the NT path to the object represented by the given handle.
475 *
476 * @returns IPRT status code.
477 * @param pNtName Where to return the NT path. Free using
478 * RTUtf16Alloc.
479 * @param hHandle The handle.
480 * @param cwcExtra How much extra space is needed.
481 */
482static int rtNtPathFromHandle(struct _UNICODE_STRING *pNtName, HANDLE hHandle, size_t cwcExtra)
483{
484 /*
485 * Query the name into a buffer.
486 */
487 ULONG cbBuf = _2K;
488 PUNICODE_STRING pUniStrBuf = (PUNICODE_STRING)RTMemTmpAllocZ(cbBuf);
489 if (!pUniStrBuf)
490 return VERR_NO_TMP_MEMORY;
491
492 ULONG cbNameBuf = cbBuf;
493 NTSTATUS rcNt = NtQueryObject(hHandle, ObjectNameInformation, pUniStrBuf, cbBuf, &cbNameBuf);
494 while ( rcNt == STATUS_BUFFER_OVERFLOW
495 || rcNt == STATUS_BUFFER_TOO_SMALL)
496 {
497 do
498 cbBuf *= 2;
499 while (cbBuf <= cbNameBuf);
500 RTMemTmpFree(pUniStrBuf);
501 pUniStrBuf = (PUNICODE_STRING)RTMemTmpAllocZ(cbBuf);
502 if (!pUniStrBuf)
503 return VERR_NO_TMP_MEMORY;
504
505 cbNameBuf = cbBuf;
506 rcNt = NtQueryObject(hHandle, ObjectNameInformation, pUniStrBuf, cbBuf, &cbNameBuf);
507 }
508 int rc;
509 if (NT_SUCCESS(rcNt))
510 {
511 /*
512 * Copy the result into the return string.
513 */
514 size_t cbNeeded = cwcExtra * sizeof(RTUTF16) + pUniStrBuf->Length + sizeof(RTUTF16);
515 if (cbNeeded < _64K)
516 {
517 pNtName->Length = pUniStrBuf->Length;
518 pNtName->MaximumLength = (uint16_t)cbNeeded;
519 pNtName->Buffer = RTUtf16Alloc(cbNeeded);
520 if (pNtName->Buffer)
521 {
522 memcpy(pNtName->Buffer, pUniStrBuf->Buffer, pUniStrBuf->Length);
523 pNtName->Buffer[pUniStrBuf->Length / sizeof(RTUTF16)] = '\0';
524 rc = VINF_SUCCESS;
525 }
526 else
527 rc = VERR_NO_UTF16_MEMORY;
528 }
529 else
530 rc = VERR_FILENAME_TOO_LONG;
531 }
532 else
533 rc = RTErrConvertFromNtStatus(rcNt);
534 RTMemTmpFree(pUniStrBuf);
535 return rc;
536}
537
538static int rtNtPathRelativeToAbs(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir)
539{
540 int rc;
541 if (pNtName->Length == 0)
542 {
543 RTUtf16Free(pNtName->Buffer);
544 rc = rtNtPathFromHandle(pNtName, *phRootDir, pNtName->Length / sizeof(RTUTF16) + 2);
545 if (RT_SUCCESS(rc))
546 {
547 *phRootDir = NULL;
548 return VINF_SUCCESS;
549 }
550 }
551 else
552 {
553
554 UNICODE_STRING RootDir;
555 size_t const cwcAppend = pNtName->Length / sizeof(RTUTF16);
556 rc = rtNtPathFromHandle(&RootDir, *phRootDir, cwcAppend + 2);
557 if (RT_SUCCESS(rc))
558 {
559 size_t cwcRoot = RootDir.Length / sizeof(RTUTF16);
560 if (RootDir.Buffer[cwcRoot - 1] != '\\')
561 RootDir.Buffer[cwcRoot++] = '\\';
562 memcpy(&RootDir.Buffer[cwcRoot], pNtName->Buffer, cwcAppend * sizeof(RTUTF16));
563 RTUtf16Free(pNtName->Buffer);
564 pNtName->Length = (uint16_t)((cwcRoot + cwcAppend) * sizeof(RTUTF16));
565 pNtName->MaximumLength = RootDir.MaximumLength;
566 pNtName->Buffer = RootDir.Buffer;
567
568 *phRootDir = NULL;
569 return VINF_SUCCESS;
570 }
571 RTUtf16Free(pNtName->Buffer);
572 }
573 pNtName->Length = 0;
574 pNtName->MaximumLength = 0;
575 pNtName->Buffer = NULL;
576 return rc;
577}
578
579
580/**
581 * Rewinds the path back to the start of the previous component.
582 *
583 * Will preserve root slash.
584 *
585 * @returns Pointer to character after the start-of-component slash, or
586 * pwszStart.
587 * @param pwcEnd The current end of the path.
588 * @param pwszStart The start of the path.
589 */
590static PRTUTF16 rtNtPathGetPrevComponent(PRTUTF16 pwcEnd, PRTUTF16 pwszStart)
591{
592 if ((uintptr_t)pwcEnd > (uintptr_t)pwszStart)
593 {
594 RTUTF16 wc = pwcEnd[-1];
595 if ( (wc == '\\' || wc == '/')
596 && (uintptr_t)(pwcEnd - pwszStart) != 1)
597 pwcEnd--;
598
599 while ( (uintptr_t)pwcEnd > (uintptr_t)pwszStart
600 && (wc = pwcEnd[-1]) != '\\'
601 && (wc = pwcEnd[-1]) != '/')
602 pwcEnd--;
603 }
604 return pwcEnd;
605}
606
607
608/**
609 * Converts a relative windows-style path to relative NT format and encoding.
610 *
611 * @returns IPRT status code.
612 * @param pNtName Where to return the NT name. Free using
613 * rtTNtPathToNative with phRootDir set to NULL.
614 * @param phRootDir On input, the handle to the directory the path
615 * is relative to. On output, the handle to
616 * specify as root directory in the object
617 * attributes when accessing the path. If
618 * enmAscent is kRTNtPathRelativeAscent_Allow, it
619 * may have been set to NULL.
620 * @param pszPath The relative UTF-8 path.
621 * @param enmAscent How to handle ascent.
622 * @param fMustReturnAbsolute Must convert to an absolute path. This
623 * is necessary if the root dir is a NT directory
624 * object (e.g. /Devices) since they cannot parse
625 * relative paths it seems.
626 */
627RTDECL(int) RTNtPathRelativeFromUtf8(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath,
628 RTNTPATHRELATIVEASCENT enmAscent, bool fMustReturnAbsolute)
629{
630 size_t cwcMax;
631 int rc = RTStrCalcUtf16LenEx(pszPath, RTSTR_MAX, &cwcMax);
632 if (RT_FAILURE(rc))
633 return rc;
634 if (cwcMax + 2 >= _32K)
635 return VERR_FILENAME_TOO_LONG;
636
637 PRTUTF16 pwszDst;
638 pNtName->Length = 0;
639 pNtName->MaximumLength = (uint16_t)((cwcMax + 2) * sizeof(RTUTF16));
640 pNtName->Buffer = pwszDst = RTUtf16Alloc((cwcMax + 2) * sizeof(RTUTF16));
641 if (!pwszDst)
642 return VERR_NO_UTF16_MEMORY;
643
644 PRTUTF16 pwszDstCur = pwszDst;
645 PRTUTF16 pwszDstComp = pwszDst;
646 for (;;)
647 {
648 RTUNICP uc;
649 rc = RTStrGetCpEx(&pszPath, &uc);
650 if (RT_SUCCESS(rc))
651 {
652 switch (uc)
653 {
654 default:
655 pwszDstCur = RTUtf16PutCp(pwszDstCur, uc);
656 break;
657
658 case '\\':
659 case '/':
660 if (pwszDstCur != pwszDstComp)
661 pwszDstComp = pwszDstCur = RTUtf16PutCp(pwszDstCur, '\\');
662 /* else: only one slash between components. */
663 break;
664
665 case '.':
666 if (pwszDstCur == pwszDstComp)
667 {
668 /*
669 * Single dot changes nothing.
670 */
671 char ch2 = *pszPath;
672 if (ch2 == '\0')
673 {
674 /* Trailing single dot means we need to drop trailing slash. */
675 if (pwszDstCur != pwszDst)
676 pwszDstCur--;
677 *pwszDstCur = '\0';
678 pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
679 if (!fMustReturnAbsolute || *phRootDir == NULL)
680 return VINF_SUCCESS;
681 return rtNtPathRelativeToAbs(pNtName, phRootDir);
682 }
683
684 if (ch2 == '\\' || ch2 == '/')
685 {
686 pszPath++; /* Ignore lone dot followed but another component. */
687 break;
688 }
689
690 /*
691 * Two dots drops off the last directory component. This gets complicated
692 * when we start out without any path and we need to consult enmAscent.
693 */
694 if (ch2 == '.')
695 {
696 char ch3 = pszPath[1];
697 if ( ch3 == '\\'
698 || ch3 == '/'
699 || ch3 == '\0')
700 {
701 /* Drop a path component. */
702 if (pwszDstComp != pwszDst)
703 pwszDstComp = pwszDstCur = rtNtPathGetPrevComponent(pwszDstCur, pwszDst);
704 /* Hit the start, which is a bit complicated. */
705 else
706 switch (enmAscent)
707 {
708 case kRTNtPathRelativeAscent_Allow:
709 if (*phRootDir != NULL)
710 {
711 RTUtf16Free(pwszDst);
712 rc = rtNtPathFromHandle(pNtName, *phRootDir, cwcMax + 2);
713 if (RT_FAILURE(rc))
714 return rc;
715
716 *phRootDir = NULL;
717 pwszDst = pNtName->Buffer;
718 pwszDstCur = &pwszDst[pNtName->Length / sizeof(RTUTF16)];
719 if ( pwszDst != pwszDstCur
720 && pwszDstCur[-1] != '\\'
721 && pwszDstCur[-1] != '/')
722 *pwszDstCur++ = '\\';
723 pwszDstComp = pwszDstCur = rtNtPathGetPrevComponent(pwszDstCur, pwszDst);
724 }
725 /* else: ignore attempt to ascend beyond the NT root (won't get here). */
726 break;
727
728 case kRTNtPathRelativeAscent_Ignore:
729 /* nothing to do here */
730 break;
731
732 default:
733 case kRTNtPathRelativeAscent_Fail:
734 RTUtf16Free(pwszDst);
735 return VERR_PATH_NOT_FOUND;
736 }
737
738 if (ch3 == '\0')
739 {
740 *pwszDstCur = '\0';
741 pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
742 if (!fMustReturnAbsolute || *phRootDir == NULL)
743 return VINF_SUCCESS;
744 return rtNtPathRelativeToAbs(pNtName, phRootDir);
745 }
746 pszPath += 2;
747 break;
748 }
749 }
750 }
751
752 /* Neither '.' nor '..'. */
753 pwszDstCur = RTUtf16PutCp(pwszDstCur, '.');
754 break;
755
756 case '\0':
757 *pwszDstCur = '\0';
758 pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
759 if (!fMustReturnAbsolute || *phRootDir == NULL)
760 return VINF_SUCCESS;
761 return rtNtPathRelativeToAbs(pNtName, phRootDir);
762 }
763 }
764 }
765}
766
767
768/**
769 * Frees the native path and root handle.
770 *
771 * @param pNtName The NT path after a successful rtNtPathToNative
772 * call or RTNtPathRelativeFromUtf8.
773 * @param phRootDir The root handle variable from rtNtPathToNative,
774 * but NOT RTNtPathRelativeFromUtf8.
775 */
776static void rtNtPathFreeNative(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir)
777{
778 RTUtf16Free(pNtName->Buffer);
779 pNtName->Buffer = NULL;
780
781 RT_NOREF_PV(phRootDir); /* never returned by rtNtPathToNative, shouldn't be freed in connection with RTNtPathRelativeFromUtf8 */
782}
783
784
785/**
786 * Frees the native path and root handle.
787 *
788 * @param pNtName The NT path after a successful rtNtPathToNative
789 * call or RTNtPathRelativeFromUtf8.
790 * @param phRootDir The root handle variable from rtNtPathToNative,
791 */
792RTDECL(void) RTNtPathFree(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir)
793{
794 rtNtPathFreeNative(pNtName, phRootDir);
795}
796
797
798/**
799 * Wrapper around NtCreateFile.
800 *
801 * @returns IPRT status code.
802 * @param pszPath The UTF-8 path.
803 * @param fDesiredAccess See NtCreateFile.
804 * @param fFileAttribs See NtCreateFile.
805 * @param fShareAccess See NtCreateFile.
806 * @param fCreateDisposition See NtCreateFile.
807 * @param fCreateOptions See NtCreateFile.
808 * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
809 * NtCreateFile and InitializeObjectAttributes.
810 * @param phHandle Where to return the handle.
811 * @param puAction Where to return the action taken. Optional.
812 */
813RTDECL(int) RTNtPathOpen(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, ULONG fShareAccess,
814 ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs,
815 PHANDLE phHandle, PULONG_PTR puAction)
816{
817 *phHandle = RTNT_INVALID_HANDLE_VALUE;
818
819 HANDLE hRootDir;
820 UNICODE_STRING NtName;
821 int rc = rtNtPathToNative(&NtName, &hRootDir, pszPath);
822 if (RT_SUCCESS(rc))
823 {
824 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
825 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
826 OBJECT_ATTRIBUTES ObjAttr;
827 InitializeObjectAttributes(&ObjAttr, &NtName, fObjAttribs, hRootDir, NULL);
828
829 NTSTATUS rcNt = NtCreateFile(&hFile,
830 fDesiredAccess,
831 &ObjAttr,
832 &Ios,
833 NULL /* AllocationSize*/,
834 fFileAttribs,
835 fShareAccess,
836 fCreateDisposition,
837 fCreateOptions,
838 NULL /*EaBuffer*/,
839 0 /*EaLength*/);
840 if (NT_SUCCESS(rcNt))
841 {
842 if (puAction)
843 *puAction = Ios.Information;
844 *phHandle = hFile;
845 rc = VINF_SUCCESS;
846 }
847 else
848 rc = RTErrConvertFromNtStatus(rcNt);
849 rtNtPathFreeNative(&NtName, &hRootDir);
850 }
851 return rc;
852}
853
854
855/**
856 * Wrapper around NtCreateFile.
857 *
858 * @returns IPRT status code.
859 * @param pszPath The UTF-8 path.
860 * @param fDesiredAccess See NtCreateFile.
861 * @param fShareAccess See NtCreateFile.
862 * @param fCreateOptions See NtCreateFile.
863 * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
864 * NtCreateFile and InitializeObjectAttributes.
865 * @param phHandle Where to return the handle.
866 * @param pfObjDir If not NULL, the variable pointed to will be set
867 * to @c true if we opened an object directory and
868 * @c false if we opened an directory file (normal
869 * directory).
870 */
871RTDECL(int) RTNtPathOpenDir(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fShareAccess, ULONG fCreateOptions,
872 ULONG fObjAttribs, PHANDLE phHandle, bool *pfObjDir)
873{
874 *phHandle = RTNT_INVALID_HANDLE_VALUE;
875
876 HANDLE hRootDir;
877 UNICODE_STRING NtName;
878 int rc = rtNtPathToNative(&NtName, &hRootDir, pszPath);
879 if (RT_SUCCESS(rc))
880 {
881 if (pfObjDir)
882 {
883 *pfObjDir = false;
884#ifdef IPRT_WITH_NT_PATH_PASSTHRU
885 if ( !RTPATH_IS_SLASH(pszPath[0])
886 || !RTPATH_IS_SLASH(pszPath[1])
887 || pszPath[2] != ':'
888 || pszPath[3] != 'i'
889 || pszPath[4] != 'p'
890 || pszPath[5] != 'r'
891 || pszPath[6] != 't'
892 || pszPath[7] != 'n'
893 || pszPath[8] != 't'
894 || pszPath[9] != ':'
895 || !RTPATH_IS_SLASH(pszPath[10]) )
896#endif
897 pfObjDir = NULL;
898 }
899 rc = RTNtPathOpenDirEx(hRootDir, &NtName, fDesiredAccess, fShareAccess, fCreateOptions, fObjAttribs, phHandle, pfObjDir);
900 rtNtPathFreeNative(&NtName, &hRootDir);
901 }
902 return rc;
903}
904
905
906
907/**
908 * Wrapper around NtCreateFile, extended version.
909 *
910 * @returns IPRT status code.
911 * @param hRootDir The root director the path is relative to. NULL
912 * if none.
913 * @param pNtName The NT path.
914 * @param fDesiredAccess See NtCreateFile.
915 * @param fShareAccess See NtCreateFile.
916 * @param fCreateOptions See NtCreateFile.
917 * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
918 * NtCreateFile and InitializeObjectAttributes.
919 * @param phHandle Where to return the handle.
920 * @param pfObjDir If not NULL, the variable pointed to will be set
921 * to @c true if we opened an object directory and
922 * @c false if we opened an directory file (normal
923 * directory).
924 */
925RTDECL(int) RTNtPathOpenDirEx(HANDLE hRootDir, struct _UNICODE_STRING *pNtName, ACCESS_MASK fDesiredAccess, ULONG fShareAccess,
926 ULONG fCreateOptions, ULONG fObjAttribs, PHANDLE phHandle, bool *pfObjDir)
927{
928 *phHandle = RTNT_INVALID_HANDLE_VALUE;
929
930 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
931 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
932 OBJECT_ATTRIBUTES ObjAttr;
933 InitializeObjectAttributes(&ObjAttr, pNtName, fObjAttribs, hRootDir, NULL);
934
935 NTSTATUS rcNt = NtCreateFile(&hFile,
936 fDesiredAccess,
937 &ObjAttr,
938 &Ios,
939 NULL /* AllocationSize*/,
940 FILE_ATTRIBUTE_NORMAL,
941 fShareAccess,
942 FILE_OPEN,
943 fCreateOptions,
944 NULL /*EaBuffer*/,
945 0 /*EaLength*/);
946 if (NT_SUCCESS(rcNt))
947 {
948 if (pfObjDir)
949 *pfObjDir = false;
950 *phHandle = hFile;
951 return VINF_SUCCESS;
952 }
953
954 /*
955 * Try add a slash in case this is a device object with a file system attached.
956 */
957 if ( rcNt == STATUS_INVALID_PARAMETER
958 && pNtName->Length < _64K - 4
959 && ( pNtName->Length == 0
960 || pNtName->Buffer[pNtName->Length / sizeof(RTUTF16)] != '\\') )
961 {
962 UNICODE_STRING NtTmp;
963 NtTmp.Length = pNtName->Length + 2;
964 NtTmp.MaximumLength = NtTmp.Length + 2;
965 NtTmp.Buffer = (PRTUTF16)RTMemTmpAlloc(NtTmp.MaximumLength);
966 if (NtTmp.Buffer)
967 {
968 memcpy(NtTmp.Buffer, pNtName->Buffer, pNtName->Length);
969 NtTmp.Buffer[pNtName->Length / sizeof(RTUTF16)] = '\\';
970 NtTmp.Buffer[pNtName->Length / sizeof(RTUTF16) + 1] = '\0';
971
972 hFile = RTNT_INVALID_HANDLE_VALUE;
973 Ios.Status = -1;
974 Ios.Information = 0;
975 ObjAttr.ObjectName = &NtTmp;
976
977 rcNt = NtCreateFile(&hFile,
978 fDesiredAccess,
979 &ObjAttr,
980 &Ios,
981 NULL /* AllocationSize*/,
982 FILE_ATTRIBUTE_NORMAL,
983 fShareAccess,
984 FILE_OPEN,
985 fCreateOptions,
986 NULL /*EaBuffer*/,
987 0 /*EaLength*/);
988 RTMemTmpFree(NtTmp.Buffer);
989 if (NT_SUCCESS(rcNt))
990 {
991 if (pfObjDir)
992 *pfObjDir = false;
993 *phHandle = hFile;
994 return VINF_SUCCESS;
995 }
996 ObjAttr.ObjectName = pNtName;
997 }
998 }
999
1000 /*
1001 * Try open it as a directory object if it makes sense.
1002 */
1003 if ( pfObjDir
1004 && ( rcNt == STATUS_OBJECT_NAME_INVALID
1005 || rcNt == STATUS_OBJECT_TYPE_MISMATCH ))
1006 {
1007 /* Strip trailing slash. */
1008 struct _UNICODE_STRING NtName2 = *pNtName;
1009 if ( NtName2.Length > 2
1010 && RTPATH_IS_SLASH(NtName2.Buffer[(NtName2.Length / 2) - 1]))
1011 NtName2.Length -= 2;
1012 ObjAttr.ObjectName = &NtName2;
1013
1014 /* Rought conversion of the access flags. */
1015 ULONG fObjDesiredAccess = 0;
1016 if ( (fDesiredAccess & GENERIC_ALL)
1017 || (fDesiredAccess & STANDARD_RIGHTS_ALL) == STANDARD_RIGHTS_ALL)
1018 fObjDesiredAccess = DIRECTORY_ALL_ACCESS;
1019 else
1020 {
1021 if (fDesiredAccess & (GENERIC_WRITE | STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA))
1022 fObjDesiredAccess |= DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_SUBDIRECTORY;
1023
1024 if ( (fDesiredAccess & (GENERIC_READ | STANDARD_RIGHTS_READ | FILE_LIST_DIRECTORY))
1025 || !fObjDesiredAccess)
1026 fObjDesiredAccess |= DIRECTORY_QUERY;
1027
1028 if (fDesiredAccess & FILE_TRAVERSE)
1029 fObjDesiredAccess |= DIRECTORY_TRAVERSE;
1030 }
1031
1032 rcNt = NtOpenDirectoryObject(&hFile, fObjDesiredAccess, &ObjAttr);
1033 if (NT_SUCCESS(rcNt))
1034 {
1035 *pfObjDir = true;
1036 *phHandle = hFile;
1037 return VINF_SUCCESS;
1038 }
1039 }
1040
1041 return RTErrConvertFromNtStatus(rcNt);
1042}
1043
1044
1045
1046/**
1047 * Closes an handled open by rtNtPathOpen.
1048 *
1049 * @returns IPRT status code
1050 * @param hHandle The handle value.
1051 */
1052RTDECL(int) RTNtPathClose(HANDLE hHandle)
1053{
1054 NTSTATUS rcNt = NtClose(hHandle);
1055 if (NT_SUCCESS(rcNt))
1056 return VINF_SUCCESS;
1057 return RTErrConvertFromNtStatus(rcNt);
1058}
1059
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette