Create component folders in the CreateFolders action.
[wine.git] / dlls / msi / files.c
bloba32ef1b52777ef4e6deae19608c737a73457c800
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};
61 * This is a helper function for handling embedded cabinet media
63 static UINT writeout_cabinet_stream(MSIPACKAGE *package, LPCWSTR stream_name,
64 WCHAR* source)
66 UINT rc;
67 USHORT* data;
68 UINT size;
69 DWORD write;
70 HANDLE the_file;
71 WCHAR tmp[MAX_PATH];
73 rc = read_raw_stream_data(package->db,stream_name,&data,&size);
74 if (rc != ERROR_SUCCESS)
75 return rc;
77 write = MAX_PATH;
78 if (MSI_GetPropertyW(package, cszTempFolder, tmp, &write))
79 GetTempPathW(MAX_PATH,tmp);
81 GetTempFileNameW(tmp,stream_name,0,source);
83 track_tempfile(package,strrchrW(source,'\\'), source);
84 the_file = CreateFileW(source, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
85 FILE_ATTRIBUTE_NORMAL, NULL);
87 if (the_file == INVALID_HANDLE_VALUE)
89 ERR("Unable to create file %s\n",debugstr_w(source));
90 rc = ERROR_FUNCTION_FAILED;
91 goto end;
94 WriteFile(the_file,data,size,&write,NULL);
95 CloseHandle(the_file);
96 TRACE("wrote %li bytes to %s\n",write,debugstr_w(source));
97 end:
98 msi_free(data);
99 return rc;
103 /* Support functions for FDI functions */
104 typedef struct
106 MSIPACKAGE* package;
107 LPCSTR cab_path;
108 } CabData;
110 static void * cabinet_alloc(ULONG cb)
112 return msi_alloc(cb);
115 static void cabinet_free(void *pv)
117 msi_free(pv);
120 static INT_PTR cabinet_open(char *pszFile, int oflag, int pmode)
122 DWORD dwAccess = 0;
123 DWORD dwShareMode = 0;
124 DWORD dwCreateDisposition = OPEN_EXISTING;
125 switch (oflag & _O_ACCMODE)
127 case _O_RDONLY:
128 dwAccess = GENERIC_READ;
129 dwShareMode = FILE_SHARE_READ | FILE_SHARE_DELETE;
130 break;
131 case _O_WRONLY:
132 dwAccess = GENERIC_WRITE;
133 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
134 break;
135 case _O_RDWR:
136 dwAccess = GENERIC_READ | GENERIC_WRITE;
137 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
138 break;
140 if ((oflag & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL))
141 dwCreateDisposition = CREATE_NEW;
142 else if (oflag & _O_CREAT)
143 dwCreateDisposition = CREATE_ALWAYS;
144 return (INT_PTR)CreateFileA(pszFile, dwAccess, dwShareMode, NULL,
145 dwCreateDisposition, 0, NULL);
148 static UINT cabinet_read(INT_PTR hf, void *pv, UINT cb)
150 DWORD dwRead;
151 if (ReadFile((HANDLE)hf, pv, cb, &dwRead, NULL))
152 return dwRead;
153 return 0;
156 static UINT cabinet_write(INT_PTR hf, void *pv, UINT cb)
158 DWORD dwWritten;
159 if (WriteFile((HANDLE)hf, pv, cb, &dwWritten, NULL))
160 return dwWritten;
161 return 0;
164 static int cabinet_close(INT_PTR hf)
166 return CloseHandle((HANDLE)hf) ? 0 : -1;
169 static long cabinet_seek(INT_PTR hf, long dist, int seektype)
171 /* flags are compatible and so are passed straight through */
172 return SetFilePointer((HANDLE)hf, dist, NULL, seektype);
175 static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
177 switch (fdint)
179 case fdintCOPY_FILE:
181 CabData *data = (CabData*) pfdin->pv;
182 ULONG len = strlen(data->cab_path) + strlen(pfdin->psz1);
183 char *file;
185 LPWSTR trackname;
186 LPWSTR trackpath;
187 LPWSTR tracknametmp;
188 static const WCHAR tmpprefix[] = {'C','A','B','T','M','P','_',0};
189 LPWSTR given_file;
191 MSIRECORD * uirow;
192 LPWSTR uipath;
193 MSIFILE *f;
195 given_file = strdupAtoW(pfdin->psz1);
196 f = get_loaded_file(data->package, given_file);
197 msi_free(given_file);
199 if (!f)
201 ERR("Unknown File in Cabinet (%s)\n",debugstr_a(pfdin->psz1));
202 return 0;
205 if (!((f->State == 1 || f->State == 2)))
207 TRACE("Skipping extraction of %s\n",debugstr_a(pfdin->psz1));
208 return 0;
211 file = cabinet_alloc((len+1)*sizeof(char));
212 strcpy(file, data->cab_path);
213 strcat(file, pfdin->psz1);
215 TRACE("file: %s\n", debugstr_a(file));
217 /* track this file so it can be deleted if not installed */
218 trackpath=strdupAtoW(file);
219 tracknametmp=strdupAtoW(strrchr(file,'\\')+1);
220 trackname = msi_alloc((strlenW(tracknametmp) +
221 strlenW(tmpprefix)+1) * sizeof(WCHAR));
223 strcpyW(trackname,tmpprefix);
224 strcatW(trackname,tracknametmp);
226 track_tempfile(data->package, trackname, trackpath);
228 msi_free(trackpath);
229 msi_free(trackname);
230 msi_free(tracknametmp);
232 /* the UI chunk */
233 uirow=MSI_CreateRecord(9);
234 MSI_RecordSetStringW( uirow, 1, f->FileName );
235 uipath = strdupW( f->TargetPath );
236 *(strrchrW(uipath,'\\')+1)=0;
237 MSI_RecordSetStringW(uirow,9,uipath);
238 MSI_RecordSetInteger( uirow, 6, f->FileSize );
239 ui_actiondata(data->package,szInstallFiles,uirow);
240 msiobj_release( &uirow->hdr );
241 msi_free(uipath);
243 ui_progress( data->package, 2, f->FileSize, 0, 0);
245 return cabinet_open(file, _O_WRONLY | _O_CREAT, 0);
247 case fdintCLOSE_FILE_INFO:
249 FILETIME ft;
250 FILETIME ftLocal;
251 if (!DosDateTimeToFileTime(pfdin->date, pfdin->time, &ft))
252 return -1;
253 if (!LocalFileTimeToFileTime(&ft, &ftLocal))
254 return -1;
255 if (!SetFileTime((HANDLE)pfdin->hf, &ftLocal, 0, &ftLocal))
256 return -1;
258 cabinet_close(pfdin->hf);
259 return 1;
261 default:
262 return 0;
266 /***********************************************************************
267 * extract_cabinet_file
269 * Extract files from a cab file.
271 static BOOL extract_cabinet_file(MSIPACKAGE* package, LPCWSTR source,
272 LPCWSTR path)
274 HFDI hfdi;
275 ERF erf;
276 BOOL ret;
277 char *cabinet;
278 char *cab_path;
279 CabData data;
281 TRACE("Extracting %s to %s\n",debugstr_w(source), debugstr_w(path));
283 hfdi = FDICreate(cabinet_alloc,
284 cabinet_free,
285 cabinet_open,
286 cabinet_read,
287 cabinet_write,
288 cabinet_close,
289 cabinet_seek,
291 &erf);
292 if (!hfdi)
294 ERR("FDICreate failed\n");
295 return FALSE;
298 if (!(cabinet = strdupWtoA( source )))
300 FDIDestroy(hfdi);
301 return FALSE;
303 if (!(cab_path = strdupWtoA( path )))
305 FDIDestroy(hfdi);
306 msi_free(cabinet);
307 return FALSE;
310 data.package = package;
311 data.cab_path = cab_path;
313 ret = FDICopy(hfdi, cabinet, "", 0, cabinet_notify, NULL, &data);
315 if (!ret)
316 ERR("FDICopy failed\n");
318 FDIDestroy(hfdi);
320 msi_free(cabinet);
321 msi_free(cab_path);
323 return ret;
326 static VOID set_file_source(MSIPACKAGE* package, MSIFILE* file, MSICOMPONENT*
327 comp, LPCWSTR path)
329 if (file->Attributes & msidbFileAttributesNoncompressed)
331 LPWSTR p;
332 p = resolve_folder(package, comp->Directory, TRUE, FALSE, NULL);
333 file->SourcePath = build_directory_name(2, p, file->ShortName);
334 msi_free(p);
336 else
337 file->SourcePath = build_directory_name(2, path, file->File);
340 static BOOL check_volume(LPCWSTR path, LPCWSTR want_volume, LPWSTR volume,
341 UINT *intype)
343 WCHAR drive[4];
344 WCHAR name[MAX_PATH];
345 UINT type;
347 if (!(path[0] && path[1] == ':'))
348 return TRUE;
350 drive[0] = path[0];
351 drive[1] = path[1];
352 drive[2] = '\\';
353 drive[3] = 0;
354 TRACE("Checking volume %s .. (%s)\n",debugstr_w(drive), debugstr_w(want_volume));
355 type = GetDriveTypeW(drive);
356 TRACE("drive is of type %x\n",type);
358 if (type == DRIVE_UNKNOWN || type == DRIVE_NO_ROOT_DIR ||
359 type == DRIVE_FIXED || type == DRIVE_RAMDISK)
360 return TRUE;
362 GetVolumeInformationW(drive, name, MAX_PATH, NULL, NULL, NULL, NULL, 0);
363 TRACE("Drive contains %s\n", debugstr_w(name));
364 volume = strdupW(name);
365 if (*intype)
366 *intype=type;
367 return (strcmpiW(want_volume,name)==0);
370 static BOOL check_for_sourcefile(LPCWSTR source)
372 DWORD attrib = GetFileAttributesW(source);
373 return (!(attrib == INVALID_FILE_ATTRIBUTES));
376 static UINT ready_volume(MSIPACKAGE* package, LPCWSTR path, LPWSTR last_volume,
377 MSIRECORD *row,UINT *type )
379 LPWSTR volume = NULL;
380 LPCWSTR want_volume = MSI_RecordGetString(row, 5);
381 BOOL ok = check_volume(path, want_volume, volume, type);
383 TRACE("Readying Volume for %s (%s, %s)\n",debugstr_w(path), debugstr_w(want_volume), debugstr_w(last_volume));
385 if (check_for_sourcefile(path) && !ok)
387 FIXME("Found the Sourcefile but not on the correct volume.(%s,%s,%s)\n",
388 debugstr_w(path),debugstr_w(want_volume), debugstr_w(volume));
389 return ERROR_SUCCESS;
392 while (!ok)
394 INT rc;
395 LPCWSTR prompt;
396 LPWSTR msg;
398 prompt = MSI_RecordGetString(row,3);
399 msg = generate_error_string(package, 1302, 1, prompt);
400 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
401 msi_free(volume);
402 msi_free(msg);
403 if (rc == IDOK)
404 ok = check_for_sourcefile(path);
405 else
406 return ERROR_INSTALL_USEREXIT;
409 msi_free(last_volume);
410 last_volume = strdupW(volume);
411 return ERROR_SUCCESS;
414 struct media_info {
415 UINT last_sequence;
416 LPWSTR last_volume;
417 LPWSTR last_path;
418 DWORD count;
419 WCHAR source[MAX_PATH];
422 static struct media_info *create_media_info( void )
424 struct media_info *mi;
426 mi = msi_alloc( sizeof *mi );
427 if (mi)
429 mi->last_sequence = 0;
430 mi->last_volume = NULL;
431 mi->last_path = NULL;
432 mi->count = 0;
433 mi->source[0] = 0;
436 return mi;
439 static void free_media_info( struct media_info *mi )
441 msi_free( mi->last_path );
442 msi_free( mi );
445 static UINT ready_media_for_file( MSIPACKAGE *package, struct media_info *mi,
446 MSIFILE *file )
448 UINT rc = ERROR_SUCCESS;
449 MSIRECORD * row = 0;
450 static const WCHAR ExecSeqQuery[] =
451 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
452 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
453 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',
454 ' ','%', 'i',' ','O','R','D','E','R',' ','B','Y',' ',
455 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',0};
456 LPCWSTR cab, volume;
457 DWORD sz;
458 INT seq;
459 UINT type;
460 LPCWSTR prompt;
461 MSICOMPONENT *comp = file->Component;
463 if (file->Sequence <= mi->last_sequence)
465 set_file_source(package,file,comp,mi->last_path);
466 TRACE("Media already ready (%u, %u)\n",file->Sequence,mi->last_sequence);
467 return ERROR_SUCCESS;
470 mi->count ++;
471 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, file->Sequence);
472 if (!row)
474 TRACE("Unable to query row\n");
475 return ERROR_FUNCTION_FAILED;
478 seq = MSI_RecordGetInteger(row,2);
479 mi->last_sequence = seq;
481 volume = MSI_RecordGetString(row, 5);
482 prompt = MSI_RecordGetString(row, 3);
484 msi_free(mi->last_path);
485 mi->last_path = NULL;
487 if (file->Attributes & msidbFileAttributesNoncompressed)
489 mi->last_path = resolve_folder(package, comp->Directory, TRUE, FALSE, NULL);
490 set_file_source(package,file,comp,mi->last_path);
491 rc = ready_volume(package, file->SourcePath, mi->last_volume, row,&type);
493 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
494 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, mi->count, volume,
495 prompt);
497 if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM ||
498 type == DRIVE_RAMDISK)
499 MsiSourceListSetInfoW(package->ProductCode, NULL,
500 MSIINSTALLCONTEXT_USERMANAGED,
501 MSICODE_PRODUCT|MSISOURCETYPE_MEDIA,
502 INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
503 else
504 MsiSourceListSetInfoW(package->ProductCode, NULL,
505 MSIINSTALLCONTEXT_USERMANAGED,
506 MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
507 INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
508 msiobj_release(&row->hdr);
509 return rc;
512 cab = MSI_RecordGetString(row,4);
513 if (cab)
515 TRACE("Source is CAB %s\n",debugstr_w(cab));
516 /* the stream does not contain the # character */
517 if (cab[0]=='#')
519 LPWSTR path;
521 writeout_cabinet_stream(package,&cab[1],mi->source);
522 mi->last_path = strdupW(mi->source);
523 *(strrchrW(mi->last_path,'\\')+1)=0;
525 path = msi_dup_property( package, cszSourceDir );
527 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
528 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, mi->count,
529 volume, prompt);
531 MsiSourceListSetInfoW(package->ProductCode, NULL,
532 MSIINSTALLCONTEXT_USERMANAGED,
533 MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
534 INSTALLPROPERTY_LASTUSEDSOURCEW, path);
536 msi_free(path);
538 else
540 sz = MAX_PATH;
541 mi->last_path = msi_alloc(MAX_PATH*sizeof(WCHAR));
542 if (MSI_GetPropertyW(package, cszSourceDir, mi->source, &sz))
544 ERR("No Source dir defined \n");
545 rc = ERROR_FUNCTION_FAILED;
547 else
549 strcpyW(mi->last_path,mi->source);
550 strcatW(mi->source,cab);
552 rc = ready_volume(package, mi->source, mi->last_volume, row, &type);
553 if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM ||
554 type == DRIVE_RAMDISK)
555 MsiSourceListSetInfoW(package->ProductCode, NULL,
556 MSIINSTALLCONTEXT_USERMANAGED,
557 MSICODE_PRODUCT|MSISOURCETYPE_MEDIA,
558 INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
559 else
560 MsiSourceListSetInfoW(package->ProductCode, NULL,
561 MSIINSTALLCONTEXT_USERMANAGED,
562 MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
563 INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
565 /* extract the cab file into a folder in the temp folder */
566 sz = MAX_PATH;
567 if (MSI_GetPropertyW(package, cszTempFolder,mi->last_path, &sz)
568 != ERROR_SUCCESS)
569 GetTempPathW(MAX_PATH,mi->last_path);
572 rc = !extract_cabinet_file(package, mi->source, mi->last_path);
574 else
576 sz = MAX_PATH;
577 mi->last_path = msi_alloc(MAX_PATH*sizeof(WCHAR));
578 MSI_GetPropertyW(package,cszSourceDir,mi->source,&sz);
579 strcpyW(mi->last_path,mi->source);
580 rc = ready_volume(package, mi->last_path, mi->last_volume, row, &type);
582 if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM ||
583 type == DRIVE_RAMDISK)
584 MsiSourceListSetInfoW(package->ProductCode, NULL,
585 MSIINSTALLCONTEXT_USERMANAGED,
586 MSICODE_PRODUCT|MSISOURCETYPE_MEDIA,
587 INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
588 else
589 MsiSourceListSetInfoW(package->ProductCode, NULL,
590 MSIINSTALLCONTEXT_USERMANAGED,
591 MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
592 INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
594 set_file_source(package, file, comp, mi->last_path);
596 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
597 MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, mi->count, volume,
598 prompt);
600 msiobj_release(&row->hdr);
602 return rc;
605 static UINT get_file_target(MSIPACKAGE *package, LPCWSTR file_key,
606 LPWSTR* file_source)
608 MSIFILE *file;
610 if (!package)
611 return ERROR_INVALID_HANDLE;
613 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
615 if (lstrcmpW( file_key, file->File )==0)
617 if (file->State >= 2)
619 *file_source = strdupW( file->TargetPath );
620 return ERROR_SUCCESS;
622 else
623 return ERROR_FILE_NOT_FOUND;
627 return ERROR_FUNCTION_FAILED;
631 * In order to make this work more effeciencly I am going to do this in 2
632 * passes.
633 * Pass 1) Correct all the TargetPaths and determin what files are to be
634 * installed.
635 * Pass 2) Extract Cabinents and copy files.
637 UINT ACTION_InstallFiles(MSIPACKAGE *package)
639 struct media_info *mi;
640 UINT rc = ERROR_SUCCESS;
641 LPWSTR ptr;
642 MSIFILE *file;
644 if (!package)
645 return ERROR_INVALID_HANDLE;
647 /* increment progress bar each time action data is sent */
648 ui_progress(package,1,1,0,0);
650 /* handle the keys for the SouceList */
651 ptr = strrchrW(package->PackagePath,'\\');
652 if (ptr)
654 ptr ++;
655 MsiSourceListSetInfoW(package->ProductCode, NULL,
656 MSIINSTALLCONTEXT_USERMANAGED,
657 MSICODE_PRODUCT,
658 INSTALLPROPERTY_PACKAGENAMEW, ptr);
660 FIXME("Write DiskPrompt\n");
662 /* Pass 1 */
663 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
665 if (!ACTION_VerifyComponentForAction(package, file->Component,
666 INSTALLSTATE_LOCAL))
668 ui_progress(package,2,file->FileSize,0,0);
669 TRACE("File %s is not scheduled for install\n",
670 debugstr_w(file->File));
672 file->State = 5;
676 mi = create_media_info();
678 /* Pass 2 */
679 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
681 if ((file->State == 1) || (file->State == 2))
683 TRACE("Pass 2: %s\n",debugstr_w(file->File));
685 rc = ready_media_for_file( package, mi, file );
686 if (rc != ERROR_SUCCESS)
688 ERR("Unable to ready media\n");
689 rc = ERROR_FUNCTION_FAILED;
690 break;
693 TRACE("file paths %s to %s\n",debugstr_w(file->SourcePath),
694 debugstr_w(file->TargetPath));
696 if (file->Attributes & msidbFileAttributesNoncompressed)
697 rc = CopyFileW(file->SourcePath,file->TargetPath,FALSE);
698 else
699 rc = MoveFileW(file->SourcePath, file->TargetPath);
701 if (!rc)
703 rc = GetLastError();
704 ERR("Unable to move/copy file (%s -> %s) (error %d)\n",
705 debugstr_w(file->SourcePath), debugstr_w(file->TargetPath),
706 rc);
707 if (rc == ERROR_ALREADY_EXISTS && file->State == 2)
709 if (!CopyFileW(file->SourcePath,file->TargetPath,FALSE))
710 ERR("Unable to copy file (%s -> %s) (error %ld)\n",
711 debugstr_w(file->SourcePath),
712 debugstr_w(file->TargetPath), GetLastError());
713 if (!(file->Attributes & msidbFileAttributesNoncompressed))
714 DeleteFileW(file->SourcePath);
715 rc = 0;
717 else if (rc == ERROR_FILE_NOT_FOUND)
719 ERR("Source File Not Found! Continuing\n");
720 rc = 0;
722 else if (file->Attributes & msidbFileAttributesVital)
724 ERR("Ignoring Error and continuing (nonvital file)...\n");
725 rc = 0;
728 else
730 file->State = 4;
731 rc = ERROR_SUCCESS;
736 /* cleanup */
737 free_media_info( mi );
738 return rc;
741 static UINT ITERATE_DuplicateFiles(MSIRECORD *row, LPVOID param)
743 MSIPACKAGE *package = (MSIPACKAGE*)param;
744 WCHAR *file_source = NULL;
745 WCHAR dest_name[0x100];
746 LPWSTR dest_path, dest;
747 LPCWSTR file_key, component;
748 DWORD sz;
749 DWORD rc;
750 MSICOMPONENT *comp;
752 component = MSI_RecordGetString(row,2);
753 comp = get_loaded_component(package,component);
755 if (!ACTION_VerifyComponentForAction(package, comp, INSTALLSTATE_LOCAL))
757 TRACE("Skipping copy due to disabled component %s\n",
758 debugstr_w(component));
760 /* the action taken was the same as the current install state */
761 comp->Action = comp->Installed;
763 return ERROR_SUCCESS;
766 comp->Action = INSTALLSTATE_LOCAL;
768 file_key = MSI_RecordGetString(row,3);
769 if (!file_key)
771 ERR("Unable to get file key\n");
772 return ERROR_FUNCTION_FAILED;
775 rc = get_file_target(package,file_key,&file_source);
777 if (rc != ERROR_SUCCESS)
779 ERR("Original file unknown %s\n",debugstr_w(file_key));
780 msi_free(file_source);
781 return ERROR_SUCCESS;
784 if (MSI_RecordIsNull(row,4))
785 strcpyW(dest_name,strrchrW(file_source,'\\')+1);
786 else
788 sz=0x100;
789 MSI_RecordGetStringW(row,4,dest_name,&sz);
790 reduce_to_longfilename(dest_name);
793 if (MSI_RecordIsNull(row,5))
795 LPWSTR p;
796 dest_path = strdupW(file_source);
797 p = strrchrW(dest_path,'\\');
798 if (p)
799 *p=0;
801 else
803 LPCWSTR destkey;
804 destkey = MSI_RecordGetString(row,5);
805 dest_path = resolve_folder(package, destkey, FALSE,FALSE,NULL);
806 if (!dest_path)
808 /* try a Property */
809 dest_path = msi_dup_property( package, destkey );
810 if (!dest_path)
812 FIXME("Unable to get destination folder, try AppSearch properties\n");
813 msi_free(file_source);
814 return ERROR_SUCCESS;
819 dest = build_directory_name(2, dest_path, dest_name);
821 TRACE("Duplicating file %s to %s\n",debugstr_w(file_source),
822 debugstr_w(dest));
824 if (strcmpW(file_source,dest))
825 rc = !CopyFileW(file_source,dest,TRUE);
826 else
827 rc = ERROR_SUCCESS;
829 if (rc != ERROR_SUCCESS)
830 ERR("Failed to copy file %s -> %s, last error %ld\n", debugstr_w(file_source), debugstr_w(dest_path), GetLastError());
832 FIXME("We should track these duplicate files as well\n");
834 msi_free(dest_path);
835 msi_free(dest);
836 msi_free(file_source);
838 return ERROR_SUCCESS;
841 UINT ACTION_DuplicateFiles(MSIPACKAGE *package)
843 UINT rc;
844 MSIQUERY * view;
845 static const WCHAR ExecSeqQuery[] =
846 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
847 '`','D','u','p','l','i','c','a','t','e','F','i','l','e','`',0};
849 if (!package)
850 return ERROR_INVALID_HANDLE;
852 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
853 if (rc != ERROR_SUCCESS)
854 return ERROR_SUCCESS;
856 rc = MSI_IterateRecords(view, NULL, ITERATE_DuplicateFiles, package);
857 msiobj_release(&view->hdr);
859 return rc;