mirror of
https://github.com/reactos/reactos.git
synced 2024-11-19 05:22:59 +00:00
224 lines
7.5 KiB
C
224 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 */
|