VirtualBox

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

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

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use