2 * Copyright 2012 Austin English
3 * Copyright 2015 Michael Müller
4 * Copyright 2015 Sebastian Lackner
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "wine/debug.h"
28 #include "wine/list.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(wusa
);
40 struct installer_tempdir
46 struct installer_state
51 struct list assemblies
;
55 static void * CDECL
cabinet_alloc(ULONG cb
)
60 static void CDECL
cabinet_free(void *pv
)
65 static INT_PTR CDECL
cabinet_open(char *pszFile
, int oflag
, int pmode
)
68 DWORD dwShareMode
= 0;
69 DWORD dwCreateDisposition
= OPEN_EXISTING
;
71 switch (oflag
& _O_ACCMODE
)
74 dwAccess
= GENERIC_READ
;
75 dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_DELETE
;
78 dwAccess
= GENERIC_WRITE
;
79 dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
;
82 dwAccess
= GENERIC_READ
| GENERIC_WRITE
;
83 dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
;
87 if ((oflag
& (_O_CREAT
| _O_EXCL
)) == (_O_CREAT
| _O_EXCL
))
88 dwCreateDisposition
= CREATE_NEW
;
89 else if (oflag
& _O_CREAT
)
90 dwCreateDisposition
= CREATE_ALWAYS
;
92 return (INT_PTR
)CreateFileA(pszFile
, dwAccess
, dwShareMode
, NULL
, dwCreateDisposition
, 0, NULL
);
95 static UINT CDECL
cabinet_read(INT_PTR hf
, void *pv
, UINT cb
)
97 HANDLE handle
= (HANDLE
)hf
;
100 if (ReadFile(handle
, pv
, cb
, &read
, NULL
))
106 static UINT CDECL
cabinet_write(INT_PTR hf
, void *pv
, UINT cb
)
108 HANDLE handle
= (HANDLE
)hf
;
111 if (WriteFile(handle
, pv
, cb
, &written
, NULL
))
117 static int CDECL
cabinet_close(INT_PTR hf
)
119 HANDLE handle
= (HANDLE
)hf
;
120 return CloseHandle(handle
) ? 0 : -1;
123 static LONG CDECL
cabinet_seek(INT_PTR hf
, LONG dist
, int seektype
)
125 HANDLE handle
= (HANDLE
)hf
;
126 /* flags are compatible and so are passed straight through */
127 return SetFilePointer(handle
, dist
, NULL
, seektype
);
130 static WCHAR
*path_combine(const WCHAR
*path
, const WCHAR
*filename
)
135 if (!path
|| !filename
) return NULL
;
136 length
= lstrlenW(path
) + lstrlenW(filename
) + 2;
137 if (!(result
= malloc(length
* sizeof(WCHAR
)))) return NULL
;
139 lstrcpyW(result
, path
);
140 if (result
[0] && result
[lstrlenW(result
) - 1] != '\\') lstrcatW(result
, L
"\\");
141 lstrcatW(result
, filename
);
145 static WCHAR
*get_uncompressed_path(PFDINOTIFICATION pfdin
)
147 WCHAR
*file
= strdupAtoW(pfdin
->psz1
);
148 WCHAR
*path
= path_combine(pfdin
->pv
, file
);
153 static BOOL
is_directory(const WCHAR
*path
)
155 DWORD attrs
= GetFileAttributesW(path
);
156 if (attrs
== INVALID_FILE_ATTRIBUTES
) return FALSE
;
157 return (attrs
& FILE_ATTRIBUTE_DIRECTORY
) != 0;
160 static BOOL
create_directory(const WCHAR
*path
)
162 if (is_directory(path
)) return TRUE
;
163 if (CreateDirectoryW(path
, NULL
)) return TRUE
;
164 return (GetLastError() == ERROR_ALREADY_EXISTS
);
167 static BOOL
create_parent_directory(const WCHAR
*filename
)
169 WCHAR
*p
, *path
= strdupW(filename
);
172 if (!path
) return FALSE
;
173 if (!PathRemoveFileSpecW(path
)) goto done
;
174 if (is_directory(path
))
180 for (p
= path
; *p
; p
++)
182 if (*p
!= '\\') continue;
184 if (!create_directory(path
)) goto done
;
187 ret
= create_directory(path
);
194 static INT_PTR
cabinet_copy_file(FDINOTIFICATIONTYPE fdint
, PFDINOTIFICATION pfdin
)
196 HANDLE handle
= INVALID_HANDLE_VALUE
;
200 if (!(file
= get_uncompressed_path(pfdin
)))
203 TRACE("Extracting %s -> %s\n", debugstr_a(pfdin
->psz1
), debugstr_w(file
));
205 if (create_parent_directory(file
))
207 attrs
= pfdin
->attribs
;
208 if (!attrs
) attrs
= FILE_ATTRIBUTE_NORMAL
;
209 handle
= CreateFileW(file
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, attrs
, NULL
);
213 return (handle
!= INVALID_HANDLE_VALUE
) ? (INT_PTR
)handle
: -1;
216 static INT_PTR
cabinet_close_file_info(FDINOTIFICATIONTYPE fdint
, PFDINOTIFICATION pfdin
)
218 HANDLE handle
= (HANDLE
)pfdin
->hf
;
223 static INT_PTR CDECL
cabinet_notify(FDINOTIFICATIONTYPE fdint
, PFDINOTIFICATION pfdin
)
227 case fdintPARTIAL_FILE
:
228 FIXME("fdintPARTIAL_FILE not implemented\n");
231 case fdintNEXT_CABINET
:
232 FIXME("fdintNEXT_CABINET not implemented\n");
236 return cabinet_copy_file(fdint
, pfdin
);
238 case fdintCLOSE_FILE_INFO
:
239 return cabinet_close_file_info(fdint
, pfdin
);
246 static BOOL
extract_cabinet(const WCHAR
*filename
, const WCHAR
*destination
)
248 char *filenameA
= NULL
;
253 hfdi
= FDICreate(cabinet_alloc
, cabinet_free
, cabinet_open
, cabinet_read
,
254 cabinet_write
, cabinet_close
, cabinet_seek
, 0, &erf
);
255 if (!hfdi
) return FALSE
;
257 if ((filenameA
= strdupWtoA(filename
)))
259 ret
= FDICopy(hfdi
, filenameA
, NULL
, 0, cabinet_notify
, NULL
, (void *)destination
);
267 static const WCHAR
*create_temp_directory(struct installer_state
*state
)
270 struct installer_tempdir
*entry
;
273 if (!GetTempPathW(ARRAY_SIZE(tmp
), tmp
)) return NULL
;
274 if (!(entry
= malloc(sizeof(*entry
)))) return NULL
;
275 if (!(entry
->path
= malloc((MAX_PATH
+ 20) * sizeof(WCHAR
))))
282 if (!GetTempFileNameW(tmp
, L
"msu", ++id
, entry
->path
))
288 if (CreateDirectoryW(entry
->path
, NULL
)) break;
291 list_add_tail(&state
->tempdirs
, &entry
->entry
);
295 static BOOL
delete_directory(const WCHAR
*path
)
297 WIN32_FIND_DATAW data
;
301 if (!(full_path
= path_combine(path
, L
"*"))) return FALSE
;
302 search
= FindFirstFileW(full_path
, &data
);
305 if (search
!= INVALID_HANDLE_VALUE
)
309 if (!wcscmp(data
.cFileName
, L
".")) continue;
310 if (!wcscmp(data
.cFileName
, L
"..")) continue;
311 if (!(full_path
= path_combine(path
, data
.cFileName
))) continue;
312 if (data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
313 delete_directory(full_path
);
315 DeleteFileW(full_path
);
318 while (FindNextFileW(search
, &data
));
322 return RemoveDirectoryW(path
);
325 static void installer_cleanup(struct installer_state
*state
)
327 struct installer_tempdir
*tempdir
, *tempdir2
;
328 struct assembly_entry
*assembly
, *assembly2
;
329 struct dependency_entry
*dependency
, *dependency2
;
331 LIST_FOR_EACH_ENTRY_SAFE(tempdir
, tempdir2
, &state
->tempdirs
, struct installer_tempdir
, entry
)
333 list_remove(&tempdir
->entry
);
334 delete_directory(tempdir
->path
);
338 LIST_FOR_EACH_ENTRY_SAFE(assembly
, assembly2
, &state
->assemblies
, struct assembly_entry
, entry
)
340 list_remove(&assembly
->entry
);
341 free_assembly(assembly
);
343 LIST_FOR_EACH_ENTRY_SAFE(dependency
, dependency2
, &state
->updates
, struct dependency_entry
, entry
)
345 list_remove(&dependency
->entry
);
346 free_dependency(dependency
);
350 static BOOL
str_ends_with(const WCHAR
*str
, const WCHAR
*suffix
)
352 DWORD str_len
= lstrlenW(str
), suffix_len
= lstrlenW(suffix
);
353 if (suffix_len
> str_len
) return FALSE
;
354 return !wcsicmp(str
+ str_len
- suffix_len
, suffix
);
357 static BOOL
load_assemblies_from_cab(const WCHAR
*filename
, struct installer_state
*state
)
359 struct assembly_entry
*assembly
;
360 const WCHAR
*temp_path
;
361 WIN32_FIND_DATAW data
;
365 TRACE("Processing cab file %s\n", debugstr_w(filename
));
367 if (!(temp_path
= create_temp_directory(state
))) return FALSE
;
368 if (!extract_cabinet(filename
, temp_path
))
370 ERR("Failed to extract %s\n", debugstr_w(filename
));
374 if (!(path
= path_combine(temp_path
, L
"_manifest_.cix.xml"))) return FALSE
;
375 if (GetFileAttributesW(path
) != INVALID_FILE_ATTRIBUTES
)
377 FIXME("Cabinet uses proprietary msdelta file compression which is not (yet) supported\n");
378 FIXME("Installation of msu file will most likely fail\n");
382 if (!(path
= path_combine(temp_path
, L
"*"))) return FALSE
;
383 search
= FindFirstFileW(path
, &data
);
386 if (search
!= INVALID_HANDLE_VALUE
)
390 if (data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) continue;
391 if (!str_ends_with(data
.cFileName
, L
".manifest") &&
392 !str_ends_with(data
.cFileName
, L
".mum")) continue;
393 if (!(path
= path_combine(temp_path
, data
.cFileName
))) continue;
394 if ((assembly
= load_manifest(path
)))
395 list_add_tail(&state
->assemblies
, &assembly
->entry
);
398 while (FindNextFileW(search
, &data
));
405 static BOOL
compare_assembly_string(const WCHAR
*str1
, const WCHAR
*str2
)
407 return !wcscmp(str1
, str2
) || !wcscmp(str1
, L
"*") || !wcscmp(str2
, L
"*");
410 static struct assembly_entry
*lookup_assembly(struct list
*manifest_list
, struct assembly_identity
*identity
)
412 struct assembly_entry
*assembly
;
414 LIST_FOR_EACH_ENTRY(assembly
, manifest_list
, struct assembly_entry
, entry
)
416 if (wcsicmp(assembly
->identity
.name
, identity
->name
)) continue;
417 if (!compare_assembly_string(assembly
->identity
.architecture
, identity
->architecture
)) continue;
418 if (!compare_assembly_string(assembly
->identity
.language
, identity
->language
)) continue;
419 if (!compare_assembly_string(assembly
->identity
.pubkey_token
, identity
->pubkey_token
)) continue;
420 if (!compare_assembly_string(assembly
->identity
.version
, identity
->version
))
422 WARN("Ignoring version difference for %s (expected %s, found %s)\n",
423 debugstr_w(identity
->name
), debugstr_w(identity
->version
), debugstr_w(assembly
->identity
.version
));
431 static WCHAR
*get_assembly_source(struct assembly_entry
*assembly
)
433 WCHAR
*p
, *path
= strdupW(assembly
->filename
);
434 if (path
&& (p
= wcsrchr(path
, '.'))) *p
= 0;
438 static BOOL
strbuf_init(struct strbuf
*buf
)
442 buf
->buf
= malloc(buf
->len
* sizeof(WCHAR
));
443 return buf
->buf
!= NULL
;
446 static void strbuf_free(struct strbuf
*buf
)
452 static BOOL
strbuf_append(struct strbuf
*buf
, const WCHAR
*str
, DWORD len
)
457 if (!buf
->buf
) return FALSE
;
458 if (!str
) return TRUE
;
460 if (len
== ~0U) len
= lstrlenW(str
);
461 if (buf
->pos
+ len
+ 1 > buf
->len
)
463 new_len
= max(buf
->pos
+ len
+ 1, buf
->len
* 2);
464 new_buf
= realloc(buf
->buf
, new_len
* sizeof(WCHAR
));
474 memcpy(&buf
->buf
[buf
->pos
], str
, len
* sizeof(WCHAR
));
475 buf
->buf
[buf
->pos
+ len
] = 0;
480 static WCHAR
*lookup_expression(struct assembly_entry
*assembly
, const WCHAR
*key
)
482 WCHAR path
[MAX_PATH
];
485 if (!wcsicmp(key
, L
"runtime.system32") || !wcsicmp(key
, L
"runtime.drivers") || !wcsicmp(key
, L
"runtime.wbem"))
488 if (!wcsicmp(assembly
->identity
.architecture
, L
"x86")) csidl
= CSIDL_SYSTEMX86
;
490 if (!csidl
) csidl
= CSIDL_SYSTEM
;
492 else if (!wcsicmp(key
, L
"runtime.windows") || !wcsicmp(key
, L
"runtime.inf")) csidl
= CSIDL_WINDOWS
;
493 else if (!wcsicmp(key
, L
"runtime.programfiles"))
496 if (!wcsicmp(assembly
->identity
.architecture
, L
"x86")) csidl
= CSIDL_PROGRAM_FILESX86
;
498 if (!csidl
) csidl
= CSIDL_PROGRAM_FILES
;
500 else if (!wcsicmp(key
, L
"runtime.commonfiles"))
503 if (!wcsicmp(assembly
->identity
.architecture
, L
"x86")) csidl
= CSIDL_PROGRAM_FILES_COMMONX86
;
505 if (!csidl
) csidl
= CSIDL_PROGRAM_FILES_COMMON
;
508 else if (!wcsicmp(key
, L
"runtime.programfilesx86")) csidl
= CSIDL_PROGRAM_FILESX86
;
509 else if (!wcsicmp(key
, L
"runtime.commonfilesx86")) csidl
= CSIDL_PROGRAM_FILES_COMMONX86
;
511 else if (!wcsicmp(key
, L
"runtime.programdata")) csidl
= CSIDL_COMMON_APPDATA
;
512 else if (!wcsicmp(key
, L
"runtime.fonts")) csidl
= CSIDL_FONTS
;
516 FIXME("Unknown expression %s\n", debugstr_w(key
));
519 if (!SHGetSpecialFolderPathW(NULL
, path
, csidl
, TRUE
))
521 ERR("Failed to get folder path for %s\n", debugstr_w(key
));
525 if (!wcsicmp(key
, L
"runtime.inf")) wcscat(path
, L
"\\inf");
526 else if (!wcsicmp(key
, L
"runtime.drivers")) wcscat(path
, L
"\\drivers");
527 else if (!wcsicmp(key
, L
"runtime.wbem")) wcscat(path
, L
"\\wbem");
528 return strdupW(path
);
531 static WCHAR
*expand_expression(struct assembly_entry
*assembly
, const WCHAR
*expression
)
533 const WCHAR
*pos
, *next
;
537 if (!expression
|| !strbuf_init(&buf
)) return NULL
;
539 for (pos
= expression
; (next
= wcsstr(pos
, L
"$(")); pos
= next
+ 1)
541 strbuf_append(&buf
, pos
, next
- pos
);
543 if (!(next
= wcsstr(pos
, L
")")))
545 strbuf_append(&buf
, L
"$(", 2);
549 if (!(key
= strdupWn(pos
, next
- pos
))) goto error
;
550 value
= lookup_expression(assembly
, key
);
552 if (!value
) goto error
;
553 strbuf_append(&buf
, value
, ~0U);
557 strbuf_append(&buf
, pos
, ~0U);
561 FIXME("Couldn't resolve expression %s\n", debugstr_w(expression
));
566 static BOOL
install_files_copy(struct assembly_entry
*assembly
, const WCHAR
*source_path
, struct fileop_entry
*fileop
, BOOL dryrun
)
568 WCHAR
*target_path
, *target
, *source
= NULL
;
571 if (!(target_path
= expand_expression(assembly
, fileop
->target
))) return FALSE
;
572 if (!(target
= path_combine(target_path
, fileop
->source
))) goto error
;
573 if (!(source
= path_combine(source_path
, fileop
->source
))) goto error
;
577 if (!(ret
= PathFileExistsW(source
)))
579 ERR("Required file %s not found\n", debugstr_w(source
));
585 TRACE("Copying %s -> %s\n", debugstr_w(source
), debugstr_w(target
));
587 if (!create_parent_directory(target
))
589 ERR("Failed to create parent directory for %s\n", debugstr_w(target
));
592 if (!(ret
= CopyFileExW(source
, target
, NULL
, NULL
, NULL
, 0)))
594 ERR("Failed to copy %s to %s\n", debugstr_w(source
), debugstr_w(target
));
606 static BOOL
install_files(struct assembly_entry
*assembly
, BOOL dryrun
)
608 struct fileop_entry
*fileop
;
612 if (!(source_path
= get_assembly_source(assembly
)))
614 ERR("Failed to get assembly source directory\n");
618 LIST_FOR_EACH_ENTRY(fileop
, &assembly
->fileops
, struct fileop_entry
, entry
)
620 if (!(ret
= install_files_copy(assembly
, source_path
, fileop
, dryrun
))) break;
627 static WCHAR
*split_registry_key(WCHAR
*key
, HKEY
*root
)
632 if (!(p
= wcschr(key
, '\\'))) return NULL
;
635 if (lstrlenW(L
"HKEY_CLASSES_ROOT") == size
&& !wcsncmp(key
, L
"HKEY_CLASSES_ROOT", size
))
636 *root
= HKEY_CLASSES_ROOT
;
637 else if (lstrlenW(L
"HKEY_CURRENT_CONFIG") == size
&& !wcsncmp(key
, L
"HKEY_CURRENT_CONFIG", size
))
638 *root
= HKEY_CURRENT_CONFIG
;
639 else if (lstrlenW(L
"HKEY_CURRENT_USER") == size
&& !wcsncmp(key
, L
"HKEY_CURRENT_USER", size
))
640 *root
= HKEY_CURRENT_USER
;
641 else if (lstrlenW(L
"HKEY_LOCAL_MACHINE") == size
&& !wcsncmp(key
, L
"HKEY_LOCAL_MACHINE", size
))
642 *root
= HKEY_LOCAL_MACHINE
;
643 else if (lstrlenW(L
"HKEY_USERS") == size
&& !wcsncmp(key
, L
"HKEY_USERS", size
))
647 FIXME("Unknown root key %s\n", debugstr_wn(key
, size
));
654 static BOOL
install_registry_string(struct assembly_entry
*assembly
, HKEY key
, struct registrykv_entry
*registrykv
, DWORD type
, BOOL dryrun
)
657 WCHAR
*value
= expand_expression(assembly
, registrykv
->value
);
660 if (registrykv
->value
&& !value
)
663 value_size
= value
? (lstrlenW(value
) + 1) * sizeof(WCHAR
) : 0;
664 if (!dryrun
&& RegSetValueExW(key
, registrykv
->name
, 0, type
, (void *)value
, value_size
))
666 ERR("Failed to set registry key %s\n", debugstr_w(registrykv
->name
));
674 static WCHAR
*parse_multisz(const WCHAR
*input
, DWORD
*size
)
676 const WCHAR
*pos
, *next
;
680 if (!input
|| !input
[0] || !strbuf_init(&buf
)) return NULL
;
682 for (pos
= input
; pos
[0] == '"'; pos
++)
685 if (!(next
= wcsstr(pos
, L
"\""))) goto error
;
686 strbuf_append(&buf
, pos
, next
- pos
);
687 strbuf_append(&buf
, L
"", ARRAY_SIZE(L
""));
693 FIXME("Error while parsing REG_MULTI_SZ string: Expected comma but got '%c'\n", pos
[0]);
700 FIXME("Error while parsing REG_MULTI_SZ string: Garbage at end of string\n");
704 strbuf_append(&buf
, L
"", ARRAY_SIZE(L
""));
705 *size
= buf
.pos
* sizeof(WCHAR
);
713 static BOOL
install_registry_multisz(struct assembly_entry
*assembly
, HKEY key
, struct registrykv_entry
*registrykv
, BOOL dryrun
)
716 WCHAR
*value
= parse_multisz(registrykv
->value
, &value_size
);
719 if (registrykv
->value
&& registrykv
->value
[0] && !value
)
722 if (!dryrun
&& RegSetValueExW(key
, registrykv
->name
, 0, REG_MULTI_SZ
, (void *)value
, value_size
))
724 ERR("Failed to set registry key %s\n", debugstr_w(registrykv
->name
));
732 static BOOL
install_registry_dword(struct assembly_entry
*assembly
, HKEY key
, struct registrykv_entry
*registrykv
, BOOL dryrun
)
734 DWORD value
= registrykv
->value_type
? wcstoul(registrykv
->value_type
, NULL
, 16) : 0;
737 if (!dryrun
&& RegSetValueExW(key
, registrykv
->name
, 0, REG_DWORD
, (void *)&value
, sizeof(value
)))
739 ERR("Failed to set registry key %s\n", debugstr_w(registrykv
->name
));
746 static BYTE
*parse_hex(const WCHAR
*input
, DWORD
*size
)
748 WCHAR number
[3] = {0, 0, 0};
753 if (!input
) return NULL
;
754 length
= lstrlenW(input
);
755 if (length
& 1) return NULL
;
758 if (!(output
= malloc(length
))) return NULL
;
759 for (p
= output
; *input
; input
+= 2)
761 number
[0] = input
[0];
762 number
[1] = input
[1];
763 *p
++ = wcstoul(number
, 0, 16);
769 static BOOL
install_registry_binary(struct assembly_entry
*assembly
, HKEY key
, struct registrykv_entry
*registrykv
, BOOL dryrun
)
772 BYTE
*value
= parse_hex(registrykv
->value
, &value_size
);
775 if (registrykv
->value
&& !value
)
778 if (!dryrun
&& RegSetValueExW(key
, registrykv
->name
, 0, REG_BINARY
, value
, value_size
))
780 ERR("Failed to set registry key %s\n", debugstr_w(registrykv
->name
));
788 static BOOL
install_registry_value(struct assembly_entry
*assembly
, HKEY key
, struct registrykv_entry
*registrykv
, BOOL dryrun
)
790 TRACE("Setting registry key %s = %s\n", debugstr_w(registrykv
->name
), debugstr_w(registrykv
->value
));
792 if (!wcscmp(registrykv
->value_type
, L
"REG_SZ"))
793 return install_registry_string(assembly
, key
, registrykv
, REG_SZ
, dryrun
);
794 if (!wcscmp(registrykv
->value_type
, L
"REG_EXPAND_SZ"))
795 return install_registry_string(assembly
, key
, registrykv
, REG_EXPAND_SZ
, dryrun
);
796 if (!wcscmp(registrykv
->value_type
, L
"REG_MULTI_SZ"))
797 return install_registry_multisz(assembly
, key
, registrykv
, dryrun
);
798 if (!wcscmp(registrykv
->value_type
, L
"REG_DWORD"))
799 return install_registry_dword(assembly
, key
, registrykv
, dryrun
);
800 if (!wcscmp(registrykv
->value_type
, L
"REG_BINARY"))
801 return install_registry_binary(assembly
, key
, registrykv
, dryrun
);
803 FIXME("Unsupported registry value type %s\n", debugstr_w(registrykv
->value_type
));
807 static BOOL
install_registry(struct assembly_entry
*assembly
, BOOL dryrun
)
809 struct registryop_entry
*registryop
;
810 struct registrykv_entry
*registrykv
;
813 REGSAM sam
= KEY_ALL_ACCESS
;
817 if (!wcscmp(assembly
->identity
.architecture
, L
"x86")) sam
|= KEY_WOW64_32KEY
;
820 LIST_FOR_EACH_ENTRY(registryop
, &assembly
->registryops
, struct registryop_entry
, entry
)
822 if (!(path
= split_registry_key(registryop
->key
, &root
)))
828 TRACE("Processing registry key %s\n", debugstr_w(registryop
->key
));
830 if (!dryrun
&& RegCreateKeyExW(root
, path
, 0, NULL
, 0, sam
, NULL
, &subkey
, NULL
))
832 ERR("Failed to open registry key %s\n", debugstr_w(registryop
->key
));
837 LIST_FOR_EACH_ENTRY(registrykv
, ®istryop
->keyvalues
, struct registrykv_entry
, entry
)
839 if (!(ret
= install_registry_value(assembly
, subkey
, registrykv
, dryrun
))) break;
842 if (!dryrun
) RegCloseKey(subkey
);
849 static BOOL
install_assembly(struct list
*manifest_list
, struct assembly_identity
*identity
, BOOL dryrun
)
851 struct dependency_entry
*dependency
;
852 struct assembly_entry
*assembly
;
855 if (!(assembly
= lookup_assembly(manifest_list
, identity
)))
857 FIXME("Assembly %s not found\n", debugstr_w(identity
->name
));
861 name
= assembly
->identity
.name
;
863 if (assembly
->status
== ASSEMBLY_STATUS_INSTALLED
)
865 TRACE("Assembly %s already installed\n", debugstr_w(name
));
868 if (assembly
->status
== ASSEMBLY_STATUS_IN_PROGRESS
)
870 ERR("Assembly %s caused circular dependency\n", debugstr_w(name
));
875 if (!wcscmp(assembly
->identity
.architecture
, L
"amd64"))
877 ERR("Cannot install amd64 assembly in 32-bit prefix\n");
882 assembly
->status
= ASSEMBLY_STATUS_IN_PROGRESS
;
884 LIST_FOR_EACH_ENTRY(dependency
, &assembly
->dependencies
, struct dependency_entry
, entry
)
886 if (!install_assembly(manifest_list
, &dependency
->identity
, dryrun
)) return FALSE
;
889 TRACE("Installing assembly %s%s\n", debugstr_w(name
), dryrun
? " (dryrun)" : "");
891 if (!install_files(assembly
, dryrun
))
893 ERR("Failed to install all files for %s\n", debugstr_w(name
));
897 if (!install_registry(assembly
, dryrun
))
899 ERR("Failed to install registry keys for %s\n", debugstr_w(name
));
903 TRACE("Installation of %s finished\n", debugstr_w(name
));
905 assembly
->status
= ASSEMBLY_STATUS_INSTALLED
;
909 static BOOL
install_updates(struct installer_state
*state
, BOOL dryrun
)
911 struct dependency_entry
*dependency
;
912 LIST_FOR_EACH_ENTRY(dependency
, &state
->updates
, struct dependency_entry
, entry
)
914 if (!install_assembly(&state
->assemblies
, &dependency
->identity
, dryrun
))
916 ERR("Failed to install update %s\n", debugstr_w(dependency
->identity
.name
));
923 static void set_assembly_status(struct list
*manifest_list
, DWORD status
)
925 struct assembly_entry
*assembly
;
926 LIST_FOR_EACH_ENTRY(assembly
, manifest_list
, struct assembly_entry
, entry
)
928 assembly
->status
= status
;
932 static BOOL
install_msu(const WCHAR
*filename
, struct installer_state
*state
)
934 const WCHAR
*temp_path
;
935 WIN32_FIND_DATAW data
;
940 list_init(&state
->tempdirs
);
941 list_init(&state
->assemblies
);
942 list_init(&state
->updates
);
945 TRACE("Processing msu file %s\n", debugstr_w(filename
));
947 if (!(temp_path
= create_temp_directory(state
))) return FALSE
;
948 if (!extract_cabinet(filename
, temp_path
))
950 ERR("Failed to extract %s\n", debugstr_w(filename
));
954 /* load all manifests from contained cabinet archives */
955 if (!(path
= path_combine(temp_path
, L
"*.cab"))) goto done
;
956 search
= FindFirstFileW(path
, &data
);
959 if (search
!= INVALID_HANDLE_VALUE
)
963 if (data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) continue;
964 if (!wcsicmp(data
.cFileName
, L
"WSUSSCAN.cab")) continue;
965 if (!(path
= path_combine(temp_path
, data
.cFileName
))) continue;
966 if (!load_assemblies_from_cab(path
, state
))
967 ERR("Failed to load all manifests from %s, ignoring\n", debugstr_w(path
));
970 while (FindNextFileW(search
, &data
));
974 /* load all update descriptions */
975 if (!(path
= path_combine(temp_path
, L
"*.xml"))) goto done
;
976 search
= FindFirstFileW(path
, &data
);
979 if (search
!= INVALID_HANDLE_VALUE
)
983 if (data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) continue;
984 if (!(path
= path_combine(temp_path
, data
.cFileName
))) continue;
985 if (!load_update(path
, &state
->updates
))
986 ERR("Failed to load all updates from %s, ignoring\n", debugstr_w(path
));
989 while (FindNextFileW(search
, &data
));
993 /* dump package information (for debugging) */
996 struct dependency_entry
*dependency
;
997 struct assembly_entry
*assembly
;
999 TRACE("List of updates:\n");
1000 LIST_FOR_EACH_ENTRY(dependency
, &state
->updates
, struct dependency_entry
, entry
)
1001 TRACE(" * %s\n", debugstr_w(dependency
->identity
.name
));
1003 TRACE("List of manifests (with dependencies):\n");
1004 LIST_FOR_EACH_ENTRY(assembly
, &state
->assemblies
, struct assembly_entry
, entry
)
1006 TRACE(" * %s\n", debugstr_w(assembly
->identity
.name
));
1007 LIST_FOR_EACH_ENTRY(dependency
, &assembly
->dependencies
, struct dependency_entry
, entry
)
1008 TRACE(" -> %s\n", debugstr_w(dependency
->identity
.name
));
1012 if (list_empty(&state
->updates
))
1014 ERR("No updates found, probably incompatible MSU file format?\n");
1018 /* perform dry run */
1019 set_assembly_status(&state
->assemblies
, ASSEMBLY_STATUS_NONE
);
1020 if (!install_updates(state
, TRUE
))
1022 ERR("Dry run failed, aborting installation\n");
1027 set_assembly_status(&state
->assemblies
, ASSEMBLY_STATUS_NONE
);
1028 if (!install_updates(state
, FALSE
))
1030 ERR("Installation failed\n");
1034 TRACE("Installation finished\n");
1038 installer_cleanup(state
);
1042 static void restart_as_x86_64(void)
1044 WCHAR filename
[MAX_PATH
];
1045 PROCESS_INFORMATION pi
;
1047 DWORD exit_code
= 1;
1050 memset(&si
, 0, sizeof(si
));
1052 GetSystemDirectoryW( filename
, MAX_PATH
);
1053 wcscat( filename
, L
"\\wusa.exe" );
1055 Wow64DisableWow64FsRedirection(&redir
);
1056 if (CreateProcessW(filename
, GetCommandLineW(), NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
))
1058 TRACE("Restarting %s\n", wine_dbgstr_w(filename
));
1059 WaitForSingleObject(pi
.hProcess
, INFINITE
);
1060 GetExitCodeProcess(pi
.hProcess
, &exit_code
);
1061 CloseHandle(pi
.hProcess
);
1062 CloseHandle(pi
.hThread
);
1064 else ERR("Failed to restart 64-bit %s, err %lu\n", wine_dbgstr_w(filename
), GetLastError());
1065 Wow64RevertWow64FsRedirection(redir
);
1067 ExitProcess(exit_code
);
1070 int __cdecl
wmain(int argc
, WCHAR
*argv
[])
1072 struct installer_state state
;
1073 const WCHAR
*filename
= NULL
;
1077 if (IsWow64Process( GetCurrentProcess(), &is_wow64
) && is_wow64
) restart_as_x86_64();
1079 state
.norestart
= FALSE
;
1080 state
.quiet
= FALSE
;
1084 TRACE("Command line:");
1085 for (i
= 0; i
< argc
; i
++)
1086 TRACE(" %s", wine_dbgstr_w(argv
[i
]));
1090 for (i
= 1; i
< argc
; i
++)
1092 if (argv
[i
][0] == '/')
1094 if (!wcscmp(argv
[i
], L
"/norestart"))
1095 state
.norestart
= TRUE
;
1096 else if (!wcscmp(argv
[i
], L
"/quiet"))
1099 FIXME("Unknown option: %s\n", wine_dbgstr_w(argv
[i
]));
1104 FIXME("Unknown option: %s\n", wine_dbgstr_w(argv
[i
]));
1109 FIXME("Missing filename argument\n");
1113 return !install_msu(filename
, &state
);