1 /* 2 * MKLINK.C - mklink internal command. 3 */ 4 5 #include "precomp.h" 6 7 #ifdef INCLUDE_CMD_MKLINK 8 9 /* There is no API for creating junctions, so we must do it the hard way */ 10 static BOOL CreateJunction(LPCTSTR LinkName, LPCTSTR TargetName) 11 { 12 /* Structure for reparse point daya when ReparseTag is one of 13 * the tags defined by Microsoft. Copied from MinGW winnt.h */ 14 typedef struct _REPARSE_DATA_BUFFER 15 { 16 DWORD ReparseTag; 17 WORD ReparseDataLength; 18 WORD Reserved; 19 _ANONYMOUS_UNION union 20 { 21 struct 22 { 23 WORD SubstituteNameOffset; 24 WORD SubstituteNameLength; 25 WORD PrintNameOffset; 26 WORD PrintNameLength; 27 ULONG Flags; 28 WCHAR PathBuffer[1]; 29 } SymbolicLinkReparseBuffer; 30 struct 31 { 32 WORD SubstituteNameOffset; 33 WORD SubstituteNameLength; 34 WORD PrintNameOffset; 35 WORD PrintNameLength; 36 WCHAR PathBuffer[1]; 37 } MountPointReparseBuffer; 38 struct 39 { 40 BYTE DataBuffer[1]; 41 } GenericReparseBuffer; 42 } DUMMYUNIONNAME; 43 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; 44 45 HMODULE hNTDLL = GetModuleHandle(_T("NTDLL")); 46 BOOLEAN (WINAPI *RtlDosPathNameToNtPathName_U)(PCWSTR, PUNICODE_STRING, PCWSTR *, CURDIR *) 47 = (BOOLEAN (WINAPI *)(PCWSTR, PUNICODE_STRING, PCWSTR *, CURDIR *))GetProcAddress(hNTDLL, "RtlDosPathNameToNtPathName_U"); 48 VOID (WINAPI *RtlFreeUnicodeString)(PUNICODE_STRING) 49 = (VOID (WINAPI *)(PUNICODE_STRING))GetProcAddress(hNTDLL, "RtlFreeUnicodeString"); 50 51 TCHAR TargetFullPath[MAX_PATH]; 52 #ifdef UNICODE 53 #define TargetFullPathW TargetFullPath 54 #else 55 WCHAR TargetFullPathW[MAX_PATH]; 56 #endif 57 UNICODE_STRING TargetNTPath; 58 HANDLE hJunction; 59 60 /* The data for this kind of reparse point has two strings: 61 * The first ("SubstituteName") is the full target path in NT format, 62 * the second ("PrintName") is the full target path in Win32 format. 63 * Both of these must be wide-character strings. */ 64 if (!RtlDosPathNameToNtPathName_U || 65 !RtlFreeUnicodeString || 66 !GetFullPathName(TargetName, MAX_PATH, TargetFullPath, NULL) || 67 #ifndef UNICODE 68 !MultiByteToWideChar(CP_ACP, 0, TargetFullPath, -1, TargetFullPathW, -1) || 69 #endif 70 !RtlDosPathNameToNtPathName_U(TargetFullPathW, &TargetNTPath, NULL, NULL)) 71 { 72 return FALSE; 73 } 74 75 /* We have both the names we need, so time to create the junction. 76 * Start with an empty directory */ 77 if (!CreateDirectory(LinkName, NULL)) 78 { 79 RtlFreeUnicodeString(&TargetNTPath); 80 return FALSE; 81 } 82 83 /* Open the directory we just created */ 84 hJunction = CreateFile(LinkName, GENERIC_WRITE, 0, NULL, 85 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 86 if (hJunction != INVALID_HANDLE_VALUE) 87 { 88 /* Allocate a buffer large enough to hold both strings, including trailing NULs */ 89 SIZE_T TargetLen = wcslen(TargetFullPathW) * sizeof(WCHAR); 90 DWORD DataSize = (DWORD)(FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) 91 + TargetNTPath.Length + sizeof(WCHAR) 92 + TargetLen + sizeof(WCHAR)); 93 PREPARSE_DATA_BUFFER Data = _alloca(DataSize); 94 95 /* Fill it out and use it to turn the directory into a reparse point */ 96 Data->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; 97 Data->ReparseDataLength = (WORD)(DataSize - FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer)); 98 Data->Reserved = 0; 99 Data->MountPointReparseBuffer.SubstituteNameOffset = 0; 100 Data->MountPointReparseBuffer.SubstituteNameLength = TargetNTPath.Length; 101 wcscpy(Data->MountPointReparseBuffer.PathBuffer, 102 TargetNTPath.Buffer); 103 Data->MountPointReparseBuffer.PrintNameOffset = TargetNTPath.Length + sizeof(WCHAR); 104 Data->MountPointReparseBuffer.PrintNameLength = (USHORT)TargetLen; 105 wcscpy((WCHAR *)((BYTE *)Data->MountPointReparseBuffer.PathBuffer 106 + Data->MountPointReparseBuffer.PrintNameOffset), 107 TargetFullPathW); 108 if (DeviceIoControl(hJunction, FSCTL_SET_REPARSE_POINT, 109 Data, DataSize, NULL, 0, &DataSize, NULL)) 110 { 111 /* Success */ 112 CloseHandle(hJunction); 113 RtlFreeUnicodeString(&TargetNTPath); 114 return TRUE; 115 } 116 CloseHandle(hJunction); 117 } 118 RemoveDirectory(LinkName); 119 RtlFreeUnicodeString(&TargetNTPath); 120 return FALSE; 121 } 122 123 INT 124 cmd_mklink(LPTSTR param) 125 { 126 HMODULE hKernel32 = GetModuleHandle(_T("KERNEL32")); 127 DWORD Flags = 0; 128 enum { SYMBOLIC, HARD, JUNCTION } LinkType = SYMBOLIC; 129 INT NumFiles = 0; 130 LPTSTR Name[2]; 131 INT argc, i; 132 LPTSTR *arg; 133 134 if (!_tcsncmp(param, _T("/?"), 2)) 135 { 136 ConOutResPuts(STRING_MKLINK_HELP); 137 return 0; 138 } 139 140 arg = split(param, &argc, FALSE, FALSE); 141 for (i = 0; i < argc; i++) 142 { 143 if (arg[i][0] == _T('/')) 144 { 145 if (!_tcsicmp(arg[i], _T("/D"))) 146 Flags |= 1; /* SYMBOLIC_LINK_FLAG_DIRECTORY */ 147 else if (!_tcsicmp(arg[i], _T("/H"))) 148 LinkType = HARD; 149 else if (!_tcsicmp(arg[i], _T("/J"))) 150 LinkType = JUNCTION; 151 else 152 { 153 error_invalid_switch(arg[i][1]); 154 freep(arg); 155 return 1; 156 } 157 } 158 else 159 { 160 if (NumFiles == 2) 161 { 162 error_too_many_parameters(arg[i]); 163 freep(arg); 164 return 1; 165 } 166 Name[NumFiles++] = arg[i]; 167 } 168 } 169 freep(arg); 170 171 if (NumFiles != 2) 172 { 173 error_req_param_missing(); 174 return 1; 175 } 176 177 nErrorLevel = 0; 178 179 if (LinkType == SYMBOLIC) 180 { 181 /* CreateSymbolicLink doesn't exist in old versions of Windows, 182 * so load dynamically */ 183 BOOL (WINAPI *CreateSymbolicLink)(LPCTSTR, LPCTSTR, DWORD) 184 #ifdef UNICODE 185 = (BOOL (WINAPI *)(LPCTSTR, LPCTSTR, DWORD))GetProcAddress(hKernel32, "CreateSymbolicLinkW"); 186 #else 187 = (BOOL (WINAPI *)(LPCTSTR, LPCTSTR, DWORD))GetProcAddress(hKernel32, "CreateSymbolicLinkA"); 188 #endif 189 if (CreateSymbolicLink && CreateSymbolicLink(Name[0], Name[1], Flags)) 190 { 191 ConOutResPrintf(STRING_MKLINK_CREATED_SYMBOLIC, Name[0], Name[1]); 192 return 0; 193 } 194 } 195 else if (LinkType == HARD) 196 { 197 /* CreateHardLink doesn't exist in old versions of Windows, 198 * so load dynamically */ 199 BOOL (WINAPI *CreateHardLink)(LPCTSTR, LPCTSTR, LPSECURITY_ATTRIBUTES) 200 #ifdef UNICODE 201 = (BOOL (WINAPI *)(LPCTSTR, LPCTSTR, LPSECURITY_ATTRIBUTES))GetProcAddress(hKernel32, "CreateHardLinkW"); 202 #else 203 = (BOOL (WINAPI *)(LPCTSTR, LPCTSTR, LPSECURITY_ATTRIBUTES))GetProcAddress(hKernel32, "CreateHardLinkA"); 204 #endif 205 if (CreateHardLink && CreateHardLink(Name[0], Name[1], NULL)) 206 { 207 ConOutResPrintf(STRING_MKLINK_CREATED_HARD, Name[0], Name[1]); 208 return 0; 209 } 210 } 211 else 212 { 213 if (CreateJunction(Name[0], Name[1])) 214 { 215 ConOutResPrintf(STRING_MKLINK_CREATED_JUNCTION, Name[0], Name[1]); 216 return 0; 217 } 218 } 219 220 ErrorMessage(GetLastError(), _T("MKLINK")); 221 return 1; 222 } 223 224 #endif /* INCLUDE_CMD_MKLINK */ 225