storage: add Pool.included_in() method for checking nested pools

It may happen that one pool is inside a volume of other pool. This is
the case for example for varlibqubes pool (file driver,
dir_path=/var/lib/qubes) and default lvm pool (lvm_thin driver). The
latter include whole root filesystem, so /var/lib/qubes too.
This is relevant for proper disk space calculation - to not count some
space twice.

QubesOS/qubes-issues#3240
QubesOS/qubes-issues#3241
This commit is contained in:
Marek Marczykowski-Górecki 2018-03-19 22:36:45 +01:00
parent 99f430511a
commit d40fae9756
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
4 changed files with 51 additions and 0 deletions

View File

@ -815,6 +815,17 @@ class Pool(object):
'''
raise self._not_implemented("get_volume")
def included_in(self, app):
''' Check if this pool is physically included in another one
This works on best-effort basis, because one pool driver may not know
all the other drivers.
:param app: Qubes() object to lookup other pools in
:returns pool or None
'''
pass
@property
def size(self):
''' Storage pool size in bytes '''
@ -865,6 +876,27 @@ def isodate(seconds=time.time()):
''' Helper method which returns an iso date '''
return datetime.utcfromtimestamp(seconds).isoformat("T")
def search_pool_containing_dir(pools, dir_path):
''' Helper function looking for a pool containing given directory.
This is useful for implementing Pool.included_in method
'''
# prefer filesystem pools
for pool in pools:
if hasattr(pool, 'dir_path'):
if dir_path.startswith(pool.dir_path):
return pool
# then look for lvm
for pool in pools:
if hasattr(pool, 'thin_pool') and hasattr(pool, 'volume_group'):
if (pool.volume_group, pool.thin_pool) == \
DirectoryThinPool.thin_pool(dir_path):
return pool
return None
class VmCreationManager(object):
''' A `ContextManager` which cleans up if volume creation fails.

View File

@ -163,6 +163,12 @@ class FilePool(qubes.storage.Pool):
statvfs = os.statvfs(self.dir_path)
return statvfs.f_frsize * (statvfs.f_blocks - statvfs.f_bfree)
def included_in(self, app):
''' Check if there is pool containing this one - either as a
filesystem or its LVM volume'''
return qubes.storage.search_pool_containing_dir(
[pool for pool in app.pools.values() if pool is not self],
self.dir_path)
class FileVolume(qubes.storage.Volume):
''' Parent class for the xen volumes implementation which expects a

View File

@ -24,6 +24,7 @@
import os
import qubes.exc
import qubes.storage
from qubes.storage import Pool, StoragePoolException, Volume
@ -202,6 +203,12 @@ class LinuxKernel(Pool):
raise qubes.exc.QubesValueError(
'LinuxModules supports only revisions_to_keep=0')
def included_in(self, app):
''' Check if there is pool containing /var/lib/qubes/vm-kernels '''
return qubes.storage.search_pool_containing_dir(
[pool for pool in app.pools.values() if pool is not self],
self.dir_path)
@property
def volumes(self):
''' Return all known kernel volumes '''

View File

@ -108,6 +108,12 @@ class ReflinkPool(qubes.storage.Pool):
statvfs = os.statvfs(self.dir_path)
return statvfs.f_frsize * (statvfs.f_blocks - statvfs.f_bfree)
def included_in(self, app):
''' Check if there is pool containing this one - either as a
filesystem or its LVM volume'''
return qubes.storage.search_pool_containing_dir(
[pool for pool in app.pools.values() if pool is not self],
self.dir_path)
class ReflinkVolume(qubes.storage.Volume):
def create(self):