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
= wcsdup(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
= wcsdup(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 BOOL
assembly_is_wow64(const struct assembly_entry
*assembly
)
483 return !wcsicmp(assembly
->identity
.architecture
, L
"x86") || !wcsicmp(assembly
->identity
.architecture
, L
"wow64");
488 static WCHAR
*lookup_expression(struct assembly_entry
*assembly
, const WCHAR
*key
)
490 WCHAR path
[MAX_PATH
];
493 if (!wcsicmp(key
, L
"runtime.system32") || !wcsicmp(key
, L
"runtime.drivers") || !wcsicmp(key
, L
"runtime.wbem"))
495 if (assembly_is_wow64(assembly
)) csidl
= CSIDL_SYSTEMX86
;
496 else csidl
= CSIDL_SYSTEM
;
498 else if (!wcsicmp(key
, L
"runtime.windows") || !wcsicmp(key
, L
"runtime.inf")) csidl
= CSIDL_WINDOWS
;
499 else if (!wcsicmp(key
, L
"runtime.programfiles"))
501 if (assembly_is_wow64(assembly
)) csidl
= CSIDL_PROGRAM_FILESX86
;
502 else csidl
= CSIDL_PROGRAM_FILES
;
504 else if (!wcsicmp(key
, L
"runtime.commonfiles"))
506 if (assembly_is_wow64(assembly
)) csidl
= CSIDL_PROGRAM_FILES_COMMONX86
;
507 else csidl
= CSIDL_PROGRAM_FILES_COMMON
;
510 else if (!wcsicmp(key
, L
"runtime.programfilesx86")) csidl
= CSIDL_PROGRAM_FILESX86
;
511 else if (!wcsicmp(key
, L
"runtime.commonfilesx86")) csidl
= CSIDL_PROGRAM_FILES_COMMONX86
;
513 else if (!wcsicmp(key
, L
"runtime.programdata")) csidl
= CSIDL_COMMON_APPDATA
;
514 else if (!wcsicmp(key
, L
"runtime.fonts")) csidl
= CSIDL_FONTS
;
518 FIXME("Unknown expression %s\n", debugstr_w(key
));
521 if (!SHGetSpecialFolderPathW(NULL
, path
, csidl
, TRUE
))
523 ERR("Failed to get folder path for %s\n", debugstr_w(key
));
527 if (!wcsicmp(key
, L
"runtime.inf")) wcscat(path
, L
"\\inf");
528 else if (!wcsicmp(key
, L
"runtime.drivers")) wcscat(path
, L
"\\drivers");
529 else if (!wcsicmp(key
, L
"runtime.wbem")) wcscat(path
, L
"\\wbem");
533 static WCHAR
*expand_expression(struct assembly_entry
*assembly
, const WCHAR
*expression
)
535 const WCHAR
*pos
, *next
;
539 if (!expression
|| !strbuf_init(&buf
)) return NULL
;
541 for (pos
= expression
; (next
= wcsstr(pos
, L
"$(")); pos
= next
+ 1)
543 strbuf_append(&buf
, pos
, next
- pos
);
545 if (!(next
= wcsstr(pos
, L
")")))
547 strbuf_append(&buf
, L
"$(", 2);
551 if (!(key
= strdupWn(pos
, next
- pos
))) goto error
;
552 value
= lookup_expression(assembly
, key
);
554 if (!value
) goto error
;
555 strbuf_append(&buf
, value
, ~0U);
559 strbuf_append(&buf
, pos
, ~0U);
563 FIXME("Couldn't resolve expression %s\n", debugstr_w(expression
));
568 static BOOL
install_files_copy(struct assembly_entry
*assembly
, const WCHAR
*source_path
, struct fileop_entry
*fileop
, BOOL dryrun
)
570 WCHAR
*target_path
, *target
, *source
= NULL
;
573 if (!(target_path
= expand_expression(assembly
, fileop
->target
))) return FALSE
;
574 if (!(target
= path_combine(target_path
, fileop
->source
))) goto error
;
575 if (!(source
= path_combine(source_path
, fileop
->source
))) goto error
;
579 if (!(ret
= PathFileExistsW(source
)))
581 ERR("Required file %s not found\n", debugstr_w(source
));
587 TRACE("Copying %s -> %s\n", debugstr_w(source
), debugstr_w(target
));
589 if (!create_parent_directory(target
))
591 ERR("Failed to create parent directory for %s\n", debugstr_w(target
));
594 if (!(ret
= CopyFileExW(source
, target
, NULL
, NULL
, NULL
, 0)))
596 ERR("Failed to copy %s to %s\n", debugstr_w(source
), debugstr_w(target
));
608 static BOOL
install_files(struct assembly_entry
*assembly
, BOOL dryrun
)
610 struct fileop_entry
*fileop
;
614 if (!(source_path
= get_assembly_source(assembly
)))
616 ERR("Failed to get assembly source directory\n");
620 LIST_FOR_EACH_ENTRY(fileop
, &assembly
->fileops
, struct fileop_entry
, entry
)
622 if (!(ret
= install_files_copy(assembly
, source_path
, fileop
, dryrun
))) break;
629 static WCHAR
*split_registry_key(WCHAR
*key
, HKEY
*root
)
634 if (!(p
= wcschr(key
, '\\'))) return NULL
;
637 if (lstrlenW(L
"HKEY_CLASSES_ROOT") == size
&& !wcsncmp(key
, L
"HKEY_CLASSES_ROOT", size
))
638 *root
= HKEY_CLASSES_ROOT
;
639 else if (lstrlenW(L
"HKEY_CURRENT_CONFIG") == size
&& !wcsncmp(key
, L
"HKEY_CURRENT_CONFIG", size
))
640 *root
= HKEY_CURRENT_CONFIG
;
641 else if (lstrlenW(L
"HKEY_CURRENT_USER") == size
&& !wcsncmp(key
, L
"HKEY_CURRENT_USER", size
))
642 *root
= HKEY_CURRENT_USER
;
643 else if (lstrlenW(L
"HKEY_LOCAL_MACHINE") == size
&& !wcsncmp(key
, L
"HKEY_LOCAL_MACHINE", size
))
644 *root
= HKEY_LOCAL_MACHINE
;
645 else if (lstrlenW(L
"HKEY_USERS") == size
&& !wcsncmp(key
, L
"HKEY_USERS", size
))
649 FIXME("Unknown root key %s\n", debugstr_wn(key
, size
));
656 static BOOL
install_registry_string(struct assembly_entry
*assembly
, HKEY key
, struct registrykv_entry
*registrykv
, DWORD type
, BOOL dryrun
)
659 WCHAR
*value
= expand_expression(assembly
, registrykv
->value
);
662 if (registrykv
->value
&& !value
)
665 value_size
= value
? (lstrlenW(value
) + 1) * sizeof(WCHAR
) : 0;
666 if (!dryrun
&& RegSetValueExW(key
, registrykv
->name
, 0, type
, (void *)value
, value_size
))
668 ERR("Failed to set registry key %s\n", debugstr_w(registrykv
->name
));
676 static WCHAR
*parse_multisz(const WCHAR
*input
, DWORD
*size
)
678 const WCHAR
*pos
, *next
;
682 if (!input
|| !input
[0] || !strbuf_init(&buf
)) return NULL
;
684 for (pos
= input
; pos
[0] == '"'; pos
++)
687 if (!(next
= wcsstr(pos
, L
"\""))) goto error
;
688 strbuf_append(&buf
, pos
, next
- pos
);
689 strbuf_append(&buf
, L
"", ARRAY_SIZE(L
""));
695 FIXME("Error while parsing REG_MULTI_SZ string: Expected comma but got '%c'\n", pos
[0]);
702 FIXME("Error while parsing REG_MULTI_SZ string: Garbage at end of string\n");
706 strbuf_append(&buf
, L
"", ARRAY_SIZE(L
""));
707 *size
= buf
.pos
* sizeof(WCHAR
);
715 static BOOL
install_registry_multisz(struct assembly_entry
*assembly
, HKEY key
, struct registrykv_entry
*registrykv
, BOOL dryrun
)
718 WCHAR
*value
= parse_multisz(registrykv
->value
, &value_size
);
721 if (registrykv
->value
&& registrykv
->value
[0] && !value
)
724 if (!dryrun
&& RegSetValueExW(key
, registrykv
->name
, 0, REG_MULTI_SZ
, (void *)value
, value_size
))
726 ERR("Failed to set registry key %s\n", debugstr_w(registrykv
->name
));
734 static BOOL
install_registry_dword(struct assembly_entry
*assembly
, HKEY key
, struct registrykv_entry
*registrykv
, BOOL dryrun
)
736 DWORD value
= registrykv
->value_type
? wcstoul(registrykv
->value_type
, NULL
, 16) : 0;
739 if (!dryrun
&& RegSetValueExW(key
, registrykv
->name
, 0, REG_DWORD
, (void *)&value
, sizeof(value
)))
741 ERR("Failed to set registry key %s\n", debugstr_w(registrykv
->name
));
748 static BYTE
*parse_hex(const WCHAR
*input
, DWORD
*size
)
750 WCHAR number
[3] = {0, 0, 0};
755 if (!input
) return NULL
;
756 length
= lstrlenW(input
);
757 if (length
& 1) return NULL
;
760 if (!(output
= malloc(length
))) return NULL
;
761 for (p
= output
; *input
; input
+= 2)
763 number
[0] = input
[0];
764 number
[1] = input
[1];
765 *p
++ = wcstoul(number
, 0, 16);
771 static BOOL
install_registry_binary(struct assembly_entry
*assembly
, HKEY key
, struct registrykv_entry
*registrykv
, BOOL dryrun
)
774 BYTE
*value
= parse_hex(registrykv
->value
, &value_size
);
777 if (registrykv
->value
&& !value
)
780 if (!dryrun
&& RegSetValueExW(key
, registrykv
->name
, 0, REG_BINARY
, value
, value_size
))
782 ERR("Failed to set registry key %s\n", debugstr_w(registrykv
->name
));
790 static BOOL
install_registry_value(struct assembly_entry
*assembly
, HKEY key
, struct registrykv_entry
*registrykv
, BOOL dryrun
)
792 TRACE("Setting registry key %s = %s\n", debugstr_w(registrykv
->name
), debugstr_w(registrykv
->value
));
794 if (!wcscmp(registrykv
->value_type
, L
"REG_SZ"))
795 return install_registry_string(assembly
, key
, registrykv
, REG_SZ
, dryrun
);
796 if (!wcscmp(registrykv
->value_type
, L
"REG_EXPAND_SZ"))
797 return install_registry_string(assembly
, key
, registrykv
, REG_EXPAND_SZ
, dryrun
);
798 if (!wcscmp(registrykv
->value_type
, L
"REG_MULTI_SZ"))
799 return install_registry_multisz(assembly
, key
, registrykv
, dryrun
);
800 if (!wcscmp(registrykv
->value_type
, L
"REG_DWORD"))
801 return install_registry_dword(assembly
, key
, registrykv
, dryrun
);
802 if (!wcscmp(registrykv
->value_type
, L
"REG_BINARY"))
803 return install_registry_binary(assembly
, key
, registrykv
, dryrun
);
805 FIXME("Unsupported registry value type %s\n", debugstr_w(registrykv
->value_type
));
809 static BOOL
install_registry(struct assembly_entry
*assembly
, BOOL dryrun
)
811 struct registryop_entry
*registryop
;
812 struct registrykv_entry
*registrykv
;
815 REGSAM sam
= KEY_ALL_ACCESS
;
819 if (!wcscmp(assembly
->identity
.architecture
, L
"x86")) sam
|= KEY_WOW64_32KEY
;
822 LIST_FOR_EACH_ENTRY(registryop
, &assembly
->registryops
, struct registryop_entry
, entry
)
824 if (!(path
= split_registry_key(registryop
->key
, &root
)))
830 TRACE("Processing registry key %s\n", debugstr_w(registryop
->key
));
832 if (!dryrun
&& RegCreateKeyExW(root
, path
, 0, NULL
, 0, sam
, NULL
, &subkey
, NULL
))
834 ERR("Failed to open registry key %s\n", debugstr_w(registryop
->key
));
839 LIST_FOR_EACH_ENTRY(registrykv
, ®istryop
->keyvalues
, struct registrykv_entry
, entry
)
841 if (!(ret
= install_registry_value(assembly
, subkey
, registrykv
, dryrun
))) break;
844 if (!dryrun
) RegCloseKey(subkey
);
851 static BOOL
install_assembly(struct list
*manifest_list
, struct assembly_identity
*identity
, BOOL dryrun
)
853 struct dependency_entry
*dependency
;
854 struct assembly_entry
*assembly
;
857 if (!(assembly
= lookup_assembly(manifest_list
, identity
)))
859 FIXME("Assembly %s not found\n", debugstr_w(identity
->name
));
863 name
= assembly
->identity
.name
;
865 if (assembly
->status
== ASSEMBLY_STATUS_INSTALLED
)
867 TRACE("Assembly %s already installed\n", debugstr_w(name
));
870 if (assembly
->status
== ASSEMBLY_STATUS_IN_PROGRESS
)
872 ERR("Assembly %s caused circular dependency\n", debugstr_w(name
));
877 if (!wcscmp(assembly
->identity
.architecture
, L
"amd64"))
879 ERR("Cannot install amd64 assembly in 32-bit prefix\n");
884 assembly
->status
= ASSEMBLY_STATUS_IN_PROGRESS
;
886 LIST_FOR_EACH_ENTRY(dependency
, &assembly
->dependencies
, struct dependency_entry
, entry
)
888 if (!install_assembly(manifest_list
, &dependency
->identity
, dryrun
)) return FALSE
;
891 TRACE("Installing assembly %s%s\n", debugstr_w(name
), dryrun
? " (dryrun)" : "");
893 if (!install_files(assembly
, dryrun
))
895 ERR("Failed to install all files for %s\n", debugstr_w(name
));
899 if (!install_registry(assembly
, dryrun
))
901 ERR("Failed to install registry keys for %s\n", debugstr_w(name
));
905 TRACE("Installation of %s finished\n", debugstr_w(name
));
907 assembly
->status
= ASSEMBLY_STATUS_INSTALLED
;
911 static BOOL
install_updates(struct installer_state
*state
, BOOL dryrun
)
913 struct dependency_entry
*dependency
;
914 LIST_FOR_EACH_ENTRY(dependency
, &state
->updates
, struct dependency_entry
, entry
)
916 if (!install_assembly(&state
->assemblies
, &dependency
->identity
, dryrun
))
918 ERR("Failed to install update %s\n", debugstr_w(dependency
->identity
.name
));
925 static void set_assembly_status(struct list
*manifest_list
, DWORD status
)
927 struct assembly_entry
*assembly
;
928 LIST_FOR_EACH_ENTRY(assembly
, manifest_list
, struct assembly_entry
, entry
)
930 assembly
->status
= status
;
934 static BOOL
install_msu(const WCHAR
*filename
, struct installer_state
*state
)
936 const WCHAR
*temp_path
;
937 WIN32_FIND_DATAW data
;
942 list_init(&state
->tempdirs
);
943 list_init(&state
->assemblies
);
944 list_init(&state
->updates
);
947 TRACE("Processing msu file %s\n", debugstr_w(filename
));
949 if (!(temp_path
= create_temp_directory(state
))) return FALSE
;
950 if (!extract_cabinet(filename
, temp_path
))
952 ERR("Failed to extract %s\n", debugstr_w(filename
));
956 /* load all manifests from contained cabinet archives */
957 if (!(path
= path_combine(temp_path
, L
"*.cab"))) goto done
;
958 search
= FindFirstFileW(path
, &data
);
961 if (search
!= INVALID_HANDLE_VALUE
)
965 if (data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) continue;
966 if (!wcsicmp(data
.cFileName
, L
"WSUSSCAN.cab")) continue;
967 if (!(path
= path_combine(temp_path
, data
.cFileName
))) continue;
968 if (!load_assemblies_from_cab(path
, state
))
969 ERR("Failed to load all manifests from %s, ignoring\n", debugstr_w(path
));
972 while (FindNextFileW(search
, &data
));
976 /* load all update descriptions */
977 if (!(path
= path_combine(temp_path
, L
"*.xml"))) goto done
;
978 search
= FindFirstFileW(path
, &data
);
981 if (search
!= INVALID_HANDLE_VALUE
)
985 if (data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) continue;
986 if (!(path
= path_combine(temp_path
, data
.cFileName
))) continue;
987 if (!load_update(path
, &state
->updates
))
988 ERR("Failed to load all updates from %s, ignoring\n", debugstr_w(path
));
991 while (FindNextFileW(search
, &data
));
995 /* dump package information (for debugging) */
998 struct dependency_entry
*dependency
;
999 struct assembly_entry
*assembly
;
1001 TRACE("List of updates:\n");
1002 LIST_FOR_EACH_ENTRY(dependency
, &state
->updates
, struct dependency_entry
, entry
)
1003 TRACE(" * %s\n", debugstr_w(dependency
->identity
.name
));
1005 TRACE("List of manifests (with dependencies):\n");
1006 LIST_FOR_EACH_ENTRY(assembly
, &state
->assemblies
, struct assembly_entry
, entry
)
1008 TRACE(" * %s\n", debugstr_w(assembly
->identity
.name
));
1009 LIST_FOR_EACH_ENTRY(dependency
, &assembly
->dependencies
, struct dependency_entry
, entry
)
1010 TRACE(" -> %s\n", debugstr_w(dependency
->identity
.name
));
1014 if (list_empty(&state
->updates
))
1016 ERR("No updates found, probably incompatible MSU file format?\n");
1020 /* perform dry run */
1021 set_assembly_status(&state
->assemblies
, ASSEMBLY_STATUS_NONE
);
1022 if (!install_updates(state
, TRUE
))
1024 ERR("Dry run failed, aborting installation\n");
1029 set_assembly_status(&state
->assemblies
, ASSEMBLY_STATUS_NONE
);
1030 if (!install_updates(state
, FALSE
))
1032 ERR("Installation failed\n");
1036 TRACE("Installation finished\n");
1040 installer_cleanup(state
);
1044 static void restart_as_x86_64(void)
1046 WCHAR filename
[MAX_PATH
];
1047 PROCESS_INFORMATION pi
;
1049 DWORD exit_code
= 1;
1052 memset(&si
, 0, sizeof(si
));
1054 GetSystemDirectoryW( filename
, MAX_PATH
);
1055 wcscat( filename
, L
"\\wusa.exe" );
1057 Wow64DisableWow64FsRedirection(&redir
);
1058 if (CreateProcessW(filename
, GetCommandLineW(), NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
))
1060 TRACE("Restarting %s\n", wine_dbgstr_w(filename
));
1061 WaitForSingleObject(pi
.hProcess
, INFINITE
);
1062 GetExitCodeProcess(pi
.hProcess
, &exit_code
);
1063 CloseHandle(pi
.hProcess
);
1064 CloseHandle(pi
.hThread
);
1066 else ERR("Failed to restart 64-bit %s, err %lu\n", wine_dbgstr_w(filename
), GetLastError());
1067 Wow64RevertWow64FsRedirection(redir
);
1069 ExitProcess(exit_code
);
1072 int __cdecl
wmain(int argc
, WCHAR
*argv
[])
1074 struct installer_state state
;
1075 const WCHAR
*filename
= NULL
;
1079 if (IsWow64Process( GetCurrentProcess(), &is_wow64
) && is_wow64
) restart_as_x86_64();
1081 state
.norestart
= FALSE
;
1082 state
.quiet
= FALSE
;
1086 TRACE("Command line:");
1087 for (i
= 0; i
< argc
; i
++)
1088 TRACE(" %s", wine_dbgstr_w(argv
[i
]));
1092 for (i
= 1; i
< argc
; i
++)
1094 if (argv
[i
][0] == '/')
1096 if (!wcscmp(argv
[i
], L
"/norestart"))
1097 state
.norestart
= TRUE
;
1098 else if (!wcscmp(argv
[i
], L
"/quiet"))
1101 FIXME("Unknown option: %s\n", wine_dbgstr_w(argv
[i
]));
1106 FIXME("Unknown option: %s\n", wine_dbgstr_w(argv
[i
]));
1111 FIXME("Missing filename argument\n");
1115 return !install_msu(filename
, &state
);