@@ -28,6 +28,7 @@ require_once("openmediavault/functions.inc");
2828require_once ("openmediavault/luks.inc " );
2929require_once ("openmediavault/rpcservice.inc " );
3030require_once ("openmediavault/notify.inc " );
31+ include_once ("openmediavault/lvm.inc " );
3132
3233class OMVRpcServiceLuksMgmt extends OMVRpcServiceAbstract {
3334
@@ -276,94 +277,155 @@ class OMVRpcServiceLuksMgmt extends OMVRpcServiceAbstract {
276277 // Check that the container is not already open, then use
277278 // the supplied passphrase or key file to unlock it if not.
278279 if (FALSE === $ luks ->isOpen ()) {
279- if (isset ($ params ['keyfile ' ]) && !empty ($ params ['keyfile ' ]))
280- $ success = $ luks ->open ($ params ['keyfile ' ], TRUE );
281- else
282- $ success = $ luks ->open ($ params ['passphrase ' ]);
283- if ($ success === FALSE ) {
280+ if (isset ($ params ['keyfile ' ]) && !empty ($ params ['keyfile ' ])) {
281+ $ key = $ params ['keyfile ' ];
282+ $ keyIsFile = TRUE ;
283+ } else {
284+ $ key = $ params ['passphrase ' ];
285+ $ keyIsFile = FALSE ;
286+ }
287+ if ($ luks ->open ($ key , $ keyIsFile ) === FALSE ) {
284288 throw new OMVException (OMVErrorMsg::E_EXEC_MISC ,
285289 sprintf (gettext ("Unable to unlock encrypted device: %s " ),
286290 $ luks ->getLastError ()));
287291 }
288292 }
293+ /* Downstream operations with the decrypted container */
294+ $ sdluks = new OMVStorageDeviceLUKS ($ luks ->getDecryptedDeviceFile ());
295+ $ df = $ sdluks ->getDeviceFile ();
296+ // If the container contains an LVM physical volume, determine the
297+ // volume group and activate it (otherwise the logical volume and any
298+ // filesystem on it won't be accessible)
299+ if (class_exists ("OMVLvmPhysicalVolume " )) {
300+ // Use LVM plugin if it's installed to get VG name
301+ $ pv = new OMVLvmPhysicalVolume ($ df );
302+ if (TRUE === $ pv ->exists ()) {
303+ $ vgName = $ pv ->getVGName ();
304+ }
305+ } else {
306+ // Fall back to manual method without LVM plugin
307+ $ cmd = sprintf ("export LANG=C; pvdisplay --noheadings " .
308+ "-C -o vg_name %s " ,
309+ escapeshellarg ($ df ));
310+ @OMVUtil::exec ($ cmd , $ output , $ result );
311+ if ($ result === 0 ) {
312+ $ vgName = trim ($ output [0 ]);
313+ }
314+ }
315+ // PV/VG was found - activate the VG, set devicefile to LV via
316+ // LVM plugin if available - although LVM/udev seems to
317+ // automatically mount filesystems in fstab from logical
318+ // volumes when activated, so probably not strictly necessary.
319+ if (isset ($ vgName )) {
320+ $ this ->debug (sprintf ("%s contains an LVM2 PV, part of VG: %s " ,
321+ $ df , $ vgName ));
322+ $ cmd = sprintf ("export LANG=C; vgchange -a y %s " ,
323+ escapeshellarg ($ vgName ));
324+ @OMVUtil::exec ($ cmd , $ output , $ result );
325+ if ($ result !== 0 ) {
326+ $ this ->debug ($ output );
327+ } else {
328+ if (class_exists ("OMVLvmVolumeGroup " )) {
329+ $ vg = new OMVLvmVolumeGroup ($ vgName );
330+ $ lvNames = $ vg ->getLVName ();
331+ $ fsDevs = array_map (function ($ lv ) use ($ vgName ) {
332+ return "/dev/ $ vgName- $ lv " ;
333+ }, $ lvNames );
334+ }
335+ }
336+ } else {
337+ // If not an LVM2 PV, just pass the container devicefile
338+ $ fsDevs = array ($ df );
339+ }
289340 // If the container contains a (referenced) filesystem, then mount it
290341 // (unless this automounting is disabled by the global configuration
291342 // option, OMV_LUKS_MOUNT_ON_UNLOCK - see initialize() above)
292343 if (TRUE === $ this ->mountOnUnlock ) {
293- $ sdluks = new OMVStorageDeviceLUKS ($ luks ->getDecryptedDeviceFile ());
294- $ df = $ sdluks ->getDeviceFile ();
295- if (FALSE !== OMVRpc::exec ("FileSystemMgmt " , "hasFilesystem " ,
296- array ("devicefile " => $ df ), $ context )) {
297- if (FALSE !== ($ meObject = OMVRpc::exec ("FsTab " , "getByFsName " ,
298- array ("id " => $ df ), $ context ))) {
299- switch (strtolower ($ meObject ['type ' ])) {
300- case "btrfs " :
301- /**
302- * Check if the unlocked device is part of a multi-
303- * device BTRFS filesystem, and don't attempt to mount
304- * it if it's not ready (not all devices are available,
305- * e.g. if more containers must be unlocked first, wait
306- * until they are all open).
307- * Note: using 'btrfs device ready' only works for the
308- * first time devices are opened - even if LUKS devices
309- * that are part of a multi-device BTRFS filesystem are
310- * later closed, some information is cached and so btrfs
311- * subsequently always reports the filesystem as ready.
312- * Hence, this workaround checks the number of devices
313- * online for the filesystem via 'btrfs filesystem show'
314- * instead, which should be more reliable.
315- * TODO: submit a patch to the core BTRFS backend that
316- * exposes this in a class function instead?
317- */
318- // Find out how many devices are in the filesystem
319- $ cmd = sprintf ("export LANG=C; btrfs filesystem " .
320- "show %s | grep 'Total devices' | " .
321- "awk '{print $3}' " ,
322- escapeshellarg ($ meObject ['uuid ' ]));
323- @OMVUtil::exec ($ cmd , $ output , $ result );
324- if ($ result !== 0 ) {
325- $ this ->setLastError ($ output );
326- continue ;
327- }
328- $ totalDevices = (int )$ output [0 ];
329- // If the fs has fewer than one device (an error
330- // of some kind occurred), skip mounting
331- if ($ totalDevices < 1 )
332- continue ;
333- unset($ cmd , $ output , $ result );
334- // Find out how many are online
335- $ cmd = sprintf ("export LANG=C; btrfs filesystem " .
336- "show %s | grep 'devid' | wc -l " ,
337- escapeshellarg ($ meObject ['uuid ' ]));
338- @OMVUtil::exec ($ cmd , $ output , $ result );
339- if ($ result !== 0 ) {
340- $ this ->setLastError ($ output );
341- continue ;
342- }
343- $ availableDevices = (int )$ output [0 ];
344- // If not all devices are online, skip mounting
345- if ($ availableDevices !== $ totalDevices )
346- continue ;
347- unset($ cmd , $ output , $ result );
348- case "ext2 " :
349- case "ext3 " :
350- case "ext4 " :
351- case "jfs " :
352- case "xfs " :
353- case "hfsplus " :
354- case "reiserfs " :
355- case "iso9660 " :
356- case "udf " :
357- case "vfat " :
358- case "ntfs " :
359- default :
360- OMVRpc::exec ("FileSystemMgmt " , "mount " ,
361- array (
362- "id " => $ meObject ['fsname ' ],
363- "fstab " => FALSE
364- ),
365- $ context );
344+ foreach ($ fsDevs as $ dev ) {
345+ $ this ->mountContainerFS ($ dev , $ context );
346+ }
347+ }
348+ }
349+
350+ /**
351+ * Helper function for mounting filesystems inside containers on unlocking.
352+ * @param devicefile The decrypted block special device of the
353+ * filesystem (inside the LUKS container) to mount.
354+ * @param context The context of the caller.
355+ * @return None.
356+ */
357+ private function mountContainerFS ($ deviceFile , $ context ) {
358+ if (FALSE !== OMVRpc::exec ("FileSystemMgmt " , "hasFilesystem " ,
359+ array ("devicefile " => $ deviceFile ), $ context )) {
360+ if (FALSE !== ($ meObject = OMVRpc::exec ("FsTab " , "getByFsName " ,
361+ array ("id " => $ deviceFile ), $ context ))) {
362+ switch (strtolower ($ meObject ['type ' ])) {
363+ case "btrfs " :
364+ /**
365+ * Check if the unlocked device is part of a multi-
366+ * device BTRFS filesystem, and don't attempt to mount
367+ * it if it's not ready (not all devices are available,
368+ * e.g. if more containers must be unlocked first, wait
369+ * until they are all open).
370+ * Note: using 'btrfs device ready' only works for the
371+ * first time devices are opened - even if LUKS devices
372+ * that are part of a multi-device BTRFS filesystem are
373+ * later closed, some information is cached and so btrfs
374+ * subsequently always reports the filesystem as ready.
375+ * Hence, this workaround checks the number of devices
376+ * online for the filesystem via 'btrfs filesystem show'
377+ * instead, which should be more reliable.
378+ * TODO: submit a patch to the core BTRFS backend that
379+ * exposes this in a class function instead?
380+ */
381+ // Find out how many devices are in the filesystem
382+ $ cmd = sprintf ("export LANG=C; btrfs filesystem " .
383+ "show %s | grep 'Total devices' | " .
384+ "awk '{print $3}' " ,
385+ escapeshellarg ($ meObject ['uuid ' ]));
386+ @OMVUtil::exec ($ cmd , $ output , $ result );
387+ if ($ result !== 0 ) {
388+ $ this ->setLastError ($ output );
389+ continue ;
390+ }
391+ $ totalDevices = (int )$ output [0 ];
392+ // If the fs has fewer than one device (an error
393+ // of some kind occurred), skip mounting
394+ if ($ totalDevices < 1 )
395+ continue ;
396+ unset($ cmd , $ output , $ result );
397+ // Find out how many are online
398+ $ cmd = sprintf ("export LANG=C; btrfs filesystem " .
399+ "show %s | grep 'devid' | wc -l " ,
400+ escapeshellarg ($ meObject ['uuid ' ]));
401+ @OMVUtil::exec ($ cmd , $ output , $ result );
402+ if ($ result !== 0 ) {
403+ $ this ->setLastError ($ output );
404+ continue ;
366405 }
406+ $ availableDevices = (int )$ output [0 ];
407+ // If not all devices are online, skip mounting
408+ if ($ availableDevices !== $ totalDevices )
409+ continue ;
410+ unset($ cmd , $ output , $ result );
411+ case "ext2 " :
412+ case "ext3 " :
413+ case "ext4 " :
414+ case "jfs " :
415+ case "xfs " :
416+ case "hfsplus " :
417+ case "reiserfs " :
418+ case "iso9660 " :
419+ case "udf " :
420+ case "vfat " :
421+ case "ntfs " :
422+ default :
423+ OMVRpc::exec ("FileSystemMgmt " , "mount " ,
424+ array (
425+ "id " => $ meObject ['fsname ' ],
426+ "fstab " => FALSE
427+ ),
428+ $ context );
367429 }
368430 }
369431 }
@@ -482,14 +544,14 @@ class OMVRpcServiceLuksMgmt extends OMVRpcServiceAbstract {
482544 }
483545 // Create the container.
484546 $ luks = new OMVLuksContainer ($ sd ->getDeviceFile ());
485- if (isset ($ params ['keyfile ' ]) && !empty ($ params ['keyfile ' ]))
486- $ success = $ luks -> create ( $ sd -> getDeviceFile (),
487- $ params [ ' keyfile ' ],
488- TRUE );
489- else
490- $ success = $ luks -> create ( $ sd -> getDeviceFile (),
491- $ params [ ' passphrase ' ]);
492- if ($ success === FALSE ) {
547+ if (isset ($ params ['keyfile ' ]) && !empty ($ params ['keyfile ' ])) {
548+ $ key = $ params [ ' keyfile ' ];
549+ $ keyIsFile = TRUE ;
550+ } else {
551+ $ key = $ params [ ' passphrase ' ];
552+ $ keyIsFile = FALSE ;
553+ }
554+ if ($ luks -> create ( $ key , $ keyIsFile ) === FALSE ) {
493555 throw new OMVException (OMVErrorMsg::E_EXEC_MISC , sprintf (
494556 gettext ("Unable to create the encrypted device: %s " ),
495557 $ luks ->getLastError ()));
@@ -747,11 +809,14 @@ class OMVRpcServiceLuksMgmt extends OMVRpcServiceAbstract {
747809 $ params ['devicefile ' ]));
748810 }
749811 // Remove the key
750- if (isset ($ params ['keyfile ' ]) && !empty ($ params ['keyfile ' ]))
751- $ success = $ luks ->removeKey ($ params ['keyfile ' ], TRUE );
752- else
753- $ success = $ luks ->removeKey ($ params ['passphrase ' ]);
754- if ($ success === FALSE ) {
812+ if (isset ($ params ['keyfile ' ]) && !empty ($ params ['keyfile ' ])) {
813+ $ key = $ params ['keyfile ' ];
814+ $ keyIsFile = TRUE ;
815+ } else {
816+ $ key = $ params ['passphrase ' ];
817+ $ keyIsFile = FALSE ;
818+ }
819+ if ($ luks ->removeKey ($ key , $ keyIsFile ) === FALSE ) {
755820 throw new OMVException (OMVErrorMsg::E_EXEC_MISC ,
756821 sprintf (gettext ("Unable to remove the key from the encrypted device: %s " ),
757822 $ luks ->getLastError ()));
@@ -871,12 +936,12 @@ class OMVRpcServiceLuksMgmt extends OMVRpcServiceAbstract {
871936 "devicefile":{ ' .$ GLOBALS ['OMV_JSONSCHEMA_DEVICEFILE ' ].'}
872937 }
873938 } ' );
874- // Check if container exists.
939+ // Validate the container
875940 $ luks = new OMVLuksContainer ($ params ['devicefile ' ]);
876- if (FALSE === $ luks ->exists ()) {
941+ if (is_null ( $ luks ) || ! $ luks ->exists ()) {
877942 throw new OMVException (OMVErrorMsg::E_MISC_FAILURE ,
878- sprintf (gettext ("No encryption found on '%s' " ),
879- $ params ['devicefile ' ]));
943+ sprintf (gettext ("LUKS container on '%s' not found " ),
944+ $ params ['devicefile ' ]));
880945 }
881946 // Extract the header to a temporary location and modify the
882947 // file mode/owner to allow the WebGUI PHP backend to unlink it.
@@ -892,9 +957,19 @@ class OMVRpcServiceLuksMgmt extends OMVRpcServiceAbstract {
892957 chmod ($ tmpFilePath , 0600 );
893958 chgrp ($ tmpFilePath , $ GLOBALS ['OMV_WEBGUI_FILE_OWNERGROUP_NAME ' ]);
894959 chown ($ tmpFilePath , $ GLOBALS ['OMV_WEBGUI_FILE_OWNERGROUP_NAME ' ]);
960+ // Build filename for file
961+ $ fn = preg_replace ('/\s+/ ' ,
962+ '- ' ,
963+ array (
964+ $ luks ->getVendor (),
965+ $ luks ->getModel (),
966+ $ luks ->getSerialNumber (),
967+ $ luks ->getUuid ()
968+ )
969+ );
895970 // Return values required by generic download RPC implementation.
896971 return array (
897- "filename " => "LUKS_header_ " .$ luks -> getUuid ( ).".bak " ,
972+ "filename " => "LUKS_header_ " .implode ( ' _ ' , $ fn ).".bak " ,
898973 "filepath " => $ tmpFilePath ,
899974 "contenttype " => "application/octet-stream " ,
900975 "unlink " => TRUE
0 commit comments