VirtualBox

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

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

RTNtPathOpenDirEx: more access fixes

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