From: Marcin Haba Date: Mon, 27 Nov 2017 20:03:11 +0000 (+0100) Subject: baculum: Add restore hardlinks support in api X-Git-Tag: Release-9.0.7~27 X-Git-Url: https://git.sur5r.net/?p=bacula%2Fbacula;a=commitdiff_plain;h=b313a39ee65173614ad0618f76464811b31908cf baculum: Add restore hardlinks support in api --- diff --git a/gui/baculum/protected/API/Pages/API/BVFSRestore.php b/gui/baculum/protected/API/Pages/API/BVFSRestore.php index 73eaa66e78..f94c1d0b95 100644 --- a/gui/baculum/protected/API/Pages/API/BVFSRestore.php +++ b/gui/baculum/protected/API/Pages/API/BVFSRestore.php @@ -21,51 +21,56 @@ */ class BVFSRestore extends BaculumAPIServer { + public function create($params) { + $misc = $this->getModule('misc'); $jobids = property_exists($params, 'jobids') ? $params->jobids : null; $fileids = property_exists($params, 'fileid') ? $params->fileid : null; $dirids = property_exists($params, 'dirid') ? $params->dirid : null; + $findexes = property_exists($params, 'findex') ? $params->findex : null; $path = property_exists($params, 'path') ? $params->path : null; - $is_valid = true; - if (!is_null($jobids)) { - $jobidsList = explode(',', $jobids); - if(is_array($jobidsList)) { - for($i = 0; $i < count($jobidsList); $i++) { - $job = $this->getModule('job')->getJobById($jobidsList[$i]); - if(is_null($job)) { - $is_valid = false; - break; - } - } - } else { - $is_valid = false; - } - } else { - $is_valid = false; + if (!is_null($jobids) && !$misc->isValidIdsList($jobids)) { + $this->output = BVFSError::MSG_ERROR_INVALID_JOBID_LIST; + $this->error = BVFSError::ERROR_INVALID_JOBID_LIST; + return; + } + if (!is_null($fileids) && !$misc->isValidIdsList($fileids)) { + $this->output = BVFSError::MSG_ERROR_INVALID_FILEID_LIST; + $this->error = BVFSError::ERROR_INVALID_FILEID_LIST; + return; + } + if (!is_null($dirids) && !$misc->isValidIdsList($dirids)) { + $this->output = BVFSError::MSG_ERROR_INVALID_DIRID_LIST; + $this->error = BVFSError::ERROR_INVALID_DIRID_LIST; + return; + } + if (!is_null($findexes) && !$misc->isValidIdsList($findexes)) { + $this->output = BVFSError::MSG_ERROR_INVALID_FILEINDEX_LIST; + $this->error = BVFSError::ERROR_INVALID_FILEINDEX_LIST; + return; } - if($is_valid === true) { - if(!is_null($path)) { - $cmd = array('.bvfs_restore', 'jobid="' . $jobids . '"', 'path="' . $path . '"'); - if(!is_null($fileids)) { - array_push($cmd, 'fileid="' . $fileids . '"'); - } - if(!is_null($dirids)) { - array_push($cmd, 'dirid="' . $dirids . '"'); - } + if (!is_null($path) && !$misc->isValidBvfsPath($path)) { + $this->output = BVFSError::MSG_ERROR_INVALID_RPATH; + $this->error = BVFSError::ERROR_INVALID_RPATH; + return; + } - $result = $this->getModule('bconsole')->bconsoleCommand($this->director, $cmd, $this->user); - $this->output = $result->output; - $this->error = $result->exitcode; - } else { - $this->output = BVFSError::MSG_ERROR_INVALID_RPATH; - $this->error = BVFSError::ERROR_INVALID_RPATH; - } - } else { - $this->output = BVFSError::MSG_ERROR_JOB_DOES_NOT_EXISTS; - $this->error = BVFSError::ERROR_JOB_DOES_NOT_EXISTS; + $cmd = array('.bvfs_restore', 'jobid="' . $jobids . '"', 'path="' . $path . '"'); + if (is_string($fileids)) { + array_push($cmd, 'fileid="' . $fileids . '"'); + } + if (is_string($dirids)) { + array_push($cmd, 'dirid="' . $dirids . '"'); } + if (is_string($findexes)) { + array_push($cmd, 'hardlink="' . $findexes . '"'); + } + + $result = $this->getModule('bconsole')->bconsoleCommand($this->director, $cmd, $this->user); + $this->output = $result->output; + $this->error = $result->exitcode; } } ?> diff --git a/gui/baculum/protected/Common/Class/Errors.php b/gui/baculum/protected/Common/Class/Errors.php index 8a308d5fb3..b0b7304f7c 100644 --- a/gui/baculum/protected/Common/Class/Errors.php +++ b/gui/baculum/protected/Common/Class/Errors.php @@ -111,13 +111,19 @@ class FileSetError extends GenericError { } class BVFSError extends GenericError { - const ERROR_JOB_DOES_NOT_EXISTS = 70; + const ERROR_INVALID_JOBID_LIST = 70; const ERROR_INVALID_RPATH = 71; const ERROR_INVALID_RESTORE_PATH = 72; + const ERROR_INVALID_FILEID_LIST = 73; + const ERROR_INVALID_FILEINDEX_LIST = 74; + const ERROR_INVALID_DIRID_LIST = 75; - const MSG_ERROR_JOB_DOES_NOT_EXISTS = 'Job with inputted jobid does not exist.'; + const MSG_ERROR_INVALID_JOBID_LIST = 'Invalid jobid list.'; const MSG_ERROR_INVALID_RPATH = 'Inputted path for restore is invalid. Proper format is b2[0-9]+.'; const MSG_ERROR_INVALID_RESTORE_PATH = 'Inputted BVFS path param is invalid.'; + const MSG_ERROR_INVALID_FILEID_LIST = 'Invalid fileid list.'; + const MSG_ERROR_INVALID_FILEINDEX_LIST = 'Invalid file index list.'; + const MSG_ERROR_INVALID_DIRID_LIST = 'Invalid dirid list.'; } class JSONToolsError extends GenericError { diff --git a/gui/baculum/protected/Common/Class/Miscellaneous.php b/gui/baculum/protected/Common/Class/Miscellaneous.php index 833f23352e..0d9871bc15 100644 --- a/gui/baculum/protected/Common/Class/Miscellaneous.php +++ b/gui/baculum/protected/Common/Class/Miscellaneous.php @@ -47,7 +47,7 @@ class Miscellaneous extends TModule { 'd' => 'DiskToCatalog' ); - private $jobStates = array( + public $jobStates = array( 'C' => array('value' => 'Created', 'description' =>'Created but not yet running'), 'R' => array('value' => 'Running', 'description' => 'Running'), 'B' => array('value' => 'Blocked', 'description' => 'Blocked'), @@ -90,6 +90,14 @@ class Miscellaneous extends TModule { 'bcons' => array('full_name' => 'Console', 'main_resource' => 'Director') ); + private $replace_opts = array( + 'always', + 'ifnewer', + 'ifolder', + 'never' + ); + + /** * Getting the licence from file. * @@ -166,6 +174,9 @@ class Miscellaneous extends TModule { return $statesByType; } + /* + * @TODO: Move it to separate validation module. + */ public function isValidJobLevel($jobLevel) { return array_key_exists($jobLevel, $this->getJobLevels()); } @@ -174,6 +185,22 @@ class Miscellaneous extends TModule { return (preg_match('/^[\w:\.\-\s]{1,127}$/', $name) === 1); } + public function isValidPath($path) { + return (preg_match('/^[\p{L}\p{N}\p{Z}\[\]\(\)\-\+\/\\\:\.#~_,{}!]{1,1000}$/', $path) === 1); + } + + public function isValidReplace($replace) { + return in_array($replace, $this->replace_opts); + } + + public function isValidIdsList($list) { + return (preg_match('/^[\d,]+$/', $list) === 1); + } + + public function isValidBvfsPath($path) { + return (preg_match('/^b2\d+$/', $path) === 1); + } + /** * Writing INI-style configuration file. * @@ -257,13 +284,51 @@ class Miscellaneous extends TModule { $base64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; $lstat = trim($lstat); $lstat_fields = explode(' ', $lstat); - - if(count($lstat_fields) !== 16) { - die('Błąd! Niepoprawna ilość pól wartości LStat. Proszę upewnić się, że podany ciąg znaków jest poprawną wartością LStat'); + $lstat_len = count($lstat_fields); + if ($lstat_len < 16) { + // not known or empty lstat value + return; + } elseif ($lstat_len > 16) { + // cut off unknown fields + array_splice($lstat_fields, 16); } - list($dev, $inode, $mode, $nlink, $uid, $gid, $rdev, $size, $blocksize, $blocks, $atime, $mtime, $ctime, $linkfi, $flags, $data) = $lstat_fields; - $encoded_values = array('dev' => $dev, 'inode' => $inode, 'mode' => $mode, 'nlink' => $nlink, 'uid' => $uid, 'gid' => $gid, 'rdev' => $rdev, 'size' => $size, 'blocksize' => $blocksize, 'blocks' => $blocks, 'atime' => $atime, 'mtime' => $mtime, 'ctime' => $ctime, 'linkfi' => $linkfi, 'flags' => $flags, 'data' => $data); + list( + $dev, + $inode, + $mode, + $nlink, + $uid, + $gid, + $rdev, + $size, + $blocksize, + $blocks, + $atime, + $mtime, + $ctime, + $linkfi, + $flags, + $data + ) = $lstat_fields; + $encoded_values = array( + 'dev' => $dev, + 'inode' => $inode, + 'mode' => $mode, + 'nlink' => $nlink, + 'uid' => $uid, + 'gid' => $gid, + 'rdev' => $rdev, + 'size' => $size, + 'blocksize' => $blocksize, + 'blocks' => $blocks, + 'atime' => $atime, + 'mtime' => $mtime, + 'ctime' => $ctime, + 'linkfi' => $linkfi, + 'flags' => $flags, + 'data' => $data + ); $ret = array(); foreach($encoded_values as $key => $val) { @@ -294,12 +359,28 @@ class Miscellaneous extends TModule { } elseif($match['name'] != '..') { $match['name'] .= '/'; } - $elements[] = array('pathid' => $match['pathid'], 'filenameid' => $match['filenameid'], 'fileid' => $match['fileid'], 'jobid' => $match['jobid'], 'lstat' => $match['lstat'], 'name' => $match['name'], 'type' => 'dir'); + $elements[] = array( + 'pathid' => $match['pathid'], + 'filenameid' => $match['filenameid'], + 'fileid' => $match['fileid'], + 'jobid' => $match['jobid'], + 'lstat' => $this->decode_bacula_lstat($match['lstat']), + 'name' => $match['name'], + 'type' => 'dir' + ); } elseif(preg_match('/^(?P\d+)\t(?P\d+)\t(?P\d+)\t(?P\d+)\t(?P[a-zA-z0-9\+\/\ ]+)\t(?P[^\/]+)$/', $list[$i], $match) == 1) { if($match['name'] == '.') { continue; } - $elements[] = array('pathid' => $match['pathid'], 'filenameid' => $match['filenameid'], 'fileid' => $match['fileid'], 'jobid' => $match['jobid'], 'lstat' => $match['lstat'], 'name' => $match['name'], 'type' => 'file'); + $elements[] = array( + 'pathid' => $match['pathid'], + 'filenameid' => $match['filenameid'], + 'fileid' => $match['fileid'], + 'jobid' => $match['jobid'], + 'lstat' => $this->decode_bacula_lstat($match['lstat']), + 'name' => $match['name'], + 'type' => 'file' + ); } } usort($elements, 'sortFilesListByName'); @@ -310,7 +391,18 @@ class Miscellaneous extends TModule { $elements = array(); for($i = 0; $i < count($list); $i++) { if(preg_match('/^(?P\d+)\t(?P\d+)\t(?P\d+)\t(?P\d+)\t(?P[a-zA-Z0-9\+\/\ ]+)\t(?P.+)\t(?P.+)\t(?P\d+)$/', $list[$i], $match) == 1) { - $elements[$match['fileid']] = array('name' => $filename, 'pathid' => $match['pathid'], 'filenameid' => $match['filenameid'], 'fileid' => $match['fileid'], 'jobid' => $match['jobid'], 'lstat' => $this->decode_bacula_lstat($match['lstat']), 'md5' => $match['md5'], 'volname' => $match['volname'], 'inchanger' => $match['inchanger'], 'type' => 'file'); + $elements[$match['fileid']] = array( + 'name' => $filename, + 'pathid' => $match['pathid'], + 'filenameid' => $match['filenameid'], + 'fileid' => $match['fileid'], + 'jobid' => $match['jobid'], + 'lstat' => $this->decode_bacula_lstat($match['lstat']), + 'md5' => $match['md5'], + 'volname' => $match['volname'], + 'inchanger' => $match['inchanger'], + 'type' => 'file' + ); } } return $elements;