reactos/base/shell/cmd/mklink.c

225 lines
7.5 KiB
C

/*
* MKLINK.C - mklink internal command.
*/
#include "precomp.h"
#ifdef INCLUDE_CMD_MKLINK
/* There is no API for creating junctions, so we must do it the hard way */
static BOOL CreateJunction(LPCTSTR LinkName, LPCTSTR TargetName)
{
/* Structure for reparse point daya when ReparseTag is one of
* the tags defined by Microsoft. Copied from MinGW winnt.h */
typedef struct _REPARSE_DATA_BUFFER
{
DWORD ReparseTag;
WORD ReparseDataLength;
WORD Reserved;
_ANONYMOUS_UNION union
{
struct
{
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct
{
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct
{
BYTE DataBuffer[1];
} GenericReparseBuffer;
} DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
HMODULE hNTDLL = GetModuleHandle(_T("NTDLL"));
BOOLEAN (WINAPI *RtlDosPathNameToNtPathName_U)(PCWSTR, PUNICODE_STRING, PCWSTR *, CURDIR *)
= (BOOLEAN (WINAPI *)(PCWSTR, PUNICODE_STRING, PCWSTR *, CURDIR *))GetProcAddress(hNTDLL, "RtlDosPathNameToNtPathName_U");
VOID (WINAPI *RtlFreeUnicodeString)(PUNICODE_STRING)
= (VOID (WINAPI *)(PUNICODE_STRING))GetProcAddress(hNTDLL, "RtlFreeUnicodeString");
TCHAR TargetFullPath[MAX_PATH];
#ifdef UNICODE
#define TargetFullPathW TargetFullPath
#else
WCHAR TargetFullPathW[MAX_PATH];
#endif
UNICODE_STRING TargetNTPath;
HANDLE hJunction;
/* The data for this kind of reparse point has two strings:
* The first ("SubstituteName") is the full target path in NT format,
* the second ("PrintName") is the full target path in Win32 format.
* Both of these must be wide-character strings. */
if (!RtlDosPathNameToNtPathName_U ||
!RtlFreeUnicodeString ||
!GetFullPathName(TargetName, MAX_PATH, TargetFullPath, NULL) ||
#ifndef UNICODE
!MultiByteToWideChar(CP_ACP, 0, TargetFullPath, -1, TargetFullPathW, -1) ||
#endif
!RtlDosPathNameToNtPathName_U(TargetFullPathW, &TargetNTPath, NULL, NULL))
{
return FALSE;
}
/* We have both the names we need, so time to create the junction.
* Start with an empty directory */
if (!CreateDirectory(LinkName, NULL))
{
RtlFreeUnicodeString(&TargetNTPath);
return FALSE;
}
/* Open the directory we just created */
hJunction = CreateFile(LinkName, GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (hJunction != INVALID_HANDLE_VALUE)
{
/* Allocate a buffer large enough to hold both strings, including trailing NULs */
SIZE_T TargetLen = wcslen(TargetFullPathW) * sizeof(WCHAR);
DWORD DataSize = (DWORD)(FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer)
+ TargetNTPath.Length + sizeof(WCHAR)
+ TargetLen + sizeof(WCHAR));
PREPARSE_DATA_BUFFER Data = _alloca(DataSize);
/* Fill it out and use it to turn the directory into a reparse point */
Data->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
Data->ReparseDataLength = (WORD)(DataSize - FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer));
Data->Reserved = 0;
Data->MountPointReparseBuffer.SubstituteNameOffset = 0;
Data->MountPointReparseBuffer.SubstituteNameLength = TargetNTPath.Length;
wcscpy(Data->MountPointReparseBuffer.PathBuffer,
TargetNTPath.Buffer);
Data->MountPointReparseBuffer.PrintNameOffset = TargetNTPath.Length + sizeof(WCHAR);
Data->MountPointReparseBuffer.PrintNameLength = (USHORT)TargetLen;
wcscpy((WCHAR *)((BYTE *)Data->MountPointReparseBuffer.PathBuffer
+ Data->MountPointReparseBuffer.PrintNameOffset),
TargetFullPathW);
if (DeviceIoControl(hJunction, FSCTL_SET_REPARSE_POINT,
Data, DataSize, NULL, 0, &DataSize, NULL))
{
/* Success */
CloseHandle(hJunction);
RtlFreeUnicodeString(&TargetNTPath);
return TRUE;
}
CloseHandle(hJunction);
}
RemoveDirectory(LinkName);
RtlFreeUnicodeString(&TargetNTPath);
return FALSE;
}
INT
cmd_mklink(LPTSTR param)
{
HMODULE hKernel32 = GetModuleHandle(_T("KERNEL32"));
DWORD Flags = 0;
enum { SYMBOLIC, HARD, JUNCTION } LinkType = SYMBOLIC;
INT NumFiles = 0;
LPTSTR Name[2];
INT argc, i;
LPTSTR *arg;
if (!_tcsncmp(param, _T("/?"), 2))
{
ConOutResPuts(STRING_MKLINK_HELP);
return 0;
}
arg = split(param, &argc, FALSE, FALSE);
for (i = 0; i < argc; i++)
{
if (arg[i][0] == _T('/'))
{
if (!_tcsicmp(arg[i], _T("/D")))
Flags |= 1; /* SYMBOLIC_LINK_FLAG_DIRECTORY */
else if (!_tcsicmp(arg[i], _T("/H")))
LinkType = HARD;
else if (!_tcsicmp(arg[i], _T("/J")))
LinkType = JUNCTION;
else
{
error_invalid_switch(arg[i][1]);
freep(arg);
return 1;
}
}
else
{
if (NumFiles == 2)
{
error_too_many_parameters(arg[i]);
freep(arg);
return 1;
}
Name[NumFiles++] = arg[i];
}
}
freep(arg);
if (NumFiles != 2)
{
error_req_param_missing();
return 1;
}
nErrorLevel = 0;
if (LinkType == SYMBOLIC)
{
/* CreateSymbolicLink doesn't exist in old versions of Windows,
* so load dynamically */
BOOL (WINAPI *CreateSymbolicLink)(LPCTSTR, LPCTSTR, DWORD)
#ifdef UNICODE
= (BOOL (WINAPI *)(LPCTSTR, LPCTSTR, DWORD))GetProcAddress(hKernel32, "CreateSymbolicLinkW");
#else
= (BOOL (WINAPI *)(LPCTSTR, LPCTSTR, DWORD))GetProcAddress(hKernel32, "CreateSymbolicLinkA");
#endif
if (CreateSymbolicLink && CreateSymbolicLink(Name[0], Name[1], Flags))
{
ConOutResPrintf(STRING_MKLINK_CREATED_SYMBOLIC, Name[0], Name[1]);
return 0;
}
}
else if (LinkType == HARD)
{
/* CreateHardLink doesn't exist in old versions of Windows,
* so load dynamically */
BOOL (WINAPI *CreateHardLink)(LPCTSTR, LPCTSTR, LPSECURITY_ATTRIBUTES)
#ifdef UNICODE
= (BOOL (WINAPI *)(LPCTSTR, LPCTSTR, LPSECURITY_ATTRIBUTES))GetProcAddress(hKernel32, "CreateHardLinkW");
#else
= (BOOL (WINAPI *)(LPCTSTR, LPCTSTR, LPSECURITY_ATTRIBUTES))GetProcAddress(hKernel32, "CreateHardLinkA");
#endif
if (CreateHardLink && CreateHardLink(Name[0], Name[1], NULL))
{
ConOutResPrintf(STRING_MKLINK_CREATED_HARD, Name[0], Name[1]);
return 0;
}
}
else
{
if (CreateJunction(Name[0], Name[1]))
{
ConOutResPrintf(STRING_MKLINK_CREATED_JUNCTION, Name[0], Name[1]);
return 0;
}
}
ErrorMessage(GetLastError(), _T("MKLINK"));
return 1;
}
#endif /* INCLUDE_CMD_MKLINK */