浏览代码

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
Marek Marczykowski-Górecki 6 年之前
父节点
当前提交
d40fae9756
共有 4 个文件被更改,包括 51 次插入0 次删除
  1. 32 0
      qubes/storage/__init__.py
  2. 6 0
      qubes/storage/file.py
  3. 7 0
      qubes/storage/kernels.py
  4. 6 0
      qubes/storage/reflink.py

+ 32 - 0
qubes/storage/__init__.py

@@ -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.

+ 6 - 0
qubes/storage/file.py

@@ -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

+ 7 - 0
qubes/storage/kernels.py

@@ -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 '''

+ 6 - 0
qubes/storage/reflink.py

@@ -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):