From c3df74e24f311b3a33c1f143bd4f4b3829112012 Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Thu, 1 Nov 2007 03:09:02 -0500 Subject: [PATCH] msi: Implement the MoveFiles standard action. --- dlls/msi/action.c | 315 ++++++++++++++++++++++++++++++++++++++- dlls/msi/tests/install.c | 48 +++--- include/msidefs.h | 5 + 3 files changed, 338 insertions(+), 30 deletions(-) diff --git a/dlls/msi/action.c b/dlls/msi/action.c index cff5368d3b..f79f9a4f81 100644 --- a/dlls/msi/action.c +++ b/dlls/msi/action.c @@ -4965,6 +4965,315 @@ static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package ) return rc; } +#define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0)))) + +typedef struct +{ + struct list entry; + LPWSTR sourcename; + LPWSTR destname; + LPWSTR source; + LPWSTR dest; +} FILE_LIST; + +static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options) +{ + BOOL ret; + + if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY || + GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY) + { + WARN("Source or dest is directory, not moving\n"); + return FALSE; + } + + if (options == msidbMoveFileOptionsMove) + { + TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest)); + ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING); + if (!ret) + { + WARN("MoveFile failed: %d\n", GetLastError()); + return FALSE; + } + } + else + { + TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest)); + ret = CopyFileW(source, dest, FALSE); + if (!ret) + { + WARN("CopyFile failed: %d\n", GetLastError()); + return FALSE; + } + } + + return TRUE; +} + +static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename) +{ + LPWSTR path, ptr; + DWORD dirlen, pathlen; + + ptr = strrchrW(wildcard, '\\'); + dirlen = ptr - wildcard + 1; + + pathlen = dirlen + lstrlenW(filename) + 1; + path = msi_alloc(pathlen * sizeof(WCHAR)); + + lstrcpynW(path, wildcard, dirlen + 1); + lstrcatW(path, filename); + + return path; +} + +static void free_file_entry(FILE_LIST *file) +{ + msi_free(file->source); + msi_free(file->dest); + msi_free(file); +} + +static void free_list(FILE_LIST *list) +{ + while (!list_empty(&list->entry)) + { + FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry); + + list_remove(&file->entry); + free_file_entry(file); + } +} + +static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest) +{ + FILE_LIST *new, *file; + LPWSTR ptr, filename; + DWORD size; + + new = msi_alloc_zero(sizeof(FILE_LIST)); + if (!new) + return FALSE; + + new->source = strdupW(source); + ptr = strrchrW(dest, '\\') + 1; + filename = strrchrW(new->source, '\\') + 1; + + new->sourcename = filename; + + if (*ptr) + new->destname = ptr; + else + new->destname = new->sourcename; + + size = (ptr - dest) + lstrlenW(filename) + 1; + new->dest = msi_alloc(size * sizeof(WCHAR)); + if (!new->dest) + { + free_file_entry(new); + return FALSE; + } + + lstrcpynW(new->dest, dest, ptr - dest + 1); + lstrcatW(new->dest, filename); + + if (list_empty(&files->entry)) + { + list_add_head(&files->entry, &new->entry); + return TRUE; + } + + LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry) + { + if (lstrcmpW(source, file->source) < 0) + { + list_add_before(&file->entry, &new->entry); + return TRUE; + } + } + + list_add_after(&file->entry, &new->entry); + return TRUE; +} + +BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options) +{ + WIN32_FIND_DATAW wfd; + HANDLE hfile; + LPWSTR path; + BOOL res; + FILE_LIST files, *file; + DWORD size; + + hfile = FindFirstFileW(source, &wfd); + if (hfile == INVALID_HANDLE_VALUE) return FALSE; + + list_init(&files.entry); + + for (res = TRUE; res; res = FindNextFileW(hfile, &wfd)) + { + if (is_dot_dir(wfd.cFileName)) continue; + + path = wildcard_to_file(source, wfd.cFileName); + if (!path) + { + free_list(&files); + return FALSE; + } + + add_wildcard(&files, path, dest); + msi_free(path); + } + + /* only the first wildcard match gets renamed to dest */ + file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry); + size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2; + file->dest = msi_realloc(file->dest, size * sizeof(WCHAR)); + if (!file->dest) + { + free_list(&files); + return FALSE; + } + + lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname); + + while (!list_empty(&files.entry)) + { + file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry); + + msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options); + + list_remove(&file->entry); + free_file_entry(file); + } + + return TRUE; +} + +static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param ) +{ + MSIPACKAGE *package = param; + MSICOMPONENT *comp; + LPCWSTR sourcename, destname; + LPWSTR sourcedir = NULL, destdir = NULL; + LPWSTR source = NULL, dest = NULL; + int options; + DWORD size; + BOOL ret, wildcards; + + static const WCHAR backslash[] = {'\\',0}; + + comp = get_loaded_component(package, MSI_RecordGetString(rec, 2)); + if (!comp || !comp->Enabled || + !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE))) + { + TRACE("Component not set for install, not moving file\n"); + return ERROR_SUCCESS; + } + + sourcename = MSI_RecordGetString(rec, 3); + destname = MSI_RecordGetString(rec, 4); + options = MSI_RecordGetInteger(rec, 7); + + sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5)); + if (!sourcedir) + goto done; + + destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6)); + if (!destdir) + goto done; + + if (!sourcename) + { + if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES) + goto done; + + source = strdupW(sourcedir); + if (!source) + goto done; + } + else + { + size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2; + source = msi_alloc(size * sizeof(WCHAR)); + if (!source) + goto done; + + lstrcpyW(source, sourcedir); + if (source[lstrlenW(source) - 1] != '\\') + lstrcatW(source, backslash); + lstrcatW(source, sourcename); + } + + wildcards = strchrW(source, '*') || strchrW(source, '?'); + + if (!destname && !wildcards) + { + destname = strdupW(sourcename); + if (!destname) + goto done; + } + + size = 0; + if (destname) + size = lstrlenW(destname); + + size += lstrlenW(destdir) + 2; + dest = msi_alloc(size * sizeof(WCHAR)); + if (!dest) + goto done; + + lstrcpyW(dest, destdir); + if (dest[lstrlenW(dest) - 1] != '\\') + lstrcatW(dest, backslash); + + if (destname) + lstrcatW(dest, destname); + + if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES) + { + ret = CreateDirectoryW(destdir, NULL); + if (!ret) + { + WARN("CreateDirectory failed: %d\n", GetLastError()); + return ERROR_SUCCESS; + } + } + + if (!wildcards) + msi_move_file(source, dest, options); + else + move_files_wildcard(source, dest, options); + +done: + msi_free(sourcedir); + msi_free(destdir); + msi_free(source); + msi_free(dest); + + return ERROR_SUCCESS; +} + +static UINT ACTION_MoveFiles( MSIPACKAGE *package ) +{ + UINT rc; + MSIQUERY *view; + + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','M','o','v','e','F','i','l','e','`',0}; + + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package); + msiobj_release(&view->hdr); + + return rc; +} + static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, LPCSTR action, LPCWSTR table ) { @@ -5002,12 +5311,6 @@ static UINT ACTION_RemoveIniValues( MSIPACKAGE *package ) return msi_unimplemented_action_stub( package, "RemoveIniValues", table ); } -static UINT ACTION_MoveFiles( MSIPACKAGE *package ) -{ - static const WCHAR table[] = { 'M','o','v','e','F','i','l','e',0 }; - return msi_unimplemented_action_stub( package, "MoveFiles", table ); -} - static UINT ACTION_PatchFiles( MSIPACKAGE *package ) { static const WCHAR table[] = { 'P','a','t','c','h',0 }; diff --git a/dlls/msi/tests/install.c b/dlls/msi/tests/install.c index 46cc1ecf26..3d5458a97d 100644 --- a/dlls/msi/tests/install.c +++ b/dlls/msi/tests/install.c @@ -2981,54 +2981,54 @@ static void test_movefiles(void) ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); ok(delete_pf("msitest\\augustus", TRUE), "File not installed\n"); ok(!delete_pf("msitest\\dest", TRUE), "File copied\n"); - todo_wine ok(delete_pf("msitest\\canada", TRUE), "File not copied\n"); - todo_wine ok(delete_pf("msitest\\dominica", TRUE), "File not moved\n"); + ok(delete_pf("msitest\\canada", TRUE), "File not copied\n"); + ok(delete_pf("msitest\\dominica", TRUE), "File not moved\n"); ok(!delete_pf("msitest\\elsalvador", TRUE), "File moved\n"); ok(!delete_pf("msitest\\france", TRUE), "File moved\n"); ok(!delete_pf("msitest\\georgia", TRUE), "File moved\n"); - todo_wine ok(delete_pf("msitest\\hungary", TRUE), "File not moved\n"); + ok(delete_pf("msitest\\hungary", TRUE), "File not moved\n"); ok(!delete_pf("msitest\\indonesia", TRUE), "File moved\n"); ok(!delete_pf("msitest\\jordan", TRUE), "File moved\n"); - todo_wine ok(delete_pf("msitest\\kiribati", TRUE), "File not moved\n"); + ok(delete_pf("msitest\\kiribati", TRUE), "File not moved\n"); ok(!delete_pf("msitest\\lebanon", TRUE), "File moved\n"); ok(!delete_pf("msitest\\lebanon", FALSE), "Directory moved\n"); ok(!delete_pf("msitest\\apple", TRUE), "File should not exist\n"); - todo_wine ok(delete_pf("msitest\\wildcard", TRUE), "File not moved\n"); - todo_wine ok(delete_pf("msitest\\application", TRUE), "File not moved\n"); + ok(delete_pf("msitest\\wildcard", TRUE), "File not moved\n"); + ok(delete_pf("msitest\\application", TRUE), "File not moved\n"); ok(!delete_pf("msitest\\ape", TRUE), "File moved\n"); - todo_wine ok(delete_pf("msitest\\foo", TRUE), "File not moved\n"); + ok(delete_pf("msitest\\foo", TRUE), "File not moved\n"); ok(!delete_pf("msitest\\fao", TRUE), "File should not exist\n"); - todo_wine ok(delete_pf("msitest\\single", TRUE), "File not moved\n"); + ok(delete_pf("msitest\\single", TRUE), "File not moved\n"); ok(!delete_pf("msitest\\fbod", TRUE), "File moved\n"); - todo_wine ok(delete_pf("msitest\\budding", TRUE), "File not moved\n"); - todo_wine ok(delete_pf("msitest\\buddy", TRUE), "File not moved\n"); + ok(delete_pf("msitest\\budding", TRUE), "File not moved\n"); + ok(delete_pf("msitest\\buddy", TRUE), "File not moved\n"); ok(!delete_pf("msitest\\bud", TRUE), "File moved\n"); - todo_wine ok(delete_pf("msitest\\bar", TRUE), "File not moved\n"); - todo_wine ok(delete_pf("msitest\\bur", TRUE), "File not moved\n"); + ok(delete_pf("msitest\\bar", TRUE), "File not moved\n"); + ok(delete_pf("msitest\\bur", TRUE), "File not moved\n"); ok(!delete_pf("msitest\\bird", TRUE), "File moved\n"); ok(delete_pf("msitest", FALSE), "File not installed\n"); ok(DeleteFileA("cameroon"), "File moved\n"); - todo_wine ok(!DeleteFileA("djibouti"), "File not moved\n"); + ok(!DeleteFileA("djibouti"), "File not moved\n"); ok(DeleteFileA("egypt"), "File moved\n"); ok(DeleteFileA("finland"), "File moved\n"); ok(DeleteFileA("gambai"), "File moved\n"); - todo_wine ok(!DeleteFileA("honduras"), "File not moved\n"); + ok(!DeleteFileA("honduras"), "File not moved\n"); ok(DeleteFileA("msitest\\india"), "File moved\n"); ok(DeleteFileA("japan"), "File moved\n"); - todo_wine ok(!DeleteFileA("kenya"), "File not moved\n"); + ok(!DeleteFileA("kenya"), "File not moved\n"); ok(RemoveDirectoryA("latvia"), "Directory moved\n"); - todo_wine ok(!DeleteFileA("nauru"), "File not moved\n"); - todo_wine ok(!DeleteFileA("apple"), "File not moved\n"); - todo_wine ok(!DeleteFileA("application"), "File not moved\n"); + ok(!DeleteFileA("nauru"), "File not moved\n"); + ok(!DeleteFileA("apple"), "File not moved\n"); + ok(!DeleteFileA("application"), "File not moved\n"); ok(DeleteFileA("ape"), "File moved\n"); - todo_wine ok(!DeleteFileA("foo"), "File not moved\n"); - todo_wine ok(!DeleteFileA("fao"), "File not moved\n"); + ok(!DeleteFileA("foo"), "File not moved\n"); + ok(!DeleteFileA("fao"), "File not moved\n"); ok(DeleteFileA("fbod"), "File moved\n"); - todo_wine ok(!DeleteFileA("budding"), "File not moved\n"); - todo_wine ok(!DeleteFileA("buddy"), "File not moved\n"); + ok(!DeleteFileA("budding"), "File not moved\n"); + ok(!DeleteFileA("buddy"), "File not moved\n"); ok(DeleteFileA("bud"), "File moved\n"); - todo_wine ok(!DeleteFileA("bar"), "File not moved\n"); - todo_wine ok(!DeleteFileA("bur"), "File not moved\n"); + ok(!DeleteFileA("bar"), "File not moved\n"); + ok(!DeleteFileA("bur"), "File not moved\n"); ok(DeleteFileA("bird"), "File moved\n"); DeleteFile("msitest\\augustus"); diff --git a/include/msidefs.h b/include/msidefs.h index ab9dcb6f99..379a22e19a 100644 --- a/include/msidefs.h +++ b/include/msidefs.h @@ -196,6 +196,11 @@ enum msidbServiceControlEvent msidbServiceControlEventUninstallDelete = 0x00000080, }; +enum msidbMoveFileOptions +{ + msidbMoveFileOptionsMove = 0x00000001, +}; + /* * Windows SDK braindamage alert * -- 2.32.0.93.g670b81a890