VirtualBox

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

Last change on this file since 69691 was 69691, checked in by vboxsync, 8 years ago

iprt: Started on RTDirRel for NT.

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