VirtualBox

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

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

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

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