diff --git a/backup/restorefile.php b/backup/restorefile.php index 0c4095ba4c9..1d12d0dd18a 100644 --- a/backup/restorefile.php +++ b/backup/restorefile.php @@ -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')); diff --git a/backup/util/ui/restore_ui_stage.class.php b/backup/util/ui/restore_ui_stage.class.php index 189d62753b8..2cad0d7857a 100644 --- a/backup/util/ui/restore_ui_stage.class.php +++ b/backup/util/ui/restore_ui_stage.class.php @@ -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) {