VirtualBox

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

Last change on this file since 34801 was 33437, checked in by vboxsync, 14 years ago

iprt: Made tstRTSymlink work on Windows.

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