msi: rework cabinet extraction

Aric Stewart aric at codeweavers.com
Thu Jun 23 09:29:21 CDT 2005


Redo how we extract files from a cabinet in order to dramatically 
improve performance, especially if picking and choosing files out of the 
cabinet.
-------------- next part --------------
Index: dlls/msi/files.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/files.c,v
retrieving revision 1.3
diff -u -r1.3 files.c
--- dlls/msi/files.c	21 Jun 2005 20:03:30 -0000	1.3
+++ dlls/msi/files.c	23 Jun 2005 14:27:52 -0000
@@ -125,7 +125,6 @@
 {
     MSIPACKAGE* package;
     LPCSTR cab_path;
-    LPCSTR file_name;
 } CabData;
 
 static void * cabinet_alloc(ULONG cb)
@@ -195,7 +194,6 @@
 
 static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
 {
-    /* FIXME: try to do more processing in this function */
     switch (fdint)
     {
     case fdintCOPY_FILE:
@@ -208,10 +206,30 @@
         LPWSTR trackpath;
         LPWSTR tracknametmp;
         static const WCHAR tmpprefix[] = {'C','A','B','T','M','P','_',0};
-       
-        if (data->file_name && lstrcmpiA(data->file_name,pfdin->psz1))
-                return 0;
-        
+        LPWSTR given_file;
+        INT index;
+
+        MSIRECORD * uirow;
+        LPWSTR uipath;
+
+        given_file = strdupAtoW(pfdin->psz1);
+        index = get_loaded_file(data->package, given_file);
+
+        if (index < 0)
+        {
+            ERR("Unknown File in Cabinent (%s)\n",debugstr_w(given_file));
+            HeapFree(GetProcessHeap(),0,given_file);
+            return 0;
+        }
+
+        if (!((data->package->files[index].State == 1 ||
+               data->package->files[index].State == 2)))
+        {
+            TRACE("Skipping extraction of %s\n",debugstr_w(given_file));
+            HeapFree(GetProcessHeap(),0,given_file);
+            return 0;
+        }
+
         file = cabinet_alloc((len+1)*sizeof(char));
         strcpy(file, data->cab_path);
         strcat(file, pfdin->psz1);
@@ -233,6 +251,19 @@
         HeapFree(GetProcessHeap(),0,trackname);
         HeapFree(GetProcessHeap(),0,tracknametmp);
 
+        /* the UI chunk */
+        uirow=MSI_CreateRecord(9);
+        MSI_RecordSetStringW(uirow,1,data->package->files[index].File);
+        uipath = strdupW(data->package->files[index].TargetPath);
+        *(strrchrW(uipath,'\\')+1)=0;
+        MSI_RecordSetStringW(uirow,9,uipath);
+        MSI_RecordSetInteger(uirow,6,data->package->files[index].FileSize);
+        ui_actiondata(data->package,szInstallFiles,uirow);
+        msiobj_release( &uirow->hdr );
+        HeapFree(GetProcessHeap(),0,uipath);
+
+        ui_progress(data->package,2,data->package->files[index].FileSize,0,0);
+
         return cabinet_open(file, _O_WRONLY | _O_CREAT, 0);
     }
     case fdintCLOSE_FILE_INFO:
@@ -259,19 +290,17 @@
  *
  * Extract files from a cab file.
  */
-static BOOL extract_a_cabinet_file(MSIPACKAGE* package, const WCHAR* source, 
-                                 const WCHAR* path, const WCHAR* file)
+static BOOL extract_cabinet_file(MSIPACKAGE* package, LPCWSTR source, 
+                                 LPCWSTR path)
 {
     HFDI hfdi;
     ERF erf;
     BOOL ret;
     char *cabinet;
     char *cab_path;
-    char *file_name;
     CabData data;
 
-    TRACE("Extracting %s (%s) to %s\n",debugstr_w(source), 
-                    debugstr_w(file), debugstr_w(path));
+    TRACE("Extracting %s to %s\n",debugstr_w(source), debugstr_w(path));
 
     hfdi = FDICreate(cabinet_alloc,
                      cabinet_free,
@@ -302,11 +331,6 @@
 
     data.package = package;
     data.cab_path = cab_path;
-    if (file)
-        file_name = strdupWtoA(file);
-    else
-        file_name = NULL;
-    data.file_name = file_name;
 
     ret = FDICopy(hfdi, cabinet, "", 0, cabinet_notify, NULL, &data);
 
@@ -317,13 +341,26 @@
 
     HeapFree(GetProcessHeap(), 0, cabinet);
     HeapFree(GetProcessHeap(), 0, cab_path);
-    HeapFree(GetProcessHeap(), 0, file_name);
 
     return ret;
 }
 
-static UINT ready_media_for_file(MSIPACKAGE *package, WCHAR* path, 
-                                 MSIFILE* file)
+static VOID set_file_source(MSIPACKAGE* package, MSIFILE* file, MSICOMPONENT*
+        comp, LPCWSTR path)
+{
+    if (file->Attributes & msidbFileAttributesNoncompressed)
+    {
+        LPWSTR p;
+        p = resolve_folder(package, comp->Directory, TRUE, FALSE, NULL);
+        file->SourcePath = build_directory_name(2, p, file->ShortName);
+        HeapFree(GetProcessHeap(),0,p);
+    }
+    else
+        file->SourcePath = build_directory_name(2, path, file->File);
+}
+
+static UINT ready_media_for_file(MSIPACKAGE *package, int fileindex,
+                                 MSICOMPONENT* comp)
 {
     UINT rc = ERROR_SUCCESS;
     MSIRECORD * row = 0;
@@ -338,27 +375,46 @@
     DWORD sz;
     INT seq;
     static UINT last_sequence = 0; 
+    static LPWSTR last_path = NULL;
+    MSIFILE* file = NULL;
 
-    if (file->Attributes & msidbFileAttributesNoncompressed)
+    /* cleanup signal */
+    if (!package)
     {
-        TRACE("Uncompressed File, no media to ready.\n");
+        HeapFree(GetProcessHeap(),0,last_path);
         return ERROR_SUCCESS;
     }
 
+    file = &package->files[fileindex];
+
     if (file->Sequence <= last_sequence)
     {
+        set_file_source(package,file,comp,last_path);
         TRACE("Media already ready (%u, %u)\n",file->Sequence,last_sequence);
-        /*extract_a_cabinet_file(package, source,path,file->File); */
         return ERROR_SUCCESS;
     }
 
     row = MSI_QueryGetRecord(package->db, ExecSeqQuery, file->Sequence);
     if (!row)
+    {
+        TRACE("Unable to query row\n");
         return ERROR_FUNCTION_FAILED;
+    }
 
     seq = MSI_RecordGetInteger(row,2);
     last_sequence = seq;
 
+    HeapFree(GetProcessHeap(),0,last_path);
+    last_path = NULL;
+
+    if (file->Attributes & msidbFileAttributesNoncompressed)
+    {
+        last_path = resolve_folder(package, comp->Directory, TRUE, FALSE, NULL);
+        set_file_source(package,file,comp,last_path);
+        msiobj_release(&row->hdr);
+        return rc;
+    }
+
     cab = MSI_RecordGetString(row,4);
     if (cab)
     {
@@ -367,12 +423,13 @@
         if (cab[0]=='#')
         {
             writeout_cabinet_stream(package,&cab[1],source);
-            strcpyW(path,source);
-            *(strrchrW(path,'\\')+1)=0;
+            last_path = strdupW(source);
+            *(strrchrW(last_path,'\\')+1)=0;
         }
         else
         {
             sz = MAX_PATH;
+            last_path = HeapAlloc(GetProcessHeap(),0,MAX_PATH*sizeof(WCHAR));
             if (MSI_GetPropertyW(package, cszSourceDir, source, &sz))
             {
                 ERR("No Source dir defined \n");
@@ -380,24 +437,29 @@
             }
             else
             {
-                strcpyW(path,source);
+                strcpyW(last_path,source);
                 strcatW(source,cab);
                 /* extract the cab file into a folder in the temp folder */
                 sz = MAX_PATH;
-                if (MSI_GetPropertyW(package, cszTempFolder,path, &sz) 
+                if (MSI_GetPropertyW(package, cszTempFolder,last_path, &sz) 
                                     != ERROR_SUCCESS)
-                    GetTempPathW(MAX_PATH,path);
+                    GetTempPathW(MAX_PATH,last_path);
             }
         }
-        rc = !extract_a_cabinet_file(package, source,path,NULL);
+        rc = !extract_cabinet_file(package, source, last_path);
+        /* reaquire file ptr */
+        file = &package->files[fileindex];
     }
     else
     {
         sz = MAX_PATH;
+        last_path = HeapAlloc(GetProcessHeap(),0,MAX_PATH*sizeof(WCHAR));
         MSI_GetPropertyW(package,cszSourceDir,source,&sz);
-        strcpyW(path,source);
+        strcpyW(last_path,source);
     }
+    set_file_source(package, file, comp, last_path);
     msiobj_release(&row->hdr);
+
     return rc;
 }
 
@@ -426,12 +488,17 @@
     return ERROR_FUNCTION_FAILED;
 }
 
+/*
+ * In order to make this work more effeciencly I am going to do this in 2
+ * passes.
+ * Pass 1) Correct all the TargetPaths and determin what files are to be
+ * installed.
+ * Pass 2) Extract Cabinents and copy files.
+ */
 UINT ACTION_InstallFiles(MSIPACKAGE *package)
 {
     UINT rc = ERROR_SUCCESS;
     DWORD index;
-    MSIRECORD * uirow;
-    WCHAR uipath[MAX_PATH];
 
     if (!package)
         return ERROR_INVALID_HANDLE;
@@ -439,17 +506,17 @@
     /* increment progress bar each time action data is sent */
     ui_progress(package,1,1,0,0);
 
+    /* Pass 1 */
     for (index = 0; index < package->loaded_files; index++)
     {
-        WCHAR path_to_source[MAX_PATH];
         MSIFILE *file;
-        
+        MSICOMPONENT* comp = NULL;
+
         file = &package->files[index];
 
         if (file->Temporary)
             continue;
 
-
         if (!ACTION_VerifyComponentForAction(package, file->ComponentIndex, 
                                        INSTALLSTATE_LOCAL))
         {
@@ -457,28 +524,15 @@
             TRACE("File %s is not scheduled for install\n",
                    debugstr_w(file->File));
 
+            file->State = 5;
             continue;
         }
 
         if ((file->State == 1) || (file->State == 2))
         {
-            LPWSTR p;
-            MSICOMPONENT* comp = NULL;
+            LPWSTR p = NULL;
 
-            TRACE("Installing %s\n",debugstr_w(file->File));
-            rc = ready_media_for_file(package, path_to_source, file);
-            /* 
-             * WARNING!
-             * our file table could change here because a new temp file
-             * may have been created
-             */
-            file = &package->files[index];
-            if (rc != ERROR_SUCCESS)
-            {
-                ERR("Unable to ready media\n");
-                rc = ERROR_FUNCTION_FAILED;
-                break;
-            }
+            TRACE("Pass 1: %s\n",debugstr_w(file->File));
 
             create_component_directory( package, file->ComponentIndex);
 
@@ -486,39 +540,56 @@
 
             if (file->ComponentIndex >= 0)
                 comp = &package->components[file->ComponentIndex];
+            else
+            {
+                ERR("No Component for file\n");
+                continue;
+            }
 
             p = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL);
             HeapFree(GetProcessHeap(),0,file->TargetPath);
 
             file->TargetPath = build_directory_name(2, p, file->FileName);
             HeapFree(GetProcessHeap(),0,p);
+        }
+    }
 
-            if (file->Attributes & msidbFileAttributesNoncompressed)
+    /* Pass 2 */
+    for (index = 0; index < package->loaded_files; index++)
+    {
+        MSIFILE *file;
+        MSICOMPONENT* comp = NULL;
+
+        file = &package->files[index];
+
+        if (file->Temporary)
+            continue;
+
+        if ((file->State == 1) || (file->State == 2))
+        {
+            TRACE("Pass 2: %s\n",debugstr_w(file->File));
+
+            if (file->ComponentIndex >= 0)
+                comp = &package->components[file->ComponentIndex];
+
+            rc = ready_media_for_file(package, index, comp);
+            if (rc != ERROR_SUCCESS)
             {
-                p = resolve_folder(package, comp->Directory, TRUE, FALSE, NULL);
-                file->SourcePath = build_directory_name(2, p, file->ShortName);
-                HeapFree(GetProcessHeap(),0,p);
+                ERR("Unable to ready media\n");
+                rc = ERROR_FUNCTION_FAILED;
+                break;
             }
-            else
-                file->SourcePath = build_directory_name(2, path_to_source, 
-                            file->File);
 
+            /*
+             * WARNING!
+             * our file table could change here because a new temp file
+             * may have been created. So reaquire our ptr.
+             */
+            file = &package->files[index];
 
             TRACE("file paths %s to %s\n",debugstr_w(file->SourcePath),
                   debugstr_w(file->TargetPath));
 
-            /* the UI chunk */
-            uirow=MSI_CreateRecord(9);
-            MSI_RecordSetStringW(uirow,1,file->File);
-            strcpyW(uipath,file->TargetPath);
-            *(strrchrW(uipath,'\\')+1)=0;
-            MSI_RecordSetStringW(uirow,9,uipath);
-            MSI_RecordSetInteger(uirow,6,file->FileSize);
-            ui_actiondata(package,szInstallFiles,uirow);
-            msiobj_release( &uirow->hdr );
-            ui_progress(package,2,file->FileSize,0,0);
-
-            
             if (file->Attributes & msidbFileAttributesNoncompressed)
                 rc = CopyFileW(file->SourcePath,file->TargetPath,FALSE);
             else
@@ -559,6 +630,8 @@
         }
     }
 
+    /* cleanup */
+    ready_media_for_file(NULL, 0, NULL);
     return rc;
 }
 
@@ -576,15 +649,15 @@
     component = MSI_RecordGetString(row,2);
     component_index = get_loaded_component(package,component);
 
-    if (!ACTION_VerifyComponentForAction(package, component_index, 
-                                   INSTALLSTATE_LOCAL))
+    if (!ACTION_VerifyComponentForAction(package, component_index,
+                            INSTALLSTATE_LOCAL))
     {
         TRACE("Skipping copy due to disabled component %s\n",
                         debugstr_w(component));
 
         /* the action taken was the same as the current install state */        
         package->components[component_index].Action =
-            package->components[component_index].Installed;
+                package->components[component_index].Installed;
 
         return ERROR_SUCCESS;
     }
@@ -608,15 +681,13 @@
     }
 
     if (MSI_RecordIsNull(row,4))
-    {
         strcpyW(dest_name,strrchrW(file_source,'\\')+1);
-    }
     else
     {
         sz=0x100;
         MSI_RecordGetStringW(row,4,dest_name,&sz);
         reduce_to_longfilename(dest_name);
-     }
+    }
 
     if (MSI_RecordIsNull(row,5))
     {
@@ -645,15 +716,15 @@
     }
 
     dest = build_directory_name(2, dest_path, dest_name);
-       
+
     TRACE("Duplicating file %s to %s\n",debugstr_w(file_source),
-          debugstr_w(dest)); 
-    
+                    debugstr_w(dest)); 
+
     if (strcmpW(file_source,dest))
         rc = !CopyFileW(file_source,dest,TRUE);
     else
         rc = ERROR_SUCCESS;
-    
+
     if (rc != ERROR_SUCCESS)
         ERR("Failed to copy file %s -> %s, last error %ld\n", debugstr_w(file_source), debugstr_w(dest_path), GetLastError());
 


More information about the wine-patches mailing list