From d455093672ba35c6c27a2bd8c7be7f0ddd3ad942 Mon Sep 17 00:00:00 2001 From: Umer Saleem Date: Thu, 28 Mar 2024 16:28:32 +0500 Subject: [PATCH] Recursively mount root dataset during boot if it is not encrypted Import the pools during boot without mounting any of the datasets. Check on 'feature@encryption' for imported pool. If feature is not active, recursively mount the root datasets, which effectively mounts all datasets present on the pool. If the encryption feature is active, continue and unlock the datasets. Signed-off-by: Umer Saleem --- .../middlewared/plugins/pool_/import_pool.py | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/middlewared/middlewared/plugins/pool_/import_pool.py b/src/middlewared/middlewared/plugins/pool_/import_pool.py index 360fb25cba24..7441f587744f 100644 --- a/src/middlewared/middlewared/plugins/pool_/import_pool.py +++ b/src/middlewared/middlewared/plugins/pool_/import_pool.py @@ -212,6 +212,59 @@ async def import_pool(self, job, data): return True + @private + def recursive_mount(self, name): + cmd = [ + 'zfs', 'mount', + '-R', # recursive flag + name, # name of the zpool / root dataset + ] + try: + self.logger.debug('Going to mount root dataset recusively: %r', name) + cp = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + if cp.returncode != 0: + self.logger.error( + 'Failed to mount datasets for pool: %r with error: %r', + name, cp.stdout.decode() + ) + return False + return True + except Exception: + self.logger.error( + 'Unhandled exception while mounting datasets for pool: %r', + name, exc_info=True + ) + return False + + @private + def encryption_is_active(self, name): + cmd = [ + 'zpool', 'get', + '-H', # use in script + '-o', 'value', # retrieve the value + 'feature@encryption', # property to retrieve + name, # name of the zpool + ] + try: + self.logger.debug('Checking if feature@encryption is active on pool: %r', name) + cp = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + if cp.returncode != 0: + self.logger.error( + 'Failed to get feature@encryption for pool: %r with error: %r', + name, cp.stdout.decode() + ) + return False + if cp.stdout.decode().strip() == 'active': + return True + else: + return False + except Exception: + self.logger.error( + 'Unhandled exception while checking on feature@encryption for pool: %r', + name, exc_info=True + ) + return False + @private def import_on_boot_impl(self, vol_name, vol_guid, set_cachefile=False): cmd = [ @@ -219,6 +272,7 @@ def import_on_boot_impl(self, vol_name, vol_guid, set_cachefile=False): vol_guid, # the GUID of the zpool '-R', '/mnt', # altroot '-m', # import pool with missing log device(s) + '-N', # do not mount the datasets '-f', # force import since hostid can change (upgrade from CORE to SCALE changes it, for example) '-o', f'cachefile={ZPOOL_CACHE_FILE}' if set_cachefile else 'cachefile=none', ] @@ -421,6 +475,10 @@ def import_on_boot(self, job): if not self.import_on_boot_impl(name, guid, set_cachefile_property): continue + if not self.encryption_is_active(name): + self.recursive_mount(name) + continue + self.unlock_on_boot_impl(name) # no reason to wait on this to complete