VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/pathint-win.cpp@ 95084

Last change on this file since 95084 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 7.3 KB
Line 
1/* $Id: pathint-win.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * IPRT - Windows, Internal Path stuff.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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 <iprt/path.h>
33
34#include <iprt/assert.h>
35#include <iprt/ctype.h>
36#include <iprt/errcore.h>
37#include <iprt/string.h>
38#include <iprt/utf16.h>
39
40#include <iprt/nt/nt-and-windows.h>
41
42
43/*********************************************************************************************************************************
44* Defined Constants And Macros *
45*********************************************************************************************************************************/
46/** The max number of non-null characters we pass to an Win32 API.
47 * You would think that MAX_PATH gives this length, however CreateDirectoryW was
48 * found to fail on Windows 10 (1803++) if given a perfectly formed path argument
49 * of 248 or more characters. Same when going thru UNC.
50 *
51 * So, to be conservative, we put the max number of characters in a non-\\?\
52 * path to 243, not counting the terminator.
53 */
54#define ACTUAL_MAX_PATH 243
55
56
57DECL_NO_INLINE(static, bool) rtPathWinTryConvertToAbs(PRTUTF16 *ppwszPath)
58{
59 RTUTF16 wszFullPath[MAX_PATH + 1];
60 DWORD cwcFull = GetFullPathNameW(*ppwszPath, MAX_PATH + 1, wszFullPath, NULL);
61 if (cwcFull <= ACTUAL_MAX_PATH)
62 {
63 RTUtf16Free(*ppwszPath);
64 PRTUTF16 const pwszCopy = RTUtf16Dup(wszFullPath);
65 *ppwszPath = pwszCopy;
66 if (pwszCopy)
67 return true;
68 }
69 return false;
70}
71
72
73RTDECL(int) RTPathWinFromUtf8(PRTUTF16 *ppwszPath, const char *pszPath, uint32_t fFlags)
74{
75 Assert(fFlags == 0);
76 RT_NOREF(fFlags);
77
78 /*
79 * Do a straight conversion first.
80 */
81 *ppwszPath = NULL;
82 size_t cwcResult = 0;
83 int rc = RTStrToUtf16Ex(pszPath, RTSTR_MAX, ppwszPath, 0, &cwcResult);
84 if (RT_SUCCESS(rc))
85 {
86 /*
87 * Check the resulting length. This is straight forward for absolute
88 * paths, but gets complicated for relative ones.
89 */
90 if (cwcResult <= ACTUAL_MAX_PATH)
91 {
92 if (RT_C_IS_ALPHA(pszPath[0]) && pszPath[1] == ':')
93 {
94 if (RTPATH_IS_SLASH(pszPath[2]))
95 return VINF_SUCCESS;
96
97 /* Drive relative path. Found no simple way of getting the current
98 path of a drive, so we try convert it to an absolute path and see
99 how that works out. It is what the API we're calling will have to
100 do anyway, so this should perform just as well. */
101 if (rtPathWinTryConvertToAbs(ppwszPath))
102 return VINF_SUCCESS;
103 }
104 else if (RTPATH_IS_SLASH(pszPath[0]))
105 {
106 if ( RTPATH_IS_SLASH(pszPath[1])
107 && !RTPATH_IS_SLASH(pszPath[2])
108 && pszPath[2] != '\0')
109 {
110 /* Passthru prefix '\\?\' is fine. */
111 if ( pszPath[2] == '?'
112 && !RTPATH_IS_SLASH(pszPath[3]))
113 return VINF_SUCCESS;
114
115 /* UNC requires a longer prefix ('\??\UNC\' instead of '\??\'), so
116 subtract 3 chars from the max limit to be on the safe side. */
117 if (cwcResult <= ACTUAL_MAX_PATH - 3)
118 return VINF_SUCCESS;
119 }
120 else
121 {
122 /* Drive relative. Win32 will prepend a two letter drive specification. */
123 if (cwcResult <= ACTUAL_MAX_PATH - 2)
124 return VINF_SUCCESS;
125 }
126 }
127 else
128 {
129 /* Relative to CWD. We can use the API to get it's current length.
130 Any race conditions here is entirely the caller's problem. */
131 size_t cwcCwd = GetCurrentDirectoryW(0, NULL);
132 if (cwcCwd + cwcResult <= ACTUAL_MAX_PATH - 1)
133 return VINF_SUCCESS;
134 }
135 }
136 /*
137 * We're good if the caller already supplied the passthru/length prefix: '\\?\'
138 */
139 else if ( pszPath[1] == '?'
140 && RTPATH_IS_SLASH(pszPath[3])
141 && RTPATH_IS_SLASH(pszPath[1])
142 && RTPATH_IS_SLASH(pszPath[0]))
143 return VINF_SUCCESS;
144
145 /*
146 * Long path requiring \\?\ prefixing.
147 *
148 * We piggy back on the NT conversion here and ASSUME RTUtf16Free is the right
149 * way to free the result.
150 */
151 RTUtf16Free(*ppwszPath);
152 *ppwszPath = NULL;
153
154 struct _UNICODE_STRING NtName = { 0, 0, NULL };
155 HANDLE hRootDir = NULL;
156 rc = RTNtPathFromWinUtf8(&NtName, &hRootDir, pszPath);
157 if (RT_SUCCESS(rc))
158 {
159 /* No root dir handle. */
160 if (hRootDir == NULL)
161 {
162 /* Convert the NT '\??\' prefix to a win32 passthru prefix '\\?\' */
163 if ( NtName.Buffer[0] == '\\'
164 && NtName.Buffer[1] == '?'
165 && NtName.Buffer[2] == '?'
166 && NtName.Buffer[3] == '\\')
167 {
168 NtName.Buffer[1] = '\\';
169
170 /* Zero termination paranoia. */
171 if (NtName.Buffer[NtName.Length / sizeof(RTUTF16)] == '\0')
172 {
173 *ppwszPath = NtName.Buffer;
174 return VINF_SUCCESS;
175 }
176 AssertMsgFailed(("Length=%u %.*ls\n", NtName.Length, NtName.Length / sizeof(RTUTF16), NtName.Buffer));
177 }
178 else
179 AssertMsgFailed(("%ls\n", NtName.Buffer));
180 }
181 else
182 AssertMsgFailed(("%s\n", pszPath));
183 RTNtPathFree(&NtName, &hRootDir);
184 }
185 }
186 return rc;
187}
188
189
190RTDECL(void) RTPathWinFree(PRTUTF16 pwszPath)
191{
192 RTUtf16Free(pwszPath);
193}
194
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