VirtualBox

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

Last change on this file since 68246 was 67963, checked in by vboxsync, 7 years ago

RTNtPathFromWinUtf16Ex: Remove special trailing dot.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.7 KB
Line 
1/* $Id: pathint-nt.cpp 67963 2017-07-14 12:43:28Z vboxsync $ */
2/** @file
3 * IPRT - Native NT, Internal Path stuff.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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/string.h>
36#include <iprt/err.h>
37#include <iprt/assert.h>
38
39
40/*********************************************************************************************************************************
41* Global Variables *
42*********************************************************************************************************************************/
43static char const g_szPrefixUnc[] = "\\??\\UNC\\";
44static char const g_szPrefix[] = "\\??\\";
45
46
47/**
48 * Handles the pass thru case for UTF-8 input.
49 * Win32 path uses "\\?\" prefix which is converted to "\??\" NT prefix.
50 *
51 * @returns IPRT status code.
52 * @param pNtName Where to return the NT name.
53 * @param phRootDir Where to return the root handle, if applicable.
54 * @param pszPath The UTF-8 path.
55 */
56static int rtNtPathFromWinUtf8PassThru(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
57{
58 PRTUTF16 pwszPath = NULL;
59 size_t cwcLen;
60 int rc = RTStrToUtf16Ex(pszPath, RTSTR_MAX, &pwszPath, 0, &cwcLen);
61 if (RT_SUCCESS(rc))
62 {
63 if (cwcLen < _32K - 1)
64 {
65 pwszPath[0] = '\\';
66 pwszPath[1] = '?';
67 pwszPath[2] = '?';
68 pwszPath[3] = '\\';
69
70 pNtName->Buffer = pwszPath;
71 pNtName->Length = (uint16_t)(cwcLen * sizeof(RTUTF16));
72 pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
73 *phRootDir = NULL;
74 return VINF_SUCCESS;
75 }
76
77 RTUtf16Free(pwszPath);
78 rc = VERR_FILENAME_TOO_LONG;
79 }
80 return rc;
81}
82
83
84/**
85 * Handles the pass thru case for UTF-16 input.
86 * Win32 path uses "\\?\" prefix which is converted to "\??\" NT prefix.
87 *
88 * @returns IPRT status code.
89 * @param pNtName Where to return the NT name.
90 * @param phRootDir Stores NULL here, as we don't use it.
91 * @param pwszWinPath The UTF-16 windows-style path.
92 * @param cwcWinPath The length of the windows-style input path.
93 */
94static int rtNtPathFromWinUtf16PassThru(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir,
95 PCRTUTF16 pwszWinPath, size_t cwcWinPath)
96{
97 /* Check length and allocate memory for it. */
98 int rc;
99 if (cwcWinPath < _32K - 1)
100 {
101 PRTUTF16 pwszNtPath = (PRTUTF16)RTUtf16Alloc((cwcWinPath + 1) * sizeof(RTUTF16));
102 if (pwszNtPath)
103 {
104 /* Intialize the path. */
105 pwszNtPath[0] = '\\';
106 pwszNtPath[1] = '?';
107 pwszNtPath[2] = '?';
108 pwszNtPath[3] = '\\';
109 memcpy(pwszNtPath + 4, pwszWinPath + 4, (cwcWinPath - 4) * sizeof(RTUTF16));
110 pwszNtPath[cwcWinPath] = '\0';
111
112 /* Initialize the return values. */
113 pNtName->Buffer = pwszNtPath;
114 pNtName->Length = (uint16_t)(cwcWinPath * sizeof(RTUTF16));
115 pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
116 *phRootDir = NULL;
117
118 rc = VINF_SUCCESS;
119 }
120 else
121 rc = VERR_NO_UTF16_MEMORY;
122 }
123 else
124 rc = VERR_FILENAME_TOO_LONG;
125 return rc;
126}
127
128
129
130
131
132/**
133 * Converts the path to UTF-16 and sets all the return values.
134 *
135 * @returns IPRT status code.
136 * @param pNtName Where to return the NT name.
137 * @param phRootDir Where to return the root handle, if applicable.
138 * @param pszPath The UTF-8 path.
139 */
140static int rtNtPathUtf8ToUniStr(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
141{
142 PRTUTF16 pwszPath = NULL;
143 size_t cwcLen;
144 int rc = RTStrToUtf16Ex(pszPath, RTSTR_MAX, &pwszPath, 0, &cwcLen);
145 if (RT_SUCCESS(rc))
146 {
147 if (cwcLen < _32K - 1)
148 {
149 pNtName->Buffer = pwszPath;
150 pNtName->Length = (uint16_t)(cwcLen * sizeof(RTUTF16));
151 pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
152 *phRootDir = NULL;
153 return VINF_SUCCESS;
154 }
155
156 RTUtf16Free(pwszPath);
157 rc = VERR_FILENAME_TOO_LONG;
158 }
159 return rc;
160}
161
162
163/**
164 * Converts a windows-style path to NT format and encoding.
165 *
166 * @returns IPRT status code.
167 * @param pNtName Where to return the NT name. Free using
168 * rtTNtPathToNative.
169 * @param phRootDir Where to return the root handle, if applicable.
170 * @param pszPath The UTF-8 path.
171 */
172static int rtNtPathToNative(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
173{
174/** @todo This code sucks a bit performance wise, esp. calling
175 * generic RTPathAbs. Too many buffers involved, I think. */
176
177 /*
178 * Very simple conversion of a win32-like path into an NT path.
179 */
180 const char *pszPrefix = g_szPrefix;
181 size_t cchPrefix = sizeof(g_szPrefix) - 1;
182 size_t cchSkip = 0;
183
184 if ( RTPATH_IS_SLASH(pszPath[0])
185 && RTPATH_IS_SLASH(pszPath[1])
186 && !RTPATH_IS_SLASH(pszPath[2])
187 && pszPath[2])
188 {
189 if ( pszPath[2] == '?'
190 && RTPATH_IS_SLASH(pszPath[3]))
191 return rtNtPathFromWinUtf8PassThru(pNtName, phRootDir, pszPath);
192
193#ifdef IPRT_WITH_NT_PATH_PASSTHRU
194 /* Special hack: The path starts with "\\\\!\\", we will skip past the bang and pass it thru. */
195 if ( pszPath[2] == '!'
196 && RTPATH_IS_SLASH(pszPath[3]))
197 return rtNtPathUtf8ToUniStr(pNtName, phRootDir, pszPath + 3);
198#endif
199
200 if ( pszPath[2] == '.'
201 && RTPATH_IS_SLASH(pszPath[3]))
202 {
203 /*
204 * Device path.
205 * Note! I suspect \\.\stuff\..\otherstuff may be handled differently by windows.
206 */
207 cchSkip = 4;
208 }
209 else
210 {
211 /* UNC */
212 pszPrefix = g_szPrefixUnc;
213 cchPrefix = sizeof(g_szPrefixUnc) - 1;
214 cchSkip = 2;
215 }
216 }
217
218 /*
219 * Straighten out all .. and uncessary . references and convert slashes.
220 */
221 char szPath[RTPATH_MAX];
222 int rc = RTPathAbs(pszPath, &szPath[cchPrefix - cchSkip], sizeof(szPath) - (cchPrefix - cchSkip));
223 if (RT_FAILURE(rc))
224 return rc;
225
226 /*
227 * Add prefix and convert it to UTF16.
228 */
229 memcpy(szPath, pszPrefix, cchPrefix);
230 return rtNtPathUtf8ToUniStr(pNtName, phRootDir, szPath);
231}
232
233
234/**
235 * Converts a windows-style path to NT format and encoding.
236 *
237 * @returns IPRT status code.
238 * @param pNtName Where to return the NT name. Free using
239 * RTNtPathToNative.
240 * @param phRootDir Where to return the root handle, if applicable.
241 * @param pszPath The UTF-8 path.
242 */
243RTDECL(int) RTNtPathFromWinUtf8(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir, const char *pszPath)
244{
245 return rtNtPathToNative(pNtName, phRootDir, pszPath);
246}
247
248
249/**
250 * Converts a UTF-16 windows-style path to NT format.
251 *
252 * @returns IPRT status code.
253 * @param pNtName Where to return the NT name. Free using
254 * RTNtPathFree.
255 * @param phRootDir Where to return the root handle, if applicable.
256 * @param pwszWinPath The UTF-16 windows-style path.
257 * @param cwcWinPath The max length of the windows-style path in
258 * RTUTF16 units. Use RTSTR_MAX if unknown and @a
259 * pwszWinPath is correctly terminated.
260 */
261RTDECL(int) RTNtPathFromWinUtf16Ex(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir, PCRTUTF16 pwszWinPath, size_t cwcWinPath)
262{
263 /*
264 * Validate the input, calculating the correct length.
265 */
266 if (cwcWinPath == 0 || *pwszWinPath == '\0')
267 return VERR_INVALID_NAME;
268
269 RTUtf16NLenEx(pwszWinPath, cwcWinPath, &cwcWinPath);
270 int rc = RTUtf16ValidateEncodingEx(pwszWinPath, cwcWinPath, 0);
271 if (RT_FAILURE(rc))
272 return rc;
273
274 /*
275 * Very simple conversion of a win32-like path into an NT path.
276 */
277 const char *pszPrefix = g_szPrefix;
278 size_t cchPrefix = sizeof(g_szPrefix) - 1;
279 size_t cchSkip = 0;
280
281 if ( RTPATH_IS_SLASH(pwszWinPath[0])
282 && cwcWinPath >= 3
283 && RTPATH_IS_SLASH(pwszWinPath[1])
284 && !RTPATH_IS_SLASH(pwszWinPath[2]) )
285 {
286 if ( pwszWinPath[2] == '?'
287 && cwcWinPath >= 4
288 && RTPATH_IS_SLASH(pwszWinPath[3]))
289 return rtNtPathFromWinUtf16PassThru(pNtName, phRootDir, pwszWinPath, cwcWinPath);
290
291#ifdef IPRT_WITH_NT_PATH_PASSTHRU
292 /* Special hack: The path starts with "\\\\!\\", we will skip past the bang and pass it thru. */
293 if ( pwszWinPath[2] == '!'
294 && cwcWinPath >= 4
295 && RTPATH_IS_SLASH(pwszWinPath[3]))
296 {
297 pwszWinPath += 3;
298 cwcWinPath -= 3;
299 if (cwcWinPath < _32K - 1)
300 {
301 PRTUTF16 pwszNtPath = RTUtf16Alloc((cwcWinPath + 1) * sizeof(RTUTF16));
302 if (pwszNtPath)
303 {
304 memcpy(pwszNtPath, pwszWinPath, cwcWinPath * sizeof(RTUTF16));
305 pwszNtPath[cwcWinPath] = '\0';
306 pNtName->Buffer = pwszNtPath;
307 pNtName->Length = (uint16_t)(cwcWinPath * sizeof(RTUTF16));
308 pNtName->MaximumLength = pNtName->Length + sizeof(RTUTF16);
309 *phRootDir = NULL;
310 return VINF_SUCCESS;
311 }
312 rc = VERR_NO_UTF16_MEMORY;
313 }
314 else
315 rc = VERR_FILENAME_TOO_LONG;
316 return rc;
317 }
318#endif
319
320 if ( pwszWinPath[2] == '.'
321 && cwcWinPath >= 4
322 && RTPATH_IS_SLASH(pwszWinPath[3]))
323 {
324 /*
325 * Device path.
326 * Note! I suspect \\.\stuff\..\otherstuff may be handled differently by windows.
327 */
328 cchSkip = 4;
329 }
330 else
331 {
332 /* UNC */
333 pszPrefix = g_szPrefixUnc;
334 cchPrefix = sizeof(g_szPrefixUnc) - 1;
335 cchSkip = 2;
336 }
337 }
338
339 /*
340 * Straighten out all .. and unnecessary . references and convert slashes.
341 */
342 char szAbsPath[RTPATH_MAX];
343 char szRelPath[RTPATH_MAX];
344 char *pszRelPath = szRelPath;
345 size_t cchRelPath;
346 rc = RTUtf16ToUtf8Ex(pwszWinPath, cwcWinPath, &pszRelPath, sizeof(szRelPath), &cchRelPath);
347 if (RT_SUCCESS(rc))
348 rc = RTPathAbs(szRelPath, &szAbsPath[cchPrefix - cchSkip], sizeof(szAbsPath) - (cchPrefix - cchSkip));
349 if (RT_SUCCESS(rc))
350 {
351 /*
352 * Add prefix
353 */
354 memcpy(szAbsPath, pszPrefix, cchPrefix);
355
356 /*
357 * Remove trailing '.' that is used to specify no extension in the Win32/DOS world.
358 */
359 size_t cchAbsPath = strlen(szAbsPath);
360 if ( cchAbsPath > 2
361 && szAbsPath[cchAbsPath - 1] == '.')
362 {
363 char const ch = szAbsPath[cchAbsPath - 2];
364 if ( ch != '/'
365 && ch != '\\'
366 && ch != ':'
367 && ch != '.')
368 szAbsPath[--cchAbsPath] = '\0';
369 }
370
371 /*
372 * Finally convert to UNICODE_STRING.
373 */
374 return rtNtPathUtf8ToUniStr(pNtName, phRootDir, szAbsPath);
375 }
376 return rc;
377}
378
379
380/**
381 * Ensures that the NT string has sufficient storage to hold @a cwcMin RTUTF16
382 * chars plus a terminator.
383 *
384 * The NT string must have been returned by RTNtPathFromWinUtf8 or
385 * RTNtPathFromWinUtf16Ex.
386 *
387 * @returns IPRT status code.
388 * @param pNtName The NT path string.
389 * @param cwcMin The minimum number of RTUTF16 chars. Max 32767.
390 * @sa RTNtPathFree
391 */
392RTDECL(int) RTNtPathEnsureSpace(struct _UNICODE_STRING *pNtName, size_t cwcMin)
393{
394 if (pNtName->MaximumLength / sizeof(RTUTF16) > cwcMin)
395 return VINF_SUCCESS;
396
397 AssertReturn(cwcMin < _64K / sizeof(RTUTF16), VERR_OUT_OF_RANGE);
398
399 size_t const cbMin = (cwcMin + 1) * sizeof(RTUTF16);
400 int rc = RTUtf16Realloc(&pNtName->Buffer, cbMin);
401 if (RT_SUCCESS(rc))
402 pNtName->MaximumLength = (uint16_t)cbMin;
403 return rc;
404}
405
406
407/**
408 * Frees the native path and root handle.
409 *
410 * @param pNtName The NT path after a successful rtNtPathToNative
411 * call.
412 * @param phRootDir The root handle variable from the same call.
413 */
414static void rtNtPathFreeNative(struct _UNICODE_STRING *pNtName, PHANDLE phRootDir)
415{
416 RTUtf16Free(pNtName->Buffer);
417 pNtName->Buffer = NULL;
418
419 RT_NOREF_PV(phRootDir); /* never returned by rtNtPathToNative */
420}
421
422
423/**
424 * Frees the native path and root handle.
425 *
426 * @param pNtName The NT path from a successful RTNtPathToNative
427 * or RTNtPathFromWinUtf16Ex call.
428 * @param phRootDir The root handle variable from the same call.
429 */
430RTDECL(void) RTNtPathFree(struct _UNICODE_STRING *pNtName, HANDLE *phRootDir)
431{
432 rtNtPathFreeNative(pNtName, phRootDir);
433}
434
435
436/**
437 * Wrapper around NtCreateFile.
438 *
439 * @returns IPRT status code.
440 * @param pszPath The UTF-8 path.
441 * @param fDesiredAccess See NtCreateFile.
442 * @param fFileAttribs See NtCreateFile.
443 * @param fShareAccess See NtCreateFile.
444 * @param fCreateDisposition See NtCreateFile.
445 * @param fCreateOptions See NtCreateFile.
446 * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
447 * NtCreateFile and InitializeObjectAttributes.
448 * @param phHandle Where to return the handle.
449 * @param puAction Where to return the action taken. Optional.
450 */
451RTDECL(int) RTNtPathOpen(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fFileAttribs, ULONG fShareAccess,
452 ULONG fCreateDisposition, ULONG fCreateOptions, ULONG fObjAttribs,
453 PHANDLE phHandle, PULONG_PTR puAction)
454{
455 *phHandle = RTNT_INVALID_HANDLE_VALUE;
456
457 HANDLE hRootDir;
458 UNICODE_STRING NtName;
459 int rc = rtNtPathToNative(&NtName, &hRootDir, pszPath);
460 if (RT_SUCCESS(rc))
461 {
462 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
463 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
464 OBJECT_ATTRIBUTES ObjAttr;
465 InitializeObjectAttributes(&ObjAttr, &NtName, fObjAttribs, hRootDir, NULL);
466
467 NTSTATUS rcNt = NtCreateFile(&hFile,
468 fDesiredAccess,
469 &ObjAttr,
470 &Ios,
471 NULL /* AllocationSize*/,
472 fFileAttribs,
473 fShareAccess,
474 fCreateDisposition,
475 fCreateOptions,
476 NULL /*EaBuffer*/,
477 0 /*EaLength*/);
478 if (NT_SUCCESS(rcNt))
479 {
480 if (puAction)
481 *puAction = Ios.Information;
482 *phHandle = hFile;
483 rc = VINF_SUCCESS;
484 }
485 else
486 rc = RTErrConvertFromNtStatus(rcNt);
487 rtNtPathFreeNative(&NtName, &hRootDir);
488 }
489 return rc;
490}
491
492
493/**
494 * Wrapper around NtCreateFile.
495 *
496 * @returns IPRT status code.
497 * @param pszPath The UTF-8 path.
498 * @param fDesiredAccess See NtCreateFile.
499 * @param fShareAccess See NtCreateFile.
500 * @param fCreateOptions See NtCreateFile.
501 * @param fObjAttribs The OBJECT_ATTRIBUTES::Attributes value, see
502 * NtCreateFile and InitializeObjectAttributes.
503 * @param phHandle Where to return the handle.
504 * @param pfObjDir If not NULL, the variable pointed to will be set
505 * to @c true if we opened an object directory and
506 * @c false if we opened an directory file (normal
507 * directory).
508 */
509RTDECL(int) RTNtPathOpenDir(const char *pszPath, ACCESS_MASK fDesiredAccess, ULONG fShareAccess, ULONG fCreateOptions,
510 ULONG fObjAttribs, PHANDLE phHandle, bool *pfObjDir)
511{
512 *phHandle = RTNT_INVALID_HANDLE_VALUE;
513
514 HANDLE hRootDir;
515 UNICODE_STRING NtName;
516 int rc = rtNtPathToNative(&NtName, &hRootDir, pszPath);
517 if (RT_SUCCESS(rc))
518 {
519 HANDLE hFile = RTNT_INVALID_HANDLE_VALUE;
520 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
521 OBJECT_ATTRIBUTES ObjAttr;
522 InitializeObjectAttributes(&ObjAttr, &NtName, fObjAttribs, hRootDir, NULL);
523
524 NTSTATUS rcNt = NtCreateFile(&hFile,
525 fDesiredAccess,
526 &ObjAttr,
527 &Ios,
528 NULL /* AllocationSize*/,
529 FILE_ATTRIBUTE_NORMAL,
530 fShareAccess,
531 FILE_OPEN,
532 fCreateOptions,
533 NULL /*EaBuffer*/,
534 0 /*EaLength*/);
535 if (NT_SUCCESS(rcNt))
536 {
537 if (pfObjDir)
538 *pfObjDir = false;
539 *phHandle = hFile;
540 rc = VINF_SUCCESS;
541 }
542#ifdef IPRT_WITH_NT_PATH_PASSTHRU
543 else if ( pfObjDir
544 && (rcNt == STATUS_OBJECT_NAME_INVALID || rcNt == STATUS_OBJECT_TYPE_MISMATCH)
545 && RTPATH_IS_SLASH(pszPath[0])
546 && RTPATH_IS_SLASH(pszPath[1])
547 && pszPath[2] == '!'
548 && RTPATH_IS_SLASH(pszPath[3]))
549 {
550 /* Strip trailing slash. */
551 if ( NtName.Length > 2
552 && RTPATH_IS_SLASH(NtName.Buffer[(NtName.Length / 2) - 1]))
553 NtName.Length -= 2;
554
555 /* Rought conversion of the access flags. */
556 ULONG fObjDesiredAccess = 0;
557 if (fDesiredAccess & (GENERIC_ALL | STANDARD_RIGHTS_ALL))
558 fObjDesiredAccess = DIRECTORY_ALL_ACCESS;
559 else
560 {
561 if (fDesiredAccess & (FILE_GENERIC_WRITE | GENERIC_WRITE | STANDARD_RIGHTS_WRITE))
562 fObjDesiredAccess |= DIRECTORY_CREATE_OBJECT | DIRECTORY_CREATE_OBJECT;
563 if ( (fDesiredAccess & (FILE_LIST_DIRECTORY | FILE_GENERIC_READ | GENERIC_READ | STANDARD_RIGHTS_READ))
564 || !fObjDesiredAccess)
565 fObjDesiredAccess |= DIRECTORY_QUERY | FILE_LIST_DIRECTORY;
566 }
567
568 rcNt = NtOpenDirectoryObject(&hFile, fObjDesiredAccess, &ObjAttr);
569 if (NT_SUCCESS(rcNt))
570 {
571 *pfObjDir = true;
572 *phHandle = hFile;
573 rc = VINF_SUCCESS;
574 }
575 else
576 rc = RTErrConvertFromNtStatus(rcNt);
577 }
578#endif
579 else
580 rc = RTErrConvertFromNtStatus(rcNt);
581 rtNtPathFreeNative(&NtName, &hRootDir);
582 }
583 return rc;
584}
585
586
587/**
588 * Closes an handled open by rtNtPathOpen.
589 *
590 * @returns IPRT status code
591 * @param hHandle The handle value.
592 */
593RTDECL(int) RTNtPathClose(HANDLE hHandle)
594{
595 NTSTATUS rcNt = NtClose(hHandle);
596 if (NT_SUCCESS(rcNt))
597 return VINF_SUCCESS;
598 return RTErrConvertFromNtStatus(rcNt);
599}
600
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