svn: /pear/pear-core/trunk/PEAR/ Installer.php

[email protected] (Helgi Þormar Þorbjörnsson) Tue, 07 Dec 2010 00:54:37 +0000
Newsgroups php.pear.cvs,php.pear.core
Message-ID <[email protected]>
dufuz                                    Tue, 07 Dec 2010 00:54:37 +0000

Revision: http://svn.php.net/viewvc?view=revision&revision=306033

Log:
Fix Bug #17986: PEAR Installer cannot handle files moved between packages [dufuz]

Bug: http://pear.php.net/bugs/17986 (unknown) 
      
Changed paths:
    U   pear/pear-core/trunk/PEAR/Installer.php
svn-diffs-306033.txt (text/x-diff, 14.4 KB)
Modified: pear/pear-core/trunk/PEAR/Installer.php
===================================================================
--- pear/pear-core/trunk/PEAR/Installer.php	2010-12-07 00:23:04 UTC (rev 306032)
+++ pear/pear-core/trunk/PEAR/Installer.php	2010-12-07 00:54:37 UTC (rev 306033)
@@ -42,8 +42,6 @@
  */
 class PEAR_Installer extends PEAR_Downloader
 {
-    // {{{ properties
-
     /** name of the package directory, for example Foo-1.0
      * @var string
      */
@@ -105,10 +103,6 @@
      */
     var $file_operations = array();

-    // }}}
-
-    // {{{ constructor
-
     /**
      * PEAR_Installer constructor.
      *
@@ -134,8 +128,6 @@
         $this->_registry = &$config->getRegistry();
     }

-    // }}}
-
     function _removeBackups($files)
     {
         foreach ($files as $path) {
@@ -143,8 +135,6 @@
         }
     }

-    // {{{ _deletePackageFiles()
-
     /**
      * Delete a package's installed files, does not remove empty directories.
      *
@@ -203,9 +193,6 @@
         return true;
     }

-    // }}}
-    // {{{ _installFile()
-
     /**
      * @param string filename
      * @param array attributes from <file> tag in package.xml
@@ -215,7 +202,7 @@
      */
     function _installFile($file, $atts, $tmp_path, $options)
     {
-        // {{{ return if this file is meant for another platform
+        // return if this file is meant for another platform
         static $os;
         if (!isset($this->_registry)) {
             $this->_registry = &$this->config->getRegistry();
@@ -239,10 +226,9 @@
                 return PEAR_INSTALLER_SKIPPED;
             }
         }
-        // }}}

         $channel = $this->pkginfo->getChannel();
-        // {{{ assemble the destination paths
+        // assemble the destination paths
         switch ($atts['role']) {
             case 'src':
             case 'extsrc':
@@ -304,7 +290,6 @@
         if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
             return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
         }
-        // }}}

         if (empty($this->_options['register-only']) &&
               (!file_exists($dest_dir) || !is_dir($dest_dir))) {
@@ -333,7 +318,7 @@
                     $md5sum = md5_file($dest_file);
                 }
             } else {
-                // {{{ file with replacements
+                // file with replacements
                 if (!file_exists($orig_file)) {
                     return $this->raiseError("file does not exist",
                                              PEAR_INSTALLER_FAILED);
@@ -410,10 +395,9 @@
                 }

                 fclose($wp);
-                // }}}
             }

-            // {{{ check the md5
+            // check the md5
             if (isset($md5sum)) {
                 if (strtolower($md5sum) === strtolower($atts['md5sum'])) {
                     $this->log(2, "md5sum ok: $final_dest_file");
@@ -439,8 +423,8 @@
                     }
                 }
             }
-            // }}}
-            // {{{ set file permissions
+
+            // set file permissions
             if (!OS_WINDOWS) {
                 if ($atts['role'] == 'script') {
                     $mode = 0777 & ~(int)octdec($this->config->get('umask'));
@@ -458,7 +442,6 @@
                     }
                 }
             }
-            // }}}

             if ($atts['role'] == 'src') {
                 rename($dest_file, $final_dest_file);
@@ -486,9 +469,6 @@
         return PEAR_INSTALLER_OK;
     }

-    // }}}
-    // {{{ _installFile2()
-
     /**
      * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
      * @param string filename
@@ -505,7 +485,7 @@
         }

         $channel = $pkg->getChannel();
-        // {{{ assemble the destination paths
+        // assemble the destination paths
         if (!in_array($atts['attribs']['role'],
               PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
             return $this->raiseError('Invalid role `' . $atts['attribs']['role'] .
@@ -540,7 +520,6 @@

         $dest_dir  = dirname($final_dest_file);
         $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
-        // }}}

         if (empty($this->_options['register-only'])) {
             if (!file_exists($dest_dir) || !is_dir($dest_dir)) {
@@ -619,7 +598,7 @@
                 }
             }

-            // {{{ check the md5
+            // check the md5
             if (isset($md5sum)) {
                 // Make sure the original md5 sum matches with expected
                 if (strtolower($md5sum) === strtolower($attribs['md5sum'])) {
@@ -654,8 +633,7 @@
                 $real_atts['attribs']['md5sum'] = md5_file($dest_file);
             }

-            // }}}
-            // {{{ set file permissions
+            //set file permissions
             if (!OS_WINDOWS) {
                 if ($role->isExecutable()) {
                     $mode = 0777 & ~(int)octdec($this->config->get('umask'));
@@ -673,7 +651,6 @@
                     }
                 }
             }
-            // }}}

             if ($attribs['role'] == 'src') {
                 rename($dest_file, $final_dest_file);
@@ -695,9 +672,6 @@
         return PEAR_INSTALLER_OK;
     }

-    // }}}
-    // {{{ addFileOperation()
-
     /**
      * Add a file operation to the current file transaction.
      *
@@ -738,9 +712,6 @@
         $this->file_operations[] = array($type, $data);
     }

-    // }}}
-    // {{{ startFileTransaction()
-
     function startFileTransaction($rollback_in_case = false)
     {
         if (count($this->file_operations) && $rollback_in_case) {
@@ -749,16 +720,11 @@
         $this->file_operations = array();
     }

-    // }}}
-    // {{{ commitFileTransaction()
-
     function commitFileTransaction()
     {
-        $n = count($this->file_operations);
-        $this->log(2, "about to commit $n file operations");
-        // {{{ first, check permissions and such manually
+        //first, check permissions and such manually
         $errors = array();
-        foreach ($this->file_operations as $tr) {
+        foreach ($this->file_operations as $key => $tr) {
             list($type, $data) = $tr;
             switch ($type) {
                 case 'rename':
@@ -781,6 +747,7 @@
                     if (!file_exists($data[0])) {
                         $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted");
                     }
+
                     // check that directory is writable
                     if (file_exists($data[0])) {
                         if (!is_writable(dirname($data[0]))) {
@@ -795,12 +762,34 @@
                                 fclose($fp);
                             }
                         }
+
+                        /* Verify we are not deleting a file owned by another package
+                         * This can happen when a file moves from package A to B in
+                         * an upgrade ala http://pear.php.net/17986
+                         */
+                        if ($this->pkginfo->getName() == 'PHPUnit') {
+                            $info = array(
+                                'package' => strtolower($this->pkginfo->getName()),
+                                'channel' => strtolower($this->pkginfo->getChannel()),
+                            );
+                            $result = $this->_registry->checkFileMap($data[0], $info, '1.1');
+                            if (is_array($result)) {
+                                $res = array_diff($result, $info);
+                                if (!empty($res)) {
+                                    $new = $this->_registry->getPackage($result[1], $result[0]);
+                                    $this->file_operations[$key] = false;
+                                    $this->log(3, "file $data[0] was scheduled for removal from {$this->pkginfo->getName()} but is owned by {$new->getChannel()}/{$new->getName()}, removal has been cancelled.");
+                                }
+                            }
+                        }
                     }
                     break;
             }
-
         }
-        // }}}
+
+        $n = count($this->file_operations);
+        $this->log(2, "about to commit $n file operations for " . $this->pkginfo->getName());
+
         $m = count($errors);
         if ($m > 0) {
             foreach ($errors as $error) {
@@ -815,7 +804,7 @@
         }

         $this->_dirtree = array();
-        // {{{ really commit the transaction
+        // really commit the transaction
         foreach ($this->file_operations as $i => $tr) {
             if (!$tr) {
                 // support removal of non-existing backups
@@ -936,15 +925,12 @@
                     break;
             }
         }
-        // }}}
+
         $this->log(2, "successfully committed $n file operations");
         $this->file_operations = array();
         return true;
     }

-    // }}}
-    // {{{ rollbackFileTransaction()
-
     function rollbackFileTransaction()
     {
         $n = count($this->file_operations);
@@ -988,18 +974,12 @@
         $this->file_operations = array();
     }

-    // }}}
-    // {{{ mkDirHier($dir)
-
     function mkDirHier($dir)
     {
         $this->addFileOperation('mkdir', array($dir));
         return parent::mkDirHier($dir);
     }

-    // }}}
-    // {{{ download()
-
     /**
      * Download any files and their dependencies, if necessary
      *
@@ -1033,15 +1013,12 @@
         return $ret;
     }

-    // }}}
-    // {{{ _parsePackageXml()
-
     function _parsePackageXml(&$descfile, &$tmpdir)
     {
         if (substr($descfile, -4) == '.xml') {
             $tmpdir = false;
         } else {
-            // {{{ Decompress pack in tmp dir -------------------------------------
+            // Decompress pack in tmp dir

             // To allow relative package file names
             $descfile = realpath($descfile);
@@ -1050,10 +1027,9 @@
                 return $tmpdir;
             }
             $this->log(3, '+ tmp dir created at ' . $tmpdir);
-            // }}}
         }

-        // Parse xml file -----------------------------------------------
+        // Parse xml file
         $pkg = new PEAR_PackageFile($this->config, $this->debug, $tmpdir);
         PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
         $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING);
@@ -1074,7 +1050,6 @@
         return $p;
     }

-    // }}}
     /**
      * Set the list of PEAR_Downloader_Package objects to allow more sane
      * dependency validation
@@ -1106,8 +1081,6 @@
         return $this->_downloadedPackages;
     }

-    // {{{ install()
-
     /**
      * Installs the files within the package file specified.
      *
@@ -1188,7 +1161,7 @@
             $this->installroot = '';
         }

-        // {{{ checks to do when not in "force" mode
+        // checks to do when not in "force" mode
         if (empty($options['force']) &&
               (file_exists($this->config->get('php_dir')) &&
                is_dir($this->config->get('php_dir')))) {
@@ -1303,7 +1276,6 @@
                 }
             }
         }
-        // }}}

         $this->startFileTransaction();

@@ -1359,7 +1331,7 @@
             }
         }

-        // {{{ Copy files to dest dir ---------------------------------------
+        // Copy files to dest dir

         // info from the package it self we want to access from _installFile
         $this->pkginfo = &$pkg;
@@ -1379,7 +1351,7 @@
         }

         $this->configSet('default_channel', $channel);
-        // {{{ install files
+        // install files

         $ver = $pkg->getPackagexmlVersion();
         if (version_compare($ver, '2.0', '>=')) {
@@ -1428,16 +1400,14 @@
                 $pkg->installedFile($file, $atts);
             }
         }
-        // }}}

-        // {{{ compile and install source files
+        // compile and install source files
         if ($this->source_files > 0 && empty($options['nobuild'])) {
             if (PEAR::isError($err =
                   $this->_compileSourceFiles($savechannel, $pkg))) {
                 return $err;
             }
         }
-        // }}}

         if (isset($backedup)) {
             $this->_removeBackups($backedup);
@@ -1448,12 +1418,11 @@
             $this->configSet('default_channel', $savechannel);
             return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
         }
-        // }}}

         $ret          = false;
         $installphase = 'install';
         $oldversion   = false;
-        // {{{ Register that the package is installed -----------------------
+        // Register that the package is installed
         if (empty($options['upgrade'])) {
             // if 'force' is used, replace the info in registry
             $usechannel = $channel;
@@ -1524,9 +1493,6 @@
         return $pkg->toArray(true);
     }

-    // }}}
-
-    // {{{ _compileSourceFiles()
     /**
      * @param string
      * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
@@ -1596,7 +1562,6 @@
                 }
             }

-
             $data = array(
                 'role'         => $role,
                 'name'         => $bn,
@@ -1614,12 +1579,10 @@
         }
     }

-    // }}}
     function &getUninstallPackages()
     {
         return $this->_downloadedPackages;
     }
-    // {{{ uninstall()

     /**
      * Uninstall a package
@@ -1701,7 +1664,7 @@
         $this->pkginfo = &$pkg;
         // pretty much nothing happens if we are only registering the uninstall
         if (empty($options['register-only'])) {
-            // {{{ Delete the files
+            // Delete the files
             $this->startFileTransaction();
             PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
             if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) {
@@ -1753,7 +1716,6 @@
                     }
                 }
             }
-            // }}}
         }

         $this->configSet('default_channel', $savechannel);
@@ -1798,8 +1760,6 @@
         return 0;
     }

-    // }}}
-    // {{{ _sortDirs()
     function _sortDirs($a, $b)
     {
         if (strnatcmp($a, $b) == -1) return 1;
@@ -1807,10 +1767,6 @@
         return 0;
     }

-    // }}}
-
-    // {{{ _buildCallback()
-
     function _buildCallback($what, $data)
     {
         if (($what == 'cmdoutput' && $this->debug > 1) ||
@@ -1818,6 +1774,4 @@
             $this->ui->outputData(rtrim($data), 'build');
         }
     }
-
-    // }}}
 }
\ No newline at end of file