VirtualBox

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

最後變更 在這個檔案從69753是 69705,由 vboxsync 提交於 7 年 前

IPRT: VFS and NT path handling fixes.

  • Rewrote RTDirQueryInfo for NT. When RTDirOpen* now opens directories, it will request read-attribute access in additions to listing.
  • Major adjustment of the VFS path parser. It now accepts both slashes and will deal differently with '..' in operations on directories.
  • Implemented native RTDirRelPathQueryInfo for NT.
  • NT directory object (NT namespace objects, not file system dirs) fixes for NT specific RTDirRel APIs.
  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 35.5 KB
 
1/* $Id: pathint-nt.cpp 69705 2017-11-15 16:42:59Z 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.alldomusa.eu.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[] = "\\??\\";
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) + pUniStrBuf->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
473static int rtNtPathRelativeToAbs(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir)
474{
475 int rc;
476 if (pNtName->Length == 0)
477 {
478 RTUtf16Free(pNtName->Buffer);
479 rc = rtNtPathFromHandle(pNtName, *phRootDir, pNtName->Length / sizeof(RTUTF16) + 2);
480 if (RT_SUCCESS(rc))
481 {
482 *phRootDir = NULL;
483 return VINF_SUCCESS;
484 }
485 }
486 else
487 {
488
489 UNICODE_STRING RootDir;
490 size_t const cwcAppend = pNtName->Length / sizeof(RTUTF16);
491 rc = rtNtPathFromHandle(&RootDir, *phRootDir, cwcAppend + 2);
492 if (RT_SUCCESS(rc))
493 {
494 size_t cwcRoot = RootDir.Length / sizeof(RTUTF16);
495 if (RootDir.Buffer[cwcRoot - 1] != '\\')
496 RootDir.Buffer[cwcRoot++] = '\\';
497 memcpy(&RootDir.Buffer[cwcRoot], pNtName->Buffer, cwcAppend * sizeof(RTUTF16));
498 RTUtf16Free(pNtName->Buffer);
499 pNtName->Length = (uint16_t)((cwcRoot + cwcAppend) * sizeof(RTUTF16));
500 pNtName->MaximumLength = RootDir.MaximumLength;
501 pNtName->Buffer = RootDir.Buffer;
502
503 *phRootDir = NULL;
504 return VINF_SUCCESS;
505 }
506 RTUtf16Free(pNtName->Buffer);
507 }
508 pNtName->Length = 0;
509 pNtName->MaximumLength = 0;
510 pNtName->Buffer = NULL;
511 return rc;
512}
513
514
515/**
516 * Rewinds the path back to the start of the previous component.
517 *
518 * Will preserve root slash.
519 *
520 * @returns Pointer to character after the start-of-component slash, or
521 * pwszStart.
522 * @param pwcEnd The current end of the path.
523 * @param pwszStart The start of the path.
524 */
525static PRTUTF16 rtNtPathGetPrevComponent(PRTUTF16 pwcEnd, PRTUTF16 pwszStart)
526{
527 if ((uintptr_t)pwcEnd > (uintptr_t)pwszStart)
528 {
529 RTUTF16 wc = pwcEnd[-1];
530 if ( (wc == '\\' || wc == '/')
531 && (uintptr_t)(pwcEnd - pwszStart) != 1)
532 pwcEnd--;
533
534 while ( (uintptr_t)pwcEnd > (uintptr_t)pwszStart
535 && (wc = pwcEnd[-1]) != '\\'
536 && (wc = pwcEnd[-1]) != '/')
537 pwcEnd--;
538 }
539 return pwcEnd;
540}
541
542
543/**
544 * Converts a relative windows-style path to relative NT format and encoding.
545 *
546 * @returns IPRT status code.
547 * @param pNtName Where to return the NT name. Free using
548 * rtTNtPathToNative with phRootDir set to NULL.
549 * @param phRootDir On input, the handle to the directory the path
550 * is relative to. On output, the handle to
551 * specify as root directory in the object
552 * attributes when accessing the path. If
553 * enmAscent is kRTNtPathRelativeAscent_Allow, it
554 * may have been set to NULL.
555 * @param pszPath The relative UTF-8 path.
556 * @param enmAscent How to handle ascent.
557 * @param fMustReturnAbsolute Must convert to an absolute path. This
558 * is necessary if the root dir is a NT directory
559 * object (e.g. /Devices) since they cannot parse
560 * relative paths it seems.
561 */
562RTDECL(int) RTNtPathRelativeFromUtf8(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath,
563 RTNTPATHRELATIVEASCENT enmAscent, bool fMustReturnAbsolute)
564{
565 size_t cwcMax;
566 int rc = RTStrCalcUtf16LenEx(pszPath, RTSTR_MAX, &cwcMax);
567 if (RT_FAILURE(rc))
568 return rc;
569 if (cwcMax + 2 >= _32K)
570 return VERR_FILENAME_TOO_LONG;
571
572 PRTUTF16 pwszDst;
573 pNtName->Length = 0;
574 pNtName->MaximumLength = (uint16_t)((cwcMax + 2) * sizeof(RTUTF16));
575 pNtName->Buffer = pwszDst = RTUtf16Alloc((cwcMax + 2) * sizeof(RTUTF16));
576 if (!pwszDst)
577 return VERR_NO_UTF16_MEMORY;
578
579 PRTUTF16 pwszDstCur = pwszDst;
580 PRTUTF16 pwszDstComp = pwszDst;
581 for (;;)
582 {
583 RTUNICP uc;
584 rc = RTStrGetCpEx(&pszPath, &uc);
585 if (RT_SUCCESS(rc))
586 {
587 switch (uc)
588 {
589 default:
590 pwszDstCur = RTUtf16PutCp(pwszDstCur, uc);
591 break;
592
593 case '\\':
594 case '/':
595 if (pwszDstCur != pwszDstComp)
596 pwszDstComp = pwszDstCur = RTUtf16PutCp(pwszDstCur, '\\');
597 /* else: only one slash between components. */
598 break;
599
600 case '.':
601 if (pwszDstCur == pwszDstComp)
602 {
603 /*
604 * Single dot changes nothing.
605 */
606 char ch2 = *pszPath;
607 if (ch2 == '\0')
608 {
609 /* Trailing single dot means we need to drop trailing slash. */
610 if (pwszDstCur != pwszDst)
611 pwszDstCur--;
612 *pwszDstCur = '\0';
613 pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
614 if (!fMustReturnAbsolute || *phRootDir == NULL)
615 return VINF_SUCCESS;
616 return rtNtPathRelativeToAbs(pNtName, phRootDir);
617 }
618
619 if (ch2 == '\\' || ch2 == '/')
620 {
621 pszPath++; /* Ignore lone dot followed but another component. */
622 break;
623 }
624
625 /*
626 * Two dots drops off the last directory component. This gets complicated
627 * when we start out without any path and we need to consult enmAscent.
628 */
629 if (ch2 == '.')
630 {
631 char ch3 = pszPath[1];
632 if ( ch3 == '\\'
633 || ch3 == '/'
634 || ch3 == '\0')
635 {
636 /* Drop a path component. */
637 if (pwszDstComp != pwszDst)
638 pwszDstComp = pwszDstCur = rtNtPathGetPrevComponent(pwszDstCur, pwszDst);
639 /* Hit the start, which is a bit complicated. */
640 else
641 switch (enmAscent)
642 {
643 case kRTNtPathRelativeAscent_Allow:
644 if (*phRootDir != NULL)
645 {
646 RTUtf16Free(pwszDst);
647 rc = rtNtPathFromHandle(pNtName, *phRootDir, cwcMax + 2);
648 if (RT_FAILURE(rc))
649 return rc;
650
651 *phRootDir = NULL;
652 pwszDst = pNtName->Buffer;
653 pwszDstCur = &pwszDst[pNtName->Length / sizeof(RTUTF16)];
654 if ( pwszDst != pwszDstCur
655 && pwszDstCur[-1] != '\\'
656 && pwszDstCur[-1] != '/')
657 *pwszDstCur++ = '\\';
658 pwszDstComp = pwszDstCur = rtNtPathGetPrevComponent(pwszDstCur, pwszDst);
659 }
660 /* else: ignore attempt to ascend beyond the NT root (won't get here). */
661 break;
662
663 case kRTNtPathRelativeAscent_Ignore:
664 /* nothing to do here */
665 break;
666
667 default:
668 case kRTNtPathRelativeAscent_Fail:
669 RTUtf16Free(pwszDst);
670 return VERR_PATH_NOT_FOUND;
671 }
672
673 if (ch3 == '\0')
674 {
675 *pwszDstCur = '\0';
676 pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
677 if (!fMustReturnAbsolute || *phRootDir == NULL)
678 return VINF_SUCCESS;
679 return rtNtPathRelativeToAbs(pNtName, phRootDir);
680 }
681 pszPath += 2;
682 break;
683 }
684 }
685 }
686
687 /* Neither '.' nor '..'. */
688 pwszDstCur = RTUtf16PutCp(pwszDstCur, '.');
689 break;
690
691 case '\0':
692 *pwszDstCur = '\0';
693 pNtName->Length = (uint16_t)((uintptr_t)pwszDstCur - (uintptr_t)pwszDst);
694 if (!fMustReturnAbsolute || *phRootDir == NULL)
695 return VINF_SUCCESS;
696 return rtNtPathRelativeToAbs(pNtName, phRootDir);
697 }
698 }
699 }
700}
701
702
703/**
704 * Frees the native path and root handle.
705 *
706 * @param pNtName The NT path after a successful rtNtPathToNative
707 * call or RTNtPathRelativeFromUtf8.
708 * @param phRootDir The root handle variable from rtNtPathToNative,
709 * but NOT RTNtPathRelativeFromUtf8.
710 */
711static void rtNtPathFreeNative(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir)
712{
713 RTUtf16Free(pNtName->Buffer);
714 pNtName->Buffer = NULL;
715
716 RT_NOREF_PV(phRootDir); /* never returned by rtNtPathToNative, shouldn't be freed in connection with RTNtPathRelativeFromUtf8 */
717}
718
719
720/**
721 * Frees the native path and root handle.
722 *
723 * @param pNtName The NT path after a successful rtNtPathToNative
724 * call or RTNtPathRelativeFromUtf8.
725 * @param phRootDir The root handle variable from rtNtPathToNative,
726 */
727RTDECL(void) RTNtPathFree(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir)
728{
729 rtNtPathFreeNative(pNtName, phRootDir);
730}
731
732
733/**
734 * Wrapper around NtCreateFile.
735 *
736 * @returns IPRT status code.
737 * @param pszPath The UTF-8 path.
738 * @param fDesiredAccess See NtCreateFile.
739 * @param fFileAttribs See NtCreateFile.
740 * @param fShareAccess See NtCreateFile.
741 * @param fCreateDisposition See NtCreateFile.
742 * @param fCreateOptions See NtCreateFile.
743 * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
744 * NtCreateFile and InitializeObjectAttributes.
745 * @param phHandle Where to return the handle.
746 * @param puAction Where to return the action taken. Optional.
747 */
748RTDECL(int) RTNtPathOpen(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, ULONG fShareAccess,
749 ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs,
750 PHANDLE phHandle, PULONG_PTR puAction)
751{
752 *phHandle = RTNT_INVALID_HANDLE_VALUE;
753
754 HANDLE hRootDir;
755 UNICODE_STRING NtName;
756 int rc = rtNtPathToNative(&NtName, &hRootDir, pszPath);
757 if (RT_SUCCESS(rc))
758 {
759 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
760 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
761 OBJECT_ATTRIBUTES ObjAttr;
762 InitializeObjectAttributes(&ObjAttr, &NtName, fObjAttribs, hRootDir, NULL);
763
764 NTSTATUS rcNt = NtCreateFile(&hFile,
765 fDesiredAccess,
766 &ObjAttr,
767 &Ios,
768 NULL /* AllocationSize*/,
769 fFileAttribs,
770 fShareAccess,
771 fCreateDisposition,
772 fCreateOptions,
773 NULL /*EaBuffer*/,
774 0 /*EaLength*/);
775 if (NT_SUCCESS(rcNt))
776 {
777 if (puAction)
778 *puAction = Ios.Information;
779 *phHandle = hFile;
780 rc = VINF_SUCCESS;
781 }
782 else
783 rc = RTErrConvertFromNtStatus(rcNt);
784 rtNtPathFreeNative(&NtName, &hRootDir);
785 }
786 return rc;
787}
788
789
790/**
791 * Wrapper around NtCreateFile.
792 *
793 * @returns IPRT status code.
794 * @param pszPath The UTF-8 path.
795 * @param fDesiredAccess See NtCreateFile.
796 * @param fShareAccess See NtCreateFile.
797 * @param fCreateOptions See NtCreateFile.
798 * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
799 * NtCreateFile and InitializeObjectAttributes.
800 * @param phHandle Where to return the handle.
801 * @param pfObjDir If not NULL, the variable pointed to will be set
802 * to @c true if we opened an object directory and
803 * @c false if we opened an directory file (normal
804 * directory).
805 */
806RTDECL(int) RTNtPathOpenDir(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fShareAccess, ULONG fCreateOptions,
807 ULONG fObjAttribs, PHANDLE phHandle, bool *pfObjDir)
808{
809 *phHandle = RTNT_INVALID_HANDLE_VALUE;
810
811 HANDLE hRootDir;
812 UNICODE_STRING NtName;
813 int rc = rtNtPathToNative(&NtName, &hRootDir, pszPath);
814 if (RT_SUCCESS(rc))
815 {
816 if (pfObjDir)
817 {
818 *pfObjDir = false;
819#ifdef IPRT_WITH_NT_PATH_PASSTHRU
820 if ( !RTPATH_IS_SLASH(pszPath[0])
821 || !RTPATH_IS_SLASH(pszPath[1])
822 || pszPath[2] != '!'
823 || RTPATH_IS_SLASH(pszPath[3]))
824#endif
825 pfObjDir = NULL;
826 }
827 rc = RTNtPathOpenDirEx(hRootDir, &NtName, fDesiredAccess, fShareAccess, fCreateOptions, fObjAttribs, phHandle, pfObjDir);
828 rtNtPathFreeNative(&NtName, &hRootDir);
829 }
830 return rc;
831}
832
833
834
835/**
836 * Wrapper around NtCreateFile, extended version.
837 *
838 * @returns IPRT status code.
839 * @param hRootDir The root director the path is relative to. NULL
840 * if none.
841 * @param pNtName The NT path.
842 * @param fDesiredAccess See NtCreateFile.
843 * @param fShareAccess See NtCreateFile.
844 * @param fCreateOptions See NtCreateFile.
845 * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
846 * NtCreateFile and InitializeObjectAttributes.
847 * @param phHandle Where to return the handle.
848 * @param pfObjDir If not NULL, the variable pointed to will be set
849 * to @c true if we opened an object directory and
850 * @c false if we opened an directory file (normal
851 * directory).
852 */
853RTDECL(int) RTNtPathOpenDirEx(HANDLE hRootDir, struct _UNICODE_STRING *pNtName, ACCESS_MASK fDesiredAccess, ULONG fShareAccess,
854 ULONG fCreateOptions, ULONG fObjAttribs, PHANDLE phHandle, bool *pfObjDir)
855{
856 *phHandle = RTNT_INVALID_HANDLE_VALUE;
857
858 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
859 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
860 OBJECT_ATTRIBUTES ObjAttr;
861 InitializeObjectAttributes(&ObjAttr, pNtName, fObjAttribs, hRootDir, NULL);
862
863 NTSTATUS rcNt = NtCreateFile(&hFile,
864 fDesiredAccess,
865 &ObjAttr,
866 &Ios,
867 NULL /* AllocationSize*/,
868 FILE_ATTRIBUTE_NORMAL,
869 fShareAccess,
870 FILE_OPEN,
871 fCreateOptions,
872 NULL /*EaBuffer*/,
873 0 /*EaLength*/);
874 if (NT_SUCCESS(rcNt))
875 {
876 if (pfObjDir)
877 *pfObjDir = false;
878 *phHandle = hFile;
879 return VINF_SUCCESS;
880 }
881
882 /*
883 * Try add a slash in case this is a device object with a file system attached.
884 */
885 if ( rcNt == STATUS_INVALID_PARAMETER
886 && pNtName->Length < _64K - 4
887 && ( pNtName->Length == 0
888 || pNtName->Buffer[pNtName->Length / sizeof(RTUTF16)] != '\\') )
889 {
890 UNICODE_STRING NtTmp;
891 NtTmp.Length = pNtName->Length + 2;
892 NtTmp.MaximumLength = NtTmp.Length + 2;
893 NtTmp.Buffer = (PRTUTF16)RTMemTmpAlloc(NtTmp.MaximumLength);
894 if (NtTmp.Buffer)
895 {
896 memcpy(NtTmp.Buffer, pNtName->Buffer, pNtName->Length);
897 NtTmp.Buffer[pNtName->Length / sizeof(RTUTF16)] = '\\';
898 NtTmp.Buffer[pNtName->Length / sizeof(RTUTF16) + 1] = '\0';
899
900 hFile = RTNT_INVALID_HANDLE_VALUE;
901 Ios.Status = -1;
902 Ios.Information = 0;
903 ObjAttr.ObjectName = &NtTmp;
904
905 rcNt = NtCreateFile(&hFile,
906 fDesiredAccess,
907 &ObjAttr,
908 &Ios,
909 NULL /* AllocationSize*/,
910 FILE_ATTRIBUTE_NORMAL,
911 fShareAccess,
912 FILE_OPEN,
913 fCreateOptions,
914 NULL /*EaBuffer*/,
915 0 /*EaLength*/);
916 RTMemTmpFree(NtTmp.Buffer);
917 if (NT_SUCCESS(rcNt))
918 {
919 if (pfObjDir)
920 *pfObjDir = false;
921 *phHandle = hFile;
922 return VINF_SUCCESS;
923 }
924 ObjAttr.ObjectName = pNtName;
925 }
926 }
927
928 /*
929 * Try open it as a directory object if it makes sense.
930 */
931 if ( pfObjDir
932 && ( rcNt == STATUS_OBJECT_NAME_INVALID
933 || rcNt == STATUS_OBJECT_TYPE_MISMATCH ))
934 {
935 /* Strip trailing slash. */
936 struct _UNICODE_STRING NtName2 = *pNtName;
937 if ( NtName2.Length > 2
938 && RTPATH_IS_SLASH(NtName2.Buffer[(NtName2.Length / 2) - 1]))
939 NtName2.Length -= 2;
940 ObjAttr.ObjectName = &NtName2;
941
942 /* Rought conversion of the access flags. */
943 ULONG fObjDesiredAccess = 0;
944 if (fDesiredAccess & (GENERIC_ALL | STANDARD_RIGHTS_ALL))
945 fObjDesiredAccess = DIRECTORY_ALL_ACCESS;
946 else
947 {
948 if (fDesiredAccess & (FILE_GENERIC_WRITE | GENERIC_WRITE | STANDARD_RIGHTS_WRITE))
949 fObjDesiredAccess |= DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_OBJECT;
950 if ( (fDesiredAccess & (FILE_LIST_DIRECTORY | FILE_GENERIC_READ | GENERIC_READ | STANDARD_RIGHTS_READ))
951 || !fObjDesiredAccess)
952 fObjDesiredAccess |= DIRECTORY_QUERY | FILE_LIST_DIRECTORY;
953 }
954
955 rcNt = NtOpenDirectoryObject(&hFile, fObjDesiredAccess, &ObjAttr);
956 if (NT_SUCCESS(rcNt))
957 {
958 *pfObjDir = true;
959 *phHandle = hFile;
960 return VINF_SUCCESS;
961 }
962 }
963
964 return RTErrConvertFromNtStatus(rcNt);
965}
966
967
968
969/**
970 * Closes an handled open by rtNtPathOpen.
971 *
972 * @returns IPRT status code
973 * @param hHandle The handle value.
974 */
975RTDECL(int) RTNtPathClose(HANDLE hHandle)
976{
977 NTSTATUS rcNt = NtClose(hHandle);
978 if (NT_SUCCESS(rcNt))
979 return VINF_SUCCESS;
980 return RTErrConvertFromNtStatus(rcNt);
981}
982
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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