msi: Implement the MoveFiles standard action.
This commit is contained in:
parent
b9a29e221f
commit
c3df74e24f
|
@ -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 };
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -196,6 +196,11 @@ enum msidbServiceControlEvent
|
|||
msidbServiceControlEventUninstallDelete = 0x00000080,
|
||||
};
|
||||
|
||||
enum msidbMoveFileOptions
|
||||
{
|
||||
msidbMoveFileOptionsMove = 0x00000001,
|
||||
};
|
||||
|
||||
/*
|
||||
* Windows SDK braindamage alert
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue