- build a standard Wine list of components instead of using an array
[wine/dcerpc.git] / dlls / msi / files.c
blob9cc79c006724ce8106477b4987b9a5928bf38621
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2005 Aric Stewart for CodeWeavers
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * Actions dealing with files These are
25 * InstallFiles
26 * DuplicateFiles
27 * MoveFiles (TODO)
28 * PatchFiles (TODO)
29 * RemoveDuplicateFiles(TODO)
30 * RemoveFiles(TODO)
33 #include <stdarg.h>
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winerror.h"
38 #include "wine/debug.h"
39 #include "fdi.h"
40 #include "msi.h"
41 #include "msidefs.h"
42 #include "msvcrt/fcntl.h"
43 #include "msipriv.h"
44 #include "winuser.h"
45 #include "wine/unicode.h"
46 #include "action.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(msi);
50 extern const WCHAR szInstallFiles[];
51 extern const WCHAR szDuplicateFiles[];
52 extern const WCHAR szMoveFiles[];
53 extern const WCHAR szPatchFiles[];
54 extern const WCHAR szRemoveDuplicateFiles[];
55 extern const WCHAR szRemoveFiles[];
57 static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};
59 static UINT create_component_directory( MSIPACKAGE* package, MSICOMPONENT *comp )
61 UINT rc = ERROR_SUCCESS;
62 MSIFOLDER *folder;
63 LPWSTR install_path;
65 install_path = resolve_folder(package, comp->Directory, FALSE, FALSE, &folder);
66 if (!install_path)
67 return ERROR_FUNCTION_FAILED;
69 /* create the path */
70 if (folder->State == 0)
72 create_full_pathW(install_path);
73 folder->State = 2;
75 HeapFree(GetProcessHeap(), 0, install_path);
77 return rc;
81 * This is a helper function for handling embedded cabinet media
83 static UINT writeout_cabinet_stream(MSIPACKAGE *package, LPCWSTR stream_name,
84 WCHAR* source)
86 UINT rc;
87 USHORT* data;
88 UINT size;
89 DWORD write;
90 HANDLE the_file;
91 WCHAR tmp[MAX_PATH];
93 rc = read_raw_stream_data(package->db,stream_name,&data,&size);
94 if (rc != ERROR_SUCCESS)
95 return rc;
97 write = MAX_PATH;
98 if (MSI_GetPropertyW(package, cszTempFolder, tmp, &write))
99 GetTempPathW(MAX_PATH,tmp);
101 GetTempFileNameW(tmp,stream_name,0,source);
103 track_tempfile(package,strrchrW(source,'\\'), source);
104 the_file = CreateFileW(source, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
105 FILE_ATTRIBUTE_NORMAL, NULL);
107 if (the_file == INVALID_HANDLE_VALUE)
109 ERR("Unable to create file %s\n",debugstr_w(source));
110 rc = ERROR_FUNCTION_FAILED;
111 goto end;
114 WriteFile(the_file,data,size,&write,NULL);
115 CloseHandle(the_file);
116 TRACE("wrote %li bytes to %s\n",write,debugstr_w(source));
117 end:
118 HeapFree(GetProcessHeap(),0,data);
119 return rc;
123 /* Support functions for FDI functions */
124 typedef struct
126 MSIPACKAGE* package;
127 LPCSTR cab_path;
128 } CabData;
130 static void * cabinet_alloc(ULONG cb)
132 return HeapAlloc(GetProcessHeap(), 0, cb);
135 static void cabinet_free(void *pv)
137 HeapFree(GetProcessHeap(), 0, pv);
140 static INT_PTR cabinet_open(char *pszFile, int oflag, int pmode)
142 DWORD dwAccess = 0;
143 DWORD dwShareMode = 0;
144 DWORD dwCreateDisposition = OPEN_EXISTING;
145 switch (oflag & _O_ACCMODE)
147 case _O_RDONLY:
148 dwAccess = GENERIC_READ;
149 dwShareMode = FILE_SHARE_READ | FILE_SHARE_DELETE;
150 break;
151 case _O_WRONLY:
152 dwAccess = GENERIC_WRITE;
153 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
154 break;
155 case _O_RDWR:
156 dwAccess = GENERIC_READ | GENERIC_WRITE;
157 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
158 break;
160 if ((oflag & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL))
161 dwCreateDisposition = CREATE_NEW;
162 else if (oflag & _O_CREAT)
163 dwCreateDisposition = CREATE_ALWAYS;
164 return (INT_PTR)CreateFileA(pszFile, dwAccess, dwShareMode, NULL,
165 dwCreateDisposition, 0, NULL);
168 static UINT cabinet_read(INT_PTR hf, void *pv, UINT cb)
170 DWORD dwRead;
171 if (ReadFile((HANDLE)hf, pv, cb, &dwRead, NULL))
172 return dwRead;
173 return 0;
176 static UINT cabinet_write(INT_PTR hf, void *pv, UINT cb)
178 DWORD dwWritten;
179 if (WriteFile((HANDLE)hf, pv, cb, &dwWritten, NULL))
180 return dwWritten;
181 return 0;
184 static int cabinet_close(INT_PTR hf)
186 return CloseHandle((HANDLE)hf) ? 0 : -1;
189 static long cabinet_seek(INT_PTR hf, long dist, int seektype)
191 /* flags are compatible and so are passed straight through */
192 return SetFilePointer((HANDLE)hf, dist, NULL, seektype);
195 static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
197 switch (fdint)
199 case fdintCOPY_FILE:
201 CabData *data = (CabData*) pfdin->pv;
202 ULONG len = strlen(data->cab_path) + strlen(pfdin->psz1);
203 char *file;
205 LPWSTR trackname;
206 LPWSTR trackpath;
207 LPWSTR tracknametmp;
208 static const WCHAR tmpprefix[] = {'C','A','B','T','M','P','_',0};
209 LPWSTR given_file;
210 INT index;
212 MSIRECORD * uirow;
213 LPWSTR uipath;
215 given_file = strdupAtoW(pfdin->psz1);
216 index = get_loaded_file(data->package, given_file);
218 if (index < 0)
220 ERR("Unknown File in Cabinent (%s)\n",debugstr_w(given_file));
221 HeapFree(GetProcessHeap(),0,given_file);
222 return 0;
225 if (!((data->package->files[index].State == 1 ||
226 data->package->files[index].State == 2)))
228 TRACE("Skipping extraction of %s\n",debugstr_w(given_file));
229 HeapFree(GetProcessHeap(),0,given_file);
230 return 0;
233 file = cabinet_alloc((len+1)*sizeof(char));
234 strcpy(file, data->cab_path);
235 strcat(file, pfdin->psz1);
237 TRACE("file: %s\n", debugstr_a(file));
239 /* track this file so it can be deleted if not installed */
240 trackpath=strdupAtoW(file);
241 tracknametmp=strdupAtoW(strrchr(file,'\\')+1);
242 trackname = HeapAlloc(GetProcessHeap(),0,(strlenW(tracknametmp) +
243 strlenW(tmpprefix)+1) * sizeof(WCHAR));
245 strcpyW(trackname,tmpprefix);
246 strcatW(trackname,tracknametmp);
248 track_tempfile(data->package, trackname, trackpath);
250 HeapFree(GetProcessHeap(),0,trackpath);
251 HeapFree(GetProcessHeap(),0,trackname);
252 HeapFree(GetProcessHeap(),0,tracknametmp);
254 /* the UI chunk */
255 uirow=MSI_CreateRecord(9);
256 MSI_RecordSetStringW(uirow,1,data->package->files[index].File);
257 uipath = strdupW(data->package->files[index].TargetPath);
258 *(strrchrW(uipath,'\\')+1)=0;
259 MSI_RecordSetStringW(uirow,9,uipath);
260 MSI_RecordSetInteger(uirow,6,data->package->files[index].FileSize);
261 ui_actiondata(data->package,szInstallFiles,uirow);
262 msiobj_release( &uirow->hdr );
263 HeapFree(GetProcessHeap(),0,uipath);
265 ui_progress(data->package,2,data->package->files[index].FileSize,0,0);
267 return cabinet_open(file, _O_WRONLY | _O_CREAT, 0);
269 case fdintCLOSE_FILE_INFO:
271 FILETIME ft;
272 FILETIME ftLocal;
273 if (!DosDateTimeToFileTime(pfdin->date, pfdin->time, &ft))
274 return -1;
275 if (!LocalFileTimeToFileTime(&ft, &ftLocal))
276 return -1;
277 if (!SetFileTime((HANDLE)pfdin->hf, &ftLocal, 0, &ftLocal))
278 return -1;
280 cabinet_close(pfdin->hf);
281 return 1;
283 default:
284 return 0;
288 /***********************************************************************
289 * extract_cabinet_file
291 * Extract files from a cab file.
293 static BOOL extract_cabinet_file(MSIPACKAGE* package, LPCWSTR source,
294 LPCWSTR path)
296 HFDI hfdi;
297 ERF erf;
298 BOOL ret;
299 char *cabinet;
300 char *cab_path;
301 CabData data;
303 TRACE("Extracting %s to %s\n",debugstr_w(source), debugstr_w(path));
305 hfdi = FDICreate(cabinet_alloc,
306 cabinet_free,
307 cabinet_open,
308 cabinet_read,
309 cabinet_write,
310 cabinet_close,
311 cabinet_seek,
313 &erf);
314 if (!hfdi)
316 ERR("FDICreate failed\n");
317 return FALSE;
320 if (!(cabinet = strdupWtoA( source )))
322 FDIDestroy(hfdi);
323 return FALSE;
325 if (!(cab_path = strdupWtoA( path )))
327 FDIDestroy(hfdi);
328 HeapFree(GetProcessHeap(), 0, cabinet);
329 return FALSE;
332 data.package = package;
333 data.cab_path = cab_path;
335 ret = FDICopy(hfdi, cabinet, "", 0, cabinet_notify, NULL, &data);
337 if (!ret)
338 ERR("FDICopy failed\n");
340 FDIDestroy(hfdi);
342 HeapFree(GetProcessHeap(), 0, cabinet);
343 HeapFree(GetProcessHeap(), 0, cab_path);
345 return ret;
348 static VOID set_file_source(MSIPACKAGE* package, MSIFILE* file, MSICOMPONENT*
349 comp, LPCWSTR path)
351 if (file->Attributes & msidbFileAttributesNoncompressed)
353 LPWSTR p;
354 p = resolve_folder(package, comp->Directory, TRUE, FALSE, NULL);
355 file->SourcePath = build_directory_name(2, p, file->ShortName);
356 HeapFree(GetProcessHeap(),0,p);
358 else
359 file->SourcePath = build_directory_name(2, path, file->File);
362 static BOOL check_volume(LPCWSTR path, LPCWSTR want_volume, LPWSTR volume,
363 UINT *intype)
365 WCHAR drive[4];
366 WCHAR name[MAX_PATH];
367 UINT type;
369 if (!(path[0] && path[1] == ':'))
370 return TRUE;
372 drive[0] = path[0];
373 drive[1] = path[1];
374 drive[2] = '\\';
375 drive[3] = 0;
376 TRACE("Checking volume %s .. (%s)\n",debugstr_w(drive), debugstr_w(want_volume));
377 type = GetDriveTypeW(drive);
378 TRACE("drive is of type %x\n",type);
380 if (type == DRIVE_UNKNOWN || type == DRIVE_NO_ROOT_DIR ||
381 type == DRIVE_FIXED || type == DRIVE_RAMDISK)
382 return TRUE;
384 GetVolumeInformationW(drive, name, MAX_PATH, NULL, NULL, NULL, NULL, 0);
385 TRACE("Drive contains %s\n", debugstr_w(name));
386 volume = strdupW(name);
387 if (*intype)
388 *intype=type;
389 return (strcmpiW(want_volume,name)==0);
392 static BOOL check_for_sourcefile(LPCWSTR source)
394 DWORD attrib = GetFileAttributesW(source);
395 return (!(attrib == INVALID_FILE_ATTRIBUTES));
398 static UINT ready_volume(MSIPACKAGE* package, LPCWSTR path, LPWSTR last_volume,
399 MSIRECORD *row,UINT *type )
401 LPWSTR volume = NULL;
402 LPCWSTR want_volume = MSI_RecordGetString(row, 5);
403 BOOL ok = check_volume(path, want_volume, volume, type);
405 TRACE("Readying Volume for %s (%s, %s)\n",debugstr_w(path), debugstr_w(want_volume), debugstr_w(last_volume));
407 if (check_for_sourcefile(path) && !ok)
409 FIXME("Found the Sourcefile but not on the correct volume.(%s,%s,%s)\n",
410 debugstr_w(path),debugstr_w(want_volume), debugstr_w(volume));
411 return ERROR_SUCCESS;
414 while (!ok)
416 INT rc;
417 LPCWSTR prompt;
418 LPWSTR msg;
420 prompt = MSI_RecordGetString(row,3);
421 msg = generate_error_string(package, 1302, 1, prompt);
422 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
423 HeapFree(GetProcessHeap(),0,volume);
424 HeapFree(GetProcessHeap(),0,msg);
425 if (rc == IDOK)
426 ok = check_for_sourcefile(path);
427 else
428 return ERROR_INSTALL_USEREXIT;
431 HeapFree(GetProcessHeap(),0,last_volume);
432 last_volume = strdupW(volume);
433 return ERROR_SUCCESS;
436 static UINT ready_media_for_file(MSIPACKAGE *package, int fileindex,
437 MSICOMPONENT* comp)
439 UINT rc = ERROR_SUCCESS;
440 MSIRECORD * row = 0;
441 static WCHAR source[MAX_PATH];
442 static const WCHAR ExecSeqQuery[] =
443 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
444 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
445 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',
446 ' ','%', 'i',' ','O','R','D','E','R',' ','B','Y',' ',
447 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',0};
448 LPCWSTR cab, volume;
449 DWORD sz;
450 INT seq;
451 static UINT last_sequence = 0;
452 static LPWSTR last_volume = NULL;
453 static LPWSTR last_path = NULL;
454 MSIFILE* file = NULL;
455 UINT type;
456 LPCWSTR prompt;
457 static DWORD count = 0;
459 /* cleanup signal */
460 if (!package)
462 HeapFree(GetProcessHeap(),0,last_path);
463 HeapFree(GetProcessHeap(),0,last_volume);
464 last_sequence = 0;
465 last_path = NULL;
466 last_volume = NULL;
467 count = 0;
468 memset(source,0,sizeof(source));
469 return ERROR_SUCCESS;
472 file = &package->files[fileindex];
474 if (file->Sequence <= last_sequence)
476 set_file_source(package,file,comp,last_path);
477 TRACE("Media already ready (%u, %u)\n",file->Sequence,last_sequence);
478 return ERROR_SUCCESS;
481 count ++;
482 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, file->Sequence);
483 if (!row)
485 TRACE("Unable to query row\n");
486 return ERROR_FUNCTION_FAILED;
489 seq = MSI_RecordGetInteger(row,2);
490 last_sequence = seq;
492 volume = MSI_RecordGetString(row, 5);
493 prompt = MSI_RecordGetString(row, 3);
495 HeapFree(GetProcessHeap(),0,last_path);
496 last_path = NULL;
498 if (file->Attributes & msidbFileAttributesNoncompressed)
500 last_path = resolve_folder(package, comp->Directory, TRUE, FALSE, NULL);
501 set_file_source(package,file,comp,last_path);
502 rc = ready_volume(package, file->SourcePath, last_volume, row,&type);
504 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
505 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, count, volume,
506 prompt);
508 if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM ||
509 type == DRIVE_RAMDISK)
510 MsiSourceListSetInfoW(package->ProductCode, NULL,
511 MSIINSTALLCONTEXT_USERMANAGED,
512 MSICODE_PRODUCT|MSISOURCETYPE_MEDIA,
513 INSTALLPROPERTY_LASTUSEDSOURCEW, last_path);
514 else
515 MsiSourceListSetInfoW(package->ProductCode, NULL,
516 MSIINSTALLCONTEXT_USERMANAGED,
517 MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
518 INSTALLPROPERTY_LASTUSEDSOURCEW, last_path);
519 msiobj_release(&row->hdr);
520 return rc;
523 cab = MSI_RecordGetString(row,4);
524 if (cab)
526 TRACE("Source is CAB %s\n",debugstr_w(cab));
527 /* the stream does not contain the # character */
528 if (cab[0]=='#')
530 LPWSTR path;
532 writeout_cabinet_stream(package,&cab[1],source);
533 last_path = strdupW(source);
534 *(strrchrW(last_path,'\\')+1)=0;
536 path = load_dynamic_property(package,cszSourceDir,NULL);
538 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
539 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, count,
540 volume, prompt);
542 MsiSourceListSetInfoW(package->ProductCode, NULL,
543 MSIINSTALLCONTEXT_USERMANAGED,
544 MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
545 INSTALLPROPERTY_LASTUSEDSOURCEW, path);
547 HeapFree(GetProcessHeap(),0,path);
549 else
551 sz = MAX_PATH;
552 last_path = HeapAlloc(GetProcessHeap(),0,MAX_PATH*sizeof(WCHAR));
553 if (MSI_GetPropertyW(package, cszSourceDir, source, &sz))
555 ERR("No Source dir defined \n");
556 rc = ERROR_FUNCTION_FAILED;
558 else
560 strcpyW(last_path,source);
561 strcatW(source,cab);
563 rc = ready_volume(package, source, last_volume, row, &type);
564 if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM ||
565 type == DRIVE_RAMDISK)
566 MsiSourceListSetInfoW(package->ProductCode, NULL,
567 MSIINSTALLCONTEXT_USERMANAGED,
568 MSICODE_PRODUCT|MSISOURCETYPE_MEDIA,
569 INSTALLPROPERTY_LASTUSEDSOURCEW, last_path);
570 else
571 MsiSourceListSetInfoW(package->ProductCode, NULL,
572 MSIINSTALLCONTEXT_USERMANAGED,
573 MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
574 INSTALLPROPERTY_LASTUSEDSOURCEW, last_path);
576 /* extract the cab file into a folder in the temp folder */
577 sz = MAX_PATH;
578 if (MSI_GetPropertyW(package, cszTempFolder,last_path, &sz)
579 != ERROR_SUCCESS)
580 GetTempPathW(MAX_PATH,last_path);
583 rc = !extract_cabinet_file(package, source, last_path);
584 /* reaquire file ptr */
585 file = &package->files[fileindex];
587 else
589 sz = MAX_PATH;
590 last_path = HeapAlloc(GetProcessHeap(),0,MAX_PATH*sizeof(WCHAR));
591 MSI_GetPropertyW(package,cszSourceDir,source,&sz);
592 strcpyW(last_path,source);
593 rc = ready_volume(package, last_path, last_volume, row, &type);
595 if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM ||
596 type == DRIVE_RAMDISK)
597 MsiSourceListSetInfoW(package->ProductCode, NULL,
598 MSIINSTALLCONTEXT_USERMANAGED,
599 MSICODE_PRODUCT|MSISOURCETYPE_MEDIA,
600 INSTALLPROPERTY_LASTUSEDSOURCEW, last_path);
601 else
602 MsiSourceListSetInfoW(package->ProductCode, NULL,
603 MSIINSTALLCONTEXT_USERMANAGED,
604 MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
605 INSTALLPROPERTY_LASTUSEDSOURCEW, last_path);
607 set_file_source(package, file, comp, last_path);
609 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
610 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, count, volume,
611 prompt);
613 msiobj_release(&row->hdr);
615 return rc;
618 inline static UINT get_file_target(MSIPACKAGE *package, LPCWSTR file_key,
619 LPWSTR* file_source)
621 DWORD index;
623 if (!package)
624 return ERROR_INVALID_HANDLE;
626 for (index = 0; index < package->loaded_files; index ++)
628 if (strcmpW(file_key,package->files[index].File)==0)
630 if (package->files[index].State >= 2)
632 *file_source = strdupW(package->files[index].TargetPath);
633 return ERROR_SUCCESS;
635 else
636 return ERROR_FILE_NOT_FOUND;
640 return ERROR_FUNCTION_FAILED;
644 * In order to make this work more effeciencly I am going to do this in 2
645 * passes.
646 * Pass 1) Correct all the TargetPaths and determin what files are to be
647 * installed.
648 * Pass 2) Extract Cabinents and copy files.
650 UINT ACTION_InstallFiles(MSIPACKAGE *package)
652 UINT rc = ERROR_SUCCESS;
653 DWORD index;
654 LPWSTR ptr;
656 if (!package)
657 return ERROR_INVALID_HANDLE;
659 /* increment progress bar each time action data is sent */
660 ui_progress(package,1,1,0,0);
662 /* handle the keys for the SouceList */
663 ptr = strrchrW(package->PackagePath,'\\');
664 if (ptr)
666 ptr ++;
667 MsiSourceListSetInfoW(package->ProductCode, NULL,
668 MSIINSTALLCONTEXT_USERMANAGED,
669 MSICODE_PRODUCT,
670 INSTALLPROPERTY_PACKAGENAMEW, ptr);
672 FIXME("Write DiskPrompt\n");
674 /* Pass 1 */
675 for (index = 0; index < package->loaded_files; index++)
677 MSIFILE *file;
678 MSICOMPONENT* comp = NULL;
680 file = &package->files[index];
682 if (file->Temporary)
683 continue;
685 if (!ACTION_VerifyComponentForAction(package, file->Component,
686 INSTALLSTATE_LOCAL))
688 ui_progress(package,2,file->FileSize,0,0);
689 TRACE("File %s is not scheduled for install\n",
690 debugstr_w(file->File));
692 file->State = 5;
693 continue;
696 if ((file->State == 1) || (file->State == 2))
698 LPWSTR p = NULL;
700 TRACE("Pass 1: %s\n",debugstr_w(file->File));
702 create_component_directory( package, file->Component );
704 /* recalculate file paths because things may have changed */
706 comp = file->Component;
707 if (!comp)
709 ERR("No Component for file\n");
710 continue;
713 p = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL);
714 HeapFree(GetProcessHeap(),0,file->TargetPath);
716 file->TargetPath = build_directory_name(2, p, file->FileName);
717 HeapFree(GetProcessHeap(),0,p);
721 /* Pass 2 */
722 for (index = 0; index < package->loaded_files; index++)
724 MSIFILE *file;
726 file = &package->files[index];
728 if (file->Temporary)
729 continue;
731 if ((file->State == 1) || (file->State == 2))
733 TRACE("Pass 2: %s\n",debugstr_w(file->File));
735 rc = ready_media_for_file( package, index, file->Component );
736 if (rc != ERROR_SUCCESS)
738 ERR("Unable to ready media\n");
739 rc = ERROR_FUNCTION_FAILED;
740 break;
744 * WARNING!
745 * our file table could change here because a new temp file
746 * may have been created. So reaquire our ptr.
748 file = &package->files[index];
750 TRACE("file paths %s to %s\n",debugstr_w(file->SourcePath),
751 debugstr_w(file->TargetPath));
753 if (file->Attributes & msidbFileAttributesNoncompressed)
754 rc = CopyFileW(file->SourcePath,file->TargetPath,FALSE);
755 else
756 rc = MoveFileW(file->SourcePath, file->TargetPath);
758 if (!rc)
760 rc = GetLastError();
761 ERR("Unable to move/copy file (%s -> %s) (error %d)\n",
762 debugstr_w(file->SourcePath), debugstr_w(file->TargetPath),
763 rc);
764 if (rc == ERROR_ALREADY_EXISTS && file->State == 2)
766 if (!CopyFileW(file->SourcePath,file->TargetPath,FALSE))
767 ERR("Unable to copy file (%s -> %s) (error %ld)\n",
768 debugstr_w(file->SourcePath),
769 debugstr_w(file->TargetPath), GetLastError());
770 if (!(file->Attributes & msidbFileAttributesNoncompressed))
771 DeleteFileW(file->SourcePath);
772 rc = 0;
774 else if (rc == ERROR_FILE_NOT_FOUND)
776 ERR("Source File Not Found! Continuing\n");
777 rc = 0;
779 else if (file->Attributes & msidbFileAttributesVital)
781 ERR("Ignoring Error and continuing (nonvital file)...\n");
782 rc = 0;
785 else
787 file->State = 4;
788 rc = ERROR_SUCCESS;
793 /* cleanup */
794 ready_media_for_file(NULL, 0, NULL);
795 return rc;
798 static UINT ITERATE_DuplicateFiles(MSIRECORD *row, LPVOID param)
800 MSIPACKAGE *package = (MSIPACKAGE*)param;
801 WCHAR *file_source = NULL;
802 WCHAR dest_name[0x100];
803 LPWSTR dest_path, dest;
804 LPCWSTR file_key, component;
805 DWORD sz;
806 DWORD rc;
807 MSICOMPONENT *comp;
809 component = MSI_RecordGetString(row,2);
810 comp = get_loaded_component(package,component);
812 if (!ACTION_VerifyComponentForAction(package, comp, INSTALLSTATE_LOCAL))
814 TRACE("Skipping copy due to disabled component %s\n",
815 debugstr_w(component));
817 /* the action taken was the same as the current install state */
818 comp->Action = comp->Installed;
820 return ERROR_SUCCESS;
823 comp->Action = INSTALLSTATE_LOCAL;
825 file_key = MSI_RecordGetString(row,3);
826 if (!file_key)
828 ERR("Unable to get file key\n");
829 return ERROR_FUNCTION_FAILED;
832 rc = get_file_target(package,file_key,&file_source);
834 if (rc != ERROR_SUCCESS)
836 ERR("Original file unknown %s\n",debugstr_w(file_key));
837 HeapFree(GetProcessHeap(),0,file_source);
838 return ERROR_SUCCESS;
841 if (MSI_RecordIsNull(row,4))
842 strcpyW(dest_name,strrchrW(file_source,'\\')+1);
843 else
845 sz=0x100;
846 MSI_RecordGetStringW(row,4,dest_name,&sz);
847 reduce_to_longfilename(dest_name);
850 if (MSI_RecordIsNull(row,5))
852 LPWSTR p;
853 dest_path = strdupW(file_source);
854 p = strrchrW(dest_path,'\\');
855 if (p)
856 *p=0;
858 else
860 LPCWSTR destkey;
861 destkey = MSI_RecordGetString(row,5);
862 dest_path = resolve_folder(package, destkey, FALSE,FALSE,NULL);
863 if (!dest_path)
865 /* try a Property */
866 dest_path = load_dynamic_property(package, destkey, NULL);
867 if (!dest_path)
869 FIXME("Unable to get destination folder, try AppSearch properties\n");
870 HeapFree(GetProcessHeap(),0,file_source);
871 return ERROR_SUCCESS;
876 dest = build_directory_name(2, dest_path, dest_name);
878 TRACE("Duplicating file %s to %s\n",debugstr_w(file_source),
879 debugstr_w(dest));
881 if (strcmpW(file_source,dest))
882 rc = !CopyFileW(file_source,dest,TRUE);
883 else
884 rc = ERROR_SUCCESS;
886 if (rc != ERROR_SUCCESS)
887 ERR("Failed to copy file %s -> %s, last error %ld\n", debugstr_w(file_source), debugstr_w(dest_path), GetLastError());
889 FIXME("We should track these duplicate files as well\n");
891 HeapFree(GetProcessHeap(),0,dest_path);
892 HeapFree(GetProcessHeap(),0,dest);
893 HeapFree(GetProcessHeap(),0,file_source);
895 return ERROR_SUCCESS;
898 UINT ACTION_DuplicateFiles(MSIPACKAGE *package)
900 UINT rc;
901 MSIQUERY * view;
902 static const WCHAR ExecSeqQuery[] =
903 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
904 '`','D','u','p','l','i','c','a','t','e','F','i','l','e','`',0};
906 if (!package)
907 return ERROR_INVALID_HANDLE;
909 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
910 if (rc != ERROR_SUCCESS)
911 return ERROR_SUCCESS;
913 rc = MSI_IterateRecords(view, NULL, ITERATE_DuplicateFiles, package);
914 msiobj_release(&view->hdr);
916 return rc;