Say, I created a directory symbolic link using the mklink command as such:
mklink /d "test dir link1" "dest dir"
How can I copy it as a link to the target directory? When I try to use the CopyFileEx API and the COPY_FILE_COPY_SYMLINK flag as such:
::CopyFileEx(L"D:\\Path to source\\test dir link1",
L"D:\\Path to destination\\test dir link1 copy",
NULL, NULL, NULL,
COPY_FILE_COPY_SYMLINK);
it returns error code ERROR_ACCESS_DENIED
.
PS. I tried running my process elevated (just in unlikely scenario that I need to run elevated) and it still gave me the same error code.
You're asking to copy a directory using the CopyFileEx
API. The fact that it's a symbolic link doesn't get resolved until after the check that it's a directory, which means you can't use this API to copy a directory symlink.
Directory symlinks are treated slightly differently from file symlinks - when you create them you have to pass in an extra parameter to the CreateSymbolicLink
API.
There is a subtle hint as to this behavior in the API documentation, where it states:
To remove a symbolic link, delete the file (using
DeleteFile
or similar APIs) or remove the directory (usingRemoveDirectory
or similar APIs) depending on what type of symbolic link is used.
This strongly indicates that APIs that work on files will not work on directory symlinks.
Now as to how to copy it, I strongly expect you'll have to create the symbolic link again, the process for this is to read the symlink target and then create the link a second time in the destination; something akin to (no error handling at all, transforms relative links to absolute links):
HANDLE h = CreateFile(srcFile, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
TCHAR outbuffer[2048];
DWORD written = GetFinalPathNameByHandle(h, outbuffer, 2048, 0);
CreateSymbolicLink(targetFile, outbuffer, SYMBOLIC_LINK_FLAG_DIRECTORY);
CloseHandle(h);
Now to create the symlink using the underlying reparse point data things get a little more complicated. If you don't have ntifs.h
then you need to define the Microsoft reparse point data structure (copied from the MSDN page for REPARSE_DATA_STRUCTURE):
typedef struct _REPARSE_DATA_BUFFER {
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
union {
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct {
UCHAR DataBuffer[1];
} GenericReparseBuffer;
};
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
We change the routine:
HANDLE h = CreateFile(srcFile, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
if (h != INVALID_HANDLE_VALUE) {
char tmpBuffer[32 * 1024];
REPARSE_DATA_BUFFER *repBuffer = reinterpret_cast<REPARSE_DATA_BUFFER *>(tmpBuffer);
DWORD retBytes;
if (DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, 0, 0, (void *)tmpBuffer,
sizeof tmpBuffer, &retBytes, 0)) {
if (repBuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
wchar_t dest[2048];
memcpy(dest, repBuffer->SymbolicLinkReparseBuffer.PathBuffer +
repBuffer->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t),
repBuffer->SymbolicLinkReparseBuffer.PrintNameLength);
dest[repBuffer->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t)] = 0;
if (!CreateSymbolicLink(targetFile, dest, SYMBOLIC_LINK_FLAG_DIRECTORY)) {
// Error Handling
}
}
}
CloseHandle(h);
} else {
// Open Error Handling
}
It's significantly more complicated, but it preserves the symbolic link in it's underlying form, i.e. if it's a relative link, then the created link will also be a relative link. In addition if the target does not exist the link still gets created.
This still does not remove the need to be either (a) an administrator, or (b) edited the group policy to permit non-admin users to create symbolic links.