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 */
CreateJunction(LPCTSTR LinkName,LPCTSTR TargetName)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
cmd_mklink(LPTSTR param)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