[PATCH 1/5][blivet] Handle nested btrfs subvolumes correctly. (#1016959)

David Lehman dlehman at redhat.com
Fri Nov 22 16:00:07 UTC 2013


Subvolumes are nested. That is to say that removing subvolume foo
will also remove subvolumes foo/bar and foo/bar/baz.
---
 blivet/devicelibs/btrfs.py | 12 +++++++++---
 blivet/devices.py          | 14 ++++++++++++--
 blivet/devicetree.py       | 17 ++++++++++++++++-
 3 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/blivet/devicelibs/btrfs.py b/blivet/devicelibs/btrfs.py
index 2f0cee4..bde165d 100644
--- a/blivet/devicelibs/btrfs.py
+++ b/blivet/devicelibs/btrfs.py
@@ -32,6 +32,9 @@ _ = lambda x: gettext.ldgettext("blivet", x)
 import logging
 log = logging.getLogger("blivet")
 
+# this is the volume id btrfs always assigns to the top-level volume/tree
+MAIN_VOLUME_ID = 5
+
 def btrfs(args, capture=False):
     if capture:
         exec_func = util.capture_output
@@ -98,15 +101,18 @@ def create_snapshot(source, dest):
 def scan_device(path):
     return btrfs(["device", "scan", path])
 
+_subvol_list_regex = r'ID (\d+) gen \d+ parent (\d+) top level \d+ path (.+)\n'
+
 # get a list of subvolumes from a mounted btrfs filesystem
 def list_subvolumes(mountpoint):
     if not os.path.ismount(mountpoint):
         raise ValueError("volume not mounted")
 
-    args = ["subvol", "list", mountpoint]
+    args = ["subvol", "list", "-p", mountpoint]
     buf = btrfs(args, capture=True)
     vols = []
-    for group in re.findall(r'ID (\d+) gen \d+ top level \d+ path (.+)\n', buf):
-        vols.append({"id": int(group[0]), "path": group[1]})
+    for group in re.findall(_subvol_list_regex, buf):
+        vols.append({"id": int(group[0]), "parent": int(group[1]),
+                     "path": group[2]})
 
     return vols
diff --git a/blivet/devices.py b/blivet/devices.py
index 54ece22..82f1872 100644
--- a/blivet/devices.py
+++ b/blivet/devices.py
@@ -4346,6 +4346,7 @@ class BTRFSDevice(StorageDevice):
 
 class BTRFSVolumeDevice(BTRFSDevice):
     _type = "btrfs volume"
+    vol_id = btrfs.MAIN_VOLUME_ID
 
     def __init__(self, *args, **kwargs):
         self.dataLevel = kwargs.pop("dataLevel", None)
@@ -4373,7 +4374,7 @@ class BTRFSVolumeDevice(BTRFSDevice):
                                     label=label,
                                     volUUID=self.uuid,
                                     device=self.path,
-                                    mountopts="subvolid=0")
+                                    mountopts="subvolid=%d" % self.vol_id)
             self.originalFormat = copy.copy(self.format)
 
     def _setFormat(self, format):
@@ -4529,7 +4530,16 @@ class BTRFSSubVolumeDevice(BTRFSDevice):
 
     @property
     def volume(self):
-        return self.parents[0]
+        parent = self.parents[0]
+        vol = None
+        while True:
+            if not isinstance(parent, BTRFSSubVolumeDevice):
+                vol = parent
+                break
+
+            parent = parent.parents[0]
+
+        return vol
 
     def setupParents(self, orig=False):
         """ Run setup method of all parent devices. """
diff --git a/blivet/devicetree.py b/blivet/devicetree.py
index bad135c..8ab1e5f 100644
--- a/blivet/devicetree.py
+++ b/blivet/devicetree.py
@@ -1590,14 +1590,29 @@ class DeviceTree(object):
             for subvol_dict in btrfs_dev.listSubVolumes():
                 vol_id = subvol_dict["id"]
                 vol_path = subvol_dict["path"]
+                parent_id = subvol_dict["parent"]
                 if vol_path in [sv.name for sv in btrfs_dev.subvolumes]:
                     continue
+
+                # look up the parent subvol
+                parent = None
+                subvols = [btrfs_dev] + btrfs_dev.subvolumes
+                for sv in subvols:
+                    if sv.vol_id == parent_id:
+                        parent = sv
+                        break
+
+                if parent is None:
+                    log.error("failed to find parent (%d) for subvol %s"
+                              % (parent_id, vol_path))
+                    raise DeviceTreeError("could not find parent for subvol")
+
                 fmt = getFormat("btrfs", device=btrfs_dev.path, exists=True,
                                 mountopts="subvol=%s" % vol_path)
                 subvol = BTRFSSubVolumeDevice(vol_path,
                                               vol_id=vol_id,
                                               format=fmt,
-                                              parents=[btrfs_dev],
+                                              parents=[parent],
                                               exists=True)
                 self._addDevice(subvol)
 
-- 
1.8.1.4



More information about the anaconda-patches mailing list