VirtualBox

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

Last change on this file since 76357 was 74460, checked in by vboxsync, 6 years ago

IPRT: Implemented long filename support for windows (except for LoadLibrary). bugref:9248

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