VirtualBox

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

Last change on this file since 70195 was 70195, checked in by vboxsync, 7 years ago

IPRT/R3: Made the core work on NT 3.51 (still experimental).

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

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