VirtualBox

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

Last change on this file since 52163 was 49043, checked in by vboxsync, 11 years ago

symlink-win.cpp: Fixed bug in RTSymlinkCreate on windows, seems windows doesn't like unix slashes in symlink target paths.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.3 KB
Line 
1/* $Id: symlink-win.cpp 49043 2013-10-11 00:58:02Z vboxsync $ */
2/** @file
3 * IPRT - Symbolic Links, Windows.
4 */
5
6/*
7 * Copyright (C) 2010-2011 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 <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
129 /*
130 * Resolve the API.
131 */
132 typedef BOOLEAN (WINAPI *PFNCREATESYMBOLICLINKW)(LPCWSTR, LPCWSTR, DWORD);
133 static PFNCREATESYMBOLICLINKW s_pfnCreateSymbolicLinkW = NULL;
134 static bool s_fTried = FALSE;
135 if (!s_fTried)
136 {
137 PFNCREATESYMBOLICLINKW pfn = (PFNCREATESYMBOLICLINKW)GetProcAddress(g_hModKernel32, "CreateSymbolicLinkW");
138 if (pfn)
139 s_pfnCreateSymbolicLinkW = pfn;
140 s_fTried = true;
141 }
142 if (!s_pfnCreateSymbolicLinkW)
143 {
144 LogFlow(("RTSymlinkCreate(%p={%s}, %p={%s}, %d, %#x): returns VERR_NOT_SUPPORTED - Windows API not found\n",
145 pszSymlink, pszSymlink, pszTarget, pszTarget, enmType, fCreate));
146 return VERR_NOT_SUPPORTED;
147 }
148
149 /*
150 * Convert the paths.
151 */
152 PRTUTF16 pwszNativeSymlink;
153 int rc = RTStrToUtf16(pszSymlink, &pwszNativeSymlink);
154 if (RT_SUCCESS(rc))
155 {
156 PRTUTF16 pwszNativeTarget;
157 rc = RTStrToUtf16(pszTarget, &pwszNativeTarget);
158 if (RT_SUCCESS(rc))
159 {
160 /* The link target path must use backslashes to work reliably. */
161 RTUTF16 wc;
162 PRTUTF16 pwsz = pwszNativeTarget;
163 while ((wc = *pwsz) != '\0')
164 {
165 if (wc == '/')
166 *pwsz = '\\';
167 pwsz++;
168 }
169
170 /*
171 * Massage the target path, determin the link type.
172 */
173 size_t cchTarget = strlen(pszTarget);
174 size_t cchVolSpecTarget = rtPathVolumeSpecLen(pszTarget);
175#if 0 /* looks like this isn't needed after all. That makes everything much simper :-) */
176 if ( cchTarget > RT_MIN(cchVolSpecTarget, 1)
177 && RTPATH_IS_SLASH(pszTarget[cchTarget - 1]))
178 {
179 size_t cwcNativeTarget = RTUtf16Len(pwszNativeTarget);
180 size_t offFromEnd = 1;
181 while ( offFromEnd < cchTarget
182 && cchTarget - offFromEnd >= cchVolSpecTarget
183 && RTPATH_IS_SLASH(pszTarget[cchTarget - offFromEnd]))
184 {
185 Assert(offFromEnd < cwcNativeTarget);
186 pwszNativeTarget[cwcNativeTarget - offFromEnd] = 0;
187 offFromEnd++;
188 }
189 }
190#endif
191
192 if (enmType == RTSYMLINKTYPE_UNKNOWN)
193 {
194 if ( cchTarget > cchVolSpecTarget
195 && RTPATH_IS_SLASH(pszTarget[cchTarget - 1]))
196 enmType = RTSYMLINKTYPE_DIR;
197 else if (cchVolSpecTarget)
198 {
199 /** @todo this is subject to sharing violations. */
200 DWORD dwAttr = GetFileAttributesW(pwszNativeTarget);
201 if ( dwAttr != INVALID_FILE_ATTRIBUTES
202 && (dwAttr & FILE_ATTRIBUTE_DIRECTORY))
203 enmType = RTSYMLINKTYPE_DIR;
204 }
205 else
206 {
207 /** @todo Join the symlink directory with the target and
208 * look up the attributes on that. -lazy bird. */
209 }
210 }
211
212 /*
213 * Create the link.
214 */
215 if (s_pfnCreateSymbolicLinkW(pwszNativeSymlink, pwszNativeTarget, enmType == RTSYMLINKTYPE_DIR))
216 rc = VINF_SUCCESS;
217 else
218 rc = RTErrConvertFromWin32(GetLastError());
219
220 RTUtf16Free(pwszNativeTarget);
221 }
222 RTUtf16Free(pwszNativeSymlink);
223 }
224
225 LogFlow(("RTSymlinkCreate(%p={%s}, %p={%s}, %d, %#x): returns %Rrc\n", pszSymlink, pszSymlink, pszTarget, pszTarget, enmType, fCreate, rc));
226 return rc;
227}
228
229
230RTDECL(int) RTSymlinkDelete(const char *pszSymlink, uint32_t fDelete)
231{
232 /*
233 * Convert the path.
234 */
235 PRTUTF16 pwszNativeSymlink;
236 int rc = RTStrToUtf16(pszSymlink, &pwszNativeSymlink);
237 if (RT_SUCCESS(rc))
238 {
239 /*
240 * We have to use different APIs depending on whether this is a
241 * directory or file link. This means we're subject to one more race
242 * than on posix at the moment. We could probably avoid this though,
243 * if we wanted to go talk with the native API layer below Win32...
244 */
245 DWORD dwAttr = GetFileAttributesW(pwszNativeSymlink);
246 if (dwAttr != INVALID_FILE_ATTRIBUTES)
247 {
248 if (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT)
249 {
250 BOOL fRc;
251 if (dwAttr & FILE_ATTRIBUTE_DIRECTORY)
252 fRc = RemoveDirectoryW(pwszNativeSymlink);
253 else
254 fRc = DeleteFileW(pwszNativeSymlink);
255 if (fRc)
256 rc = VINF_SUCCESS;
257 else
258 rc = RTErrConvertFromWin32(GetLastError());
259 }
260 else
261 rc = VERR_NOT_SYMLINK;
262 }
263 else
264 rc = RTErrConvertFromWin32(GetLastError());
265 RTUtf16Free(pwszNativeSymlink);
266 }
267
268 LogFlow(("RTSymlinkDelete(%p={%s}, %#x): returns %Rrc\n", pszSymlink, pszSymlink, fDelete, rc));
269 return rc;
270}
271
272
273RTDECL(int) RTSymlinkRead(const char *pszSymlink, char *pszTarget, size_t cbTarget, uint32_t fRead)
274{
275 char *pszMyTarget;
276 int rc = RTSymlinkReadA(pszSymlink, &pszMyTarget);
277 if (RT_SUCCESS(rc))
278 {
279 rc = RTStrCopy(pszTarget, cbTarget, pszMyTarget);
280 RTStrFree(pszMyTarget);
281 }
282 LogFlow(("RTSymlinkRead(%p={%s}): returns %Rrc\n", pszSymlink, pszSymlink, rc));
283 return rc;
284}
285
286
287RTDECL(int) RTSymlinkReadA(const char *pszSymlink, char **ppszTarget)
288{
289 AssertPtr(ppszTarget);
290 PRTUTF16 pwszNativeSymlink;
291 int rc = RTStrToUtf16(pszSymlink, &pwszNativeSymlink);
292 if (RT_SUCCESS(rc))
293 {
294 HANDLE hSymlink = CreateFileW(pwszNativeSymlink,
295 GENERIC_READ,
296 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
297 NULL,
298 OPEN_EXISTING,
299 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
300 NULL);
301 if (hSymlink != INVALID_HANDLE_VALUE)
302 {
303 DWORD cbReturned = 0;
304 union
305 {
306 MY_REPARSE_DATA_BUFFER Buf;
307 uint8_t abBuf[16*_1K + sizeof(WCHAR)];
308 } u;
309 if (DeviceIoControl(hSymlink,
310 MY_FSCTL_GET_REPARSE_POINT,
311 NULL /*pInBuffer */,
312 0 /*cbInBuffer */,
313 &u.Buf,
314 sizeof(u) - sizeof(WCHAR),
315 &cbReturned,
316 NULL /*pOverlapped*/))
317 {
318 if (u.Buf.ReparseTag == MY_IO_REPARSE_TAG_SYMLINK)
319 {
320 PWCHAR pwszTarget = &u.Buf.SymbolicLinkReparseBuffer.PathBuffer[0];
321 pwszTarget += u.Buf.SymbolicLinkReparseBuffer.SubstituteNameOffset / 2;
322 pwszTarget[u.Buf.SymbolicLinkReparseBuffer.SubstituteNameLength / 2] = 0;
323 if ( !(u.Buf.SymbolicLinkReparseBuffer.Flags & MY_SYMLINK_FLAG_RELATIVE)
324 && pwszTarget[0] == '\\'
325 && pwszTarget[1] == '?'
326 && pwszTarget[2] == '?'
327 && pwszTarget[3] == '\\'
328 && pwszTarget[4] != 0
329 )
330 pwszTarget += 4;
331 rc = RTUtf16ToUtf8(pwszTarget, ppszTarget);
332 }
333 else
334 rc = VERR_NOT_SYMLINK;
335 }
336 else
337 rc = RTErrConvertFromWin32(GetLastError());
338 CloseHandle(hSymlink);
339 }
340 else
341 rc = RTErrConvertFromWin32(GetLastError());
342 RTUtf16Free(pwszNativeSymlink);
343 }
344
345 if (RT_SUCCESS(rc))
346 LogFlow(("RTSymlinkReadA(%p={%s},%p): returns %Rrc *ppszTarget=%p:{%s}\n", pszSymlink, pszSymlink, ppszTarget, rc, *ppszTarget, *ppszTarget));
347 else
348 LogFlow(("RTSymlinkReadA(%p={%s},%p): returns %Rrc\n", pszSymlink, pszSymlink, ppszTarget, rc));
349 return rc;
350}
351
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