1 /*
2 * Copyright 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <windows.h>
33 #include <commctrl.h>
34 #include <nsis/pluginapi.h>
35 #include <winternl.h>
36
37 enum { kLargeBuf = 1024, kSmallBuf = 256 } ;
38
39 #if defined(_MSC_VER)
40 /* Ensure these are treated as functions and not inlined as intrinsics, or disable /Oi */
41 #pragma warning(disable:4164) /* intrinsic function not declared */
42 #pragma function(memcpy, memset, memcmp)
43 #endif
44
45 HMODULE hNrDll = NULL;
46 NTSTATUS (NTAPI *fNtCreateFile) (PHANDLE FileHandle,
47 ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
48 PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize,
49 ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition,
50 ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength);
51 NTSTATUS (NTAPI *fNtClose) (HANDLE Handle);
52
53 HMODULE hKernel32 = NULL;
54 BOOL (WINAPI *fCreateHardLink) (TCHAR * linkFileName,
55 TCHAR * existingFileName, LPSECURITY_ATTRIBUTES lpSecurityAttributes);
56 BOOL (WINAPI *fCreateSymbolicLink) (TCHAR * linkFileName,
57 TCHAR * existingFileName, DWORD flags);
58
59 #undef CreateHardLink
60 #undef CreateSymbolicLink
61 #ifdef UNICODE
62 #define CreateHardLink "CreateHardLinkW"
63 #define CreateSymbolicLink "CreateSymbolicLinkW"
64 #else
65 #define CreateHardLink "CreateHardLinkA"
66 #define CreateSymbolicLink "CreateSymbolicLinkA"
67 #endif
68
MakeHardLink(TCHAR * linkFileName,TCHAR * existingFileName)69 BOOL MakeHardLink(TCHAR *linkFileName, TCHAR *existingFileName) {
70 if (!hKernel32)
71 hKernel32 = LoadLibrary(_T("KERNEL32.DLL"));
72 if (hKernel32) {
73 if (!fCreateHardLink)
74 fCreateHardLink = GetProcAddress(hKernel32, CreateHardLink);
75 if (fCreateHardLink)
76 return fCreateHardLink(linkFileName, existingFileName, NULL);
77 }
78 return FALSE;
79 }
80
MakeSymLink(TCHAR * linkFileName,TCHAR * existingFileName,BOOL dirLink)81 BOOL MakeSymLink(TCHAR *linkFileName, TCHAR *existingFileName, BOOL dirLink) {
82 TCHAR *f1 = HeapAlloc(GetProcessHeap(), 0, sizeof(TCHAR)*kLargeBuf);
83 TCHAR *f2 = HeapAlloc(GetProcessHeap(), 0, sizeof(TCHAR)*kLargeBuf);
84 SECURITY_ATTRIBUTES sec_attr = { sizeof (SECURITY_ATTRIBUTES), NULL, FALSE};
85 OBJECT_ATTRIBUTES obj_attr;
86 IO_STATUS_BLOCK io_block;
87 TCHAR *p, *q;
88 HANDLE f;
89 BOOL status;
90 if (!f1 || !f2)
91 return FALSE;
92 lstrcpy(f1, linkFileName);
93 for (p = f1; p[0]; p++)
94 if (p[0] == _T('/'))
95 p[0] = _T('\\');
96 q = f1;
97 while (q[0]) {
98 p = q;
99 do {
100 q++;
101 } while (q[0] && q[0] != '\\');
102 }
103 if (p[0] = '\\') {
104 TCHAR c = p[1];
105 p[1] = 0;
106 status = GetVolumeInformation(f1, NULL, 0, NULL, NULL, NULL,
107 f2, sizeof(f2));
108 p[1] = c;
109 } else {
110 status = GetVolumeInformation(NULL, NULL, 0, NULL, NULL, NULL,
111 f2, sizeof(f2));
112 }
113 /* If it's NFS then we can create real symbolic link. */
114 if (!lstrcmpi(f2, _T("NFS"))) {
115 lstrcpy(f2, existingFileName);
116 for (p = f2; p[0]; p++)
117 if (p[0] == _T('\\'))
118 p[0] = _T('/');
119 if (!hNrDll)
120 hNrDll = LoadLibrary(_T("NTDLL.DLL"));
121 if (hNrDll) {
122 if (!fNtCreateFile)
123 fNtCreateFile = GetProcAddress(hNrDll, "NtCreateFile");
124 if (!fNtClose)
125 fNtClose = GetProcAddress(hNrDll, "NtClose");
126 if (fNtCreateFile && fNtClose) {
127 struct {
128 ULONG offset;
129 UCHAR flags;
130 UCHAR nameLength;
131 USHORT valueLength;
132 CHAR name[21];
133 /* To prevent troubles with alignment */
134 CHAR value[kLargeBuf*sizeof(WCHAR)];
135 } *ea_info = HeapAlloc(GetProcessHeap(),
136 HEAP_ZERO_MEMORY,
137 sizeof(*ea_info));
138 WCHAR *fn = HeapAlloc(GetProcessHeap(),
139 0,
140 sizeof(TCHAR)*kLargeBuf);
141 UNICODE_STRING n = { lstrlen(f1), kLargeBuf, fn };
142 ea_info->nameLength = 20;
143 lstrcpy(ea_info->name, "NfsSymlinkTargetName");
144 #ifdef UNICODE
145 lstrcpy(fn, f1);
146 lstrcpy((LPWSTR)ea_info->value, existingFileName);
147 #else
148 MultiByteToWideChar(CP_ACP, 0, f1, -1, fn, kLargeBuf);
149 MultiByteToWideChar(CP_ACP, 0, existingFileName, -1,
150 (LPWSTR)ea_info->value,
151 sizeof(ea_info->value)/sizeof(WCHAR));
152 #endif
153 ea_info->valueLength =
154 lstrlenW((LPWSTR)ea_info->value)*sizeof(WCHAR);
155 InitializeObjectAttributes(&obj_attr, &n, OBJ_CASE_INSENSITIVE,
156 NULL, NULL);
157 status = fNtCreateFile(
158 &f, FILE_WRITE_DATA | FILE_WRITE_EA | SYNCHRONIZE, &obj_attr,
159 &io_block, NULL, FILE_ATTRIBUTE_SYSTEM, FILE_SHARE_READ |
160 FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_CREATE,
161 FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
162 &ea_info, 1024 * sizeof (WCHAR));
163 if (NT_SUCCESS(status)) {
164 fNtClose(f);
165 HeapFree(GetProcessHeap(), 0, fn);
166 HeapFree(GetProcessHeap(), 0, ea_info);
167 HeapFree(GetProcessHeap(), 0, f2);
168 HeapFree(GetProcessHeap(), 0, f1);
169 return TRUE;
170 }
171 HeapFree(GetProcessHeap(), 0, fn);
172 HeapFree(GetProcessHeap(), 0, ea_info);
173 }
174 }
175 }
176 lstrcpy(f2, existingFileName);
177 for (p = f2; p[0]; p++)
178 if (p[0] == _T('/'))
179 p[0] = _T('\\');
180 if (!hKernel32)
181 hKernel32 = LoadLibrary(_T("KERNEL32.DLL"));
182 if (hKernel32) {
183 if (!fCreateSymbolicLink)
184 fCreateSymbolicLink = GetProcAddress(hKernel32, CreateSymbolicLink);
185 if (fCreateSymbolicLink) {
186 if (fCreateSymbolicLink(f1, f2,
187 dirLink ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0)) {
188 HeapFree(GetProcessHeap(), 0, f2);
189 HeapFree(GetProcessHeap(), 0, f1);
190 return TRUE;
191 }
192 }
193 }
194 if (dirLink) {
195 /* Ignore errors - file may already exist */
196 CreateDirectory(f1, NULL);
197 f = CreateFile(f1, GENERIC_READ | GENERIC_WRITE,
198 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
199 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
200 NULL );
201 if (f != INVALID_HANDLE_VALUE) {
202 struct rp_info {
203 DWORD tag;
204 DWORD dataLength;
205 WORD reserved1;
206 WORD targetLength;
207 WORD targetMaxLength;
208 WORD reserved2;
209 WCHAR target[kLargeBuf+4];
210 } *rp_info = HeapAlloc(GetProcessHeap(),
211 HEAP_ZERO_MEMORY,
212 sizeof(*rp_info));
213 DWORD len;
214 WCHAR *startlink, *endlink;
215 rp_info->tag = IO_REPARSE_TAG_MOUNT_POINT;
216 rp_info->target[0] = L'\\';
217 rp_info->target[1] = L'?';
218 rp_info->target[2] = L'?';
219 rp_info->target[3] = L'\\';
220 if (((f2[0] == _T('\\')) && (f2[1] == _T('\\'))) ||
221 ((f2[1] == _T(':')) && (f2[2] == _T('\\')))) {
222 #ifdef UNICODE
223 lstrcpy(rp_info->target+4, f2);
224 #else
225 MultiByteToWideChar(CP_ACP, 0, f2, -1,
226 rp_info->target+4, kLargeBuf);
227 #endif
228 } else {
229 #ifdef UNICODE
230 GetFullPathNameW(f1, 1024, rp_info->target+4, &startlink);
231 lstrcpy(startlink, f2);
232 #else
233 MultiByteToWideChar(CP_ACP, 0, f1, -1,
234 (LPWSTR)f1, kLargeBuf/sizeof(WCHAR));
235 GetFullPathNameW(f1, 1024, rp_info->target+4, &startlink);
236 MultiByteToWideChar(CP_ACP, 0, f2, -1,
237 startlink, kLargeBuf+4-(startlink-rp_info->target));
238 #endif
239 }
240 /* Remove "XXX/../" and replace "/" with "\" */
241 for (startlink = endlink = rp_info->target+4;
242 endlink[0]; startlink++, endlink++) {
243 startlink[0] = endlink[0];
244 if ((startlink[0] == L'\\') &&
245 (startlink[-1] == L'.') &&
246 (startlink[-2] == L'.')) {
247 for (startlink--; startlink > rp_info->target+4 &&
248 startlink[0] != L'\\'; startlink--)
249 { }
250 for (startlink--; startlink > rp_info->target+4 &&
251 startlink[0] != L'\\'; startlink--)
252 { }
253 if (startlink < rp_info->target+4)
254 startlink = rp_info->target+4;
255 }
256 }
257 startlink[0] = endlink[0];
258 rp_info->targetLength = lstrlenW(rp_info->target)*sizeof(WCHAR);
259 rp_info->targetMaxLength = rp_info->targetLength+sizeof(WCHAR);
260 rp_info->dataLength = rp_info->targetMaxLength
261 +FIELD_OFFSET(struct rp_info, target)
262 -FIELD_OFFSET(struct rp_info, reserved1)
263 +sizeof(WCHAR);
264 if (DeviceIoControl(f, 0x900A4, rp_info,
265 rp_info->dataLength
266 +FIELD_OFFSET(struct rp_info, reserved1),
267 NULL, 0, &len, NULL)) {
268 CloseHandle(f);
269 HeapFree(GetProcessHeap(), 0, rp_info);
270 HeapFree(GetProcessHeap(), 0, f2);
271 HeapFree(GetProcessHeap(), 0, f1);
272 return TRUE;
273 }
274 CloseHandle(f);
275 RemoveDirectory(f1);
276 HeapFree(GetProcessHeap(), 0, rp_info);
277 }
278 }
279 for (p = f2; p[0]; p++)
280 if (p[0] == _T('\\'))
281 p[0] = _T('/');
282 f = CreateFile(f1, GENERIC_READ | GENERIC_WRITE,
283 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
284 CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM, NULL);
285 if (f != INVALID_HANDLE_VALUE) {
286 struct {
287 WCHAR sig[4];
288 WCHAR value[kLargeBuf];
289 } *link_info = HeapAlloc(GetProcessHeap(),
290 HEAP_ZERO_MEMORY,
291 sizeof(*link_info));
292 DWORD towrite, written;
293 link_info->sig[0] = 0x6e49;
294 link_info->sig[1] = 0x7874;
295 link_info->sig[2] = 0x4e4c;
296 link_info->sig[3] = 0x014b;
297 #ifdef UNICODE
298 lstrcpy(link_info->value, f2);
299 #else
300 MultiByteToWideChar(CP_ACP, 0, f2, -1, link_info->value, kLargeBuf);
301 #endif
302 towrite = lstrlenW(link_info->value)*sizeof(WCHAR)+sizeof(link_info->sig);
303 WriteFile(f, link_info, towrite, &written, NULL);
304 CloseHandle(f);
305 if (written == towrite) {
306 HeapFree(GetProcessHeap(), 0, link_info);
307 HeapFree(GetProcessHeap(), 0, f2);
308 HeapFree(GetProcessHeap(), 0, f1);
309 return TRUE;
310 }
311 HeapFree(GetProcessHeap(), 0, link_info);
312 }
313 HeapFree(GetProcessHeap(), 0, f2);
314 HeapFree(GetProcessHeap(), 0, f1);
315 return FALSE;
316 }
317
318 HINSTANCE instance;
319
320 HWND parent, list;
321
322 /*
323 * Show message in NSIS details window.
324 */
NSISprint(const TCHAR * str)325 void NSISprint(const TCHAR *str) {
326 if (list && *str) {
327 LVITEM item = {
328 /* mask */ LVIF_TEXT,
329 /* iItem */ SendMessage(list, LVM_GETITEMCOUNT, 0, 0),
330 /* iSubItem */ 0, /* state */ 0, /* stateMask */ 0,
331 /* pszText */ (TCHAR *) str, /* cchTextMax */ 0,
332 /* iImage */ 0, /* lParam */ 0, /* iIndent */ 0,
333 /* iGroupId */ 0, /* cColumns */ 0, /* puColumns */ NULL,
334 /* piColFmt */ NULL, /* iGroup */ 0};
335 ListView_InsertItem(list, &item);
336 ListView_EnsureVisible(list, item.iItem, 0);
337 }
338 }
339
340 enum linktype { HARDLINK, SOFTLINKD, SOFTLINKF };
341
makelink(HWND hwndParent,int string_size,TCHAR * variables,stack_t ** stacktop,extra_parameters * extra,enum linktype type)342 void makelink(HWND hwndParent, int string_size, TCHAR *variables,
343 stack_t **stacktop, extra_parameters *extra, enum linktype type) {
344 TCHAR *msg = HeapAlloc(GetProcessHeap(), 0, sizeof(TCHAR)*kLargeBuf);
345 TCHAR *from = HeapAlloc(GetProcessHeap(), 0, sizeof(TCHAR)*kSmallBuf);
346 TCHAR *to = HeapAlloc(GetProcessHeap(), 0, sizeof(TCHAR)*kSmallBuf);
347 TCHAR *msgFormat =
348 type == HARDLINK ? _T("Link: \"%s\" to \"%s\"%s") :
349 type == SOFTLINKD ? _T("Symbolic Directory Link: \"%s\" to \"%s\"%s") :
350 _T("Symbolic Link: \"%s\" to \"%s\"%s");
351 BOOL res;
352 parent = hwndParent;
353 list = FindWindowEx(FindWindowEx(parent, NULL, _T("#32770"), NULL),
354 NULL, _T("SysListView32"), NULL);
355
356 EXDLL_INIT();
357
358 if (!msg || !from || !to) {
359 MessageBox(parent, _T("Fatal error: no memory for MkLink"), 0, MB_OK);
360 }
361
362 if (popstringn(from, kSmallBuf)) {
363 MessageBox(parent,
364 _T("Usage: MkLink::Hard \"to_file\" \"from_file\" "), 0, MB_OK);
365 }
366
367 if (popstringn(to, kSmallBuf)) {
368 MessageBox(parent,
369 _T("Usage: MkLink::Hard \"fo_file\" \"from_file\" "),0,MB_OK);
370 }
371
372 switch (type) {
373 case HARDLINK:
374 res = MakeHardLink(from, to);
375 break;
376 case SOFTLINKD:
377 res = MakeSymLink(from, to, TRUE);
378 break;
379 case SOFTLINKF:
380 res = MakeSymLink(from, to, FALSE);
381 break;
382 }
383 wsprintf(msg, msgFormat, to, from, res ? _T("") : _T(" - fail..."));
384 NSISprint(msg);
385
386 HeapFree(GetProcessHeap(), 0, to);
387 HeapFree(GetProcessHeap(), 0, from);
388 HeapFree(GetProcessHeap(), 0, msg);
389 }
390
Hard(HWND hwndParent,int string_size,TCHAR * variables,stack_t ** stacktop,extra_parameters * extra)391 void __declspec(dllexport) Hard(HWND hwndParent, int string_size,
392 TCHAR *variables, stack_t **stacktop,
393 extra_parameters *extra) {
394 makelink(hwndParent, string_size, variables, stacktop, extra, HARDLINK);
395 }
396
SoftD(HWND hwndParent,int string_size,TCHAR * variables,stack_t ** stacktop,extra_parameters * extra)397 void __declspec(dllexport) SoftD(HWND hwndParent, int string_size,
398 TCHAR *variables, stack_t **stacktop,
399 extra_parameters *extra) {
400 makelink(hwndParent, string_size, variables, stacktop, extra, SOFTLINKD);
401 }
402
SoftF(HWND hwndParent,int string_size,TCHAR * variables,stack_t ** stacktop,extra_parameters * extra)403 void __declspec(dllexport) SoftF(HWND hwndParent, int string_size,
404 TCHAR *variables, stack_t **stacktop,
405 extra_parameters *extra) {
406 makelink(hwndParent, string_size, variables, stacktop, extra, SOFTLINKF);
407 }
408
DllMain(HANDLE hInst,ULONG ul_reason_for_call,LPVOID lpReserved)409 BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) {
410 instance = hInst;
411 return TRUE;
412 }
413