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