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