Problem/Motivation

Backup & Migrate does not properly support StreamWappers. I'm using Windows Azure Blob to backup my site, and I've created stream wrappers in the form of "backup://manual" and "backup://scheduled". While it does properly backup to the cloud, it does not properly load the files, download, restore or delete.

Steps to reproduce

Create a stream wrapper for an external site, such as Azure Blob. Create a new destination using the Server Directory. For the location, use the stream interface, such as "backups://manual"

Proposed resolution

I was able to get this to work with a few changes. Primarily, instead of calling drupal_realpath() to get the real URL, it should use the stream wrapper interface.

here are the changes:

In /include/file.inc, return the path variable:

  public function filepath() {
    if ($filepath = drupal_realpath($this->path)) {
      //return $filepath; 
    }
    return $this->path;
  }

in include/destination.file.inc, use get_location() instead of get_realpath():

  /**
   * Get the filepath from the given file id.
   */
  public function get_filepath($file_id) {
    //if ($dir = $this->get_realpath()) {
    if ($dir = $this->get_location()) {
      $filepath = rtrim($dir, '/') . '/' . $file_id;
      return $filepath;
    }
    return FALSE;
  }

Also, in the _list_files() function, support is required for reading from stream wrappers:

  /**
   * File list destination callback.
   */
  public function _list_files() {
    $files = array();
    if ($dir = $this->get_realpath()) {
      if ($handle = @opendir($dir)) {
        require_once dirname(__FILE__) . '/files.inc';

        while (FALSE !== ($file = readdir($handle))) {
          if (substr($file, 0, 1) !== '.') {
            $filepath = $dir . "/" . $file;
            $files[$file] = new backup_file(array('filepath' => $filepath));
          }
        }
      }
     // support for stream wrappers
      else {
        list($scheme, $target) = explode('://', $this->location, 2);
        $scheme.='://';
        $wrapper = file_stream_wrapper_get_instance_by_uri( $scheme );
        if ( $wrapper->dir_opendir($this->location,'') )
        {
          while (FALSE !== ($file = $wrapper->dir_readdir())) {
            if (substr($file, 0, 1) !== '.') {
              $filepath = $this->location . "/" . $file;
              $files[$file] = new backup_file(array('filepath' => $filepath));
            }
          }
          $wrapper->dir_closedir();
        }
      }
    }
    return $files;
  }

Remaining tasks

This now seems to work, but if the community can review and let me know if I missed anything. Hope this helps anyone having issues saving backups to external cloud sites like Azure.

Comments

dczaretsky created an issue. See original summary.

dczaretsky’s picture

One more thought: another way to handle both realpaths and stream wrapper paths would be to check if the file is accessible.

For example, in /includes/file.inc we check the filepath is accessible, otherwise return the stream wrapper path.

 /**
   * Get the current filepath.
   */
  public function filepath() {
    if ($filepath = drupal_realpath($this->path)) {
      if ( is_readable(dirname($filepath)) )
         return $filepath;
    }
    return $this->path;
  }

Similarly in /includes/destinations.files.inc, we can check if the dir is readable, or otherwise return the stream wrapper path:

  /**
   * Get the filepath from the given file id.
   */
  public function get_filepath($file_id) {
    if ( ($dir = $this->get_realpath()) && is_readable($dir) ) {
      $filepath = rtrim($dir, '/') . '/' . $file_id;
      return $filepath;
    } else {
      return rtrim($this->get_location(),'/') . '/' . $file_id;
    }
    return FALSE;
  }

ivnish’s picture

Status: Needs review » Closed (outdated)

Drupal 7 is EOL. This issue will be closed.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.