VirtualBox

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

Last change on this file since 106579 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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