3 require_once("openmediavault/object.inc");
4 require_once("openmediavault/config.inc");
5 require_once("openmediavault/error.inc");
6 require_once("openmediavault/util.inc");
7 require_once("openmediavault/rpcservice.inc");
8 require_once("openmediavault/notify.inc");
9 require_once("zfs/Utils.php");
10 require_once("zfs/Dataset.php");
11 require_once("zfs/Snapshot.php");
12 require_once("zfs/Zvol.php");
13 require_once("zfs/Zpool.php");
14 require_once("zfs/NotifyListener.php");
16 class OMVRpcServiceZFS extends OMVRpcServiceAbstract {
17 public function getName() {
18 return "ZFS"; // RPC Service name. Same as in .js files
21 /* Initialize the RPC service. Different methods of the RPC service are declared here*/
22 public function initialize() {
23 $this->registerMethod("addPool");
24 $this->registerMethod("getObjectTree");
25 $this->registermethod("passParam");
26 $this->registermethod("addObject");
27 $this->registermethod("deleteObject");
28 $this->registermethod("getProperties");
29 $this->registermethod("setProperties");
30 $this->registermethod("inherit");
31 $this->registermethod("getSharedParams");
32 $this->registermethod("createShare");
33 $this->registermethod("getObjectDetails");
34 $this->registermethod("expandPool");
37 public function addPool($params, $context) {
38 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
39 // Validate the parameters of the RPC service method.
40 $this->validateMethodParams($params, '{
43 "pooltype":{"type":"string","enum":["basic","mirror",' .
44 '"raidz1","raidz2","raidz3"]},
45 "force":{"type":"boolean"},
46 "mountpoint":{"type":"string"},
47 "name":{"type":"string"},
48 "devices":{"type":"string"}
51 switch ($params['pooltype']) {
53 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSPLAIN;
56 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSMIRROR;
59 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ1;
62 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ2;
65 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ3;
68 throw new OMVModuleZFSException("Incorrect pool type specified");
71 //Check for user supplied options
73 if ($params['force']) {
76 if (strlen($params['mountpoint']) > 0) {
77 $opts .= "-m " . $params['mountpoint'] . " ";
80 //Use /dev/disk/by-path as deafult when creating new pools as suggested in ZoL FAQ.
81 $disks = preg_split("/[,;]/", $params['devices']);
82 if (file_exists("/dev/disk/by-path/")) {
84 foreach ($disks as $disk) {
85 $tmp_disks[] = OMVModuleZFSUtil::getDiskPath($disk);
90 $vdev = new OMVModuleZFSVdev($params['name'], $pooltype, $disks);
91 $pool = new OMVModuleZFSZpool($vdev, $opts);
92 //Ugly fix to solve the problem of blkid not displaying info on newly created pools
94 $pool->import($pool->getName());
97 public function getObjectTree($params, $context) {
98 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
99 $objects = OMVModuleZFSUtil::getZFSFlatArray();
101 foreach ($objects as $a){
102 $new[$a['parentid']][] = $a;
104 $tree = OMVModuleZFSUtil::createTree($new, $new['root']);
105 OMVModuleZFSUtil::addMissingOMVMntEnt(); //Adds missing ZFS filesystems to the OMV core
109 public function passParam($params, $context) {
110 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
111 // Validate the parameters of the RPC service method.
112 $this->validateMethodParams($params, '{
115 "key":{"type":"string"},
116 "value":{"type":"string"}
119 return array($params['key'] => $params['value']);
122 public function addObject($params, $context) {
123 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
124 // Validate the parameters of the RPC service method.
125 $this->validateMethodParams($params, '{
128 "type":{"type":"string","enum":["filesystem","snapshot",' .
130 "path":{"type":"string"},
131 "name":{"type":"string"},
132 "size":{"type":"string"}
135 switch ($params['type']) {
137 $name = $params['path'] . "/" . $params['name'];
138 $tmp = new OMVModuleZFSDataset($name);
141 $name = $params['path'] . "@" . $params['name'];
142 $tmp = new OMVModuleZFSSnapshot($name);
145 $name = $params['path'] . "/" . $params['name'];
146 $tmp = new OMVModuleZFSZvol($name);
147 $tmp->create($params['size']);
150 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
155 public function deleteObject($params, $context) {
156 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
157 // Validate the parameters of the RPC service method.
158 $this->validateMethodParams($params, '{
161 "type":{"type":"string","enum":["Filesystem","Snapshot",' .
162 '"Volume","Clone","Pool"]},
163 "name":{"type":"string"}
167 $name = $params['name'];
168 switch ($params['type']) {
170 OMVModuleZFSUtil::deleteShares($name);
171 $tmp = new OMVModuleZFSDataset($name);
175 $tmp = new OMVModuleZFSDataset($name);
179 $tmp = new OMVModuleZFSSnapshot($name);
183 $tmp = new OMVModuleZFSZvol($name);
187 $disks = OMVModuleZFSUtil::getDevDisksByPool($name);
188 $pooluuid = OMVModuleZFSUtil::getUUIDbyName($name);
189 $tmp = new OMVModuleZFSZpool($name);
191 $xpath = "//system/fstab/mntent[fsname='" . $pooluuid . "' and type='zfs']";
192 $object = $xmlConfig->get($xpath);
193 $xmlConfig->delete($xpath);
194 OMVModuleZFSUtil::clearZFSLabel($disks);
197 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
202 public function getProperties($params, $context) {
203 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
204 // Validate the parameters of the RPC service method.
205 $this->validateMethodParams($params, '{
208 "type":{"type":"string"},
209 "name":{"type":"string"},
210 "start":{"type":"integer"},
211 "limit":{'.$GLOBALS['OMV_JSONSCHEMA_COUNTFIELD'].'},
212 "sortfield":{'.$GLOBALS['OMV_JSONSCHEMA_SORTFIELD'].'},
213 "sortdir":{'.$GLOBALS['OMV_JSONSCHEMA_SORTDIR'].'}
217 $name = $params['name'];
218 switch ($params['type']) {
221 $tmp = new OMVModuleZFSDataset($name);
224 $tmp = new OMVModuleZFSSnapshot($name);
227 $tmp = new OMVModuleZFSZvol($name);
230 $tmp = new OMVModuleZFSZpool($name);
233 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
236 $properties = $tmp->getProperties();
237 foreach ($properties as $propertyk => $propertyv) {
238 if (!(strcmp($propertyv['source'], "-") == 0)) {
239 $objects[] = array('property' => $propertyk,
240 'value' => $propertyv['value'],
241 'source' => $propertyv['source'],
242 'modified' => "false");
248 public function setProperties($params, $context) {
249 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
250 // Validate the parameters of the RPC service method.
251 $this->validateMethodParams($params, '{
254 "type":{"type":"string","enum":["Filesystem","Snapshot",' .
255 '"Volume","Clone","Pool"]},
256 "name":{"type":"string"},
257 "properties":{"type":"array","items":{
260 "property":{"type":"string"},
261 "value":{"type":"string"}}}}
265 switch ($params['type']) {
268 $tmp = new OMVModuleZFSDataset($params['name']);
271 $tmp = new OMVModuleZFSSnapshot($params['name']);
274 $tmp = new OMVModuleZFSZvol($params['name']);
277 $tmp = new OMVModuleZFSZpool($params['name']);
280 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
283 foreach ($params['properties'] as $property) {
286 $objects[$property['property']] = $property['value'];
287 $tmp->setProperties($objects);
288 if ((strcmp($property['property'], "mountpoint") === 0) && (strcmp($params['type'], "Filesystem") === 0)) {
289 OMVModuleZFSUtil::relocateFilesystem($params['name']);
294 public function inherit($params, $context) {
295 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
296 // Validate the parameters of the RPC service method.
297 $this->validateMethodParams($params, '{
300 "type":{"type":"string","enum":["Filesystem","Snapshot",' .
301 '"Volume","Clone","Pool"]},
302 "name":{"type":"string"},
303 "property":{"type":"string"}
306 // Create a background process.
307 $bgStatusFilename = $this->createBgProcStatus();
308 $pid = $this->fork();
309 if($pid > 0) { // Parent process.
310 $this->initializeBgProcStatus($bgStatusFilename, $pid);
311 return $bgStatusFilename;
315 $bgOutputFilename = $this->createBgProcOutput();
316 $this->updateBgProcStatus($bgStatusFilename, "outputfilename", $bgOutputFilename);
317 switch ($params['type']) {
320 $tmp = new OMVModuleZFSDataset($params['name']);
323 $tmp = new OMVModuleZFSSnapshot($params['name']);
326 $tmp = new OMVModuleZFSZvol($params['name']);
329 $tmp = new OMVModuleZFSZpool($params['name']);
332 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
335 $tmp->inherit($params['property']);
336 $this->finalizeBgProcStatus($bgStatusFilename, $output);
338 } catch(Exception $e) {
339 $this->finalizeBgProcStatus($bgStatusFilename, "", $e);
344 public function getSharedParams($params, $context) {
345 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
346 // Validate the parameters of the RPC service method.
347 $this->validateMethodParams($params, '{
350 "type":{"type":"string"},
351 "name":{"type":"string"}
355 //$ds = new OMVModuleZFSDataset($params['name']);
356 //$mountpoint = $ds->getMountPoint();
358 //"mountpoint" => $mountpoint,
359 "name" => $params['name'],
360 "type" => $params['type']);
363 public function createShare($params, $context) {
365 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
366 // Validate the parameters of the RPC service method.
367 $this->validateMethodParams($params, '{
370 "name":{"type":"string"},
371 "type":{"type":"string","enum":["Filesystem","Clone"]},
372 "sharename":{'.$GLOBALS['OMV_JSONSCHEMA_SHARENAME'].'},
373 "comment":{"type":"string"},
374 "mode":{"type":"string","enum":["700","750","755",'.
375 '"770","775","777"],"optional":true},
376 "mountpoint":{"type":"string"}
380 // The field 'reldirpath' may not contain the characters '..'. This
381 // is because of security reasons: the given canonicalized absolute
382 // path MUST be below the given mount point.
383 if(1 == preg_match("/\.\./", $params['mountpoint'])) {
384 throw new OMVException(OMVErrorMsg::E_RPC_SERVICE_METHOD_INVALID_PARAMS,
385 sprintf(gettext("The field '%s' contains forbidden two-dot symbols"), "mountpoint"));
388 // Prepare the configuration object. Use the name of the shared
389 // folder as the relative directory name of the share.
390 switch ($params['type']) {
392 $tmp = new OMVModuleZFSDataset($params['name']);
395 $tmp = new OMVModuleZFSDataset($params['name']);
398 throw new OMVModuleZFSException("Illegal type provided: " . $params['type']);
402 $poolname = OMVModuleZFSUtil::getPoolname($params['name']);
403 $pooluuid = OMVModuleZFSUtil::getUUIDbyName($poolname);
404 $dir = $tmp->getMountPoint();
406 //Get the mntent object and fetch it's uuid.
407 $xpath = "//system/fstab/mntent[fsname='" . $pooluuid . "' and dir='" . $dir . "' and type='zfs']";
408 $mountpoint = $xmlConfig->get($xpath);
409 $mntentref = $mountpoint['uuid'];
411 $uuid = OMVUtil::uuid();
412 $pathName = $dir . "/" . trim($params['mountpoint'], "/");
413 $reldirpath = trim($params['mountpoint'], "/");
416 "name" => $params['sharename'],
417 "comment" => $params['comment'] . "*** ZFS share on " . $params['name'] . " ***",
418 "mntentref" => $mntentref,
419 "reldirpath" => $reldirpath
422 // Set the configuration object.
424 // Check uniqueness. The share name must be global unique because
425 // the name is also used when exporting a shared folder via NFS for
427 $xpath = sprintf("//system/shares/sharedfolder[name='%s']",
429 if(TRUE === $xmlConfig->exists($xpath)) {
430 throw new OMVException(OMVErrorMsg::E_CONFIG_OBJECT_UNIQUENESS,
431 gettext("A shared folder with the given name already exists"));
434 // Add empty list of privileges per default.
435 $object['privileges'] = array();
437 // Append object to configuration.
438 $success = $xmlConfig->set("//system/shares",
439 array("sharedfolder" => $object));
440 if(FALSE === $success) {
441 throw new OMVException(OMVErrorMsg::E_CONFIG_SET_OBJECT_FAILED);
444 // Append the file mode field to the notification object if set.
446 $object['mode'] = "775";
447 if(array_key_exists("mode", $params)) {
448 $object['mode'] = $params['mode'];
451 // Create the shared folder directory if necessary.
452 if(FALSE === file_exists($pathName)) {
453 // Create the directory. Note, the function seems to have a bug
454 // when using the mask parameter. E.g. octdec("777") does not
455 // create the correct permissions as expected, thus change the
457 if(FALSE === mkdir($pathName, 0700, TRUE)) {
458 throw new OMVException(OMVErrorMsg::E_MISC_FAILURE,
459 sprintf("Failed to create the directory '%s'", $pathName));
461 // Change the directory mode.
462 if(FALSE === chmod($pathName, octdec($object['mode']))) {
463 throw new OMVException(OMVErrorMsg::E_MISC_FAILURE,
464 sprintf("Failed to set file mode to '%s' for '%s'",
465 $object['mode'], $pathName));
469 // Change group owner of directory to configured default group,
471 if(FALSE === chgrp($pathName, $GLOBALS['OMV_USERMGMT_DEFAULT_GROUP'])) {
472 throw new OMVException(OMVErrorMsg::E_MISC_FAILURE,
473 sprintf("Failed to set file group to '%s' for '%s'",
474 $GLOBALS['OMV_USERMGMT_DEFAULT_GROUP'], $pathName));
477 // Set the setgid bit. Setting this permission means that all files
478 // created in the folder will inherit the group of the folder rather
479 // than the primary group of the user who creates the file.
480 $mode = fileperms($pathName) | 02000;
481 if(FALSE === chmod($pathName, $mode)) {
482 throw new OMVException(OMVErrorMsg::E_MISC_FAILURE,
483 sprintf("Failed to set file mode to '%o' for '%s'",
487 // Notify configuration changes.
488 $dispatcher = &OMVNotifyDispatcher::getInstance();
489 $dispatcher->notify(OMV_NOTIFY_CREATE,"org.openmediavault.system.shares.sharedfolder", $object);
490 // Return the configuration object.
494 public function getObjectDetails($params, $context) {
495 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
496 // Validate the parameters of the RPC service method.
497 $this->validateMethodParams($params, '{
500 "name":{"type":"string"},
501 "type":{"type":"string"}
505 switch ($params['type']) {
507 $output .= "Filesystem details (zfs get all):\n\r\n\r";
508 $cmd = "zfs get all {$params['name']}";
511 $output .= "Volume details (zfs get all):\n\r\n\r";
512 $cmd = "zfs get all {$params['name']}";
515 $output .= "Snapshot details (zfs get all):\n\r\n\r";
516 $cmd = "zfs get all {$params['name']}";
519 $output .= "Pool details (zpool get all):\n\r\n\r";
520 $cmd = "zpool get all {$params['name']}";
523 throw new OMVModuleZFSException("Incorrect type provided");
525 OMVModuleZFSUtil::exec($cmd,$out,$res);
526 $output .= implode("\n\r", $out);
527 return array("details" => $output);
530 public function expandPool($params, $context) {
531 $this->validateMethodContext($context, array("role" => OMV_ROLE_ADMINISTRATOR));
532 // Validate the parameters of the RPC service method.
533 $this->validateMethodParams($params, '{
536 "pool_type":{"type":"string","enum":["Basic","Mirror",' .
537 '"Raidz1","Raidz2","Raidz3"]},
538 "name":{"type":"string"},
539 "devices":{"type":"string"}
542 $pool = new OMVModuleZFSZpool($params['name']);
543 switch ($params['pool_type']) {
545 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSPLAIN;
548 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSMIRROR;
551 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ1;
554 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ2;
557 $pooltype = OMVModuleZFSVdevType::OMVMODULEZFSRAIDZ3;
560 throw new OMVModuleZFSException("Incorrect pool type specified");
563 //Use /dev/disk/by-path as deafult when creating new pools as suggested in ZoL FAQ.
564 $disks = preg_split("/[,;]/", $params['devices']);
565 if (file_exists("/dev/disk/by-path/")) {
566 $tmp_disks = array();
567 foreach ($disks as $disk) {
568 $tmp_disks[] = OMVModuleZFSUtil::getDiskPath($disk);
572 $vdev[] = new OMVModuleZFSVdev($params['name'], $pooltype, $disks);
573 $pool->addVdev($vdev);
574 //Ugly fix to solve the problem of blkid not displaying info on newly created pools
576 $pool->import($pool->getName());
580 // Register the RPC service.
581 $rpcServiceMgr = &OMVRpcServiceMgr::getInstance(); // Get the "root" instance for the Services
582 $rpcServiceMgr->registerService(new OMVRpcServiceZFS()); // Register a new instance of the RPC service described above