VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/symlink-win.cpp@ 106061

Last change on this file since 106061 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: 13.1 KB
Line 
1/* $Id: symlink-win.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - Symbolic Links, Windows.
4 */
5
6/*
7 * Copyright (C) 2010-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_SYMLINK
42#include <iprt/win/windows.h>
43
44#include <iprt/symlink.h>
45#include "internal-r3-win.h"
46
47#include <iprt/assert.h>
48#include <iprt/err.h>
49#include <iprt/log.h>
50#include <iprt/path.h>
51#include <iprt/mem.h>
52#include <iprt/string.h>
53#include <iprt/utf16.h>
54#include "internal/path.h"
55
56
57
58/*********************************************************************************************************************************
59* Structures and Typedefs *
60*********************************************************************************************************************************/
61typedef struct MY_REPARSE_DATA_BUFFER
62{
63 ULONG ReparseTag;
64#define MY_IO_REPARSE_TAG_SYMLINK 0xa000000c
65#define MY_IO_REPARSE_TAG_MOUNT_POINT 0xa0000003
66
67 USHORT ReparseDataLength;
68 USHORT Reserved;
69 union
70 {
71 struct
72 {
73 USHORT SubstituteNameOffset;
74 USHORT SubstituteNameLength;
75 USHORT PrintNameOffset;
76 USHORT PrintNameLength;
77 ULONG Flags;
78#define MY_SYMLINK_FLAG_RELATIVE 1
79 WCHAR PathBuffer[1];
80 } SymbolicLinkReparseBuffer;
81 struct
82 {
83 USHORT SubstituteNameOffset;
84 USHORT SubstituteNameLength;
85 USHORT PrintNameOffset;
86 USHORT PrintNameLength;
87 WCHAR PathBuffer[1];
88 } MountPointReparseBuffer;
89 struct
90 {
91 UCHAR DataBuffer[1];
92 } GenericReparseBuffer;
93 };
94} MY_REPARSE_DATA_BUFFER;
95#define MY_FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
96
97
98RTDECL(bool) RTSymlinkExists(const char *pszSymlink)
99{
100 bool fRc = false;
101 RTFSOBJINFO ObjInfo;
102 int rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
103 if (RT_SUCCESS(rc))
104 fRc = RTFS_IS_SYMLINK(ObjInfo.Attr.fMode);
105
106 LogFlow(("RTSymlinkExists(%p={%s}): returns %RTbool\n", pszSymlink, pszSymlink, fRc));
107 return fRc;
108}
109
110
111RTDECL(bool) RTSymlinkIsDangling(const char *pszSymlink)
112{
113 bool fRc = false;
114 RTFSOBJINFO ObjInfo;
115 int rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
116 if (RT_SUCCESS(rc))
117 {
118 fRc = RTFS_IS_SYMLINK(ObjInfo.Attr.fMode);
119 if (fRc)
120 {
121 rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
122 fRc = !RT_SUCCESS_NP(rc);
123 }
124 }
125
126 LogFlow(("RTSymlinkIsDangling(%p={%s}): returns %RTbool\n", pszSymlink, pszSymlink, fRc));
127 return fRc;
128}
129
130
131RTDECL(int) RTSymlinkCreate(const char *pszSymlink, const char *pszTarget, RTSYMLINKTYPE enmType, uint32_t fCreate)
132{
133 /*
134 * Validate the input.
135 */
136 AssertReturn(enmType > RTSYMLINKTYPE_INVALID && enmType < RTSYMLINKTYPE_END, VERR_INVALID_PARAMETER);
137 AssertPtrReturn(pszSymlink, VERR_INVALID_POINTER);
138 AssertPtrReturn(pszTarget, VERR_INVALID_POINTER);
139 RT_NOREF_PV(fCreate);
140
141 /*
142 * Resolve the API.
143 */
144 typedef BOOLEAN (WINAPI *PFNCREATESYMBOLICLINKW)(LPCWSTR, LPCWSTR, DWORD);
145 static PFNCREATESYMBOLICLINKW s_pfnCreateSymbolicLinkW = NULL;
146 static bool s_fTried = FALSE;
147 if (!s_fTried)
148 {
149 PFNCREATESYMBOLICLINKW pfn = (PFNCREATESYMBOLICLINKW)GetProcAddress(g_hModKernel32, "CreateSymbolicLinkW");
150 if (pfn)
151 s_pfnCreateSymbolicLinkW = pfn;
152 s_fTried = true;
153 }
154 if (!s_pfnCreateSymbolicLinkW)
155 {
156 LogFlow(("RTSymlinkCreate(%p={%s}, %p={%s}, %d, %#x): returns VERR_NOT_SUPPORTED - Windows API not found\n",
157 pszSymlink, pszSymlink, pszTarget, pszTarget, enmType, fCreate));
158 return VERR_NOT_SUPPORTED;
159 }
160
161 /*
162 * Convert the paths.
163 */
164 PRTUTF16 pwszNativeSymlink;
165 int rc = RTPathWinFromUtf8(&pwszNativeSymlink, pszSymlink, 0 /*fFlags*/);
166 if (RT_SUCCESS(rc))
167 {
168 PRTUTF16 pwszNativeTarget;
169 rc = RTPathWinFromUtf8(&pwszNativeTarget, pszTarget, 0 /*fFlags*/);
170 if (RT_SUCCESS(rc))
171 {
172 /* The link target path must use backslashes to work reliably. */
173 RTUTF16 wc;
174 PRTUTF16 pwsz = pwszNativeTarget;
175 while ((wc = *pwsz) != '\0')
176 {
177 if (wc == '/')
178 *pwsz = '\\';
179 pwsz++;
180 }
181
182 /*
183 * Massage the target path, determin the link type.
184 */
185 size_t cchTarget = strlen(pszTarget);
186 size_t cchVolSpecTarget = rtPathVolumeSpecLen(pszTarget);
187#if 0 /* looks like this isn't needed after all. That makes everything much simper :-) */
188 if ( cchTarget > RT_MIN(cchVolSpecTarget, 1)
189 && RTPATH_IS_SLASH(pszTarget[cchTarget - 1]))
190 {
191 size_t cwcNativeTarget = RTUtf16Len(pwszNativeTarget);
192 size_t offFromEnd = 1;
193 while ( offFromEnd < cchTarget
194 && cchTarget - offFromEnd >= cchVolSpecTarget
195 && RTPATH_IS_SLASH(pszTarget[cchTarget - offFromEnd]))
196 {
197 Assert(offFromEnd < cwcNativeTarget);
198 pwszNativeTarget[cwcNativeTarget - offFromEnd] = 0;
199 offFromEnd++;
200 }
201 }
202#endif
203
204 if (enmType == RTSYMLINKTYPE_UNKNOWN)
205 {
206 if ( cchTarget > cchVolSpecTarget
207 && RTPATH_IS_SLASH(pszTarget[cchTarget - 1]))
208 enmType = RTSYMLINKTYPE_DIR;
209 else if (cchVolSpecTarget)
210 {
211 /** @todo this is subject to sharing violations. */
212 DWORD dwAttr = GetFileAttributesW(pwszNativeTarget);
213 if ( dwAttr != INVALID_FILE_ATTRIBUTES
214 && (dwAttr & FILE_ATTRIBUTE_DIRECTORY))
215 enmType = RTSYMLINKTYPE_DIR;
216 }
217 else
218 {
219 /** @todo Join the symlink directory with the target and
220 * look up the attributes on that. -lazy bird. */
221 }
222 }
223
224 /*
225 * Create the link.
226 */
227 if (s_pfnCreateSymbolicLinkW(pwszNativeSymlink, pwszNativeTarget, enmType == RTSYMLINKTYPE_DIR))
228 rc = VINF_SUCCESS;
229 else
230 rc = RTErrConvertFromWin32(GetLastError());
231
232 RTPathWinFree(pwszNativeTarget);
233 }
234 RTPathWinFree(pwszNativeSymlink);
235 }
236
237 LogFlow(("RTSymlinkCreate(%p={%s}, %p={%s}, %d, %#x): returns %Rrc\n", pszSymlink, pszSymlink, pszTarget, pszTarget, enmType, fCreate, rc));
238 return rc;
239}
240
241
242RTDECL(int) RTSymlinkDelete(const char *pszSymlink, uint32_t fDelete)
243{
244 RT_NOREF_PV(fDelete);
245
246 /*
247 * Convert the path.
248 */
249 PRTUTF16 pwszNativeSymlink;
250 int rc = RTPathWinFromUtf8(&pwszNativeSymlink, pszSymlink, 0 /*fFlags*/);
251 if (RT_SUCCESS(rc))
252 {
253 /*
254 * We have to use different APIs depending on whether this is a
255 * directory or file link. This means we're subject to one more race
256 * than on posix at the moment. We could probably avoid this though,
257 * if we wanted to go talk with the native API layer below Win32...
258 */
259 DWORD dwAttr = GetFileAttributesW(pwszNativeSymlink);
260 if (dwAttr != INVALID_FILE_ATTRIBUTES)
261 {
262 if (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT)
263 {
264 BOOL fRc;
265 if (dwAttr & FILE_ATTRIBUTE_DIRECTORY)
266 fRc = RemoveDirectoryW(pwszNativeSymlink);
267 else
268 fRc = DeleteFileW(pwszNativeSymlink);
269 if (fRc)
270 rc = VINF_SUCCESS;
271 else
272 rc = RTErrConvertFromWin32(GetLastError());
273 }
274 else
275 rc = VERR_NOT_SYMLINK;
276 }
277 else
278 rc = RTErrConvertFromWin32(GetLastError());
279 RTPathWinFree(pwszNativeSymlink);
280 }
281
282 LogFlow(("RTSymlinkDelete(%p={%s}, %#x): returns %Rrc\n", pszSymlink, pszSymlink, fDelete, rc));
283 return rc;
284}
285
286
287RTDECL(int) RTSymlinkRead(const char *pszSymlink, char *pszTarget, size_t cbTarget, uint32_t fRead)
288{
289 RT_NOREF_PV(fRead);
290
291 char *pszMyTarget;
292 int rc = RTSymlinkReadA(pszSymlink, &pszMyTarget);
293 if (RT_SUCCESS(rc))
294 {
295 rc = RTStrCopy(pszTarget, cbTarget, pszMyTarget);
296 RTStrFree(pszMyTarget);
297 }
298 LogFlow(("RTSymlinkRead(%p={%s}): returns %Rrc\n", pszSymlink, pszSymlink, rc));
299 return rc;
300}
301
302
303RTDECL(int) RTSymlinkReadA(const char *pszSymlink, char **ppszTarget)
304{
305 AssertPtr(ppszTarget);
306 PRTUTF16 pwszNativeSymlink;
307 int rc = RTPathWinFromUtf8(&pwszNativeSymlink, pszSymlink, 0 /*fFlags*/);
308 if (RT_SUCCESS(rc))
309 {
310 HANDLE hSymlink = CreateFileW(pwszNativeSymlink,
311 GENERIC_READ,
312 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
313 NULL,
314 OPEN_EXISTING,
315 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
316 NULL);
317 if (hSymlink != INVALID_HANDLE_VALUE)
318 {
319 DWORD cbReturned = 0;
320 union
321 {
322 MY_REPARSE_DATA_BUFFER Buf;
323 uint8_t abBuf[16*_1K + sizeof(WCHAR)];
324 } u;
325 if (DeviceIoControl(hSymlink,
326 MY_FSCTL_GET_REPARSE_POINT,
327 NULL /*pInBuffer */,
328 0 /*cbInBuffer */,
329 &u.Buf,
330 sizeof(u) - sizeof(WCHAR),
331 &cbReturned,
332 NULL /*pOverlapped*/))
333 {
334 if (u.Buf.ReparseTag == MY_IO_REPARSE_TAG_SYMLINK)
335 {
336 PWCHAR pwszTarget = &u.Buf.SymbolicLinkReparseBuffer.PathBuffer[0];
337 pwszTarget += u.Buf.SymbolicLinkReparseBuffer.SubstituteNameOffset / 2;
338 pwszTarget[u.Buf.SymbolicLinkReparseBuffer.SubstituteNameLength / 2] = 0;
339 if ( !(u.Buf.SymbolicLinkReparseBuffer.Flags & MY_SYMLINK_FLAG_RELATIVE)
340 && pwszTarget[0] == '\\'
341 && pwszTarget[1] == '?'
342 && pwszTarget[2] == '?'
343 && pwszTarget[3] == '\\'
344 && pwszTarget[4] != 0
345 )
346 pwszTarget += 4;
347 rc = RTUtf16ToUtf8(pwszTarget, ppszTarget);
348 }
349 else
350 rc = VERR_NOT_SYMLINK;
351 }
352 else
353 rc = RTErrConvertFromWin32(GetLastError());
354 CloseHandle(hSymlink);
355 }
356 else
357 rc = RTErrConvertFromWin32(GetLastError());
358 RTPathWinFree(pwszNativeSymlink);
359 }
360
361 if (RT_SUCCESS(rc))
362 LogFlow(("RTSymlinkReadA(%p={%s},%p): returns %Rrc *ppszTarget=%p:{%s}\n", pszSymlink, pszSymlink, ppszTarget, rc, *ppszTarget, *ppszTarget));
363 else
364 LogFlow(("RTSymlinkReadA(%p={%s},%p): returns %Rrc\n", pszSymlink, pszSymlink, ppszTarget, rc));
365 return rc;
366}
367
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