VirtualBox

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

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