Ejemplo n.º 1
0
 /**
  * Get/Create a transaction.
  *
  * @param string|Pyrus\Installer\Role\Common $path The directory path.
  * @return Transaction A Pyrus\AtomicFileTransaction\Transaction instance
  */
 public function getTransaction($path)
 {
     if ($path instanceof \Pyrus\Installer\Role\Common) {
         $path = \Pyrus\Config::current()->{$path->getLocationConfig()};
     }
     $path = FS::path((string) $path);
     if (isset($this->transactions[$path])) {
         return $this->transactions[$path];
     }
     // Create new transaction object
     $class = $this->getTransactionClass();
     try {
         $this->transactions[$path] = new $class($path, $this);
         return $this->transactions[$path];
     } catch (\Exception $e) {
         $errs = new MultiErrors();
         $errs->E_ERROR[] = $e;
         $errs->merge($this->rollbackTransactions());
         throw new MultiException('Unable to begin transaction', $errs);
     }
 }
Ejemplo n.º 2
0
 /**
  * Commit the transaction.
  * Replaces the journal path with the original path.
  *
  * @throws RuntimeException
  * @return void
  */
 public function commit()
 {
     $this->checkActive();
     if (file_exists($this->path)) {
         FS::rmrf($this->path);
     }
     // here is the only critical moment - a failure in between these two renames
     // leaves us with no source
     if (!@rename($this->journalPath, $this->path)) {
         throw new IOException('CRITICAL - unable to complete transaction, rename of journal to actual path failed');
     }
     $this->inTransaction = false;
 }
Ejemplo n.º 3
0
 /**
  * Completely remove all traces of an xml registry
  */
 public static function removeRegistry($path)
 {
     if (!file_exists($path . '/.xmlregistry')) {
         return;
     }
     try {
         \Pyrus\Filesystem::rmrf(realpath($path . DIRECTORY_SEPARATOR . '.xmlregistry'));
     } catch (AtomicFileTransaction\Exception $e) {
         throw new Exception('Cannot remove XML registry: ' . $e->getMessage(), $e);
     }
 }
Ejemplo n.º 4
0
 /**
  * Get the full journal path based on a relative path.
  *
  * @param string $relativePath
  * @return string
  */
 protected function getPath($relativePath)
 {
     return \Pyrus\Filesystem::path($this->journalPath . '/' . (string) $relativePath);
 }
Ejemplo n.º 5
0
 /**
  * Render packages from the creators passed into the constructor.
  *
  * This will take any package source and an array mapping internal
  * path => file name and create new packages in the formats requested.
  *
  * All files in package.xml will have the string @PACKAGE_VERSION@
  * automatically replaced with the current package's version
  * @param \Pyrus\Package $package
  * @param array $extrafiles
  */
 function render(\Pyrus\Package $package, array $extrafiles = array())
 {
     foreach ($this->_creators as $creator) {
         $creator->init();
     }
     $this->prepend = $prepend = $package->name . '-' . $package->version['release'];
     if ($package->isNewPackage()) {
         $packagexml = $prepend . '/.xmlregistry/packages/' . str_replace('/', '!', $package->channel) . '/' . $package->name . '/' . $package->version['release'] . '-info.xml';
     } else {
         if ($package->isOldAndCrustyCompatible()) {
             $packagexml = 'package2.xml';
             $old = file_get_contents('package.xml');
         } else {
             $packagexml = 'package.xml';
         }
     }
     if (self::VERSION === '@' . 'PACKAGE_VERSION@') {
         // we're running straight from SVN, so pretend to be 2.0.0
         $package->packagerversion = '2.0.0';
     } else {
         $package->packagerversion = self::VERSION;
     }
     // get packaging package.xml
     $packageingstr = (string) new \Pyrus\XMLWriter($package->toArray(true));
     foreach ($this->_creators as $creator) {
         $creator->addFile($packagexml, $packageingstr);
     }
     if ($package->isOldAndCrustyCompatible()) {
         foreach ($this->_creators as $creator) {
             $creator->addFile('package.xml', $old);
         }
     }
     if ($package->getInternalPackage() instanceof Xml) {
         // check for package_compatible.xml
         if ($package->isNewPackage() && file_exists($package->getFilePath('package_compatible.xml'))) {
             foreach ($this->_creators as $creator) {
                 $creator->addFile('package.xml', file_get_contents($package->getFilePath('package_compatible.xml')));
             }
         }
     }
     $packagingloc = \Pyrus\Config::current()->temp_dir . DIRECTORY_SEPARATOR . 'pyrpackage';
     if (file_exists($packagingloc)) {
         \Pyrus\Filesystem::rmrf($packagingloc, false, false);
     }
     mkdir($packagingloc, 0777, true);
     // $packageat is the relative path within the archive
     // $info is an array of format:
     // array('attribs' => array('name' => ...)[, 'tasks:blah' ...])
     $alreadyPackaged = array();
     $globalreplace = array('attribs' => array('from' => '@' . 'PACKAGE_VERSION@', 'to' => 'version', 'type' => 'package-info'));
     foreach ($package->packagingcontents as $packageat => $info) {
         $role = \Pyrus\Installer\Role::factory($package->getPackageType(), $info['attribs']['role']);
         try {
             $role->packageTimeValidate($package, $info);
         } catch (\Exception $e) {
             throw new Creator\Exception('Invalid file ' . $packageat . ': ' . $e->getMessage(), $e);
         }
         $packageat = str_replace('\\', '/', $packageat);
         $packageat = str_replace('//', '/', $packageat);
         if ($packageat[0] === '/' || strlen($packageat) > 2 && ($packageat[1] === ':' && $packageat[2] == '/')) {
             throw new Creator\Exception('Invalid path, cannot save a root path ' . $packageat);
         }
         if (preg_match('@^\\.\\.?/|/\\.\\.?\\z|/\\.\\./@', $packageat)) {
             throw new Creator\Exception('Invalid path, cannot use directory ' . 'reference . or .. ' . $packageat);
         }
         $alreadyPackaged[$packageat] = true;
         $packageat = $prepend . '/' . $packageat;
         $contents = $package->getFileContents($info['attribs']['name'], true);
         if (!file_exists(dirname($packagingloc . DIRECTORY_SEPARATOR . $packageat))) {
             mkdir(dirname($packagingloc . DIRECTORY_SEPARATOR . $packageat), 0777, true);
         }
         $fp = fopen($packagingloc . DIRECTORY_SEPARATOR . $packageat, 'wb+');
         ftruncate($fp, 0);
         stream_copy_to_stream($contents, $fp);
         fclose($contents);
         rewind($fp);
         if ($package->isNewPackage() && $info['attribs']['role'] == 'php') {
             if (isset($info['tasks:replace'])) {
                 if (isset($info['tasks:replace'][0])) {
                     $info['tasks:replace'][] = $globalreplace;
                 } else {
                     $info['tasks:replace'] = array($info['tasks:replace'], $globalreplace);
                 }
             } else {
                 $info['tasks:replace'] = $globalreplace;
             }
         }
         if (isset(\Pyrus\Config::current()->registry->package[$package->channel . '/' . $package->name])) {
             $version = \Pyrus\Config::current()->registry->info($package->name, $package->channel, 'version');
         } else {
             $version = null;
         }
         foreach (new Creator\TaskIterator($info, $package, \Pyrus\Task\Common::PACKAGE, $version) as $task) {
             // do pre-processing of file contents
             try {
                 $task->startSession($fp, $packageat);
             } catch (\Exception $e) {
                 // TODO: handle exceptions
             }
         }
         fclose($fp);
     }
     foreach ($this->_creators as $creator) {
         $creator->addDir($packagingloc);
     }
     if ($package->isNewPackage()) {
         $this->addPEAR2Stuff($alreadyPackaged);
     }
     $this->addExtraFiles($extrafiles);
     foreach ($this->_creators as $creator) {
         $creator->close();
     }
     \Pyrus\Filesystem::rmrf($packagingloc, false, false);
 }
Ejemplo n.º 6
0
 /**
  * Repair from a previously failed transaction cut off mid-transaction
  */
 public static function repair()
 {
     if (static::inTransaction()) {
         throw new AtomicFileTransaction\RuntimeException('Cannot repair while in a transaction');
     }
     static::$instance = null;
     $config = Config::current();
     $remove = array();
     foreach ($config->systemvars as $var) {
         if (!strpos($var, '_dir')) {
             continue;
         }
         $path = FS::path($config->{$var});
         $backuppath = dirname($path) . DIRECTORY_SEPARATOR . '.old-' . basename($path);
         if (file_exists($backuppath) && is_dir($backuppath)) {
             if (file_exists($path)) {
                 if (!is_dir($path)) {
                     throw new AtomicFileTransaction\RuntimeException('Repair failed - ' . $var . ' path ' . $path . ' is not a directory.  Move this file out of the way and ' . 'try the repair again');
                 }
                 // this is the new stuff from journal path, so move it out of the way
                 $journalpath = dirname($path) . DIRECTORY_SEPARATOR . '.journal-' . basename($path);
                 $remove[] = $journalpath;
                 rename($path, $journalpath);
             }
             // restore backup
             rename($backuppath, $path);
         }
     }
     foreach ($remove as $path) {
         FS::rmrf($path);
     }
 }
Ejemplo n.º 7
0
 /**
  * Rollback the entire transaction.
  * This should restore the backup and remove any other new files.
  *
  * @return void
  */
 public function revert()
 {
     if ($this->inTransaction) {
         throw new RuntimeException('Cannot revert - still in a transaction');
     }
     if (!$this->hasBackup()) {
         return;
     }
     if (!file_exists($this->backupPath)) {
         throw new IOException('Cannot restore backup, no backup directory available.');
     }
     if (file_exists($this->journalPath)) {
         throw new IOException('Cannot restore backup, a journal directory/file still exists.');
     }
     // Rename current path to journal path
     if (file_exists($this->path)) {
         if (!rename($this->path, $this->journalPath)) {
             throw new IOException('Cannot restore backup, unable to rename directory.');
         }
     }
     // Rename backup path to path
     if (!rename($this->backupPath, $this->path)) {
         throw new IOException('Cannot restore backup, unable to rename backup directory.');
     }
     // Remove journal directory
     if (file_exists($this->journalPath)) {
         FS::rmrf($this->journalPath);
     }
 }
Ejemplo n.º 8
0
 /**
  * Completely remove all traces of a PEAR 1.x registry
  */
 public static function removeRegistry($path)
 {
     $path = $path . DIRECTORY_SEPARATOR . 'php';
     if (!file_exists($path . '/.registry')) {
         return;
     }
     try {
         \Pyrus\Filesystem::rmrf(realpath($path . DIRECTORY_SEPARATOR . '.registry'));
     } catch (AtomicFileTransaction\Exception $e) {
         throw new Exception('Cannot remove Pear1 registry: ' . $e->getMessage(), $e);
     }
     $errs = new \PEAR2\MultiErrors();
     try {
         if (file_exists($path . '/.channels')) {
             \Pyrus\Filesystem::rmrf(realpath($path . DIRECTORY_SEPARATOR . '.channels'));
         }
     } catch (AtomicFileTransaction\Exception $e) {
         $errs->E_ERROR[] = new Exception('Cannot remove Pear1 registry: ' . $e->getMessage(), $e);
     }
     foreach (array('.filemap', '.lock', '.depdb', '.depdblock') as $file) {
         if (file_exists($path . DIRECTORY_SEPARATOR . $file) && !@unlink(realpath($path . DIRECTORY_SEPARATOR . $file))) {
             $errs->E_ERROR[] = new Exception('Cannot remove Pear1 registry: Unable to remove ' . $file);
         }
     }
     if (count($errs->E_ERROR)) {
         throw new Exception('Unable to remove Pear1 registry', $errs);
     }
 }