xref: /reactos/base/shell/cmd/mklink.c (revision c2c66aff)
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