MDL-42039 Restore: Avoid unnecessary archive file copy at start

Change the start of restore process so that the archive file can
be passed as a pathnamehash pointing to the Moodle file object,
rather than only as a filename in the temporary directory. This
avoids making an unnecessary copy of the archive file, which can
take a long time (and potentially cause a timeout) in the case
of multi-gigabyte backups.
This commit is contained in:
sam marshall 2013-10-01 13:14:04 +01:00 committed by Damyon Wiese
parent 1cd39657b5
commit a658b73cd0
2 changed files with 64 additions and 14 deletions

View File

@ -72,10 +72,23 @@ if (!check_dir_exists($tmpdir, true, true)) {
// choose the backup file from backup files tree
if ($action == 'choosebackupfile') {
if ($fileinfo = $browser->get_file_info($filecontext, $component, $filearea, $itemid, $filepath, $filename)) {
$filename = restore_controller::get_tempdir_name($course->id, $USER->id);
$pathname = $tmpdir . '/' . $filename;
$fileinfo->copy_to_pathname($pathname);
$restore_url = new moodle_url('/backup/restore.php', array('contextid'=>$contextid, 'filename'=>$filename));
if (is_a($fileinfo, 'file_info_stored')) {
// Use the contenthash rather than copying the file where possible,
// to improve performance and avoid timeouts with large files.
$fs = get_file_storage();
$params = $fileinfo->get_params();
$file = $fs->get_file($params['contextid'], $params['component'], $params['filearea'],
$params['itemid'], $params['filepath'], $params['filename']);
$restore_url = new moodle_url('/backup/restore.php', array('contextid' => $contextid,
'pathnamehash' => $file->get_pathnamehash(), 'contenthash' => $file->get_contenthash()));
} else {
// If it's some weird other kind of file then use old code.
$filename = restore_controller::get_tempdir_name($course->id, $USER->id);
$pathname = $tmpdir . '/' . $filename;
$fileinfo->copy_to_pathname($pathname);
$restore_url = new moodle_url('/backup/restore.php', array(
'contextid' => $contextid, 'filename' => $filename));
}
redirect($restore_url);
} else {
redirect($url, get_string('filenotfound', 'error'));

View File

@ -185,6 +185,16 @@ class restore_ui_stage_confirm extends restore_ui_independent_stage implements f
protected $contextid;
protected $filename = null;
protected $filepath = null;
/**
* @var string Content hash of archive file to restore (if specified by hash)
*/
protected $contenthash = null;
/**
* @var string Pathname hash of stored_file object to restore
*/
protected $pathnamehash = null;
protected $details;
/**
@ -194,27 +204,54 @@ class restore_ui_stage_confirm extends restore_ui_independent_stage implements f
public function __construct($contextid) {
$this->contextid = $contextid;
$this->filename = required_param('filename', PARAM_FILE);
$this->filename = optional_param('filename', null, PARAM_FILE);
if ($this->filename === null) {
// Identify file object by its pathname hash.
$this->pathnamehash = required_param('pathnamehash', PARAM_ALPHANUM);
// The file content hash is also passed for security; users
// cannot guess the content hash (unless they know the file contents),
// so this guarantees that either the system generated this link or
// else the user has access to the restore archive anyhow.
$this->contenthash = required_param('contenthash', PARAM_ALPHANUM);
}
}
public function process() {
global $CFG;
if (!file_exists("$CFG->tempdir/backup/".$this->filename)) {
throw new restore_ui_exception('invalidrestorefile');
}
$outcome = $this->extract_file_to_dir();
if ($outcome) {
fulldelete($this->filename);
if ($this->filename) {
$archivepath = $CFG->tempdir . '/backup/' . $this->filename;
if (!file_exists($archivepath)) {
throw new restore_ui_exception('invalidrestorefile');
}
$outcome = $this->extract_file_to_dir($archivepath);
if ($outcome) {
fulldelete($archivepath);
}
} else {
$fs = get_file_storage();
$storedfile = $fs->get_file_by_hash($this->pathnamehash);
if (!$storedfile || $storedfile->get_contenthash() !== $this->contenthash) {
throw new restore_ui_exception('invalidrestorefile');
}
$outcome = $this->extract_file_to_dir($storedfile);
}
return $outcome;
}
protected function extract_file_to_dir() {
/**
* Extracts the file.
*
* @param string|stored_file $source Archive file to extract
*/
protected function extract_file_to_dir($source) {
global $CFG, $USER;
$this->filepath = restore_controller::get_tempdir_name($this->contextid, $USER->id);
$fb = get_file_packer('application/vnd.moodle.backup');
$result = $fb->extract_to_pathname("$CFG->tempdir/backup/".$this->filename,
"$CFG->tempdir/backup/$this->filepath/", null, $this);
$result = $fb->extract_to_pathname($source,
$CFG->tempdir . '/backup/' . $this->filepath . '/', null, $this);
// If any progress happened, end it.
if ($this->startedprogress) {