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
26 #include "wine/debug.h"
27 #include "wine/list.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(wusa
);
39 struct installer_tempdir
45 struct installer_state
50 struct list assemblies
;
54 static void * CDECL
cabinet_alloc(ULONG cb
)
56 return heap_alloc(cb
);
59 static void CDECL
cabinet_free(void *pv
)
64 static INT_PTR CDECL
cabinet_open(char *pszFile
, int oflag
, int pmode
)
67 DWORD dwShareMode
= 0;
68 DWORD dwCreateDisposition
= OPEN_EXISTING
;
70 switch (oflag
& _O_ACCMODE
)
73 dwAccess
= GENERIC_READ
;
74 dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_DELETE
;
77 dwAccess
= GENERIC_WRITE
;
78 dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
;
81 dwAccess
= GENERIC_READ
| GENERIC_WRITE
;
82 dwShareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
;
86 if ((oflag
& (_O_CREAT
| _O_EXCL
)) == (_O_CREAT
| _O_EXCL
))
87 dwCreateDisposition
= CREATE_NEW
;
88 else if (oflag
& _O_CREAT
)
89 dwCreateDisposition
= CREATE_ALWAYS
;
91 return (INT_PTR
)CreateFileA(pszFile
, dwAccess
, dwShareMode
, NULL
, dwCreateDisposition
, 0, NULL
);
94 static UINT CDECL
cabinet_read(INT_PTR hf
, void *pv
, UINT cb
)
96 HANDLE handle
= (HANDLE
)hf
;
99 if (ReadFile(handle
, pv
, cb
, &read
, NULL
))
105 static UINT CDECL
cabinet_write(INT_PTR hf
, void *pv
, UINT cb
)
107 HANDLE handle
= (HANDLE
)hf
;
110 if (WriteFile(handle
, pv
, cb
, &written
, NULL
))
116 static int CDECL
cabinet_close(INT_PTR hf
)
118 HANDLE handle
= (HANDLE
)hf
;
119 return CloseHandle(handle
) ? 0 : -1;
122 static LONG CDECL
cabinet_seek(INT_PTR hf
, LONG dist
, int seektype
)
124 HANDLE handle
= (HANDLE
)hf
;
125 /* flags are compatible and so are passed straight through */
126 return SetFilePointer(handle
, dist
, NULL
, seektype
);
129 static WCHAR
*path_combine(const WCHAR
*path
, const WCHAR
*filename
)
134 if (!path
|| !filename
) return NULL
;
135 length
= lstrlenW(path
) + lstrlenW(filename
) + 2;
136 if (!(result
= heap_alloc(length
* sizeof(WCHAR
)))) return NULL
;
138 lstrcpyW(result
, path
);
139 if (result
[0] && result
[lstrlenW(result
) - 1] != '\\') lstrcatW(result
, L
"\\");
140 lstrcatW(result
, filename
);
144 static WCHAR
*get_uncompressed_path(PFDINOTIFICATION pfdin
)
146 WCHAR
*file
= strdupAtoW(pfdin
->psz1
);
147 WCHAR
*path
= path_combine(pfdin
->pv
, file
);
152 static BOOL
is_directory(const WCHAR
*path
)
154 DWORD attrs
= GetFileAttributesW(path
);
155 if (attrs
== INVALID_FILE_ATTRIBUTES
) return FALSE
;
156 return (attrs
& FILE_ATTRIBUTE_DIRECTORY
) != 0;
159 static BOOL
create_directory(const WCHAR
*path
)
161 if (is_directory(path
)) return TRUE
;
162 if (CreateDirectoryW(path
, NULL
)) return TRUE
;
163 return (GetLastError() == ERROR_ALREADY_EXISTS
);
166 static BOOL
create_parent_directory(const WCHAR
*filename
)
168 WCHAR
*p
, *path
= strdupW(filename
);
171 if (!path
) return FALSE
;
172 if (!PathRemoveFileSpecW(path
)) goto done
;
173 if (is_directory(path
))
179 for (p
= path
; *p
; p
++)
181 if (*p
!= '\\') continue;
183 if (!create_directory(path
)) goto done
;
186 ret
= create_directory(path
);
193 static INT_PTR
cabinet_copy_file(FDINOTIFICATIONTYPE fdint
, PFDINOTIFICATION pfdin
)
195 HANDLE handle
= INVALID_HANDLE_VALUE
;
199 if (!(file
= get_uncompressed_path(pfdin
)))
202 TRACE("Extracting %s -> %s\n", debugstr_a(pfdin
->psz1
), debugstr_w(file
));
204 if (create_parent_directory(file
))
206 attrs
= pfdin
->attribs
;
207 if (!attrs
) attrs
= FILE_ATTRIBUTE_NORMAL
;
208 handle
= CreateFileW(file
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, attrs
, NULL
);
212 return (handle
!= INVALID_HANDLE_VALUE
) ? (INT_PTR
)handle
: -1;
215 static INT_PTR
cabinet_close_file_info(FDINOTIFICATIONTYPE fdint
, PFDINOTIFICATION pfdin
)
217 HANDLE handle
= (HANDLE
)pfdin
->hf
;
222 static INT_PTR CDECL
cabinet_notify(FDINOTIFICATIONTYPE fdint
, PFDINOTIFICATION pfdin
)
226 case fdintPARTIAL_FILE
:
227 FIXME("fdintPARTIAL_FILE not implemented\n");
230 case fdintNEXT_CABINET
:
231 FIXME("fdintNEXT_CABINET not implemented\n");
235 return cabinet_copy_file(fdint
, pfdin
);
237 case fdintCLOSE_FILE_INFO
:
238 return cabinet_close_file_info(fdint
, pfdin
);
245 static BOOL
extract_cabinet(const WCHAR
*filename
, const WCHAR
*destination
)
247 char *filenameA
= NULL
;
252 hfdi
= FDICreate(cabinet_alloc
, cabinet_free
, cabinet_open
, cabinet_read
,
253 cabinet_write
, cabinet_close
, cabinet_seek
, 0, &erf
);
254 if (!hfdi
) return FALSE
;
256 if ((filenameA
= strdupWtoA(filename
)))
258 ret
= FDICopy(hfdi
, filenameA
, NULL
, 0, cabinet_notify
, NULL
, (void *)destination
);
259 heap_free(filenameA
);
266 static const WCHAR
*create_temp_directory(struct installer_state
*state
)
269 struct installer_tempdir
*entry
;
272 if (!GetTempPathW(ARRAY_SIZE(tmp
), tmp
)) return NULL
;
273 if (!(entry
= heap_alloc(sizeof(*entry
)))) return NULL
;
274 if (!(entry
->path
= heap_alloc((MAX_PATH
+ 20) * sizeof(WCHAR
))))
281 if (!GetTempFileNameW(tmp
, L
"msu", ++id
, entry
->path
))
283 heap_free(entry
->path
);
287 if (CreateDirectoryW(entry
->path
, NULL
)) break;
290 list_add_tail(&state
->tempdirs
, &entry
->entry
);
294 static BOOL
delete_directory(const WCHAR
*path
)
296 WIN32_FIND_DATAW data
;
300 if (!(full_path
= path_combine(path
, L
"*"))) return FALSE
;
301 search
= FindFirstFileW(full_path
, &data
);
302 heap_free(full_path
);
304 if (search
!= INVALID_HANDLE_VALUE
)
308 if (!wcscmp(data
.cFileName
, L
".")) continue;
309 if (!wcscmp(data
.cFileName
, L
"..")) continue;
310 if (!(full_path
= path_combine(path
, data
.cFileName
))) continue;
311 if (data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
312 delete_directory(full_path
);
314 DeleteFileW(full_path
);
315 heap_free(full_path
);
317 while (FindNextFileW(search
, &data
));
321 return RemoveDirectoryW(path
);
324 static void installer_cleanup(struct installer_state
*state
)
326 struct installer_tempdir
*tempdir
, *tempdir2
;
327 struct assembly_entry
*assembly
, *assembly2
;
328 struct dependency_entry
*dependency
, *dependency2
;
330 LIST_FOR_EACH_ENTRY_SAFE(tempdir
, tempdir2
, &state
->tempdirs
, struct installer_tempdir
, entry
)
332 list_remove(&tempdir
->entry
);
333 delete_directory(tempdir
->path
);
334 heap_free(tempdir
->path
);
337 LIST_FOR_EACH_ENTRY_SAFE(assembly
, assembly2
, &state
->assemblies
, struct assembly_entry
, entry
)
339 list_remove(&assembly
->entry
);
340 free_assembly(assembly
);
342 LIST_FOR_EACH_ENTRY_SAFE(dependency
, dependency2
, &state
->updates
, struct dependency_entry
, entry
)
344 list_remove(&dependency
->entry
);
345 free_dependency(dependency
);
349 static BOOL
str_ends_with(const WCHAR
*str
, const WCHAR
*suffix
)
351 DWORD str_len
= lstrlenW(str
), suffix_len
= lstrlenW(suffix
);
352 if (suffix_len
> str_len
) return FALSE
;
353 return !wcsicmp(str
+ str_len
- suffix_len
, suffix
);
356 static BOOL
load_assemblies_from_cab(const WCHAR
*filename
, struct installer_state
*state
)
358 struct assembly_entry
*assembly
;
359 const WCHAR
*temp_path
;
360 WIN32_FIND_DATAW data
;
364 TRACE("Processing cab file %s\n", debugstr_w(filename
));
366 if (!(temp_path
= create_temp_directory(state
))) return FALSE
;
367 if (!extract_cabinet(filename
, temp_path
))
369 ERR("Failed to extract %s\n", debugstr_w(filename
));
373 if (!(path
= path_combine(temp_path
, L
"_manifest_.cix.xml"))) return FALSE
;
374 if (GetFileAttributesW(path
) != INVALID_FILE_ATTRIBUTES
)
376 FIXME("Cabinet uses proprietary msdelta file compression which is not (yet) supported\n");
377 FIXME("Installation of msu file will most likely fail\n");
381 if (!(path
= path_combine(temp_path
, L
"*"))) return FALSE
;
382 search
= FindFirstFileW(path
, &data
);
385 if (search
!= INVALID_HANDLE_VALUE
)
389 if (data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) continue;
390 if (!str_ends_with(data
.cFileName
, L
".manifest") &&
391 !str_ends_with(data
.cFileName
, L
".mum")) continue;
392 if (!(path
= path_combine(temp_path
, data
.cFileName
))) continue;
393 if ((assembly
= load_manifest(path
)))
394 list_add_tail(&state
->assemblies
, &assembly
->entry
);
397 while (FindNextFileW(search
, &data
));
404 static BOOL
compare_assembly_string(const WCHAR
*str1
, const WCHAR
*str2
)
406 return !wcscmp(str1
, str2
) || !wcscmp(str1
, L
"*") || !wcscmp(str2
, L
"*");
409 static struct assembly_entry
*lookup_assembly(struct list
*manifest_list
, struct assembly_identity
*identity
)
411 struct assembly_entry
*assembly
;
413 LIST_FOR_EACH_ENTRY(assembly
, manifest_list
, struct assembly_entry
, entry
)
415 if (wcsicmp(assembly
->identity
.name
, identity
->name
)) continue;
416 if (!compare_assembly_string(assembly
->identity
.architecture
, identity
->architecture
)) continue;
417 if (!compare_assembly_string(assembly
->identity
.language
, identity
->language
)) continue;
418 if (!compare_assembly_string(assembly
->identity
.pubkey_token
, identity
->pubkey_token
)) continue;
419 if (!compare_assembly_string(assembly
->identity
.version
, identity
->version
))
421 WARN("Ignoring version difference for %s (expected %s, found %s)\n",
422 debugstr_w(identity
->name
), debugstr_w(identity
->version
), debugstr_w(assembly
->identity
.version
));
430 static WCHAR
*get_assembly_source(struct assembly_entry
*assembly
)
432 WCHAR
*p
, *path
= strdupW(assembly
->filename
);
433 if (path
&& (p
= wcsrchr(path
, '.'))) *p
= 0;
437 static BOOL
strbuf_init(struct strbuf
*buf
)
441 buf
->buf
= heap_alloc(buf
->len
* sizeof(WCHAR
));
442 return buf
->buf
!= NULL
;
445 static void strbuf_free(struct strbuf
*buf
)
451 static BOOL
strbuf_append(struct strbuf
*buf
, const WCHAR
*str
, DWORD len
)
456 if (!buf
->buf
) return FALSE
;
457 if (!str
) return TRUE
;
459 if (len
== ~0U) len
= lstrlenW(str
);
460 if (buf
->pos
+ len
+ 1 > buf
->len
)
462 new_len
= max(buf
->pos
+ len
+ 1, buf
->len
* 2);
463 new_buf
= heap_realloc(buf
->buf
, new_len
* sizeof(WCHAR
));
473 memcpy(&buf
->buf
[buf
->pos
], str
, len
* sizeof(WCHAR
));
474 buf
->buf
[buf
->pos
+ len
] = 0;
479 static WCHAR
*lookup_expression(struct assembly_entry
*assembly
, const WCHAR
*key
)
481 WCHAR path
[MAX_PATH
];
483 if (!wcscmp(key
, L
"runtime.system32"))
486 if (!wcscmp(assembly
->identity
.architecture
, L
"x86"))
488 GetSystemWow64DirectoryW(path
, ARRAY_SIZE(path
));
489 return strdupW(path
);
492 GetSystemDirectoryW(path
, ARRAY_SIZE(path
));
493 return strdupW(path
);
495 if (!wcscmp(key
, L
"runtime.windows"))
497 GetWindowsDirectoryW(path
, ARRAY_SIZE(path
));
498 return strdupW(path
);
501 FIXME("Unknown expression %s\n", debugstr_w(key
));
505 static WCHAR
*expand_expression(struct assembly_entry
*assembly
, const WCHAR
*expression
)
507 const WCHAR
*pos
, *next
;
511 if (!expression
|| !strbuf_init(&buf
)) return NULL
;
513 for (pos
= expression
; (next
= wcsstr(pos
, L
"$(")); pos
= next
+ 1)
515 strbuf_append(&buf
, pos
, next
- pos
);
517 if (!(next
= wcsstr(pos
, L
")")))
519 strbuf_append(&buf
, L
"$(", 2);
523 if (!(key
= strdupWn(pos
, next
- pos
))) goto error
;
524 value
= lookup_expression(assembly
, key
);
526 if (!value
) goto error
;
527 strbuf_append(&buf
, value
, ~0U);
531 strbuf_append(&buf
, pos
, ~0U);
535 FIXME("Couldn't resolve expression %s\n", debugstr_w(expression
));
540 static BOOL
install_files_copy(struct assembly_entry
*assembly
, const WCHAR
*source_path
, struct fileop_entry
*fileop
, BOOL dryrun
)
542 WCHAR
*target_path
, *target
, *source
= NULL
;
545 if (!(target_path
= expand_expression(assembly
, fileop
->target
))) return FALSE
;
546 if (!(target
= path_combine(target_path
, fileop
->source
))) goto error
;
547 if (!(source
= path_combine(source_path
, fileop
->source
))) goto error
;
551 if (!(ret
= PathFileExistsW(source
)))
553 ERR("Required file %s not found\n", debugstr_w(source
));
559 TRACE("Copying %s -> %s\n", debugstr_w(source
), debugstr_w(target
));
561 if (!create_parent_directory(target
))
563 ERR("Failed to create parent directory for %s\n", debugstr_w(target
));
566 if (!(ret
= CopyFileExW(source
, target
, NULL
, NULL
, NULL
, 0)))
568 ERR("Failed to copy %s to %s\n", debugstr_w(source
), debugstr_w(target
));
574 heap_free(target_path
);
580 static BOOL
install_files(struct assembly_entry
*assembly
, BOOL dryrun
)
582 struct fileop_entry
*fileop
;
586 if (!(source_path
= get_assembly_source(assembly
)))
588 ERR("Failed to get assembly source directory\n");
592 LIST_FOR_EACH_ENTRY(fileop
, &assembly
->fileops
, struct fileop_entry
, entry
)
594 if (!(ret
= install_files_copy(assembly
, source_path
, fileop
, dryrun
))) break;
597 heap_free(source_path
);
601 static WCHAR
*split_registry_key(WCHAR
*key
, HKEY
*root
)
606 if (!(p
= wcschr(key
, '\\'))) return NULL
;
609 if (lstrlenW(L
"HKEY_CLASSES_ROOT") == size
&& !wcsncmp(key
, L
"HKEY_CLASSES_ROOT", size
))
610 *root
= HKEY_CLASSES_ROOT
;
611 else if (lstrlenW(L
"HKEY_CURRENT_CONFIG") == size
&& !wcsncmp(key
, L
"HKEY_CURRENT_CONFIG", size
))
612 *root
= HKEY_CURRENT_CONFIG
;
613 else if (lstrlenW(L
"HKEY_CURRENT_USER") == size
&& !wcsncmp(key
, L
"HKEY_CURRENT_USER", size
))
614 *root
= HKEY_CURRENT_USER
;
615 else if (lstrlenW(L
"HKEY_LOCAL_MACHINE") == size
&& !wcsncmp(key
, L
"HKEY_LOCAL_MACHINE", size
))
616 *root
= HKEY_LOCAL_MACHINE
;
617 else if (lstrlenW(L
"HKEY_USERS") == size
&& !wcsncmp(key
, L
"HKEY_USERS", size
))
621 FIXME("Unknown root key %s\n", debugstr_wn(key
, size
));
628 static BOOL
install_registry_string(struct assembly_entry
*assembly
, HKEY key
, struct registrykv_entry
*registrykv
, DWORD type
, BOOL dryrun
)
631 WCHAR
*value
= expand_expression(assembly
, registrykv
->value
);
634 if (registrykv
->value
&& !value
)
637 value_size
= value
? (lstrlenW(value
) + 1) * sizeof(WCHAR
) : 0;
638 if (!dryrun
&& RegSetValueExW(key
, registrykv
->name
, 0, type
, (void *)value
, value_size
))
640 ERR("Failed to set registry key %s\n", debugstr_w(registrykv
->name
));
648 static WCHAR
*parse_multisz(const WCHAR
*input
, DWORD
*size
)
650 const WCHAR
*pos
, *next
;
654 if (!input
|| !input
[0] || !strbuf_init(&buf
)) return NULL
;
656 for (pos
= input
; pos
[0] == '"'; pos
++)
659 if (!(next
= wcsstr(pos
, L
"\""))) goto error
;
660 strbuf_append(&buf
, pos
, next
- pos
);
661 strbuf_append(&buf
, L
"", ARRAY_SIZE(L
""));
667 FIXME("Error while parsing REG_MULTI_SZ string: Expected comma but got '%c'\n", pos
[0]);
674 FIXME("Error while parsing REG_MULTI_SZ string: Garbage at end of string\n");
678 strbuf_append(&buf
, L
"", ARRAY_SIZE(L
""));
679 *size
= buf
.pos
* sizeof(WCHAR
);
687 static BOOL
install_registry_multisz(struct assembly_entry
*assembly
, HKEY key
, struct registrykv_entry
*registrykv
, BOOL dryrun
)
690 WCHAR
*value
= parse_multisz(registrykv
->value
, &value_size
);
693 if (registrykv
->value
&& registrykv
->value
[0] && !value
)
696 if (!dryrun
&& RegSetValueExW(key
, registrykv
->name
, 0, REG_MULTI_SZ
, (void *)value
, value_size
))
698 ERR("Failed to set registry key %s\n", debugstr_w(registrykv
->name
));
706 static BOOL
install_registry_dword(struct assembly_entry
*assembly
, HKEY key
, struct registrykv_entry
*registrykv
, BOOL dryrun
)
708 DWORD value
= registrykv
->value_type
? wcstoul(registrykv
->value_type
, NULL
, 16) : 0;
711 if (!dryrun
&& RegSetValueExW(key
, registrykv
->name
, 0, REG_DWORD
, (void *)&value
, sizeof(value
)))
713 ERR("Failed to set registry key %s\n", debugstr_w(registrykv
->name
));
720 static BYTE
*parse_hex(const WCHAR
*input
, DWORD
*size
)
722 WCHAR number
[3] = {0, 0, 0};
727 if (!input
) return NULL
;
728 length
= lstrlenW(input
);
729 if (length
& 1) return NULL
;
732 if (!(output
= heap_alloc(length
))) return NULL
;
733 for (p
= output
; *input
; input
+= 2)
735 number
[0] = input
[0];
736 number
[1] = input
[1];
737 *p
++ = wcstoul(number
, 0, 16);
743 static BOOL
install_registry_binary(struct assembly_entry
*assembly
, HKEY key
, struct registrykv_entry
*registrykv
, BOOL dryrun
)
746 BYTE
*value
= parse_hex(registrykv
->value
, &value_size
);
749 if (registrykv
->value
&& !value
)
752 if (!dryrun
&& RegSetValueExW(key
, registrykv
->name
, 0, REG_BINARY
, value
, value_size
))
754 ERR("Failed to set registry key %s\n", debugstr_w(registrykv
->name
));
762 static BOOL
install_registry_value(struct assembly_entry
*assembly
, HKEY key
, struct registrykv_entry
*registrykv
, BOOL dryrun
)
764 TRACE("Setting registry key %s = %s\n", debugstr_w(registrykv
->name
), debugstr_w(registrykv
->value
));
766 if (!wcscmp(registrykv
->value_type
, L
"REG_SZ"))
767 return install_registry_string(assembly
, key
, registrykv
, REG_SZ
, dryrun
);
768 if (!wcscmp(registrykv
->value_type
, L
"REG_EXPAND_SZ"))
769 return install_registry_string(assembly
, key
, registrykv
, REG_EXPAND_SZ
, dryrun
);
770 if (!wcscmp(registrykv
->value_type
, L
"REG_MULTI_SZ"))
771 return install_registry_multisz(assembly
, key
, registrykv
, dryrun
);
772 if (!wcscmp(registrykv
->value_type
, L
"REG_DWORD"))
773 return install_registry_dword(assembly
, key
, registrykv
, dryrun
);
774 if (!wcscmp(registrykv
->value_type
, L
"REG_BINARY"))
775 return install_registry_binary(assembly
, key
, registrykv
, dryrun
);
777 FIXME("Unsupported registry value type %s\n", debugstr_w(registrykv
->value_type
));
781 static BOOL
install_registry(struct assembly_entry
*assembly
, BOOL dryrun
)
783 struct registryop_entry
*registryop
;
784 struct registrykv_entry
*registrykv
;
787 REGSAM sam
= KEY_ALL_ACCESS
;
791 if (!wcscmp(assembly
->identity
.architecture
, L
"x86")) sam
|= KEY_WOW64_32KEY
;
794 LIST_FOR_EACH_ENTRY(registryop
, &assembly
->registryops
, struct registryop_entry
, entry
)
796 if (!(path
= split_registry_key(registryop
->key
, &root
)))
802 TRACE("Processing registry key %s\n", debugstr_w(registryop
->key
));
804 if (!dryrun
&& RegCreateKeyExW(root
, path
, 0, NULL
, 0, sam
, NULL
, &subkey
, NULL
))
806 ERR("Failed to open registry key %s\n", debugstr_w(registryop
->key
));
811 LIST_FOR_EACH_ENTRY(registrykv
, ®istryop
->keyvalues
, struct registrykv_entry
, entry
)
813 if (!(ret
= install_registry_value(assembly
, subkey
, registrykv
, dryrun
))) break;
816 if (!dryrun
) RegCloseKey(subkey
);
823 static BOOL
install_assembly(struct list
*manifest_list
, struct assembly_identity
*identity
, BOOL dryrun
)
825 struct dependency_entry
*dependency
;
826 struct assembly_entry
*assembly
;
829 if (!(assembly
= lookup_assembly(manifest_list
, identity
)))
831 FIXME("Assembly %s not found\n", debugstr_w(identity
->name
));
835 name
= assembly
->identity
.name
;
837 if (assembly
->status
== ASSEMBLY_STATUS_INSTALLED
)
839 TRACE("Assembly %s already installed\n", debugstr_w(name
));
842 if (assembly
->status
== ASSEMBLY_STATUS_IN_PROGRESS
)
844 ERR("Assembly %s caused circular dependency\n", debugstr_w(name
));
849 if (!wcscmp(assembly
->identity
.architecture
, L
"amd64"))
851 ERR("Cannot install amd64 assembly in 32-bit prefix\n");
856 assembly
->status
= ASSEMBLY_STATUS_IN_PROGRESS
;
858 LIST_FOR_EACH_ENTRY(dependency
, &assembly
->dependencies
, struct dependency_entry
, entry
)
860 if (!install_assembly(manifest_list
, &dependency
->identity
, dryrun
)) return FALSE
;
863 TRACE("Installing assembly %s%s\n", debugstr_w(name
), dryrun
? " (dryrun)" : "");
865 if (!install_files(assembly
, dryrun
))
867 ERR("Failed to install all files for %s\n", debugstr_w(name
));
871 if (!install_registry(assembly
, dryrun
))
873 ERR("Failed to install registry keys for %s\n", debugstr_w(name
));
877 TRACE("Installation of %s finished\n", debugstr_w(name
));
879 assembly
->status
= ASSEMBLY_STATUS_INSTALLED
;
883 static BOOL
install_updates(struct installer_state
*state
, BOOL dryrun
)
885 struct dependency_entry
*dependency
;
886 LIST_FOR_EACH_ENTRY(dependency
, &state
->updates
, struct dependency_entry
, entry
)
888 if (!install_assembly(&state
->assemblies
, &dependency
->identity
, dryrun
))
890 ERR("Failed to install update %s\n", debugstr_w(dependency
->identity
.name
));
897 static void set_assembly_status(struct list
*manifest_list
, DWORD status
)
899 struct assembly_entry
*assembly
;
900 LIST_FOR_EACH_ENTRY(assembly
, manifest_list
, struct assembly_entry
, entry
)
902 assembly
->status
= status
;
906 static BOOL
install_msu(const WCHAR
*filename
, struct installer_state
*state
)
908 const WCHAR
*temp_path
;
909 WIN32_FIND_DATAW data
;
914 list_init(&state
->tempdirs
);
915 list_init(&state
->assemblies
);
916 list_init(&state
->updates
);
919 TRACE("Processing msu file %s\n", debugstr_w(filename
));
921 if (!(temp_path
= create_temp_directory(state
))) return FALSE
;
922 if (!extract_cabinet(filename
, temp_path
))
924 ERR("Failed to extract %s\n", debugstr_w(filename
));
928 /* load all manifests from contained cabinet archives */
929 if (!(path
= path_combine(temp_path
, L
"*.cab"))) goto done
;
930 search
= FindFirstFileW(path
, &data
);
933 if (search
!= INVALID_HANDLE_VALUE
)
937 if (data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) continue;
938 if (!wcsicmp(data
.cFileName
, L
"WSUSSCAN.cab")) continue;
939 if (!(path
= path_combine(temp_path
, data
.cFileName
))) continue;
940 if (!load_assemblies_from_cab(path
, state
))
941 ERR("Failed to load all manifests from %s, ignoring\n", debugstr_w(path
));
944 while (FindNextFileW(search
, &data
));
948 /* load all update descriptions */
949 if (!(path
= path_combine(temp_path
, L
"*.xml"))) goto done
;
950 search
= FindFirstFileW(path
, &data
);
953 if (search
!= INVALID_HANDLE_VALUE
)
957 if (data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) continue;
958 if (!(path
= path_combine(temp_path
, data
.cFileName
))) continue;
959 if (!load_update(path
, &state
->updates
))
960 ERR("Failed to load all updates from %s, ignoring\n", debugstr_w(path
));
963 while (FindNextFileW(search
, &data
));
967 /* dump package information (for debugging) */
970 struct dependency_entry
*dependency
;
971 struct assembly_entry
*assembly
;
973 TRACE("List of updates:\n");
974 LIST_FOR_EACH_ENTRY(dependency
, &state
->updates
, struct dependency_entry
, entry
)
975 TRACE(" * %s\n", debugstr_w(dependency
->identity
.name
));
977 TRACE("List of manifests (with dependencies):\n");
978 LIST_FOR_EACH_ENTRY(assembly
, &state
->assemblies
, struct assembly_entry
, entry
)
980 TRACE(" * %s\n", debugstr_w(assembly
->identity
.name
));
981 LIST_FOR_EACH_ENTRY(dependency
, &assembly
->dependencies
, struct dependency_entry
, entry
)
982 TRACE(" -> %s\n", debugstr_w(dependency
->identity
.name
));
986 if (list_empty(&state
->updates
))
988 ERR("No updates found, probably incompatible MSU file format?\n");
992 /* perform dry run */
993 set_assembly_status(&state
->assemblies
, ASSEMBLY_STATUS_NONE
);
994 if (!install_updates(state
, TRUE
))
996 ERR("Dry run failed, aborting installation\n");
1001 set_assembly_status(&state
->assemblies
, ASSEMBLY_STATUS_NONE
);
1002 if (!install_updates(state
, FALSE
))
1004 ERR("Installation failed\n");
1008 TRACE("Installation finished\n");
1012 installer_cleanup(state
);
1016 static void restart_as_x86_64(void)
1018 WCHAR filename
[MAX_PATH
];
1019 PROCESS_INFORMATION pi
;
1021 DWORD exit_code
= 1;
1024 memset(&si
, 0, sizeof(si
));
1026 GetModuleFileNameW(0, filename
, MAX_PATH
);
1028 Wow64DisableWow64FsRedirection(&redir
);
1029 if (CreateProcessW(filename
, GetCommandLineW(), NULL
, NULL
, FALSE
, 0, NULL
, NULL
, &si
, &pi
))
1031 TRACE("Restarting %s\n", wine_dbgstr_w(filename
));
1032 WaitForSingleObject(pi
.hProcess
, INFINITE
);
1033 GetExitCodeProcess(pi
.hProcess
, &exit_code
);
1034 CloseHandle(pi
.hProcess
);
1035 CloseHandle(pi
.hThread
);
1037 else ERR("Failed to restart 64-bit %s, err %u\n", wine_dbgstr_w(filename
), GetLastError());
1038 Wow64RevertWow64FsRedirection(redir
);
1040 ExitProcess(exit_code
);
1043 int __cdecl
wmain(int argc
, WCHAR
*argv
[])
1045 struct installer_state state
;
1046 const WCHAR
*filename
= NULL
;
1050 if (IsWow64Process( GetCurrentProcess(), &is_wow64
) && is_wow64
) restart_as_x86_64();
1052 state
.norestart
= FALSE
;
1053 state
.quiet
= FALSE
;
1057 TRACE("Command line:");
1058 for (i
= 0; i
< argc
; i
++)
1059 TRACE(" %s", wine_dbgstr_w(argv
[i
]));
1063 for (i
= 1; i
< argc
; i
++)
1065 if (argv
[i
][0] == '/')
1067 if (!wcscmp(argv
[i
], L
"/norestart"))
1068 state
.norestart
= TRUE
;
1069 else if (!wcscmp(argv
[i
], L
"/quiet"))
1072 FIXME("Unknown option: %s\n", wine_dbgstr_w(argv
[i
]));
1077 FIXME("Unknown option: %s\n", wine_dbgstr_w(argv
[i
]));
1082 FIXME("Missing filename argument\n");
1086 return !install_msu(filename
, &state
);