VirtualBox

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

Last change on this file since 86186 was 85898, checked in by vboxsync, 4 years ago

iprt/RTNtPathFromHandle: documentation fix. bugref:9224

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.8 KB
Line 
1/* $Id: pathint-nt.cpp 85898 2020-08-27 09:33:08Z vboxsync $ */
2/** @file
3 * IPRT - Native NT, Internal Path stuff.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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 szAbsPathBuf[RTPATH_MAX];
279 size_t cbAbsPath = sizeof(szAbsPathBuf) - (cchPrefix - cchSkip);
280 char *pszAbsPath = szAbsPathBuf;
281 char *pszAbsPathFree = NULL;
282 int rc = RTPathAbsEx(NULL, pszPath, RTPATH_STR_F_STYLE_DOS, &pszAbsPath[cchPrefix - cchSkip], &cbAbsPath);
283 if (RT_SUCCESS(rc))
284 { /* likely */ }
285 else if (rc == VERR_BUFFER_OVERFLOW)
286 {
287 unsigned cTries = 8;
288 size_t cbAbsPathBuf = RTPATH_MAX;
289 for (;;)
290 {
291 cbAbsPathBuf = RT_MAX(RT_ALIGN_Z((cchPrefix - cchSkip) + cbAbsPath + 32, 64), cbAbsPathBuf + 256);
292 if (cTries == 1)
293 cbAbsPathBuf = RT_MAX(cbAbsPathBuf, RTPATH_BIG_MAX * 2);
294 pszAbsPathFree = pszAbsPath = (char *)RTMemTmpAlloc(cbAbsPathBuf);
295 if (!pszAbsPath)
296 return VERR_NO_TMP_MEMORY;
297
298 cbAbsPath = cbAbsPathBuf - (cchPrefix - cchSkip);
299 rc = RTPathAbsEx(NULL, pszPath, RTPATH_STR_F_STYLE_DOS, &pszAbsPath[cchPrefix - cchSkip], &cbAbsPath);
300 if (RT_SUCCESS(rc))
301 break;
302 RTMemTmpFree(pszAbsPathFree);
303 pszAbsPathFree = NULL;
304 if (rc != VERR_BUFFER_OVERFLOW)
305 return rc;
306 if (--cTries == 0)
307 return VERR_FILENAME_TOO_LONG;
308 }
309 }
310 else
311 return rc;
312
313 /*
314 * Add prefix and convert it to UTF16.
315 */
316 memcpy(pszAbsPath, pszPrefix, cchPrefix);
317 rc = rtNtPathUtf8ToUniStr(pNtName, phRootDir, pszAbsPath);
318
319 if (pszAbsPathFree)
320 RTMemTmpFree(pszAbsPathFree);
321 return rc;
322}
323
324
325/**
326 * Converts a windows-style path to NT format and encoding.
327 *
328 * @returns IPRT status code.
329 * @param pNtName Where to return the NT name. Free using
330 * RTNtPathToNative.
331 * @param phRootDir Where to return the root handle, if applicable.
332 * @param pszPath The UTF-8 path.
333 */
334RTDECL(int) RTNtPathFromWinUtf8(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
335{
336 return rtNtPathToNative(pNtName, phRootDir, pszPath);
337}
338
339
340/**
341 * Converts a UTF-16 windows-style path to NT format.
342 *
343 * @returns IPRT status code.
344 * @param pNtName Where to return the NT name. Free using
345 * RTNtPathFree.
346 * @param phRootDir Where to return the root handle, if applicable.
347 * @param pwszWinPath The UTF-16 windows-style path.
348 * @param cwcWinPath The max length of the windows-style path in
349 * RTUTF16 units. Use RTSTR_MAX if unknown and @a
350 * pwszWinPath is correctly terminated.
351 */
352RTDECL(int) RTNtPathFromWinUtf16Ex(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir, PCRTUTF16 pwszWinPath, size_t cwcWinPath)
353{
354 /*
355 * Validate the input, calculating the correct length.
356 */
357 if (cwcWinPath == 0 || *pwszWinPath == '\0')
358 return VERR_INVALID_NAME;
359
360 RTUtf16NLenEx(pwszWinPath, cwcWinPath, &cwcWinPath);
361 int rc = RTUtf16ValidateEncodingEx(pwszWinPath, cwcWinPath, 0);
362 if (RT_FAILURE(rc))
363 return rc;
364
365 /*
366 * Very simple conversion of a win32-like path into an NT path.
367 */
368 const char *pszPrefix = g_szPrefix;
369 size_t cchPrefix = sizeof(g_szPrefix) - 1;
370 size_t cchSkip = 0;
371
372 if ( RTPATH_IS_SLASH(pwszWinPath[0])
373 && cwcWinPath >= 3
374 && RTPATH_IS_SLASH(pwszWinPath[1])
375 && !RTPATH_IS_SLASH(pwszWinPath[2]) )
376 {
377#ifdef IPRT_WITH_NT_PATH_PASSTHRU
378 /*
379 * Special trick: The path starts with RTPATH_NT_PASSTHRU_PREFIX, we will skip past the bang and pass it thru.
380 */
381 if ( cwcWinPath >= sizeof(RTPATH_NT_PASSTHRU_PREFIX) - 1U
382 && pwszWinPath[2] == ':'
383 && pwszWinPath[3] == 'i'
384 && pwszWinPath[4] == 'p'
385 && pwszWinPath[5] == 'r'
386 && pwszWinPath[6] == 't'
387 && pwszWinPath[7] == 'n'
388 && pwszWinPath[8] == 't'
389 && pwszWinPath[9] == ':'
390 && RTPATH_IS_SLASH(pwszWinPath[10]) )
391 {
392 pwszWinPath += 10;
393 cwcWinPath -= 10;
394 if (cwcWinPath < _32K - 1)
395 {
396 PRTUTF16 pwszNtPath = RTUtf16Alloc((cwcWinPath + 1) * sizeof(RTUTF16));
397 if (pwszNtPath)
398 {
399 memcpy(pwszNtPath, pwszWinPath, cwcWinPath * sizeof(RTUTF16));
400 pwszNtPath[cwcWinPath] = '\0';
401 pNtName->Buffer = pwszNtPath;
402 pNtName->Length = (uint16_t)(cwcWinPath * sizeof(RTUTF16));
403 pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
404 *phRootDir = NULL;
405 return VINF_SUCCESS;
406 }
407 rc = VERR_NO_UTF16_MEMORY;
408 }
409 else
410 rc = VERR_FILENAME_TOO_LONG;
411 return rc;
412 }
413#endif
414
415 if ( pwszWinPath[2] == '?'
416 && cwcWinPath >= 4
417 && RTPATH_IS_SLASH(pwszWinPath[3]))
418 return rtNtPathFromWinUtf16PassThru(pNtName, phRootDir, pwszWinPath, cwcWinPath);
419
420 if ( pwszWinPath[2] == '.'
421 && cwcWinPath >= 4
422 && RTPATH_IS_SLASH(pwszWinPath[3]))
423 {
424 /*
425 * Device path.
426 * Note! I suspect \\.\stuff\..\otherstuff may be handled differently by windows.
427 */
428 cchSkip = 4;
429 }
430 else
431 {
432 /* UNC */
433 pszPrefix = g_szPrefixUnc;
434 cchPrefix = sizeof(g_szPrefixUnc) - 1;
435 cchSkip = 2;
436 }
437 }
438
439 /*
440 * Straighten out all .. and unnecessary . references and convert slashes.
441 */
442 /* UTF-16 -> UTF-8 (relative path) */
443 char szRelPath[RTPATH_MAX];
444 char *pszRelPathFree = NULL;
445 char *pszRelPath = szRelPath;
446 size_t cchRelPath;
447 rc = RTUtf16ToUtf8Ex(pwszWinPath, cwcWinPath, &pszRelPath, sizeof(szRelPath), &cchRelPath);
448 if (RT_SUCCESS(rc))
449 { /* likely */ }
450 else if (rc == VERR_BUFFER_OVERFLOW)
451 {
452 pszRelPath = NULL;
453 rc = RTUtf16ToUtf8Ex(pwszWinPath, cwcWinPath, &pszRelPath, 0, &cchRelPath);
454 if (RT_SUCCESS(rc))
455 pszRelPathFree = pszRelPath;
456 }
457 if (RT_SUCCESS(rc))
458 {
459 /* Relative -> Absolute. */
460 char szAbsPathBuf[RTPATH_MAX];
461 char *pszAbsPathFree = NULL;
462 char *pszAbsPath = szAbsPathBuf;
463 size_t cbAbsPath = sizeof(szAbsPathBuf) - (cchPrefix - cchSkip);
464 rc = RTPathAbsEx(NULL, szRelPath, RTPATH_STR_F_STYLE_DOS, &pszAbsPath[cchPrefix - cchSkip], &cbAbsPath);
465 if (RT_SUCCESS(rc))
466 { /* likely */ }
467 else if (rc == VERR_BUFFER_OVERFLOW)
468 {
469 unsigned cTries = 8;
470 size_t cbAbsPathBuf = RTPATH_MAX;
471 for (;;)
472 {
473 cbAbsPathBuf = RT_MAX(RT_ALIGN_Z((cchPrefix - cchSkip) + cbAbsPath + 32, 64), cbAbsPathBuf + 256);
474 if (cTries == 1)
475 cbAbsPathBuf = RT_MAX(cbAbsPathBuf, RTPATH_BIG_MAX * 2);
476 pszAbsPathFree = pszAbsPath = (char *)RTMemTmpAlloc(cbAbsPathBuf);
477 if (!pszAbsPath)
478 {
479 rc = VERR_NO_TMP_MEMORY;
480 break;
481 }
482
483 cbAbsPath = cbAbsPathBuf - (cchPrefix - cchSkip);
484 rc = RTPathAbsEx(NULL, szRelPath, RTPATH_STR_F_STYLE_DOS, &pszAbsPath[cchPrefix - cchSkip], &cbAbsPath);
485 if (RT_SUCCESS(rc))
486 break;
487
488 RTMemTmpFree(pszAbsPathFree);
489 pszAbsPathFree = NULL;
490 if (rc != VERR_BUFFER_OVERFLOW)
491 break;
492 if (--cTries == 0)
493 {
494 rc = VERR_FILENAME_TOO_LONG;
495 break;
496 }
497 }
498
499 }
500 if (pszRelPathFree)
501 RTStrFree(pszRelPathFree);
502
503 if (RT_SUCCESS(rc))
504 {
505 /*
506 * Add prefix
507 */
508 memcpy(pszAbsPath, pszPrefix, cchPrefix);
509
510 /*
511 * Remove trailing '.' that is used to specify no extension in the Win32/DOS world.
512 */
513 size_t cchAbsPath = strlen(pszAbsPath);
514 if ( cchAbsPath > 2
515 && pszAbsPath[cchAbsPath - 1] == '.')
516 {
517 char const ch = pszAbsPath[cchAbsPath - 2];
518 if ( ch != '/'
519 && ch != '\\'
520 && ch != ':'
521 && ch != '.')
522 pszAbsPath[--cchAbsPath] = '\0';
523 }
524
525 /*
526 * Finally convert to UNICODE_STRING.
527 */
528 rc = rtNtPathUtf8ToUniStr(pNtName, phRootDir, pszAbsPath);
529
530 if (pszAbsPathFree)
531 RTMemTmpFree(pszAbsPathFree);
532 }
533 }
534 return rc;
535}
536
537
538/**
539 * Ensures that the NT string has sufficient storage to hold @a cwcMin RTUTF16
540 * chars plus a terminator.
541 *
542 * The NT string must have been returned by RTNtPathFromWinUtf8 or
543 * RTNtPathFromWinUtf16Ex.
544 *
545 * @returns IPRT status code.
546 * @param pNtName The NT path string.
547 * @param cwcMin The minimum number of RTUTF16 chars. Max 32767.
548 * @sa RTNtPathFree
549 */
550RTDECL(int) RTNtPathEnsureSpace(struct _UNICODE_STRING *pNtName, size_t cwcMin)
551{
552 if (pNtName->MaximumLength / sizeof(RTUTF16) > cwcMin)
553 return VINF_SUCCESS;
554
555 AssertReturn(cwcMin < _64K / sizeof(RTUTF16), VERR_OUT_OF_RANGE);
556
557 size_t const cbMin = (cwcMin + 1) * sizeof(RTUTF16);
558 int rc = RTUtf16Realloc(&pNtName->Buffer, cbMin);
559 if (RT_SUCCESS(rc))
560 pNtName->MaximumLength = (uint16_t)cbMin;
561 return rc;
562}
563
564
565/**
566 * Gets the NT path to the object represented by the given handle.
567 *
568 * @returns IPRT status code.
569 * @param pNtName Where to return the NT path. Free using
570 * RTNtPathFree.
571 * @param hHandle The handle.
572 * @param cwcExtra How much extra space is needed.
573 */
574RTDECL(int) RTNtPathFromHandle(struct _UNICODE_STRING *pNtName, HANDLE hHandle, size_t cwcExtra)
575{
576 /*
577 * Query the name into a buffer.
578 */
579 ULONG cbBuf = _2K;
580 PUNICODE_STRING pUniStrBuf = (PUNICODE_STRING)RTMemTmpAllocZ(cbBuf);
581 if (!pUniStrBuf)
582 return VERR_NO_TMP_MEMORY;
583
584 ULONG cbNameBuf = cbBuf;
585 NTSTATUS rcNt = NtQueryObject(hHandle, ObjectNameInformation, pUniStrBuf, cbBuf, &cbNameBuf);
586 while ( rcNt == STATUS_BUFFER_OVERFLOW
587 || rcNt == STATUS_BUFFER_TOO_SMALL)
588 {
589 do
590 cbBuf *= 2;
591 while (cbBuf <= cbNameBuf);
592 RTMemTmpFree(pUniStrBuf);
593 pUniStrBuf = (PUNICODE_STRING)RTMemTmpAllocZ(cbBuf);
594 if (!pUniStrBuf)
595 return VERR_NO_TMP_MEMORY;
596
597 cbNameBuf = cbBuf;
598 rcNt = NtQueryObject(hHandle, ObjectNameInformation, pUniStrBuf, cbBuf, &cbNameBuf);
599 }
600 int rc;
601 if (NT_SUCCESS(rcNt))
602 {
603 /*
604 * Copy the result into the return string.
605 */
606 size_t cbNeeded = cwcExtra * sizeof(RTUTF16) + pUniStrBuf->Length + sizeof(RTUTF16);
607 if (cbNeeded < _64K)
608 {
609 pNtName->Length = pUniStrBuf->Length;
610 pNtName->MaximumLength = (uint16_t)cbNeeded;
611 pNtName->Buffer = RTUtf16Alloc(cbNeeded);
612 if (pNtName->Buffer)
613 {
614 memcpy(pNtName->Buffer, pUniStrBuf->Buffer, pUniStrBuf->Length);
615 pNtName->Buffer[pUniStrBuf->Length / sizeof(RTUTF16)] = '\0';
616 rc = VINF_SUCCESS;
617 }
618 else
619 rc = VERR_NO_UTF16_MEMORY;
620 }
621 else
622 rc = VERR_FILENAME_TOO_LONG;
623 }
624 else
625 rc = RTErrConvertFromNtStatus(rcNt);
626 RTMemTmpFree(pUniStrBuf);
627 return rc;
628}
629
630static int rtNtPathRelativeToAbs(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir)
631{
632 int rc;
633 if (pNtName->Length == 0)
634 {
635 RTUtf16Free(pNtName->Buffer);
636 rc = RTNtPathFromHandle(pNtName, *phRootDir, pNtName->Length / sizeof(RTUTF16) + 2);
637 if (RT_SUCCESS(rc))
638 {
639 *phRootDir = NULL;
640 return VINF_SUCCESS;
641 }
642 }
643 else
644 {
645
646 UNICODE_STRING RootDir;
647 size_t const cwcAppend = pNtName->Length / sizeof(RTUTF16);
648 rc = RTNtPathFromHandle(&RootDir, *phRootDir, cwcAppend + 2);
649 if (RT_SUCCESS(rc))
650 {
651 size_t cwcRoot = RootDir.Length / sizeof(RTUTF16);
652 if (RootDir.Buffer[cwcRoot - 1] != '\\')
653 RootDir.Buffer[cwcRoot++] = '\\';
654 memcpy(&RootDir.Buffer[cwcRoot], pNtName->Buffer, cwcAppend * sizeof(RTUTF16));
655 RTUtf16Free(pNtName->Buffer);
656 pNtName->Length = (uint16_t)((cwcRoot + cwcAppend) * sizeof(RTUTF16));
657 pNtName->MaximumLength = RootDir.MaximumLength;
658 pNtName->Buffer = RootDir.Buffer;
659
660 *phRootDir = NULL;
661 return VINF_SUCCESS;
662 }
663 RTUtf16Free(pNtName->Buffer);
664 }
665 pNtName->Length = 0;
666 pNtName->MaximumLength = 0;
667 pNtName->Buffer = NULL;
668 return rc;
669}
670
671
672/**
673 * Rewinds the path back to the start of the previous component.
674 *
675 * Will preserve root slash.
676 *
677 * @returns Pointer to character after the start-of-component slash, or
678 * pwszStart.
679 * @param pwcEnd The current end of the path.
680 * @param pwszStart The start of the path.
681 */
682static PRTUTF16 rtNtPathGetPrevComponent(PRTUTF16 pwcEnd, PRTUTF16 pwszStart)
683{
684 if ((uintptr_t)pwcEnd > (uintptr_t)pwszStart)
685 {
686 RTUTF16 wc = pwcEnd[-1];
687 if ( (wc == '\\' || wc == '/')
688 && (uintptr_t)(pwcEnd - pwszStart) != 1)
689 pwcEnd--;
690
691 while ( (uintptr_t)pwcEnd > (uintptr_t)pwszStart
692 && (wc = pwcEnd[-1]) != '\\'
693 && (wc = pwcEnd[-1]) != '/')
694 pwcEnd--;
695 }
696 return pwcEnd;
697}
698
699
700/**
701 * Converts a relative windows-style path to relative NT format and encoding.
702 *
703 * @returns IPRT status code.
704 * @param pNtName Where to return the NT name. Free using
705 * rtTNtPathToNative with phRootDir set to NULL.
706 * @param phRootDir On input, the handle to the directory the path
707 * is relative to. On output, the handle to
708 * specify as root directory in the object
709 * attributes when accessing the path. If
710 * enmAscent is kRTNtPathRelativeAscent_Allow, it
711 * may have been set to NULL.
712 * @param pszPath The relative UTF-8 path.
713 * @param enmAscent How to handle ascent.
714 * @param fMustReturnAbsolute Must convert to an absolute path. This
715 * is necessary if the root dir is a NT directory
716 * object (e.g. /Devices) since they cannot parse
717 * relative paths it seems.
718 */
719RTDECL(int) RTNtPathRelativeFromUtf8(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath,
720 RTNTPATHRELATIVEASCENT enmAscent, bool fMustReturnAbsolute)
721{
722 size_t cwcMax;
723 int rc = RTStrCalcUtf16LenEx(pszPath, RTSTR_MAX, &cwcMax);
724 if (RT_FAILURE(rc))
725 return rc;
726 if (cwcMax + 2 >= _32K)
727 return VERR_FILENAME_TOO_LONG;
728
729 PRTUTF16 pwszDst;
730 pNtName->Length = 0;
731 pNtName->MaximumLength = (uint16_t)((cwcMax + 2) * sizeof(RTUTF16));
732 pNtName->Buffer = pwszDst = RTUtf16Alloc((cwcMax + 2) * sizeof(RTUTF16));
733 if (!pwszDst)
734 return VERR_NO_UTF16_MEMORY;
735
736 PRTUTF16 pwszDstCur = pwszDst;
737 PRTUTF16 pwszDstComp = pwszDst;
738 for (;;)
739 {
740 RTUNICP uc;
741 rc = RTStrGetCpEx(&pszPath, &uc);
742 if (RT_SUCCESS(rc))
743 {
744 switch (uc)
745 {
746 default:
747 pwszDstCur = RTUtf16PutCp(pwszDstCur, uc);
748 break;
749
750 case '\\':
751 case '/':
752 if (pwszDstCur != pwszDstComp)
753 pwszDstComp = pwszDstCur = RTUtf16PutCp(pwszDstCur, '\\');
754 /* else: only one slash between components. */
755 break;
756
757 case '.':
758 if (pwszDstCur == pwszDstComp)
759 {
760 /*
761 * Single dot changes nothing.
762 */
763 char ch2 = *pszPath;
764 if (ch2 == '\0')
765 {
766 /* Trailing single dot means we need to drop trailing slash. */
767 if (pwszDstCur != pwszDst)
768 pwszDstCur--;
769 *pwszDstCur = '\0';
770 pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
771 if (!fMustReturnAbsolute || *phRootDir == NULL)
772 return VINF_SUCCESS;
773 return rtNtPathRelativeToAbs(pNtName, phRootDir);
774 }
775
776 if (ch2 == '\\' || ch2 == '/')
777 {
778 pszPath++; /* Ignore lone dot followed but another component. */
779 break;
780 }
781
782 /*
783 * Two dots drops off the last directory component. This gets complicated
784 * when we start out without any path and we need to consult enmAscent.
785 */
786 if (ch2 == '.')
787 {
788 char ch3 = pszPath[1];
789 if ( ch3 == '\\'
790 || ch3 == '/'
791 || ch3 == '\0')
792 {
793 /* Drop a path component. */
794 if (pwszDstComp != pwszDst)
795 pwszDstComp = pwszDstCur = rtNtPathGetPrevComponent(pwszDstCur, pwszDst);
796 /* Hit the start, which is a bit complicated. */
797 else
798 switch (enmAscent)
799 {
800 case kRTNtPathRelativeAscent_Allow:
801 if (*phRootDir != NULL)
802 {
803 RTUtf16Free(pwszDst);
804 rc = RTNtPathFromHandle(pNtName, *phRootDir, cwcMax + 2);
805 if (RT_FAILURE(rc))
806 return rc;
807
808 *phRootDir = NULL;
809 pwszDst = pNtName->Buffer;
810 pwszDstCur = &pwszDst[pNtName->Length / sizeof(RTUTF16)];
811 if ( pwszDst != pwszDstCur
812 && pwszDstCur[-1] != '\\'
813 && pwszDstCur[-1] != '/')
814 *pwszDstCur++ = '\\';
815 pwszDstComp = pwszDstCur = rtNtPathGetPrevComponent(pwszDstCur, pwszDst);
816 }
817 /* else: ignore attempt to ascend beyond the NT root (won't get here). */
818 break;
819
820 case kRTNtPathRelativeAscent_Ignore:
821 /* nothing to do here */
822 break;
823
824 default:
825 case kRTNtPathRelativeAscent_Fail:
826 RTUtf16Free(pwszDst);
827 return VERR_PATH_NOT_FOUND;
828 }
829
830 if (ch3 == '\0')
831 {
832 *pwszDstCur = '\0';
833 pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
834 if (!fMustReturnAbsolute || *phRootDir == NULL)
835 return VINF_SUCCESS;
836 return rtNtPathRelativeToAbs(pNtName, phRootDir);
837 }
838 pszPath += 2;
839 break;
840 }
841 }
842 }
843
844 /* Neither '.' nor '..'. */
845 pwszDstCur = RTUtf16PutCp(pwszDstCur, '.');
846 break;
847
848 case '\0':
849 *pwszDstCur = '\0';
850 pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
851 if (!fMustReturnAbsolute || *phRootDir == NULL)
852 return VINF_SUCCESS;
853 return rtNtPathRelativeToAbs(pNtName, phRootDir);
854 }
855 }
856 }
857}
858
859
860/**
861 * Frees the native path and root handle.
862 *
863 * @param pNtName The NT path after a successful rtNtPathToNative
864 * call or RTNtPathRelativeFromUtf8.
865 * @param phRootDir The root handle variable from rtNtPathToNative,
866 * but NOT RTNtPathRelativeFromUtf8.
867 */
868static void rtNtPathFreeNative(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir)
869{
870 RTUtf16Free(pNtName->Buffer);
871 pNtName->Buffer = NULL;
872
873 RT_NOREF_PV(phRootDir); /* never returned by rtNtPathToNative, shouldn't be freed in connection with RTNtPathRelativeFromUtf8 */
874}
875
876
877/**
878 * Frees the native path and root handle.
879 *
880 * @param pNtName The NT path after a successful rtNtPathToNative
881 * call or RTNtPathRelativeFromUtf8.
882 * @param phRootDir The root handle variable from rtNtPathToNative,
883 */
884RTDECL(void) RTNtPathFree(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir)
885{
886 rtNtPathFreeNative(pNtName, phRootDir);
887}
888
889
890/**
891 * Wrapper around NtCreateFile.
892 *
893 * @returns IPRT status code.
894 * @param pszPath The UTF-8 path.
895 * @param fDesiredAccess See NtCreateFile.
896 * @param fFileAttribs See NtCreateFile.
897 * @param fShareAccess See NtCreateFile.
898 * @param fCreateDisposition See NtCreateFile.
899 * @param fCreateOptions See NtCreateFile.
900 * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
901 * NtCreateFile and InitializeObjectAttributes.
902 * @param phHandle Where to return the handle.
903 * @param puAction Where to return the action taken. Optional.
904 */
905RTDECL(int) RTNtPathOpen(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, ULONG fShareAccess,
906 ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs,
907 PHANDLE phHandle, PULONG_PTR puAction)
908{
909 *phHandle = RTNT_INVALID_HANDLE_VALUE;
910
911 HANDLE hRootDir;
912 UNICODE_STRING NtName;
913 int rc = rtNtPathToNative(&NtName, &hRootDir, pszPath);
914 if (RT_SUCCESS(rc))
915 {
916 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
917 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
918 OBJECT_ATTRIBUTES ObjAttr;
919 InitializeObjectAttributes(&ObjAttr, &NtName, fObjAttribs, hRootDir, NULL);
920
921 NTSTATUS rcNt = NtCreateFile(&hFile,
922 fDesiredAccess,
923 &ObjAttr,
924 &Ios,
925 NULL /* AllocationSize*/,
926 fFileAttribs,
927 fShareAccess,
928 fCreateDisposition,
929 fCreateOptions,
930 NULL /*EaBuffer*/,
931 0 /*EaLength*/);
932 if (NT_SUCCESS(rcNt))
933 {
934 if (puAction)
935 *puAction = Ios.Information;
936 *phHandle = hFile;
937 rc = VINF_SUCCESS;
938 }
939 else
940 rc = RTErrConvertFromNtStatus(rcNt);
941 rtNtPathFreeNative(&NtName, &hRootDir);
942 }
943 return rc;
944}
945
946
947/**
948 * Wrapper around NtCreateFile.
949 *
950 * @returns IPRT status code.
951 * @param pszPath The UTF-8 path.
952 * @param fDesiredAccess See NtCreateFile.
953 * @param fShareAccess See NtCreateFile.
954 * @param fCreateOptions See NtCreateFile.
955 * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
956 * NtCreateFile and InitializeObjectAttributes.
957 * @param phHandle Where to return the handle.
958 * @param pfObjDir If not NULL, the variable pointed to will be set
959 * to @c true if we opened an object directory and
960 * @c false if we opened an directory file (normal
961 * directory).
962 */
963RTDECL(int) RTNtPathOpenDir(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fShareAccess, ULONG fCreateOptions,
964 ULONG fObjAttribs, PHANDLE phHandle, bool *pfObjDir)
965{
966 *phHandle = RTNT_INVALID_HANDLE_VALUE;
967
968 HANDLE hRootDir;
969 UNICODE_STRING NtName;
970 int rc = rtNtPathToNative(&NtName, &hRootDir, pszPath);
971 if (RT_SUCCESS(rc))
972 {
973 if (pfObjDir)
974 {
975 *pfObjDir = false;
976#ifdef IPRT_WITH_NT_PATH_PASSTHRU
977 if ( !RTPATH_IS_SLASH(pszPath[0])
978 || !RTPATH_IS_SLASH(pszPath[1])
979 || pszPath[2] != ':'
980 || pszPath[3] != 'i'
981 || pszPath[4] != 'p'
982 || pszPath[5] != 'r'
983 || pszPath[6] != 't'
984 || pszPath[7] != 'n'
985 || pszPath[8] != 't'
986 || pszPath[9] != ':'
987 || !RTPATH_IS_SLASH(pszPath[10]) )
988#endif
989 pfObjDir = NULL;
990 }
991 rc = RTNtPathOpenDirEx(hRootDir, &NtName, fDesiredAccess, fShareAccess, fCreateOptions, fObjAttribs, phHandle, pfObjDir);
992 rtNtPathFreeNative(&NtName, &hRootDir);
993 }
994 return rc;
995}
996
997
998
999/**
1000 * Wrapper around NtCreateFile, extended version.
1001 *
1002 * @returns IPRT status code.
1003 * @param hRootDir The root director the path is relative to. NULL
1004 * if none.
1005 * @param pNtName The NT path.
1006 * @param fDesiredAccess See NtCreateFile.
1007 * @param fShareAccess See NtCreateFile.
1008 * @param fCreateOptions See NtCreateFile.
1009 * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
1010 * NtCreateFile and InitializeObjectAttributes.
1011 * @param phHandle Where to return the handle.
1012 * @param pfObjDir If not NULL, the variable pointed to will be set
1013 * to @c true if we opened an object directory and
1014 * @c false if we opened an directory file (normal
1015 * directory).
1016 */
1017RTDECL(int) RTNtPathOpenDirEx(HANDLE hRootDir, struct _UNICODE_STRING *pNtName, ACCESS_MASK fDesiredAccess, ULONG fShareAccess,
1018 ULONG fCreateOptions, ULONG fObjAttribs, PHANDLE phHandle, bool *pfObjDir)
1019{
1020 *phHandle = RTNT_INVALID_HANDLE_VALUE;
1021
1022 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
1023 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1024 OBJECT_ATTRIBUTES ObjAttr;
1025 InitializeObjectAttributes(&ObjAttr, pNtName, fObjAttribs, hRootDir, NULL);
1026
1027 NTSTATUS rcNt = NtCreateFile(&hFile,
1028 fDesiredAccess,
1029 &ObjAttr,
1030 &Ios,
1031 NULL /* AllocationSize*/,
1032 FILE_ATTRIBUTE_NORMAL,
1033 fShareAccess,
1034 FILE_OPEN,
1035 fCreateOptions,
1036 NULL /*EaBuffer*/,
1037 0 /*EaLength*/);
1038 if (NT_SUCCESS(rcNt))
1039 {
1040 if (pfObjDir)
1041 *pfObjDir = false;
1042 *phHandle = hFile;
1043 return VINF_SUCCESS;
1044 }
1045
1046 /*
1047 * Try add a slash in case this is a device object with a file system attached.
1048 */
1049 if ( rcNt == STATUS_INVALID_PARAMETER
1050 && pNtName->Length < _64K - 4
1051 && ( pNtName->Length == 0
1052 || pNtName->Buffer[pNtName->Length / sizeof(RTUTF16)] != '\\') )
1053 {
1054 UNICODE_STRING NtTmp;
1055 NtTmp.Length = pNtName->Length + 2;
1056 NtTmp.MaximumLength = NtTmp.Length + 2;
1057 NtTmp.Buffer = (PRTUTF16)RTMemTmpAlloc(NtTmp.MaximumLength);
1058 if (NtTmp.Buffer)
1059 {
1060 memcpy(NtTmp.Buffer, pNtName->Buffer, pNtName->Length);
1061 NtTmp.Buffer[pNtName->Length / sizeof(RTUTF16)] = '\\';
1062 NtTmp.Buffer[pNtName->Length / sizeof(RTUTF16) + 1] = '\0';
1063
1064 hFile = RTNT_INVALID_HANDLE_VALUE;
1065 Ios.Status = -1;
1066 Ios.Information = 0;
1067 ObjAttr.ObjectName = &NtTmp;
1068
1069 rcNt = NtCreateFile(&hFile,
1070 fDesiredAccess,
1071 &ObjAttr,
1072 &Ios,
1073 NULL /* AllocationSize*/,
1074 FILE_ATTRIBUTE_NORMAL,
1075 fShareAccess,
1076 FILE_OPEN,
1077 fCreateOptions,
1078 NULL /*EaBuffer*/,
1079 0 /*EaLength*/);
1080 RTMemTmpFree(NtTmp.Buffer);
1081 if (NT_SUCCESS(rcNt))
1082 {
1083 if (pfObjDir)
1084 *pfObjDir = false;
1085 *phHandle = hFile;
1086 return VINF_SUCCESS;
1087 }
1088 ObjAttr.ObjectName = pNtName;
1089 }
1090 }
1091
1092 /*
1093 * Try open it as a directory object if it makes sense.
1094 */
1095 if ( pfObjDir
1096 && ( rcNt == STATUS_OBJECT_NAME_INVALID
1097 || rcNt == STATUS_OBJECT_TYPE_MISMATCH ))
1098 {
1099 /* Strip trailing slash. */
1100 struct _UNICODE_STRING NtName2 = *pNtName;
1101 if ( NtName2.Length > 2
1102 && RTPATH_IS_SLASH(NtName2.Buffer[(NtName2.Length / 2) - 1]))
1103 NtName2.Length -= 2;
1104 ObjAttr.ObjectName = &NtName2;
1105
1106 /* Rought conversion of the access flags. */
1107 ULONG fObjDesiredAccess = 0;
1108 if ( (fDesiredAccess & GENERIC_ALL)
1109 || (fDesiredAccess & STANDARD_RIGHTS_ALL) == STANDARD_RIGHTS_ALL)
1110 fObjDesiredAccess = DIRECTORY_ALL_ACCESS;
1111 else
1112 {
1113 if (fDesiredAccess & (GENERIC_WRITE | STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA))
1114 fObjDesiredAccess |= DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_SUBDIRECTORY;
1115
1116 if ( (fDesiredAccess & (GENERIC_READ | STANDARD_RIGHTS_READ | FILE_LIST_DIRECTORY))
1117 || !fObjDesiredAccess)
1118 fObjDesiredAccess |= DIRECTORY_QUERY;
1119
1120 if (fDesiredAccess & FILE_TRAVERSE)
1121 fObjDesiredAccess |= DIRECTORY_TRAVERSE;
1122 }
1123
1124 rcNt = NtOpenDirectoryObject(&hFile, fObjDesiredAccess, &ObjAttr);
1125 if (NT_SUCCESS(rcNt))
1126 {
1127 *pfObjDir = true;
1128 *phHandle = hFile;
1129 return VINF_SUCCESS;
1130 }
1131 }
1132
1133 return RTErrConvertFromNtStatus(rcNt);
1134}
1135
1136
1137
1138/**
1139 * Closes an handled open by rtNtPathOpen.
1140 *
1141 * @returns IPRT status code
1142 * @param hHandle The handle value.
1143 */
1144RTDECL(int) RTNtPathClose(HANDLE hHandle)
1145{
1146 NTSTATUS rcNt = NtClose(hHandle);
1147 if (NT_SUCCESS(rcNt))
1148 return VINF_SUCCESS;
1149 return RTErrConvertFromNtStatus(rcNt);
1150}
1151
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