1
/* $Id: symlink-win.cpp 33437 2010-10-25 16:28:14Z vboxsync $ */
3
* IPRT - Symbolic Links, Windows.
7
* Copyright (C) 2010 Oracle Corporation
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.
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.
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.
28
/*******************************************************************************
30
*******************************************************************************/
31
#define LOG_GROUP RTLOGGROUP_SYMLINK
33
#include <iprt/symlink.h>
35
#include <iprt/assert.h>
38
#include <iprt/path.h>
40
#include <iprt/string.h>
41
#include "internal/path.h"
46
/*******************************************************************************
47
* Structures and Typedefs *
48
*******************************************************************************/
49
typedef struct MY_REPARSE_DATA_BUFFER
52
#define MY_IO_REPARSE_TAG_SYMLINK 0xa000000c
53
#define MY_IO_REPARSE_TAG_MOUNT_POINT 0xa0000003
55
USHORT ReparseDataLength;
61
USHORT SubstituteNameOffset;
62
USHORT SubstituteNameLength;
63
USHORT PrintNameOffset;
64
USHORT PrintNameLength;
66
#define MY_SYMLINK_FLAG_RELATIVE 1
68
} SymbolicLinkReparseBuffer;
71
USHORT SubstituteNameOffset;
72
USHORT SubstituteNameLength;
73
USHORT PrintNameOffset;
74
USHORT PrintNameLength;
76
} MountPointReparseBuffer;
80
} GenericReparseBuffer;
82
} MY_REPARSE_DATA_BUFFER;
83
#define MY_FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
86
RTDECL(bool) RTSymlinkExists(const char *pszSymlink)
90
int rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
92
fRc = RTFS_IS_SYMLINK(ObjInfo.Attr.fMode);
94
LogFlow(("RTSymlinkExists(%p={%s}): returns %RTbool\n", pszSymlink, pszSymlink, fRc));
99
RTDECL(bool) RTSymlinkIsDangling(const char *pszSymlink)
103
int rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
106
fRc = RTFS_IS_SYMLINK(ObjInfo.Attr.fMode);
109
rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
110
fRc = !RT_SUCCESS_NP(rc);
114
LogFlow(("RTSymlinkIsDangling(%p={%s}): returns %RTbool\n", pszSymlink, pszSymlink, fRc));
119
RTDECL(int) RTSymlinkCreate(const char *pszSymlink, const char *pszTarget, RTSYMLINKTYPE enmType)
122
* Validate the input.
124
AssertReturn(enmType > RTSYMLINKTYPE_INVALID && enmType < RTSYMLINKTYPE_END, VERR_INVALID_PARAMETER);
125
AssertPtrReturn(pszSymlink, VERR_INVALID_POINTER);
126
AssertPtrReturn(pszTarget, VERR_INVALID_POINTER);
131
typedef BOOLEAN (WINAPI *PFNCREATESYMBOLICLINKW)(LPCWSTR, LPCWSTR, DWORD);
132
static PFNCREATESYMBOLICLINKW s_pfnCreateSymbolicLinkW = NULL;
133
static bool s_fTried = FALSE;
136
HMODULE hmod = LoadLibrary("KERNEL32.DLL");
139
PFNCREATESYMBOLICLINKW pfn = (PFNCREATESYMBOLICLINKW)GetProcAddress(hmod, "CreateSymbolicLinkW");
141
s_pfnCreateSymbolicLinkW = pfn;
145
if (!s_pfnCreateSymbolicLinkW)
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;
155
PRTUTF16 pwszNativeSymlink;
156
int rc = RTStrToUtf16(pszSymlink, &pwszNativeSymlink);
159
PRTUTF16 pwszNativeTarget;
160
rc = RTStrToUtf16(pszTarget, &pwszNativeTarget);
164
* Massage the target path, determin the link type.
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]))
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]))
178
Assert(offFromEnd < cwcNativeTarget);
179
pwszNativeTarget[cwcNativeTarget - offFromEnd] = 0;
185
if (enmType == RTSYMLINKTYPE_UNKNOWN)
187
if ( cchTarget > cchVolSpecTarget
188
&& RTPATH_IS_SLASH(pszTarget[cchTarget - 1]))
189
enmType = RTSYMLINKTYPE_DIR;
190
else if (cchVolSpecTarget)
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;
200
/** @todo Join the symlink directory with the target and
201
* look up the attributes on that. -lazy bird. */
208
if (s_pfnCreateSymbolicLinkW(pwszNativeSymlink, pwszNativeTarget, enmType == RTSYMLINKTYPE_DIR))
211
rc = RTErrConvertFromWin32(GetLastError());
213
RTUtf16Free(pwszNativeTarget);
215
RTUtf16Free(pwszNativeSymlink);
218
LogFlow(("RTSymlinkCreate(%p={%s}, %p={%s}, %d): returns %Rrc\n", pszSymlink, pszSymlink, pszTarget, pszTarget, enmType, rc));
223
RTDECL(int) RTSymlinkDelete(const char *pszSymlink)
228
PRTUTF16 pwszNativeSymlink;
229
int rc = RTStrToUtf16(pszSymlink, &pwszNativeSymlink);
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...
238
DWORD dwAttr = GetFileAttributesW(pwszNativeSymlink);
239
if (dwAttr != INVALID_FILE_ATTRIBUTES)
241
if (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT)
244
if (dwAttr & FILE_ATTRIBUTE_DIRECTORY)
245
fRc = RemoveDirectoryW(pwszNativeSymlink);
247
fRc = DeleteFileW(pwszNativeSymlink);
251
rc = RTErrConvertFromWin32(GetLastError());
254
rc = VERR_NOT_SYMLINK;
257
rc = RTErrConvertFromWin32(GetLastError());
258
RTUtf16Free(pwszNativeSymlink);
261
LogFlow(("RTSymlinkDelete(%p={%s}): returns %Rrc\n", pszSymlink, pszSymlink, rc));
266
RTDECL(int) RTSymlinkRead(const char *pszSymlink, char *pszTarget, size_t cbTarget)
269
int rc = RTSymlinkReadA(pszSymlink, &pszMyTarget);
272
rc = RTStrCopy(pszTarget, cbTarget, pszMyTarget);
273
RTStrFree(pszMyTarget);
275
LogFlow(("RTSymlinkRead(%p={%s}): returns %Rrc\n", pszSymlink, pszSymlink, rc));
280
RTDECL(int) RTSymlinkReadA(const char *pszSymlink, char **ppszTarget)
282
AssertPtr(ppszTarget);
283
PRTUTF16 pwszNativeSymlink;
284
int rc = RTStrToUtf16(pszSymlink, &pwszNativeSymlink);
287
HANDLE hSymlink = CreateFileW(pwszNativeSymlink,
289
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
292
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
294
if (hSymlink != INVALID_HANDLE_VALUE)
296
DWORD cbReturned = 0;
299
MY_REPARSE_DATA_BUFFER Buf;
300
uint8_t abBuf[16*_1K + sizeof(WCHAR)];
302
if (DeviceIoControl(hSymlink,
303
MY_FSCTL_GET_REPARSE_POINT,
307
sizeof(u) - sizeof(WCHAR),
309
NULL /*pOverlapped*/))
311
if (u.Buf.ReparseTag == MY_IO_REPARSE_TAG_SYMLINK)
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
324
rc = RTUtf16ToUtf8(pwszTarget, ppszTarget);
327
rc = VERR_NOT_SYMLINK;
330
rc = RTErrConvertFromWin32(GetLastError());
331
CloseHandle(hSymlink);
334
rc = RTErrConvertFromWin32(GetLastError());
335
RTUtf16Free(pwszNativeSymlink);
339
LogFlow(("RTSymlinkReadA(%p={%s},%p): returns %Rrc *ppszTarget=%p:{%s}\n", pszSymlink, pszSymlink, ppszTarget, rc, *ppszTarget, *ppszTarget));
341
LogFlow(("RTSymlinkReadA(%p={%s},%p): returns %Rrc\n", pszSymlink, pszSymlink, ppszTarget, rc));